]> git.lizzy.rs Git - rust.git/commitdiff
Suggest comma when missing in macro call
authorEsteban Küber <esteban@kuber.com.ar>
Wed, 8 Aug 2018 05:28:09 +0000 (22:28 -0700)
committerEsteban Küber <esteban@kuber.com.ar>
Wed, 8 Aug 2018 05:31:57 +0000 (22:31 -0700)
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.

src/libsyntax/ext/tt/macro_rules.rs
src/libsyntax/tokenstream.rs
src/test/ui/macros/missing-comma.rs
src/test/ui/macros/missing-comma.stderr

index e7e94614ac8658e1683332953703486e2139e8e6..f51d079a6c058fca39408024557156c80a6634b0 100644 (file)
@@ -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(),
                         );
                     }
                 }
index f84b5307a1137895e4240590621d723789cc1267..fda975e6c456bdd16dbad09bba0d029c4e8f27cb 100644 (file)
@@ -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
index ac82171a4e8cc065779490bbe848d6ad713bf7fd..07e69b9619d1f9683c8d6aa2ba2281e795767c1e 100644 (file)
@@ -9,7 +9,11 @@
 // 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() {
@@ -17,4 +21,10 @@ fn main() {
     //~^ 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`
 }
index 3467032d9b5f3ada3818ed757e40f75795d7db21..9d8de87e5bb7f154237d97be5c51e0bf5d91c7ca 100644 (file)
@@ -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