From 3f31c43a0d8312e86d42d0952818c0054a730b61 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 16:29:53 +0100 Subject: [PATCH 01/12] Move `is_word` --- compiler/rustc_ast/src/attr/mod.rs | 19 +++++++------------ compiler/rustc_attr_ir/src/attr.rs | 10 ---------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 8ab79eb4068ea..6dec3c866de78 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -151,15 +151,6 @@ impl AttributeExt for Attribute { } } - fn is_word(&self) -> bool { - match &self.kind { - AttrKind::Normal(normal) => matches!(normal.item.args, AttrArgs::Empty), - AttrKind::Synthetic(..) => unreachable!(), - AttrKind::DocComment(..) => false, - AttrKind::Generated(generated) => generated.is_word(), - } - } - /// Returns a list of meta items if the attribute is delimited with parenthesis: /// /// ```text @@ -933,8 +924,6 @@ pub trait AttributeExt: Debug + 'static { fn has_any_name(&self, names: &[Symbol]) -> bool { names.iter().any(|&name| self.has_name(name)) } - /// Returns whether the attribute is a path, without any arguments. - fn is_word(&self) -> bool; fn path(&self) -> SmallVec<[Symbol; 1]> { self.symbol_path().unwrap_or(smallvec![sym::doc]) @@ -1010,8 +999,14 @@ impl Attribute { AttributeExt::has_any_name(self, names) } + /// Returns whether the attribute is a path, without any arguments. pub fn is_word(&self) -> bool { - AttributeExt::is_word(self) + match &self.kind { + AttrKind::Normal(normal) => matches!(normal.item.args, AttrArgs::Empty), + AttrKind::Synthetic(..) => unreachable!(), + AttrKind::DocComment(..) => false, + AttrKind::Generated(generated) => generated.is_word(), + } } pub fn path(&self) -> SmallVec<[Symbol; 1]> { diff --git a/compiler/rustc_attr_ir/src/attr.rs b/compiler/rustc_attr_ir/src/attr.rs index 44fe94fcd44f4..f6f1e8e116b36 100644 --- a/compiler/rustc_attr_ir/src/attr.rs +++ b/compiler/rustc_attr_ir/src/attr.rs @@ -205,16 +205,6 @@ impl AttributeExt for Attribute { } } - #[inline] - fn is_word(&self) -> bool { - match &self { - Attribute::Tool(n) | Attribute::DeriveHelper(n) => { - matches!(n.args, AttrArgs::Empty) - } - _ => false, - } - } - #[inline] fn symbol_path(&self) -> Option> { match &self { From 23a6fce25287db14481201bb79b53b45971c7e56 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 16:34:23 +0100 Subject: [PATCH 02/12] Move `has_any_name` --- compiler/rustc_ast/src/attr/mod.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 6dec3c866de78..e42c9d67dcfc5 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -918,13 +918,6 @@ pub trait AttributeExt: Debug + 'static { self.name().map(|x| x == name).unwrap_or(false) } - /// Returns true if the attribute's first *and only* path segment is any of the passed-in - /// symbols. - #[inline] - fn has_any_name(&self, names: &[Symbol]) -> bool { - names.iter().any(|&name| self.has_name(name)) - } - fn path(&self) -> SmallVec<[Symbol; 1]> { self.symbol_path().unwrap_or(smallvec![sym::doc]) } @@ -994,9 +987,11 @@ impl Attribute { AttributeExt::has_name(self, name) } + /// Returns true if the attribute's first *and only* path segment is any of the passed-in + /// symbols. #[inline] pub fn has_any_name(&self, names: &[Symbol]) -> bool { - AttributeExt::has_any_name(self, names) + names.iter().any(|&name| self.has_name(name)) } /// Returns whether the attribute is a path, without any arguments. From 0052b004f0c8c5118e4f2362d6d44ae5a4b00330 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 16:35:16 +0100 Subject: [PATCH 03/12] Move `has_name` --- compiler/rustc_ast/src/attr/mod.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index e42c9d67dcfc5..843cfb6dd55ec 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -911,13 +911,6 @@ pub trait AttributeExt: Debug + 'static { /// a doc comment) will return `false`. fn is_doc_comment(&self) -> Option; - /// Returns true if the attribute's first *and only* path segment is equal to the passed-in - /// symbol. - #[inline] - fn has_name(&self, name: Symbol) -> bool { - self.name().map(|x| x == name).unwrap_or(false) - } - fn path(&self) -> SmallVec<[Symbol; 1]> { self.symbol_path().unwrap_or(smallvec![sym::doc]) } @@ -982,9 +975,11 @@ impl Attribute { AttributeExt::is_doc_comment(self).is_some() } + /// Returns true if the attribute's first *and only* path segment is equal to the passed-in + /// symbol. #[inline] pub fn has_name(&self, name: Symbol) -> bool { - AttributeExt::has_name(self, name) + self.name().map(|x| x == name).unwrap_or(false) } /// Returns true if the attribute's first *and only* path segment is any of the passed-in From d543d29f04458f65f34d3d5b5c5e4742cb6d3296 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 16:38:10 +0100 Subject: [PATCH 04/12] Move `path` --- compiler/rustc_ast/src/attr/mod.rs | 15 ++++++++++----- src/tools/clippy/clippy_utils/src/attrs.rs | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 843cfb6dd55ec..f5129fc259e69 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -911,10 +911,6 @@ pub trait AttributeExt: Debug + 'static { /// a doc comment) will return `false`. fn is_doc_comment(&self) -> Option; - fn path(&self) -> SmallVec<[Symbol; 1]> { - self.symbol_path().unwrap_or(smallvec![sym::doc]) - } - fn path_span(&self) -> Option; /// Returns None for doc comments @@ -1000,7 +996,16 @@ impl Attribute { } pub fn path(&self) -> SmallVec<[Symbol; 1]> { - AttributeExt::path(self) + use SyntheticAttr::*; + match &self.kind { + AttrKind::Normal(normal) => { + normal.item.path.segments.iter().map(|i| i.ident.name).collect() + } + AttrKind::Synthetic(CfgTrace(_)) => smallvec![sym::cfg], + AttrKind::Synthetic(CfgAttrTrace(_)) => smallvec![sym::cfg_attr], + AttrKind::DocComment(_, _) => smallvec![sym::doc], + AttrKind::Generated(generated) => generated.symbol_path(), + } } pub fn doc_str(&self) -> Option { diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index b1e32de466d1e..29b0d82747806 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -14,7 +14,7 @@ use rustc_span::{Span, Symbol}; use std::str::FromStr as _; /// Validates a single clippy attribute and emits errors for unknown or deprecated ones. -pub fn check_clippy_attr(sess: &Session, attr: &A) { +pub fn check_clippy_attr(sess: &Session, attr: &rustc_ast::ast::Attribute) { if let [clippy, segment2] = &*attr.path() && *clippy == sym::clippy { From 9f0606aef9df02bd67b90d77d33e8dfdb487d852 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 16:45:18 +0100 Subject: [PATCH 05/12] Move `is_doc_comment` --- compiler/rustc_ast/src/ast.rs | 14 +++++++------- compiler/rustc_ast/src/attr/mod.rs | 26 ++++++++------------------ compiler/rustc_attr_ir/src/attr.rs | 9 --------- 3 files changed, 15 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index b3b85655bb261..12c759fd80246 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3523,14 +3523,14 @@ impl GeneratedAttr { } } - pub fn is_doc_comment(&self) -> Option { + pub fn is_doc_comment(&self) -> bool { match self { - GeneratedAttr::AutomaticallyDerived => None, - GeneratedAttr::RustcConstUnstableDerived => None, - GeneratedAttr::Inline => None, - GeneratedAttr::DocHidden => None, - GeneratedAttr::CoverageOff => None, - GeneratedAttr::RustcTestMarker(_) => None, + GeneratedAttr::AutomaticallyDerived => false, + GeneratedAttr::RustcConstUnstableDerived => false, + GeneratedAttr::Inline => false, + GeneratedAttr::DocHidden => false, + GeneratedAttr::CoverageOff => false, + GeneratedAttr::RustcTestMarker(_) => false, } } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index f5129fc259e69..af459a455dcf0 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -91,17 +91,6 @@ impl AttributeExt for Attribute { } } - /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example). - /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not - /// a doc comment) will return `false`. - fn is_doc_comment(&self) -> Option { - match &self.kind { - AttrKind::Normal(..) | AttrKind::Synthetic(..) => None, - AttrKind::DocComment(..) => Some(self.span), - AttrKind::Generated(generated) => generated.is_doc_comment(), - } - } - /// For a single-segment attribute, returns its name; otherwise, returns `None`. fn name(&self) -> Option { use SyntheticAttr::*; @@ -906,11 +895,6 @@ pub trait AttributeExt: Debug + 'static { /// Matches one segment of the path to each element in `name` fn path_matches(&self, name: &[Symbol]) -> bool; - /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example). - /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not - /// a doc comment) will return `false`. - fn is_doc_comment(&self) -> Option; - fn path_span(&self) -> Option; /// Returns None for doc comments @@ -966,9 +950,15 @@ impl Attribute { AttributeExt::path_matches(self, name) } - // on ast attributes we return a bool since that's what most code already expects + /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example). + /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not + /// a doc comment) will return `false`. pub fn is_doc_comment(&self) -> bool { - AttributeExt::is_doc_comment(self).is_some() + match &self.kind { + AttrKind::Normal(..) | AttrKind::Synthetic(..) => false, + AttrKind::DocComment(..) => true, + AttrKind::Generated(generated) => generated.is_doc_comment(), + } } /// Returns true if the attribute's first *and only* path segment is equal to the passed-in diff --git a/compiler/rustc_attr_ir/src/attr.rs b/compiler/rustc_attr_ir/src/attr.rs index f6f1e8e116b36..4fd3b7ea40179 100644 --- a/compiler/rustc_attr_ir/src/attr.rs +++ b/compiler/rustc_attr_ir/src/attr.rs @@ -196,15 +196,6 @@ impl AttributeExt for Attribute { } } - #[inline] - fn is_doc_comment(&self) -> Option { - if let Attribute::Parsed(AttributeKind::DocComment { span, .. }) = self { - Some(*span) - } else { - None - } - } - #[inline] fn symbol_path(&self) -> Option> { match &self { From 89cded6c9b361aaafd0fdedb06905d810a92f45a Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 16:47:52 +0100 Subject: [PATCH 06/12] Move `doc_str` --- compiler/rustc_ast/src/attr/mod.rs | 28 ++++++------------- compiler/rustc_attr_ir/src/attr.rs | 8 ------ .../clippy_lints/src/derivable_impls.rs | 4 +-- 3 files changed, 11 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index af459a455dcf0..8201625f0478a 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -202,18 +202,6 @@ impl AttributeExt for Attribute { } } - /// Returns the documentation if this is a doc comment or a sugared doc comment. - /// * `///doc` returns `Some("doc")`. - /// * `#[doc = "doc"]` returns `Some("doc")`. - /// * `#[doc(...)]` returns `None`. - fn doc_str(&self) -> Option { - match &self.kind { - AttrKind::DocComment(.., data) => Some(*data), - AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(), - _ => None, - } - } - fn doc_resolution_scope(&self) -> Option { match &self.kind { AttrKind::DocComment(..) => Some(self.style), @@ -900,12 +888,6 @@ pub trait AttributeExt: Debug + 'static { /// Returns None for doc comments fn symbol_path(&self) -> Option>; - /// Returns the documentation if this is a doc comment or a sugared doc comment. - /// * `///doc` returns `Some("doc")`. - /// * `#[doc = "doc"]` returns `Some("doc")`. - /// * `#[doc(...)]` returns `None`. - fn doc_str(&self) -> Option; - /// Returns whether this attribute is any of the proc macro attributes. /// i.e. `proc_macro`, `proc_macro_attribute` or `proc_macro_derive`. fn is_proc_macro_attr(&self) -> bool; @@ -998,8 +980,16 @@ impl Attribute { } } + /// Returns the documentation if this is a doc comment or a sugared doc comment. + /// * `///doc` returns `Some("doc")`. + /// * `#[doc = "doc"]` returns `Some("doc")`. + /// * `#[doc(...)]` returns `None`. pub fn doc_str(&self) -> Option { - AttributeExt::doc_str(self) + match &self.kind { + AttrKind::DocComment(.., data) => Some(*data), + AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(), + _ => None, + } } pub fn is_proc_macro_attr(&self) -> bool { diff --git a/compiler/rustc_attr_ir/src/attr.rs b/compiler/rustc_attr_ir/src/attr.rs index 4fd3b7ea40179..83b088a4cdad7 100644 --- a/compiler/rustc_attr_ir/src/attr.rs +++ b/compiler/rustc_attr_ir/src/attr.rs @@ -213,14 +213,6 @@ impl AttributeExt for Attribute { } } - #[inline] - fn doc_str(&self) -> Option { - match &self { - Attribute::Parsed(AttributeKind::DocComment { comment, .. }) => Some(*comment), - _ => None, - } - } - #[inline] fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind, Span)> { match &self { diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index 21bd1a5cf39d7..c6566f89841a4 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::indent_of; use clippy_utils::{is_default_equivalent, peel_blocks}; -use rustc_ast::attr::AttributeExt as _; +use rustc_attr_ir::find_attr; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{ @@ -250,7 +250,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { .skip_norm_wip() .kind() && let attrs = cx.tcx.hir_attrs(item.hir_id()) - && !attrs.iter().any(|attr| attr.doc_str().is_some()) + && !find_attr!(attrs, DocComment { .. }) && cx.tcx.hir_attrs(impl_item_hir).is_empty() { let is_const = matches!(constness, hir::Constness::Const { always: false }); From 1c36299a589980d1b26745e6383b4ed28b983f22 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 16:52:24 +0100 Subject: [PATCH 07/12] Move `meta_item_list` --- compiler/rustc_ast/src/attr/mod.rs | 35 ++++++++++++------------------ compiler/rustc_attr_ir/src/attr.rs | 14 ------------ 2 files changed, 14 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 8201625f0478a..bfb0a67407e7b 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -140,20 +140,6 @@ impl AttributeExt for Attribute { } } - /// Returns a list of meta items if the attribute is delimited with parenthesis: - /// - /// ```text - /// #[attr(a, b = "c")] // Returns `Some()`. - /// #[attr = ""] // Returns `None`. - /// #[attr] // Returns `None`. - /// ``` - fn meta_item_list(&self) -> Option> { - match &self.kind { - AttrKind::Normal(normal) => normal.item.meta_item_list(), - AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => None, - } - } - /// Returns the string value in: /// /// ```text @@ -294,6 +280,20 @@ impl Attribute { _ => None, } } + + /// Returns a list of meta items if the attribute is delimited with parenthesis: + /// + /// ```text + /// #[attr(a, b = "c")] // Returns `Some()`. + /// #[attr = ""] // Returns `None`. + /// #[attr] // Returns `None`. + /// ``` + pub fn meta_item_list(&self) -> Option> { + match &self.kind { + AttrKind::Normal(normal) => normal.item.meta_item_list(), + AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => None, + } + } } impl AttrItem { @@ -869,9 +869,6 @@ pub trait AttributeExt: Debug + 'static { /// return the name of the attribute; otherwise, returns `None`. fn name(&self) -> Option; - /// Get the meta item list, `#[attr(meta item list)]` - fn meta_item_list(&self) -> Option>; - /// Gets the value literal, as string, when using `#[attr = value]` fn value_str(&self) -> Option; @@ -916,10 +913,6 @@ impl Attribute { AttributeExt::name(self) } - pub fn meta_item_list(&self) -> Option> { - AttributeExt::meta_item_list(self) - } - pub fn value_str(&self) -> Option { AttributeExt::value_str(self) } diff --git a/compiler/rustc_attr_ir/src/attr.rs b/compiler/rustc_attr_ir/src/attr.rs index 83b088a4cdad7..bcf884be08324 100644 --- a/compiler/rustc_attr_ir/src/attr.rs +++ b/compiler/rustc_attr_ir/src/attr.rs @@ -8,7 +8,6 @@ use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_span::{AttrId, DUMMY_SP, Ident, Span, Symbol}; use smallvec::SmallVec; -use thin_vec::ThinVec; use crate::AttributeKind; @@ -148,19 +147,6 @@ impl Attribute { } impl AttributeExt for Attribute { - #[inline] - fn meta_item_list(&self) -> Option> { - match &self { - Attribute::Tool(n) | Attribute::DeriveHelper(n) => match n.as_ref() { - AttrItem { args: AttrArgs::Delimited(d), .. } => { - ast::MetaItemKind::list_from_tokens(d.tokens.clone()) - } - _ => None, - }, - _ => None, - } - } - #[inline] fn value_str(&self) -> Option { self.value_lit().and_then(|x| x.value_as_str()) From 721a73a7a5422677dc094ab68c6cf6d69f6572ad Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 16:54:08 +0100 Subject: [PATCH 08/12] Move `path_span` --- compiler/rustc_ast/src/attr/mod.rs | 20 +++++++++---------- compiler/rustc_attr_ir/src/attr.rs | 7 ------- .../src/attrs/allow_attributes.rs | 1 - 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index bfb0a67407e7b..52dff624f7c1b 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -114,15 +114,6 @@ impl AttributeExt for Attribute { } } - fn path_span(&self) -> Option { - match &self.kind { - AttrKind::Normal(attr) => Some(attr.item.path.span), - AttrKind::Synthetic(..) => unreachable!(), - AttrKind::DocComment(_, _) => None, - AttrKind::Generated(generated) => generated.path_span(), - } - } - fn path_matches(&self, name: &[Symbol]) -> bool { match &self.kind { AttrKind::Normal(normal) => { @@ -294,6 +285,15 @@ impl Attribute { AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => None, } } + + pub fn path_span(&self) -> Option { + match &self.kind { + AttrKind::Normal(attr) => Some(attr.item.path.span), + AttrKind::Synthetic(..) => unreachable!(), + AttrKind::DocComment(_, _) => None, + AttrKind::Generated(generated) => generated.path_span(), + } + } } impl AttrItem { @@ -880,8 +880,6 @@ pub trait AttributeExt: Debug + 'static { /// Matches one segment of the path to each element in `name` fn path_matches(&self, name: &[Symbol]) -> bool; - fn path_span(&self) -> Option; - /// Returns None for doc comments fn symbol_path(&self) -> Option>; diff --git a/compiler/rustc_attr_ir/src/attr.rs b/compiler/rustc_attr_ir/src/attr.rs index bcf884be08324..89aeac9fd2453 100644 --- a/compiler/rustc_attr_ir/src/attr.rs +++ b/compiler/rustc_attr_ir/src/attr.rs @@ -192,13 +192,6 @@ impl AttributeExt for Attribute { } } - fn path_span(&self) -> Option { - match &self { - Attribute::Tool(attr) | Attribute::DeriveHelper(attr) => Some(attr.path.span), - Attribute::Parsed(_) => None, - } - } - #[inline] fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind, Span)> { match &self { diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs index 6b493afddb486..01d442133ccf0 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs @@ -1,7 +1,6 @@ use super::ALLOW_ATTRIBUTES; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use rustc_ast::attr::AttributeExt as _; use rustc_ast::{AttrStyle, Attribute}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; From d37a3bb5c4dedfc66a897758f6c4fa86e8608e36 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 16:56:47 +0100 Subject: [PATCH 09/12] Move `value_span` --- compiler/rustc_ast/src/attr/mod.rs | 24 +++++++++--------------- compiler/rustc_attr_ir/src/attr.rs | 5 ----- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 52dff624f7c1b..f3db358677ed0 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -80,17 +80,6 @@ impl Attribute { } impl AttributeExt for Attribute { - fn value_span(&self) -> Option { - match &self.kind { - AttrKind::Normal(normal) => match &normal.item.args { - AttrArgs::Eq { expr, .. } => Some(expr.span), - _ => None, - }, - AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None, - AttrKind::Generated(generated) => generated.value_span(), - } - } - /// For a single-segment attribute, returns its name; otherwise, returns `None`. fn name(&self) -> Option { use SyntheticAttr::*; @@ -872,9 +861,6 @@ pub trait AttributeExt: Debug + 'static { /// Gets the value literal, as string, when using `#[attr = value]` fn value_str(&self) -> Option; - /// Gets the span of the value literal, as string, when using `#[attr = value]` - fn value_span(&self) -> Option; - /// Checks whether the path of this attribute matches the name. /// /// Matches one segment of the path to each element in `name` @@ -915,8 +901,16 @@ impl Attribute { AttributeExt::value_str(self) } + /// Gets the span of the value literal, as string, when using `#[attr = value]` pub fn value_span(&self) -> Option { - AttributeExt::value_span(self) + match &self.kind { + AttrKind::Normal(normal) => match &normal.item.args { + AttrArgs::Eq { expr, .. } => Some(expr.span), + _ => None, + }, + AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None, + AttrKind::Generated(generated) => generated.value_span(), + } } pub fn path_matches(&self, name: &[Symbol]) -> bool { diff --git a/compiler/rustc_attr_ir/src/attr.rs b/compiler/rustc_attr_ir/src/attr.rs index 89aeac9fd2453..f57a911553f18 100644 --- a/compiler/rustc_attr_ir/src/attr.rs +++ b/compiler/rustc_attr_ir/src/attr.rs @@ -152,11 +152,6 @@ impl AttributeExt for Attribute { self.value_lit().and_then(|x| x.value_as_str()) } - #[inline] - fn value_span(&self) -> Option { - self.value_lit().map(|i| i.span) - } - /// For a single-segment attribute, returns its name; otherwise, returns `None`. #[inline] fn name(&self) -> Option { From b5d23e6efeb3c28da6e7da588f53ecc62f1f1c9a Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 16:59:22 +0100 Subject: [PATCH 10/12] Move `is_proc_macro_attr` --- compiler/rustc_ast/src/attr/mod.rs | 18 +++++------------- compiler/rustc_attr_ir/src/attr.rs | 11 ----------- src/tools/clippy/clippy_utils/src/attrs.rs | 9 +++++++-- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index f3db358677ed0..4bd7c732512bc 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -179,14 +179,6 @@ impl AttributeExt for Attribute { _ => None, } } - - /// Returns whether this attribute is any of the proc macro attributes. - /// i.e. `proc_macro`, `proc_macro_attribute` or `proc_macro_derive`. - fn is_proc_macro_attr(&self) -> bool { - [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] - .iter() - .any(|kind| self.has_name(*kind)) - } } impl Attribute { @@ -869,10 +861,6 @@ pub trait AttributeExt: Debug + 'static { /// Returns None for doc comments fn symbol_path(&self) -> Option>; - /// Returns whether this attribute is any of the proc macro attributes. - /// i.e. `proc_macro`, `proc_macro_attribute` or `proc_macro_derive`. - fn is_proc_macro_attr(&self) -> bool; - /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment. /// * `///doc` returns `Some(("doc", CommentKind::Line))`. /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`. @@ -977,8 +965,12 @@ impl Attribute { } } + /// Returns whether this attribute is any of the proc macro attributes. + /// i.e. `proc_macro`, `proc_macro_attribute` or `proc_macro_derive`. pub fn is_proc_macro_attr(&self) -> bool { - AttributeExt::is_proc_macro_attr(self) + [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] + .iter() + .any(|kind| self.has_name(*kind)) } pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind, Span)> { diff --git a/compiler/rustc_attr_ir/src/attr.rs b/compiler/rustc_attr_ir/src/attr.rs index f57a911553f18..557c0a2a13b81 100644 --- a/compiler/rustc_attr_ir/src/attr.rs +++ b/compiler/rustc_attr_ir/src/attr.rs @@ -203,15 +203,4 @@ impl AttributeExt for Attribute { _ => None, } } - - fn is_proc_macro_attr(&self) -> bool { - matches!( - self, - Attribute::Parsed( - AttributeKind::ProcMacro - | AttributeKind::ProcMacroAttribute - | AttributeKind::ProcMacroDerive { .. } - ) - ) - } } diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 29b0d82747806..be112441d0b1b 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -84,8 +84,13 @@ pub fn get_clippy_attr(tcx: TyCtxt<'_>, did: impl Into, name: Symbol) -> /// Checks whether `attrs` contain any of `proc_macro`, `proc_macro_derive` or /// `proc_macro_attribute` -pub fn is_proc_macro(attrs: &[impl AttributeExt]) -> bool { - attrs.iter().any(AttributeExt::is_proc_macro_attr) +pub fn is_proc_macro(attrs: &[rustc_attr_ir::Attribute]) -> bool { + find_attr!( + attrs, + ProcMacro + | ProcMacroAttribute + | ProcMacroDerive { .. } + ) } /// Checks whether `attrs` contain `#[doc(hidden)]` From e6b98421302923f747791fc318b59a0ac4da9735 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Oct 2026 17:03:51 +0100 Subject: [PATCH 11/12] Move `symbol_path` --- compiler/rustc_ast/src/ast.rs | 18 +++++++++--------- compiler/rustc_ast/src/attr/mod.rs | 17 +---------------- compiler/rustc_attr_ir/src/attr.rs | 11 ----------- .../src/attrs/mixed_attributes_style.rs | 2 +- 4 files changed, 11 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 12c759fd80246..773a717855e52 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3534,14 +3534,14 @@ impl GeneratedAttr { } } - pub fn name(&self) -> Option { + pub fn name(&self) -> Symbol { match self { - GeneratedAttr::AutomaticallyDerived => Some(sym::automatically_derived), - GeneratedAttr::RustcConstUnstableDerived => Some(sym::rustc_const_unstable), - GeneratedAttr::Inline => Some(sym::inline), - GeneratedAttr::DocHidden => Some(sym::doc), - GeneratedAttr::CoverageOff => Some(sym::coverage), - GeneratedAttr::RustcTestMarker(_) => Some(sym::rustc_test_marker), + GeneratedAttr::AutomaticallyDerived => sym::automatically_derived, + GeneratedAttr::RustcConstUnstableDerived => sym::rustc_const_unstable, + GeneratedAttr::Inline => sym::inline, + GeneratedAttr::DocHidden => sym::doc, + GeneratedAttr::CoverageOff => sym::coverage, + GeneratedAttr::RustcTestMarker(_) => sym::rustc_test_marker, } } @@ -3558,8 +3558,8 @@ impl GeneratedAttr { path == name } - pub fn symbol_path(&self) -> Option> { - self.name().map(|n| smallvec![n]) + pub fn symbol_path(&self) -> SmallVec<[Symbol; 1]> { + smallvec![self.name()] } pub fn path_span(&self) -> Option { diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 4bd7c732512bc..ce34265078af5 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -87,19 +87,7 @@ impl AttributeExt for Attribute { AttrKind::Normal(normal) => normal.item.name(), AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None, AttrKind::DocComment(..) => None, - AttrKind::Generated(generated) => generated.name(), - } - } - - fn symbol_path(&self) -> Option> { - use SyntheticAttr::*; - match &self.kind { - AttrKind::Normal(normal) => { - Some(normal.item.path.segments.iter().map(|i| i.ident.name).collect()) - } - AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None, - AttrKind::DocComment(_, _) => None, - AttrKind::Generated(generated) => generated.symbol_path(), + AttrKind::Generated(generated) => Some(generated.name()), } } @@ -858,9 +846,6 @@ pub trait AttributeExt: Debug + 'static { /// Matches one segment of the path to each element in `name` fn path_matches(&self, name: &[Symbol]) -> bool; - /// Returns None for doc comments - fn symbol_path(&self) -> Option>; - /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment. /// * `///doc` returns `Some(("doc", CommentKind::Line))`. /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`. diff --git a/compiler/rustc_attr_ir/src/attr.rs b/compiler/rustc_attr_ir/src/attr.rs index 557c0a2a13b81..760e498a736a8 100644 --- a/compiler/rustc_attr_ir/src/attr.rs +++ b/compiler/rustc_attr_ir/src/attr.rs @@ -7,7 +7,6 @@ use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHashe use rustc_error_messages::{DiagArgValue, IntoDiagArg}; use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_span::{AttrId, DUMMY_SP, Ident, Span, Symbol}; -use smallvec::SmallVec; use crate::AttributeKind; @@ -177,16 +176,6 @@ impl AttributeExt for Attribute { } } - #[inline] - fn symbol_path(&self) -> Option> { - match &self { - Attribute::Tool(n) | Attribute::DeriveHelper(n) => { - Some(n.path.segments.iter().copied().collect()) - } - _ => None, - } - } - #[inline] fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind, Span)> { match &self { diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index dc38fc4c11cd2..ec7de8e9ec953 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -34,7 +34,7 @@ impl From<&AttrKind> for SimpleAttrKind { SyntheticAttr::CfgAttrTrace(_) => Self::CfgAttrTrace, }, AttrKind::DocComment(..) => Self::Doc, - AttrKind::Generated(generated) => Self::Normal(vec![generated.name().unwrap()]), + AttrKind::Generated(generated) => Self::Normal(vec![generated.name()]), } } } From 427fe59605b000be9fb04544095c67ecb4bbab45 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sat, 31 Oct 2026 11:31:53 +0100 Subject: [PATCH 12/12] Rename `GeneratedAttr::name` to `path`, to support tool attributes --- compiler/rustc_ast/src/ast.rs | 22 ++----------------- compiler/rustc_ast/src/attr/mod.rs | 9 +++++--- .../src/attrs/mixed_attributes_style.rs | 2 +- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 773a717855e52..320bf5b65ff4b 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -34,7 +34,6 @@ use rustc_span::{ ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, LocalExpnId, Span, Spanned, Symbol, kw, respan, sym, }; -use smallvec::{SmallVec, smallvec}; use thin_vec::{ThinVec, thin_vec}; use crate::attr::data_structures::CfgEntry; @@ -3534,32 +3533,15 @@ impl GeneratedAttr { } } - pub fn name(&self) -> Symbol { + pub fn path(&self) -> &[Symbol] { match self { - GeneratedAttr::AutomaticallyDerived => sym::automatically_derived, - GeneratedAttr::RustcConstUnstableDerived => sym::rustc_const_unstable, - GeneratedAttr::Inline => sym::inline, - GeneratedAttr::DocHidden => sym::doc, - GeneratedAttr::CoverageOff => sym::coverage, - GeneratedAttr::RustcTestMarker(_) => sym::rustc_test_marker, - } - } - - pub fn path_matches(&self, name: &[Symbol]) -> bool { - let path = match self { GeneratedAttr::AutomaticallyDerived => &[sym::automatically_derived], GeneratedAttr::RustcConstUnstableDerived => &[sym::rustc_const_unstable], GeneratedAttr::Inline => &[sym::inline], GeneratedAttr::DocHidden => &[sym::doc], GeneratedAttr::CoverageOff => &[sym::coverage], GeneratedAttr::RustcTestMarker(_) => &[sym::rustc_test_marker], - }; - - path == name - } - - pub fn symbol_path(&self) -> SmallVec<[Symbol; 1]> { - smallvec![self.name()] + } } pub fn path_span(&self) -> Option { diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index ce34265078af5..88eec44407fa6 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -87,7 +87,10 @@ impl AttributeExt for Attribute { AttrKind::Normal(normal) => normal.item.name(), AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None, AttrKind::DocComment(..) => None, - AttrKind::Generated(generated) => Some(generated.name()), + AttrKind::Generated(generated) => match generated.path() { + [name] => Some(*name), + _ => None, + }, } } @@ -104,7 +107,7 @@ impl AttributeExt for Attribute { .all(|(s, n)| s.args.is_none() && s.ident.name == *n) } AttrKind::Synthetic(..) | AttrKind::DocComment(..) => false, - AttrKind::Generated(generated) => generated.path_matches(name), + AttrKind::Generated(generated) => generated.path() == name, } } @@ -934,7 +937,7 @@ impl Attribute { AttrKind::Synthetic(CfgTrace(_)) => smallvec![sym::cfg], AttrKind::Synthetic(CfgAttrTrace(_)) => smallvec![sym::cfg_attr], AttrKind::DocComment(_, _) => smallvec![sym::doc], - AttrKind::Generated(generated) => generated.symbol_path(), + AttrKind::Generated(generated) => generated.path().iter().copied().collect(), } } diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index ec7de8e9ec953..e26e215d71dc0 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -34,7 +34,7 @@ impl From<&AttrKind> for SimpleAttrKind { SyntheticAttr::CfgAttrTrace(_) => Self::CfgAttrTrace, }, AttrKind::DocComment(..) => Self::Doc, - AttrKind::Generated(generated) => Self::Normal(vec![generated.name()]), + AttrKind::Generated(generated) => Self::Normal(generated.path().iter().copied().collect()), } } }