diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index e7e94614ac865..f51d079a6c058 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 f84b5307a1137..fda975e6c456b 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 diff --git a/src/test/ui/macros/missing-comma.rs b/src/test/ui/macros/missing-comma.rs index ac82171a4e8cc..07e69b9619d1f 100644 --- a/src/test/ui/macros/missing-comma.rs +++ b/src/test/ui/macros/missing-comma.rs @@ -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` } diff --git a/src/test/ui/macros/missing-comma.stderr b/src/test/ui/macros/missing-comma.stderr index 3467032d9b5f3..9d8de87e5bb7f 100644 --- a/src/test/ui/macros/missing-comma.stderr +++ b/src/test/ui/macros/missing-comma.stderr @@ -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