From 2fa016a961681fa7d59e15f516b371fdb211b401 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 9 Nov 2026 12:24:38 +0100 Subject: [PATCH 1/3] Remove parsed arm of `check_attr` --- .../rustc_attr_parsing/src/validate_attr.rs | 37 ++++--------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index 74cc6690d54cf..1b0cb8657015f 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -30,41 +30,20 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) { | AttrKind::Generated(..) => return, } - let builtin_attr_info = attr.name().and_then(|name| BUILTIN_ATTRIBUTE_SET.get(&name)); + // Parsed attributes are validated during attribute parsing + if AttributeParser::is_parsed_attribute(&attr.path()) { + return; + } - // Check input tokens for built-in and key-value attributes. - if let Some(name) = builtin_attr_info { - if AttributeParser::is_parsed_attribute(slice::from_ref(name)) { - return; - } + let attr_item = attr.get_normal_item(); + if let AttrArgs::Eq { .. } = attr_item.args { + // All key-value attributes are restricted to meta-item syntax. match parse_meta(psess, attr) { - // Don't check safety again, we just did that - Ok(meta) => { - // FIXME The only unparsed builtin attributes that are left are the lint attributes, so we can hardcode the template here - let lint_attrs = [sym::forbid, sym::allow, sym::warn, sym::deny, sym::expect]; - assert!(lint_attrs.contains(name)); - - let template = template!( - List: &["lint1", "lint1, lint2, ...", r#"lint1, lint2, lint3, reason = "...""#], - "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes" - ); - check_builtin_meta_item(psess, &meta, attr.style, *name, template, false) - } + Ok(_) => {} Err(err) => { err.emit(); } } - } else { - let attr_item = attr.get_normal_item(); - if let AttrArgs::Eq { .. } = attr_item.args { - // All key-value attributes are restricted to meta-item syntax. - match parse_meta(psess, attr) { - Ok(_) => {} - Err(err) => { - err.emit(); - } - } - } } } From 6fd528c98200347a54cc5a73b13909531ee75440 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 9 Nov 2026 12:58:11 +0100 Subject: [PATCH 2/3] Move `check_attr` to attribute parsing --- .../rustc_ast_passes/src/ast_validation.rs | 5 ---- compiler/rustc_attr_parsing/src/interface.rs | 16 +++++++++- .../rustc_attr_parsing/src/validate_attr.rs | 30 +------------------ compiler/rustc_expand/src/expand.rs | 1 - tests/ui/attributes/issue-90873.stderr | 24 +++++++-------- .../key-value-nested-attr-fail.stderr | 24 +++++++-------- .../ui/attributes/unused-item-in-attr.stderr | 12 ++++---- tests/ui/consts/issue-90878-2.stderr | 12 ++++---- 8 files changed, 52 insertions(+), 72 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a6e88f21ba74f..88816587ae3b4 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -25,7 +25,6 @@ use rustc_abi::{CVariadicStatus, CanonAbi, ExternAbi, InterruptKind}; use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list}; use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; -use rustc_attr_parsing::validate_attr; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{DiagCtxtHandle, Diagnostic, LintBuffer}; use rustc_feature::Features; @@ -1402,10 +1401,6 @@ fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericPara } impl Visitor<'_> for AstValidator<'_> { - fn visit_attribute(&mut self, attr: &Attribute) { - validate_attr::check_attr(&self.sess.psess, attr); - } - fn visit_ty(&mut self, ty: &Ty) { self.visit_ty_common(ty); self.walk_ty(ty) diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index a4cf7b9342dd5..9bd489a5aea8f 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -23,6 +23,7 @@ use crate::diagnostics::ParsedDescription; use crate::generated::GeneratedAttrState; use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser}; use crate::synthetic::SyntheticAttrState; +use crate::validate_attr::parse_meta; use crate::{AttributeTemplate, ShouldEmit, diagnostics}; pub struct EmitAttribute( @@ -359,7 +360,8 @@ impl<'sess> AttributeParser<'sess> { n.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); let inner_span = lower_span(n.item.span); - if let Some(accept) = ATTRIBUTE_PARSERS.accepters.get(parts.as_slice()) { + let accepter = ATTRIBUTE_PARSERS.accepters.get(parts.as_slice()); + if let Some(accept) = accepter { self.check_attribute_safety( &attr_path, inner_span, @@ -492,6 +494,18 @@ impl<'sess> AttributeParser<'sess> { attributes.push(attr); }; + + if accepter.is_none() + && let rustc_ast::AttrArgs::Eq { .. } = n.item.args + { + // All key-value attributes are restricted to meta-item syntax. + match parse_meta(&self.sess.psess, &attr) { + Ok(_) => {} + Err(err) => { + err.emit(); + } + } + } } } } diff --git a/compiler/rustc_attr_parsing/src/validate_attr.rs b/compiler/rustc_attr_parsing/src/validate_attr.rs index 1b0cb8657015f..9dd67bb0dd24c 100644 --- a/compiler/rustc_attr_parsing/src/validate_attr.rs +++ b/compiler/rustc_attr_parsing/src/validate_attr.rs @@ -1,7 +1,6 @@ //! Meta-syntax validation logic of attributes for post-expansion. use std::convert::identity; -use std::slice; use rustc_ast::SyntheticAttr::CfgAttrTrace; use rustc_ast::token::Delimiter; @@ -12,40 +11,13 @@ use rustc_ast::{ }; use rustc_attr_ir::AttrPath; use rustc_errors::{Applicability, Diagnostic, PResult}; -use rustc_feature::BUILTIN_ATTRIBUTE_SET; use rustc_lint_defs::builtin::ILL_FORMED_ATTRIBUTE_INPUT; use rustc_parse::parse_in; use rustc_session::diagnostics::report_lit_error; use rustc_session::parse::ParseSess; use rustc_span::{Span, Symbol, sym}; -use crate::{AttributeParser, AttributeTemplate, diagnostics as errors, template}; - -pub fn check_attr(psess: &ParseSess, attr: &Attribute) { - use ast::SyntheticAttr::*; - match &attr.kind { - AttrKind::Normal(_) => {} - AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) - | AttrKind::DocComment(..) - | AttrKind::Generated(..) => return, - } - - // Parsed attributes are validated during attribute parsing - if AttributeParser::is_parsed_attribute(&attr.path()) { - return; - } - - let attr_item = attr.get_normal_item(); - if let AttrArgs::Eq { .. } = attr_item.args { - // All key-value attributes are restricted to meta-item syntax. - match parse_meta(psess, attr) { - Ok(_) => {} - Err(err) => { - err.emit(); - } - } - } -} +use crate::{AttributeTemplate, diagnostics as errors}; pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, MetaItem> { let item = attr.get_normal_item(); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index b9c1bb9545d2f..af1e4be5e8399 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2235,7 +2235,6 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { let mut attrs = attrs.iter().peekable(); let mut span: Option = None; while let Some(attr) = attrs.next() { - validate_attr::check_attr(&self.cx.sess.psess, attr); AttributeParser::parse_limited_all( self.cx.sess, slice::from_ref(attr), diff --git a/tests/ui/attributes/issue-90873.stderr b/tests/ui/attributes/issue-90873.stderr index 444497538e8dc..afe36156f0e3a 100644 --- a/tests/ui/attributes/issue-90873.stderr +++ b/tests/ui/attributes/issue-90873.stderr @@ -1,15 +1,3 @@ -error: attribute value must be a literal - --> $DIR/issue-90873.rs:1:6 - | -LL | #![u=||{static d=||1;}] - | ^^^^^^^^^^^^^^^^^ - -error: attribute value must be a literal - --> $DIR/issue-90873.rs:6:6 - | -LL | #![a={impl std::ops::Neg for i8 {}}] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: cannot find attribute `u` in this scope --> $DIR/issue-90873.rs:1:4 | @@ -22,6 +10,18 @@ error: cannot find attribute `a` in this scope LL | #![a={impl std::ops::Neg for i8 {}}] | ^ +error: attribute value must be a literal + --> $DIR/issue-90873.rs:1:6 + | +LL | #![u=||{static d=||1;}] + | ^^^^^^^^^^^^^^^^^ + +error: attribute value must be a literal + --> $DIR/issue-90873.rs:6:6 + | +LL | #![a={impl std::ops::Neg for i8 {}}] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0601]: `main` function not found in crate `issue_90873` --> $DIR/issue-90873.rs:6:37 | diff --git a/tests/ui/attributes/key-value-nested-attr-fail.stderr b/tests/ui/attributes/key-value-nested-attr-fail.stderr index d01fd85b70489..468e95a113ef5 100644 --- a/tests/ui/attributes/key-value-nested-attr-fail.stderr +++ b/tests/ui/attributes/key-value-nested-attr-fail.stderr @@ -16,18 +16,6 @@ error: `cfg` is not supported in this position LL | #[clippy::insubstantial = #[cfg(true)] "insignificant"] | ^^^^^^^^^^^^ -error: this attribute is not allowed in this context - --> $DIR/key-value-nested-attr-fail.rs:51:24 - | -LL | #[clippy::immaterial = #[allow(dead_code)] "incorporeal"] - | ^^^^^^^^^^^^^^^^^^^ - -error: this attribute is not allowed in this context - --> $DIR/key-value-nested-attr-fail.rs:59:9 - | -LL | #[arg = #[allow(dead_code)] 0] - | ^^^^^^^^^^^^^^^^^^^ - error: this attribute is not allowed in this context --> $DIR/key-value-nested-attr-fail.rs:17:10 | @@ -61,6 +49,18 @@ error: this attribute is not allowed in this context LL | repr(Rust) | ^^^^^^^^^^ +error: this attribute is not allowed in this context + --> $DIR/key-value-nested-attr-fail.rs:51:24 + | +LL | #[clippy::immaterial = #[allow(dead_code)] "incorporeal"] + | ^^^^^^^^^^^^^^^^^^^ + +error: this attribute is not allowed in this context + --> $DIR/key-value-nested-attr-fail.rs:59:9 + | +LL | #[arg = #[allow(dead_code)] 0] + | ^^^^^^^^^^^^^^^^^^^ + error: attribute value must be a literal --> $DIR/key-value-nested-attr-fail.rs:65:25 | diff --git a/tests/ui/attributes/unused-item-in-attr.stderr b/tests/ui/attributes/unused-item-in-attr.stderr index 84130965d31cf..1d4437647d3cc 100644 --- a/tests/ui/attributes/unused-item-in-attr.stderr +++ b/tests/ui/attributes/unused-item-in-attr.stderr @@ -1,14 +1,14 @@ -error: attribute value must be a literal - --> $DIR/unused-item-in-attr.rs:1:7 - | -LL | #[w = { extern crate alloc; }] - | ^^^^^^^^^^^^^^^^^^^^^^^ - error: cannot find attribute `w` in this scope --> $DIR/unused-item-in-attr.rs:1:3 | LL | #[w = { extern crate alloc; }] | ^ +error: attribute value must be a literal + --> $DIR/unused-item-in-attr.rs:1:7 + | +LL | #[w = { extern crate alloc; }] + | ^^^^^^^^^^^^^^^^^^^^^^^ + error: aborting due to 2 previous errors diff --git a/tests/ui/consts/issue-90878-2.stderr b/tests/ui/consts/issue-90878-2.stderr index 0b332840042ef..f5991ef9bcf7b 100644 --- a/tests/ui/consts/issue-90878-2.stderr +++ b/tests/ui/consts/issue-90878-2.stderr @@ -1,14 +1,14 @@ -error: attribute value must be a literal - --> $DIR/issue-90878-2.rs:1:7 - | -LL | #![l=|x|[b;x ]] - | ^^^^^^^^^ - error: cannot find attribute `l` in this scope --> $DIR/issue-90878-2.rs:1:5 | LL | #![l=|x|[b;x ]] | ^ +error: attribute value must be a literal + --> $DIR/issue-90878-2.rs:1:7 + | +LL | #![l=|x|[b;x ]] + | ^^^^^^^^^ + error: aborting due to 2 previous errors From 0a179cceeaa018512a43b369e91ffce586e62c83 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 9 Nov 2026 12:58:27 +0100 Subject: [PATCH 3/3] Add test checking that the behaviour for diagnostic attrs is preserved --- tests/ui/diagnostic_namespace/invalid_lit.rs | 8 +++++++ .../diagnostic_namespace/invalid_lit.stderr | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 tests/ui/diagnostic_namespace/invalid_lit.rs create mode 100644 tests/ui/diagnostic_namespace/invalid_lit.stderr diff --git a/tests/ui/diagnostic_namespace/invalid_lit.rs b/tests/ui/diagnostic_namespace/invalid_lit.rs new file mode 100644 index 0000000000000..2198258fa498a --- /dev/null +++ b/tests/ui/diagnostic_namespace/invalid_lit.rs @@ -0,0 +1,8 @@ +#[diagnostic::on_unimplemented = 1 + 1] +//~^ ERROR attribute value must be a literal +#[diagnostic::not_exist = 1 + 1] +//~^ ERROR attribute value must be a literal +//~| WARN unknown diagnostic attribute +fn main() { + +} diff --git a/tests/ui/diagnostic_namespace/invalid_lit.stderr b/tests/ui/diagnostic_namespace/invalid_lit.stderr new file mode 100644 index 0000000000000..5659d4f56b02f --- /dev/null +++ b/tests/ui/diagnostic_namespace/invalid_lit.stderr @@ -0,0 +1,22 @@ +error: attribute value must be a literal + --> $DIR/invalid_lit.rs:1:34 + | +LL | #[diagnostic::on_unimplemented = 1 + 1] + | ^^^^^ + +error: attribute value must be a literal + --> $DIR/invalid_lit.rs:3:27 + | +LL | #[diagnostic::not_exist = 1 + 1] + | ^^^^^ + +warning: unknown diagnostic attribute + --> $DIR/invalid_lit.rs:3:15 + | +LL | #[diagnostic::not_exist = 1 + 1] + | ^^^^^^^^^ + | + = note: `#[warn(unknown_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error: aborting due to 2 previous errors; 1 warning emitted +