Skip to content

Commit 88ea12d

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 more context and a better description. 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 b276ff8 commit 88ea12d

File tree

5 files changed

+87
-18
lines changed

5 files changed

+87
-18
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_extra_tokens =
137145
unexpected trailing tokens
138146
.label = for this metavariable expression

compiler/rustc_expand/src/errors.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,30 @@ mod metavar_exprs {
540540
pub valid_expr_list: &'static str,
541541
}
542542

543+
#[derive(Diagnostic)]
544+
#[diag(expand_mve_expected_ident)]
545+
pub(crate) struct MveExpectedIdent {
546+
#[primary_span]
547+
pub span: Span,
548+
#[label(expand_not_ident)]
549+
pub not_ident_label: Option<Span>,
550+
/// This error is reused a handful of places, the context here tells us how to customize
551+
/// the message.
552+
#[subdiagnostic]
553+
pub context: MveExpectedIdentContext,
554+
}
555+
556+
#[derive(Subdiagnostic)]
557+
pub(crate) enum MveExpectedIdentContext {
558+
#[note(expand_expr_name)]
559+
#[note(expand_expr_name_note)]
560+
ExprName { valid_expr_list: &'static str },
561+
#[note(expand_ignore_expr_note)]
562+
Ignore,
563+
#[note(expand_count_expr_note)]
564+
Count,
565+
}
566+
543567
#[derive(Diagnostic)]
544568
#[diag(expand_mve_unrecognized_var)]
545569
pub(crate) struct MveUnrecognizedVar {

compiler/rustc_expand/src/mbe/metavar_expr.rs

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

10-
use crate::errors;
10+
use crate::errors::{self, MveExpectedIdentContext};
1111

1212
pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers";
1313
pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal";
@@ -64,7 +64,13 @@ impl MetaVarExpr {
6464
psess: &'psess ParseSess,
6565
) -> PResult<'psess, MetaVarExpr> {
6666
let mut iter = input.iter();
67-
let ident = parse_ident(&mut iter, psess, outer_span)?;
67+
let ident = parse_ident(
68+
&mut iter,
69+
psess,
70+
outer_span,
71+
MveExpectedIdentContext::ExprName { valid_expr_list: VALID_METAVAR_EXPR_NAMES },
72+
)?;
73+
6874
let next = iter.next();
6975
let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = next else {
7076
// No `()`; wrong or no delimiters. Point at a problematic span or a place to
@@ -97,7 +103,9 @@ impl MetaVarExpr {
97103
"count" => parse_count(&mut iter, psess, ident.span)?,
98104
"ignore" => {
99105
eat_dollar(&mut iter, psess, ident.span)?;
100-
MetaVarExpr::Ignore(parse_ident(&mut iter, psess, ident.span)?)
106+
let ident =
107+
parse_ident(&mut iter, psess, outer_span, MveExpectedIdentContext::Ignore)?;
108+
MetaVarExpr::Ignore(ident)
101109
}
102110
"index" => MetaVarExpr::Index(parse_depth(&mut iter, psess, ident.span)?),
103111
"len" => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?),
@@ -241,7 +249,7 @@ fn parse_count<'psess>(
241249
span: Span,
242250
) -> PResult<'psess, MetaVarExpr> {
243251
eat_dollar(iter, psess, span)?;
244-
let ident = parse_ident(iter, psess, span)?;
252+
let ident = parse_ident(iter, psess, span, MveExpectedIdentContext::Count)?;
245253
let depth = if try_eat_comma(iter) {
246254
if iter.peek().is_none() {
247255
return Err(psess.dcx().struct_span_err(
@@ -279,14 +287,32 @@ fn parse_depth<'psess>(
279287
}
280288
}
281289

282-
/// Parses an generic ident
290+
/// Tries to parse a generic ident. If this fails, create a missing identifier diagnostic with
291+
/// `context` explanation.
283292
fn parse_ident<'psess>(
284293
iter: &mut TokenStreamIter<'_>,
285294
psess: &'psess ParseSess,
286295
fallback_span: Span,
296+
context: MveExpectedIdentContext,
287297
) -> PResult<'psess, Ident> {
288-
let token = parse_token(iter, psess, fallback_span)?;
289-
parse_ident_from_token(psess, token)
298+
let Some(tt) = iter.next() else {
299+
let err = errors::MveExpectedIdent { span: fallback_span, not_ident_label: None, context };
300+
return Err(psess.dcx().create_err(err));
301+
};
302+
303+
let TokenTree::Token(token, _) = tt else {
304+
let span = tt.span();
305+
let err = errors::MveExpectedIdent { span, not_ident_label: Some(span), context };
306+
return Err(psess.dcx().create_err(err));
307+
};
308+
309+
let Some((elem, _)) = token.ident() else {
310+
let span = token.span;
311+
let err = errors::MveExpectedIdent { span, not_ident_label: Some(span), context };
312+
return Err(psess.dcx().create_err(err));
313+
};
314+
315+
Ok(elem)
290316
}
291317

292318
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! mvs_missing_paren {
@@ -57,13 +57,13 @@ macro_rules! mve_wrong_delim {
5757

5858
macro_rules! invalid_metavar {
5959
() => { ${ignore($123)} }
60-
//~^ ERROR expected identifier, found `123`
60+
//~^ ERROR expected an identifier
6161
}
6262

6363
#[rustfmt::skip]
6464
macro_rules! open_brackets_with_group {
6565
( $( $i:ident ),* ) => { ${ {} } };
66-
//~^ ERROR expected identifier
66+
//~^ ERROR expected an identifier
6767
}
6868

6969
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
@@ -68,11 +68,13 @@ LL | ( $( $i:ident ),* ) => { ${ count{i} } };
6868
|
6969
= note: metavariable expressions use function-like parentheses syntax
7070

71-
error: expected identifier, found `123`
71+
error: expected an identifier
7272
--> $DIR/syntax-errors.rs:59:23
7373
|
7474
LL | () => { ${ignore($123)} }
75-
| ^^^ help: try removing `123`
75+
| ^^^ not a valid identifier
76+
|
77+
= note: `ignore` takes a metavariable argument
7678

7779
error: unexpected trailing tokens
7880
--> $DIR/syntax-errors.rs:71:19
@@ -200,23 +202,32 @@ LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } };
200202
|
201203
= note: valid metavariable expressions are `count`, `ignore`, `index`, `len`, and `concat`
202204

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

209-
error: expected identifier, found `"hi"`
214+
error: expected an identifier
210215
--> $DIR/syntax-errors.rs:44:17
211216
|
212217
LL | () => { ${ "hi" } };
213-
| ^^^^ help: try removing `"hi"`
218+
| ^^^^ not a valid identifier
219+
|
220+
= note: expected a metavariable expression name: `${expr( /* ... */ )}`
221+
= note: valid metavariable expressions are `count`, `ignore`, `index`, `len`, and `concat`
214222

215-
error: expected identifier or string literal
223+
error: expected an identifier
216224
--> $DIR/syntax-errors.rs:65:33
217225
|
218226
LL | ( $( $i:ident ),* ) => { ${ {} } };
219-
| ^^
227+
| ^^ not a valid identifier
228+
|
229+
= note: expected a metavariable expression name: `${expr( /* ... */ )}`
230+
= note: valid metavariable expressions are `count`, `ignore`, `index`, `len`, and `concat`
220231

221232
error: aborting due to 27 previous errors
222233

0 commit comments

Comments
 (0)