Skip to content

Commit e811ab9

Browse files
committed
mbe: Refactor diagnostics for missing identifiers
The current messages have the potential to be more accurate; "expected identifier or string literal" is printed in a few cases where only an identifier should be expected, and it suggests removing string literals when that might not solve the problem. Add a new diagnostic for these kind of errors that gives some more context. For `count` and `ignore` this should likely be combined with the diagnositcs for `eat_dollar` to produce a helpful error if they get anything other than a metavariable first argument. I am planning to do this in a followup.
1 parent 837c5dd commit e811ab9

File tree

5 files changed

+89
-17
lines changed

5 files changed

+89
-17
lines changed

compiler/rustc_expand/messages.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,14 @@ expand_module_multiple_candidates =
133133
expand_must_repeat_once =
134134
this must repeat at least once
135135

136+
expand_mve_expected_ident =
137+
expected an identifier
138+
.not_ident = not a valid identifier
139+
.expr_name = expected a metavariable expression name: `{"${expr( /* ... */ )}"}`
140+
.expr_name_note = valid metavariable expressions are {$valid_expr_list}
141+
.ignore_expr_note = `ignore` takes a metavariable argument
142+
.count_expr_note = `count` takes a metavariable argument
143+
136144
expand_mve_unrecognized_var =
137145
variable `{$key}` is not recognized in meta-variable expression
138146

