Skip to content

Commit 3f4f18f

Browse files
committed
Auto merge of #52397 - estebank:println-comma, r=oli-obk
Suggest comma when writing `println!("{}" a);` Fix #49370.
2 parents e90dc6f + daa5bd3 commit 3f4f18f

File tree

6 files changed

+92
-10
lines changed

6 files changed

+92
-10
lines changed

src/libsyntax/ext/tt/macro_rules.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,32 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt,
174174
}
175175

176176
let best_fail_msg = parse_failure_msg(best_fail_tok.expect("ran no matchers"));
177-
cx.span_err(best_fail_spot.substitute_dummy(sp), &best_fail_msg);
177+
let mut err = cx.struct_span_err(best_fail_spot.substitute_dummy(sp), &best_fail_msg);
178+
179+
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
180+
if let Some((arg, comma_span)) = arg.add_comma() {
181+
for lhs in lhses { // try each arm's matchers
182+
let lhs_tt = match *lhs {
183+
quoted::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
184+
_ => cx.span_bug(sp, "malformed macro lhs")
185+
};
186+
match TokenTree::parse(cx, lhs_tt, arg.clone()) {
187+
Success(_) => {
188+
if comma_span == DUMMY_SP {
189+
err.note("you might be missing a comma");
190+
} else {
191+
err.span_suggestion_short(
192+
comma_span,
193+
"missing comma here",
194+
",".to_string(),
195+
);
196+
}
197+
}
198+
_ => {}
199+
}
200+
}
201+
}
202+
err.emit();
178203
cx.trace_macros_diag();
179204
DummyResult::any(sp)
180205
}

src/libsyntax/tokenstream.rs

+25
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,31 @@ pub struct TokenStream {
182182
kind: TokenStreamKind,
183183
}
184184

185+
impl TokenStream {
186+
/// Given a `TokenStream` with a `Stream` of only two arguments, return a new `TokenStream`
187+
/// separating the two arguments with a comma for diagnostic suggestions.
188+
pub(crate) fn add_comma(&self) -> Option<(TokenStream, Span)> {
189+
// Used to suggest if a user writes `println!("{}" a);`
190+
if let TokenStreamKind::Stream(ref slice) = self.kind {
191+
if slice.len() == 2 {
192+
let comma_span = match slice[0] {
193+
TokenStream { kind: TokenStreamKind::Tree(TokenTree::Token(sp, _)) } |
194+
TokenStream { kind: TokenStreamKind::Tree(TokenTree::Delimited(sp, _)) } => {
195+
sp.shrink_to_hi()
196+
}
197+
_ => DUMMY_SP,
198+
};
199+
let comma = TokenStream {
200+
kind: TokenStreamKind::Tree(TokenTree::Token(comma_span, token::Comma)),
201+
};
202+
let slice = RcSlice::new(vec![slice[0].clone(), comma, slice[1].clone()]);
203+
return Some((TokenStream { kind: TokenStreamKind::Stream(slice) }, comma_span));
204+
}
205+
}
206+
None
207+
}
208+
}
209+
185210
#[derive(Clone, Debug)]
186211
enum TokenStreamKind {
187212
Empty,

src/libsyntax_ext/format.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ fn parse_args(ecx: &mut ExtCtxt,
147147
let mut named = false;
148148
while p.token != token::Eof {
149149
if !p.eat(&token::Comma) {
150-
ecx.span_err(sp, "expected token: `,`");
150+
ecx.span_err(p.span, "expected token: `,`");
151151
return None;
152152
}
153153
if p.token == token::Eof {

src/test/ui/codemap_tests/bad-format-args.stderr

+4-8
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,16 @@ LL | format!(); //~ ERROR requires at least a format string argument
77
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
88

99
error: expected token: `,`
10-
--> $DIR/bad-format-args.rs:13:5
10+
--> $DIR/bad-format-args.rs:13:16
1111
|
1212
LL | format!("" 1); //~ ERROR expected token: `,`
13-
| ^^^^^^^^^^^^^^
14-
|
15-
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
13+
| ^
1614

1715
error: expected token: `,`
18-
--> $DIR/bad-format-args.rs:14:5
16+
--> $DIR/bad-format-args.rs:14:19
1917
|
2018
LL | format!("", 1 1); //~ ERROR expected token: `,`
21-
| ^^^^^^^^^^^^^^^^^
22-
|
23-
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
19+
| ^
2420

2521
error: aborting due to 3 previous errors
2622

src/test/ui/macros/missing-comma.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! foo {
12+
($a:ident, $b:ident) => ()
13+
}
14+
15+
fn main() {
16+
println!("{}" a);
17+
//~^ ERROR expected token: `,`
18+
foo!(a b);
19+
//~^ ERROR no rules expected the token `b`
20+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: expected token: `,`
2+
--> $DIR/missing-comma.rs:16:19
3+
|
4+
LL | println!("{}" a);
5+
| ^
6+
7+
error: no rules expected the token `b`
8+
--> $DIR/missing-comma.rs:18:12
9+
|
10+
LL | foo!(a b);
11+
| -^
12+
| |
13+
| help: missing comma here
14+
15+
error: aborting due to 2 previous errors
16+

0 commit comments

Comments
 (0)