diff --git a/Cargo.lock b/Cargo.lock index 38b009bfc7000..03a8f3c6f5b51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3332,6 +3332,7 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_ast_pretty", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -3339,6 +3340,7 @@ dependencies = [ "rustc_index", "rustc_macros", "rustc_middle", + "rustc_parse", "rustc_session", "rustc_span", "rustc_target", @@ -3354,7 +3356,7 @@ dependencies = [ "itertools", "rustc_ast", "rustc_ast_pretty", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -3380,21 +3382,37 @@ dependencies = [ ] [[package]] -name = "rustc_attr" +name = "rustc_attr_data_structures" +version = "0.0.0" +dependencies = [ + "rustc_abi", + "rustc_ast", + "rustc_data_structures", + "rustc_macros", + "rustc_serialize", + "rustc_span", + "thin-vec", +] + +[[package]] +name = "rustc_attr_parsing" version = "0.0.0" dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_feature", "rustc_fluent_macro", + "rustc_hir", "rustc_lexer", "rustc_macros", "rustc_serialize", "rustc_session", "rustc_span", + "thin-vec", ] [[package]] @@ -3441,12 +3459,13 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_ast_pretty", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_expand", "rustc_feature", "rustc_fluent_macro", + "rustc_hir", "rustc_index", "rustc_lexer", "rustc_lint_defs", @@ -3473,7 +3492,7 @@ dependencies = [ "rustc-demangle", "rustc_abi", "rustc_ast", - "rustc_attr", + "rustc_attr_parsing", "rustc_codegen_ssa", "rustc_data_structures", "rustc_errors", @@ -3516,12 +3535,13 @@ dependencies = [ "rustc_arena", "rustc_ast", "rustc_ast_pretty", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", "rustc_fs_util", "rustc_hir", + "rustc_hir_pretty", "rustc_incremental", "rustc_index", "rustc_macros", @@ -3553,7 +3573,7 @@ dependencies = [ "rustc_abi", "rustc_apfloat", "rustc_ast", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -3620,7 +3640,7 @@ dependencies = [ "rustc_ast_lowering", "rustc_ast_passes", "rustc_ast_pretty", - "rustc_attr", + "rustc_attr_parsing", "rustc_borrowck", "rustc_builtin_macros", "rustc_codegen_ssa", @@ -3697,6 +3717,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_error_codes", "rustc_error_messages", @@ -3724,11 +3745,12 @@ dependencies = [ "rustc_ast", "rustc_ast_passes", "rustc_ast_pretty", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_feature", "rustc_fluent_macro", + "rustc_hir", "rustc_lexer", "rustc_lint_defs", "rustc_macros", @@ -3780,6 +3802,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_index", "rustc_macros", @@ -3787,6 +3810,7 @@ dependencies = [ "rustc_span", "rustc_target", "smallvec", + "thin-vec", "tracing", ] @@ -3798,7 +3822,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -3825,6 +3849,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", + "rustc_attr_parsing", "rustc_hir", "rustc_span", ] @@ -3837,7 +3862,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_ir", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -3926,7 +3951,7 @@ dependencies = [ "rustc_ast_lowering", "rustc_ast_passes", "rustc_ast_pretty", - "rustc_attr", + "rustc_attr_parsing", "rustc_borrowck", "rustc_builtin_macros", "rustc_codegen_llvm", @@ -3982,7 +4007,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -4056,7 +4081,7 @@ dependencies = [ "odht", "rustc_abi", "rustc_ast", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_expand", @@ -4093,7 +4118,7 @@ dependencies = [ "rustc_arena", "rustc_ast", "rustc_ast_ir", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_error_messages", "rustc_errors", @@ -4172,7 +4197,7 @@ dependencies = [ "rustc_abi", "rustc_arena", "rustc_ast", - "rustc_attr", + "rustc_attr_parsing", "rustc_const_eval", "rustc_data_structures", "rustc_errors", @@ -4198,7 +4223,7 @@ name = "rustc_monomorphize" version = "0.0.0" dependencies = [ "rustc_abi", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4266,7 +4291,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_pretty", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_expand", @@ -4313,7 +4338,7 @@ name = "rustc_privacy" version = "0.0.0" dependencies = [ "rustc_ast", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", @@ -4352,6 +4377,7 @@ dependencies = [ "parking_lot", "rustc-rayon-core", "rustc_ast", + "rustc_attr_data_structures", "rustc_data_structures", "rustc_errors", "rustc_feature", @@ -4377,7 +4403,7 @@ dependencies = [ "rustc_arena", "rustc_ast", "rustc_ast_pretty", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_expand", @@ -4402,6 +4428,7 @@ version = "0.0.0" dependencies = [ "bitflags", "rustc_abi", + "rustc_ast", "rustc_data_structures", "rustc_hir", "rustc_middle", @@ -4455,9 +4482,9 @@ version = "0.0.0" dependencies = [ "rustc_abi", "rustc_ast", - "rustc_ast_pretty", "rustc_data_structures", "rustc_hir", + "rustc_hir_pretty", "rustc_middle", "rustc_session", "rustc_span", @@ -4495,6 +4522,7 @@ dependencies = [ "punycode", "rustc-demangle", "rustc_abi", + "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_hir", @@ -4534,7 +4562,7 @@ dependencies = [ "rustc_abi", "rustc_ast", "rustc_ast_ir", - "rustc_attr", + "rustc_attr_parsing", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", diff --git a/a.out b/a.out new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 650525a2f520e..7304480be1c6c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -19,7 +19,7 @@ //! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators. use std::borrow::Cow; -use std::{cmp, fmt, mem}; +use std::{cmp, fmt}; pub use GenericArgs::*; pub use UnsafeSource::*; @@ -1739,53 +1739,16 @@ pub enum AttrArgs { Eq { /// Span of the `=` token. eq_span: Span, - - value: AttrArgsEq, + expr: P, }, } -// The RHS of an `AttrArgs::Eq` starts out as an expression. Once macro -// expansion is completed, all cases end up either as a meta item literal, -// which is the form used after lowering to HIR, or as an error. -#[derive(Clone, Encodable, Decodable, Debug)] -pub enum AttrArgsEq { - Ast(P), - Hir(MetaItemLit), -} - -impl AttrArgsEq { - pub fn span(&self) -> Span { - match self { - AttrArgsEq::Ast(p) => p.span, - AttrArgsEq::Hir(lit) => lit.span, - } - } - - pub fn unwrap_ast(&self) -> &Expr { - match self { - AttrArgsEq::Ast(p) => p, - AttrArgsEq::Hir(lit) => { - unreachable!("in literal form when getting inner tokens: {lit:?}") - } - } - } - - pub fn unwrap_ast_mut(&mut self) -> &mut P { - match self { - AttrArgsEq::Ast(p) => p, - AttrArgsEq::Hir(lit) => { - unreachable!("in literal form when getting inner tokens: {lit:?}") - } - } - } -} - impl AttrArgs { pub fn span(&self) -> Option { match self { AttrArgs::Empty => None, AttrArgs::Delimited(args) => Some(args.dspan.entire()), - AttrArgs::Eq { eq_span, value } => Some(eq_span.to(value.span())), + AttrArgs::Eq { eq_span, expr } => Some(eq_span.to(expr.span)), } } @@ -1795,27 +1758,7 @@ impl AttrArgs { match self { AttrArgs::Empty => TokenStream::default(), AttrArgs::Delimited(args) => args.tokens.clone(), - AttrArgs::Eq { value, .. } => TokenStream::from_ast(value.unwrap_ast()), - } - } -} - -impl HashStable for AttrArgs -where - CTX: crate::HashStableContext, -{ - fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { - mem::discriminant(self).hash_stable(ctx, hasher); - match self { - AttrArgs::Empty => {} - AttrArgs::Delimited(args) => args.hash_stable(ctx, hasher), - AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => { - unreachable!("hash_stable {:?}", expr); - } - AttrArgs::Eq { eq_span, value: AttrArgsEq::Hir(lit) } => { - eq_span.hash_stable(ctx, hasher); - lit.hash_stable(ctx, hasher); - } + AttrArgs::Eq { expr, .. } => TokenStream::from_ast(expr), } } } @@ -3024,7 +2967,7 @@ impl NormalAttr { } } -#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug)] pub struct AttrItem { pub unsafety: Safety, pub path: Path, diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 0d79cadef34de..8ee3d4d590cd3 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -1,5 +1,6 @@ //! Functions dealing with attributes and meta items. +use std::fmt::Debug; use std::iter; use std::sync::atomic::{AtomicU32, Ordering}; @@ -10,9 +11,9 @@ use smallvec::{SmallVec, smallvec}; use thin_vec::{ThinVec, thin_vec}; use crate::ast::{ - AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, - DelimArgs, Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, - NormalAttr, Path, PathSegment, Safety, + AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs, + Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path, + PathSegment, Safety, }; use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter, Token}; @@ -66,11 +67,27 @@ impl Attribute { AttrKind::DocComment(..) => panic!("unexpected doc comment"), } } +} + +impl AttributeExt for Attribute { + fn id(&self) -> AttrId { + self.id + } + + fn value_span(&self) -> Option { + match &self.kind { + AttrKind::Normal(normal) => match &normal.item.args { + AttrArgs::Eq { expr, .. } => Some(expr.span), + _ => None, + }, + AttrKind::DocComment(..) => None, + } + } /// 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 { + fn is_doc_comment(&self) -> bool { match self.kind { AttrKind::Normal(..) => false, AttrKind::DocComment(..) => true, @@ -78,7 +95,7 @@ impl Attribute { } /// For a single-segment attribute, returns its name; otherwise, returns `None`. - pub fn ident(&self) -> Option { + fn ident(&self) -> Option { match &self.kind { AttrKind::Normal(normal) => { if let [ident] = &*normal.item.path.segments { @@ -91,28 +108,14 @@ impl Attribute { } } - pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or_else(Ident::empty).name - } - - pub fn path(&self) -> SmallVec<[Symbol; 1]> { - match &self.kind { - AttrKind::Normal(normal) => { - normal.item.path.segments.iter().map(|s| s.ident.name).collect() - } - AttrKind::DocComment(..) => smallvec![sym::doc], - } - } - - #[inline] - pub fn has_name(&self, name: Symbol) -> bool { + fn ident_path(&self) -> Option> { match &self.kind { - AttrKind::Normal(normal) => normal.item.path == name, - AttrKind::DocComment(..) => false, + AttrKind::Normal(p) => Some(p.item.path.segments.iter().map(|i| i.ident).collect()), + AttrKind::DocComment(_, _) => None, } } - pub fn path_matches(&self, name: &[Symbol]) -> bool { + fn path_matches(&self, name: &[Symbol]) -> bool { match &self.kind { AttrKind::Normal(normal) => { normal.item.path.segments.len() == name.len() @@ -128,7 +131,11 @@ impl Attribute { } } - pub fn is_word(&self) -> bool { + fn span(&self) -> Span { + self.span + } + + fn is_word(&self) -> bool { if let AttrKind::Normal(normal) = &self.kind { matches!(normal.item.args, AttrArgs::Empty) } else { @@ -143,7 +150,7 @@ impl Attribute { /// #[attr = ""] // Returns `None`. /// #[attr] // Returns `None`. /// ``` - pub fn meta_item_list(&self) -> Option> { + fn meta_item_list(&self) -> Option> { match &self.kind { AttrKind::Normal(normal) => normal.item.meta_item_list(), AttrKind::DocComment(..) => None, @@ -165,7 +172,7 @@ impl Attribute { /// ```text /// #[attr("value")] /// ``` - pub fn value_str(&self) -> Option { + fn value_str(&self) -> Option { match &self.kind { AttrKind::Normal(normal) => normal.item.value_str(), AttrKind::DocComment(..) => None, @@ -177,7 +184,7 @@ impl Attribute { /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`. /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`. /// * `#[doc(...)]` returns `None`. - pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { + fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { match &self.kind { AttrKind::DocComment(kind, data) => Some((*data, *kind)), AttrKind::Normal(normal) if normal.item.path == sym::doc => { @@ -191,7 +198,7 @@ impl Attribute { /// * `///doc` returns `Some("doc")`. /// * `#[doc = "doc"]` returns `Some("doc")`. /// * `#[doc(...)]` returns `None`. - pub fn doc_str(&self) -> Option { + 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(), @@ -199,14 +206,14 @@ impl Attribute { } } - pub fn may_have_doc_links(&self) -> bool { - self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str())) + fn style(&self) -> AttrStyle { + self.style } +} - pub 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 { + pub fn may_have_doc_links(&self) -> bool { + self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str())) } /// Extracts the MetaItem from inside this Attribute. @@ -268,7 +275,12 @@ impl AttrItem { /// ``` fn value_str(&self) -> Option { match &self.args { - AttrArgs::Eq { value, .. } => value.value_str(), + AttrArgs::Eq { expr, .. } => match expr.kind { + ExprKind::Lit(token_lit) => { + LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str()) + } + _ => None, + }, AttrArgs::Delimited(_) | AttrArgs::Empty => None, } } @@ -287,20 +299,6 @@ impl AttrItem { } } -impl AttrArgsEq { - fn value_str(&self) -> Option { - match self { - AttrArgsEq::Ast(expr) => match expr.kind { - ExprKind::Lit(token_lit) => { - LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str()) - } - _ => None, - }, - AttrArgsEq::Hir(lit) => lit.kind.str(), - } - } -} - impl MetaItem { /// For a single-segment meta item, returns its name; otherwise, returns `None`. pub fn ident(&self) -> Option { @@ -439,7 +437,8 @@ impl MetaItem { } impl MetaItemKind { - fn list_from_tokens(tokens: TokenStream) -> Option> { + // public because it can be called in the hir + pub fn list_from_tokens(tokens: TokenStream) -> Option> { let mut tokens = tokens.trees().peekable(); let mut result = ThinVec::new(); while tokens.peek().is_some() { @@ -492,7 +491,7 @@ impl MetaItemKind { MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List) } AttrArgs::Delimited(..) => None, - AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => match expr.kind { + AttrArgs::Eq { expr, .. } => match expr.kind { ExprKind::Lit(token_lit) => { // Turn failures to `None`, we'll get parse errors elsewhere. MetaItemLit::from_token_lit(token_lit, expr.span) @@ -501,9 +500,6 @@ impl MetaItemKind { } _ => None, }, - AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => { - Some(MetaItemKind::NameValue(lit.clone())) - } } } } @@ -704,26 +700,175 @@ pub fn mk_attr_name_value_str( tokens: None, }); let path = Path::from_ident(Ident::new(name, span)); - let args = AttrArgs::Eq { eq_span: span, value: AttrArgsEq::Ast(expr) }; + let args = AttrArgs::Eq { eq_span: span, expr }; mk_attr(g, style, unsafety, path, args, span) } -pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator { +pub fn filter_by_name(attrs: &[A], name: Symbol) -> impl Iterator { attrs.iter().filter(move |attr| attr.has_name(name)) } -pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> { +pub fn find_by_name(attrs: &[A], name: Symbol) -> Option<&A> { filter_by_name(attrs, name).next() } -pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option { +pub fn first_attr_value_str_by_name(attrs: &[impl AttributeExt], name: Symbol) -> Option { find_by_name(attrs, name).and_then(|attr| attr.value_str()) } -pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool { +pub fn contains_name(attrs: &[impl AttributeExt], name: Symbol) -> bool { find_by_name(attrs, name).is_some() } pub fn list_contains_name(items: &[MetaItemInner], name: Symbol) -> bool { items.iter().any(|item| item.has_name(name)) } + +impl MetaItemLit { + pub fn value_str(&self) -> Option { + LitKind::from_token_lit(self.as_token_lit()).ok().and_then(|lit| lit.str()) + } +} + +pub trait AttributeExt: Debug { + fn id(&self) -> AttrId; + + fn name_or_empty(&self) -> Symbol { + self.ident().unwrap_or_else(Ident::empty).name + } + + /// 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; + + /// Gets the span of the value literal, as string, when using `#[attr = value]` + fn value_span(&self) -> Option; + + /// For a single-segment attribute, returns its name; otherwise, returns `None`. + fn ident(&self) -> Option; + + /// Checks whether the path of this attribute matches the name. + /// + /// 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) -> bool; + + #[inline] + fn has_name(&self, name: Symbol) -> bool { + self.ident().map(|x| x.name == name).unwrap_or(false) + } + + /// get the span of the entire attribute + fn span(&self) -> Span; + + fn is_word(&self) -> bool; + + fn path(&self) -> SmallVec<[Symbol; 1]> { + self.ident_path() + .map(|i| i.into_iter().map(|i| i.name).collect()) + .unwrap_or(smallvec![sym::doc]) + } + + /// Returns None for doc comments + fn ident_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; + + 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)) + } + + /// 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))`. + /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`. + /// * `#[doc(...)]` returns `None`. + fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)>; + + fn style(&self) -> AttrStyle; +} + +// FIXME(fn_delegation): use function delegation instead of manually forwarding + +impl Attribute { + pub fn id(&self) -> AttrId { + AttributeExt::id(self) + } + + pub fn name_or_empty(&self) -> Symbol { + AttributeExt::name_or_empty(self) + } + + pub fn meta_item_list(&self) -> Option> { + AttributeExt::meta_item_list(self) + } + + pub fn value_str(&self) -> Option { + AttributeExt::value_str(self) + } + + pub fn value_span(&self) -> Option { + AttributeExt::value_span(self) + } + + pub fn ident(&self) -> Option { + AttributeExt::ident(self) + } + + pub fn path_matches(&self, name: &[Symbol]) -> bool { + AttributeExt::path_matches(self, name) + } + + pub fn is_doc_comment(&self) -> bool { + AttributeExt::is_doc_comment(self) + } + + #[inline] + pub fn has_name(&self, name: Symbol) -> bool { + AttributeExt::has_name(self, name) + } + + pub fn span(&self) -> Span { + AttributeExt::span(self) + } + + pub fn is_word(&self) -> bool { + AttributeExt::is_word(self) + } + + pub fn path(&self) -> SmallVec<[Symbol; 1]> { + AttributeExt::path(self) + } + + pub fn ident_path(&self) -> Option> { + AttributeExt::ident_path(self) + } + + pub fn doc_str(&self) -> Option { + AttributeExt::doc_str(self) + } + + pub fn is_proc_macro_attr(&self) -> bool { + AttributeExt::is_proc_macro_attr(self) + } + + pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { + AttributeExt::doc_str_and_comment_kind(self) + } + + pub fn style(&self) -> AttrStyle { + AttributeExt::style(self) + } +} diff --git a/compiler/rustc_ast/src/entry.rs b/compiler/rustc_ast/src/entry.rs index 45c4caca6e9ee..fffcb3ef98890 100644 --- a/compiler/rustc_ast/src/entry.rs +++ b/compiler/rustc_ast/src/entry.rs @@ -1,7 +1,7 @@ use rustc_span::Symbol; use rustc_span::symbol::sym; -use crate::{Attribute, attr}; +use crate::attr::{self, AttributeExt}; #[derive(Debug)] pub enum EntryPointType { @@ -37,7 +37,7 @@ pub enum EntryPointType { } pub fn entry_point_type( - attrs: &[Attribute], + attrs: &[impl AttributeExt], at_root: bool, name: Option, ) -> EntryPointType { diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 7730d0b4b78df..6372c66050e7c 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -44,20 +44,10 @@ pub mod token; pub mod tokenstream; pub mod visit; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; - pub use self::ast::*; pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasTokens}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. -pub trait HashStableContext: rustc_span::HashStableContext { - fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher); -} - -impl HashStable for ast::Attribute { - fn hash_stable(&self, hcx: &mut AstCtx, hasher: &mut StableHasher) { - hcx.hash_attr(self, hasher) - } -} +pub trait HashStableContext: rustc_span::HashStableContext {} diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 2c09059fe1904..50f9423ded68f 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -451,8 +451,8 @@ fn visit_attr_args(vis: &mut T, args: &mut AttrArgs) { match args { AttrArgs::Empty => {} AttrArgs::Delimited(args) => visit_delim_args(vis, args), - AttrArgs::Eq { eq_span, value } => { - vis.visit_expr(value.unwrap_ast_mut()); + AttrArgs::Eq { eq_span, expr } => { + vis.visit_expr(expr); vis.visit_span(eq_span); } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index a7f7c37693a84..d2b0bae05dbd9 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -1279,7 +1279,7 @@ pub fn walk_attr_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a AttrArgs) - match args { AttrArgs::Empty => {} AttrArgs::Delimited(_args) => {} - AttrArgs::Eq { value, .. } => try_visit!(visitor.visit_expr(value.unwrap_ast())), + AttrArgs::Eq { expr, .. } => try_visit!(visitor.visit_expr(expr)), } V::Result::output() } diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml index 8cc4521e0a78d..bf84013613309 100644 --- a/compiler/rustc_ast_lowering/Cargo.toml +++ b/compiler/rustc_ast_lowering/Cargo.toml @@ -10,6 +10,7 @@ doctest = false # tidy-alphabetical-start rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } @@ -17,6 +18,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } +rustc_parse = { path = "../rustc_parse" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 20d3ce65facc7..af4d5491a571f 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -97,7 +97,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { }; let span = self.lower_span(l.span); let source = hir::LocalSource::Normal; - self.lower_attrs(hir_id, &l.attrs); + self.lower_attrs(hir_id, &l.attrs, l.span); self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source }) } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 2ad0ff3200e74..9fc36c38fbd9f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -78,9 +78,8 @@ impl<'hir> LoweringContext<'_, 'hir> { self.attrs.insert( ex.hir_id.local_id, &*self.arena.alloc_from_iter( - e.attrs - .iter() - .map(|a| self.lower_attr(a)) + self.lower_attrs_vec(&e.attrs, e.span) + .into_iter() .chain(old_attrs.iter().cloned()), ), ); @@ -99,7 +98,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } let expr_hir_id = self.lower_node_id(e.id); - self.lower_attrs(expr_hir_id, &e.attrs); + self.lower_attrs(expr_hir_id, &e.attrs, e.span); let kind = match &e.kind { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), @@ -635,7 +634,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond)); let hir_id = self.next_id(); let span = self.lower_span(arm.span); - self.lower_attrs(hir_id, &arm.attrs); + self.lower_attrs(hir_id, &arm.attrs, arm.span); let is_never_pattern = pat.is_never_pattern(); let body = if let Some(body) = &arm.body && !is_never_pattern @@ -793,15 +792,19 @@ impl<'hir> LoweringContext<'_, 'hir> { span, Some(Lrc::clone(&self.allow_gen_future)), ); - self.lower_attrs(inner_hir_id, &[Attribute { - kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new( - sym::track_caller, - span, - )))), - id: self.tcx.sess.psess.attr_id_generator.mk_attr_id(), - style: AttrStyle::Outer, - span: unstable_span, - }]); + self.lower_attrs( + inner_hir_id, + &[Attribute { + kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new( + sym::track_caller, + span, + )))), + id: self.tcx.sess.psess.attr_id_generator.mk_attr_id(), + style: AttrStyle::Outer, + span: unstable_span, + }], + span, + ); } } @@ -1606,7 +1609,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_expr_field(&mut self, f: &ExprField) -> hir::ExprField<'hir> { let hir_id = self.lower_node_id(f.id); - self.lower_attrs(hir_id, &f.attrs); + self.lower_attrs(hir_id, &f.attrs, f.span); hir::ExprField { hir_id, ident: self.lower_ident(f.ident), @@ -1869,7 +1872,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // // Also, add the attributes to the outer returned expr node. let expr = self.expr_drop_temps_mut(for_span, match_expr); - self.lower_attrs(expr.hir_id, &e.attrs); + self.lower_attrs(expr.hir_id, &e.attrs, e.span); expr } @@ -1926,7 +1929,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let val_ident = Ident::with_dummy_span(sym::val); let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident); let val_expr = self.expr_ident(span, val_ident, val_pat_nid); - self.lower_attrs(val_expr.hir_id, &attrs); + self.lower_attrs(val_expr.hir_id, &attrs, span); let continue_pat = self.pat_cf_continue(unstable_span, val_pat); self.arm(continue_pat, val_expr) }; @@ -1956,7 +1959,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { self.arena.alloc(self.expr(try_span, hir::ExprKind::Ret(Some(from_residual_expr)))) }; - self.lower_attrs(ret_expr.hir_id, &attrs); + self.lower_attrs(ret_expr.hir_id, &attrs, ret_expr.span); let break_pat = self.pat_cf_break(try_span, residual_local); self.arm(break_pat, ret_expr) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 7d6c41992eb09..c462babe30dcc 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -11,7 +11,7 @@ use rustc_middle::span_bug; use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{Ident, kw, sym}; -use rustc_span::{DesugaringKind, Span, Symbol}; +use rustc_span::{DUMMY_SP, DesugaringKind, Span, Symbol}; use rustc_target::spec::abi; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; @@ -94,7 +94,8 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID); self.with_lctx(CRATE_NODE_ID, |lctx| { let module = lctx.lower_mod(&c.items, &c.spans); - lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs); + // FIXME(jdonszelman): is dummy span ever a problem here? + lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs, DUMMY_SP); hir::OwnerNode::Crate(module) }) } @@ -158,7 +159,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut ident = i.ident; let vis_span = self.lower_span(i.vis.span); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - let attrs = self.lower_attrs(hir_id, &i.attrs); + let attrs = self.lower_attrs(hir_id, &i.attrs, i.span); let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind); let item = hir::Item { owner_id: hir_id.expect_owner(), @@ -176,7 +177,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, hir_id: hir::HirId, ident: &mut Ident, - attrs: &'hir [Attribute], + attrs: &'hir [hir::Attribute], vis_span: Span, i: &ItemKind, ) -> hir::ItemKind<'hir> { @@ -467,7 +468,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, vis_span: Span, ident: &mut Ident, - attrs: &'hir [Attribute], + attrs: &'hir [hir::Attribute], ) -> hir::ItemKind<'hir> { let path = &tree.prefix; let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect(); @@ -609,7 +610,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> { let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); let owner_id = hir_id.expect_owner(); - self.lower_attrs(hir_id, &i.attrs); + self.lower_attrs(hir_id, &i.attrs, i.span); let item = hir::ForeignItem { owner_id, ident: self.lower_ident(i.ident), @@ -667,7 +668,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_variant(&mut self, v: &Variant) -> hir::Variant<'hir> { let hir_id = self.lower_node_id(v.id); - self.lower_attrs(hir_id, &v.attrs); + self.lower_attrs(hir_id, &v.attrs, v.span); hir::Variant { hir_id, def_id: self.local_def_id(v.id), @@ -729,7 +730,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::FieldDef<'hir> { let ty = self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy)); let hir_id = self.lower_node_id(f.id); - self.lower_attrs(hir_id, &f.attrs); + self.lower_attrs(hir_id, &f.attrs, f.span); hir::FieldDef { span: self.lower_span(f.span), hir_id, @@ -748,7 +749,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> { let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - self.lower_attrs(hir_id, &i.attrs); + self.lower_attrs(hir_id, &i.attrs, i.span); let trait_item_def_id = hir_id.expect_owner(); let (generics, kind, has_default) = match &i.kind { @@ -878,7 +879,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let has_value = true; let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value); let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id); - self.lower_attrs(hir_id, &i.attrs); + self.lower_attrs(hir_id, &i.attrs, i.span); let (generics, kind) = match &i.kind { AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics( @@ -1033,7 +1034,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn lower_param(&mut self, param: &Param) -> hir::Param<'hir> { let hir_id = self.lower_node_id(param.id); - self.lower_attrs(hir_id, ¶m.attrs); + self.lower_attrs(hir_id, ¶m.attrs, param.span); hir::Param { hir_id, pat: self.lower_pat(¶m.pat), @@ -1392,7 +1393,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - pub(super) fn lower_safety(&mut self, s: Safety, default: hir::Safety) -> hir::Safety { + pub(super) fn lower_safety(&self, s: Safety, default: hir::Safety) -> hir::Safety { match s { Safety::Unsafe(_) => hir::Safety::Unsafe, Safety::Default => default, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index bac3f974ccae9..1d44dccec9633 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -41,8 +41,8 @@ // tidy-alphabetical-end use rustc_ast::node_id::NodeMap; -use rustc_ast::ptr::P; use rustc_ast::{self as ast, *}; +use rustc_attr_parsing::{AttributeParseContext, OmitDoc}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; @@ -96,7 +96,7 @@ struct LoweringContext<'a, 'hir> { /// Bodies inside the owner being lowered. bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>, /// Attributes inside the owner being lowered. - attrs: SortedMap, + attrs: SortedMap, /// Collect items that were created by lowering the current owner. children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>, @@ -134,10 +134,13 @@ struct LoweringContext<'a, 'hir> { allow_async_iterator: Lrc<[Symbol]>, allow_for_await: Lrc<[Symbol]>, allow_async_fn_traits: Lrc<[Symbol]>, + + attribute_parse_context: AttributeParseContext<'hir>, } impl<'a, 'hir> LoweringContext<'a, 'hir> { fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self { + let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect(); Self { // Pseudo-globals. tcx, @@ -177,6 +180,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // FIXME(gen_blocks): how does `closure_track_caller`/`async_fn_track_caller` // interact with `gen`/`async gen` blocks allow_async_iterator: [sym::gen_future, sym::async_iterator].into(), + + attribute_parse_context: AttributeParseContext::new( + tcx.sess, + tcx.features(), + registered_tools, + ), } } @@ -212,7 +221,6 @@ impl ResolverAstLowering { None } - /// Obtains resolution for a `NodeId` with a single resolution. fn get_partial_res(&self, id: NodeId) -> Option { self.partial_res_map.get(&id).copied() } @@ -840,37 +848,27 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ret } - fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [Attribute] { + fn lower_attrs( + &mut self, + id: HirId, + attrs: &[Attribute], + target_span: Span, + ) -> &'hir [hir::Attribute] { if attrs.is_empty() { &[] } else { + let lowered_attrs = self.lower_attrs_vec(attrs, target_span); + debug_assert_eq!(id.owner, self.current_hir_id_owner); - let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a))); + let ret = self.arena.alloc_from_iter(lowered_attrs); debug_assert!(!ret.is_empty()); self.attrs.insert(id.local_id, ret); ret } } - fn lower_attr(&self, attr: &Attribute) -> Attribute { - // Note that we explicitly do not walk the path. Since we don't really - // lower attributes (we use the AST version) there is nowhere to keep - // the `HirId`s. We don't actually need HIR version of attributes anyway. - // Tokens are also not needed after macro expansion and parsing. - let kind = match attr.kind { - AttrKind::Normal(ref normal) => AttrKind::Normal(P(NormalAttr { - item: AttrItem { - unsafety: normal.item.unsafety, - path: normal.item.path.clone(), - args: self.lower_attr_args(&normal.item.args), - tokens: None, - }, - tokens: None, - })), - AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data), - }; - - Attribute { kind, id: attr.id, style: attr.style, span: self.lower_span(attr.span) } + fn lower_attrs_vec(&self, attrs: &[Attribute], target_span: Span) -> Vec { + self.attribute_parse_context.parse_attribute_list(attrs, target_span, OmitDoc::Lower) } fn alias_attrs(&mut self, id: HirId, target_id: HirId) { @@ -882,35 +880,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } - fn lower_attr_args(&self, args: &AttrArgs) -> AttrArgs { - match args { - AttrArgs::Empty => AttrArgs::Empty, - AttrArgs::Delimited(args) => AttrArgs::Delimited(self.lower_delim_args(args)), - // This is an inert key-value attribute - it will never be visible to macros - // after it gets lowered to HIR. Therefore, we can extract literals to handle - // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). - &AttrArgs::Eq { eq_span, ref value } => { - let expr = value.unwrap_ast(); - // In valid code the value always ends up as a single literal. Otherwise, a dummy - // literal suffices because the error is handled elsewhere. - let lit = if let ExprKind::Lit(token_lit) = expr.kind - && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span) - { - lit - } else { - let guar = self.dcx().has_errors().unwrap(); - MetaItemLit { - symbol: kw::Empty, - suffix: None, - kind: LitKind::Err(guar), - span: DUMMY_SP, - } - }; - AttrArgs::Eq { eq_span, value: AttrArgsEq::Hir(lit) } - } - } - } - fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs { DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.flattened() } } @@ -1801,7 +1770,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let (name, kind) = self.lower_generic_param_kind(param, source); let hir_id = self.lower_node_id(param.id); - self.lower_attrs(hir_id, ¶m.attrs); + self.lower_attrs(hir_id, ¶m.attrs, param.span()); hir::GenericParam { hir_id, def_id: self.local_def_id(param.id), @@ -2184,7 +2153,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn stmt_let_pat( &mut self, - attrs: Option<&'hir [Attribute]>, + attrs: Option<&'hir [hir::Attribute]>, span: Span, init: Option<&'hir hir::Expr<'hir>>, pat: &'hir hir::Pat<'hir>, diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index c4bae084a3f8c..24f60d013477a 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -82,7 +82,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let fs = self.arena.alloc_from_iter(fields.iter().map(|f| { let hir_id = self.lower_node_id(f.id); - self.lower_attrs(hir_id, &f.attrs); + self.lower_attrs(hir_id, &f.attrs, f.span); hir::PatField { hir_id, diff --git a/compiler/rustc_ast_passes/Cargo.toml b/compiler/rustc_ast_passes/Cargo.toml index eace5ce820892..8046765647e8d 100644 --- a/compiler/rustc_ast_passes/Cargo.toml +++ b/compiler/rustc_ast_passes/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" itertools = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 5a0ec865f9d4d..25944392a52a9 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -207,8 +207,6 @@ ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc} -ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library - ast_passes_static_without_body = free static item without body .suggestion = provide a definition for the static diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 64e91c91e2ca3..e49317943dc18 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -342,7 +342,7 @@ impl<'a> AstValidator<'a> { sym::forbid, sym::warn, ]; - !arr.contains(&attr.name_or_empty()) && rustc_attr::is_builtin_attr(attr) + !arr.contains(&attr.name_or_empty()) && rustc_attr_parsing::is_builtin_attr(*attr) }) .for_each(|attr| { if attr.is_doc_comment() { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 9b600e3ee92a1..64faf5d908be2 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -733,13 +733,6 @@ pub(crate) struct AssociatedSuggestion2 { pub potential_assoc: Ident, } -#[derive(Diagnostic)] -#[diag(ast_passes_stability_outside_std, code = E0734)] -pub(crate) struct StabilityOutsideStd { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(ast_passes_feature_on_non_nightly, code = E0554)] pub(crate) struct FeatureOnNonNightly { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index aa3b772efb159..620ccb201de61 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -209,18 +209,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ); } } - - // Emit errors for non-staged-api crates. - if !self.features.staged_api() { - if attr.has_name(sym::unstable) - || attr.has_name(sym::stable) - || attr.has_name(sym::rustc_const_unstable) - || attr.has_name(sym::rustc_const_stable) - || attr.has_name(sym::rustc_default_body_unstable) - { - self.sess.dcx().emit_err(errors::StabilityOutsideStd { span: attr.span }); - } - } } fn visit_item(&mut self, i: &'a ast::Item) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 49e4a559e7382..5b5273f378c18 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -17,9 +17,9 @@ use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{Comment, CommentStyle}; use rustc_ast::{ - self as ast, AttrArgs, AttrArgsEq, BindingMode, BlockCheckMode, ByRef, DelimArgs, GenericArg, - GenericBound, InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, - InlineAsmTemplatePiece, PatKind, RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr, + self as ast, AttrArgs, BindingMode, BlockCheckMode, ByRef, DelimArgs, GenericArg, GenericBound, + InlineAsmOperand, InlineAsmOptions, InlineAsmRegOrRegClass, InlineAsmTemplatePiece, PatKind, + RangeEnd, RangeSyntax, Safety, SelfKind, Term, attr, }; use rustc_data_structures::sync::Lrc; use rustc_span::edition::Edition; @@ -359,7 +359,7 @@ fn binop_to_string(op: BinOpToken) -> &'static str { } } -fn doc_comment_to_string( +pub fn doc_comment_to_string( comment_kind: CommentKind, attr_style: ast::AttrStyle, data: Symbol, @@ -648,20 +648,13 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere AttrArgs::Empty => { self.print_path(&item.path, false, 0); } - AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => { + AttrArgs::Eq { expr, .. } => { self.print_path(&item.path, false, 0); self.space(); self.word_space("="); let token_str = self.expr_to_string(expr); self.word(token_str); } - AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => { - self.print_path(&item.path, false, 0); - self.space(); - self.word_space("="); - let token_str = self.meta_item_lit_to_string(lit); - self.word(token_str); - } } match item.unsafety { ast::Safety::Unsafe(_) => self.pclose(), diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs deleted file mode 100644 index 94f9727eb7fbe..0000000000000 --- a/compiler/rustc_attr/src/builtin.rs +++ /dev/null @@ -1,1349 +0,0 @@ -//! Parsing and validation of builtin attributes - -use std::num::NonZero; - -use rustc_abi::Align; -use rustc_ast::{ - self as ast, Attribute, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId, - attr, -}; -use rustc_ast_pretty::pprust; -use rustc_errors::ErrorGuaranteed; -use rustc_feature::{Features, GatedCfg, find_gated_cfg, is_builtin_attr_name}; -use rustc_macros::{Decodable, Encodable, HashStable_Generic}; -use rustc_session::config::ExpectedValues; -use rustc_session::lint::BuiltinLintDiag; -use rustc_session::lint::builtin::UNEXPECTED_CFGS; -use rustc_session::parse::feature_err; -use rustc_session::{RustcVersion, Session}; -use rustc_span::Span; -use rustc_span::hygiene::Transparency; -use rustc_span::symbol::{Symbol, kw, sym}; - -use crate::fluent_generated; -use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause}; - -/// The version placeholder that recently stabilized features contain inside the -/// `since` field of the `#[stable]` attribute. -/// -/// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591). -pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION"; - -pub fn is_builtin_attr(attr: &Attribute) -> bool { - attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) -} - -pub(crate) enum UnsupportedLiteralReason { - Generic, - CfgString, - CfgBoolean, - DeprecatedString, - DeprecatedKvPair, -} - -#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] -pub enum InlineAttr { - None, - Hint, - Always, - Never, -} - -#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)] -pub enum InstructionSetAttr { - ArmA32, - ArmT32, -} - -#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] -pub enum OptimizeAttr { - None, - Speed, - Size, -} - -/// Represents the following attributes: -/// -/// - `#[stable]` -/// - `#[unstable]` -#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic)] -pub struct Stability { - pub level: StabilityLevel, - pub feature: Symbol, -} - -impl Stability { - pub fn is_unstable(&self) -> bool { - self.level.is_unstable() - } - - pub fn is_stable(&self) -> bool { - self.level.is_stable() - } - - pub fn stable_since(&self) -> Option { - self.level.stable_since() - } -} - -/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. -#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic)] -pub struct ConstStability { - pub level: StabilityLevel, - pub feature: Symbol, - /// This is true iff the `const_stable_indirect` attribute is present. - pub const_stable_indirect: bool, - /// whether the function has a `#[rustc_promotable]` attribute - pub promotable: bool, -} - -impl ConstStability { - pub fn is_const_unstable(&self) -> bool { - self.level.is_unstable() - } - - pub fn is_const_stable(&self) -> bool { - self.level.is_stable() - } -} - -/// Represents the `#[rustc_default_body_unstable]` attribute. -#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[derive(HashStable_Generic)] -pub struct DefaultBodyStability { - pub level: StabilityLevel, - pub feature: Symbol, -} - -/// The available stability levels. -#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] -#[derive(HashStable_Generic)] -pub enum StabilityLevel { - /// `#[unstable]` - Unstable { - /// Reason for the current stability level. - reason: UnstableReason, - /// Relevant `rust-lang/rust` issue. - issue: Option>, - is_soft: bool, - /// If part of a feature is stabilized and a new feature is added for the remaining parts, - /// then the `implied_by` attribute is used to indicate which now-stable feature previously - /// contained an item. - /// - /// ```pseudo-Rust - /// #[unstable(feature = "foo", issue = "...")] - /// fn foo() {} - /// #[unstable(feature = "foo", issue = "...")] - /// fn foobar() {} - /// ``` - /// - /// ...becomes... - /// - /// ```pseudo-Rust - /// #[stable(feature = "foo", since = "1.XX.X")] - /// fn foo() {} - /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")] - /// fn foobar() {} - /// ``` - implied_by: Option, - }, - /// `#[stable]` - Stable { - /// Rust release which stabilized this feature. - since: StableSince, - /// Is this item allowed to be referred to on stable, despite being contained in unstable - /// modules? - allowed_through_unstable_modules: bool, - }, -} - -/// Rust release in which a feature is stabilized. -#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)] -#[derive(HashStable_Generic)] -pub enum StableSince { - Version(RustcVersion), - /// Stabilized in the upcoming version, whatever number that is. - Current, - /// Failed to parse a stabilization version. - Err, -} - -impl StabilityLevel { - pub fn is_unstable(&self) -> bool { - matches!(self, StabilityLevel::Unstable { .. }) - } - pub fn is_stable(&self) -> bool { - matches!(self, StabilityLevel::Stable { .. }) - } - pub fn stable_since(&self) -> Option { - match *self { - StabilityLevel::Stable { since, .. } => Some(since), - StabilityLevel::Unstable { .. } => None, - } - } -} - -#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] -#[derive(HashStable_Generic)] -pub enum UnstableReason { - None, - Default, - Some(Symbol), -} - -impl UnstableReason { - fn from_opt_reason(reason: Option) -> Self { - // UnstableReason::Default constructed manually - match reason { - Some(r) => Self::Some(r), - None => Self::None, - } - } - - pub fn to_opt_reason(&self) -> Option { - match self { - Self::None => None, - Self::Default => Some(sym::unstable_location_reason_default), - Self::Some(r) => Some(*r), - } - } -} - -/// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules` -/// attributes in `attrs`. Returns `None` if no stability attributes are found. -pub fn find_stability( - sess: &Session, - attrs: &[Attribute], - item_sp: Span, -) -> Option<(Stability, Span)> { - let mut stab: Option<(Stability, Span)> = None; - let mut allowed_through_unstable_modules = false; - - for attr in attrs { - match attr.name_or_empty() { - sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true, - sym::unstable => { - if stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); - break; - } - - if let Some((feature, level)) = parse_unstability(sess, attr) { - stab = Some((Stability { level, feature }, attr.span)); - } - } - sym::stable => { - if stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); - break; - } - if let Some((feature, level)) = parse_stability(sess, attr) { - stab = Some((Stability { level, feature }, attr.span)); - } - } - _ => {} - } - } - - if allowed_through_unstable_modules { - match &mut stab { - Some(( - Stability { - level: StabilityLevel::Stable { allowed_through_unstable_modules, .. }, - .. - }, - _, - )) => *allowed_through_unstable_modules = true, - _ => { - sess.dcx() - .emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp }); - } - } - } - - stab -} - -/// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable` -/// attributes in `attrs`. Returns `None` if no stability attributes are found. -pub fn find_const_stability( - sess: &Session, - attrs: &[Attribute], - item_sp: Span, -) -> Option<(ConstStability, Span)> { - let mut const_stab: Option<(ConstStability, Span)> = None; - let mut promotable = false; - let mut const_stable_indirect = false; - - for attr in attrs { - match attr.name_or_empty() { - sym::rustc_promotable => promotable = true, - sym::rustc_const_stable_indirect => const_stable_indirect = true, - sym::rustc_const_unstable => { - if const_stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); - break; - } - - if let Some((feature, level)) = parse_unstability(sess, attr) { - const_stab = Some(( - ConstStability { - level, - feature, - const_stable_indirect: false, - promotable: false, - }, - attr.span, - )); - } - } - sym::rustc_const_stable => { - if const_stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); - break; - } - if let Some((feature, level)) = parse_stability(sess, attr) { - const_stab = Some(( - ConstStability { - level, - feature, - const_stable_indirect: false, - promotable: false, - }, - attr.span, - )); - } - } - _ => {} - } - } - - // Merge promotable and const_stable_indirect into stability info - if promotable { - match &mut const_stab { - Some((stab, _)) => stab.promotable = promotable, - _ => { - _ = sess - .dcx() - .emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp }) - } - } - } - if const_stable_indirect { - match &mut const_stab { - Some((stab, _)) => { - if stab.is_const_unstable() { - stab.const_stable_indirect = true; - } else { - _ = sess.dcx().emit_err(session_diagnostics::RustcConstStableIndirectPairing { - span: item_sp, - }) - } - } - _ => { - // This function has no const stability attribute, but has `const_stable_indirect`. - // We ignore that; unmarked functions are subject to recursive const stability - // checks by default so we do carry out the user's intent. - } - } - } - - const_stab -} - -/// Calculates the const stability for a const function in a `-Zforce-unstable-if-unmarked` crate -/// without the `staged_api` feature. -pub fn unmarked_crate_const_stab( - _sess: &Session, - attrs: &[Attribute], - regular_stab: Stability, -) -> ConstStability { - assert!(regular_stab.level.is_unstable()); - // The only attribute that matters here is `rustc_const_stable_indirect`. - // We enforce recursive const stability rules for those functions. - let const_stable_indirect = - attrs.iter().any(|a| a.name_or_empty() == sym::rustc_const_stable_indirect); - ConstStability { - feature: regular_stab.feature, - const_stable_indirect, - promotable: false, - level: regular_stab.level, - } -} - -/// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`. -/// Returns `None` if no stability attributes are found. -pub fn find_body_stability( - sess: &Session, - attrs: &[Attribute], -) -> Option<(DefaultBodyStability, Span)> { - let mut body_stab: Option<(DefaultBodyStability, Span)> = None; - - for attr in attrs { - if attr.has_name(sym::rustc_default_body_unstable) { - if body_stab.is_some() { - sess.dcx() - .emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span }); - break; - } - - if let Some((feature, level)) = parse_unstability(sess, attr) { - body_stab = Some((DefaultBodyStability { level, feature }, attr.span)); - } - } - } - - body_stab -} - -fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option) -> Option<()> { - if item.is_some() { - sess.dcx().emit_err(session_diagnostics::MultipleItem { - span: meta.span, - item: pprust::path_to_string(&meta.path), - }); - None - } else if let Some(v) = meta.value_str() { - *item = Some(v); - Some(()) - } else { - sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span }); - None - } -} - -/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and -/// its stability information. -fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> { - let meta = attr.meta()?; - let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None }; - - let mut feature = None; - let mut since = None; - for meta in metas { - let Some(mi) = meta.meta_item() else { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: meta.span(), - reason: UnsupportedLiteralReason::Generic, - is_bytestr: false, - start_point_span: sess.source_map().start_point(meta.span()), - }); - return None; - }; - - match mi.name_or_empty() { - sym::feature => insert_or_error(sess, mi, &mut feature)?, - sym::since => insert_or_error(sess, mi, &mut since)?, - _ => { - sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { - span: meta.span(), - item: pprust::path_to_string(&mi.path), - expected: &["feature", "since"], - }); - return None; - } - } - } - - let feature = match feature { - Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), - Some(_bad_feature) => { - Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span })) - } - None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span })), - }; - - let since = if let Some(since) = since { - if since.as_str() == VERSION_PLACEHOLDER { - StableSince::Current - } else if let Some(version) = parse_version(since) { - StableSince::Version(version) - } else { - sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span }); - StableSince::Err - } - } else { - sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span }); - StableSince::Err - }; - - match feature { - Ok(feature) => { - let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false }; - Some((feature, level)) - } - Err(ErrorGuaranteed { .. }) => None, - } -} - -/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable` -/// attribute, and return the feature name and its stability information. -fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> { - let meta = attr.meta()?; - let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None }; - - let mut feature = None; - let mut reason = None; - let mut issue = None; - let mut issue_num = None; - let mut is_soft = false; - let mut implied_by = None; - for meta in metas { - let Some(mi) = meta.meta_item() else { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: meta.span(), - reason: UnsupportedLiteralReason::Generic, - is_bytestr: false, - start_point_span: sess.source_map().start_point(meta.span()), - }); - return None; - }; - - match mi.name_or_empty() { - sym::feature => insert_or_error(sess, mi, &mut feature)?, - sym::reason => insert_or_error(sess, mi, &mut reason)?, - sym::issue => { - insert_or_error(sess, mi, &mut issue)?; - - // These unwraps are safe because `insert_or_error` ensures the meta item - // is a name/value pair string literal. - issue_num = match issue.unwrap().as_str() { - "none" => None, - issue => match issue.parse::>() { - Ok(num) => Some(num), - Err(err) => { - sess.dcx().emit_err( - session_diagnostics::InvalidIssueString { - span: mi.span, - cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind( - mi.name_value_literal_span().unwrap(), - err.kind(), - ), - }, - ); - return None; - } - }, - }; - } - sym::soft => { - if !mi.is_word() { - sess.dcx().emit_err(session_diagnostics::SoftNoArgs { span: mi.span }); - } - is_soft = true; - } - sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?, - _ => { - sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { - span: meta.span(), - item: pprust::path_to_string(&mi.path), - expected: &["feature", "reason", "issue", "soft", "implied_by"], - }); - return None; - } - } - } - - let feature = match feature { - Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), - Some(_bad_feature) => { - Err(sess.dcx().emit_err(session_diagnostics::NonIdentFeature { span: attr.span })) - } - None => Err(sess.dcx().emit_err(session_diagnostics::MissingFeature { span: attr.span })), - }; - - let issue = issue - .ok_or_else(|| sess.dcx().emit_err(session_diagnostics::MissingIssue { span: attr.span })); - - match (feature, issue) { - (Ok(feature), Ok(_)) => { - let level = StabilityLevel::Unstable { - reason: UnstableReason::from_opt_reason(reason), - issue: issue_num, - is_soft, - implied_by, - }; - Some((feature, level)) - } - (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None, - } -} - -pub fn find_crate_name(attrs: &[Attribute]) -> Option { - attr::first_attr_value_str_by_name(attrs, sym::crate_name) -} - -#[derive(Clone, Debug)] -pub struct Condition { - pub name: Symbol, - pub name_span: Span, - pub value: Option, - pub value_span: Option, - pub span: Span, -} - -/// Tests if a cfg-pattern matches the cfg set -pub fn cfg_matches( - cfg: &ast::MetaItemInner, - sess: &Session, - lint_node_id: NodeId, - features: Option<&Features>, -) -> bool { - eval_condition(cfg, sess, features, &mut |cfg| { - try_gate_cfg(cfg.name, cfg.span, sess, features); - match sess.psess.check_config.expecteds.get(&cfg.name) { - Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { - sess.psess.buffer_lint( - UNEXPECTED_CFGS, - cfg.span, - lint_node_id, - BuiltinLintDiag::UnexpectedCfgValue( - (cfg.name, cfg.name_span), - cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), - ); - } - None if sess.psess.check_config.exhaustive_names => { - sess.psess.buffer_lint( - UNEXPECTED_CFGS, - cfg.span, - lint_node_id, - BuiltinLintDiag::UnexpectedCfgName( - (cfg.name, cfg.name_span), - cfg.value.map(|v| (v, cfg.value_span.unwrap())), - ), - ); - } - _ => { /* not unexpected */ } - } - sess.psess.config.contains(&(cfg.name, cfg.value)) - }) -} - -fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { - let gate = find_gated_cfg(|sym| sym == name); - if let (Some(feats), Some(gated_cfg)) = (features, gate) { - gate_cfg(gated_cfg, span, sess, feats); - } -} - -#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable -fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) { - let (cfg, feature, has_feature) = gated_cfg; - if !has_feature(features) && !cfg_span.allows_unstable(*feature) { - let explain = format!("`cfg({cfg})` is experimental and subject to change"); - feature_err(sess, *feature, cfg_span, explain).emit(); - } -} - -/// Parse a rustc version number written inside string literal in an attribute, -/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are -/// not accepted in this position, unlike when parsing CFG_RELEASE. -pub fn parse_version(s: Symbol) -> Option { - let mut components = s.as_str().split('-'); - let d = components.next()?; - if components.next().is_some() { - return None; - } - let mut digits = d.splitn(3, '.'); - let major = digits.next()?.parse().ok()?; - let minor = digits.next()?.parse().ok()?; - let patch = digits.next().unwrap_or("0").parse().ok()?; - Some(RustcVersion { major, minor, patch }) -} - -/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to -/// evaluate individual items. -pub fn eval_condition( - cfg: &ast::MetaItemInner, - sess: &Session, - features: Option<&Features>, - eval: &mut impl FnMut(Condition) -> bool, -) -> bool { - let dcx = sess.dcx(); - - let cfg = match cfg { - ast::MetaItemInner::MetaItem(meta_item) => meta_item, - ast::MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { - if let Some(features) = features { - // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true` - // and `true`, and we want to keep the former working without feature gate - gate_cfg( - &( - if *b { kw::True } else { kw::False }, - sym::cfg_boolean_literals, - |features: &Features| features.cfg_boolean_literals(), - ), - cfg.span(), - sess, - features, - ); - } - return *b; - } - _ => { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: cfg.span(), - reason: UnsupportedLiteralReason::CfgBoolean, - is_bytestr: false, - start_point_span: sess.source_map().start_point(cfg.span()), - }); - return false; - } - }; - - match &cfg.kind { - ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { - try_gate_cfg(sym::version, cfg.span, sess, features); - let (min_version, span) = match &mis[..] { - [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { - (sym, span) - } - [ - MetaItemInner::Lit(MetaItemLit { span, .. }) - | MetaItemInner::MetaItem(MetaItem { span, .. }), - ] => { - dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span }); - return false; - } - [..] => { - dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { - span: cfg.span, - }); - return false; - } - }; - let Some(min_version) = parse_version(*min_version) else { - dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span }); - return false; - }; - - // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details - if sess.psess.assume_incomplete_release { - RustcVersion::CURRENT > min_version - } else { - RustcVersion::CURRENT >= min_version - } - } - ast::MetaItemKind::List(mis) => { - for mi in mis.iter() { - if mi.meta_item_or_bool().is_none() { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: mi.span(), - reason: UnsupportedLiteralReason::Generic, - is_bytestr: false, - start_point_span: sess.source_map().start_point(mi.span()), - }); - return false; - } - } - - // The unwraps below may look dangerous, but we've already asserted - // that they won't fail with the loop above. - match cfg.name_or_empty() { - sym::any => mis - .iter() - // We don't use any() here, because we want to evaluate all cfg condition - // as eval_condition can (and does) extra checks - .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), - sym::all => mis - .iter() - // We don't use all() here, because we want to evaluate all cfg condition - // as eval_condition can (and does) extra checks - .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), - sym::not => { - let [mi] = mis.as_slice() else { - dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); - return false; - }; - - !eval_condition(mi, sess, features, eval) - } - sym::target => { - if let Some(features) = features - && !features.cfg_target_compact() - { - feature_err( - sess, - sym::cfg_target_compact, - cfg.span, - fluent_generated::attr_unstable_cfg_target_compact, - ) - .emit(); - } - - mis.iter().fold(true, |res, mi| { - let Some(mut mi) = mi.meta_item().cloned() else { - dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { - span: mi.span(), - }); - return false; - }; - - if let [seg, ..] = &mut mi.path.segments[..] { - seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); - } - - res & eval_condition( - &ast::MetaItemInner::MetaItem(mi), - sess, - features, - eval, - ) - }) - } - _ => { - dcx.emit_err(session_diagnostics::InvalidPredicate { - span: cfg.span, - predicate: pprust::path_to_string(&cfg.path), - }); - false - } - } - } - ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { - dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); - true - } - MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { - dcx.emit_err(session_diagnostics::UnsupportedLiteral { - span: lit.span, - reason: UnsupportedLiteralReason::CfgString, - is_bytestr: lit.kind.is_bytestr(), - start_point_span: sess.source_map().start_point(lit.span), - }); - true - } - ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => { - let ident = cfg.ident().expect("multi-segment cfg predicate"); - eval(Condition { - name: ident.name, - name_span: ident.span, - value: cfg.value_str(), - value_span: cfg.name_value_literal_span(), - span: cfg.span, - }) - } - } -} - -#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] -pub struct Deprecation { - pub since: DeprecatedSince, - /// The note to issue a reason. - pub note: Option, - /// A text snippet used to completely replace any use of the deprecated item in an expression. - /// - /// This is currently unstable. - pub suggestion: Option, -} - -/// Release in which an API is deprecated. -#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] -pub enum DeprecatedSince { - RustcVersion(RustcVersion), - /// Deprecated in the future ("to be determined"). - Future, - /// `feature(staged_api)` is off. Deprecation versions outside the standard - /// library are allowed to be arbitrary strings, for better or worse. - NonStandard(Symbol), - /// Deprecation version is unspecified but optional. - Unspecified, - /// Failed to parse a deprecation version, or the deprecation version is - /// unspecified and required. An error has already been emitted. - Err, -} - -impl Deprecation { - /// Whether an item marked with #[deprecated(since = "X")] is currently - /// deprecated (i.e., whether X is not greater than the current rustc - /// version). - pub fn is_in_effect(&self) -> bool { - match self.since { - DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT, - DeprecatedSince::Future => false, - // The `since` field doesn't have semantic purpose without `#![staged_api]`. - DeprecatedSince::NonStandard(_) => true, - // Assume deprecation is in effect if "since" field is absent or invalid. - DeprecatedSince::Unspecified | DeprecatedSince::Err => true, - } - } - - pub fn is_since_rustc_version(&self) -> bool { - matches!(self.since, DeprecatedSince::RustcVersion(_)) - } -} - -/// Finds the deprecation attribute. `None` if none exists. -pub fn find_deprecation( - sess: &Session, - features: &Features, - attrs: &[Attribute], -) -> Option<(Deprecation, Span)> { - let mut depr: Option<(Deprecation, Span)> = None; - let is_rustc = features.staged_api(); - - 'outer: for attr in attrs { - if !attr.has_name(sym::deprecated) { - continue; - } - - let Some(meta) = attr.meta() else { - continue; - }; - let mut since = None; - let mut note = None; - let mut suggestion = None; - match &meta.kind { - MetaItemKind::Word => {} - MetaItemKind::NameValue(..) => note = meta.value_str(), - MetaItemKind::List(list) => { - let get = |meta: &MetaItem, item: &mut Option| { - if item.is_some() { - sess.dcx().emit_err(session_diagnostics::MultipleItem { - span: meta.span, - item: pprust::path_to_string(&meta.path), - }); - return false; - } - if let Some(v) = meta.value_str() { - *item = Some(v); - true - } else { - if let Some(lit) = meta.name_value_literal() { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: lit.span, - reason: UnsupportedLiteralReason::DeprecatedString, - is_bytestr: lit.kind.is_bytestr(), - start_point_span: sess.source_map().start_point(lit.span), - }); - } else { - sess.dcx().emit_err(session_diagnostics::IncorrectMetaItem { - span: meta.span, - }); - } - - false - } - }; - - for meta in list { - match meta { - MetaItemInner::MetaItem(mi) => match mi.name_or_empty() { - sym::since => { - if !get(mi, &mut since) { - continue 'outer; - } - } - sym::note => { - if !get(mi, &mut note) { - continue 'outer; - } - } - sym::suggestion => { - if !features.deprecated_suggestion() { - sess.dcx().emit_err( - session_diagnostics::DeprecatedItemSuggestion { - span: mi.span, - is_nightly: sess.is_nightly_build(), - details: (), - }, - ); - } - - if !get(mi, &mut suggestion) { - continue 'outer; - } - } - _ => { - sess.dcx().emit_err(session_diagnostics::UnknownMetaItem { - span: meta.span(), - item: pprust::path_to_string(&mi.path), - expected: if features.deprecated_suggestion() { - &["since", "note", "suggestion"] - } else { - &["since", "note"] - }, - }); - continue 'outer; - } - }, - MetaItemInner::Lit(lit) => { - sess.dcx().emit_err(session_diagnostics::UnsupportedLiteral { - span: lit.span, - reason: UnsupportedLiteralReason::DeprecatedKvPair, - is_bytestr: false, - start_point_span: sess.source_map().start_point(lit.span), - }); - continue 'outer; - } - } - } - } - } - - let since = if let Some(since) = since { - if since.as_str() == "TBD" { - DeprecatedSince::Future - } else if !is_rustc { - DeprecatedSince::NonStandard(since) - } else if let Some(version) = parse_version(since) { - DeprecatedSince::RustcVersion(version) - } else { - sess.dcx().emit_err(session_diagnostics::InvalidSince { span: attr.span }); - DeprecatedSince::Err - } - } else if is_rustc { - sess.dcx().emit_err(session_diagnostics::MissingSince { span: attr.span }); - DeprecatedSince::Err - } else { - DeprecatedSince::Unspecified - }; - - if is_rustc && note.is_none() { - sess.dcx().emit_err(session_diagnostics::MissingNote { span: attr.span }); - continue; - } - - depr = Some((Deprecation { since, note, suggestion }, attr.span)); - } - - depr -} - -#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)] -pub enum ReprAttr { - ReprInt(IntType), - ReprRust, - ReprC, - ReprPacked(Align), - ReprSimd, - ReprTransparent, - ReprAlign(Align), -} - -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -#[derive(Encodable, Decodable, HashStable_Generic)] -pub enum IntType { - SignedInt(ast::IntTy), - UnsignedInt(ast::UintTy), -} - -impl IntType { - #[inline] - pub fn is_signed(self) -> bool { - use IntType::*; - - match self { - SignedInt(..) => true, - UnsignedInt(..) => false, - } - } -} - -/// Parse #[repr(...)] forms. -/// -/// Valid repr contents: any of the primitive integral type names (see -/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use -/// the same discriminant size that the corresponding C enum would or C -/// structure layout, `packed` to remove padding, and `transparent` to delegate representation -/// concerns to the only non-ZST field. -pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec { - if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() } -} - -pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec { - assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}"); - use ReprAttr::*; - let mut acc = Vec::new(); - let dcx = sess.dcx(); - - if let Some(items) = attr.meta_item_list() { - for item in items { - let mut recognised = false; - if item.is_word() { - let hint = match item.name_or_empty() { - sym::Rust => Some(ReprRust), - sym::C => Some(ReprC), - sym::packed => Some(ReprPacked(Align::ONE)), - sym::simd => Some(ReprSimd), - sym::transparent => Some(ReprTransparent), - sym::align => { - sess.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg { - span: item.span(), - }); - recognised = true; - None - } - name => int_type_of_word(name).map(ReprInt), - }; - - if let Some(h) = hint { - recognised = true; - acc.push(h); - } - } else if let Some((name, value)) = item.singleton_lit_list() { - let mut literal_error = None; - let mut err_span = item.span(); - if name == sym::align { - recognised = true; - match parse_alignment(&value.kind) { - Ok(literal) => acc.push(ReprAlign(literal)), - Err(message) => { - err_span = value.span; - literal_error = Some(message) - } - }; - } else if name == sym::packed { - recognised = true; - match parse_alignment(&value.kind) { - Ok(literal) => acc.push(ReprPacked(literal)), - Err(message) => { - err_span = value.span; - literal_error = Some(message) - } - }; - } else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent) - || int_type_of_word(name).is_some() - { - recognised = true; - sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { - span: item.span(), - name: name.to_ident_string(), - }); - } - if let Some(literal_error) = literal_error { - sess.dcx().emit_err(session_diagnostics::InvalidReprGeneric { - span: err_span, - repr_arg: name.to_ident_string(), - error_part: literal_error, - }); - } - } else if let Some(meta_item) = item.meta_item() { - match &meta_item.kind { - MetaItemKind::NameValue(value) => { - if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) { - let name = meta_item.name_or_empty().to_ident_string(); - recognised = true; - sess.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric { - span: item.span(), - repr_arg: &name, - cause: IncorrectReprFormatGenericCause::from_lit_kind( - item.span(), - &value.kind, - &name, - ), - }); - } else if matches!( - meta_item.name_or_empty(), - sym::Rust | sym::C | sym::simd | sym::transparent - ) || int_type_of_word(meta_item.name_or_empty()).is_some() - { - recognised = true; - sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue { - span: meta_item.span, - name: meta_item.name_or_empty().to_ident_string(), - }); - } - } - MetaItemKind::List(nested_items) => { - if meta_item.has_name(sym::align) { - recognised = true; - if let [nested_item] = nested_items.as_slice() { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatExpectInteger { - span: nested_item.span(), - }, - ); - } else { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatAlignOneArg { - span: meta_item.span, - }, - ); - } - } else if meta_item.has_name(sym::packed) { - recognised = true; - if let [nested_item] = nested_items.as_slice() { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatPackedExpectInteger { - span: nested_item.span(), - }, - ); - } else { - sess.dcx().emit_err( - session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg { - span: meta_item.span, - }, - ); - } - } else if matches!( - meta_item.name_or_empty(), - sym::Rust | sym::C | sym::simd | sym::transparent - ) || int_type_of_word(meta_item.name_or_empty()).is_some() - { - recognised = true; - sess.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { - span: meta_item.span, - name: meta_item.name_or_empty().to_ident_string(), - }); - } - } - _ => (), - } - } - if !recognised { - // Not a word we recognize. This will be caught and reported by - // the `check_mod_attrs` pass, but this pass doesn't always run - // (e.g. if we only pretty-print the source), so we have to gate - // the `span_delayed_bug` call as follows: - if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) { - dcx.span_delayed_bug(item.span(), "unrecognized representation hint"); - } - } - } - } - acc -} - -fn int_type_of_word(s: Symbol) -> Option { - use IntType::*; - - match s { - sym::i8 => Some(SignedInt(ast::IntTy::I8)), - sym::u8 => Some(UnsignedInt(ast::UintTy::U8)), - sym::i16 => Some(SignedInt(ast::IntTy::I16)), - sym::u16 => Some(UnsignedInt(ast::UintTy::U16)), - sym::i32 => Some(SignedInt(ast::IntTy::I32)), - sym::u32 => Some(UnsignedInt(ast::UintTy::U32)), - sym::i64 => Some(SignedInt(ast::IntTy::I64)), - sym::u64 => Some(UnsignedInt(ast::UintTy::U64)), - sym::i128 => Some(SignedInt(ast::IntTy::I128)), - sym::u128 => Some(UnsignedInt(ast::UintTy::U128)), - sym::isize => Some(SignedInt(ast::IntTy::Isize)), - sym::usize => Some(UnsignedInt(ast::UintTy::Usize)), - _ => None, - } -} - -pub enum TransparencyError { - UnknownTransparency(Symbol, Span), - MultipleTransparencyAttrs(Span, Span), -} - -pub fn find_transparency( - attrs: &[Attribute], - macro_rules: bool, -) -> (Transparency, Option) { - let mut transparency = None; - let mut error = None; - for attr in attrs { - if attr.has_name(sym::rustc_macro_transparency) { - if let Some((_, old_span)) = transparency { - error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span)); - break; - } else if let Some(value) = attr.value_str() { - transparency = Some(( - match value { - sym::transparent => Transparency::Transparent, - sym::semitransparent => Transparency::SemiTransparent, - sym::opaque => Transparency::Opaque, - _ => { - error = Some(TransparencyError::UnknownTransparency(value, attr.span)); - continue; - } - }, - attr.span, - )); - } - } - } - let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque }; - (transparency.map_or(fallback, |t| t.0), error) -} - -pub fn allow_internal_unstable<'a>( - sess: &'a Session, - attrs: &'a [Attribute], -) -> impl Iterator + 'a { - allow_unstable(sess, attrs, sym::allow_internal_unstable) -} - -pub fn rustc_allow_const_fn_unstable<'a>( - sess: &'a Session, - attrs: &'a [Attribute], -) -> impl Iterator + 'a { - allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable) -} - -fn allow_unstable<'a>( - sess: &'a Session, - attrs: &'a [Attribute], - symbol: Symbol, -) -> impl Iterator + 'a { - let attrs = attr::filter_by_name(attrs, symbol); - let list = attrs - .filter_map(move |attr| { - attr.meta_item_list().or_else(|| { - sess.dcx().emit_err(session_diagnostics::ExpectsFeatureList { - span: attr.span, - name: symbol.to_ident_string(), - }); - None - }) - }) - .flatten(); - - list.into_iter().filter_map(move |it| { - let name = it.ident().map(|ident| ident.name); - if name.is_none() { - sess.dcx().emit_err(session_diagnostics::ExpectsFeatures { - span: it.span(), - name: symbol.to_ident_string(), - }); - } - name - }) -} - -pub fn parse_alignment(node: &ast::LitKind) -> Result { - if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node { - // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first - if literal.get().is_power_of_two() { - // Only possible error is larger than 2^29 - literal - .get() - .try_into() - .ok() - .and_then(|v| Align::from_bytes(v).ok()) - .ok_or("larger than 2^29") - } else { - Err("not a power of two") - } - } else { - Err("not an unsuffixed integer") - } -} - -/// Read the content of a `rustc_confusables` attribute, and return the list of candidate names. -pub fn parse_confusables(attr: &Attribute) -> Option> { - let meta = attr.meta()?; - let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None }; - - let mut candidates = Vec::new(); - - for meta in metas { - let MetaItemInner::Lit(meta_lit) = meta else { - return None; - }; - candidates.push(meta_lit.symbol); - } - - Some(candidates) -} diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs deleted file mode 100644 index bb207c5c9526c..0000000000000 --- a/compiler/rustc_attr/src/lib.rs +++ /dev/null @@ -1,25 +0,0 @@ -//! Functions and types dealing with attributes and meta items. -//! -//! FIXME(Centril): For now being, much of the logic is still in `rustc_ast::attr`. -//! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` -//! to this crate. - -// tidy-alphabetical-start -#![allow(internal_features)] -#![doc(rust_logo)] -#![feature(let_chains)] -#![feature(rustdoc_internals)] -#![warn(unreachable_pub)] -// tidy-alphabetical-end - -mod builtin; -mod session_diagnostics; - -pub use IntType::*; -pub use ReprAttr::*; -pub use StabilityLevel::*; -pub use builtin::*; -pub use rustc_ast::attr::*; -pub(crate) use rustc_session::HashStableContext; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_data_structures/Cargo.toml b/compiler/rustc_attr_data_structures/Cargo.toml new file mode 100644 index 0000000000000..28aafcfcf6fa6 --- /dev/null +++ b/compiler/rustc_attr_data_structures/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rustc_attr_data_structures" +version = "0.0.0" +edition = "2021" + +[dependencies] +# tidy-alphabetical-start +rustc_abi = {path = "../rustc_abi"} +rustc_ast = {path = "../rustc_ast"} +rustc_data_structures = {path = "../rustc_data_structures"} +rustc_macros = {path = "../rustc_macros"} +rustc_serialize = {path = "../rustc_serialize"} +rustc_span = {path = "../rustc_span"} +thin-vec = "0.2.12" +# tidy-alphabetical-end diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs new file mode 100644 index 0000000000000..c6a4f676ccf63 --- /dev/null +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -0,0 +1,234 @@ +use rustc_abi::Align; +use rustc_ast::token::CommentKind; +use rustc_ast::{self as ast, AttrStyle}; +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +use rustc_span::hygiene::Transparency; +use rustc_span::{Span, Symbol}; +use thin_vec::ThinVec; + +use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability}; + +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +pub enum InlineAttr { + None, + Hint, + Always, + Never, +} + +#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)] +pub enum InstructionSetAttr { + ArmA32, + ArmT32, +} + +#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] +pub enum OptimizeAttr { + None, + Speed, + Size, +} + +#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)] +pub enum DiagnosticAttribute { + // tidy-alphabetical-start + DoNotRecommend, + OnUnimplemented, + // tidy-alphabetical-end +} + +#[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone, HashStable_Generic)] +pub enum ReprAttr { + ReprInt(IntType), + ReprRust, + ReprC, + ReprPacked(Align), + ReprSimd, + ReprTransparent, + ReprAlign(Align), +} +pub use ReprAttr::*; + +pub enum TransparencyError { + UnknownTransparency(Symbol, Span), + MultipleTransparencyAttrs(Span, Span), +} + +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +#[derive(Encodable, Decodable, HashStable_Generic)] +pub enum IntType { + SignedInt(ast::IntTy), + UnsignedInt(ast::UintTy), +} + +#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] +pub struct Deprecation { + pub since: DeprecatedSince, + /// The note to issue a reason. + pub note: Option, + /// A text snippet used to completely replace any use of the deprecated item in an expression. + /// + /// This is currently unstable. + pub suggestion: Option, +} + +/// Release in which an API is deprecated. +#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)] +pub enum DeprecatedSince { + RustcVersion(RustcVersion), + /// Deprecated in the future ("to be determined"). + Future, + /// `feature(staged_api)` is off. Deprecation versions outside the standard + /// library are allowed to be arbitrary strings, for better or worse. + NonStandard(Symbol), + /// Deprecation version is unspecified but optional. + Unspecified, + /// Failed to parse a deprecation version, or the deprecation version is + /// unspecified and required. An error has already been emitted. + Err, +} + +impl Deprecation { + /// Whether an item marked with #[deprecated(since = "X")] is currently + /// deprecated (i.e., whether X is not greater than the current rustc + /// version). + pub fn is_in_effect(&self) -> bool { + match self.since { + DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT, + DeprecatedSince::Future => false, + // The `since` field doesn't have semantic purpose without `#![staged_api]`. + DeprecatedSince::NonStandard(_) => true, + // Assume deprecation is in effect if "since" field is absent or invalid. + DeprecatedSince::Unspecified | DeprecatedSince::Err => true, + } + } + + pub fn is_since_rustc_version(&self) -> bool { + matches!(self.since, DeprecatedSince::RustcVersion(_)) + } +} + +// TODO: improve these docs +/// Attributes represent parsed, *built in* attributes. That means, +/// attributes that are not actually ever expanded. They're instead used as markers, +/// to guide the compilation process in various way in most every stage of the compiler. +/// These are kept around after the AST, into the HIR and further on. +/// +/// The word parsed could be a little misleading here, because the parser already parses +/// attributes early on. However, the result, an [`ast::Attribute`] +/// is only parsed at a high level, still containing a token stream in many cases. That is +/// because the structure of the contents varies from attribute to attribute. +/// With a parsed attribute I mean that each attribute is processed individually into a +/// final structure, which on-site (the place where the attribute is useful for, think the +/// the place where `must_use` is checked) little to no extra parsing or validating needs to +/// happen. +/// +/// For more docs, look in [`rustc_attr`](https://doc.rust-lang.org/stable/nightly-rustc/rustc_attr/index.html) +// FIXME(jdonszelmann): rename to AttributeKind once hir::AttributeKind is dissolved +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)] +pub enum AttributeKind { + // tidy-alphabetical-start + Allow, + AllowConstFnUnstable(ThinVec), + AllowInternalUnsafe, + AllowInternalUnstable(ThinVec), + AutoDiff, + AutomaticallyDerived, + BodyStability { + stability: DefaultBodyStability, + /// Span of the `#[rustc_default_body_unstable(...)]` attribute + span: Span, + }, + Cfg, + CfgAttr, + CfiEncoding, // FIXME(cfi_encoding) + Cold, + CollapseDebuginfo, + Confusables { + symbols: ThinVec, + // FIXME(jdonszelmann): remove when target validation code is moved + first_span: Span, + }, + ConstStability { + stability: PartialConstStability, + /// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute + span: Span, + }, + ConstStabilityIndirect, + ConstTrait, + Coroutine, + Coverage, + CustomMir, + DebuggerVisualizer, + DefaultLibAllocator, + Deny, + DeprecatedSafe, // FIXME(deprecated_safe) + Deprecation { + deprecation: Deprecation, + span: Span, + }, + Diagnostic(DiagnosticAttribute), + Doc, + /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`). + /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal` + /// variant (which is much less compact and thus more expensive). + DocComment { + style: AttrStyle, + kind: CommentKind, + span: Span, + comment: Symbol, + }, + Expect, + ExportName, + FfiConst, + FfiPure, + Forbid, + Fundamental, + Ignore, + // TODO: must contain span for clippy + Inline, + InstructionSet, // broken on stable!!! + Lang, + Link, + Linkage, + LinkName, + LinkOrdinal, + LinkSection, + MacroExport, + MacroTransparency(Transparency), + MacroUse, + Marker, + MayDangle, + MustNotSuspend, + MustUse, + NeedsAllocator, + NoImplicitPrelude, + NoLink, + NoMangle, + NonExhaustive, + NoSanitize, + OmitGdbPrettyPrinterSection, // FIXME(omit_gdb_pretty_printer_section) + PanicHandler, + PatchableFunctionEntry, // FIXME(patchable_function_entry) + Path, + Pointee, // FIXME(derive_smart_pointer) + PreludeImport, + ProcMacro, + ProcMacroAttribute, + ProcMacroDerive, + Repr(ThinVec), + Stability { + stability: Stability, + /// Span of the `#[stable(...)]` or `#[unstable(...)]` attribute + span: Span, + }, + Start, + TargetFeature, + ThreadLocal, + TrackCaller, + Unstable, + Used, + Warn, + WindowsSubsystem, // broken on stable!!! + // tidy-alphabetical-end +} diff --git a/compiler/rustc_attr_data_structures/src/lib.rs b/compiler/rustc_attr_data_structures/src/lib.rs new file mode 100644 index 0000000000000..d779103ab2f63 --- /dev/null +++ b/compiler/rustc_attr_data_structures/src/lib.rs @@ -0,0 +1,42 @@ +//! Centralized logic for parsing and validating all attributes used after HIR. +//! +//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229). +//! There used to be only one definition of attributes in the compiler: `ast::Attribute`. +//! These were then parsed or validated or both in places distributed all over the compiler. +//! +//! Attributes are markers on items. Most are actually attribute-like proc-macros, and are expanded +//! but some remain as the built-in attributes to guide compilation. +//! +//! In this crate, syntactical attributes (sequences of tokens that look like +//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items. +//! Multiple syntactic attributes might influence a single semantic attribute. For example, +//! `#[stable(...)]` and `#[unstable()]` cannot occur together, and both semantically define +//! a "stability". Stability defines an [`AttributeExtractor`](attributes::AttributeExtractor) +//! that recognizes both `#[stable()]` and `#[unstable()]` syntactic attributes, and at the end +//! produce a single [`ParsedAttributeKind::Stability`]. +//! +//! FIXME(jdonszelmann): update devguide for best practices on attributes +//! FIXME(jdonszelmann): rename to `rustc_attr` in the future, integrating it into this crate. +//! +//! To define a new builtin, first add it + +// tidy-alphabetical-start +#![allow(internal_features)] +#![doc(rust_logo)] +#![feature(let_chains)] +#![feature(rustdoc_internals)] +#![warn(unreachable_pub)] +// tidy-alphabetical-end + +mod attributes; +mod stability; +mod version; + +pub use attributes::*; +pub use stability::*; +pub use version::*; + +/// Requirements for a `StableHashingContext` to be used in this crate. +/// This is a hack to allow using the `HashStable_Generic` derive macro +/// instead of implementing everything in `rustc_middle`. +pub trait HashStableContext: rustc_ast::HashStableContext + rustc_abi::HashStableContext {} diff --git a/compiler/rustc_attr_data_structures/src/stability.rs b/compiler/rustc_attr_data_structures/src/stability.rs new file mode 100644 index 0000000000000..021fe40e3e04c --- /dev/null +++ b/compiler/rustc_attr_data_structures/src/stability.rs @@ -0,0 +1,200 @@ +use std::num::NonZero; + +use rustc_macros::{Decodable, Encodable, HashStable_Generic}; +use rustc_span::{Symbol, sym}; + +use crate::RustcVersion; + +/// The version placeholder that recently stabilized features contain inside the +/// `since` field of the `#[stable]` attribute. +/// +/// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591). +pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION"; + +/// Represents the following attributes: +/// +/// - `#[stable]` +/// - `#[unstable]` +#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] +pub struct Stability { + pub level: StabilityLevel, + pub feature: Symbol, +} + +impl Stability { + pub fn is_unstable(&self) -> bool { + self.level.is_unstable() + } + + pub fn is_stable(&self) -> bool { + self.level.is_stable() + } + + pub fn stable_since(&self) -> Option { + self.level.stable_since() + } +} + +/// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes. +#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] +pub struct ConstStability { + pub level: StabilityLevel, + pub feature: Symbol, + /// whether the function has a `#[rustc_promotable]` attribute + pub promotable: bool, + /// This is true iff the `const_stable_indirect` attribute is present. + pub const_stable_indirect: bool, +} + +impl ConstStability { + pub fn from_partial( + PartialConstStability { level, feature, promotable }: PartialConstStability, + const_stable_indirect: bool, + ) -> Self { + Self { const_stable_indirect, level, feature, promotable } + } + + /// The stability assigned to unmarked items when -Zforce-unstable-if-unmarked is set. + pub fn unmarked(const_stable_indirect: bool, regular_stab: Stability) -> Self { + Self { + feature: regular_stab.feature, + promotable: false, + level: regular_stab.level, + const_stable_indirect, + } + } + + pub fn is_const_unstable(&self) -> bool { + self.level.is_unstable() + } + + pub fn is_const_stable(&self) -> bool { + self.level.is_stable() + } +} + +/// Excludes `const_stable_indirect`. This is necessary because when `-Zforce-unstable-if-unmarked` +/// is set, we need to encode standalone `#[rustc_const_stable_indirect]` attributes +#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] +pub struct PartialConstStability { + pub level: StabilityLevel, + pub feature: Symbol, + /// whether the function has a `#[rustc_promotable]` attribute + pub promotable: bool, +} + +impl PartialConstStability { + pub fn is_const_unstable(&self) -> bool { + self.level.is_unstable() + } + + pub fn is_const_stable(&self) -> bool { + self.level.is_stable() + } +} + +/// The available stability levels. +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] +pub enum StabilityLevel { + /// `#[unstable]` + Unstable { + /// Reason for the current stability level. + reason: UnstableReason, + /// Relevant `rust-lang/rust` issue. + issue: Option>, + is_soft: bool, + /// If part of a feature is stabilized and a new feature is added for the remaining parts, + /// then the `implied_by` attribute is used to indicate which now-stable feature previously + /// contained an item. + /// + /// ```pseudo-Rust + /// #[unstable(feature = "foo", issue = "...")] + /// fn foo() {} + /// #[unstable(feature = "foo", issue = "...")] + /// fn foobar() {} + /// ``` + /// + /// ...becomes... + /// + /// ```pseudo-Rust + /// #[stable(feature = "foo", since = "1.XX.X")] + /// fn foo() {} + /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")] + /// fn foobar() {} + /// ``` + implied_by: Option, + }, + /// `#[stable]` + Stable { + /// Rust release which stabilized this feature. + since: StableSince, + /// Is this item allowed to be referred to on stable, despite being contained in unstable + /// modules? + allowed_through_unstable_modules: bool, + }, +} + +/// Rust release in which a feature is stabilized. +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, PartialOrd, Ord, Hash)] +#[derive(HashStable_Generic)] +pub enum StableSince { + /// also stores the original symbol for printing + Version(RustcVersion), + /// Stabilized in the upcoming version, whatever number that is. + Current, + /// Failed to parse a stabilization version. + Err, +} + +impl StabilityLevel { + pub fn is_unstable(&self) -> bool { + matches!(self, StabilityLevel::Unstable { .. }) + } + pub fn is_stable(&self) -> bool { + matches!(self, StabilityLevel::Stable { .. }) + } + pub fn stable_since(&self) -> Option { + match *self { + StabilityLevel::Stable { since, .. } => Some(since), + StabilityLevel::Unstable { .. } => None, + } + } +} + +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] +#[derive(HashStable_Generic)] +pub enum UnstableReason { + None, + Default, + Some(Symbol), +} + +/// Represents the `#[rustc_default_body_unstable]` attribute. +#[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(HashStable_Generic)] +pub struct DefaultBodyStability { + pub level: StabilityLevel, + pub feature: Symbol, +} + +impl UnstableReason { + pub fn from_opt_reason(reason: Option) -> Self { + // UnstableReason::Default constructed manually + match reason { + Some(r) => Self::Some(r), + None => Self::None, + } + } + + pub fn to_opt_reason(&self) -> Option { + match self { + Self::None => None, + Self::Default => Some(sym::unstable_location_reason_default), + Self::Some(r) => Some(*r), + } + } +} diff --git a/compiler/rustc_session/src/version.rs b/compiler/rustc_attr_data_structures/src/version.rs similarity index 72% rename from compiler/rustc_session/src/version.rs rename to compiler/rustc_attr_data_structures/src/version.rs index 1696eaf902b0d..6be875ad4be86 100644 --- a/compiler/rustc_session/src/version.rs +++ b/compiler/rustc_attr_data_structures/src/version.rs @@ -1,7 +1,5 @@ -use std::borrow::Cow; use std::fmt::{self, Display}; -use rustc_errors::IntoDiagArg; use rustc_macros::{Decodable, Encodable, HashStable_Generic, current_rustc_version}; #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -21,9 +19,3 @@ impl Display for RustcVersion { write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch) } } - -impl IntoDiagArg for RustcVersion { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { - rustc_errors::DiagArgValue::Str(Cow::Owned(self.to_string())) - } -} diff --git a/compiler/rustc_attr/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml similarity index 80% rename from compiler/rustc_attr/Cargo.toml rename to compiler/rustc_attr_parsing/Cargo.toml index 3b24452450abe..adbfd588fe565 100644 --- a/compiler/rustc_attr/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rustc_attr" +name = "rustc_attr_parsing" version = "0.0.0" edition = "2021" @@ -8,13 +8,16 @@ edition = "2021" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } +rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +thin-vec = "0.2.12" # tidy-alphabetical-end diff --git a/compiler/rustc_attr/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl similarity index 55% rename from compiler/rustc_attr/messages.ftl rename to compiler/rustc_attr_parsing/messages.ftl index 235ab7572c419..11f412a224d0d 100644 --- a/compiler/rustc_attr/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -1,46 +1,48 @@ -attr_cfg_predicate_identifier = +attr_parsing_cfg_predicate_identifier = `cfg` predicate key must be an identifier -attr_deprecated_item_suggestion = +attr_parsing_deprecated_item_suggestion = suggestions on deprecated items are unstable .help = add `#![feature(deprecated_suggestion)]` to the crate root .note = see #94785 for more details -attr_expected_one_cfg_pattern = +attr_parsing_empty_confusables = + expected at least one confusable name +attr_parsing_expected_one_cfg_pattern = expected 1 cfg-pattern -attr_expected_single_version_literal = +attr_parsing_expected_single_version_literal = expected single version literal -attr_expected_version_literal = +attr_parsing_expected_version_literal = expected a version literal -attr_expects_feature_list = +attr_parsing_expects_feature_list = `{$name}` expects a list of feature names -attr_expects_features = +attr_parsing_expects_features = `{$name}` expects feature names -attr_incorrect_meta_item = - incorrect meta item +attr_parsing_incorrect_meta_item = expected a quoted string literal +attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes -attr_incorrect_repr_format_align_one_arg = +attr_parsing_incorrect_repr_format_align_one_arg = incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses -attr_incorrect_repr_format_expect_literal_integer = +attr_parsing_incorrect_repr_format_expect_literal_integer = incorrect `repr(align)` attribute format: `align` expects a literal integer as argument -attr_incorrect_repr_format_generic = +attr_parsing_incorrect_repr_format_generic = incorrect `repr({$repr_arg})` attribute format .suggestion = use parentheses instead -attr_incorrect_repr_format_packed_expect_integer = +attr_parsing_incorrect_repr_format_packed_expect_integer = incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument -attr_incorrect_repr_format_packed_one_or_zero_arg = +attr_parsing_incorrect_repr_format_packed_one_or_zero_arg = incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all -attr_invalid_issue_string = +attr_parsing_invalid_issue_string = `issue` must be a non-zero numeric string or "none" .must_not_be_zero = `issue` must not be "0", use "none" instead .empty = cannot parse integer from empty string @@ -48,77 +50,81 @@ attr_invalid_issue_string = .pos_overflow = number too large to fit in target type .neg_overflow = number too small to fit in target type -attr_invalid_predicate = +attr_parsing_invalid_predicate = invalid predicate `{$predicate}` -attr_invalid_repr_align_need_arg = +attr_parsing_invalid_repr_align_need_arg = invalid `repr(align)` attribute: `align` needs an argument .suggestion = supply an argument here -attr_invalid_repr_generic = +attr_parsing_invalid_repr_generic = invalid `repr({$repr_arg})` attribute: {$error_part} -attr_invalid_repr_hint_no_paren = +attr_parsing_invalid_repr_hint_no_paren = invalid representation hint: `{$name}` does not take a parenthesized argument list -attr_invalid_repr_hint_no_value = +attr_parsing_invalid_repr_hint_no_value = invalid representation hint: `{$name}` does not take a value -attr_invalid_since = +attr_parsing_invalid_since = 'since' must be a Rust version number, such as "1.31.0" -attr_missing_feature = +attr_parsing_missing_feature = missing 'feature' -attr_missing_issue = +attr_parsing_missing_issue = missing 'issue' -attr_missing_note = +attr_parsing_missing_note = missing 'note' -attr_missing_since = +attr_parsing_missing_since = missing 'since' -attr_multiple_item = +attr_parsing_multiple_item = multiple '{$item}' items -attr_multiple_stability_levels = +attr_parsing_multiple_stability_levels = multiple stability levels -attr_non_ident_feature = +attr_parsing_non_ident_feature = 'feature' is not an identifier -attr_rustc_allowed_unstable_pairing = +attr_parsing_rustc_allowed_unstable_pairing = `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute -attr_rustc_const_stable_indirect_pairing = - `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied - -attr_rustc_promotable_pairing = +attr_parsing_rustc_promotable_pairing = `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute -attr_soft_no_args = +attr_parsing_soft_no_args = `soft` should not have any arguments -attr_unknown_meta_item = +attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library + +attr_parsing_unknown_meta_item = unknown meta item '{$item}' .label = expected one of {$expected} -attr_unknown_version_literal = +attr_parsing_unknown_version_literal = unknown version literal format, assuming it refers to a future version -attr_unstable_cfg_target_compact = +attr_parsing_unstable_cfg_target_compact = compact `cfg(target(..))` is experimental and subject to change -attr_unsupported_literal_cfg_boolean = +attr_parsing_unsupported_literal_cfg_boolean = literal in `cfg` predicate value must be a boolean -attr_unsupported_literal_cfg_string = +attr_parsing_unsupported_literal_cfg_string = literal in `cfg` predicate value must be a string -attr_unsupported_literal_deprecated_kv_pair = +attr_parsing_unsupported_literal_deprecated_kv_pair = item in `deprecated` must be a key/value pair -attr_unsupported_literal_deprecated_string = +attr_parsing_unsupported_literal_deprecated_string = literal in `deprecated` value must be a string -attr_unsupported_literal_generic = +attr_parsing_unsupported_literal_generic = unsupported literal -attr_unsupported_literal_suggestion = +attr_parsing_unsupported_literal_suggestion = consider removing the prefix + +attr_parsing_unused_multiple = + multiple `{$name}` attributes + .suggestion = remove this attribute + .note = attribute also specified here diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs new file mode 100644 index 0000000000000..388a507678ad3 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs @@ -0,0 +1,65 @@ +use rustc_attr_data_structures::AttributeKind; +use rustc_span::{Symbol, sym}; + +use super::{CombineAttributeGroup, ConvertFn}; +use crate::context::AttributeAcceptContext; +use crate::parser::{ArgParser, GenericArgParser, MetaItemParser}; +use crate::session_diagnostics; + +pub(crate) struct AllowInternalUnstableGroup; +impl CombineAttributeGroup for AllowInternalUnstableGroup { + const PATH: &'static [rustc_span::Symbol] = &[sym::allow_internal_unstable]; + type Item = Symbol; + const CONVERT: ConvertFn = AttributeKind::AllowInternalUnstable; + + fn extend<'a>( + cx: &'a AttributeAcceptContext<'a>, + args: &'a GenericArgParser<'a, rustc_ast::Expr>, + ) -> impl IntoIterator + 'a { + parse_unstable(cx, args, Self::PATH[0]) + } +} + +pub(crate) struct AllowConstFnUnstableGroup; +impl CombineAttributeGroup for AllowConstFnUnstableGroup { + const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_allow_const_fn_unstable]; + type Item = Symbol; + const CONVERT: ConvertFn = AttributeKind::AllowConstFnUnstable; + + fn extend<'a>( + cx: &'a AttributeAcceptContext<'a>, + args: &'a GenericArgParser<'a, rustc_ast::Expr>, + ) -> impl IntoIterator + 'a { + parse_unstable(cx, args, Self::PATH[0]) + } +} + +fn parse_unstable<'a>( + cx: &AttributeAcceptContext<'_>, + args: &'a impl ArgParser<'a>, + symbol: Symbol, +) -> impl IntoIterator { + let mut res = Vec::new(); + + let Some(list) = args.list() else { + cx.dcx().emit_err(session_diagnostics::ExpectsFeatureList { + span: cx.attr_span, + name: symbol.to_ident_string(), + }); + return res; + }; + + for param in list.mixed() { + let param_span = param.span(); + if let Some(ident) = param.meta_item().and_then(|i| i.word_without_args()) { + res.push(ident.name); + } else { + cx.dcx().emit_err(session_diagnostics::ExpectsFeatures { + span: param_span, + name: symbol.to_ident_string(), + }); + } + } + + res +} diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs new file mode 100644 index 0000000000000..d033e4523c48a --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -0,0 +1,249 @@ +// TODO: convert cfg properly.... And learn how cfg works I guess +use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId}; +use rustc_ast_pretty::pprust; +use rustc_feature::{Features, GatedCfg, find_gated_cfg}; +use rustc_attr_data_structures::RustcVersion; +use rustc_session::Session; +use rustc_session::config::ExpectedValues; +use rustc_session::lint::BuiltinLintDiag; +use rustc_session::lint::builtin::UNEXPECTED_CFGS; +use rustc_session::parse::feature_err; +use rustc_span::symbol::kw; +use rustc_span::{Span, Symbol, sym}; + +use super::util::parse_version; +use crate::session_diagnostics::UnsupportedLiteralReason; +use crate::{fluent_generated, session_diagnostics}; + +#[derive(Clone, Debug)] +pub struct Condition { + pub name: Symbol, + pub name_span: Span, + pub value: Option, + pub value_span: Option, + pub span: Span, +} + +/// Tests if a cfg-pattern matches the cfg set +pub fn cfg_matches( + cfg: &MetaItemInner, + sess: &Session, + lint_node_id: NodeId, + features: Option<&Features>, +) -> bool { + eval_condition(cfg, sess, features, &mut |cfg| { + try_gate_cfg(cfg.name, cfg.span, sess, features); + match sess.psess.check_config.expecteds.get(&cfg.name) { + Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => { + sess.psess.buffer_lint( + UNEXPECTED_CFGS, + cfg.span, + lint_node_id, + BuiltinLintDiag::UnexpectedCfgValue( + (cfg.name, cfg.name_span), + cfg.value.map(|v| (v, cfg.value_span.unwrap())), + ), + ); + } + None if sess.psess.check_config.exhaustive_names => { + sess.psess.buffer_lint( + UNEXPECTED_CFGS, + cfg.span, + lint_node_id, + BuiltinLintDiag::UnexpectedCfgName( + (cfg.name, cfg.name_span), + cfg.value.map(|v| (v, cfg.value_span.unwrap())), + ), + ); + } + _ => { /* not unexpected */ } + } + sess.psess.config.contains(&(cfg.name, cfg.value)) + }) +} + +fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) { + let gate = find_gated_cfg(|sym| sym == name); + if let (Some(feats), Some(gated_cfg)) = (features, gate) { + gate_cfg(gated_cfg, span, sess, feats); + } +} + +#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable +fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) { + let (cfg, feature, has_feature) = gated_cfg; + if !has_feature(features) && !cfg_span.allows_unstable(*feature) { + let explain = format!("`cfg({cfg})` is experimental and subject to change"); + feature_err(sess, *feature, cfg_span, explain).emit(); + } +} + +/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to +/// evaluate individual items. +pub fn eval_condition( + cfg: &MetaItemInner, + sess: &Session, + features: Option<&Features>, + eval: &mut impl FnMut(Condition) -> bool, +) -> bool { + let dcx = sess.dcx(); + + let cfg = match cfg { + MetaItemInner::MetaItem(meta_item) => meta_item, + MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => { + if let Some(features) = features { + // we can't use `try_gate_cfg` as symbols don't differentiate between `r#true` + // and `true`, and we want to keep the former working without feature gate + gate_cfg( + &( + if *b { kw::True } else { kw::False }, + sym::cfg_boolean_literals, + |features: &Features| features.cfg_boolean_literals(), + ), + cfg.span(), + sess, + features, + ); + } + return *b; + } + _ => { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: cfg.span(), + reason: UnsupportedLiteralReason::CfgBoolean, + is_bytestr: false, + start_point_span: sess.source_map().start_point(cfg.span()), + }); + return false; + } + }; + + match &cfg.kind { + MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { + try_gate_cfg(sym::version, cfg.span, sess, features); + let (min_version, span) = match &mis[..] { + [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { + (sym, span) + } + [ + MetaItemInner::Lit(MetaItemLit { span, .. }) + | MetaItemInner::MetaItem(MetaItem { span, .. }), + ] => { + dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span }); + return false; + } + [..] => { + dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { + span: cfg.span, + }); + return false; + } + }; + let Some(min_version) = parse_version(*min_version) else { + dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span }); + return false; + }; + + // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details + if sess.psess.assume_incomplete_release { + RustcVersion::CURRENT > min_version + } else { + RustcVersion::CURRENT >= min_version + } + } + MetaItemKind::List(mis) => { + for mi in mis.iter() { + if mi.meta_item_or_bool().is_none() { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: mi.span(), + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: sess.source_map().start_point(mi.span()), + }); + return false; + } + } + + // The unwraps below may look dangerous, but we've already asserted + // that they won't fail with the loop above. + match cfg.name_or_empty() { + sym::any => mis + .iter() + // We don't use any() here, because we want to evaluate all cfg condition + // as eval_condition can (and does) extra checks + .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), + sym::all => mis + .iter() + // We don't use all() here, because we want to evaluate all cfg condition + // as eval_condition can (and does) extra checks + .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), + sym::not => { + let [mi] = mis.as_slice() else { + dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); + return false; + }; + + !eval_condition(mi, sess, features, eval) + } + sym::target => { + if let Some(features) = features + && !features.cfg_target_compact() + { + feature_err( + sess, + sym::cfg_target_compact, + cfg.span, + fluent_generated::attr_parsing_unstable_cfg_target_compact, + ) + .emit(); + } + + mis.iter().fold(true, |res, mi| { + let Some(mut mi) = mi.meta_item().cloned() else { + dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { + span: mi.span(), + }); + return false; + }; + + if let [seg, ..] = &mut mi.path.segments[..] { + seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); + } + + res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval) + }) + } + _ => { + dcx.emit_err(session_diagnostics::InvalidPredicate { + span: cfg.span, + predicate: pprust::path_to_string(&cfg.path), + }); + false + } + } + } + MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { + dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); + true + } + MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { + dcx.emit_err(session_diagnostics::UnsupportedLiteral { + span: lit.span, + reason: UnsupportedLiteralReason::CfgString, + is_bytestr: lit.kind.is_bytestr(), + start_point_span: sess.source_map().start_point(lit.span), + }); + true + } + MetaItemKind::Word | MetaItemKind::NameValue(..) => { + let ident = cfg.ident().expect("multi-segment cfg predicate"); + eval(Condition { + name: ident.name, + name_span: ident.span, + value: cfg.value_str(), + value_span: cfg.name_value_literal_span(), + span: cfg.span, + }) + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs new file mode 100644 index 0000000000000..3848aed25ec48 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -0,0 +1,60 @@ +use rustc_attr_data_structures::AttributeKind; +use rustc_span::{Span, Symbol, sym}; +use thin_vec::ThinVec; + +use super::{AttributeGroup, AttributeMapping}; +use crate::context::AttributeGroupContext; +use crate::parser::ArgParser; +use crate::session_diagnostics; + +// TODO: turn into CombineGroup? +#[derive(Default)] +pub(crate) struct ConfusablesGroup { + confusables: ThinVec, + first_span: Option, +} + +impl AttributeGroup for ConfusablesGroup { + const ATTRIBUTES: AttributeMapping = &[(&[sym::rustc_confusables], |this, cx, args| { + let Some(list) = args.list() else { + // TODO: error when not a list? Bring validation code here. + // NOTE: currently subsequent attributes are silently ignored using + // tcx.get_attr(). + return; + }; + + if list.is_empty() { + cx.dcx().emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span }); + } + + for param in list.mixed() { + let span = param.span(); + + let Some(lit) = param.lit() else { + cx.dcx().emit_err(session_diagnostics::IncorrectMetaItem { + span, + suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion { + lo: span.shrink_to_lo(), + hi: span.shrink_to_hi(), + }), + }); + continue; + }; + + this.confusables.push(lit.symbol); + } + + this.first_span.get_or_insert(cx.attr_span); + })]; + + fn finalize(self, _cx: &AttributeGroupContext<'_>) -> Option { + if self.confusables.is_empty() { + return None; + } + + Some(AttributeKind::Confusables { + symbols: self.confusables, + first_span: self.first_span.unwrap(), + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs new file mode 100644 index 0000000000000..70a02d4ddd03c --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -0,0 +1,163 @@ +use rustc_attr_data_structures::{AttributeKind, DeprecatedSince, Deprecation}; +use rustc_span::symbol::Ident; +use rustc_span::{Span, Symbol, sym}; + +use super::SingleAttributeGroup; +use super::util::parse_version; +use crate::context::AttributeAcceptContext; +use crate::parser::{ArgParser, GenericArgParser, MetaItemParser, NameValueParser}; +use crate::session_diagnostics; +use crate::session_diagnostics::UnsupportedLiteralReason; + +pub(crate) struct DeprecationGroup; + +fn get<'a>( + cx: &AttributeAcceptContext<'_>, + ident: Ident, + param_span: Span, + arg: impl ArgParser<'a>, + item: &mut Option, +) -> bool { + if item.is_some() { + cx.dcx().emit_err(session_diagnostics::MultipleItem { + span: param_span, + item: ident.to_string(), + }); + return false; + } + if let Some(v) = arg.name_value() { + if let Some(value_str) = v.value_as_str() { + *item = Some(value_str); + true + } else { + let lit = v.value_as_lit(); + cx.dcx().emit_err(session_diagnostics::UnsupportedLiteral { + span: v.value_span(), + reason: UnsupportedLiteralReason::DeprecatedString, + is_bytestr: lit.kind.is_bytestr(), + start_point_span: cx.sess().source_map().start_point(lit.span), + }); + false + } + } else { + // FIXME(jdonszelmann): suggestion? + cx.dcx().emit_err(session_diagnostics::IncorrectMetaItem { + span: param_span, + suggestion: None, + }); + false + } +} + +impl SingleAttributeGroup for DeprecationGroup { + const PATH: &'static [rustc_span::Symbol] = &[sym::deprecated]; + + fn on_duplicate(cx: &crate::context::AttributeAcceptContext<'_>, first_span: rustc_span::Span) { + // FIXME(jdonszelmann): merge with errors from check_attrs.rs + cx.dcx().emit_err(session_diagnostics::UnusedMultiple { + this: cx.attr_span, + other: first_span, + name: sym::deprecated, + }); + } + + fn convert( + cx: &AttributeAcceptContext<'_>, + args: &GenericArgParser<'_, rustc_ast::Expr>, + ) -> Option { + let features = cx.features(); + + let mut since = None; + let mut note = None; + let mut suggestion = None; + + let is_rustc = features.staged_api(); + + if let Some(value) = args.name_value() + && let Some(value_str) = value.value_as_str() + { + note = Some(value_str) + } else if let Some(list) = args.list() { + for param in list.mixed() { + let param_span = param.span(); + let Some(param) = param.meta_item() else { + cx.dcx().emit_err(session_diagnostics::UnsupportedLiteral { + span: param_span, + reason: UnsupportedLiteralReason::DeprecatedKvPair, + is_bytestr: false, + start_point_span: cx.sess().source_map().start_point(param_span), + }); + return None; + }; + + let (ident, arg) = param.word_or_empty(); + + match ident.name { + sym::since => { + if !get(cx, ident, param_span, arg, &mut since) { + return None; + } + } + sym::note => { + if !get(cx, ident, param_span, arg, &mut note) { + return None; + } + } + sym::suggestion => { + if !features.deprecated_suggestion() { + cx.dcx().emit_err(session_diagnostics::DeprecatedItemSuggestion { + span: param_span, + is_nightly: cx.sess().is_nightly_build(), + details: (), + }); + } + + if !get(cx, ident, param_span, arg, &mut suggestion) { + return None; + } + } + _ => { + cx.dcx().emit_err(session_diagnostics::UnknownMetaItem { + span: param_span, + item: ident.to_string(), + expected: if features.deprecated_suggestion() { + &["since", "note", "suggestion"] + } else { + &["since", "note"] + }, + }); + return None; + } + } + } + } + + let since = if let Some(since) = since { + if since.as_str() == "TBD" { + DeprecatedSince::Future + } else if !is_rustc { + DeprecatedSince::NonStandard(since) + } else if let Some(version) = parse_version(since) { + DeprecatedSince::RustcVersion(version) + } else { + cx.dcx().emit_err(session_diagnostics::InvalidSince { span: cx.attr_span }); + DeprecatedSince::Err + } + } else if is_rustc { + cx.dcx().emit_err(session_diagnostics::MissingSince { span: cx.attr_span }); + DeprecatedSince::Err + } else { + DeprecatedSince::Unspecified + }; + + if is_rustc && note.is_none() { + cx.dcx().emit_err(session_diagnostics::MissingNote { span: cx.attr_span }); + return None; + } + + Some(AttributeKind::Deprecation { + deprecation: Deprecation { since, note, suggestion }, + span: cx.attr_span, + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs new file mode 100644 index 0000000000000..b38b1cb226a41 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -0,0 +1,155 @@ +//! [`AttributeGroup`]s are groups of attributes (groups can be size 1) that are parsed together. +//! An [`AttributeGroup`] implementation defines its parser. +//! +//! You can find more docs on what groups are on [`AttributeGroup`] itself. +//! However, for many types of attributes, implementing [`AttributeGroup`] is not necessary. +//! It allows for a lot of flexibility you might not want. +//! +//! Specifically, you care about managing the state of your [AttributeGroup] state machine +//! yourself. In this case you can choose to implement: +//! +//! - [`SingleAttributeGroup`]: makes it easy to implement an attribute which should error if it +//! appears more than once in a list of attributes +//! - [`CombineAttributeGroup`]: makes it easy to implement an attribute which should combine the +//! contents of attributes, if an attribute appear multiple times in a list +//! +//! Attributes should be added to [`ATTRIBUTE_GROUP_MAPPING`](crate::context::ATTRIBUTE_GROUP_MAPPING) to be parsed. + +use std::marker::PhantomData; + +use rustc_ast::Expr; +use rustc_attr_data_structures::AttributeKind; +use rustc_span::Span; +use thin_vec::ThinVec; + +use crate::context::{AttributeAcceptContext, AttributeGroupContext}; +use crate::parser::GenericArgParser; + +pub(crate) mod allow_unstable; +pub(crate) mod cfg; +pub(crate) mod confusables; +pub(crate) mod deprecation; +pub(crate) mod repr; +pub(crate) mod stability; +pub(crate) mod transparency; +pub(crate) mod util; + +type AttributeHandler = fn(&mut T, &AttributeAcceptContext<'_>, &GenericArgParser<'_, Expr>); +type AttributeMapping = &'static [(&'static [rustc_span::Symbol], AttributeHandler)]; + +/// An [`AttributeGroup`] is a type which searches for syntactic attributes. +/// +/// Groups are often tiny state machines. [`Default::default`] +/// creates a new instance that sits in some kind of initial state, usually that the +/// attribute it is looking for was not yet seen. +/// +/// Then, it defines what paths this group will accept in [`AttributeGroup::ATTRIBUTES`]. +/// These are listed as pairs, of symbols and function pointers. The function pointer will +/// be called when that attribute is found on an item, which can influence the state. +/// +/// Finally, all `finalize` functions are called, for each piece of state, +pub(crate) trait AttributeGroup: Default + 'static { + /// The symbols for the attributes that this extractor can extract. + /// + /// If an attribute has this symbol, the `accept` function will be called on it. + const ATTRIBUTES: AttributeMapping; + + /// The extractor has gotten a chance to accept the attributes on an item, + /// now produce an attribute. + fn finalize(self, cx: &AttributeGroupContext<'_>) -> Option; +} + +/// A slightly simpler and more restricted way to convert attributes which you can implement for +/// unit types. Assumes that a single attribute can only appear a single time on an item +/// [`SingleGroup where T: SingleAttributeGroup`](Single) creates an [`AttributeGroup`] from any [`SingleAttributeGroup`]. +/// +/// [`SingleGroup`] can only convert attributes one-to-one, and cannot combine multiple +/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. +pub(crate) trait SingleAttributeGroup: 'static { + const PATH: &'static [rustc_span::Symbol]; + + /// Caled when a duplicate attribute is found. + /// + /// `first_span` is the span of the first occurrence of this attribute. + fn on_duplicate(cx: &AttributeAcceptContext<'_>, first_span: Span); + + /// The extractor has gotten a chance to accept the attributes on an item, + /// now produce an attribute. + fn convert( + cx: &AttributeAcceptContext<'_>, + args: &GenericArgParser<'_, Expr>, + ) -> Option; +} + +pub(crate) struct Single(PhantomData, Option<(AttributeKind, Span)>); + +impl Default for Single { + fn default() -> Self { + Self(Default::default(), Default::default()) + } +} + +impl AttributeGroup for Single { + const ATTRIBUTES: AttributeMapping = &[(T::PATH, |group: &mut Single, cx, args| { + if let Some((_, s)) = group.1 { + T::on_duplicate(cx, s); + return; + } + + if let Some(pa) = T::convert(cx, args) { + group.1 = Some((pa, cx.attr_span)); + } + })]; + + fn finalize(self, _cx: &AttributeGroupContext<'_>) -> Option { + Some(self.1?.0) + } +} + +type ConvertFn = fn(ThinVec) -> AttributeKind; + +/// A slightly simpler and more restricted way to convert attributes which you can implement for +/// unit types. If multiple attributes appear on an element, combines the values of each into a +/// [`ThinVec`]. +/// [`CombineGroup where T: CombineAttributeGroup`](Combine) creates an [`AttributeGroup`] from any [`CombineAttributeGroup`]. +/// +/// [`CombineAttributeGroup`] can only convert a single kind of attribute, and cannot combine multiple +/// attributes together like is necessary for `#[stable()]` and `#[unstable()]` for example. +pub(crate) trait CombineAttributeGroup: 'static { + const PATH: &'static [rustc_span::Symbol]; + + type Item; + const CONVERT: ConvertFn; + + /// The extractor has gotten a chance to accept the attributes on an item, + /// now produce an attribute. + fn extend<'a>( + cx: &'a AttributeAcceptContext<'a>, + args: &'a GenericArgParser<'a, Expr>, + ) -> impl IntoIterator + 'a; +} + +pub(crate) struct Combine( + PhantomData, + ThinVec<::Item>, +); + +impl Default for Combine { + fn default() -> Self { + Self(Default::default(), Default::default()) + } +} + +impl AttributeGroup for Combine { + const ATTRIBUTES: AttributeMapping = + &[(T::PATH, |group: &mut Combine, cx, args| group.1.extend(T::extend(cx, args)))]; + + fn finalize(self, _cx: &AttributeGroupContext<'_>) -> Option { + if self.1.is_empty() { + None + } else { + // TODO: what filter here? + Some(T::CONVERT(self.1)) + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs new file mode 100644 index 0000000000000..651001b2a3a87 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -0,0 +1,317 @@ +use rustc_abi::Align; +use rustc_ast::{IntTy, LitIntType, LitKind, MetaItemLit, UintTy}; +use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr}; +use rustc_span::symbol::Ident; +use rustc_span::{Span, Symbol, sym}; + +use super::{CombineAttributeGroup, ConvertFn}; +use crate::context::AttributeAcceptContext; +use crate::parser::{ + ArgParser, GenericArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, + NameValueParser, +}; +use crate::session_diagnostics; +use crate::session_diagnostics::IncorrectReprFormatGenericCause; + +/// Parse #[repr(...)] forms. +/// +/// Valid repr contents: any of the primitive integral type names (see +/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use +/// the same discriminant size that the corresponding C enum would or C +/// structure layout, `packed` to remove padding, and `transparent` to delegate representation +/// concerns to the only non-ZST field. +// FIXME(jdonszelmann): is a vec the right representation here even? isn't it just a struct? +pub(crate) struct ReprGroup; + +impl CombineAttributeGroup for ReprGroup { + type Item = ReprAttr; + const PATH: &'static [rustc_span::Symbol] = &[sym::repr]; + const CONVERT: ConvertFn = AttributeKind::Repr; + + fn extend<'a>( + cx: &'a AttributeAcceptContext<'a>, + args: &'a GenericArgParser<'a, rustc_ast::Expr>, + ) -> impl IntoIterator + 'a { + let mut reprs = Vec::new(); + + let Some(list) = args.list() else { + return reprs; + }; + + for param in list.mixed() { + reprs.extend(param.meta_item().and_then(|mi| parse_repr(cx, &mi))); + } + + reprs + } +} + +fn parse_repr<'a, 'b>( + cx: &AttributeAcceptContext<'_>, + param: &'a impl MetaItemParser<'b>, +) -> Option { + // FIXME(jdonszelmann): invert the parsing here to match on the word first and then the + // structure. + let (ident, args) = param.word_or_empty(); + + if args.no_args() { + parse_simple_repr(cx, ident, param.span()) + } else if let Some(list) = args.list() { + parse_list_repr(cx, ident, list, param.span()) + } else if let Some(name_value) = args.name_value() { + reject_name_value_repr(cx, ident, name_value.value_as_lit(), param.span()); + None + } else { + completely_unknown(cx, param.span()); + None + } +} + +fn parse_list_repr<'b>( + cx: &AttributeAcceptContext<'_>, + ident: Ident, + list: MetaItemListParser<'b>, + param_span: Span, +) -> Option { + if let Some(single) = list.single() { + match single { + MetaItemOrLitParser::MetaItemParser(meta) => { + reject_not_literal_list(cx, ident, meta.span(), param_span); + None + } + MetaItemOrLitParser::Lit(lit) => parse_singleton_list_repr(cx, ident, lit, param_span), + } + } else { + reject_not_one_element_list(cx, ident, param_span); + None + } +} + +fn reject_not_one_element_list(cx: &AttributeAcceptContext<'_>, ident: Ident, param_span: Span) { + match ident.name { + sym::align => { + cx.dcx() + .emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg { span: param_span }); + } + sym::packed => { + cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg { + span: param_span, + }); + } + sym::Rust | sym::C | sym::simd | sym::transparent => { + cx.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { + span: param_span, + name: ident.to_string(), + }); + } + other if int_type_of_word(other).is_some() => { + cx.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { + span: param_span, + name: ident.to_string(), + }); + } + _ => { + completely_unknown(cx, param_span); + } + } +} + +fn reject_not_literal_list( + cx: &AttributeAcceptContext<'_>, + ident: Ident, + meta_span: Span, + param_span: Span, +) { + match ident.name { + sym::align => { + cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatExpectInteger { + span: meta_span, + }); + } + + sym::packed => { + cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatPackedExpectInteger { + span: meta_span, + }); + } + sym::Rust | sym::C | sym::simd | sym::transparent => { + cx.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { + span: param_span, + name: ident.to_string(), + }); + } + other if int_type_of_word(other).is_some() => { + cx.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { + span: param_span, + name: ident.to_string(), + }); + } + _ => { + completely_unknown(cx, param_span); + } + } +} + +fn reject_name_value_repr( + cx: &AttributeAcceptContext<'_>, + ident: Ident, + value: MetaItemLit, + param_span: Span, +) { + match ident.name { + sym::align | sym::packed => { + cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatGeneric { + span: param_span, + // FIXME(jdonszelmann) can just be a string in the diag type + repr_arg: &ident.to_string(), + cause: IncorrectReprFormatGenericCause::from_lit_kind( + param_span, + &value.kind, + ident.name.as_str(), + ), + }); + } + sym::Rust | sym::C | sym::simd | sym::transparent => { + cx.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue { + span: param_span, + name: ident.to_string(), + }); + } + other if int_type_of_word(other).is_some() => { + cx.dcx().emit_err(session_diagnostics::InvalidReprHintNoValue { + span: param_span, + name: ident.to_string(), + }); + } + _ => { + completely_unknown(cx, param_span); + } + } +} + +fn parse_singleton_list_repr( + cx: &AttributeAcceptContext<'_>, + ident: Ident, + lit: MetaItemLit, + param_span: Span, +) -> Option { + match ident.name { + sym::align => match parse_alignment(&lit.kind) { + Ok(literal) => Some(ReprAttr::ReprAlign(literal)), + Err(message) => { + cx.dcx().emit_err(session_diagnostics::InvalidReprGeneric { + span: lit.span, + repr_arg: ident.to_string(), + error_part: message, + }); + None + } + }, + sym::packed => match parse_alignment(&lit.kind) { + Ok(literal) => Some(ReprAttr::ReprPacked(literal)), + Err(message) => { + cx.dcx().emit_err(session_diagnostics::InvalidReprGeneric { + span: lit.span, + repr_arg: ident.to_string(), + error_part: message, + }); + None + } + }, + sym::Rust | sym::C | sym::simd | sym::transparent => { + cx.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { + span: param_span, + name: ident.to_string(), + }); + None + } + other if int_type_of_word(other).is_some() => { + cx.dcx().emit_err(session_diagnostics::InvalidReprHintNoParen { + span: param_span, + name: ident.to_string(), + }); + None + } + _ => { + completely_unknown(cx, param_span); + None + } + } +} + +fn parse_simple_repr( + cx: &AttributeAcceptContext<'_>, + ident: Ident, + param_span: Span, +) -> Option { + use ReprAttr::*; + match ident.name { + sym::Rust => Some(ReprRust), + sym::C => Some(ReprC), + sym::packed => Some(ReprPacked(Align::ONE)), + sym::simd => Some(ReprSimd), + sym::transparent => Some(ReprTransparent), + sym::align => { + cx.dcx().emit_err(session_diagnostics::InvalidReprAlignNeedArg { span: ident.span }); + None + } + other => { + if let Some(int) = int_type_of_word(other) { + Some(ReprInt(int)) + } else { + completely_unknown(cx, param_span); + None + } + } + } +} + +// Not a word we recognize. This will be caught and reported by +// the `check_mod_attrs` pass, but this pass doesn't always run +// (e.g. if we only pretty-print the source), so we have to gate +// the `span_delayed_bug` call as follows: +// TODO: remove this in favor of just reporting the error here if we can... +fn completely_unknown(cx: &AttributeAcceptContext<'_>, param_span: Span) { + if cx.sess().opts.pretty.map_or(true, |pp| pp.needs_analysis()) { + cx.dcx().span_delayed_bug(param_span, "unrecognized representation hint"); + } +} + +fn int_type_of_word(s: Symbol) -> Option { + use IntType::*; + + match s { + sym::i8 => Some(SignedInt(IntTy::I8)), + sym::u8 => Some(UnsignedInt(UintTy::U8)), + sym::i16 => Some(SignedInt(IntTy::I16)), + sym::u16 => Some(UnsignedInt(UintTy::U16)), + sym::i32 => Some(SignedInt(IntTy::I32)), + sym::u32 => Some(UnsignedInt(UintTy::U32)), + sym::i64 => Some(SignedInt(IntTy::I64)), + sym::u64 => Some(UnsignedInt(UintTy::U64)), + sym::i128 => Some(SignedInt(IntTy::I128)), + sym::u128 => Some(UnsignedInt(UintTy::U128)), + sym::isize => Some(SignedInt(IntTy::Isize)), + sym::usize => Some(UnsignedInt(UintTy::Usize)), + _ => None, + } +} + +pub(crate) fn parse_alignment(node: &LitKind) -> Result { + if let LitKind::Int(literal, LitIntType::Unsuffixed) = node { + // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first + if literal.get().is_power_of_two() { + // Only possible error is larger than 2^29 + literal + .get() + .try_into() + .ok() + .and_then(|v| Align::from_bytes(v).ok()) + .ok_or("larger than 2^29") + } else { + Err("not a power of two") + } + } else { + Err("not an unsuffixed integer") + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs new file mode 100644 index 0000000000000..db9cd3ecc1a6e --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -0,0 +1,377 @@ +use std::num::NonZero; + +use rustc_attr_data_structures::{ + AttributeKind, DefaultBodyStability, PartialConstStability, Stability, StabilityLevel, + StableSince, UnstableReason, VERSION_PLACEHOLDER, +}; +use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; + +use super::util::parse_version; +use super::{AttributeGroup, AttributeMapping, SingleAttributeGroup}; +use crate::context::{AttributeAcceptContext, AttributeGroupContext}; +use crate::parser::{ArgParser, MetaItemParser, NameValueParser}; +use crate::session_diagnostics::{self, UnsupportedLiteralReason}; + +#[derive(Default)] +pub(crate) struct StabilityGroup { + allowed_through_unstable_modules: bool, + stability: Option<(Stability, Span)>, +} + +impl StabilityGroup { + /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate. + fn check_duplicate(&self, cx: &AttributeAcceptContext<'_>) -> bool { + if let Some((_, span)) = self.stability { + cx.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { span }); + true + } else { + false + } + } +} + +impl AttributeGroup for StabilityGroup { + const ATTRIBUTES: AttributeMapping = &[ + (&[sym::stable], |this, cx, args| { + if !this.check_duplicate(cx) + && let Some((feature, level)) = parse_stability(cx, args) + { + this.stability = Some((Stability { level, feature }, cx.attr_span)); + } + }), + (&[sym::unstable], |this, cx, args| { + if !this.check_duplicate(cx) + && let Some((feature, level)) = parse_unstability(cx, args) + { + this.stability = Some((Stability { level, feature }, cx.attr_span)); + } + }), + (&[sym::rustc_allowed_through_unstable_modules], |this, _, _| { + this.allowed_through_unstable_modules = true; + }), + ]; + + fn finalize(self, cx: &AttributeGroupContext<'_>) -> Option { + let (mut stability, span) = self.stability?; + + if self.allowed_through_unstable_modules { + if let StabilityLevel::Stable { ref mut allowed_through_unstable_modules, .. } = + stability.level + { + *allowed_through_unstable_modules = true; + } else { + cx.dcx().emit_err(session_diagnostics::RustcAllowedUnstablePairing { + span: cx.target_span, + }); + } + } + + // Emit errors for non-staged-api crates. + if !cx.features().staged_api() { + cx.dcx().emit_err(session_diagnostics::StabilityOutsideStd { span }); + } + + Some(AttributeKind::Stability { stability, span }) + } +} + +// FIXME(jdonszelmann) change to Single +#[derive(Default)] +pub(crate) struct BodyStabilityGroup { + stability: Option<(DefaultBodyStability, Span)>, +} + +impl AttributeGroup for BodyStabilityGroup { + const ATTRIBUTES: AttributeMapping = + &[(&[sym::rustc_default_body_unstable], |this, cx, args| { + if this.stability.is_some() { + cx.dcx() + .emit_err(session_diagnostics::MultipleStabilityLevels { span: cx.attr_span }); + } else if let Some((feature, level)) = parse_stability(cx, args) { + this.stability = Some((DefaultBodyStability { level, feature }, cx.attr_span)); + } + })]; + + fn finalize(self, cx: &AttributeGroupContext<'_>) -> Option { + let (stability, span) = self.stability?; + + // Emit errors for non-staged-api crates. + if !cx.features().staged_api() { + cx.dcx().emit_err(session_diagnostics::StabilityOutsideStd { span }); + } + + Some(AttributeKind::BodyStability { stability, span }) + } +} + +pub(crate) struct ConstStabilityIndirectGroup; +// TODO: single word attribute group +impl SingleAttributeGroup for ConstStabilityIndirectGroup { + const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_const_stable_indirect]; + + // ignore + fn on_duplicate(_cx: &AttributeAcceptContext<'_>, _first_span: Span) {} + + fn convert( + _cx: &AttributeAcceptContext<'_>, + _args: &super::GenericArgParser<'_, rustc_ast::Expr>, + ) -> Option { + Some(AttributeKind::ConstStabilityIndirect) + } +} + +#[derive(Default)] +pub(crate) struct ConstStabilityGroup { + promotable: bool, + stability: Option<(PartialConstStability, Span)>, +} + +impl ConstStabilityGroup { + /// Checks, and emits an error when a stability (or unstability) was already set, which would be a duplicate. + fn check_duplicate(&self, cx: &AttributeAcceptContext<'_>) -> bool { + if let Some((_, span)) = self.stability { + cx.dcx().emit_err(session_diagnostics::MultipleStabilityLevels { span }); + true + } else { + false + } + } +} + +impl AttributeGroup for ConstStabilityGroup { + const ATTRIBUTES: AttributeMapping = &[ + (&[sym::rustc_const_stable], |this, cx, args| { + if !this.check_duplicate(cx) + && let Some((feature, level)) = parse_stability(cx, args) + { + this.stability = Some(( + PartialConstStability { level, feature, promotable: false }, + cx.attr_span, + )); + } + }), + (&[sym::rustc_const_unstable], |this, cx, args| { + if !this.check_duplicate(cx) + && let Some((feature, level)) = parse_unstability(cx, args) + { + this.stability = Some(( + PartialConstStability { level, feature, promotable: false }, + cx.attr_span, + )); + } + }), + (&[sym::rustc_promotable], |this, _, _| { + this.promotable = true; + }), + ]; + + fn finalize(mut self, cx: &AttributeGroupContext<'_>) -> Option { + if self.promotable { + if let Some((ref mut stab, _)) = self.stability { + stab.promotable = true; + } else { + cx.dcx() + .emit_err(session_diagnostics::RustcPromotablePairing { span: cx.target_span }); + } + } + + let (stability, span) = self.stability?; + + // Emit errors for non-staged-api crates. + if !cx.features().staged_api() { + cx.dcx().emit_err(session_diagnostics::StabilityOutsideStd { span }); + } + + Some(AttributeKind::ConstStability { stability, span }) + } +} + +/// Tries to insert the value of a `key = value` meta item into an option. +/// +/// Emits an error when either the option was already Some, or the arguments weren't of form +/// `name = value` +fn insert_value_into_option_or_error<'a>( + cx: &AttributeAcceptContext<'_>, + param: &impl MetaItemParser<'a>, + item: &mut Option, +) -> Option<()> { + if item.is_some() { + cx.dcx().emit_err(session_diagnostics::MultipleItem { + span: param.span(), + item: param.path_without_args().to_string(), + }); + None + } else if let Some(v) = param.args().name_value() + && let Some(s) = v.value_as_str() + { + *item = Some(s); + Some(()) + } else { + cx.dcx().emit_err(session_diagnostics::IncorrectMetaItem { + span: param.span(), + suggestion: None, + }); + None + } +} + +/// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and +/// its stability information. +pub(crate) fn parse_stability<'a>( + cx: &AttributeAcceptContext<'_>, + args: &'a impl ArgParser<'a>, +) -> Option<(Symbol, StabilityLevel)> { + let mut feature = None; + let mut since = None; + + for param in args.list()?.mixed() { + let param_span = param.span(); + let Some(param) = param.meta_item() else { + cx.dcx().emit_err(session_diagnostics::UnsupportedLiteral { + span: param_span, + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: cx.sess().source_map().start_point(param_span), + }); + return None; + }; + + match param.word_or_empty_without_args().name { + sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, + sym::since => insert_value_into_option_or_error(cx, ¶m, &mut since)?, + _ => { + cx.dcx().emit_err(session_diagnostics::UnknownMetaItem { + span: param_span, + item: param.path_without_args().to_string(), + expected: &["feature", "since"], + }); + return None; + } + } + } + + let feature = match feature { + Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), + Some(_bad_feature) => { + Err(cx.dcx().emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span })) + } + None => Err(cx.dcx().emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })), + }; + + let since = if let Some(since) = since { + if since.as_str() == VERSION_PLACEHOLDER { + StableSince::Current + } else if let Some(version) = parse_version(since) { + StableSince::Version(version) + } else { + cx.dcx().emit_err(session_diagnostics::InvalidSince { span: cx.attr_span }); + StableSince::Err + } + } else { + cx.dcx().emit_err(session_diagnostics::MissingSince { span: cx.attr_span }); + StableSince::Err + }; + + match feature { + Ok(feature) => { + let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false }; + Some((feature, level)) + } + Err(ErrorGuaranteed { .. }) => None, + } +} + +// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable` +/// attribute, and return the feature name and its stability information. +pub(crate) fn parse_unstability<'a>( + cx: &AttributeAcceptContext<'_>, + args: &'a impl ArgParser<'a>, +) -> Option<(Symbol, StabilityLevel)> { + let mut feature = None; + let mut reason = None; + let mut issue = None; + let mut issue_num = None; + let mut is_soft = false; + let mut implied_by = None; + for param in args.list()?.mixed() { + let param_span = param.span(); + let Some(param) = param.meta_item() else { + cx.dcx().emit_err(session_diagnostics::UnsupportedLiteral { + span: param_span, + reason: UnsupportedLiteralReason::Generic, + is_bytestr: false, + start_point_span: cx.sess().source_map().start_point(param_span), + }); + return None; + }; + + let (word, args) = param.word_or_empty(); + match word.name { + sym::feature => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, + sym::reason => insert_value_into_option_or_error(cx, ¶m, &mut reason)?, + sym::issue => { + insert_value_into_option_or_error(cx, ¶m, &mut issue)?; + + // These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item + // is a name/value pair string literal. + issue_num = match issue.unwrap().as_str() { + "none" => None, + issue => match issue.parse::>() { + Ok(num) => Some(num), + Err(err) => { + cx.dcx().emit_err( + session_diagnostics::InvalidIssueString { + span: param_span, + cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind( + param_span, + err.kind(), + ), + }, + ); + return None; + } + }, + }; + } + sym::soft => { + if !args.no_args() { + cx.dcx().emit_err(session_diagnostics::SoftNoArgs { span: param_span }); + } + is_soft = true; + } + sym::implied_by => insert_value_into_option_or_error(cx, ¶m, &mut implied_by)?, + _ => { + cx.dcx().emit_err(session_diagnostics::UnknownMetaItem { + span: param_span, + item: param.path_without_args().to_string(), + expected: &["feature", "reason", "issue", "soft", "implied_by"], + }); + return None; + } + } + } + + let feature = match feature { + Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature), + Some(_bad_feature) => { + Err(cx.dcx().emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span })) + } + None => Err(cx.dcx().emit_err(session_diagnostics::MissingFeature { span: cx.attr_span })), + }; + + let issue = issue + .ok_or_else(|| cx.dcx().emit_err(session_diagnostics::MissingIssue { span: cx.attr_span })); + + match (feature, issue) { + (Ok(feature), Ok(_)) => { + let level = StabilityLevel::Unstable { + reason: UnstableReason::from_opt_reason(reason), + issue: issue_num, + is_soft, + implied_by, + }; + Some((feature, level)) + } + (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None, + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs new file mode 100644 index 0000000000000..d2b32a47ce7f4 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -0,0 +1,36 @@ +use rustc_attr_data_structures::AttributeKind; +use rustc_span::hygiene::Transparency; +use rustc_span::sym; + +use super::SingleAttributeGroup; +use crate::parser::{ArgParser, NameValueParser}; + +pub(crate) struct TransparencyGroup; + +// TODO: fix this but I don't want to rn +#[allow(rustc::untranslatable_diagnostic)] +#[allow(rustc::diagnostic_outside_of_impl)] +impl SingleAttributeGroup for TransparencyGroup { + const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_transparency]; + + fn on_duplicate(cx: &crate::context::AttributeAcceptContext<'_>, first_span: rustc_span::Span) { + cx.dcx().span_err(vec![first_span, cx.attr_span], "multiple macro transparency attributes"); + } + + fn convert( + cx: &crate::context::AttributeAcceptContext<'_>, + args: &crate::parser::GenericArgParser<'_, rustc_ast::Expr>, + ) -> Option { + match args.name_value().and_then(|nv| nv.value_as_str()) { + Some(sym::transparent) => Some(Transparency::Transparent), + Some(sym::semitransparent) => Some(Transparency::SemiTransparent), + Some(sym::opaque) => Some(Transparency::Opaque), + Some(other) => { + cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`")); + None + } + None => None, + } + .map(AttributeKind::MacroTransparency) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs new file mode 100644 index 0000000000000..7f57a6d3188a9 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/util.rs @@ -0,0 +1,29 @@ +use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name}; +use rustc_feature::is_builtin_attr_name; +use rustc_attr_data_structures::RustcVersion; +use rustc_span::{Symbol, sym}; + +/// Parse a rustc version number written inside string literal in an attribute, +/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are +/// not accepted in this position, unlike when parsing CFG_RELEASE. +pub fn parse_version(s: Symbol) -> Option { + let mut components = s.as_str().split('-'); + let d = components.next()?; + if components.next().is_some() { + return None; + } + let mut digits = d.splitn(3, '.'); + let major = digits.next()?.parse().ok()?; + let minor = digits.next()?.parse().ok()?; + let patch = digits.next().unwrap_or("0").parse().ok()?; + Some(RustcVersion { major, minor, patch }) +} + +// TODO: create single word attribute map +pub fn is_builtin_attr(attr: &impl AttributeExt) -> bool { + attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name)) +} + +pub fn find_crate_name(attrs: &[impl AttributeExt]) -> Option { + first_attr_value_str_by_name(attrs, sym::crate_name) +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs new file mode 100644 index 0000000000000..d91a8712edfbc --- /dev/null +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -0,0 +1,323 @@ +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::ops::Deref; +use std::sync::LazyLock; + +use rustc_ast::{self as ast, DelimArgs}; +use rustc_errors::DiagCtxtHandle; +use rustc_feature::Features; +use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId}; +use rustc_attr_data_structures::AttributeKind; +use rustc_session::Session; +use rustc_span::symbol::kw; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; + +use crate::attributes::allow_unstable::{AllowConstFnUnstableGroup, AllowInternalUnstableGroup}; +use crate::attributes::confusables::ConfusablesGroup; +use crate::attributes::deprecation::DeprecationGroup; +use crate::attributes::repr::ReprGroup; +use crate::attributes::stability::{ + BodyStabilityGroup, ConstStabilityGroup, ConstStabilityIndirectGroup, StabilityGroup, +}; +use crate::attributes::transparency::TransparencyGroup; +use crate::attributes::{AttributeGroup, Combine, Single}; +use crate::parser::{GenericArgParser, GenericMetaItemParser, MetaItemParser}; + +macro_rules! attribute_groups { + ( + pub(crate) static $name: ident = [$($names: ty),* $(,)?]; + ) => { + pub(crate) static $name: LazyLock<( + BTreeMap<&'static [Symbol], Vec, &GenericArgParser<'_, ast::Expr>) + Send + Sync>>>, + Vec) -> Option>> + )> = LazyLock::new(|| { + let mut accepts = BTreeMap::<_, Vec, &GenericArgParser<'_, ast::Expr>) + Send + Sync>>>::new(); + let mut finalizes = Vec::) -> Option>>::new(); + + $( + { + thread_local! { + static STATE_OBJECT: RefCell<$names> = RefCell::new(<$names>::default()); + }; + + for (k, v) in <$names>::ATTRIBUTES { + accepts.entry(*k).or_default().push(Box::new(|cx, args| { + STATE_OBJECT.with_borrow_mut(|s| { + v(s, cx, args) + }) + })); + } + + finalizes.push(Box::new(|cx| { + let state = STATE_OBJECT.take(); + state.finalize(cx) + })); + } + )* + + (accepts, finalizes) + }); + }; +} + +attribute_groups!( + // TODO: rename to the same as in `AttributeDuplicates` + pub(crate) static ATTRIBUTE_GROUP_MAPPING = [ + // tidy-alphabetical-start + BodyStabilityGroup, + ConfusablesGroup, + ConstStabilityGroup, + StabilityGroup, + // tidy-alphabetical-end + + // tidy-alphabetical-start + Combine, + Combine, + Combine, + // tidy-alphabetical-end + + // tidy-alphabetical-start + Single, + Single, + Single, + // tidy-alphabetical-end + ]; +); + +/// Context created for every attribute that's accepted +/// +/// Gives [`AttributeGroup`]s enough information to create errors, for example. +pub(crate) struct AttributeAcceptContext<'a> { + pub(crate) group_cx: &'a AttributeGroupContext<'a>, + /// The span of the attribute currently being parsed + pub(crate) attr_span: Span, +} + +impl<'a> Deref for AttributeAcceptContext<'a> { + type Target = AttributeGroupContext<'a>; + + fn deref(&self) -> &Self::Target { + &self.group_cx + } +} + +/// Context created for every attribute group that's parsed +/// +/// Gives [`AttributeGroup`]s enough information to create errors, for example. +pub(crate) struct AttributeGroupContext<'a> { + /// The parse context, gives access to the session and the + /// diagnostics context. + pub(crate) cx: &'a AttributeParseContext<'a>, + /// The span of the syntactical component this attribute was applied to + pub(crate) target_span: Span, +} + +impl<'a> Deref for AttributeGroupContext<'a> { + type Target = AttributeParseContext<'a>; + + fn deref(&self) -> &Self::Target { + &self.cx + } +} + +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum OmitDoc { + Lower, + Skip, +} + +/// Context created once, for example as part of the ast lowering +/// context, through which all attributes can be lowered. +pub struct AttributeParseContext<'sess> { + tools: Vec, + sess: &'sess Session, + features: Option<&'sess Features>, + + /// *only* parse attributes with this symbol. + /// + /// Used in cases where we want the lowering infrastructure for + /// parse just a single attribute. + parse_only: Option, +} + +impl<'sess> AttributeParseContext<'sess> { + /// This method allows you to parse attributes *before* you have access to features or tools. + /// One example where this is necessary, is to parse `feature` attributes themselves for + /// example. + /// + /// Try to use this as little as possible. Attributes *should* be lowered during `rustc_ast_lowering`. + /// Some attributes require access to features to parse, which would crash if you tried to do so + /// through [`parse_limited`](Self::parse_limited). + /// + /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with + /// that symbol are picked out of the list of instructions and parsed. Those are returned. + pub fn parse_limited( + sess: &'sess Session, + attrs: &[ast::Attribute], + sym: Symbol, + target_span: Span, + ) -> Option { + let mut parsed = Self { sess, features: None, tools: Vec::new(), parse_only: Some(sym) } + .parse_attribute_list(attrs, target_span, OmitDoc::Skip); + + assert!(parsed.len() <= 1); + + parsed.pop() + } + + pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec) -> Self { + Self { sess, features: Some(features), tools, parse_only: None } + } + + pub(crate) fn sess(&self) -> &'sess Session { + self.sess + } + + pub(crate) fn features(&self) -> &'sess Features { + self.features.expect("features not available at this point in the compiler") + } + + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> { + self.sess.dcx() + } + + /// Parse a list of attributes. + /// + /// `target_span` is the span of the thing this list of attributes is applied to, + /// and when `omit_doc` is set, doc attributes are filtered out. + pub fn parse_attribute_list<'a>( + &'a self, + attrs: &'a [ast::Attribute], + target_span: Span, + omit_doc: OmitDoc, + ) -> Vec { + let mut attributes = Vec::new(); + + let group_cx = AttributeGroupContext { cx: self, target_span }; + + for attr in attrs { + // if we're only looking for a single attribute, + // skip all the ones we don't care about + if let Some(expected) = self.parse_only { + if attr.name_or_empty() != expected { + continue; + } + } + + // sometimes, for example for `#![doc = include_str!("readme.md")]`, + // doc still contains a non-literal. You might say, when we're lowering attributes + // that's expanded right? But no, sometimes, when parsing attributes on macros, + // we already use the lowering logic and these are still there. So, when `omit_doc` + // is set we *also* want to ignore these + if omit_doc == OmitDoc::Skip && attr.name_or_empty() == sym::doc { + continue; + } + + match &attr.kind { + ast::AttrKind::DocComment(comment_kind, symbol) => { + if omit_doc == OmitDoc::Skip { + continue; + } + + attributes.push(Attribute::Parsed(AttributeKind::DocComment { + style: attr.style, + kind: *comment_kind, + span: attr.span, + comment: *symbol, + })) + } + // // FIXME: make doc attributes go through a proper attribute parser + // ast::AttrKind::Normal(n) if n.name_or_empty() == sym::doc => { + // let p = GenericMetaItemParser::from_attr(&n, self.dcx()); + // + // attributes.push(Attribute::Parsed(AttributeKind::DocComment { + // style: attr.style, + // kind: CommentKind::Line, + // span: attr.span, + // comment: p.args().name_value(), + // })) + // } + ast::AttrKind::Normal(n) => { + let parser = GenericMetaItemParser::from_attr(&n, self.dcx()); + let (path, args) = parser.deconstruct(); + let parts = path.segments().map(|i| i.name).collect::>(); + + if let Some(accepts) = ATTRIBUTE_GROUP_MAPPING.0.get(parts.as_slice()) { + for f in accepts { + let cx = AttributeAcceptContext { + group_cx: &group_cx, + attr_span: attr.span, + }; + + f(&cx, &args) + } + } else { + // if we're here, we must be compiling a tool attribute... Or someone forgot to + // parse their fancy new attribute. Let's warn them in any case. If you are that + // person, and you really your attribute should remain unparsed, carefully read the + // documentation in this module and if you still think so you can add an exception + // to this assertion. + + const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg]; + assert!( + self.tools.contains(&parts[0]) || true, + // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]), + "attribute {path} wasn't parsed and isn't a know tool attribute", + ); + + attributes.push(Attribute::Unparsed(Box::new(AttrItem { + path: AttrPath::from_ast(&n.item.path), + args: self.lower_attr_args(&n.item.args), + id: HashIgnoredAttrId { attr_id: attr.id }, + style: attr.style, + span: attr.span, + }))); + } + } + } + } + + let mut parsed_attributes = Vec::new(); + for f in &ATTRIBUTE_GROUP_MAPPING.1 { + if let Some(attr) = f(&group_cx) { + parsed_attributes.push(Attribute::Parsed(attr)); + } + } + + attributes.extend(parsed_attributes); + + attributes + } + + fn lower_attr_args(&self, args: &ast::AttrArgs) -> AttrArgs { + match args { + ast::AttrArgs::Empty => AttrArgs::Empty, + ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(DelimArgs { + dspan: args.dspan, + delim: args.delim, + tokens: args.tokens.flattened(), + }), + // This is an inert key-value attribute - it will never be visible to macros + // after it gets lowered to HIR. Therefore, we can extract literals to handle + // nonterminals in `#[doc]` (e.g. `#[doc = $e]`). + ast::AttrArgs::Eq { eq_span, expr } => { + // In valid code the value always ends up as a single literal. Otherwise, a dummy + // literal suffices because the error is handled elsewhere. + let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind + && let Ok(lit) = ast::MetaItemLit::from_token_lit(token_lit, expr.span) + { + lit + } else { + let guar = self.dcx().has_errors().unwrap(); + ast::MetaItemLit { + symbol: kw::Empty, + suffix: None, + kind: ast::LitKind::Err(guar), + span: DUMMY_SP, + } + }; + AttrArgs::Eq { eq_span: *eq_span, expr: lit } + } + } + } +} diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs new file mode 100644 index 0000000000000..4ae75568a6c94 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -0,0 +1,68 @@ +//! Centralized logic for parsing and validating all attributes used after HIR. +//! +//! History: Check out [#131229](https://github.com/rust-lang/rust/issues/131229). +//! There used to be only one definition of attributes in the compiler: `ast::Attribute`. +//! These were then parsed or validated or both in places distributed all over the compiler. +//! +//! Attributes are markers on items. Most are actually attribute-like proc-macros, and are expanded +//! but some remain as the built-in attributes to guide compilation. +//! +//! In this crate, syntactical attributes (sequences of tokens that look like +//! `#[something(something else)]`) are parsed into more semantic attributes, markers on items. +//! Multiple syntactic attributes might influence a single semantic attribute. For example, +//! `#[stable(...)]` and `#[unstable()]` cannot occur together, and both semantically define +//! a "stability". Stability defines an [`AttributeExtractor`](attributes::AttributeExtractor) +//! that recognizes both `#[stable()]` and `#[unstable()]` syntactic attributes, and at the end +//! produce a single [`ParsedAttributeKind::Stability`]. +//! +//! FIXME(jdonszelmann): update devguide for best practices on attributes +//! FIXME(jdonszelmann): ^ fix these docs +//! +//! To define a new builtin, first add it + +// tidy-alphabetical-start +#![allow(internal_features)] +#![doc(rust_logo)] +#![feature(let_chains)] +#![feature(rustdoc_internals)] +#![warn(unreachable_pub)] +// tidy-alphabetical-end + +#[macro_use] +mod attributes; +mod context; +mod parser; +mod session_diagnostics; + +pub use attributes::cfg::*; +pub use attributes::util::{find_crate_name, is_builtin_attr, parse_version}; +pub use context::{AttributeParseContext, OmitDoc}; +pub use rustc_attr_data_structures::*; + +rustc_fluent_macro::fluent_messages! { "../messages.ftl" } + +#[macro_export] +macro_rules! find_attr { + ($attributes_list: expr, $pattern: pat $(if $guard: expr)?) => {{ + $crate::find_attr!($attributes_list, $pattern $(if $guard)? => ()).is_some() + }}; + + ($attributes_list: expr, $pattern: pat $(if $guard: expr)? => $e: expr) => {{ + fn check_attribute_iterator<'a>(_: &'_ impl IntoIterator) {} + check_attribute_iterator(&$attributes_list); + + let find_attribute = |iter| { + for i in $attributes_list { + match i { + rustc_hir::Attribute::Parsed($pattern) $(if $guard: expr)? => { + return Some($e); + } + _ => {} + } + } + + None + }; + find_attribute($attributes_list) + }}; +} diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs new file mode 100644 index 0000000000000..9c4d5c932c4cb --- /dev/null +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -0,0 +1,684 @@ +use std::borrow::Cow; +use std::fmt::{Debug, Display}; +use std::iter::Peekable; + +use rustc_ast::token::{self, Delimiter, Token}; +use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenTree}; +use rustc_ast::{AttrArgs, DelimArgs, Expr, ExprKind, LitKind, MetaItemLit, NormalAttr, Path}; +use rustc_ast_pretty::pprust; +use rustc_errors::DiagCtxtHandle; +use rustc_hir::{self as hir, AttrPath}; +use rustc_span::symbol::{Ident, kw}; +use rustc_span::{DUMMY_SP, Span, Symbol}; + +pub(crate) struct SegmentIterator<'a> { + offset: usize, + path: &'a PathParser<'a>, +} + +impl<'a> Iterator for SegmentIterator<'a> { + type Item = &'a Ident; + + fn next(&mut self) -> Option { + if self.offset >= self.path.len() { + return None; + } + + let res = match self.path { + PathParser::Ast(ast_path) => &ast_path.segments[self.offset].ident, + PathParser::Attr(attr_path) => &attr_path.segments[self.offset], + }; + + self.offset += 1; + Some(res) + } +} + +#[derive(Clone, Debug)] +pub(crate) enum PathParser<'a> { + Ast(&'a Path), + Attr(AttrPath), +} + +impl<'a> PathParser<'a> { + pub(crate) fn get_attribute_path(&self) -> hir::AttrPath { + AttrPath { + segments: self.segments().copied().collect::>().into_boxed_slice(), + span: self.span(), + } + } + + pub(crate) fn segments(&'a self) -> impl Iterator { + SegmentIterator { offset: 0, path: self } + } + + pub(crate) fn span(&self) -> Span { + match self { + PathParser::Ast(path) => path.span, + PathParser::Attr(attr_path) => attr_path.span, + } + } + + pub(crate) fn len(&self) -> usize { + match self { + PathParser::Ast(path) => path.segments.len(), + PathParser::Attr(attr_path) => attr_path.segments.len(), + } + } + + pub(crate) fn segments_is(&self, segments: &[Symbol]) -> bool { + self.len() == segments.len() && self.segments().zip(segments).all(|(a, b)| a.name == *b) + } + + pub(crate) fn word(&self) -> Option { + (self.len() == 1).then(|| **self.segments().next().as_ref().unwrap()) + } + + pub(crate) fn word_or_empty(&self) -> Ident { + self.word().unwrap_or_else(Ident::empty) + } + + /// Asserts that this MetaItem is some specific word. + /// + /// See [`word`](Self::word) for examples of what a word is. + pub(crate) fn word_is(&self, sym: Symbol) -> bool { + self.word().map(|i| i.name == sym).unwrap_or(false) + } +} + +impl Display for PathParser<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PathParser::Ast(path) => write!(f, "{}", pprust::path_to_string(path)), + PathParser::Attr(attr_path) => write!(f, "{attr_path}"), + } + } +} + +#[derive(Clone, Debug)] +pub(crate) enum Args<'a, T: Clone> { + Empty, + Delimited(Cow<'a, DelimArgs>), + Eq { eq_span: Span, value: Cow<'a, T>, value_span: Span }, +} + +impl<'a, T: Clone> Args<'a, T> { + pub(crate) fn span(&self) -> Option { + match self { + Args::Empty => None, + Args::Delimited(delim_args) => Some(delim_args.dspan.entire()), + Args::Eq { eq_span, value_span, .. } => Some(value_span.with_lo(eq_span.lo())), + } + } +} + +impl<'a> Args<'a, Expr> { + fn from_attr_args(value: &'a AttrArgs) -> Self { + match value { + AttrArgs::Empty => Self::Empty, + AttrArgs::Delimited(delim_args) => Self::Delimited(Cow::Borrowed(delim_args)), + AttrArgs::Eq { eq_span, expr } => { + Self::Eq { eq_span: *eq_span, value: Cow::Borrowed(expr), value_span: expr.span } + } + } + } +} + +impl Args<'_, MetaItemLit> { + fn from_attr_args(value: AttrArgs, dcx: DiagCtxtHandle<'_>) -> Self { + match value { + AttrArgs::Empty => Self::Empty, + AttrArgs::Delimited(delim_args) => Self::Delimited(Cow::Owned(delim_args)), + AttrArgs::Eq { eq_span, expr } => Self::Eq { + eq_span, + value: Cow::Owned(expr_to_lit(dcx, &expr)), + value_span: expr.span, + }, + } + } +} + +#[must_use] +pub(crate) struct GenericArgParser<'a, T: Clone> { + args: Args<'a, T>, + dcx: DiagCtxtHandle<'a>, +} + +pub(crate) trait ArgParser<'a> { + type NameValueParser: NameValueParser<'a>; + + /// Asserts that this MetaItem is a list + /// + /// Some examples: + /// + /// - `#[allow(clippy::complexity)]`: `(clippy::complexity)` is a list + /// - `#[rustfmt::skip::macros(target_macro_name)]`: `(target_macro_name)` is a list + fn list<'s, 'r>(&'s self) -> Option> + where + 's: 'r, + 'a: 'r; + + /// Asserts that this MetaItem is a name-value pair. + /// + /// Some examples: + /// + /// - `#[clippy::cyclomatic_complexity = "100"]`: `clippy::cyclomatic_complexity = "100"` is a name value pair, + /// where the name is a path (`clippy::cyclomatic_complexity`). You already checked the path + /// to get an `ArgParser`, so this method will effectively only assert that the `= "100"` is + /// there + /// - `#[doc = "hello"]`: `doc = "hello` is also a name value pair + fn name_value(&self) -> Option; + + /// Asserts that there are no arguments + fn no_args(&self) -> bool; +} + +macro_rules! argparser { + ($ty: ty) => { + impl<'a> ArgParser<'a> for GenericArgParser<'a, $ty> { + type NameValueParser = GenericNameValueParser<'a, $ty>; + + fn list<'s, 'r>(&'s self) -> Option> + where + 's: 'r, + 'a: 'r, + { + match &self.args { + Args::Delimited(args) if args.delim == Delimiter::Parenthesis => Some( + MetaItemListParserContext { + inside_delimiters: args.tokens.trees().peekable(), + dcx: self.dcx, + } + .parse() + .unwrap(), + ), + Args::Delimited(_) | Args::Eq { .. } | Args::Empty => None, + } + } + + fn name_value(&self) -> Option> { + match &self.args { + Args::Eq { eq_span, value, value_span } => Some(GenericNameValueParser { + eq_span: *eq_span, + value: value.to_owned(), + value_span: *value_span, + dcx: self.dcx, + }), + Args::Delimited(_) | Args::Empty => None, + } + } + + fn no_args(&self) -> bool { + matches!(&self.args, Args::Empty) + } + } + }; +} + +argparser!(Expr); +argparser!(MetaItemLit); + +/// Inside lists, values could be either literals, or more deeply nested meta items. +/// This enum represents that. +/// +/// Choose which one you want using the provided methods. +#[derive(Debug)] +pub(crate) enum MetaItemOrLitParser<'a, T: Clone> +where + GenericMetaItemParser<'a, T>: MetaItemParser<'a>, +{ + MetaItemParser(GenericMetaItemParser<'a, T>), + Lit(MetaItemLit), +} + +impl<'a, T: Clone> MetaItemOrLitParser<'a, T> +where + GenericMetaItemParser<'a, T>: MetaItemParser<'a>, +{ + pub(crate) fn span(&self) -> Span { + match self { + MetaItemOrLitParser::MetaItemParser(generic_meta_item_parser) => { + generic_meta_item_parser.span() + } + MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span, + } + } + + pub(crate) fn lit(self) -> Option { + match self { + MetaItemOrLitParser::Lit(meta_item_lit) => Some(meta_item_lit), + _ => None, + } + } + + pub(crate) fn meta_item(self) -> Option> { + match self { + MetaItemOrLitParser::MetaItemParser(parser) => Some(parser), + _ => None, + } + } +} + +/// Utility that deconstructs a MetaItem into usable parts. +/// +/// MetaItems are syntactically extremely flexible, but specific attributes want to parse +/// them in custom, more restricted ways. This can be done using this struct. +/// +/// MetaItems consist of some path, and some args. The args could be empty. In other words: +/// +/// - `name` -> args are empty +/// - `name(...)` -> args are a [`list`](ArgParser::list), which is the bit between the parentheses +/// - `name = value`-> arg is [`name_value`](ArgParser::name_value), where the argument is the +/// `= value` part +/// +/// The syntax of MetaItems can be found at +pub(crate) struct GenericMetaItemParser<'a, T: Clone> { + path: PathParser<'a>, + args: Args<'a, T>, + dcx: DiagCtxtHandle<'a>, +} + +impl<'a, T: Clone + Debug> Debug for GenericMetaItemParser<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("GenericMetaItemParser") + .field("path", &self.path) + .field("args", &self.args) + .finish() + } +} + +impl<'a> GenericMetaItemParser<'a, Expr> { + /// Create a new parser from a [`NormalAttr`], which is stored inside of any + /// [`ast::Attribute`](Attribute) + pub(crate) fn from_attr(attr: &'a NormalAttr, dcx: DiagCtxtHandle<'a>) -> Self { + Self { + path: PathParser::Ast(&attr.item.path), + args: Args::::from_attr_args(&attr.item.args), + dcx, + } + } +} + +pub(crate) trait MetaItemParser<'a>: Debug + 'a { + type ArgParser: ArgParser<'a>; + + fn span(&self) -> Span; + + /// Gets just the path, without the args. + fn path_without_args(&self) -> PathParser<'a>; + + /// Gets just the args parser, without caring about the path. + fn args(&self) -> Self::ArgParser; + + fn deconstruct(&self) -> (PathParser<'a>, Self::ArgParser) { + (self.path_without_args(), self.args()) + } + + /// Asserts that this MetaItem starts with a path. Some examples: + /// + /// - `#[rustfmt::skip]`: `rustfmt::skip` is a path + /// - `#[allow(clippy::complexity)]`: `clippy::complexity` is a path + /// - `#[inline]`: `inline` is a single segment path + /// - `#[inline(always)]`: `always` is a single segment path, but `inline` is *not and + /// should be parsed using [`list`](Self::list) + fn path(&self) -> (PathParser<'a>, Self::ArgParser) { + self.deconstruct() + } + + /// Asserts that this MetaItem starts with a word, or single segment path. + /// Doesn't return the args parser. + /// + /// For examples. see [`Self::word`] + fn word_without_args(&self) -> Option { + Some(self.word()?.0) + } + + /// Like [`word`](Self::word), but returns an empty symbol instead of None + fn word_or_empty_without_args(&self) -> Ident { + self.word_or_empty().0 + } + + /// Asserts that this MetaItem starts with a word, or single segment path. + /// + /// Some examples: + /// - `#[inline]`: `inline` is a word + /// - `#[rustfmt::skip]`: `rustfmt::skip` is a path, and not a word + /// - `#[inline(always)]`: `always` is a word, but `inline` is *not and should be parsed + /// using [`path_list`](Self::path_list) + /// - `#[allow(clippy::complexity)]`: `clippy::complexity` is *not* a word, and should instead be parsed + /// using [`path`](Self::path) + fn word(&self) -> Option<(Ident, Self::ArgParser)> { + let (path, args) = self.deconstruct(); + Some((path.word()?, args)) + } + + /// Like [`word`](Self::word), but returns an empty symbol instead of None + fn word_or_empty(&self) -> (Ident, Self::ArgParser) { + let (path, args) = self.deconstruct(); + (path.word().unwrap_or(Ident::empty()), args) + } + + /// Asserts that this MetaItem starts with some specific word. + /// + /// See [`word`](Self::word) for examples of what a word is. + fn word_is(&self, sym: Symbol) -> Option { + self.path_without_args().word_is(sym).then(|| self.args()) + } + + /// Asserts that this MetaItem starts with some specific path. + /// + /// See [`word`](Self::path) for examples of what a word is. + fn path_is(&self, segments: &[Symbol]) -> Option { + self.path_without_args().segments_is(segments).then(|| self.args()) + } +} + +impl<'a> MetaItemParser<'a> for GenericMetaItemParser<'a, Expr> { + type ArgParser = GenericArgParser<'a, Expr>; + + fn span(&self) -> Span { + if let Some(other) = self.args.span() { + self.path.span().with_hi(other.hi()) + } else { + self.path.span() + } + } + + /// Gets just the path, without the args. + fn path_without_args(&self) -> PathParser<'a> { + self.path.clone() + } + + /// Gets just the args parser, without caring about the path. + fn args(&self) -> GenericArgParser<'a, Expr> { + GenericArgParser { args: self.args.clone(), dcx: self.dcx } + } +} + +impl<'a> MetaItemParser<'a> for GenericMetaItemParser<'a, MetaItemLit> { + type ArgParser = GenericArgParser<'a, MetaItemLit>; + + fn span(&self) -> Span { + if let Some(other) = self.args.span() { + self.path.span().with_hi(other.hi()) + } else { + self.path.span() + } + } + + /// Gets just the path, without the args. + fn path_without_args(&self) -> PathParser<'a> { + self.path.clone() + } + + /// Gets just the args parser, without caring about the path. + fn args(&self) -> GenericArgParser<'a, MetaItemLit> { + GenericArgParser { args: self.args.clone(), dcx: self.dcx } + } +} + +pub(crate) struct GenericNameValueParser<'a, T: Clone> { + pub(crate) eq_span: Span, + value: Cow<'a, T>, + pub(crate) value_span: Span, + dcx: DiagCtxtHandle<'a>, +} + +impl<'a, T: Clone + Debug> Debug for GenericNameValueParser<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("GenericNameValueParser") + .field("eq_span", &self.eq_span) + .field("value", &self.value) + .field("value_span", &self.value_span) + .finish() + } +} + +pub(crate) trait NameValueParser<'a>: Debug { + fn value_span(&self) -> Span; + + fn value_as_lit(&self) -> MetaItemLit; + fn value_as_str(&self) -> Option { + self.value_as_lit().kind.str() + } +} + +impl<'a> NameValueParser<'a> for GenericNameValueParser<'a, Expr> { + fn value_as_lit(&self) -> MetaItemLit { + expr_to_lit(self.dcx, &self.value) + } + + fn value_span(&self) -> Span { + self.value_span + } +} + +impl<'a> NameValueParser<'a> for GenericNameValueParser<'a, MetaItemLit> { + fn value_as_lit(&self) -> MetaItemLit { + self.value.clone().into_owned() + } + fn value_span(&self) -> Span { + self.value_span + } +} + +fn expr_to_lit(dcx: DiagCtxtHandle<'_>, expr: &Expr) -> MetaItemLit { + // In valid code the value always ends up as a single literal. Otherwise, a dummy + // literal suffices because the error is handled elsewhere. + if let ExprKind::Lit(token_lit) = expr.kind + && let Ok(lit) = MetaItemLit::from_token_lit(token_lit, expr.span) + { + lit + } else { + let guar = dcx.has_errors().unwrap(); + MetaItemLit { symbol: kw::Empty, suffix: None, kind: LitKind::Err(guar), span: DUMMY_SP } + } +} + +struct MetaItemListParserContext<'a> { + // the tokens inside the delimiters, so `#[some::attr(a b c)]` would have `a b c` inside + inside_delimiters: Peekable>, + dcx: DiagCtxtHandle<'a>, +} + +impl<'a> MetaItemListParserContext<'a> { + fn done(&mut self) -> bool { + self.inside_delimiters.peek().is_none() + } + + fn next_path(&mut self) -> Option { + // FIXME: Share code with `parse_path`. + let tt = self.inside_delimiters.next().map(|tt| TokenTree::uninterpolate(tt)); + + match tt.as_deref() { + Some(&TokenTree::Token( + Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span }, + _, + )) => { + let mut segments = if let &token::Ident(name, _) = kind { + if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = + self.inside_delimiters.peek() + { + self.inside_delimiters.next(); + vec![Ident::new(name, span)] + } else { + return Some(AttrPath { + segments: vec![Ident::new(name, span)].into_boxed_slice(), + span, + }); + } + } else { + vec![Ident::new(kw::PathRoot, span)] + }; + loop { + if let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) = + self.inside_delimiters + .next() + .map(|tt| TokenTree::uninterpolate(tt)) + .as_deref() + { + segments.push(Ident::new(name, span)); + } else { + return None; + } + if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = + self.inside_delimiters.peek() + { + self.inside_delimiters.next(); + } else { + break; + } + } + let span = span.with_hi(segments.last().unwrap().span.hi()); + Some(AttrPath { segments: segments.into_boxed_slice(), span }) + } + Some(TokenTree::Token( + Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, + _, + )) => { + panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tt); + } + _ => return None, + } + } + + fn value(&mut self) -> Option { + match self.inside_delimiters.next() { + Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => { + MetaItemListParserContext { + inside_delimiters: inner_tokens.trees().peekable(), + dcx: self.dcx, + } + .value() + } + Some(TokenTree::Token(ref token, _)) => MetaItemLit::from_token(token), + _ => None, + } + } + + fn next(&mut self) -> Option> { + // a list element is either a literal + if let Some(TokenTree::Token(token, _)) = self.inside_delimiters.peek() + && let Some(lit) = MetaItemLit::from_token(token) + { + self.inside_delimiters.next(); + return Some(MetaItemOrLitParser::Lit(lit)); + } + + // or a path. + let path = self.next_path()?; + + // Paths can be followed by: + // - `(more meta items)` (another list) + // - `= lit` (a name-value) + // - nothing + Some(MetaItemOrLitParser::MetaItemParser(match self.inside_delimiters.peek() { + Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &**nt { + token::Nonterminal::NtMeta(item) => GenericMetaItemParser { + path: PathParser::Ast(&item.path), + args: Args::::from_attr_args(item.args.clone(), self.dcx), + dcx: self.dcx, + }, + token::Nonterminal::NtPath(path) => GenericMetaItemParser { + path: PathParser::Ast(path), + args: Args::Empty, + dcx: self.dcx, + }, + _ => return None, + }, + Some(TokenTree::Delimited(dspan, _, delim @ Delimiter::Parenthesis, inner_tokens)) => { + let inner_tokens = inner_tokens.clone(); + self.inside_delimiters.next(); + + GenericMetaItemParser { + path: PathParser::Attr(path), + args: Args::Delimited(Cow::Owned(DelimArgs { + dspan: *dspan, + delim: *delim, + tokens: inner_tokens, + })), + dcx: self.dcx, + } + } + // FIXME(jdonszelmann) nice error? + // tests seem to say its already parsed and rejected maybe? + Some(TokenTree::Delimited(..)) => return None, + Some(TokenTree::Token(Token { kind: token::Eq, span }, _)) => { + self.inside_delimiters.next(); + let value = self.value()?; + GenericMetaItemParser { + path: PathParser::Attr(path), + args: Args::Eq { + eq_span: *span, + value_span: value.span, + value: Cow::Owned(value), + }, + dcx: self.dcx, + } + } + _ => GenericMetaItemParser { + path: PathParser::Attr(path), + args: Args::Empty, + dcx: self.dcx, + }, + })) + } + + pub(crate) fn parse(mut self) -> Option> { + let mut sub_parsers = Vec::new(); + + while !self.done() { + sub_parsers.push(self.next()?); + match self.inside_delimiters.next() { + None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {} + _ => return None, + } + } + + Some(MetaItemListParser { sub_parsers }) + } +} + +#[derive(Debug)] +pub(crate) struct MetaItemListParser<'a> { + sub_parsers: Vec>, +} + +impl<'a> MetaItemListParser<'a> { + /// Lets you pick and choose as what you want to parse each element in the list + pub(crate) fn mixed(self) -> impl Iterator> + 'a { + self.sub_parsers.into_iter() + } + + pub(crate) fn len(&self) -> usize { + self.sub_parsers.len() + } + + pub(crate) fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Asserts that every item in the list is another list starting with a word. + /// + /// See [`MetaItemParser::word`] for examples of words. + pub(crate) fn all_word_list(self) -> Option)>> { + self.mixed().map(|i| i.meta_item()?.word()).collect() + } + + /// Asserts that every item in the list is another list starting with a full path. + /// + /// See [`MetaItemParser::path`] for examples of paths. + pub(crate) fn all_path_list( + self, + ) -> Option, GenericArgParser<'a, MetaItemLit>)>> { + self.mixed().map(|i| Some(i.meta_item()?.path())).collect() + } + + /// Returns Some if the list contains only a single element. + /// + /// Inside the Some is the parser to parse this single element. + pub(crate) fn single(self) -> Option> { + let mut iter = self.mixed(); + iter.next().filter(|_| iter.next().is_none()) + } +} diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs similarity index 65% rename from compiler/rustc_attr/src/session_diagnostics.rs rename to compiler/rustc_attr_parsing/src/session_diagnostics.rs index 9d08a9f575425..50dda0d0abafa 100644 --- a/compiler/rustc_attr/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -6,17 +6,25 @@ use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuar use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; -use crate::{UnsupportedLiteralReason, fluent_generated as fluent}; +use crate::fluent_generated as fluent; + +pub(crate) enum UnsupportedLiteralReason { + Generic, + CfgString, + CfgBoolean, + DeprecatedString, + DeprecatedKvPair, +} #[derive(Diagnostic)] -#[diag(attr_expected_one_cfg_pattern, code = E0536)] +#[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)] pub(crate) struct ExpectedOneCfgPattern { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_invalid_predicate, code = E0537)] +#[diag(attr_parsing_invalid_predicate, code = E0537)] pub(crate) struct InvalidPredicate { #[primary_span] pub span: Span, @@ -25,7 +33,7 @@ pub(crate) struct InvalidPredicate { } #[derive(Diagnostic)] -#[diag(attr_multiple_item, code = E0538)] +#[diag(attr_parsing_multiple_item, code = E0538)] pub(crate) struct MultipleItem { #[primary_span] pub span: Span, @@ -34,10 +42,25 @@ pub(crate) struct MultipleItem { } #[derive(Diagnostic)] -#[diag(attr_incorrect_meta_item, code = E0539)] +#[diag(attr_parsing_incorrect_meta_item, code = E0539)] pub(crate) struct IncorrectMetaItem { #[primary_span] pub span: Span, + + #[subdiagnostic] + pub suggestion: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + attr_parsing_incorrect_meta_item_suggestion, + applicability = "maybe-incorrect" +)] +pub(crate) struct IncorrectMetaItemSuggestion { + #[suggestion_part(code = "\"")] + pub lo: Span, + #[suggestion_part(code = "\"")] + pub hi: Span, } /// Error code: E0541 @@ -51,38 +74,38 @@ pub(crate) struct UnknownMetaItem<'a> { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnknownMetaItem<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::>(); - Diag::new(dcx, level, fluent::attr_unknown_meta_item) + Diag::new(dcx, level, fluent::attr_parsing_unknown_meta_item) .with_span(self.span) .with_code(E0541) .with_arg("item", self.item) .with_arg("expected", expected.join(", ")) - .with_span_label(self.span, fluent::attr_label) + .with_span_label(self.span, fluent::attr_parsing_label) } } #[derive(Diagnostic)] -#[diag(attr_missing_since, code = E0542)] +#[diag(attr_parsing_missing_since, code = E0542)] pub(crate) struct MissingSince { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_missing_note, code = E0543)] +#[diag(attr_parsing_missing_note, code = E0543)] pub(crate) struct MissingNote { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_multiple_stability_levels, code = E0544)] +#[diag(attr_parsing_multiple_stability_levels, code = E0544)] pub(crate) struct MultipleStabilityLevels { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_invalid_issue_string, code = E0545)] +#[diag(attr_parsing_invalid_issue_string, code = E0545)] pub(crate) struct InvalidIssueString { #[primary_span] pub span: Span, @@ -95,31 +118,31 @@ pub(crate) struct InvalidIssueString { // translatable. #[derive(Subdiagnostic)] pub(crate) enum InvalidIssueStringCause { - #[label(attr_must_not_be_zero)] + #[label(attr_parsing_must_not_be_zero)] MustNotBeZero { #[primary_span] span: Span, }, - #[label(attr_empty)] + #[label(attr_parsing_empty)] Empty { #[primary_span] span: Span, }, - #[label(attr_invalid_digit)] + #[label(attr_parsing_invalid_digit)] InvalidDigit { #[primary_span] span: Span, }, - #[label(attr_pos_overflow)] + #[label(attr_parsing_pos_overflow)] PosOverflow { #[primary_span] span: Span, }, - #[label(attr_neg_overflow)] + #[label(attr_parsing_neg_overflow)] NegOverflow { #[primary_span] span: Span, @@ -140,21 +163,21 @@ impl InvalidIssueStringCause { } #[derive(Diagnostic)] -#[diag(attr_missing_feature, code = E0546)] +#[diag(attr_parsing_missing_feature, code = E0546)] pub(crate) struct MissingFeature { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_non_ident_feature, code = E0546)] +#[diag(attr_parsing_non_ident_feature, code = E0546)] pub(crate) struct NonIdentFeature { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_missing_issue, code = E0547)] +#[diag(attr_parsing_missing_issue, code = E0547)] pub(crate) struct MissingIssue { #[primary_span] pub span: Span, @@ -163,20 +186,20 @@ pub(crate) struct MissingIssue { // FIXME: Why is this the same error code as `InvalidReprHintNoParen` and `InvalidReprHintNoValue`? // It is more similar to `IncorrectReprFormatGeneric`. #[derive(Diagnostic)] -#[diag(attr_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)] +#[diag(attr_parsing_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)] pub(crate) struct IncorrectReprFormatPackedOneOrZeroArg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_incorrect_repr_format_packed_expect_integer, code = E0552)] +#[diag(attr_parsing_incorrect_repr_format_packed_expect_integer, code = E0552)] pub(crate) struct IncorrectReprFormatPackedExpectInteger { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_invalid_repr_hint_no_paren, code = E0552)] +#[diag(attr_parsing_invalid_repr_hint_no_paren, code = E0552)] pub(crate) struct InvalidReprHintNoParen { #[primary_span] pub span: Span, @@ -185,7 +208,7 @@ pub(crate) struct InvalidReprHintNoParen { } #[derive(Diagnostic)] -#[diag(attr_invalid_repr_hint_no_value, code = E0552)] +#[diag(attr_parsing_invalid_repr_hint_no_value, code = E0552)] pub(crate) struct InvalidReprHintNoValue { #[primary_span] pub span: Span, @@ -204,14 +227,18 @@ pub(crate) struct UnsupportedLiteral { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let mut diag = Diag::new(dcx, level, match self.reason { - UnsupportedLiteralReason::Generic => fluent::attr_unsupported_literal_generic, - UnsupportedLiteralReason::CfgString => fluent::attr_unsupported_literal_cfg_string, - UnsupportedLiteralReason::CfgBoolean => fluent::attr_unsupported_literal_cfg_boolean, + UnsupportedLiteralReason::Generic => fluent::attr_parsing_unsupported_literal_generic, + UnsupportedLiteralReason::CfgString => { + fluent::attr_parsing_unsupported_literal_cfg_string + } + UnsupportedLiteralReason::CfgBoolean => { + fluent::attr_parsing_unsupported_literal_cfg_boolean + } UnsupportedLiteralReason::DeprecatedString => { - fluent::attr_unsupported_literal_deprecated_string + fluent::attr_parsing_unsupported_literal_deprecated_string } UnsupportedLiteralReason::DeprecatedKvPair => { - fluent::attr_unsupported_literal_deprecated_kv_pair + fluent::attr_parsing_unsupported_literal_deprecated_kv_pair } }); diag.span(self.span); @@ -219,7 +246,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { if self.is_bytestr { diag.span_suggestion( self.start_point_span, - fluent::attr_unsupported_literal_suggestion, + fluent::attr_parsing_unsupported_literal_suggestion, "", Applicability::MaybeIncorrect, ); @@ -229,7 +256,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { } #[derive(Diagnostic)] -#[diag(attr_invalid_repr_align_need_arg, code = E0589)] +#[diag(attr_parsing_invalid_repr_align_need_arg, code = E0589)] pub(crate) struct InvalidReprAlignNeedArg { #[primary_span] #[suggestion(code = "align(...)", applicability = "has-placeholders")] @@ -237,7 +264,7 @@ pub(crate) struct InvalidReprAlignNeedArg { } #[derive(Diagnostic)] -#[diag(attr_invalid_repr_generic, code = E0589)] +#[diag(attr_parsing_invalid_repr_generic, code = E0589)] pub(crate) struct InvalidReprGeneric<'a> { #[primary_span] pub span: Span, @@ -247,21 +274,21 @@ pub(crate) struct InvalidReprGeneric<'a> { } #[derive(Diagnostic)] -#[diag(attr_incorrect_repr_format_align_one_arg, code = E0693)] +#[diag(attr_parsing_incorrect_repr_format_align_one_arg, code = E0693)] pub(crate) struct IncorrectReprFormatAlignOneArg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_incorrect_repr_format_expect_literal_integer, code = E0693)] +#[diag(attr_parsing_incorrect_repr_format_expect_literal_integer, code = E0693)] pub(crate) struct IncorrectReprFormatExpectInteger { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_incorrect_repr_format_generic, code = E0693)] +#[diag(attr_parsing_incorrect_repr_format_generic, code = E0693)] pub(crate) struct IncorrectReprFormatGeneric<'a> { #[primary_span] pub span: Span, @@ -274,7 +301,11 @@ pub(crate) struct IncorrectReprFormatGeneric<'a> { #[derive(Subdiagnostic)] pub(crate) enum IncorrectReprFormatGenericCause<'a> { - #[suggestion(attr_suggestion, code = "{name}({int})", applicability = "machine-applicable")] + #[suggestion( + attr_parsing_suggestion, + code = "{name}({int})", + applicability = "machine-applicable" + )] Int { #[primary_span] span: Span, @@ -286,7 +317,11 @@ pub(crate) enum IncorrectReprFormatGenericCause<'a> { int: u128, }, - #[suggestion(attr_suggestion, code = "{name}({symbol})", applicability = "machine-applicable")] + #[suggestion( + attr_parsing_suggestion, + code = "{name}({symbol})", + applicability = "machine-applicable" + )] Symbol { #[primary_span] span: Span, @@ -312,35 +347,28 @@ impl<'a> IncorrectReprFormatGenericCause<'a> { } #[derive(Diagnostic)] -#[diag(attr_rustc_promotable_pairing, code = E0717)] +#[diag(attr_parsing_rustc_promotable_pairing, code = E0717)] pub(crate) struct RustcPromotablePairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_rustc_const_stable_indirect_pairing)] -pub(crate) struct RustcConstStableIndirectPairing { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(attr_rustc_allowed_unstable_pairing, code = E0789)] +#[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)] pub(crate) struct RustcAllowedUnstablePairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_cfg_predicate_identifier)] +#[diag(attr_parsing_cfg_predicate_identifier)] pub(crate) struct CfgPredicateIdentifier { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_deprecated_item_suggestion)] +#[diag(attr_parsing_deprecated_item_suggestion)] pub(crate) struct DeprecatedItemSuggestion { #[primary_span] pub span: Span, @@ -353,21 +381,21 @@ pub(crate) struct DeprecatedItemSuggestion { } #[derive(Diagnostic)] -#[diag(attr_expected_single_version_literal)] +#[diag(attr_parsing_expected_single_version_literal)] pub(crate) struct ExpectedSingleVersionLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_expected_version_literal)] +#[diag(attr_parsing_expected_version_literal)] pub(crate) struct ExpectedVersionLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_expects_feature_list)] +#[diag(attr_parsing_expects_feature_list)] pub(crate) struct ExpectsFeatureList { #[primary_span] pub span: Span, @@ -376,7 +404,7 @@ pub(crate) struct ExpectsFeatureList { } #[derive(Diagnostic)] -#[diag(attr_expects_features)] +#[diag(attr_parsing_expects_features)] pub(crate) struct ExpectsFeatures { #[primary_span] pub span: Span, @@ -385,22 +413,48 @@ pub(crate) struct ExpectsFeatures { } #[derive(Diagnostic)] -#[diag(attr_invalid_since)] +#[diag(attr_parsing_invalid_since)] pub(crate) struct InvalidSince { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_soft_no_args)] +#[diag(attr_parsing_soft_no_args)] pub(crate) struct SoftNoArgs { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_unknown_version_literal)] +#[diag(attr_parsing_unknown_version_literal)] pub(crate) struct UnknownVersionLiteral { #[primary_span] pub span: Span, } + +// FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated. +#[derive(Diagnostic)] +#[diag(attr_parsing_unused_multiple)] +pub(crate) struct UnusedMultiple { + #[primary_span] + #[suggestion(code = "", applicability = "machine-applicable")] + pub this: Span, + #[note] + pub other: Span, + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_stability_outside_std, code = E0734)] +pub(crate) struct StabilityOutsideStd { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(attr_parsing_empty_confusables)] +pub(crate) struct EmptyConfusables { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_builtin_macros/Cargo.toml b/compiler/rustc_builtin_macros/Cargo.toml index ef48486f6f150..5650c5083cf08 100644 --- a/compiler/rustc_builtin_macros/Cargo.toml +++ b/compiler/rustc_builtin_macros/Cargo.toml @@ -14,12 +14,13 @@ doctest = false # tidy-alphabetical-start rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } +rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 15993dbf5ecc3..6e90f1682e309 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -7,7 +7,7 @@ use rustc_ast::tokenstream::TokenStream; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_span::Span; -use {rustc_ast as ast, rustc_attr as attr}; +use {rustc_ast as ast, rustc_attr_parsing as attr}; use crate::errors; diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 3bd8f899a4afb..59e77937c1338 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -6,9 +6,10 @@ use rustc_ast::{ self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem, TraitBoundModifiers, VariantData, WherePredicate, }; -use rustc_attr as attr; +use rustc_attr_parsing::{AttributeParseContext, AttributeKind, ReprTransparent}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_hir::Attribute; use rustc_macros::Diagnostic; use rustc_span::symbol::{Ident, sym}; use rustc_span::{Span, Symbol}; @@ -33,11 +34,11 @@ pub(crate) fn expand_deriving_coerce_pointee( let (name_ident, generics) = if let Annotatable::Item(aitem) = item && let ItemKind::Struct(struct_data, g) = &aitem.kind { - let is_transparent = aitem.attrs.iter().any(|attr| { - attr::find_repr_attrs(cx.sess, attr) - .into_iter() - .any(|r| matches!(r, attr::ReprTransparent)) - }); + let is_transparent = matches!( + AttributeParseContext::parse_limited(cx.sess, &aitem.attrs, sym::repr, span), + Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.contains(&ReprTransparent) + ); + if !is_transparent { cx.dcx().emit_err(RequireTransparent { span }); return; diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 846d8784dea52..929ab004df6e8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -182,10 +182,10 @@ pub(crate) use StaticFields::*; pub(crate) use SubstructureFields::*; use rustc_ast::ptr::P; use rustc_ast::{ - self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, - Generics, Mutability, PatKind, VariantData, + self as ast, AnonConst, BindingMode, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, Mutability, PatKind, VariantData }; -use rustc_attr as attr; +use rustc_hir::Attribute; +use rustc_attr_parsing::{AttributeParseContext, AttributeKind, ReprPacked}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::{DUMMY_SP, Span}; @@ -481,14 +481,10 @@ impl<'a> TraitDef<'a> { ) { match item { Annotatable::Item(item) => { - let is_packed = item.attrs.iter().any(|attr| { - for r in attr::find_repr_attrs(cx.sess, attr) { - if let attr::ReprPacked(_) = r { - return true; - } - } - false - }); + let is_packed = matches!( + AttributeParseContext::parse_limited(cx.sess, &item.attrs, sym::repr, item.span), + Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|x| matches!(x, ReprPacked(..))) + ); let newitem = match &item.kind { ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def( diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs index d20e13e15b944..028a5ab5f716b 100644 --- a/compiler/rustc_codegen_gcc/src/attributes.rs +++ b/compiler/rustc_codegen_gcc/src/attributes.rs @@ -2,8 +2,8 @@ use gccjit::FnAttribute; use gccjit::Function; #[cfg(feature = "master")] -use rustc_attr::InlineAttr; -use rustc_attr::InstructionSetAttr; +use rustc_attr_parsing::InlineAttr; +use rustc_attr_parsing::InstructionSetAttr; #[cfg(feature = "master")] use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty; diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 452e92bffa235..7fd5db0edbb95 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -35,7 +35,7 @@ extern crate tracing; extern crate rustc_abi; extern crate rustc_apfloat; extern crate rustc_ast; -extern crate rustc_attr; +extern crate rustc_attr_parsing; extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_errors; diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 03a871297c481..689986d642d90 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -16,7 +16,7 @@ object = { version = "0.36.3", default-features = false, features = ["std", "rea rustc-demangle = "0.1.21" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index cb958c1d4d771..9c3d6cd18e677 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -1,6 +1,6 @@ //! Set and unset common attributes on LLVM values. -use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_codegen_ssa::traits::*; use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry}; diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index ec77f32caf49a..aa9a0f34f55e9 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -106,7 +106,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t // This is a monomorphization of a generic function. if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_fn_attrs(instance_def_id).inline - == rustc_attr::InlineAttr::Never) + == rustc_attr_parsing::InlineAttr::Never) { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index c81e36dfc8d0a..d24acd1799f70 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -18,12 +18,13 @@ rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index 11bcd727501c9..0dd6d444dea19 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -26,9 +26,9 @@ use std::borrow::Cow; use std::fmt; -use rustc_ast as ast; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::{DiagArgValue, IntoDiagArg}; +use rustc_hir as hir; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::mir::mono::CodegenUnitNameBuilder; use rustc_middle::ty::TyCtxt; @@ -77,7 +77,7 @@ struct AssertModuleSource<'tcx> { } impl<'tcx> AssertModuleSource<'tcx> { - fn check_attr(&mut self, attr: &ast::Attribute) { + fn check_attr(&mut self, attr: &hir::Attribute) { let (expected_reuse, comp_kind) = if attr.has_name(sym::rustc_partition_reused) { (CguReuse::PreLto, ComparisonKind::AtLeast) } else if attr.has_name(sym::rustc_partition_codegened) { @@ -91,7 +91,7 @@ impl<'tcx> AssertModuleSource<'tcx> { other => { self.tcx .dcx() - .emit_fatal(errors::UnknownReuseKind { span: attr.span, kind: other }); + .emit_fatal(errors::UnknownReuseKind { span: attr.span(), kind: other }); } } } else { @@ -99,7 +99,7 @@ impl<'tcx> AssertModuleSource<'tcx> { }; if !self.tcx.sess.opts.unstable_opts.query_dep_graph { - self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span }); + self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span: attr.span() }); } if !self.check_config(attr) { @@ -112,7 +112,7 @@ impl<'tcx> AssertModuleSource<'tcx> { if !user_path.starts_with(&crate_name) { self.tcx.dcx().emit_fatal(errors::MalformedCguName { - span: attr.span, + span: attr.span(), user_path, crate_name, }); @@ -142,7 +142,7 @@ impl<'tcx> AssertModuleSource<'tcx> { let cgu_names: Vec<&str> = self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord(); self.tcx.dcx().emit_err(errors::NoModuleNamed { - span: attr.span, + span: attr.span(), user_path, cgu_name, cgu_names: cgu_names.join(", "), @@ -152,13 +152,13 @@ impl<'tcx> AssertModuleSource<'tcx> { self.cgu_reuse_tracker.set_expectation( cgu_name, user_path, - attr.span, + attr.span(), expected_reuse, comp_kind, ); } - fn field(&self, attr: &ast::Attribute, name: Symbol) -> Symbol { + fn field(&self, attr: &hir::Attribute, name: Symbol) -> Symbol { for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { if item.has_name(name) { if let Some(value) = item.value_str() { @@ -172,12 +172,12 @@ impl<'tcx> AssertModuleSource<'tcx> { } } - self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span, name }); + self.tcx.dcx().emit_fatal(errors::NoField { span: attr.span(), name }); } /// Scan for a `cfg="foo"` attribute and check whether we have a /// cfg flag called `foo`. - fn check_config(&self, attr: &ast::Attribute) -> bool { + fn check_config(&self, attr: &hir::Attribute) -> bool { let config = &self.tcx.sess.psess.config; let value = self.field(attr, sym::cfg); debug!("check_config(config={:?}, value={:?})", config, value); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 35d18d0206dbb..be2e55efc6329 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2990,7 +2990,7 @@ fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { match lib.cfg { - Some(ref cfg) => rustc_attr::cfg_matches(cfg, sess, CRATE_NODE_ID, None), + Some(ref cfg) => rustc_attr_parsing::cfg_matches(cfg, sess, CRATE_NODE_ID, None), None => true, } } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 788a8a13b3ee4..60ab291935256 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -311,7 +311,8 @@ fn exported_symbols_provider_local( } if !tcx.sess.opts.share_generics() { - if tcx.codegen_fn_attrs(mono_item.def_id()).inline == rustc_attr::InlineAttr::Never + if tcx.codegen_fn_attrs(mono_item.def_id()).inline + == rustc_attr_parsing::InlineAttr::Never { // this is OK, we explicitly allow sharing inline(never) across crates even // without share-generics. diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 27c9cb0b31edd..9dae34fb94444 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -5,11 +5,11 @@ use std::time::{Duration, Instant}; use itertools::Itertools; use rustc_abi::FIRST_VARIANT; use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, AllocatorKind, global_fn_name}; -use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{Lrc, par_map}; use rustc_data_structures::unord::UnordMap; +use rustc_attr_parsing::OptimizeAttr; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_metadata::EncodedMetadata; @@ -31,6 +31,7 @@ use rustc_trait_selection::infer::at::ToTrace; use rustc_trait_selection::infer::{BoundRegionConversionTime, TyCtxtInferExt}; use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use tracing::{debug, info}; +use rustc_ast as ast; use crate::assert_module_sources::CguReuse; use crate::back::link::are_upstream_rust_objects_already_included; @@ -873,7 +874,8 @@ impl CrateInfo { crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect(); let local_crate_name = tcx.crate_name(LOCAL_CRATE); let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); - let subsystem = attr::first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem); + let subsystem = + ast::attr::first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem); let windows_subsystem = subsystem.map(|subsystem| { if subsystem != sym::windows && subsystem != sym::console { tcx.dcx().emit_fatal(errors::InvalidWindowsSubsystem { subsystem }); @@ -1056,8 +1058,8 @@ pub(crate) fn provide(providers: &mut Providers) { let any_for_speed = defids.items().any(|id| { let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id); match optimize { - attr::OptimizeAttr::None | attr::OptimizeAttr::Size => false, - attr::OptimizeAttr::Speed => true, + OptimizeAttr::None | OptimizeAttr::Size => false, + OptimizeAttr::Speed => true, } }); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 11744eabab0da..2b62bc8f2840b 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -1,12 +1,14 @@ -use rustc_ast::{MetaItemInner, MetaItemKind, ast, attr}; -use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr, list_contains_name}; +use rustc_ast::attr::list_contains_name; +use rustc_ast::{MetaItemInner, attr}; +use rustc_attr_parsing::ReprAttr::ReprAlign; +use rustc_attr_parsing::{AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; -use rustc_hir::{self as hir, HirId, LangItem, lang_items}; +use rustc_hir::{self as hir, lang_items, HirId, LangItem}; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, }; @@ -92,12 +94,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if let Fn | AssocFn | Variant | Ctor(..) = def_kind { Some(tcx.fn_sig(did)) } else { - tcx.dcx() - .span_delayed_bug(attr.span, "this attribute can only be applied to functions"); + tcx.dcx().span_delayed_bug( + attr.span(), + "this attribute can only be applied to functions", + ); None } }; + if let hir::Attribute::Parsed(p) = attr { + match p { + AttributeKind::Repr(reprs) => { + codegen_fn_attrs.alignment = reprs + .iter() + .find_map(|r| if let ReprAlign(x) = r { Some(*x) } else { None }); + } + + _ => {} + } + } + let Some(Ident { name, .. }) = attr.ident() else { continue; }; @@ -118,14 +134,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if tcx.opt_item_name(did.to_def_id()).is_some() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; mixed_export_name_no_mangle_lint_state.track_no_mangle( - attr.span, + attr.span(), tcx.local_def_id_to_hir_id(did), attr, ); } else { tcx.dcx() .struct_span_err( - attr.span, + attr.span(), format!( "`#[no_mangle]` cannot be used on {} {} as it has no name", tcx.def_descr_article(did.to_def_id()), @@ -146,7 +162,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { feature_err( &tcx.sess, sym::used_with_arg, - attr.span, + attr.span(), "`#[used(linker)]` is currently unstable", ) .emit(); @@ -158,7 +174,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { feature_err( &tcx.sess, sym::used_with_arg, - attr.span, + attr.span(), "`#[used(compiler)]` is currently unstable", ) .emit(); @@ -166,7 +182,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; } Some(_) => { - tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span }); + tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span() }); } None => { // Unfortunately, unconditionally using `llvm.used` causes @@ -211,7 +227,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0737, "`#[track_caller]` requires Rust ABI" ) @@ -219,12 +235,12 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } if is_closure && !tcx.features().closure_track_caller() - && !attr.span.allows_unstable(sym::closure_track_caller) + && !attr.span().allows_unstable(sym::closure_track_caller) { feature_err( &tcx.sess, sym::closure_track_caller, - attr.span, + attr.span(), "`#[track_caller]` on closures is currently unstable", ) .emit(); @@ -238,14 +254,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // so it may not contain any null characters. struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0648, "`export_name` may not contain null characters" ) .emit(); } codegen_fn_attrs.export_name = Some(s); - mixed_export_name_no_mangle_lint_state.track_export_name(attr.span); + mixed_export_name_no_mangle_lint_state.track_export_name(attr.span()); } } sym::target_feature => { @@ -278,13 +294,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { feature_err( &tcx.sess, sym::target_feature_11, - attr.span, + attr.span(), "`#[target_feature(..)]` can only be applied to `unsafe` functions", ) .with_span_label(tcx.def_span(did), "not an `unsafe` function") .emit(); } else { - check_target_feature_trait_unsafe(tcx, did, attr.span); + check_target_feature_trait_unsafe(tcx, did, attr.span()); } } from_target_feature_attr( @@ -302,7 +318,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if tcx.is_mutable_static(did.into()) { let mut diag = tcx.dcx().struct_span_err( - attr.span, + attr.span(), "extern mutable statics are not allowed with `#[linkage]`", ); diag.note( @@ -321,7 +337,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if let Some(val) = attr.value_str() { if val.as_str().bytes().any(|b| b == 0) { let msg = format!("illegal null byte in link_section value: `{val}`"); - tcx.dcx().span_err(attr.span, msg); + tcx.dcx().span_err(attr.span(), msg); } else { codegen_fn_attrs.link_section = Some(val); } @@ -329,13 +345,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } sym::link_name => codegen_fn_attrs.link_name = attr.value_str(), sym::link_ordinal => { - link_ordinal_span = Some(attr.span); + link_ordinal_span = Some(attr.span()); if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) { codegen_fn_attrs.link_ordinal = ordinal; } } sym::no_sanitize => { - no_sanitize_span = Some(attr.span); + no_sanitize_span = Some(attr.span()); if let Some(list) = attr.meta_item_list() { for item in list.iter() { match item.name_or_empty() { @@ -372,7 +388,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if !tcx.sess.target.has_thumb_interworking { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0779, "target does not support `#[instruction_set]`" ) @@ -389,7 +405,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { _ => { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0779, "invalid instruction set specified", ) @@ -401,7 +417,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { [] => { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0778, "`#[instruction_set]` requires an argument" ) @@ -411,7 +427,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { _ => { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0779, "cannot specify more than one instruction set" ) @@ -420,27 +436,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } }) } - sym::repr => { - codegen_fn_attrs.alignment = if let Some(items) = attr.meta_item_list() - && let [item] = items.as_slice() - && let Some((sym::align, literal)) = item.singleton_lit_list() - { - rustc_attr::parse_alignment(&literal.kind) - .map_err(|msg| { - struct_span_code_err!( - tcx.dcx(), - literal.span, - E0589, - "invalid `repr(align)` attribute: {}", - msg - ) - .emit(); - }) - .ok() - } else { - None - }; - } sym::patchable_function_entry => { codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| { let mut prefix = None; @@ -506,7 +501,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } if let (None, None) = (prefix, entry) { - tcx.dcx().span_err(attr.span, "must specify at least one parameter"); + tcx.dcx().span_err(attr.span(), "must specify at least one parameter"); } Some(PatchableFunctionEntry::from_prefix_and_entry( @@ -525,28 +520,27 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { if !attr.has_name(sym::inline) { return ia; } - match attr.meta_kind() { - Some(MetaItemKind::Word) => InlineAttr::Hint, - Some(MetaItemKind::List(ref items)) => { - inline_span = Some(attr.span); - if items.len() != 1 { - struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument") - .emit(); - InlineAttr::None - } else if list_contains_name(items, sym::always) { - InlineAttr::Always - } else if list_contains_name(items, sym::never) { - InlineAttr::Never - } else { - struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument") - .with_help("valid inline arguments are `always` and `never`") - .emit(); + if attr.is_word() { + InlineAttr::Hint + } else if let Some(ref items) = attr.meta_item_list() { + inline_span = Some(attr.span()); + if items.len() != 1 { + struct_span_code_err!(tcx.dcx(), attr.span(), E0534, "expected one argument") + .emit(); + InlineAttr::None + } else if list_contains_name(items, sym::always) { + InlineAttr::Always + } else if list_contains_name(items, sym::never) { + InlineAttr::Never + } else { + struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument") + .with_help("valid inline arguments are `always` and `never`") + .emit(); - InlineAttr::None - } + InlineAttr::None } - Some(MetaItemKind::NameValue(_)) => ia, - None => ia, + } else { + ia } }); @@ -555,27 +549,24 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { return ia; } let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit(); - match attr.meta_kind() { - Some(MetaItemKind::Word) => { - err(attr.span, "expected one argument"); - ia - } - Some(MetaItemKind::List(ref items)) => { - inline_span = Some(attr.span); - if items.len() != 1 { - err(attr.span, "expected one argument"); - OptimizeAttr::None - } else if list_contains_name(items, sym::size) { - OptimizeAttr::Size - } else if list_contains_name(items, sym::speed) { - OptimizeAttr::Speed - } else { - err(items[0].span(), "invalid argument"); - OptimizeAttr::None - } + if attr.is_word() { + err(attr.span(), "expected one argument"); + ia + } else if let Some(ref items) = attr.meta_item_list() { + inline_span = Some(attr.span()); + if items.len() != 1 { + err(attr.span(), "expected one argument"); + OptimizeAttr::None + } else if list_contains_name(items, sym::size) { + OptimizeAttr::Size + } else if list_contains_name(items, sym::speed) { + OptimizeAttr::Speed + } else { + err(items[0].span(), "invalid argument"); + OptimizeAttr::None } - Some(MetaItemKind::NameValue(_)) => ia, - None => ia, + } else { + OptimizeAttr::None } }); @@ -682,7 +673,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let span = tcx .get_attrs(did, sym::target_feature) .next() - .map_or_else(|| tcx.def_span(did), |a| a.span); + .map_or_else(|| tcx.def_span(did), |a| a.span()); tcx.dcx() .create_err(errors::TargetFeatureDisableOrEnable { features, @@ -727,14 +718,14 @@ fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { false } -fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { +fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Option { use rustc_ast::{LitIntType, LitKind, MetaItemLit}; let meta_item_list = attr.meta_item_list(); let meta_item_list = meta_item_list.as_deref(); let sole_meta_list = match meta_item_list { Some([item]) => item.lit(), Some(_) => { - tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span }); + tcx.dcx().emit_err(errors::InvalidLinkOrdinalNargs { span: attr.span() }); return None; } _ => None, @@ -760,13 +751,13 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option { } else { let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`"); tcx.dcx() - .struct_span_err(attr.span, msg) + .struct_span_err(attr.span(), msg) .with_note("the value may not exceed `u16::MAX`") .emit(); None } } else { - tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span }); + tcx.dcx().emit_err(errors::InvalidLinkOrdinalFormat { span: attr.span() }); None } } @@ -792,7 +783,7 @@ struct MixedExportNameAndNoMangleState<'a> { export_name: Option, hir_id: Option, no_mangle: Option, - no_mangle_attr: Option<&'a ast::Attribute>, + no_mangle_attr: Option<&'a hir::Attribute>, } impl<'a> MixedExportNameAndNoMangleState<'a> { @@ -800,7 +791,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> { self.export_name = Some(span); } - fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a ast::Attribute) { + fn track_no_mangle(&mut self, span: Span, hir_id: HirId, attr_name: &'a hir::Attribute) { self.no_mangle = Some(span); self.hir_id = Some(hir_id); self.no_mangle_attr = Some(attr_name); @@ -821,7 +812,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> { no_mangle, errors::MixedExportNameAndNoMangle { no_mangle, - no_mangle_attr: rustc_ast_pretty::pprust::attribute_to_string(no_mangle_attr), + no_mangle_attr: rustc_hir_pretty::attribute_to_string(&tcx, no_mangle_attr), export_name, removal_span: no_mangle, }, diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index eee7cc75400d9..f20d303274831 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -1,8 +1,8 @@ -use rustc_ast::ast; -use rustc_attr::InstructionSetAttr; +use rustc_attr_parsing::InstructionSetAttr; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::Applicability; +use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_middle::middle::codegen_fn_attrs::TargetFeature; @@ -19,7 +19,7 @@ use crate::errors; /// Enabled target features are added to `target_features`. pub(crate) fn from_target_feature_attr( tcx: TyCtxt<'_>, - attr: &ast::Attribute, + attr: &hir::Attribute, rust_target_features: &UnordMap, target_features: &mut Vec, ) { diff --git a/compiler/rustc_const_eval/Cargo.toml b/compiler/rustc_const_eval/Cargo.toml index 41136019a88df..7717cd2c69664 100644 --- a/compiler/rustc_const_eval/Cargo.toml +++ b/compiler/rustc_const_eval/Cargo.toml @@ -9,7 +9,7 @@ either = "1" rustc_abi = { path = "../rustc_abi" } rustc_apfloat = "0.2.0" rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 916929b6c0bb9..cbc0d5b059629 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -6,7 +6,7 @@ use std::mem; use std::num::NonZero; use std::ops::Deref; -use rustc_attr::{ConstStability, StabilityLevel}; +use rustc_attr_parsing::{ConstStability, StabilityLevel}; use rustc_errors::{Diag, ErrorGuaranteed}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index 80d3c6448aabd..7f1281655b519 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -4,12 +4,14 @@ //! has interior mutability or needs to be dropped, as well as the visitor that emits errors when //! it finds operations that are invalid in a certain context. +use rustc_attr_parsing::find_attr; use rustc_errors::DiagCtxtHandle; +use rustc_hir as hir; +use rustc_attr_parsing::AttributeKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::ty::{self, PolyFnSig, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::Symbol; -use {rustc_attr as attr, rustc_hir as hir}; pub use self::qualifs::Qualif; @@ -81,7 +83,9 @@ pub fn rustc_allow_const_fn_unstable( feature_gate: Symbol, ) -> bool { let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id)); - attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate) + + find_attr!(attrs, AttributeKind::AllowConstFnUnstable(syms) => syms.contains(&feature_gate)) + .unwrap_or(false) } /// Returns `true` if the given `const fn` is "safe to expose on stable". diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 81f15ebcbf80a..2f0fe64b09608 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -9,7 +9,7 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_borrowck = { path = "../rustc_borrowck" } rustc_builtin_macros = { path = "../rustc_builtin_macros" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 2e01c385a6670..6ea882233dfcc 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -112,7 +112,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ crate::DEFAULT_LOCALE_RESOURCE, rustc_ast_lowering::DEFAULT_LOCALE_RESOURCE, rustc_ast_passes::DEFAULT_LOCALE_RESOURCE, - rustc_attr::DEFAULT_LOCALE_RESOURCE, + rustc_attr_parsing::DEFAULT_LOCALE_RESOURCE, rustc_borrowck::DEFAULT_LOCALE_RESOURCE, rustc_builtin_macros::DEFAULT_LOCALE_RESOURCE, rustc_codegen_ssa::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 06bae57638f3c..898dd5876e0f0 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -10,6 +10,7 @@ derive_setters = "0.1.6" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_codes = { path = "../rustc_error_codes" } rustc_error_messages = { path = "../rustc_error_messages" } diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index b451037132397..be04fbb2c07cc 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -8,6 +8,7 @@ use std::process::ExitStatus; use rustc_abi::TargetDataLayoutErrors; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast_pretty::pprust; +use rustc_attr_data_structures::RustcVersion; use rustc_macros::Subdiagnostic; use rustc_span::Span; use rustc_span::edition::Edition; @@ -96,6 +97,12 @@ into_diag_arg_using_display!( ErrCode, ); +impl IntoDiagArg for RustcVersion { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Str(Cow::Owned(self.to_string())) + } +} + impl IntoDiagArg for rustc_type_ir::TraitRef { fn into_diag_arg(self) -> DiagArgValue { self.to_string().into_diag_arg() diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index ce014364b0d01..cae7007c2c561 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -12,11 +12,12 @@ doctest = false rustc_ast = { path = "../rustc_ast" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } +rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index bed500c303242..6e92453e84717 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -4,17 +4,18 @@ use std::path::Component::Prefix; use std::path::{Path, PathBuf}; use std::rc::Rc; -use rustc_ast::attr::MarkedAttrs; +use rustc_ast::attr::{AttributeExt, MarkedAttrs}; use rustc_ast::ptr::P; use rustc_ast::token::Nonterminal; use rustc_ast::tokenstream::TokenStream; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind}; -use rustc_attr::{self as attr, Deprecation, Stability}; +use rustc_attr_parsing::find_attr; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; +use rustc_attr_parsing::{AttributeKind, Deprecation, Stability}; use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; use rustc_parse::MACRO_ARGUMENTS; use rustc_parse::parser::Parser; @@ -29,6 +30,7 @@ use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::{DUMMY_SP, FileName, Span}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; +use rustc_hir as hir; use crate::base::ast::MetaItemInner; use crate::errors; @@ -782,10 +784,12 @@ impl SyntaxExtension { } } - fn collapse_debuginfo_by_name(attr: &Attribute) -> Result { + fn collapse_debuginfo_by_name( + attr: &impl AttributeExt, + ) -> Result { let list = attr.meta_item_list(); let Some([MetaItemInner::MetaItem(item)]) = list.as_deref() else { - return Err(attr.span); + return Err(attr.span()); }; if !item.is_word() { return Err(item.span); @@ -805,9 +809,9 @@ impl SyntaxExtension { /// | (unspecified) | no | if-ext | if-ext | yes | /// | external | no | if-ext | if-ext | yes | /// | yes | yes | yes | yes | yes | - fn get_collapse_debuginfo(sess: &Session, attrs: &[ast::Attribute], ext: bool) -> bool { + fn get_collapse_debuginfo(sess: &Session, attrs: &[impl AttributeExt], ext: bool) -> bool { let flag = sess.opts.cg.collapse_macro_debuginfo; - let attr = attr::find_by_name(attrs, sym::collapse_debuginfo) + let attr = ast::attr::find_by_name(attrs, sym::collapse_debuginfo) .and_then(|attr| { Self::collapse_debuginfo_by_name(attr) .map_err(|span| { @@ -816,7 +820,7 @@ impl SyntaxExtension { .ok() }) .unwrap_or_else(|| { - if attr::contains_name(attrs, sym::rustc_builtin_macro) { + if ast::attr::contains_name(attrs, sym::rustc_builtin_macro) { CollapseMacroDebuginfo::Yes } else { CollapseMacroDebuginfo::Unspecified @@ -836,26 +840,27 @@ impl SyntaxExtension { /// and other properties converted from attributes. pub fn new( sess: &Session, - features: &Features, kind: SyntaxExtensionKind, span: Span, helper_attrs: Vec, edition: Edition, name: Symbol, - attrs: &[ast::Attribute], + attrs: &[hir::Attribute], is_local: bool, ) -> SyntaxExtension { let allow_internal_unstable = - attr::allow_internal_unstable(sess, attrs).collect::>(); + find_attr!(attrs, AttributeKind::AllowInternalUnstable(i) => i) + .map(|i| i.as_slice()) + .unwrap_or_default(); + let allow_internal_unsafe = find_attr!(attrs, AttributeKind::AllowInternalUnsafe); - let allow_internal_unsafe = attr::contains_name(attrs, sym::allow_internal_unsafe); - let local_inner_macros = attr::find_by_name(attrs, sym::macro_export) + let local_inner_macros = ast::attr::find_by_name(attrs, sym::macro_export) .and_then(|macro_export| macro_export.meta_item_list()) - .is_some_and(|l| attr::list_contains_name(&l, sym::local_inner_macros)); + .is_some_and(|l| ast::attr::list_contains_name(&l, sym::local_inner_macros)); let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); - let (builtin_name, helper_attrs) = attr::find_by_name(attrs, sym::rustc_builtin_macro) + let (builtin_name, helper_attrs) = ast::attr::find_by_name(attrs, sym::rustc_builtin_macro) .map(|attr| { // Override `helper_attrs` passed above if it's a built-in macro, // marking `proc_macro_derive` macros as built-in is not a realistic use case. @@ -865,16 +870,17 @@ impl SyntaxExtension { ) }) .unwrap_or_else(|| (None, helper_attrs)); - let stability = attr::find_stability(sess, attrs, span); - let const_stability = attr::find_const_stability(sess, attrs, span); - let body_stability = attr::find_body_stability(sess, attrs); - if let Some((_, sp)) = const_stability { + + let stability = find_attr!(attrs, AttributeKind::Stability{stability, ..} => *stability); + + // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem + if let Some(sp) = find_attr!(attrs, AttributeKind::ConstStability{span, ..} => *span) { sess.dcx().emit_err(errors::MacroConstStability { span: sp, head_span: sess.source_map().guess_head_span(span), }); } - if let Some((_, sp)) = body_stability { + if let Some(sp) = find_attr!(attrs, AttributeKind::BodyStability{span, ..} => *span) { sess.dcx().emit_err(errors::MacroBodyStability { span: sp, head_span: sess.source_map().guess_head_span(span), @@ -885,9 +891,10 @@ impl SyntaxExtension { kind, span, allow_internal_unstable: (!allow_internal_unstable.is_empty()) - .then(|| allow_internal_unstable.into()), - stability: stability.map(|(s, _)| s), - deprecation: attr::find_deprecation(sess, features, attrs).map(|(d, _)| d), + // FIXME(jdonszelmann): avoid the into_iter/collect? + .then(|| allow_internal_unstable.iter().copied().collect::>().into()), + stability, + deprecation: find_attr!(attrs, AttributeKind::Deprecation{deprecation, ..} => *deprecation), helper_attrs, edition, builtin_name, @@ -1305,7 +1312,7 @@ pub fn resolve_path(sess: &Session, path: impl Into, span: Span) -> PRe pub fn parse_macro_name_and_helper_attrs( dcx: DiagCtxtHandle<'_>, - attr: &Attribute, + attr: &impl AttributeExt, macro_type: &str, ) -> Option<(Symbol, Vec)> { // Once we've located the `#[proc_macro_derive]` attribute, verify @@ -1313,7 +1320,7 @@ pub fn parse_macro_name_and_helper_attrs( // `#[proc_macro_derive(Foo, attributes(A, ..))]` let list = attr.meta_item_list()?; let ([trait_attr] | [trait_attr, _]) = list.as_slice() else { - dcx.emit_err(errors::AttrNoArguments { span: attr.span }); + dcx.emit_err(errors::AttrNoArguments { span: attr.span() }); return None; }; let Some(trait_attr) = trait_attr.meta_item() else { diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index dc6aa110f459b..8e500f538a78b 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -8,7 +8,7 @@ use rustc_ast::tokenstream::{ use rustc_ast::{ self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId, }; -use rustc_attr as attr; +use rustc_attr_parsing as attr; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeSafety, EnabledLangFeature, EnabledLibFeature, Features, @@ -362,7 +362,7 @@ impl<'a> StripUnconfigured<'a> { )); let tokens = Some(LazyAttrTokenStream::new(AttrTokenStream::new(trees))); - let attr = attr::mk_attr_from_item( + let attr = ast::attr::mk_attr_from_item( &self.sess.psess.attr_id_generator, item, tokens, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 6a6496f982702..8dc0fe3b5ee5d 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1907,7 +1907,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.cx.current_expansion.lint_node_id, BuiltinLintDiag::UnusedDocComment(attr.span), ); - } else if rustc_attr::is_builtin_attr(attr) { + } else if rustc_attr_parsing::is_builtin_attr(attr) { let attr_name = attr.ident().unwrap().name; // `#[cfg]` and `#[cfg_attr]` are special - they are // eagerly evaluated. diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index a373c753cc114..7d4d7529233dc 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -9,10 +9,11 @@ use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId}; use rustc_ast_pretty::pprust; -use rustc_attr::{self as attr, TransparencyError}; +use rustc_attr_parsing::{find_attr, AttributeKind}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{Applicability, ErrorGuaranteed}; use rustc_feature::Features; +use rustc_hir as hir; use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, @@ -371,7 +372,7 @@ pub fn compile_declarative_macro( features: &Features, macro_def: &ast::MacroDef, ident: Ident, - attrs: &[ast::Attribute], + attrs: &[hir::Attribute], span: Span, node_id: NodeId, edition: Edition, @@ -379,7 +380,6 @@ pub fn compile_declarative_macro( let mk_syn_ext = |expander| { SyntaxExtension::new( sess, - features, SyntaxExtensionKind::LegacyBang(expander), span, Vec::new(), @@ -391,7 +391,6 @@ pub fn compile_declarative_macro( }; let dummy_syn_ext = |guar| (mk_syn_ext(Box::new(DummyExpander(guar))), Vec::new()); - let dcx = sess.dcx(); let lhs_nm = Ident::new(sym::lhs, span); let rhs_nm = Ident::new(sym::rhs, span); let tt_spec = Some(NonterminalKind::TT); @@ -533,16 +532,8 @@ pub fn compile_declarative_macro( check_emission(macro_check::check_meta_variables(&sess.psess, node_id, span, &lhses, &rhses)); - let (transparency, transparency_error) = attr::find_transparency(attrs, macro_rules); - match transparency_error { - Some(TransparencyError::UnknownTransparency(value, span)) => { - dcx.span_err(span, format!("unknown macro transparency: `{value}`")); - } - Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => { - dcx.span_err(vec![old_span, new_span], "multiple macro transparency attributes"); - } - None => {} - } + let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x) + .unwrap_or(Transparency::fallback(macro_rules)); if let Some(guar) = guar { // To avoid warning noise, only consider the rules of this diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 85c6da83379b9..580967dce75c9 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -9,6 +9,7 @@ odht = { version = "0.3.1", features = ["nightly"] } rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } @@ -16,5 +17,6 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } +thin-vec = "0.2.12" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index f1f624269aee9..88c0d223fd39a 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -6,7 +6,7 @@ macro_rules! arena_types { $macro!([ // HIR types [] asm_template: rustc_ast::InlineAsmTemplatePiece, - [] attribute: rustc_ast::Attribute, + [] attribute: rustc_hir::Attribute, [] owner_info: rustc_hir::OwnerInfo<'tcx>, [] use_path: rustc_hir::UsePath<'tcx>, [] lit: rustc_hir::Lit, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 365e4cbb55676..d4068200dab7a 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,15 +1,20 @@ +// ignore-tidy-filelength use std::fmt; use rustc_abi::ExternAbi; +use rustc_ast::attr::AttributeExt; +use rustc_ast::token::CommentKind; use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ - self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, - LitKind, TraitObjectSyntax, UintTy, + self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitKind, + TraitObjectSyntax, UintTy, }; pub use rustc_ast::{ - BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, - ImplPolarity, IsAuto, Movability, Mutability, UnOp, + AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, + ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability, + Mutability, UnOp, }; +use rustc_attr_data_structures::AttributeKind; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_index::IndexVec; @@ -21,6 +26,7 @@ use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Span}; use rustc_target::asm::InlineAsmRegOrRegClass; use smallvec::SmallVec; +use thin_vec::ThinVec; use tracing::debug; use crate::LangItem; @@ -937,6 +943,281 @@ pub struct ParentedNode<'tcx> { pub node: Node<'tcx>, } +/// Arguments passed to an attribute macro. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)] +pub enum AttrArgs { + /// No arguments: `#[attr]`. + Empty, + /// Delimited arguments: `#[attr()/[]/{}]`. + Delimited(DelimArgs), + /// Arguments of a key-value attribute: `#[attr = "value"]`. + Eq { + /// Span of the `=` token. + eq_span: Span, + /// The "value". + expr: MetaItemLit, + }, +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)] +pub struct AttrPath { + pub segments: Box<[Ident]>, + pub span: Span, +} + +impl AttrPath { + pub fn from_ast(path: &ast::Path) -> Self { + AttrPath { + segments: path.segments.iter().map(|i| i.ident).collect::>().into_boxed_slice(), + span: path.span, + } + } +} + +impl fmt::Display for AttrPath { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.segments.iter().map(|i| i.to_string()).collect::>().join("::")) + } +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable)] +pub struct AttrItem { + // Not lowered to hir::Path because we have no NodeId to resolve to. + pub path: AttrPath, + pub args: AttrArgs, + pub id: HashIgnoredAttrId, + /// Denotes if the attribute decorates the following construct (outer) + /// or the construct this attribute is contained within (inner). + pub style: AttrStyle, + /// Span of the entire attribute + pub span: Span, +} + +/// The derived implementation of [`HashStable_Generic`] on [`Attribute`]s shouldn't hash +/// [`AttrId`]s. By wrapping them in this, we make sure we never do. +#[derive(Copy, Debug, Encodable, Decodable, Clone)] +pub struct HashIgnoredAttrId { + pub attr_id: AttrId, +} + +#[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)] +pub enum Attribute { + /// A parsed built-in attribute. + /// + /// Each attribute has a span connected to it. However, you must be somewhat careful using it. + /// That's because sometimes we merge multiple attributes together, like when an item has + /// multiple `repr` attributes. In this case the span might not be very useful. + Parsed(AttributeKind), + + /// An attribute that could not be parsed, out of a token-like representation. + /// This is the case for custom tool attributes. + Unparsed(Box), +} + +impl Attribute { + pub fn get_normal_item(&self) -> &AttrItem { + match &self { + Attribute::Unparsed(normal) => &normal, + _ => panic!("unexpected parsed attribute"), + } + } + + pub fn unwrap_normal_item(self) -> AttrItem { + match self { + Attribute::Unparsed(normal) => *normal, + _ => panic!("unexpected parsed attribute"), + } + } + + pub fn value_lit(&self) -> Option<&MetaItemLit> { + match &self { + Attribute::Unparsed(n) => match n.as_ref() { + AttrItem { args: AttrArgs::Eq { eq_span: _, expr }, .. } => Some(expr), + _ => None, + }, + _ => None, + } + } +} + +impl AttributeExt for Attribute { + fn id(&self) -> AttrId { + match &self { + Attribute::Unparsed(u) => u.id.attr_id, + _ => panic!(), + } + } + + fn meta_item_list(&self) -> Option> { + match &self { + Attribute::Unparsed(n) => match n.as_ref() { + AttrItem { args: AttrArgs::Delimited(d), .. } => { + ast::MetaItemKind::list_from_tokens(d.tokens.clone()) + } + _ => None, + }, + _ => None, + } + } + + fn value_str(&self) -> Option { + self.value_lit().and_then(|x| x.value_str()) + } + + fn value_span(&self) -> Option { + self.value_lit().map(|i| i.span) + } + + /// For a single-segment attribute, returns its name; otherwise, returns `None`. + fn ident(&self) -> Option { + match &self { + Attribute::Unparsed(n) => { + if let [ident] = n.path.segments.as_ref() { + Some(*ident) + } else { + None + } + } + _ => None, + } + } + + fn path_matches(&self, name: &[Symbol]) -> bool { + match &self { + Attribute::Unparsed(n) => { + n.path.segments.len() == name.len() + && n.path.segments.iter().zip(name).all(|(s, n)| s.name == *n) + } + _ => false, + } + } + + fn is_doc_comment(&self) -> bool { + matches!(self, Attribute::Parsed(AttributeKind::DocComment { .. })) + } + + fn span(&self) -> Span { + match &self { + Attribute::Unparsed(u) => u.span, + _ => panic!(), + } + } + + fn is_word(&self) -> bool { + match &self { + Attribute::Unparsed(n) => { + matches!(n.args, AttrArgs::Empty) + } + _ => false, + } + } + + fn ident_path(&self) -> Option> { + match &self { + Attribute::Unparsed(n) => Some(n.path.segments.iter().copied().collect()), + _ => None, + } + } + + fn doc_str(&self) -> Option { + match &self { + Attribute::Parsed(AttributeKind::DocComment { comment, .. }) => Some(*comment), + Attribute::Unparsed(_) if self.has_name(sym::doc) => self.value_str(), + _ => None, + } + } + fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { + match &self { + Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => { + Some((*comment, *kind)) + } + Attribute::Unparsed(_) if self.name_or_empty() == sym::doc => { + self.value_str().map(|s| (s, CommentKind::Line)) + } + _ => None, + } + } + + fn style(&self) -> AttrStyle { + match &self { + Attribute::Unparsed(u) => u.style, + _ => panic!(), + } + } +} + +// FIXME(fn_delegation): use function delegation instead of manually forwarding +impl Attribute { + pub fn id(&self) -> AttrId { + AttributeExt::id(self) + } + + pub fn name_or_empty(&self) -> Symbol { + AttributeExt::name_or_empty(self) + } + + pub fn meta_item_list(&self) -> Option> { + AttributeExt::meta_item_list(self) + } + + pub fn value_str(&self) -> Option { + AttributeExt::value_str(self) + } + + pub fn value_span(&self) -> Option { + AttributeExt::value_span(self) + } + + pub fn ident(&self) -> Option { + AttributeExt::ident(self) + } + + pub fn path_matches(&self, name: &[Symbol]) -> bool { + AttributeExt::path_matches(self, name) + } + + pub fn is_doc_comment(&self) -> bool { + AttributeExt::is_doc_comment(self) + } + + #[inline] + pub fn has_name(&self, name: Symbol) -> bool { + AttributeExt::has_name(self, name) + } + + pub fn span(&self) -> Span { + AttributeExt::span(self) + } + + pub fn is_word(&self) -> bool { + AttributeExt::is_word(self) + } + + pub fn path(&self) -> SmallVec<[Symbol; 1]> { + AttributeExt::path(self) + } + + pub fn ident_path(&self) -> Option> { + AttributeExt::ident_path(self) + } + + pub fn doc_str(&self) -> Option { + AttributeExt::doc_str(self) + } + + pub fn is_proc_macro_attr(&self) -> bool { + AttributeExt::is_proc_macro_attr(self) + } + + pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> { + AttributeExt::doc_str_and_comment_kind(self) + } + + pub fn style(&self) -> AttrStyle { + AttributeExt::style(self) + } +} + /// Attributes owned by a HIR owner. #[derive(Debug)] pub struct AttributeMap<'tcx> { diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9abb0870bf0ff..048b27468012d 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -64,8 +64,8 @@ //! This order consistency is required in a few places in rustc, for //! example coroutine inference, and possibly also HIR borrowck. +use rustc_ast::Label; use rustc_ast::visit::{VisitorResult, try_visit, visit_opt, walk_list}; -use rustc_ast::{Attribute, Label}; use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{Ident, Symbol}; diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 15cb331d07a5f..3684695774eac 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -7,7 +7,7 @@ //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. //! * Functions called by the compiler itself. -use rustc_ast as ast; +use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; @@ -153,11 +153,11 @@ impl HashStable for LangItem { /// Extracts the first `lang = "$name"` out of a list of attributes. /// The `#[panic_handler]` attribute is also extracted out when found. -pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> { +pub fn extract(attrs: &[impl AttributeExt]) -> Option<(Symbol, Span)> { attrs.iter().find_map(|attr| { Some(match attr { - _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span), - _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span), + _ if attr.has_name(sym::lang) => (attr.value_str()?, attr.span()), + _ if attr.has_name(sym::panic_handler) => (sym::panic_impl, attr.span()), _ => return None, }) }) diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index fe169e989ec9f..b0cd26d8a914b 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -1,6 +1,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_span::def_id::DefPathHash; +use crate::HashIgnoredAttrId; use crate::hir::{ AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, }; @@ -9,9 +10,8 @@ use crate::hir_id::{HirId, ItemLocalId}; /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. -pub trait HashStableContext: - rustc_ast::HashStableContext + rustc_target::HashStableContext -{ +pub trait HashStableContext: rustc_attr_data_structures::HashStableContext { + fn hash_attr_id(&mut self, id: &HashIgnoredAttrId, hasher: &mut StableHasher); } impl ToStableHashKey for HirId { @@ -113,3 +113,9 @@ impl HashStable for Crate<'_> { opt_hir_hash.unwrap().hash_stable(hcx, hasher) } } + +impl HashStable for HashIgnoredAttrId { + fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { + hcx.hash_attr_id(self, hasher) + } +} diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index 581ef2272d12b..196d7d99e933d 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -13,7 +13,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 022a6d457ec56..549ff7f7af0a5 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -2,11 +2,13 @@ use std::cell::LazyCell; use std::ops::ControlFlow; use rustc_abi::FieldIdx; +use rustc_attr_parsing::ReprAttr::ReprPacked; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::MultiSpan; use rustc_errors::codes::*; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::{Node, Safety, intravisit}; +use rustc_attr_parsing::AttributeKind; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_lint_defs::builtin::{ @@ -31,7 +33,7 @@ use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_type_ir::fold::TypeFoldable; use tracing::{debug, instrument}; use ty::TypingMode; -use {rustc_attr as attr, rustc_hir as hir}; +use {rustc_attr_parsing as attr, rustc_hir as hir}; use super::compare_impl_item::check_type_bounds; use super::*; @@ -1159,7 +1161,7 @@ fn check_impl_items_against_trait<'tcx>( if let Some(missing_items) = must_implement_one_of { let attr_span = tcx .get_attr(trait_ref.def_id, sym::rustc_must_implement_one_of) - .map(|attr| attr.span); + .map(|attr| attr.span()); missing_items_must_implement_one_of_err( tcx, @@ -1248,11 +1250,13 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { let repr = def.repr(); if repr.packed() { - for attr in tcx.get_attrs(def.did(), sym::repr) { - for r in attr::parse_repr_attr(tcx.sess, attr) { - if let attr::ReprPacked(pack) = r + if let Some(reprs) = + attr::find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::Repr(r) => r) + { + for r in reprs { + if let ReprPacked(pack) = r && let Some(repr_pack) = repr.pack - && pack != repr_pack + && pack != &repr_pack { struct_span_code_err!( tcx.dcx(), @@ -1467,7 +1471,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { if let Some(attr) = tcx.get_attrs(def_id, sym::repr).next() { struct_span_code_err!( tcx.dcx(), - attr.span, + attr.span(), E0084, "unsupported representation for zero-variant enum" ) diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index f3dd13c84b958..977a02e80c668 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -101,7 +101,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { } for attr in tcx.get_attrs(main_def_id, sym::track_caller) { - tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span, annotated: main_span }); + tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr.span(), annotated: main_span }); error = true; } @@ -224,7 +224,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { for attr in attrs { if attr.has_name(sym::track_caller) { tcx.dcx().emit_err(errors::StartTrackCaller { - span: attr.span, + span: attr.span(), start: start_span, }); error = true; @@ -236,7 +236,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { && !tcx.sess.opts.actually_rustdoc { tcx.dcx().emit_err(errors::StartTargetFeature { - span: attr.span, + span: attr.span(), start: start_span, }); error = true; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index df4da03f0f59d..a111eae70077b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -77,7 +77,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Float(FloatTy::F128) => Some(InlineAsmType::F128), ty::FnPtr(..) => Some(asm_ty_isize), ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize), - ty::Adt(adt, args) if adt.repr().simd() => { + ty::Adt(adt, args) if { adt.repr().simd() } => { let fields = &adt.non_enum_variant().fields; let elem_ty = fields[FieldIdx::ZERO].ty(self.tcx, args); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 8f9b062603127..047c3cff66263 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1183,7 +1183,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { // and that they are all identifiers .and_then(|attr| match attr.meta_item_list() { Some(items) if items.len() < 2 => { - tcx.dcx().emit_err(errors::MustImplementOneOfAttribute { span: attr.span }); + tcx.dcx().emit_err(errors::MustImplementOneOfAttribute { span: attr.span() }); None } @@ -1195,7 +1195,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { tcx.dcx().emit_err(errors::MustBeNameOfAssociatedFunction { span }); }) .ok() - .zip(Some(attr.span)), + .zip(Some(attr.span())), // Error is reported by `rustc_attr!` None => None, }) @@ -1292,7 +1292,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { } } if !seen_attr { - tcx.dcx().span_err(attr.span, "missing `implement_via_object` meta item"); + tcx.dcx().span_err(attr.span(), "missing `implement_via_object` meta item"); } } diff --git a/compiler/rustc_hir_pretty/Cargo.toml b/compiler/rustc_hir_pretty/Cargo.toml index 9af1fb8279e9f..58a12bdda2545 100644 --- a/compiler/rustc_hir_pretty/Cargo.toml +++ b/compiler/rustc_hir_pretty/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_hir = { path = "../rustc_hir" } rustc_span = { path = "../rustc_span" } # tidy-alphabetical-end diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index a74e36c45c6ef..1aefb6818d713 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -11,16 +11,19 @@ use std::vec; use rustc_abi::ExternAbi; use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; +use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, Breaks}; +use rustc_ast_pretty::pprust::state::MacHeader; use rustc_ast_pretty::pprust::{Comments, PrintState}; +use rustc_attr_parsing::AttributeKind; use rustc_hir::{ BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, }; -use rustc_span::FileName; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::{FileName, Span}; use {rustc_ast as ast, rustc_hir as hir}; pub fn id_to_string(map: &dyn rustc_hir::intravisit::Map<'_>, hir_id: HirId) -> String { @@ -68,15 +71,95 @@ impl PpAnn for &dyn rustc_hir::intravisit::Map<'_> { pub struct State<'a> { pub s: pp::Printer, comments: Option>, - attrs: &'a dyn Fn(HirId) -> &'a [ast::Attribute], + attrs: &'a dyn Fn(HirId) -> &'a [hir::Attribute], ann: &'a (dyn PpAnn + 'a), } impl<'a> State<'a> { - fn attrs(&self, id: HirId) -> &'a [ast::Attribute] { + fn attrs(&self, id: HirId) -> &'a [hir::Attribute] { (self.attrs)(id) } + fn print_attrs_as_inner(&mut self, attrs: &[hir::Attribute]) { + self.print_either_attributes(attrs, ast::AttrStyle::Inner) + } + + fn print_attrs_as_outer(&mut self, attrs: &[hir::Attribute]) { + self.print_either_attributes(attrs, ast::AttrStyle::Outer) + } + + fn print_either_attributes(&mut self, attrs: &[hir::Attribute], style: ast::AttrStyle) { + if attrs.is_empty() { + return; + } + + for attr in attrs { + self.print_attribute_inline(attr, style); + } + self.hardbreak_if_not_bol(); + } + + fn print_attribute_inline(&mut self, attr: &hir::Attribute, style: AttrStyle) { + match &attr { + hir::Attribute::Unparsed(unparsed) => { + self.maybe_print_comment(unparsed.span.lo()); + match style { + ast::AttrStyle::Inner => self.word("#!["), + ast::AttrStyle::Outer => self.word("#["), + } + self.print_attr_item(&unparsed, unparsed.span); + self.word("]"); + } + hir::Attribute::Parsed(AttributeKind::DocComment { + style, kind, comment, .. + }) => { + self.word(rustc_ast_pretty::pprust::state::doc_comment_to_string( + *kind, *style, *comment, + )); + self.hardbreak() + } + _ => unimplemented!("pretty print parsed attributes"), + } + } + + fn print_attr_item(&mut self, item: &hir::AttrItem, span: Span) { + self.ibox(0); + let path = ast::Path { + span, + segments: item + .path + .segments + .iter() + .map(|i| ast::PathSegment { ident: *i, args: None, id: DUMMY_NODE_ID }) + .collect(), + tokens: None, + }; + + match &item.args { + hir::AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self + .print_mac_common( + Some(MacHeader::Path(&path)), + false, + None, + *delim, + &tokens, + true, + span, + ), + hir::AttrArgs::Empty => { + PrintState::print_path(self, &path, false, 0); + } + hir::AttrArgs::Eq { eq_span: _, expr } => { + PrintState::print_path(self, &path, false, 0); + self.space(); + self.word_space("="); + let token_str = self.meta_item_lit_to_string(expr); + self.word(token_str); + } + } + self.end(); + } + fn print_node(&mut self, node: Node<'_>) { match node { Node::Param(a) => self.print_param(a), @@ -164,7 +247,7 @@ pub fn print_crate<'a>( krate: &hir::Mod<'_>, filename: FileName, input: String, - attrs: &'a dyn Fn(HirId) -> &'a [ast::Attribute], + attrs: &'a dyn Fn(HirId) -> &'a [hir::Attribute], ann: &'a dyn PpAnn, ) -> String { let mut s = State { @@ -191,6 +274,10 @@ where printer.s.eof() } +pub fn attribute_to_string(ann: &dyn PpAnn, attr: &hir::Attribute) -> String { + to_string(ann, |s| s.print_attribute_inline(attr, AttrStyle::Outer)) +} + pub fn ty_to_string(ann: &dyn PpAnn, ty: &hir::Ty<'_>) -> String { to_string(ann, |s| s.print_type(ty)) } @@ -242,8 +329,8 @@ impl<'a> State<'a> { self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span); } - fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[ast::Attribute]) { - self.print_inner_attributes(attrs); + fn print_mod(&mut self, _mod: &hir::Mod<'_>, attrs: &[hir::Attribute]) { + self.print_attrs_as_inner(attrs); for &item_id in _mod.item_ids { self.ann.nested(self, Nested::Item(item_id)); } @@ -343,7 +430,7 @@ impl<'a> State<'a> { fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); - self.print_outer_attributes(self.attrs(item.hir_id())); + self.print_attrs_as_outer(self.attrs(item.hir_id())); match item.kind { hir::ForeignItemKind::Fn(sig, arg_names, generics) => { self.head(""); @@ -447,7 +534,7 @@ impl<'a> State<'a> { self.hardbreak_if_not_bol(); self.maybe_print_comment(item.span.lo()); let attrs = self.attrs(item.hir_id()); - self.print_outer_attributes(attrs); + self.print_attrs_as_outer(attrs); self.ann.pre(self, AnnNode::Item(item)); match item.kind { hir::ItemKind::ExternCrate(orig_name) => { @@ -543,7 +630,7 @@ impl<'a> State<'a> { self.head("extern"); self.word_nbsp(abi.to_string()); self.bopen(); - self.print_inner_attributes(self.attrs(item.hir_id())); + self.print_attrs_as_inner(self.attrs(item.hir_id())); for item in items { self.ann.nested(self, Nested::ForeignItem(item.id)); } @@ -611,7 +698,7 @@ impl<'a> State<'a> { self.space(); self.bopen(); - self.print_inner_attributes(attrs); + self.print_attrs_as_inner(attrs); for impl_item in items { self.ann.nested(self, Nested::ImplItem(impl_item.id)); } @@ -703,7 +790,7 @@ impl<'a> State<'a> { for v in variants { self.space_if_not_bol(); self.maybe_print_comment(v.span.lo()); - self.print_outer_attributes(self.attrs(v.hir_id)); + self.print_attrs_as_outer(self.attrs(v.hir_id)); self.ibox(INDENT_UNIT); self.print_variant(v); self.word(","); @@ -736,7 +823,7 @@ impl<'a> State<'a> { self.popen(); self.commasep(Inconsistent, struct_def.fields(), |s, field| { s.maybe_print_comment(field.span.lo()); - s.print_outer_attributes(s.attrs(field.hir_id)); + s.print_attrs_as_outer(s.attrs(field.hir_id)); s.print_type(field.ty); }); self.pclose(); @@ -763,7 +850,7 @@ impl<'a> State<'a> { for field in fields { self.hardbreak_if_not_bol(); self.maybe_print_comment(field.span.lo()); - self.print_outer_attributes(self.attrs(field.hir_id)); + self.print_attrs_as_outer(self.attrs(field.hir_id)); self.print_ident(field.ident); self.word_nbsp(":"); self.print_type(field.ty); @@ -799,7 +886,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::SubItem(ti.hir_id())); self.hardbreak_if_not_bol(); self.maybe_print_comment(ti.span.lo()); - self.print_outer_attributes(self.attrs(ti.hir_id())); + self.print_attrs_as_outer(self.attrs(ti.hir_id())); match ti.kind { hir::TraitItemKind::Const(ty, default) => { self.print_associated_const(ti.ident, ti.generics, ty, default); @@ -827,7 +914,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::SubItem(ii.hir_id())); self.hardbreak_if_not_bol(); self.maybe_print_comment(ii.span.lo()); - self.print_outer_attributes(self.attrs(ii.hir_id())); + self.print_attrs_as_outer(self.attrs(ii.hir_id())); match ii.kind { hir::ImplItemKind::Const(ty, expr) => { @@ -912,14 +999,14 @@ impl<'a> State<'a> { self.print_block_maybe_unclosed(blk, &[], false) } - fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[ast::Attribute]) { + fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[hir::Attribute]) { self.print_block_maybe_unclosed(blk, attrs, true) } fn print_block_maybe_unclosed( &mut self, blk: &hir::Block<'_>, - attrs: &[ast::Attribute], + attrs: &[hir::Attribute], close_box: bool, ) { match blk.rules { @@ -930,7 +1017,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::Block(blk)); self.bopen(); - self.print_inner_attributes(attrs); + self.print_attrs_as_inner(attrs); for st in blk.stmts { self.print_stmt(st); @@ -1120,7 +1207,7 @@ impl<'a> State<'a> { self.space(); } self.cbox(INDENT_UNIT); - self.print_outer_attributes(self.attrs(field.hir_id)); + self.print_attrs_as_outer(self.attrs(field.hir_id)); if !field.is_shorthand { self.print_ident(field.ident); self.word_space(":"); @@ -1317,7 +1404,7 @@ impl<'a> State<'a> { fn print_expr(&mut self, expr: &hir::Expr<'_>) { self.maybe_print_comment(expr.span.lo()); - self.print_outer_attributes(self.attrs(expr.hir_id)); + self.print_attrs_as_outer(self.attrs(expr.hir_id)); self.ibox(INDENT_UNIT); self.ann.pre(self, AnnNode::Expr(expr)); match expr.kind { @@ -1879,7 +1966,7 @@ impl<'a> State<'a> { self.space(); } self.cbox(INDENT_UNIT); - self.print_outer_attributes(self.attrs(field.hir_id)); + self.print_attrs_as_outer(self.attrs(field.hir_id)); if !field.is_shorthand { self.print_ident(field.ident); self.word_nbsp(":"); @@ -1889,7 +1976,7 @@ impl<'a> State<'a> { } fn print_param(&mut self, arg: &hir::Param<'_>) { - self.print_outer_attributes(self.attrs(arg.hir_id)); + self.print_attrs_as_outer(self.attrs(arg.hir_id)); self.print_pat(arg.pat); } @@ -1902,7 +1989,7 @@ impl<'a> State<'a> { self.cbox(INDENT_UNIT); self.ann.pre(self, AnnNode::Arm(arm)); self.ibox(0); - self.print_outer_attributes(self.attrs(arm.hir_id)); + self.print_attrs_as_outer(self.attrs(arm.hir_id)); self.print_pat(arm.pat); self.space(); if let Some(ref g) = arm.guard { diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index 8ddbb4b339773..1f5acaa58a972 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -9,7 +9,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6b1a288510ac1..2d9e1a04f4721 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -8,7 +8,7 @@ use std::borrow::Cow; use hir::Expr; use rustc_ast::ast::Mutability; -use rustc_attr::parse_confusables; +use rustc_attr_parsing::{find_attr, AttributeKind}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordSet; @@ -1869,9 +1869,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for inherent_method in self.tcx.associated_items(inherent_impl_did).in_definition_order() { - if let Some(attr) = - self.tcx.get_attr(inherent_method.def_id, sym::rustc_confusables) - && let Some(candidates) = parse_confusables(attr) + if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::Confusables{symbols, ..} => symbols) && candidates.contains(&item_name.name) && let ty::AssocKind::Fn = inherent_method.kind { diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 1f46155abc882..bc27e777259a7 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -50,7 +50,7 @@ use rustc_middle::{bug, span_bug}; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; use tracing::debug; -use {rustc_ast as ast, rustc_graphviz as dot, rustc_hir as hir}; +use {rustc_graphviz as dot, rustc_hir as hir}; use crate::errors; @@ -106,7 +106,7 @@ struct IfThisChanged<'tcx> { } impl<'tcx> IfThisChanged<'tcx> { - fn argument(&self, attr: &ast::Attribute) -> Option { + fn argument(&self, attr: &hir::Attribute) -> Option { let mut value = None; for list_item in attr.meta_item_list().unwrap_or_default() { match list_item.ident() { @@ -138,13 +138,13 @@ impl<'tcx> IfThisChanged<'tcx> { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { Ok(n) => n, Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode { - span: attr.span, + span: attr.span(), name: n, }), } } }; - self.if_this_changed.push((attr.span, def_id.to_def_id(), dep_node)); + self.if_this_changed.push((attr.span(), def_id.to_def_id(), dep_node)); } else if attr.has_name(sym::rustc_then_this_would_need) { let dep_node_interned = self.argument(attr); let dep_node = match dep_node_interned { @@ -152,17 +152,17 @@ impl<'tcx> IfThisChanged<'tcx> { match DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) { Ok(n) => n, Err(()) => self.tcx.dcx().emit_fatal(errors::UnrecognizedDepNode { - span: attr.span, + span: attr.span(), name: n, }), } } None => { - self.tcx.dcx().emit_fatal(errors::MissingDepNode { span: attr.span }); + self.tcx.dcx().emit_fatal(errors::MissingDepNode { span: attr.span() }); } }; self.then_this_would_need.push(( - attr.span, + attr.span(), dep_node_interned.unwrap(), hir_id, dep_node, diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 2075d4214c134..c46eacae0c778 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -19,11 +19,13 @@ //! Errors are reported if we are in the suitable configuration but //! the required condition is not met. -use rustc_ast::{self as ast, Attribute, MetaItemInner}; +use rustc_ast::{self as ast, MetaItemInner}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::unord::UnordSet; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, intravisit}; +use rustc_hir::{ + Attribute, ImplItemKind, ItemKind as HirItem, Node as HirNode, TraitItemKind, intravisit, +}; use rustc_middle::dep_graph::{DepNode, DepNodeExt, label_strs}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; @@ -199,7 +201,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let loaded_from_disk = self.loaded_from_disk(attr); for e in except.items().into_sorted_stable_ord() { if !auto.remove(e) { - self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); + self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span(), name, e }); } } Assertion { clean: auto, dirty: except, loaded_from_disk } @@ -282,7 +284,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL), _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem { - span: attr.span, + span: attr.span(), kind: format!("{:?}", item.kind), }), } @@ -298,7 +300,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { ImplItemKind::Type(..) => ("NodeImplType", LABELS_CONST_IN_IMPL), }, _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirty { - span: attr.span, + span: attr.span(), kind: format!("{node:?}"), }), }; @@ -375,7 +377,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let Some(assertion) = self.assertion_maybe(item_id, attr) else { continue; }; - self.checked_attrs.insert(attr.id); + self.checked_attrs.insert(attr.id()); for label in assertion.clean.items().into_sorted_stable_ord() { let dep_node = DepNode::from_label_string(self.tcx, label, def_path_hash).unwrap(); self.assert_clean(item_span, dep_node); @@ -405,12 +407,13 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool { debug!("check_config: searching for cfg {:?}", value); cfg = Some(config.contains(&(value, None))); } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) { - tcx.dcx().emit_err(errors::UnknownItem { span: attr.span, name: item.name_or_empty() }); + tcx.dcx() + .emit_err(errors::UnknownItem { span: attr.span(), name: item.name_or_empty() }); } } match cfg { - None => tcx.dcx().emit_fatal(errors::NoCfg { span: attr.span }), + None => tcx.dcx().emit_fatal(errors::NoCfg { span: attr.span() }), Some(c) => c, } } @@ -444,9 +447,9 @@ impl<'tcx> FindAllAttrs<'tcx> { fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet) { for attr in &self.found_attrs { - if !checked_attrs.contains(&attr.id) { - self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span }); - checked_attrs.insert(attr.id); + if !checked_attrs.contains(&attr.id()) { + self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span() }); + checked_attrs.insert(attr.id()); } } } diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index 7a2ba07ce87bd..dcb9c5d22d61b 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -11,7 +11,7 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_borrowck = { path = "../rustc_borrowck" } rustc_builtin_macros = { path = "../rustc_builtin_macros" } rustc_codegen_llvm = { path = "../rustc_codegen_llvm", optional = true } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index d3213b1263c3e..2863b4c5a0ad9 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -453,7 +453,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu .opts .crate_name .clone() - .or_else(|| rustc_attr::find_crate_name(attrs).map(|n| n.to_string())); + .or_else(|| rustc_attr_parsing::find_crate_name(attrs).map(|n| n.to_string())); match sess.io.output_file { None => { diff --git a/compiler/rustc_lint/Cargo.toml b/compiler/rustc_lint/Cargo.toml index ec5f0f06c591b..cc5a90296332d 100644 --- a/compiler/rustc_lint/Cargo.toml +++ b/compiler/rustc_lint/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 093cc16fb4c18..961582ce9eed4 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -387,7 +387,7 @@ pub struct MissingDoc; impl_lint_pass!(MissingDoc => [MISSING_DOCS]); -fn has_doc(attr: &ast::Attribute) -> bool { +fn has_doc(attr: &hir::Attribute) -> bool { if attr.is_doc_comment() { return true; } @@ -1012,7 +1012,7 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(it.hir_id()); - let check_no_mangle_on_generic_fn = |no_mangle_attr: &ast::Attribute, + let check_no_mangle_on_generic_fn = |no_mangle_attr: &hir::Attribute, impl_generics: Option<&hir::Generics<'_>>, generics: &hir::Generics<'_>, span| { @@ -1023,7 +1023,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { cx.emit_span_lint(NO_MANGLE_GENERIC_ITEMS, span, BuiltinNoMangleGeneric { - suggestion: no_mangle_attr.span, + suggestion: no_mangle_attr.span(), }); break; } @@ -1176,7 +1176,7 @@ declare_lint_pass!( ); impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { - fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) { if attr.has_name(sym::feature) && let Some(items) = attr.meta_item_list() { @@ -1236,7 +1236,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { { cx.emit_span_lint( UNGATED_ASYNC_FN_TRACK_CALLER, - attr.span, + attr.span(), BuiltinUngatedAsyncFnTrackCaller { label: span, session: &cx.tcx.sess }, ); } diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 289e2c9b72243..f9e5192466af2 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -38,7 +38,12 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { } LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { // We are an `eval_always` query, so looking at the attribute's `AttrId` is ok. - let attr_id = tcx.hir().attrs(hir_id)[attr_index as usize].id; + let attr_id = tcx.hir().attrs(hir_id)[attr_index as usize].id(); + + // TODO: we're only dealling with allow/deny/etc lint attributes. They can have an + // ID, while other attributes don't necessarily. however, how do we assert that + // nicely? + // let attr_id = todo!(); (attr_id, lint_index) } _ => panic!("fulfilled expectations must have a lint index"), diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 45b188205d228..53a9f64e67269 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -182,7 +182,7 @@ fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName { // information, we could have codegen_fn_attrs also give span information back for // where the attribute was defined. However, until this is found to be a // bottleneck, this does just fine. - (overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span) + (overridden_link_name, tcx.get_attr(fi, sym::link_name).unwrap().span()) }) { SymbolName::Link(overridden_link_name, overridden_link_name_span) diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 482650e04e85c..5cfcacab1acc9 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -405,9 +405,11 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword { if is_doc_keyword(keyword) { return; } - cx.emit_span_lint(EXISTING_DOC_KEYWORD, attr.span, NonExistentDocKeyword { - keyword, - }); + cx.emit_span_lint( + EXISTING_DOC_KEYWORD, + attr.span(), + NonExistentDocKeyword { keyword }, + ); } } } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 4b1dafbdbeea2..04099cd9001fd 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,3 +1,4 @@ +use rustc_ast::attr::AttributeExt; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; @@ -371,7 +372,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { /// FIXME(blyxyas): In a future revision, we should also graph #![allow]s, /// but that is handled with more care - fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) { + fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) { if matches!( Level::from_attr(attribute), Some( @@ -383,10 +384,9 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { ) ) { let store = unerased_lint_store(self.tcx.sess); - let Some(meta) = attribute.meta() else { return }; // Lint attributes are always a metalist inside a // metalist (even with just one lint). - let Some(meta_item_list) = meta.meta_item_list() else { return }; + let Some(meta_item_list) = attribute.meta_item_list() else { return }; for meta_list in meta_item_list { // Convert Path to String @@ -686,7 +686,12 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { }; } - fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option) { + fn add( + &mut self, + attrs: &[impl AttributeExt], + is_crate_node: bool, + source_hir_id: Option, + ) { let sess = self.sess; for (attr_index, attr) in attrs.iter().enumerate() { if attr.has_name(sym::automatically_derived) { @@ -910,7 +915,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let src = LintLevelSource::Node { name, span: sp, reason }; for &id in ids { - if self.check_gated_lint(id, attr.span, false) { + if self.check_gated_lint(id, attr.span(), false) { self.insert_spec(id, (level, src)); } } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 4d8ebf2909ef8..e34b43572d03d 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,14 +1,15 @@ use rustc_abi::ExternAbi; +use rustc_attr_parsing::{AttributeParseContext, AttributeKind, ReprAttr}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{GenericParamKind, PatKind}; +use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatKind}; use rustc_middle::ty; use rustc_session::config::CrateType; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{Ident, sym}; use rustc_span::{BytePos, Span}; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_hir as hir}; use crate::lints::{ NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub, @@ -162,10 +163,10 @@ impl NonCamelCaseTypes { impl EarlyLintPass for NonCamelCaseTypes { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { - let has_repr_c = it - .attrs - .iter() - .any(|attr| attr::find_repr_attrs(cx.sess(), attr).contains(&attr::ReprC)); + let has_repr_c = matches!( + AttributeParseContext::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span), + Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.contains(&ReprAttr::ReprC) + ); if has_repr_c { return; @@ -342,35 +343,35 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name { Some(Ident::from_str(name)) } else { - attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) - .and_then(|attr| attr.meta()) - .and_then(|meta| { - meta.name_value_literal().and_then(|lit| { - if let ast::LitKind::Str(name, ..) = lit.kind { - // Discard the double quotes surrounding the literal. - let sp = cx - .sess() - .source_map() - .span_to_snippet(lit.span) - .ok() - .and_then(|snippet| { - let left = snippet.find('"')?; - let right = - snippet.rfind('"').map(|pos| snippet.len() - pos)?; - - Some( - lit.span - .with_lo(lit.span.lo() + BytePos(left as u32 + 1)) - .with_hi(lit.span.hi() - BytePos(right as u32)), - ) - }) - .unwrap_or(lit.span); - - Some(Ident::new(name, sp)) - } else { - None - } - }) + ast::attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) + .and_then(|attr| { + if let Attribute::Unparsed(n) = attr + && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: ref lit }, .. } = + n.as_ref() + && let ast::LitKind::Str(name, ..) = lit.kind + { + // Discard the double quotes surrounding the literal. + let sp = cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .ok() + .and_then(|snippet| { + let left = snippet.find('"')?; + let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?; + + Some( + lit.span + .with_lo(lit.span.lo() + BytePos(left as u32 + 1)) + .with_hi(lit.span.hi() - BytePos(right as u32)), + ) + }) + .unwrap_or(lit.span); + + Some(Ident::new(name, sp)) + } else { + None + } }) }; @@ -502,7 +503,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(it.hir_id()); match it.kind { - hir::ItemKind::Static(..) if !attr::contains_name(attrs, sym::no_mangle) => { + hir::ItemKind::Static(..) if !ast::attr::contains_name(attrs, sym::no_mangle) => { NonUpperCaseGlobals::check_upper_case(cx, "static variable", &it.ident); } hir::ItemKind::Const(..) => { diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 9d84d36e779e8..380cbe650f046 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -42,9 +42,9 @@ macro_rules! late_lint_methods { fn check_field_def(a: &'tcx rustc_hir::FieldDef<'tcx>); fn check_variant(a: &'tcx rustc_hir::Variant<'tcx>); fn check_path(a: &rustc_hir::Path<'tcx>, b: rustc_hir::HirId); - fn check_attribute(a: &'tcx rustc_ast::Attribute); - fn check_attributes(a: &'tcx [rustc_ast::Attribute]); - fn check_attributes_post(a: &'tcx [rustc_ast::Attribute]); + fn check_attribute(a: &'tcx rustc_hir::Attribute); + fn check_attributes(a: &'tcx [rustc_hir::Attribute]); + fn check_attributes_post(a: &'tcx [rustc_hir::Attribute]); ]); ) } diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index dca42fea57d34..83942918e3b58 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -3,7 +3,7 @@ use rustc_abi::{Integer, Size}; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::{bug, ty}; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; use crate::LateContext; use crate::context::LintContext; diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index eb761bd6475fb..68393fe0c8b51 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -3,8 +3,9 @@ // tidy-alphabetical-end use rustc_abi::ExternAbi; +use rustc_ast::AttrId; +use rustc_ast::attr::AttributeExt; use rustc_ast::node_id::NodeId; -use rustc_ast::{AttrId, Attribute}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, @@ -221,20 +222,24 @@ impl Level { } /// Converts an `Attribute` to a level. - pub fn from_attr(attr: &Attribute) -> Option { - Self::from_symbol(attr.name_or_empty(), Some(attr.id)) + pub fn from_attr(attr: &impl AttributeExt) -> Option { + Self::from_symbol(attr.name_or_empty(), || Some(attr.id())) } /// Converts a `Symbol` to a level. - pub fn from_symbol(s: Symbol, id: Option) -> Option { - match (s, id) { - (sym::allow, _) => Some(Level::Allow), - (sym::expect, Some(attr_id)) => { - Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None })) + pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option) -> Option { + match s { + sym::allow => Some(Level::Allow), + sym::expect => { + if let Some(attr_id) = id() { + Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None })) + } else { + None + } } - (sym::warn, _) => Some(Level::Warn), - (sym::deny, _) => Some(Level::Deny), - (sym::forbid, _) => Some(Level::Forbid), + sym::warn => Some(Level::Warn), + sym::deny => Some(Level::Deny), + sym::forbid => Some(Level::Forbid), _ => None, } } diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 12519be9870b2..0b9fdbbd3da8d 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -11,7 +11,7 @@ libloading = "0.8.0" odht = { version = "0.3.1", features = ["nightly"] } rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 29dba2bca6149..fd1535094c982 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -17,6 +17,7 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; use rustc_errors::DiagCtxtHandle; use rustc_expand::base::SyntaxExtension; use rustc_fs_util::try_canonicalize; +use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE, LocalDefId, StableCrateId}; use rustc_hir::definitions::Definitions; use rustc_index::IndexVec; @@ -97,7 +98,13 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { } pub enum LoadedMacro { - MacroDef { def: MacroDef, ident: Ident, attrs: AttrVec, span: Span, edition: Edition }, + MacroDef { + def: MacroDef, + ident: Ident, + attrs: Vec, + span: Span, + edition: Edition, + }, ProcMacro(SyntaxExtension), } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 8bd2281981b4b..4c822b65f739f 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use rustc_abi::ExternAbi; use rustc_ast::CRATE_NODE_ID; -use rustc_attr as attr; +use rustc_attr_parsing as attr; use rustc_data_structures::fx::FxHashSet; use rustc_middle::query::LocalCrate; use rustc_middle::ty::{self, List, Ty, TyCtxt}; @@ -450,7 +450,7 @@ impl<'tcx> Collector<'tcx> { (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule)); } let Some((name, name_span)) = name else { - sess.dcx().emit_err(errors::LinkRequiresName { span: m.span }); + sess.dcx().emit_err(errors::LinkRequiresName { span: m.span() }); continue; }; @@ -485,7 +485,7 @@ impl<'tcx> Collector<'tcx> { let link_ordinal_attr = self.tcx.get_attr(child_item, sym::link_ordinal).unwrap(); sess.dcx().emit_err(errors::LinkOrdinalRawDylib { - span: link_ordinal_attr.span, + span: link_ordinal_attr.span(), }); } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index b9586338655e7..af312e1aa648e 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1061,7 +1061,6 @@ impl<'a> CrateMetadataRef<'a> { let attrs: Vec<_> = self.get_item_attrs(id, sess).collect(); SyntaxExtension::new( sess, - tcx.features(), kind, self.get_span(id, sess), helper_attrs, @@ -1369,10 +1368,10 @@ impl<'a> CrateMetadataRef<'a> { self, id: DefIndex, sess: &'a Session, - ) -> impl Iterator + 'a { + ) -> impl Iterator + 'a { self.root .tables - .attributes + .hir_attributes .get(self, id) .unwrap_or_else(|| { // Structure and variant constructors don't have any attributes encoded for them, @@ -1383,7 +1382,7 @@ impl<'a> CrateMetadataRef<'a> { let parent_id = def_key.parent.expect("no parent for a constructor"); self.root .tables - .attributes + .hir_attributes .get(self, parent_id) .expect("no encoded attributes for a structure or variant") }) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index f01ad31d0bb56..38e1b25f34b65 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -1,7 +1,7 @@ use std::any::Any; use std::mem; -use rustc_attr::Deprecation; +use rustc_attr_parsing::Deprecation; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; @@ -344,7 +344,7 @@ provide! { tcx, def_id, other, cdata, } associated_item => { cdata.get_associated_item(def_id.index, tcx.sess) } inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) } - item_attrs => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) } + hir_attrs_for_def => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) } is_mir_available => { cdata.is_item_mir_available(def_id.index) } is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) } cross_crate_inlinable => { table_direct } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 5c80d24f502d1..ff4edbc53d50b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -4,7 +4,7 @@ use std::fs::File; use std::io::{Read, Seek, Write}; use std::path::{Path, PathBuf}; -use rustc_ast::Attribute; +use rustc_ast::attr::AttributeExt; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::sync::{Lrc, join, par_for_each_in}; @@ -710,15 +710,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE), has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE), - has_default_lib_allocator: attr::contains_name(attrs, sym::default_lib_allocator), + has_default_lib_allocator: ast::attr::contains_name( + attrs, + sym::default_lib_allocator, + ), proc_macro_data, debugger_visualizers, - compiler_builtins: attr::contains_name(attrs, sym::compiler_builtins), - needs_allocator: attr::contains_name(attrs, sym::needs_allocator), - needs_panic_runtime: attr::contains_name(attrs, sym::needs_panic_runtime), - no_builtins: attr::contains_name(attrs, sym::no_builtins), - panic_runtime: attr::contains_name(attrs, sym::panic_runtime), - profiler_runtime: attr::contains_name(attrs, sym::profiler_runtime), + compiler_builtins: ast::attr::contains_name(attrs, sym::compiler_builtins), + needs_allocator: ast::attr::contains_name(attrs, sym::needs_allocator), + needs_panic_runtime: ast::attr::contains_name(attrs, sym::needs_panic_runtime), + no_builtins: ast::attr::contains_name(attrs, sym::no_builtins), + panic_runtime: ast::attr::contains_name(attrs, sym::panic_runtime), + profiler_runtime: ast::attr::contains_name(attrs, sym::profiler_runtime), symbol_mangling_version: tcx.sess.opts.get_symbol_mangling_version(), crate_deps, @@ -814,7 +817,7 @@ struct AnalyzeAttrState<'a> { /// visibility: this is a piece of data that can be computed once per defid, and not once per /// attribute. Some attributes would only be usable downstream if they are public. #[inline] -fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState<'_>) -> bool { +fn analyze_attr(attr: &impl AttributeExt, state: &mut AnalyzeAttrState<'_>) -> bool { let mut should_encode = false; if !rustc_feature::encode_cross_crate(attr.name_or_empty()) { // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates. @@ -1357,9 +1360,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { .hir() .attrs(tcx.local_def_id_to_hir_id(def_id)) .iter() - .filter(|attr| analyze_attr(attr, &mut state)); + .filter(|attr| analyze_attr(*attr, &mut state)); - record_array!(self.tables.attributes[def_id.to_def_id()] <- attr_iter); + record_array!(self.tables.hir_attributes[def_id.to_def_id()] <- attr_iter); let mut attr_flags = AttrFlags::empty(); if state.is_doc_hidden { @@ -1920,11 +1923,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Proc-macros may have attributes like `#[allow_internal_unstable]`, // so downstream crates need access to them. let attrs = hir.attrs(proc_macro); - let macro_kind = if attr::contains_name(attrs, sym::proc_macro) { + let macro_kind = if ast::attr::contains_name(attrs, sym::proc_macro) { MacroKind::Bang - } else if attr::contains_name(attrs, sym::proc_macro_attribute) { + } else if ast::attr::contains_name(attrs, sym::proc_macro_attribute) { MacroKind::Attr - } else if let Some(attr) = attr::find_by_name(attrs, sym::proc_macro_derive) { + } else if let Some(attr) = ast::attr::find_by_name(attrs, sym::proc_macro_derive) { // This unwrap chain should have been checked by the proc-macro harness. name = attr.meta_item_list().unwrap()[0] .meta_item() diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4961464833af0..e6d8bc08ccd4a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -40,7 +40,7 @@ use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span}; use rustc_target::spec::{PanicStrategy, TargetTuple}; use table::TableBuilder; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; use crate::creader::CrateMetadataRef; @@ -403,7 +403,7 @@ define_tables! { cross_crate_inlinable: Table, - optional: - attributes: Table>, + hir_attributes: Table>, // For non-reexported names in a module every name is associated with a separate `DefId`, // so we can take their names, visibilities etc from other encoded tables. module_children_non_reexports: Table>, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 3bda3a4aa631b..e64500f812a1d 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -17,7 +17,7 @@ rustc_apfloat = "0.2.0" rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_error_messages = { path = "../rustc_error_messages" } # Used for intra-doc links rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index b664230d10bce..52233d407f2e0 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -85,7 +85,7 @@ macro_rules! arena_types { [] upvars_mentioned: rustc_data_structures::fx::FxIndexMap, [] dyn_compatibility_violations: rustc_middle::traits::DynCompatibilityViolation, [] codegen_unit: rustc_middle::mir::mono::CodegenUnit<'tcx>, - [decode] attribute: rustc_ast::Attribute, + [decode] attribute: rustc_hir::Attribute, [] name_set: rustc_data_structures::unord::UnordSet, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet, [] pats: rustc_middle::ty::PatternKind<'tcx>, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 0c701c834f27e..fc3cbfd7b3e4f 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -9,11 +9,11 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::intravisit::Visitor; use rustc_hir::*; +use rustc_hir_pretty as pprust_hir; use rustc_middle::hir::nested_filter; use rustc_span::def_id::StableCrateId; use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::{ErrorGuaranteed, Span}; -use {rustc_ast as ast, rustc_hir_pretty as pprust_hir}; use crate::hir::ModuleItems; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; @@ -381,7 +381,7 @@ impl<'hir> Map<'hir> { /// Gets the attributes on the crate. This is preferable to /// invoking `krate.attrs` because it registers a tighter /// dep-graph access. - pub fn krate_attrs(self) -> &'hir [ast::Attribute] { + pub fn krate_attrs(self) -> &'hir [Attribute] { self.attrs(CRATE_HIR_ID) } @@ -792,7 +792,7 @@ impl<'hir> Map<'hir> { /// Given a node ID, gets a list of attributes associated with the AST /// corresponding to the node-ID. - pub fn attrs(self, id: HirId) -> &'hir [ast::Attribute] { + pub fn attrs(self, id: HirId) -> &'hir [Attribute] { self.tcx.hir_attrs(id.owner).get(id.local_id) } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index ad0d70152e1ad..ffefd81cd08e4 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -151,7 +151,7 @@ impl<'tcx> TyCtxt<'tcx> { self, node: OwnerNode<'_>, bodies: &SortedMap>, - attrs: &SortedMap, + attrs: &SortedMap, ) -> (Option, Option) { if self.needs_crate_hash() { self.with_stable_hashing_context(|mut hcx| { diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 44428471a5fea..509063c40d77f 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,5 +1,5 @@ use rustc_abi::Align; -use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; +use rustc_attr_parsing::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::symbol::Symbol; use rustc_target::spec::SanitizerSet; diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs index 270bcabcc86a2..3a3e84a87af5e 100644 --- a/compiler/rustc_middle/src/middle/limits.rs +++ b/compiler/rustc_middle/src/middle/limits.rs @@ -10,7 +10,7 @@ use std::num::IntErrorKind; -use rustc_ast::Attribute; +use rustc_ast::attr::AttributeExt; use rustc_session::{Limit, Limits, Session}; use rustc_span::symbol::{Symbol, sym}; @@ -35,32 +35,36 @@ pub fn provide(providers: &mut Providers) { } } -pub fn get_recursion_limit(krate_attrs: &[Attribute], sess: &Session) -> Limit { +pub fn get_recursion_limit(krate_attrs: &[impl AttributeExt], sess: &Session) -> Limit { get_limit(krate_attrs, sess, sym::recursion_limit, 128) } -fn get_limit(krate_attrs: &[Attribute], sess: &Session, name: Symbol, default: usize) -> Limit { +fn get_limit( + krate_attrs: &[impl AttributeExt], + sess: &Session, + name: Symbol, + default: usize, +) -> Limit { match get_limit_size(krate_attrs, sess, name) { Some(size) => Limit::new(size), None => Limit::new(default), } } -pub fn get_limit_size(krate_attrs: &[Attribute], sess: &Session, name: Symbol) -> Option { +pub fn get_limit_size( + krate_attrs: &[impl AttributeExt], + sess: &Session, + name: Symbol, +) -> Option { for attr in krate_attrs { if !attr.has_name(name) { continue; } - if let Some(s) = attr.value_str() { - match s.as_str().parse() { + if let Some(sym) = attr.value_str() { + match sym.as_str().parse() { Ok(n) => return Some(n), Err(e) => { - let value_span = attr - .meta() - .and_then(|meta| meta.name_value_literal_span()) - .unwrap_or(attr.span); - let error_str = match e.kind() { IntErrorKind::PosOverflow => "`limit` is too large", IntErrorKind::Empty => "`limit` must be a non-negative integer", @@ -71,7 +75,11 @@ pub fn get_limit_size(krate_attrs: &[Attribute], sess: &Session, name: Symbol) - IntErrorKind::Zero => bug!("zero is a valid `limit`"), kind => bug!("unimplemented IntErrorKind variant: {:?}", kind), }; - sess.dcx().emit_err(LimitInvalid { span: attr.span, value_span, error_str }); + sess.dcx().emit_err(LimitInvalid { + span: attr.span(), + value_span: attr.value_span().unwrap(), + error_str, + }); } } } diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 94d13021612be..ece561c7493d4 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use rustc_ast::NodeId; -use rustc_attr::{ +use rustc_attr_parsing::{ self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability, }; use rustc_data_structures::unord::UnordMap; @@ -392,7 +392,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(Stability { - level: attr::Unstable { reason, issue, is_soft, implied_by }, + level: attr::StabilityLevel::Unstable { reason, issue, is_soft, implied_by }, feature, .. }) => { @@ -475,7 +475,7 @@ impl<'tcx> TyCtxt<'tcx> { match stability { Some(DefaultBodyStability { - level: attr::Unstable { reason, issue, is_soft, .. }, + level: attr::StabilityLevel::Unstable { reason, issue, is_soft, .. }, feature, }) => { if span.allows_unstable(feature) { diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 161716610fe63..abb236dd16840 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -1,7 +1,7 @@ use std::fmt; use std::hash::Hash; -use rustc_attr::InlineAttr; +use rustc_attr_parsing::InlineAttr; use rustc_data_structures::base_n::{BaseNString, CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxIndexMap; diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 013847f0b2d53..9afcf9466b6d0 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -239,9 +239,9 @@ trivial! { bool, Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>, Option, - Option, - Option, - Option, + Option, + Option, + Option, Option, Option, Option, @@ -264,10 +264,10 @@ trivial! { Result, rustc_abi::ReprOptions, rustc_ast::expand::allocator::AllocatorKind, - rustc_attr::ConstStability, - rustc_attr::DefaultBodyStability, - rustc_attr::Deprecation, - rustc_attr::Stability, + rustc_attr_parsing::ConstStability, + rustc_attr_parsing::DefaultBodyStability, + rustc_attr_parsing::Deprecation, + rustc_attr_parsing::Stability, rustc_data_structures::svh::Svh, rustc_errors::ErrorGuaranteed, rustc_hir::Constness, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index fc3d690a8a9cc..6487bd837d24b 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -44,7 +44,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{DUMMY_SP, Span}; use rustc_target::spec::PanicStrategy; -use {rustc_abi as abi, rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_abi as abi, rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; @@ -1264,7 +1264,7 @@ rustc_queries! { /// Returns the attributes on the item at `def_id`. /// /// Do not use this directly, use `tcx.get_attrs` instead. - query item_attrs(def_id: DefId) -> &'tcx [ast::Attribute] { + query hir_attrs_for_def(def_id: DefId) -> &'tcx [hir::Attribute] { desc { |tcx| "collecting attributes of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 119a99e1bf7a0..2c22f7b8f4928 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -798,7 +798,7 @@ macro_rules! impl_ref_decoder { impl_ref_decoder! {<'tcx> Span, - rustc_ast::Attribute, + rustc_hir::Attribute, rustc_span::symbol::Ident, ty::Variance, rustc_span::def_id::DefId, diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 515aabbe2fc8a..e2ec4e9212bc4 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -75,7 +75,7 @@ impl OverlapMode { tcx.hir().attrs(tcx.local_def_id_to_hir_id(local_def_id)) }) .find(|attr| attr.has_name(sym::rustc_strict_coherence)) - .map(|attr| attr.span); + .map(|attr| attr.span()); tcx.dcx().emit_err(StrictCoherenceNeedsNegativeCoherence { span: tcx.def_span(trait_id), attr_span, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 2841470d24878..8c42c9a3e2168 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -13,7 +13,7 @@ use std::ops::{Bound, Deref}; use std::{fmt, iter, mem}; use rustc_abi::{ExternAbi, FieldIdx, Layout, LayoutData, TargetDataLayout, VariantIdx}; -use rustc_ast::{self as ast, attr}; +use rustc_ast as ast; use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::FxHashMap; @@ -29,13 +29,12 @@ use rustc_data_structures::unord::UnordSet; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan, }; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::Definitions; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{HirId, Node, TraitCandidate}; +use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate}; use rustc_index::IndexVec; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::cache::WithDepNode; @@ -1508,7 +1507,7 @@ impl<'tcx> TyCtxt<'tcx> { Bound::Included(a.get()) } else { self.dcx().span_delayed_bug( - attr.span, + attr.span(), "invalid rustc_layout_scalar_valid_range attribute", ); Bound::Unbounded @@ -3239,12 +3238,16 @@ pub fn provide(providers: &mut Providers) { providers.extern_mod_stmt_cnum = |tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned(); providers.is_panic_runtime = - |tcx, LocalCrate| attr::contains_name(tcx.hir().krate_attrs(), sym::panic_runtime); + |tcx, LocalCrate| contains_name(tcx.hir().krate_attrs(), sym::panic_runtime); providers.is_compiler_builtins = - |tcx, LocalCrate| attr::contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins); + |tcx, LocalCrate| contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins); providers.has_panic_handler = |tcx, LocalCrate| { // We want to check if the panic handler was defined in this crate tcx.lang_items().panic_impl().is_some_and(|did| did.is_local()) }; providers.source_span = |tcx, def_id| tcx.untracked.source_span.get(def_id).unwrap_or(DUMMY_SP); } + +pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool { + attrs.iter().any(|x| x.has_name(name)) +} diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 65c909e70f626..e1d0db96461c3 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -202,7 +202,7 @@ impl<'tcx> Instance<'tcx> { if !tcx.sess.opts.share_generics() // However, if the def_id is marked inline(never), then it's fine to just reuse the // upstream monomorphization. - && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr::InlineAttr::Never + && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_attr_parsing::InlineAttr::Never { return None; } @@ -277,7 +277,7 @@ impl<'tcx> InstanceKind<'tcx> { &self, tcx: TyCtxt<'tcx>, attr: Symbol, - ) -> impl Iterator { + ) -> impl Iterator { tcx.get_attrs(self.def_id(), attr) } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 70e0568b2025b..e88198f7b24a5 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -37,6 +37,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res} use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; use rustc_hir::{LangItem, Safety}; use rustc_index::IndexVec; +use rustc_attr_parsing::AttributeKind; use rustc_macros::{ Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, extension, @@ -52,7 +53,7 @@ pub use rustc_type_ir::relate::VarianceDiagInfo; pub use rustc_type_ir::*; use tracing::{debug, instrument}; pub use vtable::*; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_attr_parsing as attr, rustc_hir as hir}; pub use self::closure::{ BorrowKind, CAPTURE_STRUCT_LOCAL, CaptureInfo, CapturedPlace, ClosureTypeInfo, @@ -1504,9 +1505,10 @@ impl<'tcx> TyCtxt<'tcx> { field_shuffle_seed ^= user_seed; } - for attr in self.get_attrs(did, sym::repr) { - for r in attr::parse_repr_attr(self.sess, attr) { - flags.insert(match r { + if let Some(reprs) = attr::find_attr!(self.get_all_attrs(did), AttributeKind::Repr(r) => r) + { + for r in reprs { + flags.insert(match *r { attr::ReprRust => ReprFlags::empty(), attr::ReprC => ReprFlags::IS_C, attr::ReprPacked(pack) => { @@ -1743,11 +1745,11 @@ impl<'tcx> TyCtxt<'tcx> { } // FIXME(@lcnr): Remove this function. - pub fn get_attrs_unchecked(self, did: DefId) -> &'tcx [ast::Attribute] { + pub fn get_attrs_unchecked(self, did: DefId) -> &'tcx [hir::Attribute] { if let Some(did) = did.as_local() { self.hir().attrs(self.local_def_id_to_hir_id(did)) } else { - self.item_attrs(did) + self.hir_attrs_for_def(did) } } @@ -1756,14 +1758,22 @@ impl<'tcx> TyCtxt<'tcx> { self, did: impl Into, attr: Symbol, - ) -> impl Iterator { + ) -> impl Iterator { + self.get_all_attrs(did).filter(move |a: &&hir::Attribute| a.has_name(attr)) + } + + /// Gets all attributes. + pub fn get_all_attrs( + self, + did: impl Into, + ) -> impl Iterator { let did: DefId = did.into(); - let filter_fn = move |a: &&ast::Attribute| a.has_name(attr); if let Some(did) = did.as_local() { - self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn) + self.hir().attrs(self.local_def_id_to_hir_id(did)).iter() } else { - debug_assert!(rustc_feature::encode_cross_crate(attr)); - self.item_attrs(did).iter().filter(filter_fn) + // TODO: commented this check because we don't have the attr symbol from get_attrs here + // debug_assert!(rustc_feature::encode_cross_crate(attr)); + self.hir_attrs_for_def(did).iter() } } @@ -1779,7 +1789,7 @@ impl<'tcx> TyCtxt<'tcx> { self, did: impl Into, attr: Symbol, - ) -> Option<&'tcx ast::Attribute> { + ) -> Option<&'tcx hir::Attribute> { let did: DefId = did.into(); if did.as_local().is_some() { // it's a crate local item, we need to check feature flags @@ -1792,7 +1802,7 @@ impl<'tcx> TyCtxt<'tcx> { // we filter out unstable diagnostic attributes before // encoding attributes debug_assert!(rustc_feature::encode_cross_crate(attr)); - self.item_attrs(did) + self.hir_attrs_for_def(did) .iter() .find(|a| matches!(a.path().as_ref(), [sym::diagnostic, a] if *a == attr)) } @@ -1802,19 +1812,19 @@ impl<'tcx> TyCtxt<'tcx> { self, did: DefId, attr: &'attr [Symbol], - ) -> impl Iterator + 'attr + ) -> impl Iterator + 'attr where 'tcx: 'attr, { - let filter_fn = move |a: &&ast::Attribute| a.path_matches(attr); + let filter_fn = move |a: &&hir::Attribute| a.path_matches(attr); if let Some(did) = did.as_local() { self.hir().attrs(self.local_def_id_to_hir_id(did)).iter().filter(filter_fn) } else { - self.item_attrs(did).iter().filter(filter_fn) + self.hir_attrs_for_def(did).iter().filter(filter_fn) } } - pub fn get_attr(self, did: impl Into, attr: Symbol) -> Option<&'tcx ast::Attribute> { + pub fn get_attr(self, did: impl Into, attr: Symbol) -> Option<&'tcx hir::Attribute> { if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) { let did: DefId = did.into(); bug!("get_attr: unexpected called with DefId `{:?}`, attr `{:?}`", did, attr); diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index f7322217aa33f..59f2555be95c0 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -80,10 +80,10 @@ trivially_parameterized_over_tcx! { rustc_ast::Attribute, rustc_ast::DelimArgs, rustc_ast::expand::StrippedCfgItem, - rustc_attr::ConstStability, - rustc_attr::DefaultBodyStability, - rustc_attr::Deprecation, - rustc_attr::Stability, + rustc_attr_parsing::ConstStability, + rustc_attr_parsing::DefaultBodyStability, + rustc_attr_parsing::Deprecation, + rustc_attr_parsing::Stability, rustc_hir::Constness, rustc_hir::Defaultness, rustc_hir::Safety, @@ -111,6 +111,7 @@ trivially_parameterized_over_tcx! { rustc_span::hygiene::SyntaxContextData, rustc_span::symbol::Ident, rustc_type_ir::Variance, + rustc_hir::Attribute, } // HACK(compiler-errors): This macro rule can only take a fake path, @@ -140,5 +141,5 @@ parameterized_over_tcx! { ty::Predicate, ty::Clause, ty::ClauseKind, - ty::ImplTraitHeader + ty::ImplTraitHeader, } diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index e809c9a23f309..34cdc288f0ba1 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -17,10 +17,9 @@ //! terminators, and everything below can be found in the `parse::instruction` submodule. //! -use rustc_ast::Attribute; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::HirId; use rustc_hir::def_id::DefId; +use rustc_hir::{Attribute, HirId}; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::*; use rustc_middle::span_bug; diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index d75f01dfba09f..4e1e90611f8c7 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -378,7 +378,7 @@ impl<'tcx> Cx<'tcx> { if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_box) { if attrs.len() != 1 { tcx.dcx().emit_err(errors::RustcBoxAttributeError { - span: attrs[0].span, + span: attrs[0].span(), reason: errors::RustcBoxAttrReason::Attributes, }); } else if let Some(box_item) = tcx.lang_items().owned_box() { @@ -406,7 +406,7 @@ impl<'tcx> Cx<'tcx> { } } else { tcx.dcx().emit_err(errors::RustcBoxAttributeError { - span: attrs[0].span, + span: attrs[0].span(), reason: errors::RustcBoxAttrReason::MissingBox, }); } diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 4b648d21084ca..2f233f787f0cc 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -10,7 +10,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_const_eval = { path = "../rustc_const_eval" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index edaec3c796511..58d9eccb24c0b 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -68,7 +68,7 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { Some(_) | None => { // Other possibilities should have been rejected by `rustc_parse::validate_attr`. // Use `span_delayed_bug` to avoid an ICE in failing builds (#127880). - tcx.dcx().span_delayed_bug(attr.span, "unexpected value of coverage attribute"); + tcx.dcx().span_delayed_bug(attr.span(), "unexpected value of coverage attribute"); } } } diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 589be81558c2b..e1f1dd83f0df4 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -1,4 +1,4 @@ -use rustc_attr::InlineAttr; +use rustc_attr_parsing::InlineAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::visit::Visitor; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 79c38b50459c4..f0acbaf56b63e 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -4,7 +4,7 @@ use std::iter; use std::ops::{Range, RangeFrom}; use rustc_abi::{ExternAbi, FieldIdx}; -use rustc_attr::InlineAttr; +use rustc_attr_parsing::InlineAttr; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_index::Idx; diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index e18441ea7fc95..9bdaeb015cd5f 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index dabce72650a31..33e1065f051b2 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -833,7 +833,8 @@ fn mono_item_visibility<'tcx>( return if is_generic && (always_export_generics || (can_export_generics - && tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never)) + && tcx.codegen_fn_attrs(def_id).inline + == rustc_attr_parsing::InlineAttr::Never)) { // If it is an upstream monomorphization and we export generics, we must make // it available to downstream crates. @@ -847,7 +848,7 @@ fn mono_item_visibility<'tcx>( if is_generic { if always_export_generics || (can_export_generics - && tcx.codegen_fn_attrs(def_id).inline == rustc_attr::InlineAttr::Never) + && tcx.codegen_fn_attrs(def_id).inline == rustc_attr_parsing::InlineAttr::Never) { if tcx.is_unreachable_local_definition(def_id) { // This instance cannot be used from another crate. diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index b6fa209958831..8cf37671185a2 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,6 +1,5 @@ -use rustc_ast as ast; -use rustc_ast::attr; use rustc_ast::token::{self, Delimiter}; +use rustc_ast::{self as ast, Attribute, attr}; use rustc_errors::codes::*; use rustc_errors::{Diag, PResult}; use rustc_span::symbol::kw; @@ -48,7 +47,7 @@ impl<'a> Parser<'a> { let start_pos = self.num_bump_calls; loop { let attr = if self.check(&token::Pound) { - let prev_outer_attr_sp = outer_attrs.last().map(|attr| attr.span); + let prev_outer_attr_sp = outer_attrs.last().map(|attr: &Attribute| attr.span); let inner_error_reason = if just_parsed_doc_comment { Some(InnerAttrForbiddenReason::AfterOuterDocComment { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 37556c064d824..976ffe608a2ff 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -29,9 +29,9 @@ use rustc_ast::tokenstream::{ }; use rustc_ast::util::case::Case; use rustc_ast::{ - self as ast, AnonConst, AttrArgs, AttrArgsEq, AttrId, ByRef, Const, CoroutineKind, - DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, - Safety, StrLit, Visibility, VisibilityKind, + self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, + DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit, + Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -1376,7 +1376,7 @@ impl<'a> Parser<'a> { AttrArgs::Delimited(args) } else if self.eat(&token::Eq) { let eq_span = self.prev_token.span; - AttrArgs::Eq { eq_span, value: AttrArgsEq::Ast(self.parse_expr_force_collect()?) } + AttrArgs::Eq { eq_span, expr: self.parse_expr_force_collect()? } } else { AttrArgs::Empty }) diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index aab3f10bc667b..8b6b37c0f8f52 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -3,8 +3,7 @@ use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{ - self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, - Safety, + self as ast, AttrArgs, Attribute, DelimArgs, MetaItem, MetaItemInner, MetaItemKind, Safety, }; use rustc_errors::{Applicability, FatalError, PResult}; use rustc_feature::{AttributeSafety, AttributeTemplate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; @@ -70,7 +69,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met parse_in(psess, tokens.clone(), "meta list", |p| p.parse_meta_seq_top())?; MetaItemKind::List(nmis) } - AttrArgs::Eq { value: AttrArgsEq::Ast(expr), .. } => { + AttrArgs::Eq { expr, .. } => { if let ast::ExprKind::Lit(token_lit) = expr.kind { let res = ast::MetaItemLit::from_token_lit(token_lit, expr.span); let res = match res { @@ -116,9 +115,6 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met return Err(err); } } - AttrArgs::Eq { value: AttrArgsEq::Hir(lit), .. } => { - MetaItemKind::NameValue(lit.clone()) - } }, }) } diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml index ed5991459ac1b..497eb373c45c4 100644 --- a/compiler/rustc_passes/Cargo.toml +++ b/compiler/rustc_passes/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 7a0a518bb513a..fb0b8f05db5bc 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -308,9 +308,6 @@ passes_duplicate_lang_item_crate_depends = .first_definition_path = first definition in `{$orig_crate_name}` loaded from {$orig_path} .second_definition_path = second definition in `{$crate_name}` loaded from {$path} -passes_empty_confusables = - expected at least one confusable name - passes_export_name = attribute should be applied to a free function, impl method or static .label = not a free function, impl method or static @@ -359,9 +356,6 @@ passes_implied_feature_not_exist = passes_incorrect_do_not_recommend_location = `#[diagnostic::do_not_recommend]` can only be placed on trait implementations -passes_incorrect_meta_item = expected a quoted string literal -passes_incorrect_meta_item_suggestion = consider surrounding this with quotes - passes_incorrect_target = `{$name}` lang item must be applied to a {$kind} with {$at_least -> [true] at least {$num} @@ -643,6 +637,8 @@ passes_rustc_allow_const_fn_unstable = attribute should be applied to `const fn` .label = not a `const fn` +passes_rustc_const_stable_indirect_pairing = + `const_stable_indirect` attribute does not make sense on `rustc_const_stable` function, its behavior is already implied passes_rustc_dirty_clean = attribute requires -Z query-dep-graph to be enabled diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 4db8584b88410..ff3ae65fbea0b 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -1,4 +1,4 @@ -use rustc_ast::Attribute; +use rustc_hir::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::span_bug; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 074fe77324faf..39bd44c591350 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1,3 +1,4 @@ +// FIXME(jdonszelmann): should become rustc_attr_validation //! This module implements some validity checks for attributes. //! In particular it verifies that `#[inline]` and `#[repr]` attributes are //! attached to items that actually support them and if there are @@ -7,17 +8,17 @@ use std::cell::Cell; use std::collections::hash_map::Entry; -use rustc_ast::{ - AttrKind, AttrStyle, Attribute, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast, -}; +use rustc_ast::{AttrStyle, LitKind, MetaItemInner, MetaItemKind, MetaItemLit, ast}; +use rustc_attr_parsing::ReprAttr::ReprTransparent; +use rustc_attr_parsing::{self as attr, AttributeKind}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey}; use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, self, AssocItemKind, CRATE_HIR_ID, CRATE_OWNER_ID, FnSig, ForeignItem, HirId, - Item, ItemKind, MethodKind, Safety, Target, TraitItem, + self as hir, self, AssocItemKind, Attribute, CRATE_HIR_ID, CRATE_OWNER_ID, + FnSig, ForeignItem, HirId, Item, ItemKind, MethodKind, Safety, Target, TraitItem, }; use rustc_macros::LintDiagnostic; use rustc_middle::hir::nested_filter; @@ -116,190 +117,198 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let mut seen = FxHashMap::default(); let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { - match attr.path().as_slice() { - [sym::diagnostic, sym::do_not_recommend, ..] => { - self.check_do_not_recommend(attr.span, hir_id, target) - } - [sym::diagnostic, sym::on_unimplemented, ..] => { - self.check_diagnostic_on_unimplemented(attr.span, hir_id, target) - } - [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), - [sym::coverage, ..] => self.check_coverage(attr, span, target), - [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), - [sym::no_sanitize, ..] => self.check_no_sanitize(attr, span, target), - [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target), - [sym::marker, ..] => self.check_marker(hir_id, attr, span, target), - [sym::target_feature, ..] => { - self.check_target_feature(hir_id, attr, span, target, attrs) - } - [sym::thread_local, ..] => self.check_thread_local(attr, span, target), - [sym::track_caller, ..] => { - self.check_track_caller(hir_id, attr.span, attrs, span, target) - } - [sym::doc, ..] => self.check_doc_attrs( - attr, - hir_id, - target, - &mut specified_inline, - &mut doc_aliases, - ), - [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), - [sym::export_name, ..] => self.check_export_name(hir_id, attr, span, target), - [sym::rustc_layout_scalar_valid_range_start, ..] - | [sym::rustc_layout_scalar_valid_range_end, ..] => { - self.check_rustc_layout_scalar_valid_range(attr, span, target) - } - [sym::allow_internal_unstable, ..] => { - self.check_allow_internal_unstable(hir_id, attr, span, target, attrs) - } - [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), - [sym::rustc_allow_const_fn_unstable, ..] => { - self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target) - } - [sym::rustc_std_internal_symbol, ..] => { - self.check_rustc_std_internal_symbol(attr, span, target) - } - [sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs), - [sym::rustc_as_ptr, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } - [sym::rustc_never_returns_null_ptr, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } - [sym::rustc_legacy_const_generics, ..] => { - self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item) - } - [sym::rustc_lint_query_instability, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } - [sym::rustc_lint_untracked_query_information, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } - [sym::rustc_lint_diagnostics, ..] => { - self.check_applied_to_fn_or_method(hir_id, attr, span, target) - } - [sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target), - [sym::rustc_lint_opt_deny_field_access, ..] => { - self.check_rustc_lint_opt_deny_field_access(attr, span, target) - } - [sym::rustc_clean, ..] - | [sym::rustc_dirty, ..] - | [sym::rustc_if_this_changed, ..] - | [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr), - [sym::rustc_coinductive, ..] - | [sym::rustc_must_implement_one_of, ..] - | [sym::rustc_deny_explicit_impl, ..] - | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target), - [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target), - [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target), - [sym::must_use, ..] => self.check_must_use(hir_id, attr, target), - [sym::may_dangle, ..] => self.check_may_dangle(hir_id, attr), - [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target), - [sym::rustc_allow_incoherent_impl, ..] => { - self.check_allow_incoherent_impl(attr, span, target) - } - [sym::rustc_has_incoherent_inherent_impls, ..] => { - self.check_has_incoherent_inherent_impls(attr, span, target) - } - [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span, attrs, target), - [sym::ffi_const, ..] => self.check_ffi_const(attr.span, target), - [sym::rustc_const_unstable, ..] - | [sym::rustc_const_stable, ..] - | [sym::unstable, ..] - | [sym::stable, ..] - | [sym::rustc_allowed_through_unstable_modules, ..] - | [sym::rustc_promotable, ..] => self.check_stability_promotable(attr, target), - [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target), - [sym::rustc_confusables, ..] => self.check_confusables(attr, target), - [sym::cold, ..] => self.check_cold(hir_id, attr, span, target), - [sym::link, ..] => self.check_link(hir_id, attr, span, target), - [sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target), - [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target), - [sym::no_mangle, ..] => self.check_no_mangle(hir_id, attr, span, target), - [sym::deprecated, ..] => self.check_deprecated(hir_id, attr, span, target), - [sym::macro_use, ..] | [sym::macro_escape, ..] => { - self.check_macro_use(hir_id, attr, target) - } - [sym::path, ..] => self.check_generic_attr(hir_id, attr, target, Target::Mod), - [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), - [sym::ignore, ..] | [sym::should_panic, ..] => { - self.check_generic_attr(hir_id, attr, target, Target::Fn) - } - [sym::automatically_derived, ..] => { - self.check_generic_attr(hir_id, attr, target, Target::Impl) - } - [sym::no_implicit_prelude, ..] => { - self.check_generic_attr(hir_id, attr, target, Target::Mod) - } - [sym::rustc_object_lifetime_default, ..] => self.check_object_lifetime_default(hir_id), - [sym::proc_macro, ..] => { - self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) - } - [sym::proc_macro_attribute, ..] => { - self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); - } - [sym::proc_macro_derive, ..] => { - self.check_generic_attr(hir_id, attr, target, Target::Fn); - self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) + match attr { + Attribute::Parsed(AttributeKind::Confusables { first_span, .. }) => { + self.check_confusables(*first_span, target); } - [sym::autodiff, ..] => { - self.check_autodiff(hir_id, attr, span, target) - } - [sym::coroutine, ..] => { - self.check_coroutine(attr, target); - } - [sym::linkage, ..] => self.check_linkage(attr, span, target), - [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent( attr.span, span, attrs), - [ - // ok - sym::allow - | sym::expect - | sym::warn - | sym::deny - | sym::forbid - | sym::cfg - | sym::cfg_attr - // need to be fixed - | sym::cfi_encoding // FIXME(cfi_encoding) - | sym::pointee // FIXME(derive_coerce_pointee) - | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section) - | sym::used // handled elsewhere to restrict to static items - | sym::repr // handled elsewhere to restrict to type decls items - | sym::instruction_set // broken on stable!!! - | sym::windows_subsystem // broken on stable!!! - | sym::patchable_function_entry // FIXME(patchable_function_entry) - | sym::deprecated_safe // FIXME(deprecated_safe) - // internal - | sym::prelude_import - | sym::panic_handler - | sym::allow_internal_unsafe - | sym::fundamental - | sym::lang - | sym::needs_allocator - | sym::default_lib_allocator - | sym::start - | sym::custom_mir, - .. - ] => {} - [name, ..] => { - match BUILTIN_ATTRIBUTE_MAP.get(name) { - // checked below - Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) => {} - Some(_) => { - // FIXME: differentiate between unstable and internal attributes just - // like we do with features instead of just accepting `rustc_` - // attributes by name. That should allow trimming the above list, too. - if !name.as_str().starts_with("rustc_") { - span_bug!( - attr.span, - "builtin attribute {name:?} not handled by `CheckAttrVisitor`" - ) + _ => { + match attr.path().as_slice() { + [sym::diagnostic, sym::do_not_recommend, ..] => { + self.check_do_not_recommend(attr.span(), hir_id, target) + } + [sym::diagnostic, sym::on_unimplemented, ..] => { + self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) + } + [sym::inline, ..] => self.check_inline(hir_id, attr, span, target), + [sym::coverage, ..] => self.check_coverage(attr, span, target), + [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), + [sym::no_sanitize, ..] => { + self.check_no_sanitize(attr, span, target) + } + [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target), + [sym::marker, ..] => self.check_marker(hir_id, attr, span, target), + [sym::target_feature, ..] => { + self.check_target_feature(hir_id, attr, span, target, attrs) + } + [sym::thread_local, ..] => self.check_thread_local(attr, span, target), + [sym::track_caller, ..] => { + self.check_track_caller(hir_id, attr.span(), attrs, span, target) + } + [sym::doc, ..] => self.check_doc_attrs( + attr, + hir_id, + target, + &mut specified_inline, + &mut doc_aliases, + ), + [sym::no_link, ..] => self.check_no_link(hir_id, attr, span, target), + [sym::export_name, ..] => self.check_export_name(hir_id, attr, span, target), + [sym::rustc_layout_scalar_valid_range_start, ..] + | [sym::rustc_layout_scalar_valid_range_end, ..] => { + self.check_rustc_layout_scalar_valid_range(attr, span, target) + } + [sym::allow_internal_unstable, ..] => { + self.check_allow_internal_unstable(hir_id, attr, span, target, attrs) + } + [sym::debugger_visualizer, ..] => self.check_debugger_visualizer(attr, target), + [sym::rustc_allow_const_fn_unstable, ..] => { + self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target) + } + [sym::rustc_std_internal_symbol, ..] => { + self.check_rustc_std_internal_symbol(attr, span, target) + } + [sym::naked, ..] => self.check_naked(hir_id, attr, span, target, attrs), + [sym::rustc_as_ptr, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + [sym::rustc_never_returns_null_ptr, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + [sym::rustc_legacy_const_generics, ..] => { + self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item) + } + [sym::rustc_lint_query_instability, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + [sym::rustc_lint_untracked_query_information, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + [sym::rustc_lint_diagnostics, ..] => { + self.check_applied_to_fn_or_method(hir_id, attr, span, target) + } + [sym::rustc_lint_opt_ty, ..] => self.check_rustc_lint_opt_ty(attr, span, target), + [sym::rustc_lint_opt_deny_field_access, ..] => { + self.check_rustc_lint_opt_deny_field_access(attr, span, target) + } + [sym::rustc_clean, ..] + | [sym::rustc_dirty, ..] + | [sym::rustc_if_this_changed, ..] + | [sym::rustc_then_this_would_need, ..] => self.check_rustc_dirty_clean(attr), + [sym::rustc_coinductive, ..] + | [sym::rustc_must_implement_one_of, ..] + | [sym::rustc_deny_explicit_impl, ..] + | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target), + [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target), + [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target), + [sym::must_use, ..] => self.check_must_use(hir_id, attr, target), + [sym::may_dangle, ..] => self.check_may_dangle(hir_id, attr), + [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target), + [sym::rustc_allow_incoherent_impl, ..] => { + self.check_allow_incoherent_impl(attr, span, target) + } + [sym::rustc_has_incoherent_inherent_impls, ..] => { + self.check_has_incoherent_inherent_impls(attr, span, target) + } + [sym::ffi_pure, ..] => self.check_ffi_pure(attr.span(), attrs, target), + [sym::ffi_const, ..] => self.check_ffi_const(attr.span(), target), + [sym::rustc_const_unstable, ..] + | [sym::rustc_const_stable, ..] + | [sym::unstable, ..] + | [sym::stable, ..] + | [sym::rustc_allowed_through_unstable_modules, ..] + | [sym::rustc_promotable, ..] => self.check_stability_promotable(attr, target), + [sym::link_ordinal, ..] => self.check_link_ordinal(attr, span, target), + [sym::cold, ..] => self.check_cold(hir_id, attr, span, target), + [sym::link, ..] => self.check_link(hir_id, attr, span, target), + [sym::link_name, ..] => self.check_link_name(hir_id, attr, span, target), + [sym::link_section, ..] => self.check_link_section(hir_id, attr, span, target), + [sym::no_mangle, ..] => self.check_no_mangle(hir_id, attr, span, target), + [sym::deprecated, ..] => self.check_deprecated(hir_id, attr, span, target), + [sym::macro_use, ..] | [sym::macro_escape, ..] => { + self.check_macro_use(hir_id, attr, target) + } + [sym::path, ..] => self.check_generic_attr(hir_id, attr, target, Target::Mod), + [sym::macro_export, ..] => self.check_macro_export(hir_id, attr, target), + [sym::ignore, ..] | [sym::should_panic, ..] => { + self.check_generic_attr(hir_id, attr, target, Target::Fn) + } + [sym::automatically_derived, ..] => { + self.check_generic_attr(hir_id, attr, target, Target::Impl) + } + [sym::no_implicit_prelude, ..] => { + self.check_generic_attr(hir_id, attr, target, Target::Mod) + } + [sym::rustc_object_lifetime_default, ..] => self.check_object_lifetime_default(hir_id), + [sym::proc_macro, ..] => { + self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) + } + [sym::proc_macro_attribute, ..] => { + self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); + } + [sym::proc_macro_derive, ..] => { + self.check_generic_attr(hir_id, attr, target, Target::Fn); + self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) + } + [sym::autodiff, ..] => { + self.check_autodiff(hir_id, attr, span, target) + } + [sym::coroutine, ..] => { + self.check_coroutine(attr, target); + } + [sym::linkage, ..] => self.check_linkage(attr, span, target), + [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent( attr.span(), span, attrs), + [ + // ok + sym::allow + | sym::expect + | sym::warn + | sym::deny + | sym::forbid + | sym::cfg + | sym::cfg_attr + // need to be fixed + | sym::cfi_encoding // FIXME(cfi_encoding) + | sym::pointee // FIXME(derive_coerce_pointee) + | sym::omit_gdb_pretty_printer_section // FIXME(omit_gdb_pretty_printer_section) + | sym::used // handled elsewhere to restrict to static items + | sym::repr // handled elsewhere to restrict to type decls items + | sym::instruction_set // broken on stable!!! + | sym::windows_subsystem // broken on stable!!! + | sym::patchable_function_entry // FIXME(patchable_function_entry) + | sym::deprecated_safe // FIXME(deprecated_safe) + // internal + | sym::prelude_import + | sym::panic_handler + | sym::allow_internal_unsafe + | sym::fundamental + | sym::lang + | sym::needs_allocator + | sym::default_lib_allocator + | sym::start + | sym::custom_mir, + .. + ] => {} + [name, ..] => { + match BUILTIN_ATTRIBUTE_MAP.get(name) { + // checked below + Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) => {} + Some(_) => { + // FIXME: differentiate between unstable and internal attributes just + // like we do with features instead of just accepting `rustc_` + // attributes by name. That should allow trimming the above list, too. + if !name.as_str().starts_with("rustc_") { + span_bug!( + attr.span(), + "builtin attribute {name:?} not handled by `CheckAttrVisitor`" + ) + } + } + None => (), } } - None => (), + [] => unreachable!(), } } - [] => unreachable!(), } let builtin = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)); @@ -308,17 +317,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if let Some(BuiltinAttribute { type_: AttributeType::CrateLevel, .. }) = attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)) { - match attr.style { + match attr.style() { ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::OuterCrateLevelAttr, ), ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::InnerCrateLevelAttr, ), } @@ -340,14 +349,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::IgnoredAttrWithMacro { sym }, ); } fn inline_attr_str_error_without_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) { - self.tcx - .emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::IgnoredAttr { sym }); + self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span(), errors::IgnoredAttr { + sym, + }); } /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl. @@ -384,7 +394,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::IgnoredInlineAttrFnProto, ) } @@ -395,7 +405,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::AssocConst => self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::IgnoredInlineAttrConstants, ), // FIXME(#80564): Same for fields, arms, and macro defs @@ -404,7 +414,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::InlineNotFnOrClosure { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -423,7 +433,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.dcx().emit_err(errors::CoverageNotFnOrClosure { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -441,7 +451,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); if !is_valid { self.dcx().emit_err(errors::OptimizeInvalidTarget { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -492,7 +502,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::OnlyHasEffectOn { attr_name: attr.name_or_empty(), target_name: allowed_target.name().replace(' ', "_"), @@ -561,8 +571,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if !ALLOW_LIST.iter().any(|name| other_attr.has_name(*name)) { self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { - span: other_attr.span, - naked_span: attr.span, + span: other_attr.span(), + naked_span: attr.span(), attr: other_attr.name_or_empty(), }); @@ -579,7 +589,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -612,9 +622,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::MacroDef => {} _ => { - self.tcx - .dcx() - .emit_err(errors::CollapseDebuginfo { attr_span: attr.span, defn_span: span }); + self.tcx.dcx().emit_err(errors::CollapseDebuginfo { + attr_span: attr.span(), + defn_span: span, + }); } } } @@ -678,7 +689,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::NonExhaustiveWrongLocation { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -698,7 +709,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -726,7 +737,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap(); self.dcx().emit_err(errors::LangItemWithTargetFeature { - attr_span: attr.span, + attr_span: attr.span(), name: lang_item, sig_span: sig.span, }); @@ -739,7 +750,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::TargetFeatureOnStatement, ); } @@ -752,7 +763,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -766,7 +777,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::ForeignStatic | Target::Static => {} _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToStatic { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -1029,7 +1040,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocInlineOnlyUse { attr_span: meta.span(), - item_span: (attr.style == AttrStyle::Outer) + item_span: (attr.style() == AttrStyle::Outer) .then(|| self.tcx.hir().span(hir_id)), }, ); @@ -1051,7 +1062,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocMaskedOnlyExternCrate { attr_span: meta.span(), - item_span: (attr.style == AttrStyle::Outer) + item_span: (attr.style() == AttrStyle::Outer) .then(|| self.tcx.hir().span(hir_id)), }, ); @@ -1065,7 +1076,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { meta.span(), errors::DocMaskedNotExternCrateSelf { attr_span: meta.span(), - item_span: (attr.style == AttrStyle::Outer) + item_span: (attr.style() == AttrStyle::Outer) .then(|| self.tcx.hir().span(hir_id)), }, ); @@ -1095,11 +1106,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ) -> bool { if hir_id != CRATE_HIR_ID { // insert a bang between `#` and `[...` - let bang_span = attr.span.lo() + BytePos(1); - let sugg = (attr.style == AttrStyle::Outer + let bang_span = attr.span().lo() + BytePos(1); + let sugg = (attr.style() == AttrStyle::Outer && self.tcx.hir().get_parent_item(hir_id) == CRATE_OWNER_ID) .then_some(errors::AttrCrateLevelOnlySugg { - attr: attr.span.with_lo(bang_span).with_hi(bang_span), + attr: attr.span().with_lo(bang_span).with_hi(bang_span), }); self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, @@ -1176,10 +1187,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { specified_inline: &mut Option<(bool, Span)>, aliases: &mut FxHashMap, ) { - if let Some(mi) = attr.meta() - && let Some(list) = mi.meta_item_list() - { - for meta in list { + if let Some(list) = attr.meta_item_list() { + for meta in &list { if let Some(i_meta) = meta.meta_item() { match i_meta.name_or_empty() { sym::alias => { @@ -1275,11 +1284,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> { errors::DocTestUnknownInclude { path, value: value.to_string(), - inner: match attr.style { + inner: match attr.style() { AttrStyle::Inner => "!", AttrStyle::Outer => "", }, - sugg: (attr.meta().unwrap().span, applicability), + sugg: (attr.span(), applicability), }, ); } else if i_meta.has_name(sym::passes) @@ -1325,7 +1334,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct | Target::Enum | Target::TyAlias => {} _ => { - self.dcx().emit_err(errors::PassByValue { attr_span: attr.span, span }); + self.dcx().emit_err(errors::PassByValue { attr_span: attr.span(), span }); } } } @@ -1334,7 +1343,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Method(MethodKind::Inherent) => {} _ => { - self.dcx().emit_err(errors::AllowIncoherentImpl { attr_span: attr.span, span }); + self.dcx().emit_err(errors::AllowIncoherentImpl { attr_span: attr.span(), span }); } } } @@ -1345,7 +1354,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.tcx .dcx() - .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span, span }); + .emit_err(errors::HasIncoherentInherentImpl { attr_span: attr.span(), span }); } } } @@ -1396,7 +1405,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::MustUseNoEffect { article, target }, ); } @@ -1407,7 +1416,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct | Target::Enum | Target::Union | Target::Trait => {} _ => { - self.dcx().emit_err(errors::MustNotSuspend { attr_span: attr.span, span }); + self.dcx().emit_err(errors::MustNotSuspend { attr_span: attr.span(), span }); } } } @@ -1430,7 +1439,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; } - self.dcx().emit_err(errors::InvalidMayDangle { attr_span: attr.span }); + self.dcx().emit_err(errors::InvalidMayDangle { attr_span: attr.span() }); } /// Checks if `#[cold]` is applied to a non-function. @@ -1447,10 +1456,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { // FIXME: #[cold] was previously allowed on non-functions and some crates used // this, so only emit a warning. - self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::Cold { - span, - on_crate: hir_id == CRATE_HIR_ID, - }); + self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span(), + errors::Cold { span, on_crate: hir_id == CRATE_HIR_ID }, + ); } } } @@ -1465,7 +1476,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; } - self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::Link { + self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span(), errors::Link { span: (target != Target::ForeignMod).then_some(span), }); } @@ -1484,19 +1495,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { // FIXME: #[cold] was previously allowed on non-functions/statics and some crates // used this, so only emit a warning. - let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span); + let attr_span = matches!(target, Target::ForeignMod).then_some(attr.span()); if let Some(s) = attr.value_str() { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::LinkName { span, attr_span, value: s.as_str() }, ); } else { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::LinkName { span, attr_span, value: "..." }, ); }; @@ -1516,7 +1527,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.inline_attr_str_error_with_macro_def(hir_id, attr, "no_link"); } _ => { - self.dcx().emit_err(errors::NoLink { attr_span: attr.span, span }); + self.dcx().emit_err(errors::NoLink { attr_span: attr.span(), span }); } } } @@ -1538,7 +1549,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.inline_attr_str_error_with_macro_def(hir_id, attr, "export_name"); } _ => { - self.dcx().emit_err(errors::ExportName { attr_span: attr.span, span }); + self.dcx().emit_err(errors::ExportName { attr_span: attr.span(), span }); } } } @@ -1546,7 +1557,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_rustc_layout_scalar_valid_range(&self, attr: &Attribute, span: Span, target: Target) { if target != Target::Struct { self.dcx().emit_err(errors::RustcLayoutScalarValidRangeNotStruct { - attr_span: attr.span, + attr_span: attr.span(), span, }); return; @@ -1559,7 +1570,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if !matches!(&list[..], &[MetaItemInner::Lit(MetaItemLit { kind: LitKind::Int(..), .. })]) { self.tcx .dcx() - .emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span }); + .emit_err(errors::RustcLayoutScalarValidRangeArg { attr_span: attr.span() }); } } @@ -1575,7 +1586,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let is_function = matches!(target, Target::Fn); if !is_function { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -1599,7 +1610,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hir::GenericParamKind::Const { .. } => {} _ => { self.dcx().emit_err(errors::RustcLegacyConstGenericsOnly { - attr_span: attr.span, + attr_span: attr.span(), param_span: param.span, }); return; @@ -1609,7 +1620,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if list.len() != generics.params.len() { self.dcx().emit_err(errors::RustcLegacyConstGenericsIndex { - attr_span: attr.span, + attr_span: attr.span(), generics_span: generics.span, }); return; @@ -1649,7 +1660,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { let is_function = matches!(target, Target::Fn | Target::Method(..)); if !is_function { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); @@ -1661,7 +1672,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct => {} _ => { - self.dcx().emit_err(errors::RustcLintOptTy { attr_span: attr.span, span }); + self.dcx().emit_err(errors::RustcLintOptTy { attr_span: attr.span(), span }); } } } @@ -1673,7 +1684,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.tcx .dcx() - .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span }); + .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span(), span }); } } } @@ -1682,7 +1693,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// option is passed to the compiler. fn check_rustc_dirty_clean(&self, attr: &Attribute) { if !self.tcx.sess.opts.unstable_opts.query_dep_graph { - self.dcx().emit_err(errors::RustcDirtyClean { span: attr.span }); + self.dcx().emit_err(errors::RustcDirtyClean { span: attr.span() }); } } @@ -1692,7 +1703,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Target::Trait => {} _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToTrait { - attr_span: attr.span, + attr_span: attr.span(), defn_span: span, }); } @@ -1716,7 +1727,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::LinkSection { span }, ); } @@ -1747,8 +1758,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, - errors::NoMangleForeign { span, attr_span: attr.span, foreign_item_kind }, + attr.span(), + errors::NoMangleForeign { span, attr_span: attr.span(), foreign_item_kind }, ); } _ => { @@ -1757,7 +1768,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::NoMangle { span }, ); } @@ -1994,7 +2005,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) { if target != Target::Static { self.dcx().emit_err(errors::UsedStatic { - attr_span: attr.span, + attr_span: attr.span(), span: target_span, target: target.name(), }); @@ -2003,12 +2014,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match inner.as_deref() { Some([item]) if item.has_name(sym::linker) => { if used_linker_span.is_none() { - used_linker_span = Some(attr.span); + used_linker_span = Some(attr.span()); } } Some([item]) if item.has_name(sym::compiler) => { if used_compiler_span.is_none() { - used_compiler_span = Some(attr.span); + used_compiler_span = Some(attr.span()); } } Some(_) => { @@ -2017,7 +2028,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { None => { // Default case (compiler) when arg isn't defined. if used_compiler_span.is_none() { - used_compiler_span = Some(attr.span); + used_compiler_span = Some(attr.span()); } } } @@ -2063,7 +2074,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.tcx .dcx() - .emit_err(errors::AllowInternalUnstable { attr_span: attr.span, span }); + .emit_err(errors::AllowInternalUnstable { attr_span: attr.span(), span }); } } } @@ -2077,7 +2088,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Mod => {} _ => { - self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span }); + self.dcx().emit_err(errors::DebugVisualizerPlacement { span: attr.span() }); } } } @@ -2104,7 +2115,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.tcx .dcx() - .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span, span }); + .emit_err(errors::RustcAllowConstFnUnstable { attr_span: attr.span(), span }); } } } @@ -2115,7 +2126,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { _ => { self.tcx .dcx() - .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span, span }); + .emit_err(errors::RustcStdInternalSymbol { attr_span: attr.span(), span }); } } } @@ -2123,7 +2134,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_stability_promotable(&self, attr: &Attribute, target: Target) { match target { Target::Expression => { - self.dcx().emit_err(errors::StabilityPromotable { attr_span: attr.span }); + self.dcx().emit_err(errors::StabilityPromotable { attr_span: attr.span() }); } _ => {} } @@ -2133,44 +2144,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::ForeignFn | Target::ForeignStatic => {} _ => { - self.dcx().emit_err(errors::LinkOrdinal { attr_span: attr.span }); + self.dcx().emit_err(errors::LinkOrdinal { attr_span: attr.span() }); } } } - fn check_confusables(&self, attr: &Attribute, target: Target) { - match target { - Target::Method(MethodKind::Inherent) => { - let Some(meta) = attr.meta() else { - return; - }; - let ast::MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { - return; - }; - - let mut candidates = Vec::new(); - - for meta in metas { - let MetaItemInner::Lit(meta_lit) = meta else { - self.dcx().emit_err(errors::IncorrectMetaItem { - span: meta.span(), - suggestion: errors::IncorrectMetaItemSuggestion { - lo: meta.span().shrink_to_lo(), - hi: meta.span().shrink_to_hi(), - }, - }); - return; - }; - candidates.push(meta_lit.symbol); - } - - if candidates.is_empty() { - self.dcx().emit_err(errors::EmptyConfusables { span: attr.span }); - } - } - _ => { - self.dcx().emit_err(errors::Confusables { attr_span: attr.span }); - } + fn check_confusables(&self, span: Span, target: Target) { + if !matches!(target, Target::Method(MethodKind::Inherent)) { + self.dcx().emit_err(errors::Confusables { attr_span: span }); } } @@ -2180,7 +2161,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::Deprecated, ); } @@ -2196,7 +2177,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::MacroUse { name }, ); } @@ -2208,7 +2189,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::MacroExport::Normal, ); } else if let Some(meta_item_list) = attr.meta_item_list() @@ -2218,7 +2199,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( INVALID_MACRO_EXPORT_ARGUMENTS, hir_id, - attr.span, + attr.span(), errors::MacroExport::TooManyItems, ); } else if meta_item_list[0].name_or_empty() != sym::local_inner_macros { @@ -2238,7 +2219,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span, + attr.span(), errors::MacroExport::OnDeclMacro, ); } @@ -2277,8 +2258,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; }; - self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span, errors::Unused { - attr_span: attr.span, + self.tcx.emit_node_span_lint(UNUSED_ATTRIBUTES, hir_id, attr.span(), errors::Unused { + attr_span: attr.span(), note, }); } @@ -2394,7 +2375,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Closure => return, _ => { - self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr.span }); + self.dcx().emit_err(errors::CoroutineOnNonClosure { span: attr.span() }); } } } @@ -2407,18 +2388,14 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | Target::ForeignStatic | Target::ForeignFn => {} _ => { - self.dcx().emit_err(errors::Linkage { attr_span: attr.span, span }); + self.dcx().emit_err(errors::Linkage { attr_span: attr.span(), span }); } } } fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) { - if !attrs - .iter() - .filter(|attr| attr.has_name(sym::repr)) - .filter_map(|attr| attr.meta_item_list()) - .flatten() - .any(|nmi| nmi.has_name(sym::transparent)) + if !attr::find_attr!(attrs, AttributeKind::Repr(r) => r.contains(&ReprTransparent)) + .unwrap_or(false) { self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span }); } @@ -2569,46 +2546,42 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { ]; for attr in attrs { - // This function should only be called with crate attributes - // which are inner attributes always but lets check to make sure - if attr.style == AttrStyle::Inner { - for attr_to_check in ATTRS_TO_CHECK { - if attr.has_name(*attr_to_check) { - let item = tcx - .hir() - .items() - .map(|id| tcx.hir().item(id)) - .find(|item| !item.span.is_dummy()) // Skip prelude `use`s - .map(|item| errors::ItemFollowingInnerAttr { - span: item.ident.span, - kind: item.kind.descr(), - }); - let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel { - span: attr.span, - sugg_span: tcx - .sess - .source_map() - .span_to_snippet(attr.span) - .ok() - .filter(|src| src.starts_with("#![")) - .map(|_| { - attr.span - .with_lo(attr.span.lo() + BytePos(1)) - .with_hi(attr.span.lo() + BytePos(2)) - }), - name: *attr_to_check, - item, + for attr_to_check in ATTRS_TO_CHECK { + if attr.has_name(*attr_to_check) { + let item = tcx + .hir() + .items() + .map(|id| tcx.hir().item(id)) + .find(|item| !item.span.is_dummy()) // Skip prelude `use`s + .map(|item| errors::ItemFollowingInnerAttr { + span: item.ident.span, + kind: item.kind.descr(), }); + let err = tcx.dcx().create_err(errors::InvalidAttrAtCrateLevel { + span: attr.span(), + sugg_span: tcx + .sess + .source_map() + .span_to_snippet(attr.span()) + .ok() + .filter(|src| src.starts_with("#![")) + .map(|_| { + attr.span() + .with_lo(attr.span().lo() + BytePos(1)) + .with_hi(attr.span().lo() + BytePos(2)) + }), + name: *attr_to_check, + item, + }); - if let AttrKind::Normal(ref p) = attr.kind { - tcx.dcx().try_steal_replace_and_emit_err( - p.item.path.span, - StashKey::UndeterminedMacroResolution, - err, - ); - } else { - err.emit(); - } + if let Attribute::Unparsed(ref p) = attr { + tcx.dcx().try_steal_replace_and_emit_err( + p.path.span, + StashKey::UndeterminedMacroResolution, + err, + ); + } else { + err.emit(); } } } @@ -2620,7 +2593,7 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) for attr in attrs { if attr.has_name(sym::inline) { - tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span }); + tcx.dcx().emit_err(errors::NonExportedMacroInvalidAttrs { attr_span: attr.span() }); } } } @@ -2658,10 +2631,10 @@ fn check_duplicates( match seen.entry(attr.name_or_empty()) { Entry::Occupied(mut entry) => { let (this, other) = if matches!(duplicates, FutureWarnPreceding) { - let to_remove = entry.insert(attr.span); - (to_remove, attr.span) + let to_remove = entry.insert(attr.span()); + (to_remove, attr.span()) } else { - (attr.span, *entry.get()) + (attr.span(), *entry.get()) }; tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, @@ -2678,17 +2651,17 @@ fn check_duplicates( ); } Entry::Vacant(entry) => { - entry.insert(attr.span); + entry.insert(attr.span()); } } } ErrorFollowing | ErrorPreceding => match seen.entry(attr.name_or_empty()) { Entry::Occupied(mut entry) => { let (this, other) = if matches!(duplicates, ErrorPreceding) { - let to_remove = entry.insert(attr.span); - (to_remove, attr.span) + let to_remove = entry.insert(attr.span()); + (to_remove, attr.span()) } else { - (attr.span, *entry.get()) + (attr.span(), *entry.get()) }; tcx.dcx().emit_err(errors::UnusedMultiple { this, @@ -2697,7 +2670,7 @@ fn check_duplicates( }); } Entry::Vacant(entry) => { - entry.insert(attr.span); + entry.insert(attr.span()); } }, } diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs new file mode 100644 index 0000000000000..b035ea393838f --- /dev/null +++ b/compiler/rustc_passes/src/check_const.rs @@ -0,0 +1,238 @@ +//! This pass checks HIR bodies that may be evaluated at compile-time (e.g., `const`, `static`, +//! `const fn`) for structured control flow (e.g. `if`, `while`), which is forbidden in a const +//! context. +//! +//! By the time the MIR const-checker runs, these high-level constructs have been lowered to +//! control-flow primitives (e.g., `Goto`, `SwitchInt`), making it tough to properly attribute +//! errors. We still look for those primitives in the MIR const-checker to ensure nothing slips +//! through, but errors for structured control flow in a `const` should be emitted here. + +use rustc_hir::AttributeKind; +use rustc_hir::def_id::{LocalDefId, LocalModDefId}; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_middle::hir::nested_filter; +use rustc_middle::query::Providers; +use rustc_middle::span_bug; +use rustc_middle::ty::TyCtxt; +use rustc_session::parse::feature_err; +use rustc_span::{Span, Symbol, sym}; +use {rustc_attr as attr, rustc_hir as hir}; + +use crate::errors::SkippingConstChecks; + +/// An expression that is not *always* legal in a const context. +#[derive(Clone, Copy)] +enum NonConstExpr { + Loop(hir::LoopSource), + Match(hir::MatchSource), +} + +impl NonConstExpr { + fn name(self) -> String { + match self { + Self::Loop(src) => format!("`{}`", src.name()), + Self::Match(src) => format!("`{}`", src.name()), + } + } + + fn required_feature_gates(self) -> Option<&'static [Symbol]> { + use hir::LoopSource::*; + use hir::MatchSource::*; + + let gates: &[_] = match self { + Self::Match(AwaitDesugar) => { + return None; + } + + Self::Loop(ForLoop) | Self::Match(ForLoopDesugar) => &[sym::const_for], + + Self::Match(TryDesugar(_)) => &[sym::const_try], + + // All other expressions are allowed. + Self::Loop(Loop | While) | Self::Match(Normal | Postfix | FormatArgs) => &[], + }; + + Some(gates) + } +} + +fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { + let mut vis = CheckConstVisitor::new(tcx); + tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis); +} + +pub(crate) fn provide(providers: &mut Providers) { + *providers = Providers { check_mod_const_bodies, ..*providers }; +} + +#[derive(Copy, Clone)] +struct CheckConstVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + const_kind: Option, + def_id: Option, +} + +impl<'tcx> CheckConstVisitor<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> Self { + CheckConstVisitor { tcx, const_kind: None, def_id: None } + } + + /// Emits an error when an unsupported expression is found in a const context. + #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable + fn const_check_violated(&self, expr: NonConstExpr, span: Span) { + let Self { tcx, def_id, const_kind } = *self; + + let features = tcx.features(); + let required_gates = expr.required_feature_gates(); + + let is_feature_allowed = |feature_gate| { + // All features require that the corresponding gate be enabled, + // even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`. + if !tcx.features().enabled(feature_gate) { + return false; + } + + // If `def_id` is `None`, we don't need to consider stability attributes. + let def_id = match def_id { + Some(x) => x, + None => return true, + }; + + // If the function belongs to a trait, then it must enable the const_trait_impl + // feature to use that trait function (with a const default body). + if tcx.trait_of_item(def_id.to_def_id()).is_some() { + return true; + } + + // If this crate is not using stability attributes, or this function is not claiming to be a + // stable `const fn`, that is all that is required. + if !tcx.features().staged_api() || tcx.has_attr(def_id, sym::rustc_const_unstable) { + return true; + } + + // However, we cannot allow stable `const fn`s to use unstable features without an explicit + // opt-in via `rustc_allow_const_fn_unstable`. + let attrs = tcx.hir().attrs(tcx.local_def_id_to_hir_id(def_id)); + + attr::find_attr!(attrs, AttributeKind::AllowConstFnUnstable(names) => names.contains(&feature_gate)).unwrap_or(false) + }; + + match required_gates { + // Don't emit an error if the user has enabled the requisite feature gates. + Some(gates) if gates.iter().copied().all(is_feature_allowed) => return, + + // `-Zunleash-the-miri-inside-of-you` only works for expressions that don't have a + // corresponding feature gate. This encourages nightly users to use feature gates when + // possible. + None if tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you => { + tcx.dcx().emit_warn(SkippingConstChecks { span }); + return; + } + + _ => {} + } + + let const_kind = + const_kind.expect("`const_check_violated` may only be called inside a const context"); + + let required_gates = required_gates.unwrap_or(&[]); + let missing_gates: Vec<_> = + required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect(); + + match missing_gates.as_slice() { + [] => { + span_bug!( + span, + "we should not have reached this point, since `.await` is denied earlier" + ); + } + + [missing_primary, ref missing_secondary @ ..] => { + let msg = + format!("{} is not allowed in a `{}`", expr.name(), const_kind.keyword_name()); + let mut err = feature_err(&tcx.sess, *missing_primary, span, msg); + + // If multiple feature gates would be required to enable this expression, include + // them as help messages. Don't emit a separate error for each missing feature gate. + // + // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This + // is a pretty narrow case, however. + tcx.disabled_nightly_features( + &mut err, + def_id.map(|id| tcx.local_def_id_to_hir_id(id)), + missing_secondary.into_iter().map(|gate| (String::new(), *gate)), + ); + + err.emit(); + } + } + } + + /// Saves the parent `const_kind` before calling `f` and restores it afterwards. + fn recurse_into( + &mut self, + kind: Option, + def_id: Option, + f: impl FnOnce(&mut Self), + ) { + let parent_def_id = self.def_id; + let parent_kind = self.const_kind; + self.def_id = def_id; + self.const_kind = kind; + f(self); + self.def_id = parent_def_id; + self.const_kind = parent_kind; + } +} + +impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_anon_const(&mut self, anon: &'tcx hir::AnonConst) { + let kind = Some(hir::ConstContext::Const { inline: false }); + self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon)); + } + + fn visit_inline_const(&mut self, block: &'tcx hir::ConstBlock) { + let kind = Some(hir::ConstContext::Const { inline: true }); + self.recurse_into(kind, None, |this| intravisit::walk_inline_const(this, block)); + } + + fn visit_body(&mut self, body: &hir::Body<'tcx>) { + let owner = self.tcx.hir().body_owner_def_id(body.id()); + let kind = self.tcx.hir().body_const_context(owner); + self.recurse_into(kind, Some(owner), |this| intravisit::walk_body(this, body)); + } + + fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { + match &e.kind { + // Skip the following checks if we are not currently in a const context. + _ if self.const_kind.is_none() => {} + + hir::ExprKind::Loop(_, _, source, _) => { + self.const_check_violated(NonConstExpr::Loop(*source), e.span); + } + + hir::ExprKind::Match(_, _, source) => { + let non_const_expr = match source { + // These are handled by `ExprKind::Loop` above. + hir::MatchSource::ForLoopDesugar => None, + + _ => Some(NonConstExpr::Match(*source)), + }; + + if let Some(expr) = non_const_expr { + self.const_check_violated(expr, e.span); + } + } + + _ => {} + } + + intravisit::walk_expr(self, e); + } +} diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 1c3d3bf3dea0a..071e537233b0f 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -9,9 +9,8 @@ //! //! * Compiler internal types like `Ty` and `TyCtxt` -use rustc_ast as ast; -use rustc_hir::OwnerId; use rustc_hir::diagnostic_items::DiagnosticItems; +use rustc_hir::{Attribute, OwnerId}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::{DefId, LOCAL_CRATE}; @@ -55,7 +54,7 @@ fn report_duplicate_item( } /// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes. -fn extract(attrs: &[ast::Attribute]) -> Option { +fn extract(attrs: &[Attribute]) -> Option { attrs.iter().find_map(|attr| { if attr.has_name(sym::rustc_diagnostic_item) { attr.value_str() } else { None } }) diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 690808e75271d..bd19ef5795cdc 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -53,7 +53,7 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Option { let attrs = ctxt.tcx.hir().attrs(id.hir_id()); - attr::find_by_name(attrs, sym).map(|attr| attr.span) + attr::find_by_name(attrs, sym).map(|attr| attr.span()) } fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index fdc7e1bba2f0e..93fb00ce73c8d 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -696,31 +696,6 @@ pub(crate) struct Linkage { pub span: Span, } -#[derive(Diagnostic)] -#[diag(passes_empty_confusables)] -pub(crate) struct EmptyConfusables { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(passes_incorrect_meta_item, code = E0539)] -pub(crate) struct IncorrectMetaItem { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub suggestion: IncorrectMetaItemSuggestion, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion(passes_incorrect_meta_item_suggestion, applicability = "maybe-incorrect")] -pub(crate) struct IncorrectMetaItemSuggestion { - #[suggestion_part(code = "\"")] - pub lo: Span, - #[suggestion_part(code = "\"")] - pub hi: Span, -} - #[derive(Diagnostic)] #[diag(passes_stability_promotable)] pub(crate) struct StabilityPromotable { @@ -1850,3 +1825,11 @@ pub(crate) struct NoSanitize<'a> { pub accepted_kind: &'a str, pub attr_str: &'a str, } + +// TODO: move back to rustc_attr +#[derive(Diagnostic)] +#[diag(passes_rustc_const_stable_indirect_pairing)] +pub(crate) struct RustcConstStableIndirectPairing { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index f2a37307ceeb8..97e30f87cfadd 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -468,7 +468,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_assoc_item_constraint(self, constraint) } - fn visit_attribute(&mut self, attr: &'v ast::Attribute) { + fn visit_attribute(&mut self, attr: &'v hir::Attribute) { self.record("Attribute", None, attr); } diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index bb90b5a1e31c2..dbdc383504ca2 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -1,5 +1,5 @@ use rustc_abi::{HasDataLayout, TargetDataLayout}; -use rustc_ast::Attribute; +use rustc_hir::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::span_bug; diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs index 8a360c017adbf..9ad9dcda45903 100644 --- a/compiler/rustc_passes/src/lib_features.rs +++ b/compiler/rustc_passes/src/lib_features.rs @@ -4,12 +4,12 @@ //! but are not declared in one single location (unlike lang features), which means we need to //! collect them instead. -use rustc_ast::Attribute; -use rustc_attr::VERSION_PLACEHOLDER; +use rustc_attr_parsing::{AttributeKind, StabilityLevel, StableSince}; +use rustc_hir::Attribute; use rustc_hir::intravisit::Visitor; -use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::query::{LocalCrate, Providers}; +use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; @@ -27,62 +27,29 @@ impl<'tcx> LibFeatureCollector<'tcx> { } fn extract(&self, attr: &Attribute) -> Option<(Symbol, FeatureStability, Span)> { - let stab_attrs = [ - sym::stable, - sym::unstable, - sym::rustc_const_stable, - sym::rustc_const_unstable, - sym::rustc_default_body_unstable, - ]; - - // Find a stability attribute: one of #[stable(…)], #[unstable(…)], - // #[rustc_const_stable(…)], #[rustc_const_unstable(…)] or #[rustc_default_body_unstable]. - if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) { - if let Some(metas) = attr.meta_item_list() { - let mut feature = None; - let mut since = None; - for meta in metas { - if let Some(mi) = meta.meta_item() { - // Find the `feature = ".."` meta-item. - match (mi.name_or_empty(), mi.value_str()) { - (sym::feature, val) => feature = val, - (sym::since, val) => since = val, - _ => {} - } - } - } - - if let Some(s) = since - && s.as_str() == VERSION_PLACEHOLDER - { - since = Some(sym::env_CFG_RELEASE); - } - - if let Some(feature) = feature { - // This additional check for stability is to make sure we - // don't emit additional, irrelevant errors for malformed - // attributes. - let is_unstable = matches!( - *stab_attr, - sym::unstable - | sym::rustc_const_unstable - | sym::rustc_default_body_unstable - ); - if is_unstable { - return Some((feature, FeatureStability::Unstable, attr.span)); - } - if let Some(since) = since { - return Some((feature, FeatureStability::AcceptedSince(since), attr.span)); - } - } - // We need to iterate over the other attributes, because - // `rustc_const_unstable` is not mutually exclusive with - // the other stability attributes, so we can't just `break` - // here. + let (feature, level, span) = match attr { + Attribute::Parsed(AttributeKind::Stability { stability, span }) => { + (stability.feature, stability.level, *span) } - } - - None + Attribute::Parsed(AttributeKind::ConstStability { stability, span }) => { + (stability.feature, stability.level, *span) + } + Attribute::Parsed(AttributeKind::BodyStability { stability, span }) => { + (stability.feature, stability.level, *span) + } + _ => return None, + }; + + let feature_stability = match level { + StabilityLevel::Unstable { .. } => FeatureStability::Unstable, + StabilityLevel::Stable { since, .. } => FeatureStability::AcceptedSince(match since { + StableSince::Version(v) => Symbol::intern(&v.to_string()), + StableSince::Current => sym::env_CFG_RELEASE, + StableSince::Err => return None, + }), + }; + + Some((feature, feature_stability, span)) } fn collect_feature(&mut self, feature: Symbol, stability: FeatureStability, span: Span) { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 2809ad453ff4a..e47a944936f74 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -4,9 +4,9 @@ use std::mem::replace; use std::num::NonZero; -use rustc_attr::{ +use rustc_attr_parsing::{ self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince, - Unstable, UnstableReason, VERSION_PLACEHOLDER, + UnstableReason, VERSION_PLACEHOLDER, AttributeKind, PartialConstStability, find_attr }; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; @@ -117,7 +117,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { let attrs = self.tcx.hir().attrs(self.tcx.local_def_id_to_hir_id(def_id)); debug!("annotate(id = {:?}, attrs = {:?})", def_id, attrs); - let depr = attr::find_deprecation(self.tcx.sess, self.tcx.features(), attrs); + let depr = attr::find_attr!(attrs, AttributeKind::Deprecation{deprecation, span} => (*deprecation, *span)); + let const_stability_indirect = find_attr!(attrs, AttributeKind::ConstStabilityIndirect); + let mut is_deprecated = false; if let Some((depr, span)) = &depr { is_deprecated = true; @@ -150,9 +152,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { if inherit_deprecation.yes() && stab.is_unstable() { self.index.stab_map.insert(def_id, stab); if fn_sig.is_some_and(|s| s.header.is_const()) { - let const_stab = - attr::unmarked_crate_const_stab(self.tcx.sess, attrs, stab); - self.index.const_stab_map.insert(def_id, const_stab); + self.index.const_stab_map.insert( + def_id, + ConstStability::unmarked(const_stability_indirect, stab), + ); } } } @@ -167,9 +170,9 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } // # Regular and body stability - - let stab = attr::find_stability(self.tcx.sess, attrs, item_sp); - let body_stab = attr::find_body_stability(self.tcx.sess, attrs); + let stab = attr::find_attr!(attrs, AttributeKind::Stability { stability, span } => (*stability, *span)); + let body_stab = + attr::find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability); if let Some((depr, span)) = &depr && depr.is_since_rustc_version() @@ -178,8 +181,8 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { self.tcx.dcx().emit_err(errors::DeprecatedAttribute { span: *span }); } - if let Some((body_stab, _span)) = body_stab { - // FIXME: check that this item can have body stability + if let Some(body_stab) = body_stab { + // TODO: check that this item can have body stability self.index.default_body_stab_map.insert(def_id, body_stab); debug!(?self.index.default_body_stab_map); @@ -199,7 +202,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // this is *almost surely* an accident. if let ( &Some(DeprecatedSince::RustcVersion(dep_since)), - &attr::Stable { since: stab_since, .. }, + &attr::StabilityLevel::Stable { since: stab_since, .. }, ) = (&depr.as_ref().map(|(d, _)| d.since), &stab.level) { match stab_since { @@ -224,15 +227,17 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Stable *language* features shouldn't be used as unstable library features. // (Not doing this for stable library features is checked by tidy.) - if let Stability { level: Unstable { .. }, feature } = stab { + if let Stability { level: StabilityLevel::Unstable { .. }, feature } = stab { if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() { self.tcx .dcx() .emit_err(errors::UnstableAttrForAlreadyStableFeature { span, item_sp }); } } - if let Stability { level: Unstable { implied_by: Some(implied_by), .. }, feature } = - stab + if let Stability { + level: StabilityLevel::Unstable { implied_by: Some(implied_by), .. }, + feature, + } = stab { self.index.implications.insert(implied_by, feature); } @@ -254,10 +259,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // # Const stability - let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item_sp); + let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability { stability, span } => (*stability, *span)); // If the current node is a function with const stability attributes (directly given or - // implied), check if the function/method is const. + // implied), check if the function/method is const or the parent impl block is const. if let Some(fn_sig) = fn_sig && !fn_sig.header.is_const() && const_stab.is_some() @@ -278,8 +283,10 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Stable *language* features shouldn't be used as unstable library features. // (Not doing this for stable library features is checked by tidy.) - if let Some((ConstStability { level: Unstable { .. }, feature, .. }, const_span)) = - const_stab + if let Some(( + PartialConstStability { level: StabilityLevel::Stable { .. }, feature, .. }, + const_span, + )) = const_stab { if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() { self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature { @@ -289,9 +296,17 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } + if let Some((stab, span)) = &const_stab + && stab.is_const_stable() + && const_stability_indirect + { + self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span: *span }); + } + // After checking the immediate attributes, get rid of the span and compute implied // const stability: inherit feature gate from regular stability. - let mut const_stab = const_stab.map(|(stab, _span)| stab); + let mut const_stab = const_stab + .map(|(stab, _span)| ConstStability::from_partial(stab, const_stability_indirect)); // If this is a const fn but not annotated with stability markers, see if we can inherit regular stability. if fn_sig.is_some_and(|s| s.header.is_const()) && const_stab.is_none() && @@ -314,7 +329,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { }); if let Some(ConstStability { - level: Unstable { implied_by: Some(implied_by), .. }, + level: StabilityLevel::Unstable { implied_by: Some(implied_by), .. }, feature, .. }) = const_stab @@ -773,14 +788,22 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { let features = self.tcx.features(); if features.staged_api() { let attrs = self.tcx.hir().attrs(item.hir_id()); - let stab = attr::find_stability(self.tcx.sess, attrs, item.span); - let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item.span); + // TODO: check with https://github.com/rust-lang/rust/commit/e96808162ad7ff5906d7b58d32a25abe139e998c#diff-5f57ad10e1bdde3d046b258d390fd2ecc6f1511158aa130cebb72093da16ef29 + + let stab = attr::find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span)); + + // FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem + let const_stab = attr::find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability); // If this impl block has an #[unstable] attribute, give an // error if all involved types and traits are stable, because // it will have no effect. // See: https://github.com/rust-lang/rust/issues/55436 - if let Some((Stability { level: attr::Unstable { .. }, .. }, span)) = stab { + if let Some(( + Stability { level: attr::StabilityLevel::Unstable { .. }, .. }, + span, + )) = stab + { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; c.visit_ty(self_ty); c.visit_trait_ref(t); @@ -801,7 +824,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // needs to have an error emitted. if features.const_trait_impl() && self.tcx.is_const_trait_impl(item.owner_id.to_def_id()) - && const_stab.is_some_and(|(stab, _)| stab.is_const_stable()) + && const_stab.is_some_and(|stab| stab.is_const_stable()) { self.tcx.dcx().emit_err(errors::TraitImplConstStable { span: item.span }); } diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml index f998e0ff1547f..eb48155919fee 100644 --- a/compiler/rustc_privacy/Cargo.toml +++ b/compiler/rustc_privacy/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] # tidy-alphabetical-start rustc_ast = { path = "../rustc_ast" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 3057f13e3a75a..cfef3f7ff007c 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -24,6 +24,7 @@ use rustc_ast::MacroDef; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; +use rustc_attr_parsing::AttributeKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; @@ -41,7 +42,7 @@ use rustc_span::Span; use rustc_span::hygiene::Transparency; use rustc_span::symbol::{Ident, kw, sym}; use tracing::debug; -use {rustc_attr as attr, rustc_hir as hir}; +use {rustc_attr_parsing as attr, rustc_hir as hir}; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -492,7 +493,11 @@ impl<'tcx> EmbargoVisitor<'tcx> { // Non-opaque macros cannot make other items more accessible than they already are. let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.tcx.hir().attrs(hir_id); - if attr::find_transparency(attrs, md.macro_rules).0 != Transparency::Opaque { + + if attr::find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x) + .unwrap_or(Transparency::fallback(md.macro_rules)) + != Transparency::Opaque + { return; } diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 96b210accdb37..bd1146c4c54e6 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" parking_lot = "0.12" rustc-rayon-core = { version = "0.5.0" } rustc_ast = { path = "../rustc_ast" } +rustc_attr_data_structures = { path = "../rustc_attr_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index ad4b194eb50cd..fd43f47886844 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -129,3 +129,4 @@ impl<'a> rustc_span::HashStableContext for StableHashingContext<'a> { } impl<'a> rustc_session::HashStableContext for StableHashingContext<'a> {} +impl<'a> rustc_attr_data_structures::HashStableContext for StableHashingContext<'a> {} diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs index 5a72e80a0a5c0..383b8d13732ad 100644 --- a/compiler/rustc_query_system/src/ich/impls_syntax.rs +++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs @@ -1,18 +1,17 @@ //! This module contains `HashStable` implementations for various data types -//! from `rustc_ast` in no particular order. +//! from various crates in no particular order. -use std::assert_matches::assert_matches; - -use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir::{self as hir, HashIgnoredAttrId}; use rustc_span::SourceFile; use smallvec::SmallVec; use crate::ich::StableHashingContext; impl<'ctx> rustc_target::HashStableContext for StableHashingContext<'ctx> {} +impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> {} -impl<'a> HashStable> for [ast::Attribute] { +impl<'a> HashStable> for [hir::Attribute] { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { if self.is_empty() { self.len().hash_stable(hcx, hasher); @@ -20,10 +19,11 @@ impl<'a> HashStable> for [ast::Attribute] { } // Some attributes are always ignored during hashing. - let filtered: SmallVec<[&ast::Attribute; 8]> = self + let filtered: SmallVec<[&hir::Attribute; 8]> = self .iter() .filter(|attr| { !attr.is_doc_comment() + // FIXME(jdonszelmann) have a better way to handle ignored attrs && !attr.ident().is_some_and(|ident| hcx.is_ignored_attr(ident.name)) }) .collect(); @@ -35,30 +35,12 @@ impl<'a> HashStable> for [ast::Attribute] { } } -impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> { - fn hash_attr(&mut self, attr: &ast::Attribute, hasher: &mut StableHasher) { - // Make sure that these have been filtered out. - debug_assert!(!attr.ident().is_some_and(|ident| self.is_ignored_attr(ident.name))); - debug_assert!(!attr.is_doc_comment()); - - let ast::Attribute { kind, id: _, style, span } = attr; - if let ast::AttrKind::Normal(normal) = kind { - normal.item.hash_stable(self, hasher); - style.hash_stable(self, hasher); - span.hash_stable(self, hasher); - assert_matches!( - normal.tokens.as_ref(), - None, - "Tokens should have been removed during lowering!" - ); - } else { - unreachable!(); - } +impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> { + fn hash_attr_id(&mut self, _id: &HashIgnoredAttrId, _hasher: &mut StableHasher) { + /* we don't hash HashIgnoredAttrId, we ignore them */ } } -impl<'ctx> rustc_hir::HashStableContext for StableHashingContext<'ctx> {} - impl<'a> HashStable> for SourceFile { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let SourceFile { diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index b71853b871dc5..309227176d4e5 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -10,7 +10,7 @@ pulldown-cmark = { version = "0.11", features = ["html"], default-features = fal rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 293cee500bbd8..9be2f69207a67 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -12,7 +12,7 @@ use rustc_ast::{ self as ast, AssocItem, AssocItemKind, Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, MetaItemKind, NodeId, StmtKind, }; -use rustc_attr as attr; +use rustc_attr_parsing as attr; use rustc_data_structures::sync::Lrc; use rustc_expand::base::ResolverExpand; use rustc_expand::expand::AstFragment; @@ -634,7 +634,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } ast::UseTreeKind::Glob => { let kind = ImportKind::Glob { - is_prelude: attr::contains_name(&item.attrs, sym::prelude_import), + is_prelude: ast::attr::contains_name(&item.attrs, sym::prelude_import), max_vis: Cell::new(None), id, }; @@ -777,7 +777,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { expansion.to_expn_id(), item.span, parent.no_implicit_prelude - || attr::contains_name(&item.attrs, sym::no_implicit_prelude), + || ast::attr::contains_name(&item.attrs, sym::no_implicit_prelude), ); self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion)); @@ -831,7 +831,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // If the structure is marked as non_exhaustive then lower the visibility // to within the crate. let mut ctor_vis = if vis.is_public() - && attr::contains_name(&item.attrs, sym::non_exhaustive) + && ast::attr::contains_name(&item.attrs, sym::non_exhaustive) { ty::Visibility::Restricted(CRATE_DEF_ID) } else { @@ -1172,11 +1172,11 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { } fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> { - if attr::contains_name(&item.attrs, sym::proc_macro) { + if ast::attr::contains_name(&item.attrs, sym::proc_macro) { return Some((MacroKind::Bang, item.ident, item.span)); - } else if attr::contains_name(&item.attrs, sym::proc_macro_attribute) { + } else if ast::attr::contains_name(&item.attrs, sym::proc_macro_attribute) { return Some((MacroKind::Attr, item.ident, item.span)); - } else if let Some(attr) = attr::find_by_name(&item.attrs, sym::proc_macro_derive) { + } else if let Some(attr) = ast::attr::find_by_name(&item.attrs, sym::proc_macro_derive) { if let Some(meta_item_inner) = attr.meta_item_list().and_then(|list| list.get(0).cloned()) { @@ -1229,7 +1229,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { if macro_rules { let ident = ident.normalize_to_macros_2_0(); self.r.macro_names.insert(ident); - let is_macro_export = attr::contains_name(&item.attrs, sym::macro_export); + let is_macro_export = ast::attr::contains_name(&item.attrs, sym::macro_export); let vis = if is_macro_export { ty::Visibility::Public } else { @@ -1503,7 +1503,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // If the variant is marked as non_exhaustive then lower the visibility to within the crate. let ctor_vis = - if vis.is_public() && attr::contains_name(&variant.attrs, sym::non_exhaustive) { + if vis.is_public() && ast::attr::contains_name(&variant.attrs, sym::non_exhaustive) { ty::Visibility::Restricted(CRATE_DEF_ID) } else { vis diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 6649284258121..119d30913a1be 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -3,6 +3,7 @@ use std::mem; use rustc_ast::visit::FnKind; use rustc_ast::*; use rustc_ast_pretty::pprust; +use rustc_attr_parsing::{AttributeParseContext, OmitDoc}; use rustc_expand::expand::AstFragment; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind}; @@ -130,8 +131,19 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn, ItemKind::MacroDef(def) => { let edition = i.span.edition(); + + // FIXME(jdonszelmann) make one of these in the resolver? + // FIXME(jdonszelmann) don't care about tools here maybe? Just parse what we can. + // Does that prevents errors from happening? maybe + let parser = AttributeParseContext::new( + &self.resolver.tcx.sess, + self.resolver.tcx.features(), + Vec::new(), + ); + let attrs = parser.parse_attribute_list(&i.attrs, i.span, OmitDoc::Skip); + let macro_data = - self.resolver.compile_macro(def, i.ident, &i.attrs, i.span, i.id, edition); + self.resolver.compile_macro(def, i.ident, &attrs, i.span, i.id, edition); let macro_kind = macro_data.ext.macro_kind(); opt_macro_data = Some(macro_data); DefKind::Macro(macro_kind) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 4c76617a3918d..d0fc439786efa 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1814,7 +1814,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { && !def_id.is_local() && let Some(attr) = self.tcx.get_attr(def_id, sym::non_exhaustive) { - non_exhaustive = Some(attr.span); + non_exhaustive = Some(attr.span()); } else if let Some(span) = ctor_fields_span { let label = errors::ConstructorPrivateIfAnyFieldPrivate { span }; err.subdiagnostic(label); diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 0b4d0e04c295c..e7ff8ebd89542 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -7,7 +7,7 @@ use std::mem; use rustc_ast::expand::StrippedCfgItem; use rustc_ast::{self as ast, Crate, Inline, ItemKind, ModKind, NodeId, attr}; use rustc_ast_pretty::pprust; -use rustc_attr::StabilityLevel; +use rustc_attr_parsing::StabilityLevel; use rustc_data_structures::intern::Interned; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, StashKey}; @@ -1126,7 +1126,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &mut self, macro_def: &ast::MacroDef, ident: Ident, - attrs: &[ast::Attribute], + attrs: &[rustc_hir::Attribute], span: Span, node_id: NodeId, edition: Edition, diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 65128ceb86619..ed421da02413a 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -5,6 +5,7 @@ use pulldown_cmark::{ BrokenLink, BrokenLinkCallback, CowStr, Event, LinkType, Options, Parser, Tag, }; use rustc_ast as ast; +use rustc_ast::attr::AttributeExt; use rustc_ast::util::comments::beautify_doc_string; use rustc_data_structures::fx::FxIndexMap; use rustc_middle::ty::TyCtxt; @@ -192,19 +193,24 @@ pub fn add_doc_fragment(out: &mut String, frag: &DocFragment) { } } -pub fn attrs_to_doc_fragments<'a>( - attrs: impl Iterator)>, +pub fn attrs_to_doc_fragments<'a, A: AttributeExt + Clone + 'a>( + attrs: impl Iterator)>, doc_only: bool, -) -> (Vec, ast::AttrVec) { +) -> (Vec, Vec) { let mut doc_fragments = Vec::new(); - let mut other_attrs = ast::AttrVec::new(); + let mut other_attrs = Vec::::new(); for (attr, item_id) in attrs { if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() { let doc = beautify_doc_string(doc_str, comment_kind); let (span, kind) = if attr.is_doc_comment() { - (attr.span, DocFragmentKind::SugaredDoc) + (attr.span(), DocFragmentKind::SugaredDoc) } else { - (span_for_value(attr), DocFragmentKind::RawDoc) + ( + attr.value_span() + .map(|i| i.with_ctxt(attr.span().ctxt())) + .unwrap_or(attr.span()), + DocFragmentKind::RawDoc, + ) }; let fragment = DocFragment { span, doc, kind, item_id, indent: 0 }; doc_fragments.push(fragment); @@ -218,16 +224,6 @@ pub fn attrs_to_doc_fragments<'a>( (doc_fragments, other_attrs) } -fn span_for_value(attr: &ast::Attribute) -> Span { - if let ast::AttrKind::Normal(normal) = &attr.kind - && let ast::AttrArgs::Eq { value, .. } = &normal.item.args - { - value.span().with_ctxt(attr.span.ctxt()) - } else { - attr.span - } -} - /// Return the doc-comments on this item, grouped by the module they came from. /// The module can be different if this is a re-export with added documentation. /// @@ -353,12 +349,15 @@ pub fn strip_generics_from_path(path_str: &str) -> Result, MalformedGen /// //// If there are no doc-comments, return true. /// FIXME(#78591): Support both inner and outer attributes on the same item. -pub fn inner_docs(attrs: &[ast::Attribute]) -> bool { - attrs.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == ast::AttrStyle::Inner) +pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool { + attrs + .iter() + .find(|a| a.doc_str().is_some()) + .map_or(true, |a| a.style() == ast::AttrStyle::Inner) } /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]`. -pub fn has_primitive_or_keyword_docs(attrs: &[ast::Attribute]) -> bool { +pub fn has_primitive_or_keyword_docs(attrs: &[impl AttributeExt]) -> bool { for attr in attrs { if attr.has_name(sym::rustc_doc_primitive) { return true; @@ -408,7 +407,7 @@ pub fn may_be_doc_link(link_type: LinkType) -> bool { /// Simplified version of `preprocessed_markdown_links` from rustdoc. /// Must return at least the same links as it, but may add some more links on top of that. -pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec> { +pub(crate) fn attrs_to_preprocessed_links(attrs: &[A]) -> Vec> { let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap(); diff --git a/compiler/rustc_sanitizers/Cargo.toml b/compiler/rustc_sanitizers/Cargo.toml index 5623a493cf009..36d4c4731908b 100644 --- a/compiler/rustc_sanitizers/Cargo.toml +++ b/compiler/rustc_sanitizers/Cargo.toml @@ -8,6 +8,7 @@ bitflags = "2.5.0" tracing = "0.1" twox-hash = "1.6.3" rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index 2f4387e412d99..fd063a21f5299 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -469,7 +469,7 @@ pub(crate) fn encode_ty<'tcx>( )] tcx.dcx() .struct_span_err( - cfi_encoding.span, + cfi_encoding.span(), format!("invalid `cfi_encoding` for `{:?}`", ty.kind()), ) .emit(); @@ -520,7 +520,7 @@ pub(crate) fn encode_ty<'tcx>( )] tcx.dcx() .struct_span_err( - cfi_encoding.span, + cfi_encoding.span(), format!("invalid `cfi_encoding` for `{:?}`", ty.kind()), ) .emit(); diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 0b4470b2b0fb7..dcf86d1a4089f 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -29,9 +29,6 @@ pub mod output; pub use getopts; -mod version; -pub use version::RustcVersion; - rustc_fluent_macro::fluent_messages! { "../messages.ftl" } /// Requirements for a `StableHashingContext` to be used in this crate. diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml index 1230667ee910a..29ce24e8b7849 100644 --- a/compiler/rustc_smir/Cargo.toml +++ b/compiler/rustc_smir/Cargo.toml @@ -7,9 +7,9 @@ edition = "2021" # tidy-alphabetical-start rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } -rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } +rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 9025b47c422dd..180a231937b54 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -255,8 +255,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> { attr.iter().map(|seg| rustc_span::symbol::Symbol::intern(&seg)).collect(); tcx.get_attrs_by_path(did, &attr_name) .map(|attribute| { - let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute); - let span = attribute.span; + let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute); + let span = attribute.span(); stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) }) .collect() @@ -266,18 +266,16 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let mut tables = self.0.borrow_mut(); let tcx = tables.tcx; let did = tables[def_id]; - let filter_fn = move |a: &&rustc_ast::ast::Attribute| { - matches!(a.kind, rustc_ast::ast::AttrKind::Normal(_)) - }; + let filter_fn = move |a: &&rustc_hir::Attribute| !a.is_doc_comment(); let attrs_iter = if let Some(did) = did.as_local() { tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter().filter(filter_fn) } else { - tcx.item_attrs(did).iter().filter(filter_fn) + tcx.hir_attrs_for_def(did).iter().filter(filter_fn) }; attrs_iter .map(|attribute| { - let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute); - let span = attribute.span; + let attr_str = rustc_hir_pretty::attribute_to_string(&tcx, attribute); + let span = attribute.span(); stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables)) }) .collect() diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index a5826137181db..29fced207b71c 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -173,6 +173,12 @@ pub enum Transparency { Opaque, } +impl Transparency { + pub fn fallback(macro_rules: bool) -> Self { + if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque } + } +} + impl LocalExpnId { /// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST. pub const ROOT: LocalExpnId = LocalExpnId::ZERO; diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 1fb647cab5b57..76ea46b7e7034 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -9,6 +9,7 @@ punycode = "0.4.0" rustc-demangle = "0.1.21" rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 73bd8d7555b89..0d30815bbed68 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -62,18 +62,18 @@ impl SymbolNamesTest<'_> { ); let mangled = tcx.symbol_name(instance); tcx.dcx().emit_err(TestOutput { - span: attr.span, + span: attr.span(), kind: Kind::SymbolName, content: format!("{mangled}"), }); if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) { tcx.dcx().emit_err(TestOutput { - span: attr.span, + span: attr.span(), kind: Kind::Demangling, content: format!("{demangling}"), }); tcx.dcx().emit_err(TestOutput { - span: attr.span, + span: attr.span(), kind: Kind::DemanglingAlt, content: format!("{demangling:#}"), }); @@ -82,7 +82,7 @@ impl SymbolNamesTest<'_> { for attr in tcx.get_attrs(def_id, DEF_PATH) { tcx.dcx().emit_err(TestOutput { - span: attr.span, + span: attr.span(), kind: Kind::DefPath, content: with_no_trimmed_paths!(tcx.def_path_str(def_id)), }); diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index e29ed9a4b5659..b13a753c4ed1e 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -9,7 +9,7 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_ast = { path = "../rustc_ast" } rustc_ast_ir = { path = "../rustc_ast_ir" } -rustc_attr = { path = "../rustc_attr" } +rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index b97f3dc303ba1..769426c1328f1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -520,7 +520,8 @@ impl Trait for X { } } TypeError::TargetFeatureCast(def_id) => { - let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span); + let target_spans = + tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span()); diag.note( "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index f10314c1c9e07..d4885f5d25c44 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,11 +1,12 @@ use std::iter; use std::path::PathBuf; -use rustc_ast::{AttrArgs, AttrKind, Attribute, MetaItemInner}; +use rustc_ast::MetaItemInner; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::{AttrArgs, Attribute}; use rustc_macros::LintDiagnostic; use rustc_middle::bug; use rustc_middle::ty::print::PrintTraitRefExt as _; @@ -15,7 +16,7 @@ use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; use rustc_span::Span; use rustc_span::symbol::{Symbol, kw, sym}; use tracing::{debug, info}; -use {rustc_attr as attr, rustc_hir as hir}; +use {rustc_attr_parsing as attr, rustc_hir as hir}; use super::{ObligationCauseCode, PredicateObligation}; use crate::error_reporting::TypeErrCtxt; @@ -616,7 +617,14 @@ impl<'tcx> OnUnimplementedDirective { item_def_id: DefId, ) -> Result, ErrorGuaranteed> { let result = if let Some(items) = attr.meta_item_list() { - Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant) + Self::parse( + tcx, + item_def_id, + &items, + attr.span(), + true, + is_diagnostic_namespace_variant, + ) } else if let Some(value) = attr.value_str() { if !is_diagnostic_namespace_variant { Ok(Some(OnUnimplementedDirective { @@ -627,7 +635,7 @@ impl<'tcx> OnUnimplementedDirective { tcx, item_def_id, value, - attr.span, + attr.span(), is_diagnostic_namespace_variant, )?), notes: Vec::new(), @@ -639,7 +647,7 @@ impl<'tcx> OnUnimplementedDirective { let report_span = match &item.args { AttrArgs::Empty => item.path.span, AttrArgs::Delimited(args) => args.dspan.entire(), - AttrArgs::Eq { eq_span, value } => eq_span.to(value.span()), + AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span), }; if let Some(item_def_id) = item_def_id.as_local() { @@ -653,14 +661,14 @@ impl<'tcx> OnUnimplementedDirective { Ok(None) } } else if is_diagnostic_namespace_variant { - match &attr.kind { - AttrKind::Normal(p) if !matches!(p.item.args, AttrArgs::Empty) => { + match attr { + Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => { if let Some(item_def_id) = item_def_id.as_local() { tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, tcx.local_def_id_to_hir_id(item_def_id), - attr.span, - MalformedOnUnimplementedAttrLint::new(attr.span), + attr.span(), + MalformedOnUnimplementedAttrLint::new(attr.span()), ); } } @@ -669,7 +677,7 @@ impl<'tcx> OnUnimplementedDirective { tcx.emit_node_span_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, tcx.local_def_id_to_hir_id(item_def_id), - attr.span, + attr.span(), MissingOptionsForOnUnimplementedAttr, ) } diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index d81c47886d2d5..019a888bd2f62 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -4,6 +4,7 @@ use std::iter::once; use std::sync::Arc; use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; use rustc_hir::Mutability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId, LocalModDefId}; @@ -15,7 +16,6 @@ use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{Symbol, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, trace}; -use {rustc_ast as ast, rustc_hir as hir}; use super::Item; use crate::clean::{ @@ -43,7 +43,7 @@ pub(crate) fn try_inline( cx: &mut DocContext<'_>, res: Res, name: Symbol, - attrs: Option<(&[ast::Attribute], Option)>, + attrs: Option<(&[hir::Attribute], Option)>, visited: &mut DefIdSet, ) -> Option> { let did = res.opt_def_id()?; @@ -206,7 +206,7 @@ pub(crate) fn try_inline_glob( } } -pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [ast::Attribute] { +pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [hir::Attribute] { cx.tcx.get_attrs_unchecked(did) } @@ -360,7 +360,7 @@ fn build_type_alias( pub(crate) fn build_impls( cx: &mut DocContext<'_>, did: DefId, - attrs: Option<(&[ast::Attribute], Option)>, + attrs: Option<(&[hir::Attribute], Option)>, ret: &mut Vec, ) { let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls"); @@ -392,8 +392,8 @@ pub(crate) fn build_impls( pub(crate) fn merge_attrs( cx: &mut DocContext<'_>, - old_attrs: &[ast::Attribute], - new_attrs: Option<(&[ast::Attribute], Option)>, + old_attrs: &[hir::Attribute], + new_attrs: Option<(&[hir::Attribute], Option)>, ) -> (clean::Attributes, Option>) { // NOTE: If we have additional attributes (from a re-export), // always insert them first. This ensure that re-export @@ -404,14 +404,14 @@ pub(crate) fn merge_attrs( both.extend_from_slice(old_attrs); ( if let Some(item_id) = item_id { - Attributes::from_ast_with_additional(old_attrs, (inner, item_id.to_def_id())) + Attributes::from_hir_with_additional(old_attrs, (inner, item_id.to_def_id())) } else { - Attributes::from_ast(&both) + Attributes::from_hir(&both) }, both.cfg(cx.tcx, &cx.cache.hidden_cfg), ) } else { - (Attributes::from_ast(old_attrs), old_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg)) + (Attributes::from_hir(old_attrs), old_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg)) } } @@ -419,7 +419,7 @@ pub(crate) fn merge_attrs( pub(crate) fn build_impl( cx: &mut DocContext<'_>, did: DefId, - attrs: Option<(&[ast::Attribute], Option)>, + attrs: Option<(&[hir::Attribute], Option)>, ret: &mut Vec, ) { if !cx.inlined.insert(did.into()) { @@ -629,7 +629,7 @@ fn build_module_items( visited: &mut DefIdSet, inlined_names: &mut FxHashSet<(ItemType, Symbol)>, allowed_def_ids: Option<&DefIdSet>, - attrs: Option<(&[ast::Attribute], Option)>, + attrs: Option<(&[hir::Attribute], Option)>, ) -> Vec { let mut items = Vec::new(); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 34141c47cf3d4..2de708cdfca67 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -55,7 +55,7 @@ use rustc_trait_selection::traits::wf::object_region_bounds; use thin_vec::ThinVec; use tracing::{debug, instrument}; use utils::*; -use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir}; +use {rustc_ast as ast, rustc_hir as hir}; pub(crate) use self::types::*; pub(crate) use self::utils::{krate, register_res, synthesize_auto_trait_and_blanket_impls}; @@ -201,7 +201,7 @@ fn generate_item_with_correct_attrs( }; let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg); - let attrs = Attributes::from_ast_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false); + let attrs = Attributes::from_hir_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false); let name = renamed.or(Some(name)); let mut item = Item::from_def_id_and_attrs_and_parts(def_id, name, kind, attrs, cfg); @@ -1036,7 +1036,7 @@ fn clean_fn_or_proc_macro<'tcx>( /// This is needed to make it more "readable" when documenting functions using /// `rustc_legacy_const_generics`. More information in /// . -fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attribute]) { +fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attribute]) { for meta_item_list in attrs .iter() .filter(|a| a.has_name(sym::rustc_legacy_const_generics)) @@ -2574,7 +2574,7 @@ fn get_all_import_attributes<'hir>( import_def_id: LocalDefId, target_def_id: DefId, is_inline: bool, -) -> Vec<(Cow<'hir, ast::Attribute>, Option)> { +) -> Vec<(Cow<'hir, hir::Attribute>, Option)> { let mut attrs = Vec::new(); let mut first = true; for def_id in reexport_chain(cx.tcx, import_def_id, target_def_id) @@ -2627,9 +2627,9 @@ fn filter_doc_attr_ident(ident: Symbol, is_inline: bool) -> bool { /// Remove attributes from `normal` that should not be inherited by `use` re-export. /// Before calling this function, make sure `normal` is a `#[doc]` attribute. -fn filter_doc_attr(normal: &mut ast::NormalAttr, is_inline: bool) { - match normal.item.args { - ast::AttrArgs::Delimited(ref mut args) => { +fn filter_doc_attr(args: &mut hir::AttrArgs, is_inline: bool) { + match args { + hir::AttrArgs::Delimited(ref mut args) => { let tokens = filter_tokens_from_list(&args.tokens, |token| { !matches!( token, @@ -2647,7 +2647,7 @@ fn filter_doc_attr(normal: &mut ast::NormalAttr, is_inline: bool) { }); args.tokens = TokenStream::new(tokens); } - ast::AttrArgs::Empty | ast::AttrArgs::Eq { .. } => {} + hir::AttrArgs::Empty | hir::AttrArgs::Eq { .. } => {} } } @@ -2672,23 +2672,23 @@ fn filter_doc_attr(normal: &mut ast::NormalAttr, is_inline: bool) { /// * `doc(no_inline)` /// * `doc(hidden)` fn add_without_unwanted_attributes<'hir>( - attrs: &mut Vec<(Cow<'hir, ast::Attribute>, Option)>, - new_attrs: &'hir [ast::Attribute], + attrs: &mut Vec<(Cow<'hir, hir::Attribute>, Option)>, + new_attrs: &'hir [hir::Attribute], is_inline: bool, import_parent: Option, ) { for attr in new_attrs { - if matches!(attr.kind, ast::AttrKind::DocComment(..)) { + if attr.is_doc_comment() { attrs.push((Cow::Borrowed(attr), import_parent)); continue; } let mut attr = attr.clone(); - match attr.kind { - ast::AttrKind::Normal(ref mut normal) => { - if let [ident] = &*normal.item.path.segments { - let ident = ident.ident.name; + match attr { + hir::Attribute::Unparsed(ref mut normal) => { + if let [ident] = &*normal.path.segments { + let ident = ident.name; if ident == sym::doc { - filter_doc_attr(normal, is_inline); + filter_doc_attr(&mut normal.args, is_inline); attrs.push((Cow::Owned(attr), import_parent)); } else if is_inline || ident != sym::cfg { // If it's not a `cfg()` attribute, we keep it. @@ -2891,7 +2891,7 @@ fn clean_extern_crate<'tcx>( && attrs.iter().any(|a| { a.has_name(sym::doc) && match a.meta_item_list() { - Some(l) => attr::list_contains_name(&l, sym::inline), + Some(l) => ast::attr::list_contains_name(&l, sym::inline), None => false, } }) @@ -2996,8 +2996,8 @@ fn clean_use_statement_inner<'tcx>( a.has_name(sym::doc) && match a.meta_item_list() { Some(l) => { - attr::list_contains_name(&l, sym::no_inline) - || attr::list_contains_name(&l, sym::hidden) + ast::attr::list_contains_name(&l, sym::no_inline) + || ast::attr::list_contains_name(&l, sym::hidden) } None => false, } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index cba947eb833b2..b6db31fc4aeb3 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -6,9 +6,7 @@ use std::{fmt, iter}; use arrayvec::ArrayVec; use rustc_abi::{ExternAbi, VariantIdx}; -use rustc_ast::MetaItemInner; -use rustc_ast_pretty::pprust; -use rustc_attr::{ConstStability, Deprecation, Stability, StableSince}; +use rustc_attr_parsing::{ConstStability, Deprecation, Stability, StableSince}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; @@ -269,7 +267,7 @@ impl ExternalCrate { let attr_value = attr.value_str().expect("syntax should already be validated"); let Some(prim) = PrimitiveType::from_symbol(attr_value) else { span_bug!( - attr.span, + attr.span(), "primitive `{attr_value}` is not a member of `PrimitiveType`" ); }; @@ -454,14 +452,14 @@ impl Item { kind: ItemKind, cx: &mut DocContext<'_>, ) -> Item { - let ast_attrs = cx.tcx.get_attrs_unchecked(def_id); + let hir_attrs = cx.tcx.get_attrs_unchecked(def_id); Self::from_def_id_and_attrs_and_parts( def_id, name, kind, - Attributes::from_ast(ast_attrs), - ast_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg), + Attributes::from_hir(hir_attrs), + hir_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg), ) } @@ -742,10 +740,10 @@ impl Item { .iter() .filter_map(|attr| { if keep_as_is { - Some(pprust::attribute_to_string(attr)) + Some(rustc_hir_pretty::attribute_to_string(&tcx, attr)) } else if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { Some( - pprust::attribute_to_string(attr) + rustc_hir_pretty::attribute_to_string(&tcx, attr) .replace("\\\n", "") .replace('\n', "") .replace(" ", " "), @@ -955,7 +953,7 @@ pub(crate) trait AttributesExt { type AttributeIterator<'a>: Iterator where Self: 'a; - type Attributes<'a>: Iterator + type Attributes<'a>: Iterator where Self: 'a; @@ -1009,7 +1007,7 @@ pub(crate) trait AttributesExt { // #[doc] if attr.doc_str().is_none() && attr.has_name(sym::doc) { // #[doc(...)] - if let Some(list) = attr.meta().as_ref().and_then(|mi| mi.meta_item_list()) { + if let Some(list) = attr.meta_item_list() { for item in list { // #[doc(hidden)] if !item.has_name(sym::cfg) { @@ -1042,7 +1040,7 @@ pub(crate) trait AttributesExt { let mut meta = attr.meta_item().unwrap().clone(); meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature)); - if let Ok(feat_cfg) = Cfg::parse(&MetaItemInner::MetaItem(meta)) { + if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) { cfg &= feat_cfg; } } @@ -1053,14 +1051,14 @@ pub(crate) trait AttributesExt { } } -impl AttributesExt for [ast::Attribute] { +impl AttributesExt for [hir::Attribute] { type AttributeIterator<'a> = impl Iterator + 'a; - type Attributes<'a> = impl Iterator + 'a; + type Attributes<'a> = impl Iterator + 'a; fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> { self.iter() .filter(move |attr| attr.has_name(name)) - .filter_map(ast::Attribute::meta_item_list) + .filter_map(ast::attr::AttributeExt::meta_item_list) .flatten() } @@ -1069,20 +1067,20 @@ impl AttributesExt for [ast::Attribute] { } } -impl AttributesExt for [(Cow<'_, ast::Attribute>, Option)] { +impl AttributesExt for [(Cow<'_, hir::Attribute>, Option)] { type AttributeIterator<'a> = impl Iterator + 'a where Self: 'a; type Attributes<'a> - = impl Iterator + 'a + = impl Iterator + 'a where Self: 'a; fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> { AttributesExt::iter(self) .filter(move |attr| attr.has_name(name)) - .filter_map(ast::Attribute::meta_item_list) + .filter_map(hir::Attribute::meta_item_list) .flatten() } @@ -1152,7 +1150,7 @@ pub struct RenderedLink { #[derive(Clone, Debug, Default)] pub(crate) struct Attributes { pub(crate) doc_strings: Vec, - pub(crate) other_attrs: ast::AttrVec, + pub(crate) other_attrs: Vec, } impl Attributes { @@ -1180,22 +1178,22 @@ impl Attributes { self.has_doc_flag(sym::hidden) } - pub(crate) fn from_ast(attrs: &[ast::Attribute]) -> Attributes { - Attributes::from_ast_iter(attrs.iter().map(|attr| (attr, None)), false) + pub(crate) fn from_hir(attrs: &[hir::Attribute]) -> Attributes { + Attributes::from_hir_iter(attrs.iter().map(|attr| (attr, None)), false) } - pub(crate) fn from_ast_with_additional( - attrs: &[ast::Attribute], - (additional_attrs, def_id): (&[ast::Attribute], DefId), + pub(crate) fn from_hir_with_additional( + attrs: &[hir::Attribute], + (additional_attrs, def_id): (&[hir::Attribute], DefId), ) -> Attributes { // Additional documentation should be shown before the original documentation. let attrs1 = additional_attrs.iter().map(|attr| (attr, Some(def_id))); let attrs2 = attrs.iter().map(|attr| (attr, None)); - Attributes::from_ast_iter(attrs1.chain(attrs2), false) + Attributes::from_hir_iter(attrs1.chain(attrs2), false) } - pub(crate) fn from_ast_iter<'a>( - attrs: impl Iterator)>, + pub(crate) fn from_hir_iter<'a>( + attrs: impl Iterator)>, doc_only: bool, ) -> Attributes { let (doc_strings, other_attrs) = attrs_to_doc_fragments(attrs, doc_only); diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index d59b4e4081ca4..8aeebdde7bb45 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -578,11 +578,10 @@ pub(crate) fn has_doc_flag(tcx: TyCtxt<'_>, did: DefId, flag: Symbol) -> bool { } pub(crate) fn attrs_have_doc_flag<'a>( - mut attrs: impl Iterator, + mut attrs: impl Iterator, flag: Symbol, ) -> bool { - attrs - .any(|attr| attr.meta_item_list().is_some_and(|l| rustc_attr::list_contains_name(&l, flag))) + attrs.any(|attr| attr.meta_item_list().is_some_and(|l| ast::attr::list_contains_name(&l, flag))) } /// A link to `doc.rust-lang.org` that includes the channel name. Use this instead of manual links diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 70d9269ae5cd9..6fee049cdf073 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -13,10 +13,10 @@ use std::{panic, str}; pub(crate) use make::DocTestBuilder; pub(crate) use markdown::test as test_markdown; -use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, DiagCtxtHandle}; +use rustc_hir as hir; use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::LOCAL_CRATE; use rustc_interface::interface; @@ -325,7 +325,7 @@ pub(crate) fn run_tests( // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade. fn scrape_test_config( crate_name: String, - attrs: &[ast::Attribute], + attrs: &[hir::Attribute], args_file: PathBuf, ) -> GlobalTestOptions { use rustc_ast_pretty::pprust; diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs index dc68f48f635b3..4f4408a6b909d 100644 --- a/src/librustdoc/doctest/rust.rs +++ b/src/librustdoc/doctest/rust.rs @@ -110,7 +110,7 @@ impl HirCollector<'_> { // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with // anything else, this will combine them for us. - let attrs = Attributes::from_ast(ast_attrs); + let attrs = Attributes::from_hir(ast_attrs); if let Some(doc) = attrs.opt_doc_value() { let span = span_of_fragments(&attrs.doc_strings).unwrap_or(sp); self.collector.position = if span.edition().at_least_rust_2024() { @@ -122,7 +122,7 @@ impl HirCollector<'_> { .iter() .find(|attr| attr.doc_str().is_some()) .map(|attr| { - attr.span.ctxt().outer_expn().expansion_cause().unwrap_or(attr.span) + attr.span().ctxt().outer_expn().expansion_cause().unwrap_or(attr.span()) }) .unwrap_or(DUMMY_SP) }; diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 0a563b1df2620..0e84eebd19db1 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -15,7 +15,7 @@ use std::iter::{self, once}; use itertools::Itertools; use rustc_abi::ExternAbi; -use rustc_attr::{ConstStability, StabilityLevel, StableSince}; +use rustc_attr_parsing::{ConstStability, StabilityLevel, StableSince}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index e013829e5e0c8..eb9f39128bcb8 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -44,14 +44,15 @@ use std::path::PathBuf; use std::{fs, str}; use rinja::Template; -use rustc_attr::{ConstStability, DeprecatedSince, Deprecation, StabilityLevel, StableSince}; +use rustc_attr_parsing::{ + ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince, +}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::Mutability; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::RustcVersion; use rustc_span::symbol::{Symbol, sym}; use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName}; use serde::ser::SerializeMap; diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index bb967b7f163ed..5a12fca7dc18b 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -6,7 +6,7 @@ use rustc_abi::ExternAbi; use rustc_ast::ast; -use rustc_attr::DeprecatedSince; +use rustc_attr_parsing::DeprecatedSince; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::DefId; use rustc_metadata::rendered_const; @@ -215,8 +215,8 @@ where } } -pub(crate) fn from_deprecation(deprecation: rustc_attr::Deprecation) -> Deprecation { - let rustc_attr::Deprecation { since, note, suggestion: _ } = deprecation; +pub(crate) fn from_deprecation(deprecation: rustc_attr_parsing::Deprecation) -> Deprecation { + let rustc_attr_parsing::Deprecation { since, note, suggestion: _ } = deprecation; let since = match since { DeprecatedSince::RustcVersion(version) => Some(version.to_string()), DeprecatedSince::Future => Some("TBD".to_owned()), diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a384c286039d2..26df4c4d55013 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -36,7 +36,7 @@ extern crate pulldown_cmark; extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_attr; +extern crate rustc_attr_parsing; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs index a81b130a218b3..d924be2edce28 100644 --- a/src/librustdoc/passes/propagate_stability.rs +++ b/src/librustdoc/passes/propagate_stability.rs @@ -6,7 +6,7 @@ //! [`core::error`] module is marked as stable since 1.81.0, so we want to show //! [`core::error::Error`] as stable since 1.81.0 as well. -use rustc_attr::{Stability, StabilityLevel}; +use rustc_attr_parsing::{Stability, StabilityLevel}; use rustc_hir::def_id::CRATE_DEF_ID; use crate::clean::{Crate, Item, ItemId, ItemKind}; diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index ebd35fd2b27fe..95c85f250e985 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -2,9 +2,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; +use rustc_attr_parsing::RustcVersion; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{RustcVersion, impl_lint_pass}; +use rustc_session::impl_lint_pass; use rustc_span::symbol; use std::f64::consts as f64; diff --git a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs index d41bb580c6cd9..cb63fadb4e21c 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs @@ -1,7 +1,7 @@ use super::INLINE_ALWAYS; use super::utils::is_word; use clippy_utils::diagnostics::span_lint; -use rustc_ast::Attribute; +use rustc_hir::Attribute; use rustc_lint::LateContext; use rustc_span::symbol::Symbol; use rustc_span::{Span, sym}; @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Att span_lint( cx, INLINE_ALWAYS, - attr.span, + attr.span(), format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"), ); } diff --git a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs index 97cffeb098ef0..b4ed8a68a325a 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs @@ -2,14 +2,14 @@ use super::{Attribute, SHOULD_PANIC_WITHOUT_EXPECT}; use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::TokenTree; -use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind}; +use rustc_ast::{AttrArgs, AttrKind}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; use rustc_span::sym; pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { if let AttrKind::Normal(normal_attr) = &attr.kind { - if let AttrArgs::Eq { value: AttrArgsEq::Ast(_), .. } = &normal_attr.item.args { + if let AttrArgs::Eq { .. } = &normal_attr.item.args { // `#[should_panic = ".."]` found, good return; } diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 478ba7a187be5..6ee3290fa761d 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -15,7 +15,7 @@ pub(super) fn check( ) { if cfg_attr.has_name(sym::clippy) && let Some(ident) = behind_cfg_attr.ident() - && Level::from_symbol(ident.name, Some(attr.id)).is_some() + && Level::from_symbol(ident.name, || Some(attr.id)).is_some() && let Some(items) = behind_cfg_attr.meta_item_list() { let nb_items = items.len(); diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs index 3bb02688bf22c..7ad5a36676eb3 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs @@ -17,7 +17,7 @@ pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool { } pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool { - Level::from_symbol(symbol, Some(attr_id)).is_some() + Level::from_symbol(symbol, || Some(attr_id)).is_some() } pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 6eef0d42a550b..f68a7a89b3994 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -9,7 +9,8 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; -use rustc_session::{RustcVersion, impl_lint_pass}; +use rustc_attr_parsing::RustcVersion; +use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 383fae7992b61..a1ff20dee721f 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -5,9 +5,8 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn}; use core::ops::ControlFlow; -use rustc_ast::ast::Attribute; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, Expr, ExprKind, FnDecl}; +use rustc_hir::{Attribute, Body, Expr, ExprKind, FnDecl}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs index a0cb36f88dc05..28b4491fa5f1d 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs @@ -1,3 +1,4 @@ + use clippy_config::Conf; use clippy_config::types::create_disallowed_map; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; @@ -117,6 +118,7 @@ impl DisallowedMacros { } } +// TODO: early pass? impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); impl LateLintPass<'_> for DisallowedMacros { diff --git a/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs index de7a2c2433f46..ac8a9463e2f99 100644 --- a/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs +++ b/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs @@ -3,9 +3,10 @@ use clippy_utils::source::{SpanRangeExt, snippet_indent}; use clippy_utils::tokenize_with_text; use itertools::Itertools; use rustc_ast::token::CommentKind; -use rustc_ast::{AttrKind, AttrStyle, Attribute}; +use rustc_ast::AttrStyle; use rustc_errors::{Applicability, Diag, SuggestionStyle}; -use rustc_hir::{ItemKind, Node}; +use rustc_hir::{Attribute, ItemKind, Node}; +use rustc_attr_parsing::AttributeKind; use rustc_lexer::TokenKind; use rustc_lint::LateContext; use rustc_span::{BytePos, ExpnKind, InnerSpan, Span, SpanData}; @@ -67,14 +68,14 @@ impl Stop { } fn from_attr(cx: &LateContext<'_>, attr: &Attribute) -> Option { - let SpanData { lo, hi, .. } = attr.span.data(); + let SpanData { lo, hi, .. } = attr.span().data(); let file = cx.tcx.sess.source_map().lookup_source_file(lo); Some(Self { - span: attr.span, - kind: match attr.kind { - AttrKind::Normal(_) => StopKind::Attr, - AttrKind::DocComment(comment_kind, _) => StopKind::Doc(comment_kind), + span: attr.span(), + kind: match attr { + Attribute::Parsed(AttributeKind::DocComment{kind, ..}) => StopKind::Doc(*kind), + _ => StopKind::Attr, }, first: file.lookup_line(file.relative_position(lo))?, last: file.lookup_line(file.relative_position(hi))?, @@ -300,7 +301,7 @@ fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool { pub(super) fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool { let mut outer = attrs .iter() - .filter(|attr| attr.style == AttrStyle::Outer && !attr.span.from_expansion()) + .filter(|attr| attr.style() == AttrStyle::Outer && !attr.span().from_expansion()) .map(|attr| Stop::from_attr(cx, attr)) .collect::>>() .unwrap_or_default(); diff --git a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs index 2182689f98511..aa29705cf9386 100644 --- a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs @@ -1,18 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, AttrStyle, Attribute}; use rustc_errors::Applicability; -use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_lint::EarlyContext; +use rustc_ast::{Attribute, AttrKind, AttrArgs, AttrStyle}; use super::DOC_INCLUDE_WITHOUT_CFG; -pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) { +pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { for attr in attrs { if !attr.span.from_expansion() - && let AttrKind::Normal(ref normal) = attr.kind - && normal.item.path == sym::doc - && let AttrArgs::Eq { value: AttrArgsEq::Hir(ref meta), .. } = normal.item.args + && let AttrKind::Normal(ref item) = attr.kind + && attr.doc_str().is_some() + && let AttrArgs::Eq { expr: meta, .. } = &item.item.args && !attr.span.contains(meta.span) // Since the `include_str` is already expanded at this point, we can only take the // whole attribute snippet and then modify for our suggestion. diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 88ac871acf688..3a109a78c51a2 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -16,11 +16,10 @@ use pulldown_cmark::Event::{ }; use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd}; -use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{AnonConst, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; @@ -596,6 +595,13 @@ impl_lint_pass!(Documentation => [ DOC_INCLUDE_WITHOUT_CFG, ]); + +impl EarlyLintPass for Documentation { + fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) { + include_in_doc_without_cfg::check(cx, attrs); + } +} + impl<'tcx> LateLintPass<'tcx> for Documentation { fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { @@ -723,14 +729,13 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ Some(("fake".into(), "fake".into())) } - include_in_doc_without_cfg::check(cx, attrs); if suspicious_doc_comments::check(cx, attrs) || empty_line_after::check(cx, attrs) || is_doc_hidden(attrs) { return None; } let (fragments, _) = attrs_to_doc_fragments( attrs.iter().filter_map(|attr| { - if in_external_macro(cx.sess(), attr.span) { + if in_external_macro(cx.sess(), attr.span()) { None } else { Some((attr, None)) diff --git a/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs b/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs index f6f942b10cadb..bfc36deea7b29 100644 --- a/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs +++ b/src/tools/clippy/clippy_lints/src/doc/suspicious_doc_comments.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::AttrStyle; use rustc_ast::token::CommentKind; -use rustc_ast::{AttrKind, AttrStyle, Attribute}; use rustc_errors::Applicability; +use rustc_hir::Attribute; +use rustc_attr_parsing::AttributeKind; use rustc_lint::LateContext; use rustc_span::Span; @@ -35,15 +37,14 @@ fn collect_doc_replacements(attrs: &[Attribute]) -> Vec<(Span, String)> { attrs .iter() .filter_map(|attr| { - if let AttrKind::DocComment(com_kind, sym) = attr.kind - && let AttrStyle::Outer = attr.style - && let Some(com) = sym.as_str().strip_prefix('!') + if let Attribute::Parsed(AttributeKind::DocComment{ style: AttrStyle::Outer, kind, comment, ..}) = attr + && let Some(com) = comment.as_str().strip_prefix('!') { - let sugg = match com_kind { + let sugg = match kind { CommentKind::Line => format!("//!{com}"), CommentKind::Block => format!("/*!{com}*/"), }; - Some((attr.span, sugg)) + Some((attr.span(), sugg)) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 0165d24c7df27..472d26b535883 100644 --- a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -1,6 +1,6 @@ -use rustc_ast::ast::Attribute; use rustc_errors::Applicability; -use rustc_hir::{Item, ItemKind}; +use rustc_hir::{Attribute, Item, ItemKind}; +use rustc_attr_parsing::AttributeKind; use rustc_lint::LateContext; use clippy_utils::diagnostics::span_lint_and_then; @@ -44,9 +44,9 @@ pub(super) fn check( let mut should_suggest_empty_doc = false; for attr in attrs { - if let Some(doc) = attr.doc_str() { - spans.push(attr.span); - let doc = doc.as_str(); + if let Attribute::Parsed(AttributeKind::DocComment {span, comment, ..}) = attr { + spans.push(span); + let doc = comment.as_str(); let doc = doc.trim(); if spans.len() == 1 { // We make this suggestion only if the first doc line ends with a punctuation @@ -79,7 +79,7 @@ pub(super) fn check( && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi()) && let Some(snippet) = snippet_opt(cx, new_span) { - let Some(first) = snippet_opt(cx, first_span) else { + let Some(first) = snippet_opt(cx, *first_span) else { return; }; let Some(comment_form) = first.get(..3) else { diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs index 0599e08e6c0af..7f10938bfb04b 100644 --- a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs +++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { .hir() .attrs(item.hir_id()) .iter() - .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span)); + .fold(item.span.shrink_to_lo(), |span, attr| span.to(attr.span())); let (Some(file), _, _, end_line, _) = sm.span_to_location_info(span) else { return; }; diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 175d92d2d7902..2cbd21b56b81d 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -1,9 +1,8 @@ use hir::FnSig; -use rustc_ast::ast::Attribute; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdSet; -use rustc_hir::{self as hir, QPath}; +use rustc_hir::{self as hir, Attribute, QPath}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -111,7 +110,12 @@ fn check_needless_must_use( fn_header_span, "this unit-returning function has a `#[must_use]` attribute", |diag| { - diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable); + diag.span_suggestion( + attr.span(), + "remove the attribute", + "", + Applicability::MachineApplicable, + ); }, ); } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index f3467adacc55c..6cee7cfaca233 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -2,12 +2,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use clippy_utils::msrvs::Msrv; -use rustc_attr::{StabilityLevel, StableSince}; +use rustc_attr_parsing::{StabilityLevel, StableSince, RustcVersion}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; -use rustc_session::{RustcVersion, impl_lint_pass}; +use rustc_session::impl_lint_pass; use rustc_span::def_id::DefId; use rustc_span::{ExpnKind, Span}; diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs index 1b900f6be8e85..9b4a3b3f9c84c 100644 --- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs +++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs @@ -42,10 +42,10 @@ impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody { span_lint_and_then( cx, INLINE_FN_WITHOUT_BODY, - attr.span, + attr.span(), format!("use of `#[inline]` on trait method `{}` which has no body", item.ident), |diag| { - diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable); + diag.suggest_remove_item(cx, attr.span(), "remove", Applicability::MachineApplicable); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index b38e362410efd..53dc070833b52 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -2,11 +2,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet_opt; -use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, LitKind}; +use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_ast::{Attribute, AttrArgs, AttrKind}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -52,24 +53,6 @@ impl LargeIncludeFile { impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); -impl LargeIncludeFile { - fn emit_lint(&self, cx: &LateContext<'_>, span: Span) { - #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] - span_lint_and_then( - cx, - LARGE_INCLUDE_FILE, - span, - "attempted to include a large file", - |diag| { - diag.note(format!( - "the configuration allows a maximum size of {} bytes", - self.max_file_size - )); - }, - ); - } -} - impl LateLintPass<'_> for LargeIncludeFile { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { if let ExprKind::Lit(lit) = &expr.kind @@ -85,18 +68,32 @@ impl LateLintPass<'_> for LargeIncludeFile { && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id)) { - self.emit_lint(cx, expr.span.source_callsite()); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + cx, + LARGE_INCLUDE_FILE, + expr.span.source_callsite(), + "attempted to include a large file", + |diag| { + diag.note(format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + )); + }, + ); } } +} - fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) { +impl EarlyLintPass for LargeIncludeFile { + fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { if !attr.span.from_expansion() // Currently, rustc limits the usage of macro at the top-level of attributes, // so we don't need to recurse into each level. - && let AttrKind::Normal(ref normal) = attr.kind + && let AttrKind::Normal(ref item) = attr.kind && let Some(doc) = attr.doc_str() && doc.as_str().len() as u64 > self.max_file_size - && let AttrArgs::Eq { value: AttrArgsEq::Hir(ref meta), .. } = normal.item.args + && let AttrArgs::Eq { expr: meta, .. } = &item.item.args && !attr.span.contains(meta.span) // Since the `include_str` is already expanded at this point, we can only take the // whole attribute snippet and then modify for our suggestion. @@ -113,7 +110,19 @@ impl LateLintPass<'_> for LargeIncludeFile { && let sub_snippet = sub_snippet.trim() && (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!")) { - self.emit_lint(cx, attr.span); + #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] + span_lint_and_then( + cx, + LARGE_INCLUDE_FILE, + attr.span, + "attempted to include a large file", + |diag| { + diag.note(format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + )); + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index e80cca6e7db4b..b16e9de45238d 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -35,7 +35,7 @@ extern crate rustc_abi; extern crate rustc_arena; extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_attr; +extern crate rustc_attr_parsing; extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_errors; @@ -398,9 +398,9 @@ mod zombie_processes; use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_utils::macros::FormatArgsStorage; +use utils::attr_collector::{AttrCollector, AttrStorage}; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; -use utils::attr_collector::{AttrCollector, AttrStorage}; /// Register all pre expansion lints /// diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 22aa681b68199..f788401579d9f 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; -use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; @@ -95,7 +94,7 @@ impl LateLintPass<'_> for MacroUseImports { { for kid in cx.tcx.module_children(id) { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { - let span = mac_attr.span; + let span = mac_attr.span(); let def_path = cx.tcx.def_path_str(mac_id); self.imports.push((def_path, span, hir_id)); } @@ -104,9 +103,9 @@ impl LateLintPass<'_> for MacroUseImports { self.push_unique_macro_pat_ty(cx, item.span); } } - fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { - if attr.span.from_expansion() { - self.push_unique_macro(cx, attr.span); + fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &hir::Attribute) { + if attr.span().from_expansion() { + self.push_unique_macro(cx, attr.span()); } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 00800231fe46e..83d8a50939069 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -116,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { if let Some(non_exhaustive) = attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) { - diag.span_note(non_exhaustive.span, "the struct is already non-exhaustive"); + diag.span_note(non_exhaustive.span(), "the struct is already non-exhaustive"); } else { let indent = snippet_indent(cx, item.span).unwrap_or_default(); diag.span_suggestion_verbose( diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index 47472ab831f29..223d0dc76569b 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -2,9 +2,9 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment}; -use rustc_ast::{Attribute, LitKind}; +use rustc_ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; +use rustc_hir::{Arm, Attribute, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; use rustc_span::source_map::Spanned; diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 1300c7d106285..b6f49dcc163f1 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -10,8 +10,9 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; use clippy_utils::source::SpanRangeExt; -use rustc_ast::ast::{self, MetaItem, MetaItemKind}; +use rustc_ast::ast::MetaItemInner; use rustc_hir as hir; +use rustc_hir::Attribute; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -67,9 +68,8 @@ impl MissingDoc { *self.doc_hidden_stack.last().expect("empty doc_hidden_stack") } - fn has_include(meta: Option) -> bool { - if let Some(meta) = meta - && let MetaItemKind::List(list) = meta.kind + fn has_include(meta: Option<&[MetaItemInner]>) -> bool { + if let Some(list) = meta && let Some(meta) = list.first() && let Some(name) = meta.ident() { @@ -83,7 +83,7 @@ impl MissingDoc { &self, cx: &LateContext<'_>, def_id: LocalDefId, - attrs: &[ast::Attribute], + attrs: &[Attribute], sp: Span, article: &'static str, desc: &'static str, @@ -129,7 +129,7 @@ impl MissingDoc { let has_doc = attrs .iter() - .any(|a| a.doc_str().is_some() || Self::has_include(a.meta())) + .any(|a| a.doc_str().is_some() || Self::has_include(a.meta_item_list().as_deref())) || matches!(self.search_span(sp), Some(span) if span_to_snippet_contains_docs(cx, span)); if !has_doc { @@ -172,12 +172,12 @@ impl MissingDoc { impl_lint_pass!(MissingDoc => [MISSING_DOCS_IN_PRIVATE_ITEMS]); impl<'tcx> LateLintPass<'tcx> for MissingDoc { - fn check_attributes(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [ast::Attribute]) { + fn check_attributes(&mut self, _: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { let doc_hidden = self.doc_hidden() || is_doc_hidden(attrs); self.doc_hidden_stack.push(doc_hidden); } - fn check_attributes_post(&mut self, _: &LateContext<'tcx>, _: &'tcx [ast::Attribute]) { + fn check_attributes_post(&mut self, _: &LateContext<'tcx>, _: &'tcx [Attribute]) { self.doc_hidden_stack.pop().expect("empty doc_hidden_stack"); } diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index e587d695c846c..11ff779d53161 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; -use rustc_ast::ast; use rustc_hir as hir; +use rustc_hir::Attribute; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::AssocItemContainer; use rustc_session::declare_lint_pass; @@ -63,7 +63,7 @@ declare_clippy_lint! { "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)" } -fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) { +fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[Attribute], sp: Span, desc: &'static str) { let has_inline = attrs.iter().any(|a| a.has_name(sym::inline)); if !has_inline { span_lint( diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 6f3f371a68d6b..30846fb46ac1b 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -5,12 +5,11 @@ use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; -use rustc_ast::ast::Attribute; use rustc_errors::{Applicability, Diag}; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind, QPath, - TyKind, + Attribute, BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, + PatKind, QPath, TyKind, }; use rustc_hir_typeck::expr_use_visitor as euv; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs index 64587e08ddc6f..09fd56a82164a 100644 --- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi { .span .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len())) .shrink_to_lo(); - let attr_snippet = snippet(cx, attr.span, ".."); + let attr_snippet = snippet(cx, attr.span(), ".."); span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index 2941b9c39607a..82ff13a5aff16 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::Msrv; -use rustc_attr::{StabilityLevel, StableSince}; +use rustc_attr_parsing::{StabilityLevel, StableSince}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index b79e59f857bbf..dd49d5ed52f73 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -433,7 +433,7 @@ fn include_attrs_in_span(cx: &LateContext<'_>, hir_id: HirId, span: Span) -> Spa .hir() .attrs(hir_id) .iter() - .fold(span, |acc, attr| acc.to(attr.span))) + .fold(span, |acc, attr| acc.to(attr.span()))) } enum HasSafetyComment { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index af38e066559cf..496343d82c8d6 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -222,7 +222,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<' return; } - if rustc_attr::parse_version(value).is_none() { + if rustc_attr_parsing::parse_version(value).is_none() { span_lint_and_help( cx, INVALID_CLIPPY_VERSION_ATTRIBUTE, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 620a9b56eb557..54fe4f49a8585 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -872,8 +872,7 @@ pub fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool { match (l, r) { (Empty, Empty) => true, (Delimited(la), Delimited(ra)) => eq_delim_args(la, ra), - (Eq { value: AttrArgsEq::Ast(le), .. }, Eq{ value: AttrArgsEq::Ast(re), .. }) => eq_expr(le, re), - (Eq { value: AttrArgsEq::Hir(ll), .. }, Eq{ value: AttrArgsEq::Hir(rl), .. }) => ll.kind == rl.kind, + (Eq { eq_span: _, expr: le }, Eq { eq_span: _, expr: re }) => eq_expr(le, re), _ => false, } } diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index b2a6657baad3e..922afffb8767d 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,4 +1,5 @@ -use rustc_ast::{ast, attr}; +use rustc_ast::attr; +use rustc_ast::attr::AttributeExt; use rustc_errors::Applicability; use rustc_lexer::TokenKind; use rustc_lint::LateContext; @@ -51,33 +52,31 @@ impl LimitStack { pub fn limit(&self) -> u64 { *self.stack.last().expect("there should always be a value in the stack") } - pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) { + pub fn push_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: &'static str) { let stack = &mut self.stack; parse_attrs(sess, attrs, name, |val| stack.push(val)); } - pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) { + pub fn pop_attrs(&mut self, sess: &Session, attrs: &[impl AttributeExt], name: &'static str) { let stack = &mut self.stack; parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val))); } } -pub fn get_attr<'a>( +pub fn get_attr<'a, A: AttributeExt + 'a>( sess: &'a Session, - attrs: &'a [ast::Attribute], + attrs: &'a [A], name: &'static str, -) -> impl Iterator { +) -> impl Iterator { attrs.iter().filter(move |attr| { - let attr = if let ast::AttrKind::Normal(ref normal) = attr.kind { - &normal.item - } else { + let Some(attr_segments) = attr.ident_path() else { return false; }; - let attr_segments = &attr.path.segments; - if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy { + + if attr_segments.len() == 2 && attr_segments[0].name == sym::clippy { BUILTIN_ATTRIBUTES .iter() .find_map(|&(builtin_name, ref deprecation_status)| { - if attr_segments[1].ident.name.as_str() == builtin_name { + if attr_segments[1].name.as_str() == builtin_name { Some(deprecation_status) } else { None @@ -85,14 +84,13 @@ pub fn get_attr<'a>( }) .map_or_else( || { - sess.dcx() - .span_err(attr_segments[1].ident.span, "usage of unknown attribute"); + sess.dcx().span_err(attr_segments[1].span, "usage of unknown attribute"); false }, |deprecation_status| { let mut diag = sess .dcx() - .struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute"); + .struct_span_err(attr_segments[1].span, "usage of deprecated attribute"); match *deprecation_status { DeprecationStatus::Deprecated => { diag.emit(); @@ -100,7 +98,7 @@ pub fn get_attr<'a>( }, DeprecationStatus::Replaced(new_name) => { diag.span_suggestion( - attr_segments[1].ident.span, + attr_segments[1].span, "consider using", new_name, Applicability::MachineApplicable, @@ -110,7 +108,7 @@ pub fn get_attr<'a>( }, DeprecationStatus::None => { diag.cancel(); - attr_segments[1].ident.name.as_str() == name + attr_segments[1].as_str() == name }, } }, @@ -121,31 +119,31 @@ pub fn get_attr<'a>( }) } -fn parse_attrs(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) { +fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt], name: &'static str, mut f: F) { for attr in get_attr(sess, attrs, name) { if let Some(ref value) = attr.value_str() { if let Ok(value) = FromStr::from_str(value.as_str()) { f(value); } else { - sess.dcx().span_err(attr.span, "not a number"); + sess.dcx().span_err(attr.span(), "not a number"); } } else { - sess.dcx().span_err(attr.span, "bad clippy attribute"); + sess.dcx().span_err(attr.span(), "bad clippy attribute"); } } } -pub fn get_unique_attr<'a>( +pub fn get_unique_attr<'a, A: AttributeExt>( sess: &'a Session, - attrs: &'a [ast::Attribute], + attrs: &'a [A], name: &'static str, -) -> Option<&'a ast::Attribute> { - let mut unique_attr: Option<&ast::Attribute> = None; +) -> Option<&'a A> { + let mut unique_attr: Option<&A> = None; for attr in get_attr(sess, attrs, name) { if let Some(duplicate) = unique_attr { sess.dcx() - .struct_span_err(attr.span, format!("`{name}` is defined multiple times")) - .with_span_note(duplicate.span, "first definition found here") + .struct_span_err(attr.span(), format!("`{name}` is defined multiple times")) + .with_span_note(duplicate.span(), "first definition found here") .emit(); } else { unique_attr = Some(attr); @@ -156,16 +154,16 @@ pub fn get_unique_attr<'a>( /// Returns true if the attributes contain any of `proc_macro`, /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise -pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(rustc_ast::Attribute::is_proc_macro_attr) +pub fn is_proc_macro(attrs: &[impl AttributeExt]) -> bool { + attrs.iter().any(AttributeExt::is_proc_macro_attr) } /// Returns true if the attributes contain `#[doc(hidden)]` -pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { +pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool { attrs .iter() .filter(|attr| attr.has_name(sym::doc)) - .filter_map(ast::Attribute::meta_item_list) + .filter_map(AttributeExt::meta_item_list) .any(|l| attr::list_contains_name(&l, sym::hidden)) } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 8d48cdd3cbb4f..02bbddb413a9a 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -31,7 +31,7 @@ // (Currently there is no way to opt into sysroot crates without `extern crate`.) extern crate rustc_ast; extern crate rustc_ast_pretty; -extern crate rustc_attr; +extern crate rustc_attr_parsing; extern crate rustc_const_eval; extern crate rustc_data_structures; // The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate. @@ -135,13 +135,24 @@ use rustc_middle::hir::nested_filter; #[macro_export] macro_rules! extract_msrv_attr { - ($context:ident) => { - fn check_attributes(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { + (LateContext) => { + fn check_attributes(&mut self, cx: &rustc_lint::LateContext<'_>, attrs: &[rustc_hir::Attribute]) { let sess = rustc_lint::LintContext::sess(cx); self.msrv.check_attributes(sess, attrs); } - fn check_attributes_post(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) { + fn check_attributes_post(&mut self, cx: &rustc_lint::LateContext<'_>, attrs: &[rustc_hir::Attribute]) { + let sess = rustc_lint::LintContext::sess(cx); + self.msrv.check_attributes_post(sess, attrs); + } + }; + (EarlyContext) => { + fn check_attributes(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) { + let sess = rustc_lint::LintContext::sess(cx); + self.msrv.check_attributes(sess, attrs); + } + + fn check_attributes_post(&mut self, cx: &rustc_lint::EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) { let sess = rustc_lint::LintContext::sess(cx); self.msrv.check_attributes_post(sess, attrs); } @@ -1912,7 +1923,7 @@ pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: UintTy) -> u128 { (u << amt) >> amt } -pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool { +pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool { attrs.iter().any(|attr| attr.has_name(symbol)) } @@ -2263,21 +2274,13 @@ pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| { - if let ast::AttrKind::Normal(ref normal) = attr.kind { - normal.item.path == sym::no_std - } else { - false - } + attr.name_or_empty() == sym::no_std }) } pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| { - if let ast::AttrKind::Normal(ref normal) = attr.kind { - normal.item.path == sym::no_core - } else { - false - } + attr.name_or_empty() == sym::no_core }) } diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 1eb7d54e133d1..1e6368fab368d 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -1,6 +1,6 @@ -use rustc_ast::Attribute; -use rustc_attr::parse_version; -use rustc_session::{RustcVersion, Session}; +use rustc_ast::attr::AttributeExt; +use rustc_attr_parsing::{parse_version, RustcVersion}; +use rustc_session::Session; use rustc_span::{Symbol, sym}; use serde::Deserialize; use smallvec::{SmallVec, smallvec}; @@ -124,15 +124,15 @@ impl Msrv { self.current().is_none_or(|msrv| msrv >= required) } - fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option { + fn parse_attr(sess: &Session, attrs: &[impl AttributeExt]) -> Option { let sym_msrv = Symbol::intern("msrv"); let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym_msrv])); if let Some(msrv_attr) = msrv_attrs.next() { if let Some(duplicate) = msrv_attrs.last() { sess.dcx() - .struct_span_err(duplicate.span, "`clippy::msrv` is defined multiple times") - .with_span_note(msrv_attr.span, "first definition found here") + .struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times") + .with_span_note(msrv_attr.span(), "first definition found here") .emit(); } @@ -142,22 +142,22 @@ impl Msrv { } sess.dcx() - .span_err(msrv_attr.span, format!("`{msrv}` is not a valid Rust version")); + .span_err(msrv_attr.span(), format!("`{msrv}` is not a valid Rust version")); } else { - sess.dcx().span_err(msrv_attr.span, "bad clippy attribute"); + sess.dcx().span_err(msrv_attr.span(), "bad clippy attribute"); } } None } - pub fn check_attributes(&mut self, sess: &Session, attrs: &[Attribute]) { + pub fn check_attributes(&mut self, sess: &Session, attrs: &[impl AttributeExt]) { if let Some(version) = Self::parse_attr(sess, attrs) { self.stack.push(version); } } - pub fn check_attributes_post(&mut self, sess: &Session, attrs: &[Attribute]) { + pub fn check_attributes_post(&mut self, sess: &Session, attrs: &[impl AttributeExt]) { if Self::parse_attr(sess, attrs).is_some() { self.stack.pop(); } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index df3f10d6179a1..104ae154e3695 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -5,7 +5,7 @@ use crate::msrvs::{self, Msrv}; use hir::LangItem; -use rustc_attr::StableSince; +use rustc_attr_parsing::{RustcVersion, StableSince}; use rustc_const_eval::check_consts::ConstCx; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -381,14 +381,14 @@ fn check_terminator<'tcx>( fn is_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool { tcx.is_const_fn(def_id) && tcx.lookup_const_stability(def_id).is_none_or(|const_stab| { - if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level { + if let rustc_attr_parsing::StabilityLevel::Stable { since, .. } = const_stab.level { // Checking MSRV is manually necessary because `rustc` has no such concept. This entire // function could be removed if `rustc` provided a MSRV-aware version of `is_stable_const_fn`. // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. let const_stab_rust_version = match since { StableSince::Version(version) => version, - StableSince::Current => rustc_session::RustcVersion::CURRENT, + StableSince::Current => RustcVersion::CURRENT, StableSince::Err => return false, }; diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 85c896563dabf..e02d51afceff0 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -58,7 +58,7 @@ extern crate tracing; extern crate rustc_abi; extern crate rustc_apfloat; extern crate rustc_ast; -extern crate rustc_attr; +extern crate rustc_attr_parsing; extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 7cc22f83a24a6..bf0d55b2354ee 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -11,7 +11,7 @@ use std::{fmt, process}; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use rustc_abi::{Align, ExternAbi, Size}; -use rustc_attr::InlineAttr; +use rustc_attr_parsing::InlineAttr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; #[allow(unused)] use rustc_data_structures::static_assert_size; diff --git a/tests/ui/attributes/rustc_confusables.stderr b/tests/ui/attributes/rustc_confusables.stderr index 9e37d5f50837d..8e84d8f3284df 100644 --- a/tests/ui/attributes/rustc_confusables.stderr +++ b/tests/ui/attributes/rustc_confusables.stderr @@ -4,12 +4,6 @@ error: malformed `rustc_confusables` attribute input LL | #[rustc_confusables] | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` -error: attribute should be applied to an inherent method - --> $DIR/rustc_confusables.rs:45:1 - | -LL | #[rustc_confusables("blah")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: expected at least one confusable name --> $DIR/rustc_confusables.rs:30:5 | @@ -27,6 +21,12 @@ help: consider surrounding this with quotes LL | #[rustc_confusables("invalid_meta_item")] | + + +error: attribute should be applied to an inherent method + --> $DIR/rustc_confusables.rs:45:1 + | +LL | #[rustc_confusables("blah")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0599]: no method named `inser` found for struct `rustc_confusables_across_crate::BTreeSet` in the current scope --> $DIR/rustc_confusables.rs:12:7 | diff --git a/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout b/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout index e9ee59abfae8d..e8c88d2dcdf2c 100644 --- a/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout +++ b/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout @@ -7,9 +7,7 @@ extern crate std; // issue#97006 macro_rules! m { ($attr_path: path) => { #[$attr_path] fn f() {} } } -#[ - -inline] +#[inline] fn f() { } fn main() { }