compiler/rustc_expand/src/errors.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,30 @@ pub(crate) use metavar_exprs::*;
496496
mod metavar_exprs {
497497
use super::*;
498498

499+
#[derive(Diagnostic)]
500+
#[diag(expand_mve_expected_ident)]
501+
pub(crate) struct MveExpectedIdent {
502+
#[primary_span]
503+
pub span: Span,
504+
#[label(expand_not_ident)]
505+
pub not_ident_label: Option<Span>,
506+
/// This error is reused a handful of places, the context here tells us how to customize
507+
/// the message.
508+
#[subdiagnostic]
509+
pub context: MveExpectedIdentContext,
510+
}
511+
512+
#[derive(Subdiagnostic)]
513+
pub(crate) enum MveExpectedIdentContext {
514+
#[note(expand_expr_name)]
515+
#[note(expand_expr_name_note)]
516+
ExprName { valid_expr_list: &'static str },
517+
#[note(expand_ignore_expr_note)]
518+
Ignore,
519+
#[note(expand_count_expr_note)]
520+
Count,
521+
}
522+
499523
#[derive(Diagnostic)]
500524
#[diag(expand_mve_unrecognized_var)]
501525
pub(crate) struct MveUnrecognizedVar {

compiler/rustc_expand/src/mbe/metavar_expr.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ use rustc_macros::{Decodable, Encodable};
77
use rustc_session::parse::ParseSess;
88
use rustc_span::{Ident, Span, Symbol};
99

10+
use crate::errors::{self, MveExpectedIdentContext};
11+
1012
pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers";
1113
pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal";
1214

15+
const VALID_METAVAR_EXPR_NAMES: &str = "`count`, `ignore`, `index`, `len`, and `concat`";
16+
1317
/// A meta-variable expression, for expansions based on properties of meta-variables.
1418
#[derive(Debug, PartialEq, Encodable, Decodable)]
1519
pub(crate) enum MetaVarExpr {
@@ -39,7 +43,12 @@ impl MetaVarExpr {
3943
psess: &'psess ParseSess,
4044
) -> PResult<'psess, MetaVarExpr> {
4145
let mut iter = input.iter();
42-
let ident = parse_ident(&mut iter, psess, outer_span)?;
46+
let ident = parse_ident(
47+
&mut iter,
48+
psess,
49+
outer_span,
50+
MveExpectedIdentContext::ExprName { valid_expr_list: VALID_METAVAR_EXPR_NAMES },
51+
)?;
4352
let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = iter.next() else {
4453
let msg = "meta-variable expression parameter must be wrapped in parentheses";
4554
return Err(psess.dcx().struct_span_err(ident.span, msg));
@@ -51,7 +60,9 @@ impl MetaVarExpr {
5160
"count" => parse_count(&mut iter, psess, ident.span)?,
5261
"ignore" => {
5362
eat_dollar(&mut iter, psess, ident.span)?;
54-
MetaVarExpr::Ignore(parse_ident(&mut iter, psess, ident.span)?)
63+
let ident =
64+
parse_ident(&mut iter, psess, outer_span, MveExpectedIdentContext::Ignore)?;
65+
MetaVarExpr::Ignore(ident)
5566
}
5667
"index" => MetaVarExpr::Index(parse_depth(&mut iter, psess, ident.span)?),
5768
"len" => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?),
@@ -168,7 +179,7 @@ fn parse_count<'psess>(
168179
span: Span,
169180
) -> PResult<'psess, MetaVarExpr> {
170181
eat_dollar(iter, psess, span)?;
171-
let ident = parse_ident(iter, psess, span)?;
182+
let ident = parse_ident(iter, psess, span, MveExpectedIdentContext::Count)?;
172183
let depth = if try_eat_comma(iter) {
173184
if iter.peek().is_none() {
174185
return Err(psess.dcx().struct_span_err(
@@ -206,14 +217,32 @@ fn parse_depth<'psess>(
206217
}
207218
}
208219

209-
/// Parses an generic ident
220+
/// Tries to parse a generic ident. If this fails, create a missing identifier diagnostic with
221+
/// `context` explanation.
210222
fn parse_ident<'psess>(
211223
iter: &mut TokenStreamIter<'_>,
212224
psess: &'psess ParseSess,
213225
fallback_span: Span,
226+
context: MveExpectedIdentContext,
214227
) -> PResult<'psess, Ident> {
215-
let token = parse_token(iter, psess, fallback_span)?;
216-
parse_ident_from_token(psess, token)
228+
let Some(tt) = iter.next() else {
229+
let err = errors::MveExpectedIdent { span: fallback_span, not_ident_label: None, context };
230+
return Err(psess.dcx().create_err(err));
231+
};
232+
233+
let TokenTree::Token(token, _) = tt else {
234+
let span = tt.span();
235+
let err = errors::MveExpectedIdent { span, not_ident_label: Some(span), context };
236+
return Err(psess.dcx().create_err(err));
237+
};
238+
239+
let Some((elem, _)) = token.ident() else {
240+
let span = token.span;
241+
let err = errors::MveExpectedIdent { span, not_ident_label: Some(span), context };
242+
return Err(psess.dcx().create_err(err));
243+
};
244+
245+
Ok(elem)
217246
}
218247

219248
fn parse_ident_from_token<'psess>(

tests/ui/macros/metavar-expressions/syntax-errors.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ macro_rules! mve_without_parens {
3636
#[rustfmt::skip]
3737
macro_rules! empty_expression {
3838
() => { ${} };
39-
//~^ ERROR expected identifier or string literal
39+
//~^ ERROR expected an identifier
4040
}
4141

4242
#[rustfmt::skip]
4343
macro_rules! open_brackets_with_lit {
4444
() => { ${ "hi" } };
45-
//~^ ERROR expected identifier
45+
//~^ ERROR expected an identifier
4646
}
4747

4848
macro_rules! mve_wrong_delim {
@@ -52,13 +52,13 @@ macro_rules! mve_wrong_delim {
5252

5353
macro_rules! invalid_metavar {
5454
() => { ${ignore($123)} }
55-
//~^ ERROR expected identifier, found `123`
55+
//~^ ERROR expected an identifier
5656
}
5757

5858
#[rustfmt::skip]
5959
macro_rules! open_brackets_with_group {
6060
( $( $i:ident ),* ) => { ${ {} } };
61-
//~^ ERROR expected identifier
61+
//~^ ERROR expected an identifier
6262
}
6363

6464
macro_rules! extra_garbage_after_metavar {

tests/ui/macros/metavar-expressions/syntax-errors.stderr

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,13 @@ error: meta-variable expression parameter must be wrapped in parentheses
5252
LL | ( $( $i:ident ),* ) => { ${ count{i} } };
5353
| ^^^^^
5454

55-
error: expected identifier, found `123`
55+
error: expected an identifier
5656
--> $DIR/syntax-errors.rs:54:23
5757
|
5858
LL | () => { ${ignore($123)} }
59-
| ^^^ help: try removing `123`
59+
| ^^^ not a valid identifier
60+
|
61+
= note: `ignore` takes a metavariable argument
6062

6163
error: unexpected token: a
6264
--> $DIR/syntax-errors.rs:66:19
@@ -202,23 +204,32 @@ error: unrecognized meta-variable expression
202204
LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } };
203205
| ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and len
204206

205-
error: expected identifier or string literal
207+
error: expected an identifier
206208
--> $DIR/syntax-errors.rs:38:14
207209
|
208210
LL | () => { ${} };
209211
| ^^
212+
|
213+
= note: expected a metavariable expression name: `${expr( /* ... */ )}`
214+
= note: valid metavariable expressions are `count`, `ignore`, `index`, `len`, and `concat`
210215

211-
error: expected identifier, found `"hi"`
216+
error: expected an identifier
212217
--> $DIR/syntax-errors.rs:44:17
213218
|
214219
LL | () => { ${ "hi" } };
215-
| ^^^^ help: try removing `"hi"`
220+
| ^^^^ not a valid identifier
221+
|
222+
= note: expected a metavariable expression name: `${expr( /* ... */ )}`
223+
= note: valid metavariable expressions are `count`, `ignore`, `index`, `len`, and `concat`
216224

217-
error: expected identifier or string literal
225+
error: expected an identifier
218226
--> $DIR/syntax-errors.rs:60:33
219227
|
220228
LL | ( $( $i:ident ),* ) => { ${ {} } };
221-
| ^^
229+
| ^^ not a valid identifier
230+
|
231+
= note: expected a metavariable expression name: `${expr( /* ... */ )}`
232+
= note: valid metavariable expressions are `count`, `ignore`, `index`, `len`, and `concat`
222233

223234
error: aborting due to 25 previous errors
224235

0 commit comments

Comments
 (0)