Skip to content

Commit f4039af

Browse files
committed
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.
1 parent 26d7b64 commit f4039af

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)