about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorkennytm <kennytm@gmail.com>2018-08-09 16:58:49 +0800
committerkennytm <kennytm@gmail.com>2018-08-10 01:01:22 +0800
commit8188f12aaab8b650a13a93f71cf8d0647aa522f7 (patch)
tree0cc004c33d9d294a5a90b26308ab6925122245fe /src/libsyntax
parent7f886cf12b39e4079a9db1f3c5075b40de574a67 (diff)
parentf4039affa338fc5640c102ac5786a491bc94201f (diff)
downloadrust-8188f12aaab8b650a13a93f71cf8d0647aa522f7.tar.gz
rust-8188f12aaab8b650a13a93f71cf8d0647aa522f7.zip
Rollup merge of #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 #52397.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs4
-rw-r--r--src/libsyntax/tokenstream.rs48
2 files changed, 37 insertions, 15 deletions
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index e7e94614ac8..f51d079a6c0 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -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(_) => {
@@ -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(),
                         );
                     }
                 }
diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs
index f84b5307a11..fda975e6c45 100644
--- a/src/libsyntax/tokenstream.rs
+++ b/src/libsyntax/tokenstream.rs
@@ -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