Skip to content

Commit 811d2e7

Browse files
committed
Rework attribute recovery logic
1 parent 8c9919d commit 811d2e7

16 files changed

+118
-64
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,19 @@ pub enum StmtKind {
12351235
MacCall(Box<MacCallStmt>),
12361236
}
12371237

1238+
impl StmtKind {
1239+
pub fn descr(&self) -> &'static str {
1240+
match self {
1241+
StmtKind::Let(_) => "local",
1242+
StmtKind::Item(_) => "item",
1243+
StmtKind::Expr(_) => "expression",
1244+
StmtKind::Semi(_) => "statement",
1245+
StmtKind::Empty => "semicolon",
1246+
StmtKind::MacCall(_) => "macro",
1247+
}
1248+
}
1249+
}
1250+
12381251
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
12391252
pub struct MacCallStmt {
12401253
pub mac: Box<MacCall>,

compiler/rustc_attr_parsing/messages.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
200200
attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr}
201201
.remove_neg_sugg = negative numbers are not literals, try removing the `-` sign
202202
.quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal
203-
.label = macros are not allowed here
203+
.label = {$descr}s are not allowed here
204204
205205
attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
206206
.help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)

compiler/rustc_attr_parsing/src/parser.rs

Lines changed: 44 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use std::fmt::{Debug, Display};
88

99
use rustc_ast::token::{self, Delimiter, MetaVarKind};
1010
use rustc_ast::tokenstream::TokenStream;
11-
use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path};
11+
use rustc_ast::{
12+
AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path, StmtKind, UnOp,
13+
};
1214
use rustc_ast_pretty::pprust;
1315
use rustc_errors::{Diag, PResult};
1416
use rustc_hir::{self as hir, AttrPath};
@@ -476,51 +478,55 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
476478
descr: token_descr(&self.parser.token),
477479
quote_ident_sugg: None,
478480
remove_neg_sugg: None,
479-
macro_call: None,
481+
label: None,
480482
};
481483

