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.
This commit is contained in:
commit
8188f12aaa
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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`
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user