Skip to content

Commit

Permalink
Suggest comma when missing in macro call
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
estebank committed Aug 8, 2018
1 parent 26d7b64 commit f4039af
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 19 deletions.
4 changes: 2 additions & 2 deletions src/libsyntax/ext/tt/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt,
for lhs in lhses { // try each arm's matchers
let lhs_tt = match *lhs {
quoted::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
_ => cx.span_bug(sp, "malformed macro lhs")
_ => continue,
};
match TokenTree::parse(cx, lhs_tt, arg.clone()) {
Success(_) => {
Expand All @@ -191,7 +191,7 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt,
err.span_suggestion_short(
comma_span,
"missing comma here",
",".to_string(),
", ".to_string(),
);
}
}
Expand Down
48 changes: 35 additions & 13 deletions src/libsyntax/tokenstream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,21 +186,43 @@ impl TokenStream {
/// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream`
/// separating the two arguments with a comma for diagnostic suggestions.
pub(crate) fn add_comma(&self) -> Option<(TokenStream, Span)> {
// Used to suggest if a user writes `println!("{}" a);`
// Used to suggest if a user writes `foo!(a b);`
if let TokenStreamKind::Stream(ref slice) = self.kind {
if slice.len() == 2 {
let comma_span = match slice[0] {
TokenStream { kind: TokenStreamKind::Tree(TokenTree::Token(sp, _)) } |
TokenStream { kind: TokenStreamKind::Tree(TokenTree::Delimited(sp, _)) } => {
sp.shrink_to_hi()
let mut suggestion = None;
let mut iter = slice.iter().enumerate().peekable();
while let Some((pos, ts)) = iter.next() {
if let Some((_, next)) = iter.peek() {
match (ts, next) {
(TokenStream {
kind: TokenStreamKind::Tree(TokenTree::Token(_, token::Token::Comma))
}, _) |
(_, TokenStream {
kind: TokenStreamKind::Tree(TokenTree::Token(_, token::Token::Comma))
}) => {}
(TokenStream {
kind: TokenStreamKind::Tree(TokenTree::Token(sp, _))
}, _) |
(TokenStream {
kind: TokenStreamKind::Tree(TokenTree::Delimited(sp, _))
}, _) => {
let sp = sp.shrink_to_hi();
let comma = TokenStream {
kind: TokenStreamKind::Tree(TokenTree::Token(sp, token::Comma)),
};
suggestion = Some((pos, comma, sp));
}
_ => {}
}
_ => DUMMY_SP,
};
let comma = TokenStream {
kind: TokenStreamKind::Tree(TokenTree::Token(comma_span, token::Comma)),
};
let slice = RcSlice::new(vec![slice[0].clone(), comma, slice[1].clone()]);
return Some((TokenStream { kind: TokenStreamKind::Stream(slice) }, comma_span));
}
}
if let Some((pos, comma, sp)) = suggestion {
let mut new_slice = vec![];
let parts = slice.split_at(pos + 1);
new_slice.extend_from_slice(parts.0);
new_slice.push(comma);
new_slice.extend_from_slice(parts.1);
let slice = RcSlice::new(new_slice);
return Some((TokenStream { kind: TokenStreamKind::Stream(slice) }, sp));
}
}
None
Expand Down
12 changes: 11 additions & 1 deletion src/test/ui/macros/missing-comma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,22 @@
// except according to those terms.

macro_rules! foo {
($a:ident, $b:ident) => ()
($a:ident) => ();
($a:ident, $b:ident) => ();
($a:ident, $b:ident, $c:ident) => ();
($a:ident, $b:ident, $c:ident, $d:ident) => ();
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident) => ();
}

fn main() {
println!("{}" a);
//~^ ERROR expected token: `,`
foo!(a b);
//~^ ERROR no rules expected the token `b`
foo!(a, b, c, d e);
//~^ ERROR no rules expected the token `e`
foo!(a, b, c d, e);
//~^ ERROR no rules expected the token `d`
foo!(a, b, c d e);
//~^ ERROR no rules expected the token `d`
}
28 changes: 25 additions & 3 deletions src/test/ui/macros/missing-comma.stderr
Original file line number Diff line number Diff line change
@@ -1,16 +1,38 @@
error: expected token: `,`
--> $DIR/missing-comma.rs:16:19
--> $DIR/missing-comma.rs:20:19
|
LL | println!("{}" a);
| ^

error: no rules expected the token `b`
--> $DIR/missing-comma.rs:18:12
--> $DIR/missing-comma.rs:22:12
|
LL | foo!(a b);
| -^
| |
| help: missing comma here

error: aborting due to 2 previous errors
error: no rules expected the token `e`
--> $DIR/missing-comma.rs:24:21
|
LL | foo!(a, b, c, d e);
| -^
| |
| help: missing comma here

error: no rules expected the token `d`
--> $DIR/missing-comma.rs:26:18
|
LL | foo!(a, b, c d, e);
| -^
| |
| help: missing comma here

error: no rules expected the token `d`
--> $DIR/missing-comma.rs:28:18
|
LL | foo!(a, b, c d e);
| ^

error: aborting due to 5 previous errors

0 comments on commit f4039af

Please sign in to comment.