Skip to content

Commit 35f7927

Browse files
authored
Rollup merge of rust-lang#53183 - estebank:println-comma, r=oli-obk
Suggest comma when missing in macro call When missing a comma in a macro call, suggest it, regardless of position. When a macro call doesn't match any of the patterns, check if the call's token stream could be missing a comma between two idents, and if so, create a new token stream containing the comma and try to match against the macro patterns. If successful, emit the suggestion. This works on arbitrary macros, with no need of special support from the macro writers. ``` error: no rules expected the token `d` --> $DIR/missing-comma.rs:26:18 | LL | foo!(a, b, c d, e); | -^ | | | help: missing comma here ``` Follow up to rust-lang#52397.
2 parents 1729652 + f4039af commit 35f7927

File tree

4 files changed

+73
-19
lines changed

4 files changed

+73
-19
lines changed

src/libsyntax/ext/tt/macro_rules.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt,
181181
for lhs in lhses { // try each arm's matchers
182182
let lhs_tt = match *lhs {
183183
quoted::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
184-
_ => cx.span_bug(sp, "malformed macro lhs")
184+
_ => continue,
185185
};
186186
match TokenTree::parse(cx, lhs_tt, arg.clone()) {
187187
Success(_) => {
@@ -191,7 +191,7 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt,
191191
err.span_suggestion_short(
192192
comma_span,
193193
"missing comma here",
194-
",".to_string(),
194+
", ".to_string(),
195195
);
196196
}
197197
}

src/libsyntax/tokenstream.rs

+35-13
Original file line numberDiff line numberDiff line change
@@ -186,21 +186,43 @@ impl TokenStream {
186186
/// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream`
187187
/// separating the two arguments with a comma for diagnostic suggestions.
188188
pub(crate) fn add_comma(&self) -> Option<(TokenStream, Span)> {
189-
// Used to suggest if a user writes `println!("{}" a);`
189+
// Used to suggest if a user writes `foo!(a b);`
190190
if let TokenStreamKind::Stream(ref slice) = self.kind {
191-
if slice.len() == 2 {
192-
let comma_span = match slice[0] {
193-
TokenStream { kind: TokenStreamKind::Tree(TokenTree::Token(sp, _)) } |
194-
TokenStream { kind: TokenStreamKind::Tree(TokenTree::Delimited(sp, _)) } => {
195-
sp.shrink_to_hi()
191+
let mut suggestion = None;
192+
let mut iter = slice.iter().enumerate().peekable();
193+
while let Some((pos, ts)) = iter.next() {
194+
if let Some((_, next)) = iter.peek() {
195+
match (ts, next) {
196+
(TokenStream {
197+
kind: TokenStreamKind::Tree(TokenTree::Token(_, token::Token::Comma))
198+
}, _) |
199+
(_, TokenStream {
200+
kind: TokenStreamKind::Tree(TokenTree::Token(_, token::Token::Comma))
201+
}) => {}
202+
(TokenStream {
203+
kind: TokenStreamKind::Tree(TokenTree::Token(sp, _))
204+
}, _) |
205+
(TokenStream {
206+
kind: TokenStreamKind::Tree(TokenTree::Delimited(sp, _))
207+
}, _) => {
208+
let sp = sp.shrink_to_hi();
209+
let comma = TokenStream {
210+
kind: TokenStreamKind::Tree(TokenTree::Token(sp, token::Comma)),
211+
};
212+
suggestion = Some((pos, comma, sp));
213+
}
214+
_ => {}
196215
}
197-
_ => DUMMY_SP,
198-
};
199-
let comma = TokenStream {
200-
kind: TokenStreamKind::Tree(TokenTree::Token(comma_span, token::Comma)),
201-
};
202-
let slice = RcSlice::new(vec![slice[0].clone(), comma, slice[1].clone()]);
203-
return Some((TokenStream { kind: TokenStreamKind::Stream(slice) }, comma_span));
216+
}
217+
}
218+
if let Some((pos, comma, sp)) = suggestion {
219+
let mut new_slice = vec![];
220+
let parts = slice.split_at(pos + 1);
221+
new_slice.extend_from_slice(parts.0);
222+
new_slice.push(comma);
223+
new_slice.extend_from_slice(parts.1);
224+
let slice = RcSlice::new(new_slice);
225+
return Some((TokenStream { kind: TokenStreamKind::Stream(slice) }, sp));
204226
}
205227
}
206228
None

src/test/ui/macros/missing-comma.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,22 @@
99
// except according to those terms.
1010

1111
macro_rules! foo {
12-
($a:ident, $b:ident) => ()
12+
($a:ident) => ();
13+
($a:ident, $b:ident) => ();
14+
($a:ident, $b:ident, $c:ident) => ();
15+
($a:ident, $b:ident, $c:ident, $d:ident) => ();
16+
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident) => ();
1317
}
1418

1519
fn main() {
1620
println!("{}" a);
1721
//~^ ERROR expected token: `,`
1822
foo!(a b);
1923
//~^ ERROR no rules expected the token `b`
24+
foo!(a, b, c, d e);
25+
//~^ ERROR no rules expected the token `e`
26+
foo!(a, b, c d, e);
27+
//~^ ERROR no rules expected the token `d`
28+
foo!(a, b, c d e);
29+
//~^ ERROR no rules expected the token `d`
2030
}
+25-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,38 @@
11
error: expected token: `,`
2-
--> $DIR/missing-comma.rs:16:19
2+
--> $DIR/missing-comma.rs:20:19
33
|
44
LL | println!("{}" a);
55
| ^
66

77
error: no rules expected the token `b`
8-
--> $DIR/missing-comma.rs:18:12
8+
--> $DIR/missing-comma.rs:22:12
99
|
1010
LL | foo!(a b);
1111
| -^
1212
| |
1313
| help: missing comma here
1414

15-
error: aborting due to 2 previous errors
15+
error: no rules expected the token `e`
16+
--> $DIR/missing-comma.rs:24:21
17+
|
18+
LL | foo!(a, b, c, d e);
19+
| -^
20+
| |
21+
| help: missing comma here
22+
23+
error: no rules expected the token `d`
24+
--> $DIR/missing-comma.rs:26:18
25+
|
26+
LL | foo!(a, b, c d, e);
27+
| -^
28+
| |
29+
| help: missing comma here
30+
31+
error: no rules expected the token `d`
32+
--> $DIR/missing-comma.rs:28:18
33+
|
34+
LL | foo!(a, b, c d e);
35+
| ^
36+
37+
error: aborting due to 5 previous errors
1638

0 commit comments

Comments
 (0)