Skip to content

Commit c419562

Browse files
committed
Allow giving a name for the context and applicability.
Build the suggestion tokens while parsing rather than all at once at the end.
1 parent a4aa513 commit c419562

File tree

2 files changed

+81
-69
lines changed

2 files changed

+81
-69
lines changed

clippy_macros/src/expr_sugg.rs

Lines changed: 61 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use core::{cmp, fmt::Write, mem};
2-
use proc_macro2::{Delimiter, Ident, Spacing, Span, TokenStream, TokenTree as TT};
2+
use proc_macro2::{Delimiter, Group, Ident, Spacing, Span, TokenStream, TokenTree as TT};
33
use quote::{quote, quote_spanned, ToTokens};
44

55
macro_rules! expr_position {
@@ -83,11 +83,6 @@ expr_precedence! {
8383
Prefix => Unary,
8484
Postfix => Call,
8585
}
86-
impl Default for ExprPrecedence {
87-
fn default() -> Self {
88-
Self::Postfix
89-
}
90-
}
9186

9287
macro_rules! prec {
9388
($name:ident) => {{
@@ -172,30 +167,6 @@ impl Default for State {
172167
#[allow(clippy::enum_glob_use)]
173168
use State::*;
174169

175-
enum Output {
176-
String(String),
177-
Snip(Snip),
178-
Mut(TokenStream),
179-
}
180-
impl ToTokens for Output {
181-
fn to_tokens(&self, tokens: &mut TokenStream) {
182-
match self {
183-
Self::String(s) => tokens.extend(quote!(sugg.push_str(#s);)),
184-
Self::Snip(s) => {
185-
let stream = &s.stream;
186-
let position = s.position;
187-
tokens.extend(quote!(sugg.push_str(&clippy_utils::_internal::snip(cx, #stream, #position, &mut app));));
188-
},
189-
Self::Mut(s) => tokens.extend(quote! {
190-
match #s {
191-
rustc_ast::Mutability::Mut => sugg.push_str("mut "),
192-
rustc_ast::Mutability::Not => (),
193-
}
194-
}),
195-
}
196-
}
197-
}
198-
199170
enum Error {
200171
UnexpectedToken(Span),
201172
UnknownCommand(Span),
@@ -215,28 +186,42 @@ struct Snip {
215186
position: ExprPosition,
216187
}
217188

218-
#[derive(Default)]
219189
struct ExprParser {
220190
state: State,
221191
next_string: String,
222192
precedence: ExprPrecedence,
223-
result: Vec<Output>,
193+
result: TokenStream,
224194
}
225195
impl ExprParser {
196+
#[allow(clippy::needless_pass_by_value)]
197+
fn new(cx_tokens: TT, app_tokens: TT) -> Self {
198+
Self {
199+
state: State::default(),
200+
next_string: String::new(),
201+
precedence: ExprPrecedence::Postfix,
202+
result: quote! {
203+
let cx: &rustc_lint::LateContext<'_> = #cx_tokens;
204+
let app: &mut rustc_errors::Applicability = #app_tokens;
205+
let mut sugg = String::new();
206+
},
207+
}
208+
}
209+
226210
fn push_str(&mut self) {
227211
if !self.next_string.is_empty() {
228-
self.result.push(Output::String(self.next_string.clone()));
212+
let s = &self.next_string;
213+
self.result.extend(quote!(sugg.push_str(#s);));
229214
self.next_string.clear();
230215
}
231216
}
232217

233218
fn push_snip(&mut self, kind: ExprKind, position: ExprPosition) {
234219
if let ExprKind::Snip(s) = kind {
235220
debug_assert!(self.next_string.is_empty());
236-
self.result.push(Output::Snip(Snip {
237-
position: cmp::max(s.position, position),
238-
..s
239-
}));
221+
let stream = s.stream;
222+
let position = cmp::max(s.position, position);
223+
self.result
224+
.extend(quote!(sugg.push_str(&clippy_utils::_internal::snip(cx, #stream, #position, app));));
240225
}
241226
}
242227

@@ -254,8 +239,7 @@ impl ExprParser {
254239
}
255240

256241
#[allow(clippy::too_many_lines)]
257-
fn parse(&mut self, input: TokenStream) -> Result<(), Error> {
258-
let mut iter = input.into_iter();
242+
fn parse(&mut self, mut iter: <TokenStream as IntoIterator>::IntoIter) -> Result<(), Error> {
259243
while let Some(tt) = iter.next() {
260244
self.state = match (tt, mem::take(&mut self.state)) {
261245
(TT::Punct(p), state @ (Start | Prefix | PrefixRef | BinOp(_) | PartialBinOp(..)))
@@ -293,7 +277,14 @@ impl ExprParser {
293277
match iter.next().unwrap() {
294278
TT::Group(args) if args.delimiter() == Delimiter::Parenthesis => {
295279
self.push_str();
296-
self.result.push(Output::Mut(args.stream()));
280+
let stream = args.stream();
281+
self.result.extend(quote! {
282+
let mutability: rustc_ast::Mutability = #stream;
283+
match mutability {
284+
rustc_ast::Mutability::Mut => sugg.push_str("mut "),
285+
rustc_ast::Mutability::Not => (),
286+
}
287+
});
297288
Prefix
298289
},
299290
tt => return Err(Error::UnexpectedToken(tt.span())),
@@ -313,7 +304,7 @@ impl ExprParser {
313304
let precedence = self.precedence;
314305
self.next_string.push('[');
315306
self.state = Start;
316-
self.parse(g.stream())?;
307+
self.parse(g.stream().into_iter())?;
317308
self.next_string.push(']');
318309
self.precedence = precedence;
319310
Expr(ExprKind::Normal)
@@ -527,7 +518,7 @@ impl ExprParser {
527518
let precedence = self.precedence;
528519
self.next_string.push('(');
529520
self.state = Start;
530-
self.parse(g.stream())?;
521+
self.parse(g.stream().into_iter())?;
531522
self.next_string.push(')');
532523
self.precedence = precedence;
533524
Expr(ExprKind::Normal)
@@ -537,6 +528,7 @@ impl ExprParser {
537528
};
538529
}
539530

531+
self.push_str();
540532
match mem::take(&mut self.state) {
541533
Start => Ok(()),
542534
Expr(kind) => {
@@ -547,19 +539,14 @@ impl ExprParser {
547539
}
548540
}
549541

550-
fn into_stream(mut self) -> TokenStream {
551-
self.push_str();
552-
let output = self.result.iter();
542+
fn into_stream(self) -> TokenStream {
543+
let mut result = self.result;
553544
let precedence = self.precedence;
554-
555-
quote! {{
556-
let mut sugg = String::new();
557-
#(#output)*
558-
clippy_utils::_internal::Sugg {
559-
sugg,
560-
precedence: #precedence,
561-
}
562-
}}
545+
result.extend(quote! { clippy_utils::_internal::Sugg {
546+
sugg,
547+
precedence: #precedence,
548+
}});
549+
TT::Group(Group::new(Delimiter::Brace, result)).into()
563550
}
564551
}
565552

@@ -568,8 +555,26 @@ fn make_error(msg: &str, span: Span) -> TokenStream {
568555
}
569556

570557
pub fn expr_sugg(input: TokenStream) -> TokenStream {
571-
let mut parser = ExprParser::default();
572-
match parser.parse(input) {
558+
let mut iter = input.into_iter();
559+
let cx = match iter.next() {
560+
Some(TT::Ident(cx)) => TT::Ident(cx),
561+
_ => panic!(),
562+
};
563+
match iter.next() {
564+
Some(TT::Punct(p)) if p.as_char() == ',' => (),
565+
_ => panic!(),
566+
};
567+
let app = match iter.next() {
568+
Some(TT::Ident(app)) => TT::Ident(app),
569+
_ => panic!(),
570+
};
571+
match iter.next() {
572+
Some(TT::Punct(p)) if p.as_char() == ',' => (),
573+
_ => panic!(),
574+
};
575+
576+
let mut parser = ExprParser::new(cx, app);
577+
match parser.parse(iter) {
573578
Ok(()) => parser.into_stream(),
574579
Err(Error::UnexpectedToken(span)) => make_error("unexpected token", span),
575580
Err(Error::UnexpectedMut(span)) => make_error("unexpected `mut` command", span),

clippy_macros/tests/expr_sugg.rs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@ extern crate clippy_macros;
44
extern crate rustc_ast;
55
extern crate rustc_errors;
66

7-
struct LateContext<'a>(&'a ());
7+
mod rustc_lint {
8+
pub struct LateContext<'a>(pub &'a ());
9+
}
10+
811
struct Expr<'a>(&'a str, ExprPosition);
912
mod clippy_utils {
1013
pub(crate) mod _internal {
1114
extern crate clippy_utils;
1215
pub(crate) use clippy_utils::_internal::{ExprPosition, Sugg};
1316

1417
pub(crate) fn snip(
15-
_: &crate::LateContext<'_>,
18+
_: &crate::rustc_lint::LateContext<'_>,
1619
e: &crate::Expr<'_>,
1720
position: ExprPosition,
1821
_app: &mut rustc_errors::Applicability,
@@ -32,41 +35,45 @@ use rustc_ast::Mutability;
3235

3336
#[test]
3437
fn test() {
35-
let cx = &LateContext(&());
38+
let cx = &rustc_lint::LateContext(&());
3639
let mut app = rustc_errors::Applicability::MachineApplicable;
40+
let app = &mut app;
3741

3842
let e = &Expr("&foo", ExprPosition::Prefix);
39-
assert_eq!(expr_sugg!($snip(e).foo).into_string(), "(&foo).foo");
43+
assert_eq!(expr_sugg!(cx, app, $snip(e).foo).into_string(), "(&foo).foo");
4044

4145
let mutability = Mutability::Not;
42-
assert_eq!(expr_sugg!(&$mut(mutability) foo).into_string(), "&foo");
46+
assert_eq!(expr_sugg!(cx, app, &$mut(mutability) foo).into_string(), "&foo");
4347

4448
let mutability = Mutability::Mut;
45-
assert_eq!(expr_sugg!(&$mut(mutability) foo).into_string(), "&mut foo");
49+
assert_eq!(expr_sugg!(cx, app, &$mut(mutability) foo).into_string(), "&mut foo");
4650

4751
let e = &Expr("x + y", ExprPosition::AddLhs);
48-
assert_eq!(expr_sugg!($snip(e) + 20).into_string(), "x + y + 20");
49-
assert_eq!(expr_sugg!(20 + $snip(e)).into_string(), "20 + (x + y)");
52+
assert_eq!(expr_sugg!(cx, app, $snip(e) + 20).into_string(), "x + y + 20");
53+
assert_eq!(expr_sugg!(cx, app, 20 + $snip(e)).into_string(), "20 + (x + y)");
5054

5155
let e = &Expr("foo", ExprPosition::Postfix);
52-
assert_eq!(expr_sugg!(&$snip(e)).for_position(ExprPosition::Postfix), "(&foo)");
56+
assert_eq!(
57+
expr_sugg!(cx, app, &$snip(e)).for_position(ExprPosition::Postfix),
58+
"(&foo)"
59+
);
5360

5461
let e = &Expr("x * y", ExprPosition::MulLhs);
5562
assert_eq!(
56-
expr_sugg!(x + $snip(e)).for_position(ExprPosition::MulLhs),
63+
expr_sugg!(cx, app, x + $snip(e)).for_position(ExprPosition::MulLhs),
5764
"(x + x * y)"
5865
);
5966

6067
assert_eq!(
61-
expr_sugg!($snip(e) != $snip(e)).for_position(ExprPosition::BitAndLhs),
68+
expr_sugg!(cx, app, $snip(e) != $snip(e)).for_position(ExprPosition::BitAndLhs),
6269
"(x * y != x * y)"
6370
);
6471
assert_eq!(
65-
expr_sugg!((x..=$snip(e)).start).for_position(ExprPosition::Prefix),
72+
expr_sugg!(cx, app, (x..=$snip(e)).start).for_position(ExprPosition::Prefix),
6673
"(x..=x * y).start"
6774
);
6875
assert_eq!(
69-
expr_sugg!(x&&&$snip(e)).for_position(ExprPosition::Let),
76+
expr_sugg!(cx, app, x&&&$snip(e)).for_position(ExprPosition::Let),
7077
"(x && &(x * y))"
7178
);
7279
}

0 commit comments

Comments
 (0)