Rework attribute recovery logic
This commit is contained in:
parent
01e2cf8f44
commit
0b0e826c0f
16 changed files with 116 additions and 64 deletions
|
|
@ -1259,6 +1259,19 @@ pub enum StmtKind {
|
|||
MacCall(Box<MacCallStmt>),
|
||||
}
|
||||
|
||||
impl StmtKind {
|
||||
pub fn descr(&self) -> &'static str {
|
||||
match self {
|
||||
StmtKind::Let(_) => "local",
|
||||
StmtKind::Item(_) => "item",
|
||||
StmtKind::Expr(_) => "expression",
|
||||
StmtKind::Semi(_) => "statement",
|
||||
StmtKind::Empty => "semicolon",
|
||||
StmtKind::MacCall(_) => "macro",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
|
||||
pub struct MacCallStmt {
|
||||
pub mac: Box<MacCall>,
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ attr_parsing_invalid_link_modifier =
|
|||
attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr}
|
||||
.remove_neg_sugg = negative numbers are not literals, try removing the `-` sign
|
||||
.quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal
|
||||
.label = macros are not allowed here
|
||||
.label = {$descr}s are not allowed here
|
||||
|
||||
attr_parsing_invalid_predicate =
|
||||
invalid predicate `{$predicate}`
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use std::fmt::{Debug, Display};
|
|||
|
||||
use rustc_ast::token::{self, Delimiter, MetaVarKind};
|
||||
use rustc_ast::tokenstream::TokenStream;
|
||||
use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
|
||||
use rustc_ast::{AttrArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path, StmtKind, UnOp};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Diag, PResult};
|
||||
use rustc_hir::{self as hir, AttrPath};
|
||||
|
|
@ -488,51 +488,55 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
|
|||
descr: token_descr(&self.parser.token),
|
||||
quote_ident_sugg: None,
|
||||
remove_neg_sugg: None,
|
||||
macro_call: None,
|
||||
label: None,
|
||||
};
|
||||
|
||||
if let token::OpenInvisible(_) = self.parser.token.kind {
|
||||
// Do not attempt to suggest anything when encountered as part of a macro expansion.
|
||||
return self.parser.dcx().create_err(err);
|
||||
}
|
||||
|
||||
// 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.parser.prev_token == token::Eq
|
||||
&& let token::Ident(..) = self.parser.token.kind
|
||||
{
|
||||
if self.parser.look_ahead(1, |t| matches!(t.kind, token::TokenKind::Bang)) {
|
||||
let snapshot = self.parser.create_snapshot_for_diagnostic();
|
||||
let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false);
|
||||
match stmt {
|
||||
Ok(Some(stmt)) => {
|
||||
// The user tried to write something like
|
||||
// `#[deprecated(note = concat!("a", "b"))]`.
|
||||
err.descr = format!("macro {}", err.descr);
|
||||
err.macro_call = Some(stmt.span);
|
||||
err.span = stmt.span;
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(err) => {
|
||||
err.cancel();
|
||||
self.parser.restore_snapshot(snapshot);
|
||||
let snapshot = self.parser.create_snapshot_for_diagnostic();
|
||||
let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false);
|
||||
match stmt {
|
||||
Ok(Some(stmt)) => {
|
||||
// The user tried to write something like
|
||||
// `#[deprecated(note = concat!("a", "b"))]`.
|
||||
err.descr = stmt.kind.descr().to_string();
|
||||
err.label = Some(stmt.span);
|
||||
err.span = stmt.span;
|
||||
if let StmtKind::Expr(expr) = &stmt.kind
|
||||
&& let ExprKind::Unary(UnOp::Neg, val) = &expr.kind
|
||||
&& let ExprKind::Lit(_) = val.kind
|
||||
{
|
||||
err.remove_neg_sugg = Some(InvalidMetaItemRemoveNegSugg {
|
||||
negative_sign: expr.span.until(val.span),
|
||||
});
|
||||
} else if let StmtKind::Expr(expr) = &stmt.kind
|
||||
&& let ExprKind::Path(None, Path { segments, .. }) = &expr.kind
|
||||
&& segments.len() == 1
|
||||
{
|
||||
while let token::Ident(..) | token::Literal(_) | token::Dot =
|
||||
self.parser.token.kind
|
||||
{
|
||||
// We've got a word, so we try to consume the rest of a potential sentence.
|
||||
// We include `.` to correctly handle things like `A sentence here.`.
|
||||
self.parser.bump();
|
||||
}
|
||||
err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
|
||||
before: expr.span.shrink_to_lo(),
|
||||
after: self.parser.prev_token.span.shrink_to_hi(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let before = self.parser.token.span.shrink_to_lo();
|
||||
while let token::Ident(..) = self.parser.token.kind {
|
||||
self.parser.bump();
|
||||
}
|
||||
err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
|
||||
before,
|
||||
after: self.parser.prev_token.span.shrink_to_hi(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if self.parser.token == token::Minus
|
||||
&& self.parser.look_ahead(1, |t| matches!(t.kind, token::TokenKind::Literal { .. }))
|
||||
{
|
||||
err.remove_neg_sugg =
|
||||
Some(InvalidMetaItemRemoveNegSugg { negative_sign: self.parser.token.span });
|
||||
self.parser.bump();
|
||||
self.parser.bump();
|
||||
Ok(None) => {}
|
||||
Err(e) => {
|
||||
e.cancel();
|
||||
self.parser.restore_snapshot(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
self.parser.dcx().create_err(err)
|
||||
|
|
|
|||
|
|
@ -805,7 +805,7 @@ pub(crate) struct InvalidMetaItem {
|
|||
#[subdiagnostic]
|
||||
pub remove_neg_sugg: Option<InvalidMetaItemRemoveNegSugg>,
|
||||
#[label]
|
||||
pub macro_call: Option<Span>,
|
||||
pub label: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ fn f3() {}
|
|||
#[repr(align(16))] //~ ERROR `#[repr(align(...))]` is not supported on functions
|
||||
fn f4() {}
|
||||
|
||||
#[rustc_align(-1)] //~ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
|
||||
#[rustc_align(-1)] //~ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found
|
||||
fn f5() {}
|
||||
|
||||
#[rustc_align(3)] //~ ERROR invalid alignment value: not a power of two
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ error[E0589]: invalid alignment value: not a power of two
|
|||
LL | #[rustc_align(0)]
|
||||
| ^
|
||||
|
||||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
|
||||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
|
||||
--> $DIR/malformed-fn-align.rs:29:15
|
||||
|
|
||||
LL | #[rustc_align(-1)]
|
||||
| ^
|
||||
| ^^ expressions are not allowed here
|
||||
|
|
||||
help: negative numbers are not literals, try removing the `-` sign
|
||||
|
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@ fn main() {
|
|||
}
|
||||
|
||||
#[deprecated(note = test)]
|
||||
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `test`
|
||||
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found
|
||||
fn foo() {}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `test`
|
||||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
|
||||
--> $DIR/issue-66340-deprecated-attr-non-meta-grammar.rs:9:21
|
||||
|
|
||||
LL | #[deprecated(note = test)]
|
||||
| ^^^^
|
||||
| ^^^^ expressions are not allowed here
|
||||
|
|
||||
help: surround the identifier with quotation marks to make it into a string literal
|
||||
|
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ macro_rules! mac {
|
|||
mac!(an(arbitrary token stream));
|
||||
|
||||
#[cfg(feature = -1)]
|
||||
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
|
||||
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found
|
||||
fn handler() {}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
|
||||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
|
||||
--> $DIR/attr-bad-meta-4.rs:11:17
|
||||
|
|
||||
LL | #[cfg(feature = -1)]
|
||||
| ^
|
||||
| ^^ expressions are not allowed here
|
||||
|
|
||||
help: negative numbers are not literals, try removing the `-` sign
|
||||
|
|
||||
|
|
|
|||
|
|
@ -4,14 +4,21 @@
|
|||
|
||||
fn main() {
|
||||
#[cfg(key=foo)]
|
||||
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `foo`
|
||||
//~| HELP surround the identifier with quotation marks to make it into a string literal
|
||||
//~^ ERROR: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found
|
||||
//~| HELP: surround the identifier with quotation marks to make it into a string literal
|
||||
//~| NOTE: expressions are not allowed here
|
||||
println!();
|
||||
#[cfg(key="bar")]
|
||||
println!();
|
||||
#[cfg(key=foo bar baz)]
|
||||
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `foo`
|
||||
//~| HELP surround the identifier with quotation marks to make it into a string literal
|
||||
//~^ ERROR: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found
|
||||
//~| HELP: surround the identifier with quotation marks to make it into a string literal
|
||||
//~| NOTE: expressions are not allowed here
|
||||
println!();
|
||||
#[cfg(key=foo 1 bar 2.0 baz.)]
|
||||
//~^ ERROR: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found
|
||||
//~| HELP: surround the identifier with quotation marks to make it into a string literal
|
||||
//~| NOTE: expressions are not allowed here
|
||||
println!();
|
||||
}
|
||||
|
||||
|
|
@ -19,7 +26,7 @@ fn main() {
|
|||
|
||||
macro_rules! make {
|
||||
($name:ident) => { #[doc(alias = $name)] pub struct S; }
|
||||
//~^ ERROR expected unsuffixed literal, found identifier `nickname`
|
||||
//~^ ERROR: expected unsuffixed literal, found identifier `nickname`
|
||||
}
|
||||
|
||||
make!(nickname); //~ NOTE in this expansion
|
||||
make!(nickname); //~ NOTE: in this expansion
|
||||
|
|
|
|||
|
|
@ -1,27 +1,38 @@
|
|||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `foo`
|
||||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
|
||||
--> $DIR/attr-unquoted-ident.rs:6:15
|
||||
|
|
||||
LL | #[cfg(key=foo)]
|
||||
| ^^^
|
||||
| ^^^ expressions are not allowed here
|
||||
|
|
||||
help: surround the identifier with quotation marks to make it into a string literal
|
||||
|
|
||||
LL | #[cfg(key="foo")]
|
||||
| + +
|
||||
|
||||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `foo`
|
||||
--> $DIR/attr-unquoted-ident.rs:12:15
|
||||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
|
||||
--> $DIR/attr-unquoted-ident.rs:13:15
|
||||
|
|
||||
LL | #[cfg(key=foo bar baz)]
|
||||
| ^^^
|
||||
| ^^^ expressions are not allowed here
|
||||
|
|
||||
help: surround the identifier with quotation marks to make it into a string literal
|
||||
|
|
||||
LL | #[cfg(key="foo bar baz")]
|
||||
| + +
|
||||
|
||||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
|
||||
--> $DIR/attr-unquoted-ident.rs:18:15
|
||||
|
|
||||
LL | #[cfg(key=foo 1 bar 2.0 baz.)]
|
||||
| ^^^ expressions are not allowed here
|
||||
|
|
||||
help: surround the identifier with quotation marks to make it into a string literal
|
||||
|
|
||||
LL | #[cfg(key="foo 1 bar 2.0 baz.")]
|
||||
| + +
|
||||
|
||||
error: expected unsuffixed literal, found identifier `nickname`
|
||||
--> $DIR/attr-unquoted-ident.rs:21:38
|
||||
--> $DIR/attr-unquoted-ident.rs:28:38
|
||||
|
|
||||
LL | ($name:ident) => { #[doc(alias = $name)] pub struct S; }
|
||||
| ^^^^^
|
||||
|
|
@ -31,5 +42,5 @@ LL | make!(nickname);
|
|||
|
|
||||
= note: this error originates in the macro `make` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
|
|
|
|||
9
tests/ui/parser/macro/expr-in-attribute.rs
Normal file
9
tests/ui/parser/macro/expr-in-attribute.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// Test for #146325.
|
||||
// Ensure that when we encounter an expr invocation in an attribute, we don't suggest nonsense.
|
||||
|
||||
#[deprecated(note = a!=b)]
|
||||
struct X;
|
||||
//~^^ ERROR: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
|
||||
//~| NOTE: expressions are not allowed here
|
||||
|
||||
fn main() {}
|
||||
8
tests/ui/parser/macro/expr-in-attribute.stderr
Normal file
8
tests/ui/parser/macro/expr-in-attribute.stderr
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
|
||||
--> $DIR/expr-in-attribute.rs:4:21
|
||||
|
|
||||
LL | #[deprecated(note = a!=b)]
|
||||
| ^^^^ expressions are not allowed here
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#[deprecated(note = concat!("a", "b"))]
|
||||
struct X;
|
||||
//~^^ ERROR: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found macro `concat`
|
||||
//~^^ ERROR: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found
|
||||
//~| NOTE: macros are not allowed here
|
||||
|
||||
fn main() {}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found macro `concat`
|
||||
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found macro
|
||||
--> $DIR/macro-in-attribute.rs:4:21
|
||||
|
|
||||
LL | #[deprecated(note = concat!("a", "b"))]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue