Skip to content

Commit 4997ffc

Browse files
committed
Parse as and type expression, and paths.
1 parent 9beb6ea commit 4997ffc

File tree

2 files changed

+83
-15
lines changed

2 files changed

+83
-15
lines changed

clippy_macros/src/expr_sugg.rs

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ macro_rules! prec {
9898
enum PartialOp {
9999
Not,
100100
LoneNot,
101+
Colon,
101102
Dot,
102103
Mul,
103104
Div,
@@ -118,6 +119,7 @@ impl PartialOp {
118119
fn precedence(self) -> Result<(ExprPrecedence, ExprPosition, ExprPosition), ()> {
119120
match self {
120121
Self::Not | Self::LoneNot | Self::Dot => Err(()),
122+
Self::Colon => Ok((ExprPrecedence::Cast, ExprPosition::Cast, ExprPosition::Cast)),
121123
Self::Mul | Self::Div | Self::Mod => Ok(prec!(Mul)),
122124
Self::Add | Self::Sub => Ok(prec!(Add)),
123125
Self::Shr | Self::Shl => Ok(prec!(Shift)),
@@ -132,6 +134,7 @@ impl PartialOp {
132134
fn as_str(self) -> &'static str {
133135
match self {
134136
Self::Not | Self::LoneNot => "!",
137+
Self::Colon => ":",
135138
Self::Dot => ".",
136139
Self::Mul => "*",
137140
Self::Div => "/",
@@ -158,6 +161,7 @@ enum State {
158161
Expr(ExprKind),
159162
BinOp(ExprPosition),
160163
PartialBinOp(ExprKind, PartialOp),
164+
PathSep,
161165
}
162166
impl Default for State {
163167
fn default() -> Self {
@@ -188,7 +192,6 @@ struct Snip {
188192
}
189193

190194
struct ExprParser {
191-
state: State,
192195
next_string: String,
193196
precedence: ExprPrecedence,
194197
result: TokenStream,
@@ -197,7 +200,6 @@ impl ExprParser {
197200
#[allow(clippy::needless_pass_by_value)]
198201
fn new(cx_tokens: Vec<TT>, app_tokens: Vec<TT>, ctxt_tokens: Vec<TT>) -> Self {
199202
Self {
200-
state: State::default(),
201203
next_string: String::new(),
202204
precedence: ExprPrecedence::Postfix,
203205
result: quote! {
@@ -210,7 +212,7 @@ impl ExprParser {
210212
}
211213
}
212214

213-
fn push_str(&mut self) {
215+
fn push_next_string(&mut self) {
214216
if !self.next_string.is_empty() {
215217
let s = &self.next_string;
216218
self.result.extend(quote!(sugg.push_str(#s);));
@@ -241,11 +243,38 @@ impl ExprParser {
241243
}
242244
}
243245

246+
fn parse_ty(&mut self, iter: &mut <TokenStream as IntoIterator>::IntoIter) -> Result<(), Error> {
247+
match iter.next().unwrap() {
248+
TT::Punct(p) if p.as_char() == '$' => match iter.next().unwrap() {
249+
TT::Ident(cmd) => match &*cmd.to_string() {
250+
"ident" => match iter.next().unwrap() {
251+
TT::Group(args) if args.delimiter() == Delimiter::Parenthesis => {
252+
self.push_next_string();
253+
let stream = args.stream();
254+
self.result.extend(quote! {
255+
let _ = core::write!(sugg, "{}", #stream);
256+
});
257+
},
258+
tt => return Err(Error::UnexpectedToken(tt.span())),
259+
},
260+
_ => return Err(Error::UnknownCommand(cmd.span())),
261+
},
262+
tt => return Err(Error::UnexpectedToken(tt.span())),
263+
},
264+
TT::Ident(name) => {
265+
let _ = write!(self.next_string, "{} ", name);
266+
},
267+
tt => return Err(Error::UnexpectedToken(tt.span())),
268+
}
269+
Ok(())
270+
}
271+
244272
#[allow(clippy::too_many_lines)]
245273
fn parse(&mut self, mut iter: <TokenStream as IntoIterator>::IntoIter) -> Result<(), Error> {
274+
let mut state = State::default();
246275
while let Some(tt) = iter.next() {
247-
self.state = match (tt, mem::take(&mut self.state)) {
248-
(TT::Punct(p), state @ (Start | Prefix | PrefixRef | BinOp(_) | PartialBinOp(..)))
276+
state = match (tt, mem::take(&mut state)) {
277+
(TT::Punct(p), state @ (Start | Prefix | PrefixRef | BinOp(_) | PartialBinOp(..) | PathSep))
249278
if p.as_char() == '$' =>
250279
{
251280
match iter.next().unwrap() {
@@ -263,9 +292,9 @@ impl ExprParser {
263292
let _ = write!(self.next_string, " {} ", op.as_str());
264293
rhs_pos
265294
},
266-
Expr(_) => return Err(Error::UnexpectedSnip(cmd.span())),
295+
Expr(_) | PathSep => return Err(Error::UnexpectedSnip(cmd.span())),
267296
};
268-
self.push_str();
297+
self.push_next_string();
269298
Expr(ExprKind::Snip(Snip {
270299
position,
271300
stream: args.stream(),
@@ -279,7 +308,7 @@ impl ExprParser {
279308
}
280309
match iter.next().unwrap() {
281310
TT::Group(args) if args.delimiter() == Delimiter::Parenthesis => {
282-
self.push_str();
311+
self.push_next_string();
283312
let stream = args.stream();
284313
self.result.extend(quote! {
285314
let mutability: rustc_ast::Mutability = #stream;
@@ -301,7 +330,7 @@ impl ExprParser {
301330
} else {
302331
self.push_partial_op(state);
303332
}
304-
self.push_str();
333+
self.push_next_string();
305334
let stream = args.stream();
306335
self.result.extend(quote! {
307336
let _ = core::write!(sugg, "{}", #stream);
@@ -322,7 +351,6 @@ impl ExprParser {
322351
self.push_snip(kind, ExprPosition::Postfix);
323352
let precedence = self.precedence;
324353
self.next_string.push('(');
325-
self.state = Start;
326354
self.parse(g.stream().into_iter())?;
327355
self.next_string.push(')');
328356
self.precedence = precedence;
@@ -332,7 +360,6 @@ impl ExprParser {
332360
self.push_snip(kind, ExprPosition::Postfix);
333361
let precedence = self.precedence;
334362
self.next_string.push('[');
335-
self.state = Start;
336363
self.parse(g.stream().into_iter())?;
337364
self.next_string.push(']');
338365
self.precedence = precedence;
@@ -359,6 +386,14 @@ impl ExprParser {
359386
},
360387
tt => return Err(Error::UnexpectedToken(tt.span())),
361388
},
389+
(':', Spacing::Joint) => PartialBinOp(kind, PartialOp::Colon),
390+
(':', Spacing::Alone) => {
391+
self.push_snip(kind, ExprPosition::Cast);
392+
self.next_string.push_str(": ");
393+
self.parse_ty(&mut iter);
394+
self.push_precedence(ExprPrecedence::Cast);
395+
Expr(ExprKind::Normal)
396+
},
362397
('=', Spacing::Joint) => PartialBinOp(kind, PartialOp::Assign),
363398
('=', Spacing::Alone) => {
364399
self.push_snip(kind, ExprPosition::AssignLhs);
@@ -422,6 +457,10 @@ impl ExprParser {
422457
self.next_string.push_str("..");
423458
BinOp(ExprPosition::Range)
424459
},
460+
(':', PartialOp::Colon) if matches!(kind, ExprKind::Ident) => {
461+
self.next_string.push_str("::");
462+
PathSep
463+
},
425464
(
426465
'=',
427466
PartialOp::Mul
@@ -528,11 +567,25 @@ impl ExprParser {
528567
},
529568
s => {
530569
self.push_partial_op(state);
531-
let _ = write!(self.next_string, "{}", s);
570+
self.next_string.push_str(s);
532571
Expr(ExprKind::Ident)
533572
},
534573
}
535574
},
575+
(TT::Ident(name), PathSep) => {
576+
let _ = write!(self.next_string, "{}", name);
577+
Expr(ExprKind::Ident)
578+
},
579+
(TT::Ident(name), Expr(kind)) => match &*name.to_string() {
580+
"as" => {
581+
self.push_snip(kind, ExprPosition::Cast);
582+
self.push_precedence(ExprPrecedence::Cast);
583+
self.next_string.push_str(" as ");
584+
self.parse_ty(&mut iter);
585+
Expr(ExprKind::Normal)
586+
},
587+
_ => return Err(Error::UnexpectedToken(name.span())),
588+
},
536589

537590
// Atoms
538591
(TT::Literal(lit), state @ (Start | Prefix | PrefixRef | BinOp(_) | PartialBinOp(..))) => {
@@ -546,7 +599,6 @@ impl ExprParser {
546599
self.push_partial_op(state);
547600
let precedence = self.precedence;
548601
self.next_string.push('(');
549-
self.state = Start;
550602
self.parse(g.stream().into_iter())?;
551603
self.next_string.push(')');
552604
self.precedence = precedence;
@@ -557,8 +609,8 @@ impl ExprParser {
557609
};
558610
}
559611

560-
self.push_str();
561-
match mem::take(&mut self.state) {
612+
self.push_next_string();
613+
match state {
562614
Start => Ok(()),
563615
Expr(kind) => {
564616
self.push_snip(kind, ExprPosition::Closure);

clippy_macros/tests/expr_sugg.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,20 @@ fn test() {
9292
expr_sugg!(cx, &mut *app, ctxt.clone(), y + x.$ident(ident)()).for_position(ExprPosition::AddRhs),
9393
"(y + x.foo())"
9494
);
95+
96+
assert_eq!(
97+
expr_sugg!(cx, &mut *app, ctxt.clone(), x::y::$ident(ident)::z(0)).into_string(),
98+
"x::y::foo::z(0)"
99+
);
100+
101+
assert_eq!(
102+
expr_sugg!(cx, &mut *app, ctxt.clone(), x::y as $ident(ident)).for_position(ExprPosition::Prefix),
103+
"(x::y as foo)"
104+
);
105+
106+
let e = &Expr("x + y", ExprPosition::AddLhs);
107+
assert_eq!(
108+
expr_sugg!(cx, &mut *app, ctxt.clone(), $snip(e): $ident(ident)).for_position(ExprPosition::Postfix),
109+
"((x + y): foo)"
110+
);
95111
}

0 commit comments

Comments
 (0)