1
Fork 0

Rollup merge of #124778 - fmease:fix-diag-msg-parse-meta-item, r=nnethercote

Fix parse error message for meta items

Addresses https://github.com/rust-lang/rust/issues/122796#issuecomment-2010803906, cc [``@]Thomasdezeeuw.``

For attrs inside of a macro like `#[doc(alias = $ident)]` or `#[cfg(feature = $ident)]` where `$ident` is a macro metavariable of fragment kind `ident`, we used to say the following when expanded (with `$ident` ⟼ `ident`):

```
error: expected unsuffixed literal or identifier, found `ident`
  --> weird.rs:6:19
   |
6  |      #[cfg(feature = $ident)]
   |                      ^^^^^^
...
11 | m!(id);
   | ------ in this macro invocation
   |
   = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
```

This was incorrect and caused confusion, justifiably so (see #122796).

In this position, we only accept/expect *unsuffixed literals* which consist of numeric & string literals as well as the boolean literals / the keywords / the reserved identifiers `false` & `true` **but not** arbitrary identifiers.

Furthermore, we used to suggest garbage when encountering unexpected non-identifier tokens:

```
error: expected unsuffixed literal, found `-`
  --> weird.rs:16:17
   |
16 | #[cfg(feature = -1)]
   |                 ^
   |
help: surround the identifier with quotation marks to parse it as a string
   |
16 | #[cfg(feature =" "-1)]
   |                + +
```

Now we no longer do.
This commit is contained in:
Matthias Krüger 2024-05-10 16:10:45 +02:00 committed by GitHub
commit f605174ea7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 93 additions and 78 deletions

View file

@ -401,10 +401,8 @@ parse_invalid_logical_operator = `{$incorrect}` is not a logical operator
.use_amp_amp_for_conjunction = use `&&` to perform logical conjunction
.use_pipe_pipe_for_disjunction = use `||` to perform logical disjunction
parse_invalid_meta_item = expected unsuffixed literal or identifier, found `{$token}`
parse_invalid_meta_item_unquoted_ident = expected unsuffixed literal, found `{$token}`
.suggestion = surround the identifier with quotation marks to parse it as a string
parse_invalid_meta_item = expected unsuffixed literal, found `{$token}`
.quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal
parse_invalid_offset_of = offset_of expects dot-separated field and variant names

View file

@ -978,21 +978,13 @@ pub(crate) struct InvalidMetaItem {
#[primary_span]
pub span: Span,
pub token: Token,
}
#[derive(Diagnostic)]
#[diag(parse_invalid_meta_item_unquoted_ident)]
pub(crate) struct InvalidMetaItemUnquotedIdent {
#[primary_span]
pub span: Span,
pub token: Token,
#[subdiagnostic]
pub sugg: InvalidMetaItemSuggQuoteIdent,
pub quote_ident_sugg: Option<InvalidMetaItemQuoteIdentSugg>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
pub(crate) struct InvalidMetaItemSuggQuoteIdent {
#[multipart_suggestion(parse_quote_ident_sugg, applicability = "machine-applicable")]
pub(crate) struct InvalidMetaItemQuoteIdentSugg {
#[suggestion_part(code = "\"")]
pub before: Span,
#[suggestion_part(code = "\"")]

View file

@ -1,7 +1,4 @@
use crate::errors::{
InvalidMetaItem, InvalidMetaItemSuggQuoteIdent, InvalidMetaItemUnquotedIdent,
SuffixedLiteralInAttribute,
};
use crate::errors;
use crate::fluent_generated as fluent;
use crate::maybe_whole;
@ -318,7 +315,7 @@ impl<'a> Parser<'a> {
debug!("checking if {:?} is unsuffixed", lit);
if !lit.kind.is_unsuffixed() {
self.dcx().emit_err(SuffixedLiteralInAttribute { span: lit.span });
self.dcx().emit_err(errors::SuffixedLiteralInAttribute { span: lit.span });
}
Ok(lit)
@ -356,10 +353,11 @@ impl<'a> Parser<'a> {
Ok(nmis)
}
/// Matches the following grammar (per RFC 1559).
/// Parse a meta item per RFC 1559.
///
/// ```ebnf
/// meta_item : PATH ( '=' UNSUFFIXED_LIT | '(' meta_item_inner? ')' )? ;
/// meta_item_inner : (meta_item | UNSUFFIXED_LIT) (',' meta_item_inner)? ;
/// MetaItem = SimplePath ( '=' UNSUFFIXED_LIT | '(' MetaSeq? ')' )? ;
/// MetaSeq = MetaItemInner (',' MetaItemInner)* ','? ;
/// ```
pub fn parse_meta_item(&mut self) -> PResult<'a, ast::MetaItem> {
// We can't use `maybe_whole` here because it would bump in the `None`
@ -387,7 +385,6 @@ impl<'a> Parser<'a> {
Ok(if self.eat(&token::Eq) {
ast::MetaItemKind::NameValue(self.parse_unsuffixed_meta_item_lit()?)
} else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
// Matches `meta_seq = ( COMMASEP(meta_item_inner) )`.
let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
ast::MetaItemKind::List(list)
} else {
@ -395,38 +392,45 @@ impl<'a> Parser<'a> {
})
}
/// Matches `meta_item_inner : (meta_item | UNSUFFIXED_LIT) ;`.
/// Parse an inner meta item per RFC 1559.
///
/// ```ebnf
/// MetaItemInner = UNSUFFIXED_LIT | MetaItem ;
/// ```
fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
match self.parse_unsuffixed_meta_item_lit() {
Ok(lit) => return Ok(ast::NestedMetaItem::Lit(lit)),
Err(err) => err.cancel(),
Err(err) => err.cancel(), // we provide a better error below
}
match self.parse_meta_item() {
Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)),
Err(err) => err.cancel(),
Err(err) => err.cancel(), // we provide a better error below
}
let token = self.token.clone();
let mut err = errors::InvalidMetaItem {
span: self.token.span,
token: self.token.clone(),
quote_ident_sugg: None,
};
// Check for unquoted idents in meta items, e.g.: #[cfg(key = foo)]
// `from_expansion()` ensures we don't suggest for cases such as
// `#[cfg(feature = $expr)]` in macros
if self.prev_token == token::Eq && !self.token.span.from_expansion() {
// Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and
// don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
// when macro metavariables are involved.
if self.prev_token == token::Eq
&& let token::Ident(..) = self.token.kind
{
let before = self.token.span.shrink_to_lo();
while matches!(self.token.kind, token::Ident(..)) {
while let token::Ident(..) = self.token.kind {
self.bump();
}
let after = self.prev_token.span.shrink_to_hi();
let sugg = InvalidMetaItemSuggQuoteIdent { before, after };
return Err(self.dcx().create_err(InvalidMetaItemUnquotedIdent {
span: token.span,
token,
sugg,
}));
err.quote_ident_sugg = Some(errors::InvalidMetaItemQuoteIdentSugg {
before,
after: self.prev_token.span.shrink_to_hi(),
});
}
Err(self.dcx().create_err(InvalidMetaItem { span: token.span, token }))
Err(self.dcx().create_err(err))
}
}