484+
if let token::OpenInvisible(_) = self.parser.token.kind {
485+
// Do not attempt to suggest anything when encountered as part of a macro expansion.
486+
return self.parser.dcx().create_err(err);
487+
}
488+
482489
// Suggest quoting idents, e.g. in `#[cfg(key = value)]`. We don't use `Token::ident` and
483490
// don't `uninterpolate` the token to avoid suggesting anything butchered or questionable
484491
// when macro metavariables are involved.
485-
if self.parser.prev_token == token::Eq
486-
&& let token::Ident(..) = self.parser.token.kind
487-
{
488-
if self.parser.look_ahead(1, |t| matches!(t.kind, token::TokenKind::Bang)) {
489-
let snapshot = self.parser.create_snapshot_for_diagnostic();
490-
let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false);
491-
match stmt {
492-
Ok(Some(stmt)) => {
493-
// The user tried to write something like
494-
// `#[deprecated(note = concat!("a", "b"))]`.
495-
err.descr = format!("macro {}", err.descr);
496-
err.macro_call = Some(stmt.span);
497-
err.span = stmt.span;
498-
}
499-
Ok(None) => {}
500-
Err(err) => {
501-
err.cancel();
502-
self.parser.restore_snapshot(snapshot);
492+
let snapshot = self.parser.create_snapshot_for_diagnostic();
493+
let stmt = self.parser.parse_stmt_without_recovery(false, ForceCollect::No, false);
494+
match stmt {
495+
Ok(Some(stmt)) => {
496+
// The user tried to write something like
497+
// `#[deprecated(note = concat!("a", "b"))]`.
498+
err.descr = stmt.kind.descr().to_string();
499+
err.label = Some(stmt.span);
500+
err.span = stmt.span;
501+
if let StmtKind::Expr(expr) = &stmt.kind
502+
&& let ExprKind::Unary(UnOp::Neg, val) = &expr.kind
503+
&& let ExprKind::Lit(_) = val.kind
504+
{
505+
err.remove_neg_sugg = Some(InvalidMetaItemRemoveNegSugg {
506+
negative_sign: expr.span.until(val.span),
507+
});
508+
} else if let StmtKind::Expr(expr) = &stmt.kind
509+
&& let ExprKind::Path(None, Path { segments, .. }) = &expr.kind
510+
&& segments.len() == 1
511+
{
512+
while let token::Ident(..) | token::Literal(_) | token::Dot =
513+
self.parser.token.kind
514+
{
515+
// We've got a word, so we try to consume the rest of a potential sentence.
516+
// We include `.` to correctly handle things like `A sentence here.`.
517+
self.parser.bump();
503518
}
519+
err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
520+
before: expr.span.shrink_to_lo(),
521+
after: self.parser.prev_token.span.shrink_to_hi(),
522+
});
504523
}
505-
} else {
506-
let before = self.parser.token.span.shrink_to_lo();
507-
while let token::Ident(..) = self.parser.token.kind {
508-
self.parser.bump();
509-
}
510-
err.quote_ident_sugg = Some(InvalidMetaItemQuoteIdentSugg {
511-
before,
512-
after: self.parser.prev_token.span.shrink_to_hi(),
513-
});
514524
}
515-
}
516-
517-
if self.parser.token == token::Minus
518-
&& self.parser.look_ahead(1, |t| matches!(t.kind, token::TokenKind::Literal { .. }))
519-
{
520-
err.remove_neg_sugg =
521-
Some(InvalidMetaItemRemoveNegSugg { negative_sign: self.parser.token.span });
522-
self.parser.bump();
523-
self.parser.bump();
525+
Ok(None) => {}
526+
Err(e) => {
527+
e.cancel();
528+
self.parser.restore_snapshot(snapshot);
529+
}
524530
}
525531

526532
self.parser.dcx().create_err(err)

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,7 @@ pub(crate) struct InvalidMetaItem {
830830
#[subdiagnostic]
831831
pub remove_neg_sugg: Option<InvalidMetaItemRemoveNegSugg>,
832832
#[label]
833-
pub macro_call: Option<Span>,
833+
pub label: Option<Span>,
834834
}
835835

836836
#[derive(Subdiagnostic)]

tests/ui/attributes/malformed-fn-align.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn f3() {}
2626
#[repr(align(16))] //~ ERROR `#[repr(align(...))]` is not supported on functions
2727
fn f4() {}
2828

29-
#[rustc_align(-1)] //~ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
29+
#[rustc_align(-1)] //~ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found
3030
fn f5() {}
3131

3232
#[rustc_align(3)] //~ ERROR invalid alignment value: not a power of two

tests/ui/attributes/malformed-fn-align.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ error[E0589]: invalid alignment value: not a power of two
3737
LL | #[rustc_align(0)]
3838
| ^
3939

40-
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
40+
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
4141
--> $DIR/malformed-fn-align.rs:29:15
4242
|
4343
LL | #[rustc_align(-1)]
44-
| ^
44+
| ^^ expressions are not allowed here
4545
|
4646
help: negative numbers are not literals, try removing the `-` sign
4747
|

tests/ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ fn main() {
77
}
88

99
#[deprecated(note = test)]
10-
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `test`
10+
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found
1111
fn foo() {}

tests/ui/deprecation/issue-66340-deprecated-attr-non-meta-grammar.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `test`
1+
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
22
--> $DIR/issue-66340-deprecated-attr-non-meta-grammar.rs:9:21
33
|
44
LL | #[deprecated(note = test)]
5-
| ^^^^
5+
| ^^^^ expressions are not allowed here
66
|
77
help: surround the identifier with quotation marks to make it into a string literal
88
|

tests/ui/parser/attribute/attr-bad-meta-4.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ macro_rules! mac {
99
mac!(an(arbitrary token stream));
1010

1111
#[cfg(feature = -1)]
12-
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
12+
//~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found
1313
fn handler() {}
1414

1515
fn main() {}

tests/ui/parser/attribute/attr-bad-meta-4.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `-`
1+
error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression
22
--> $DIR/attr-bad-meta-4.rs:11:17
33
|
44
LL | #[cfg(feature = -1)]
5-
| ^
5+
| ^^ expressions are not allowed here
66
|
77
help: negative numbers are not literals, try removing the `-` sign
88
|

0 commit comments

Comments
 (0)