From 4862eee8b762257cf28bddc41d9bb709d1fb9359 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 14 Jul 2018 23:50:08 -0700 Subject: [PATCH 1/3] Suggest comma when writing `println!("{}" a);` --- src/libsyntax/ext/tt/macro_rules.rs | 27 ++++++++++++++++++++++++- src/libsyntax/tokenstream.rs | 25 +++++++++++++++++++++++ src/test/ui/macros/missing-comma.rs | 14 +++++++++++++ src/test/ui/macros/missing-comma.stderr | 10 +++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/macros/missing-comma.rs create mode 100644 src/test/ui/macros/missing-comma.stderr diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index c9ec2c7d1e86a..e7e94614ac865 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -174,7 +174,32 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt, } let best_fail_msg = parse_failure_msg(best_fail_tok.expect("ran no matchers")); - cx.span_err(best_fail_spot.substitute_dummy(sp), &best_fail_msg); + let mut err = cx.struct_span_err(best_fail_spot.substitute_dummy(sp), &best_fail_msg); + + // Check whether there's a missing comma in this macro call, like `println!("{}" a);` + if let Some((arg, comma_span)) = arg.add_comma() { + 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") + }; + match TokenTree::parse(cx, lhs_tt, arg.clone()) { + Success(_) => { + if comma_span == DUMMY_SP { + err.note("you might be missing a comma"); + } else { + err.span_suggestion_short( + comma_span, + "missing comma here", + ",".to_string(), + ); + } + } + _ => {} + } + } + } + err.emit(); cx.trace_macros_diag(); DummyResult::any(sp) } diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index 1a4236b280b45..ef914e8de53f3 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -182,6 +182,31 @@ pub struct TokenStream { kind: TokenStreamKind, } +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 ot suggest if a user writes `println!("{}" a);` + 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() + } + _ => 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)); + } + } + None + } +} + #[derive(Clone, Debug)] enum TokenStreamKind { Empty, diff --git a/src/test/ui/macros/missing-comma.rs b/src/test/ui/macros/missing-comma.rs new file mode 100644 index 0000000000000..e69b1ff5a4db7 --- /dev/null +++ b/src/test/ui/macros/missing-comma.rs @@ -0,0 +1,14 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + println!("{}" a); + //~^ ERROR no rules expected the token `a` +} diff --git a/src/test/ui/macros/missing-comma.stderr b/src/test/ui/macros/missing-comma.stderr new file mode 100644 index 0000000000000..cc12e43fc27bf --- /dev/null +++ b/src/test/ui/macros/missing-comma.stderr @@ -0,0 +1,10 @@ +error: no rules expected the token `a` + --> $DIR/missing-comma.rs:12:19 + | +LL | println!("{}" a); + | -^ + | | + | help: missing comma here + +error: aborting due to previous error + From cce4ea5149e28b938d0af8466dd9af1e489f1b55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 6 Aug 2018 20:54:51 -0700 Subject: [PATCH 2/3] Point at correct span when missing comma in `println` --- src/libsyntax_ext/format.rs | 2 +- .../ui/codemap_tests/bad-format-args.stderr | 12 ++++-------- src/test/ui/macros/missing-comma.rs | 8 +++++++- src/test/ui/macros/missing-comma.stderr | 18 ++++++++++++------ 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 46c85497ee78f..53f8fe2b0c223 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -147,7 +147,7 @@ fn parse_args(ecx: &mut ExtCtxt, let mut named = false; while p.token != token::Eof { if !p.eat(&token::Comma) { - ecx.span_err(sp, "expected token: `,`"); + ecx.span_err(p.span, "expected token: `,`"); return None; } if p.token == token::Eof { diff --git a/src/test/ui/codemap_tests/bad-format-args.stderr b/src/test/ui/codemap_tests/bad-format-args.stderr index d0cdeb2178f15..1c801f2a79039 100644 --- a/src/test/ui/codemap_tests/bad-format-args.stderr +++ b/src/test/ui/codemap_tests/bad-format-args.stderr @@ -7,20 +7,16 @@ LL | format!(); //~ ERROR requires at least a format string argument = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) error: expected token: `,` - --> $DIR/bad-format-args.rs:13:5 + --> $DIR/bad-format-args.rs:13:16 | LL | format!("" 1); //~ ERROR expected token: `,` - | ^^^^^^^^^^^^^^ - | - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + | ^ error: expected token: `,` - --> $DIR/bad-format-args.rs:14:5 + --> $DIR/bad-format-args.rs:14:19 | LL | format!("", 1 1); //~ ERROR expected token: `,` - | ^^^^^^^^^^^^^^^^^ - | - = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info) + | ^ error: aborting due to 3 previous errors diff --git a/src/test/ui/macros/missing-comma.rs b/src/test/ui/macros/missing-comma.rs index e69b1ff5a4db7..ac82171a4e8cc 100644 --- a/src/test/ui/macros/missing-comma.rs +++ b/src/test/ui/macros/missing-comma.rs @@ -8,7 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +macro_rules! foo { + ($a:ident, $b:ident) => () +} + fn main() { println!("{}" a); - //~^ ERROR no rules expected the token `a` + //~^ ERROR expected token: `,` + foo!(a b); + //~^ ERROR no rules expected the token `b` } diff --git a/src/test/ui/macros/missing-comma.stderr b/src/test/ui/macros/missing-comma.stderr index cc12e43fc27bf..3467032d9b5f3 100644 --- a/src/test/ui/macros/missing-comma.stderr +++ b/src/test/ui/macros/missing-comma.stderr @@ -1,10 +1,16 @@ -error: no rules expected the token `a` - --> $DIR/missing-comma.rs:12:19 +error: expected token: `,` + --> $DIR/missing-comma.rs:16:19 | LL | println!("{}" a); - | -^ - | | - | help: missing comma here + | ^ -error: aborting due to previous error +error: no rules expected the token `b` + --> $DIR/missing-comma.rs:18:12 + | +LL | foo!(a b); + | -^ + | | + | help: missing comma here + +error: aborting due to 2 previous errors From daa5bd35a8746126a4af014d135ddb8bc442b9e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 6 Aug 2018 23:46:28 -0700 Subject: [PATCH 3/3] fix typo --- src/libsyntax/tokenstream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index ef914e8de53f3..f84b5307a1137 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -186,7 +186,7 @@ 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 ot suggest if a user writes `println!("{}" a);` + // Used to suggest if a user writes `println!("{}" a);` if let TokenStreamKind::Stream(ref slice) = self.kind { if slice.len() == 2 { let comma_span = match slice[0] {