mbe: Refactor diagnostics for invalid metavar expression syntax
Give a more user-friendly diagnostic about the following:
* Trailing tokens within braces, e.g. `${foo() extra}`
* Missing parentheses, e.g. `${foo}`
* Incorrect number of arguments, with a hint about correct usage.
This commit is contained in:
parent
e43d139a82
commit
e7ef31d651
5 changed files with 241 additions and 129 deletions
|
|
@ -133,6 +133,27 @@ expand_module_multiple_candidates =
|
|||
expand_must_repeat_once =
|
||||
this must repeat at least once
|
||||
|
||||
expand_mve_extra_tokens =
|
||||
unexpected trailing tokens
|
||||
.label = for this metavariable expression
|
||||
.range = the `{$name}` metavariable expression takes between {$min_or_exact_args} and {$max_args} arguments
|
||||
.exact = the `{$name}` metavariable expression takes {$min_or_exact_args ->
|
||||
[zero] no arguments
|
||||
[one] a single argument
|
||||
*[other] {$min_or_exact_args} arguments
|
||||
}
|
||||
.suggestion = try removing {$extra_count ->
|
||||
[one] this token
|
||||
*[other] these tokens
|
||||
}
|
||||
|
||||
expand_mve_missing_paren =
|
||||
expected `(`
|
||||
.label = for this this metavariable expression
|
||||
.unexpected = unexpected token
|
||||
.note = metavariable expressions use function-like parentheses syntax
|
||||
.suggestion = try adding parentheses
|
||||
|
||||
expand_mve_unrecognized_var =
|
||||
variable `{$key}` is not recognized in meta-variable expression
|
||||
|
||||
|
|
|
|||
|
|
@ -496,6 +496,40 @@ pub(crate) use metavar_exprs::*;
|
|||
mod metavar_exprs {
|
||||
use super::*;
|
||||
|
||||
#[derive(Diagnostic, Default)]
|
||||
#[diag(expand_mve_extra_tokens)]
|
||||
pub(crate) struct MveExtraTokens {
|
||||
#[primary_span]
|
||||
#[suggestion(code = "", applicability = "machine-applicable")]
|
||||
pub span: Span,
|
||||
#[label]
|
||||
pub ident_span: Span,
|
||||
pub extra_count: usize,
|
||||
|
||||
// The rest is only used for specific diagnostics and can be default if neither
|
||||
// `note` is `Some`.
|
||||
#[note(expand_exact)]
|
||||
pub exact_args_note: Option<()>,
|
||||
#[note(expand_range)]
|
||||
pub range_args_note: Option<()>,
|
||||
pub min_or_exact_args: usize,
|
||||
pub max_args: usize,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[note]
|
||||
#[diag(expand_mve_missing_paren)]
|
||||
pub(crate) struct MveMissingParen {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub ident_span: Span,
|
||||
#[label(expand_unexpected)]
|
||||
pub unexpected_span: Option<Span>,
|
||||
#[suggestion(code = "( /* ... */ )", applicability = "has-placeholders")]
|
||||
pub insert_span: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(expand_mve_unrecognized_var)]
|
||||
pub(crate) struct MveUnrecognizedVar {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ use rustc_macros::{Decodable, Encodable};
|
|||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::{Ident, Span, Symbol};
|
||||
|
||||
use crate::errors;
|
||||
|
||||
pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers";
|
||||
pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal";
|
||||
|
||||
|
|
@ -40,11 +42,32 @@ impl MetaVarExpr {
|
|||
) -> PResult<'psess, MetaVarExpr> {
|
||||
let mut iter = input.iter();
|
||||
let ident = parse_ident(&mut iter, psess, outer_span)?;
|
||||
let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = iter.next() else {
|
||||
let msg = "meta-variable expression parameter must be wrapped in parentheses";
|
||||
return Err(psess.dcx().struct_span_err(ident.span, msg));
|
||||
let next = iter.next();
|
||||
let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = next else {
|
||||
// No `()`; wrong or no delimiters. Point at a problematic span or a place to
|
||||
// add parens if it makes sense.
|
||||
let (unexpected_span, insert_span) = match next {
|
||||
Some(TokenTree::Delimited(..)) => (None, None),
|
||||
Some(tt) => (Some(tt.span()), None),
|
||||
None => (None, Some(ident.span.shrink_to_hi())),
|
||||
};
|
||||
let err =
|
||||
errors::MveMissingParen { ident_span: ident.span, unexpected_span, insert_span };
|
||||
return Err(psess.dcx().create_err(err));
|
||||
};
|
||||
check_trailing_token(&mut iter, psess)?;
|
||||
|
||||
// Ensure there are no trailing tokens in the braces, e.g. `${foo() extra}`
|
||||
if iter.peek().is_some() {
|
||||
let span = iter_span(&iter).expect("checked is_some above");
|
||||
let err = errors::MveExtraTokens {
|
||||
span,
|
||||
ident_span: ident.span,
|
||||
extra_count: iter.count(),
|
||||
..Default::default()
|
||||
};
|
||||
return Err(psess.dcx().create_err(err));
|
||||
}
|
||||
|
||||
let mut iter = args.iter();
|
||||
let rslt = match ident.as_str() {
|
||||
"concat" => parse_concat(&mut iter, psess, outer_span, ident.span)?,
|
||||
|
|
@ -67,7 +90,7 @@ impl MetaVarExpr {
|
|||
return Err(err);
|
||||
}
|
||||
};
|
||||
check_trailing_token(&mut iter, psess)?;
|
||||
check_trailing_tokens(&mut iter, psess, ident)?;
|
||||
Ok(rslt)
|
||||
}
|
||||
|
||||
|
|
@ -87,20 +110,51 @@ impl MetaVarExpr {
|
|||
}
|
||||
}
|
||||
|
||||
// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
|
||||
fn check_trailing_token<'psess>(
|
||||
/// Checks if there are any remaining tokens (for example, `${ignore($valid, extra)}`) and create
|
||||
/// a diag with the correct arg count if so.
|
||||
fn check_trailing_tokens<'psess>(
|
||||
iter: &mut TokenStreamIter<'_>,
|
||||
psess: &'psess ParseSess,
|
||||
ident: Ident,
|
||||
) -> PResult<'psess, ()> {
|
||||
if let Some(tt) = iter.next() {
|
||||
let mut diag = psess
|
||||
.dcx()
|
||||
.struct_span_err(tt.span(), format!("unexpected token: {}", pprust::tt_to_string(tt)));
|
||||
diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens");
|
||||
Err(diag)
|
||||
} else {
|
||||
Ok(())
|
||||
if iter.peek().is_none() {
|
||||
// All tokens consumed, as expected
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// `None` for max indicates the arg count must be exact, `Some` indicates a range is accepted.
|
||||
let (min_or_exact_args, max_args) = match ident.as_str() {
|
||||
"concat" => panic!("concat takes unlimited tokens but didn't eat them all"),
|
||||
"ignore" => (1, None),
|
||||
// 1 or 2 args
|
||||
"count" => (1, Some(2)),
|
||||
// 0 or 1 arg
|
||||
"index" => (0, Some(1)),
|
||||
"len" => (0, Some(1)),
|
||||
other => unreachable!("unknown MVEs should be rejected earlier (got `{other}`)"),
|
||||
};
|
||||
|
||||
let err = errors::MveExtraTokens {
|
||||
span: iter_span(iter).expect("checked is_none above"),
|
||||
ident_span: ident.span,
|
||||
extra_count: iter.count(),
|
||||
|
||||
exact_args_note: if max_args.is_some() { None } else { Some(()) },
|
||||
range_args_note: if max_args.is_some() { Some(()) } else { None },
|
||||
min_or_exact_args,
|
||||
max_args: max_args.unwrap_or_default(),
|
||||
name: ident.to_string(),
|
||||
};
|
||||
Err(psess.dcx().create_err(err))
|
||||
}
|
||||
|
||||
/// Returns a span encompassing all tokens in the iterator if there is at least one item.
|
||||
fn iter_span(iter: &TokenStreamIter<'_>) -> Option<Span> {
|
||||
let mut iter = iter.clone(); // cloning is cheap
|
||||
let first_sp = iter.next()?.span();
|
||||
let last_sp = iter.last().map(TokenTree::span).unwrap_or(first_sp);
|
||||
let span = first_sp.with_hi(last_sp.hi());
|
||||
Some(span)
|
||||
}
|
||||
|
||||
/// Indicates what is placed in a `concat` parameter. For example, literals
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ macro_rules! metavar_with_literal_suffix {
|
|||
|
||||
macro_rules! mve_without_parens {
|
||||
( $( $i:ident ),* ) => { ${ count } };
|
||||
//~^ ERROR meta-variable expression parameter must be wrapped in parentheses
|
||||
//~^ ERROR expected `(`
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
|
|
@ -45,9 +45,14 @@ macro_rules! open_brackets_with_lit {
|
|||
//~^ ERROR expected identifier
|
||||
}
|
||||
|
||||
macro_rules! mvs_missing_paren {
|
||||
( $( $i:ident ),* ) => { ${ count $i ($i) } };
|
||||
//~^ ERROR expected `(`
|
||||
}
|
||||
|
||||
macro_rules! mve_wrong_delim {
|
||||
( $( $i:ident ),* ) => { ${ count{i} } };
|
||||
//~^ ERROR meta-variable expression parameter must be wrapped in parentheses
|
||||
//~^ ERROR expected `(`
|
||||
}
|
||||
|
||||
macro_rules! invalid_metavar {
|
||||
|
|
@ -64,28 +69,30 @@ macro_rules! open_brackets_with_group {
|
|||
macro_rules! extra_garbage_after_metavar {
|
||||
( $( $i:ident ),* ) => {
|
||||
${count() a b c}
|
||||
//~^ ERROR unexpected token: a
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
${count($i a b c)}
|
||||
//~^ ERROR unexpected token: a
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
${count($i, 1 a b c)}
|
||||
//~^ ERROR unexpected token: a
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
${count($i) a b c}
|
||||
//~^ ERROR unexpected token: a
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
|
||||
${ignore($i) a b c}
|
||||
//~^ ERROR unexpected token: a
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
${ignore($i a b c)}
|
||||
//~^ ERROR unexpected token: a
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
|
||||
${index() a b c}
|
||||
//~^ ERROR unexpected token: a
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
${index(1 a b c)}
|
||||
//~^ ERROR unexpected token: a
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
|
||||
${index() a b c}
|
||||
//~^ ERROR unexpected token: a
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
${index(1 a b c)}
|
||||
//~^ ERROR unexpected token: a
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
${index(1, a b c)}
|
||||
//~^ ERROR unexpected trailing tokens
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,164 +40,160 @@ error: only unsuffixes integer literals are supported in meta-variable expressio
|
|||
LL | ( $( $i:ident ),* ) => { ${ index(1u32) } };
|
||||
| ^^^^^
|
||||
|
||||
error: meta-variable expression parameter must be wrapped in parentheses
|
||||
error: expected `(`
|
||||
--> $DIR/syntax-errors.rs:32:33
|
||||
|
|
||||
LL | ( $( $i:ident ),* ) => { ${ count } };
|
||||
| ^^^^^
|
||||
| ^^^^^- help: try adding parentheses: `( /* ... */ )`
|
||||
| |
|
||||
| for this this metavariable expression
|
||||
|
|
||||
= note: metavariable expressions use function-like parentheses syntax
|
||||
|
||||
error: meta-variable expression parameter must be wrapped in parentheses
|
||||
error: expected `(`
|
||||
--> $DIR/syntax-errors.rs:49:33
|
||||
|
|
||||
LL | ( $( $i:ident ),* ) => { ${ count $i ($i) } };
|
||||
| ^^^^^ - unexpected token
|
||||
| |
|
||||
| for this this metavariable expression
|
||||
|
|
||||
= note: metavariable expressions use function-like parentheses syntax
|
||||
|
||||
error: expected `(`
|
||||
--> $DIR/syntax-errors.rs:54:33
|
||||
|
|
||||
LL | ( $( $i:ident ),* ) => { ${ count{i} } };
|
||||
| ^^^^^
|
||||
| ^^^^^ for this this metavariable expression
|
||||
|
|
||||
= note: metavariable expressions use function-like parentheses syntax
|
||||
|
||||
error: expected identifier, found `123`
|
||||
--> $DIR/syntax-errors.rs:54:23
|
||||
--> $DIR/syntax-errors.rs:59:23
|
||||
|
|
||||
LL | () => { ${ignore($123)} }
|
||||
| ^^^ help: try removing `123`
|
||||
|
||||
error: unexpected token: a
|
||||
--> $DIR/syntax-errors.rs:66:19
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:71:19
|
||||
|
|
||||
LL | ${count() a b c}
|
||||
| ^
|
||||
|
|
||||
note: meta-variable expression must not have trailing tokens
|
||||
--> $DIR/syntax-errors.rs:66:19
|
||||
|
|
||||
LL | ${count() a b c}
|
||||
| ^
|
||||
| ----- ^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
||||
error: unexpected token: a
|
||||
--> $DIR/syntax-errors.rs:68:20
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:73:20
|
||||
|
|
||||
LL | ${count($i a b c)}
|
||||
| ^
|
||||
| ----- ^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
|
||||
note: meta-variable expression must not have trailing tokens
|
||||
--> $DIR/syntax-errors.rs:68:20
|
||||
|
|
||||
LL | ${count($i a b c)}
|
||||
| ^
|
||||
= note: the `count` metavariable expression takes between 1 and 2 arguments
|
||||
|
||||
error: unexpected token: a
|
||||
--> $DIR/syntax-errors.rs:70:23
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:75:23
|
||||
|
|
||||
LL | ${count($i, 1 a b c)}
|
||||
| ^
|
||||
| ----- ^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
|
||||
note: meta-variable expression must not have trailing tokens
|
||||
--> $DIR/syntax-errors.rs:70:23
|
||||
|
|
||||
LL | ${count($i, 1 a b c)}
|
||||
| ^
|
||||
= note: the `count` metavariable expression takes between 1 and 2 arguments
|
||||
|
||||
error: unexpected token: a
|
||||
--> $DIR/syntax-errors.rs:72:21
|
||||
|
|
||||
LL | ${count($i) a b c}
|
||||
| ^
|
||||
|
|
||||
note: meta-variable expression must not have trailing tokens
|
||||
--> $DIR/syntax-errors.rs:72:21
|
||||
|
|
||||
LL | ${count($i) a b c}
|
||||
| ^
|
||||
|
||||
error: unexpected token: a
|
||||
--> $DIR/syntax-errors.rs:75:22
|
||||
|
|
||||
LL | ${ignore($i) a b c}
|
||||
| ^
|
||||
|
|
||||
note: meta-variable expression must not have trailing tokens
|
||||
--> $DIR/syntax-errors.rs:75:22
|
||||
|
|
||||
LL | ${ignore($i) a b c}
|
||||
| ^
|
||||
|
||||
error: unexpected token: a
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:77:21
|
||||
|
|
||||
LL | ${ignore($i a b c)}
|
||||
| ^
|
||||
LL | ${count($i) a b c}
|
||||
| ----- ^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:80:22
|
||||
|
|
||||
note: meta-variable expression must not have trailing tokens
|
||||
--> $DIR/syntax-errors.rs:77:21
|
||||
LL | ${ignore($i) a b c}
|
||||
| ------ ^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:82:21
|
||||
|
|
||||
LL | ${ignore($i a b c)}
|
||||
| ^
|
||||
| ------ ^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
|
||||
= note: the `ignore` metavariable expression takes a single argument
|
||||
|
||||
error: unexpected token: a
|
||||
--> $DIR/syntax-errors.rs:80:19
|
||||
|
|
||||
LL | ${index() a b c}
|
||||
| ^
|
||||
|
|
||||
note: meta-variable expression must not have trailing tokens
|
||||
--> $DIR/syntax-errors.rs:80:19
|
||||
|
|
||||
LL | ${index() a b c}
|
||||
| ^
|
||||
|
||||
error: unexpected token: a
|
||||
--> $DIR/syntax-errors.rs:82:19
|
||||
|
|
||||
LL | ${index(1 a b c)}
|
||||
| ^
|
||||
|
|
||||
note: meta-variable expression must not have trailing tokens
|
||||
--> $DIR/syntax-errors.rs:82:19
|
||||
|
|
||||
LL | ${index(1 a b c)}
|
||||
| ^
|
||||
|
||||
error: unexpected token: a
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:85:19
|
||||
|
|
||||
LL | ${index() a b c}
|
||||
| ^
|
||||
| ----- ^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:87:19
|
||||
|
|
||||
note: meta-variable expression must not have trailing tokens
|
||||
--> $DIR/syntax-errors.rs:85:19
|
||||
LL | ${index(1 a b c)}
|
||||
| ----- ^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
|
||||
= note: the `index` metavariable expression takes between 0 and 1 arguments
|
||||
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:90:19
|
||||
|
|
||||
LL | ${index() a b c}
|
||||
| ^
|
||||
| ----- ^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
||||
error: unexpected token: a
|
||||
--> $DIR/syntax-errors.rs:87:19
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:92:19
|
||||
|
|
||||
LL | ${index(1 a b c)}
|
||||
| ^
|
||||
| ----- ^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
|
||||
note: meta-variable expression must not have trailing tokens
|
||||
--> $DIR/syntax-errors.rs:87:19
|
||||
= note: the `index` metavariable expression takes between 0 and 1 arguments
|
||||
|
||||
error: unexpected trailing tokens
|
||||
--> $DIR/syntax-errors.rs:94:18
|
||||
|
|
||||
LL | ${index(1 a b c)}
|
||||
| ^
|
||||
LL | ${index(1, a b c)}
|
||||
| ----- ^^^^^^^ help: try removing these tokens
|
||||
| |
|
||||
| for this metavariable expression
|
||||
|
|
||||
= note: the `index` metavariable expression takes between 0 and 1 arguments
|
||||
|
||||
error: meta-variable expression depth must be a literal
|
||||
--> $DIR/syntax-errors.rs:94:33
|
||||
--> $DIR/syntax-errors.rs:101:33
|
||||
|
|
||||
LL | ( $( $i:ident ),* ) => { ${ index(IDX) } };
|
||||
| ^^^^^
|
||||
|
||||
error: meta-variables within meta-variable expressions must be referenced using a dollar sign
|
||||
--> $DIR/syntax-errors.rs:100:11
|
||||
--> $DIR/syntax-errors.rs:107:11
|
||||
|
|
||||
LL | ${count(foo)}
|
||||
| ^^^^^
|
||||
|
||||
error: meta-variables within meta-variable expressions must be referenced using a dollar sign
|
||||
--> $DIR/syntax-errors.rs:107:11
|
||||
--> $DIR/syntax-errors.rs:114:11
|
||||
|
|
||||
LL | ${ignore(bar)}
|
||||
| ^^^^^^
|
||||
|
||||
error: unrecognized meta-variable expression
|
||||
--> $DIR/syntax-errors.rs:113:33
|
||||
--> $DIR/syntax-errors.rs:120:33
|
||||
|
|
||||
LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } };
|
||||
| ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and len
|
||||
|
|
@ -215,10 +211,10 @@ LL | () => { ${ "hi" } };
|
|||
| ^^^^ help: try removing `"hi"`
|
||||
|
||||
error: expected identifier or string literal
|
||||
--> $DIR/syntax-errors.rs:60:33
|
||||
--> $DIR/syntax-errors.rs:65:33
|
||||
|
|
||||
LL | ( $( $i:ident ),* ) => { ${ {} } };
|
||||
| ^^
|
||||
|
||||
error: aborting due to 25 previous errors
|
||||
error: aborting due to 27 previous errors
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue