diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 71777e0117ab8..98c0ca8d24b88 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3433,7 +3433,7 @@ pub struct Attribute { /// or the construct this attribute is contained within (inner). pub style: AttrStyle, - /// The carets in the examples below show the spans for various cases. + /// The span covers the full attribute, as shown by the carets in the following examples. /// ```text /// #[foo] - A vanilla parsed attribute. /// ^^^^^^ - Its span covers it all. @@ -3447,8 +3447,10 @@ pub struct Attribute { /// /// #[cfg_attr(pred, foo)] - A parsed `cfg_attr` attribute. /// ^^^^^^^^^^^^^^^^^^^^^^ - Its span covers it all. - /// ^^^ - Span of the new replacement attribute (equivalent to `#[foo]`) - /// created by `cfg_attr` expansion (if `pred` is true). + /// ^^^^^^^^^^^^^^^^^^^^^^ - Span of the new replacement attribute (equivalent to `#[foo]`) + /// created by `cfg_attr` expansion (if `pred` is true). If multiple + /// attributes are expanded (e.g. `#[cfg_attr(p, a, b)]` -> + /// `#[a] #[b]`) they all get the same span. /// ^^^^^^^^^^^^^^^^^^^^^^ - Span of the synthetic `CfgAttrTrace` attribute created by /// `cfg_attr` expansion. (`CfgTrace` is derived from `#[cfg(..)]` and /// handled similarly.) @@ -3461,7 +3463,7 @@ pub struct Attribute { #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub enum AttrKind { /// A normal attribute. - Normal(Box), + Normal(Box), /// A synthetic attribute inserted by the compiler. Synthetic(Box), @@ -3472,42 +3474,31 @@ pub enum AttrKind { DocComment(CommentKind, Symbol), } -#[derive(Clone, Encodable, Decodable, Debug, Walkable)] -pub struct NormalAttr { - pub item: AttrItem, - // Tokens for the full attribute, e.g. `#[foo]`, `#![bar]`. (Compare this with - // `ParseNtResult::Meta`; `expand_cfg_attr_item` is where the two cases interact.) - pub tokens: Option, -} - -impl NormalAttr { - pub fn from_ident(ident: Ident) -> Self { - Self { - item: AttrItem { - unsafety: Safety::Default, - path: Path::from_ident(ident), - args: AttrArgs::Empty, - span: ident.span, - }, - tokens: None, - } - } -} - #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct AttrItem { pub unsafety: Safety, pub path: Path, pub args: AttrArgs, - /// The span of the entire attr item. For parse attrs this excludes `#[`/`]`. E.g.: + /// The span of the entire attr item. For parsed attrs this excludes `#[`/`]`. E.g.: /// ```ignore (illustrative) /// #[foo(bar)] /// ^^^^^^^^ /// #[unsafe(no_mangle)] /// ^^^^^^^^^^^^^^^^^ /// ``` + /// For attributes created by expanding `cfg_attr` this is just the embedded attribute. E.g.: + /// ```ignore (illustrative) + /// #[cfg_attr(pred, foo)] + /// ^^^ + /// ``` /// For internally constructed spans (`mk_attr_*`) the exact meaning may differ. pub span: Span, + /// Was this created by expanding a `#[cfg_attr(pred, foo)]` attribute? + pub from_cfg_attr: bool, + /// When we synthesize tokens for the attribute, can we derive precise spans for the delimiter + /// tokens (`#`, `!` (if present), and `[`/`]`) from `span`? This is the case for parsed + /// attributes with no extraneous whitespace, e.g. yes for `#[foo]` but no for `# [ foo ]`. + pub use_precise_delim_token_spans: bool, } /// A synthetic attribute. @@ -3538,18 +3529,6 @@ pub enum SyntheticAttr { CfgAttrTrace(CfgEntry), } -impl AttrItem { - pub fn is_valid_for_outer_style(&self) -> bool { - self.path == sym::cfg_attr - || self.path == sym::cfg - || self.path == sym::forbid - || self.path == sym::warn - || self.path == sym::allow - || self.path == sym::deny - || self.path == sym::expect - } -} - /// `TraitRef`s appear in impls. /// /// Resolution maps each `TraitRef`'s `ref_id` to its defining trait; that's all @@ -4452,6 +4431,7 @@ mod size_asserts { // tidy-alphabetical-start static_assert_size!(AssocItem, 72); static_assert_size!(AssocItemKind, 16); + static_assert_size!(AttrItem, 72); static_assert_size!(AttrKind, 16); static_assert_size!(Attribute, 32); static_assert_size!(Block, 24); @@ -4478,7 +4458,6 @@ mod size_asserts { static_assert_size!(MetaItem, 80); static_assert_size!(MetaItemKind, 40); static_assert_size!(MetaItemLit, 40); - static_assert_size!(NormalAttr, 80); static_assert_size!(Param, 40); static_assert_size!(Pat, 64); static_assert_size!(PatKind, 48); diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 3b0478e24b195..d5a98278541e7 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -7,9 +7,9 @@ use std::marker::PhantomData; use crate::tokenstream::{LazyAttrTokenStream, WithTokens}; use crate::{ - Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField, - FieldDef, ForeignItem, GenericParam, Item, NodeId, Param, Pat, PatField, Path, Stmt, StmtKind, - Ty, Variant, Visibility, WherePredicate, + Arm, AssocItem, AttrVec, Attribute, Block, Crate, Expr, ExprField, FieldDef, ForeignItem, + GenericParam, Item, NodeId, Param, Pat, PatField, Path, Stmt, StmtKind, Ty, Variant, + Visibility, WherePredicate, }; /// A trait for AST nodes having an ID. @@ -166,21 +166,6 @@ impl HasTokens for Stmt { } } -impl HasTokens for Attribute { - fn tokens(&self) -> Option<&LazyAttrTokenStream> { - match &self.kind { - AttrKind::Normal(normal) => normal.tokens.as_ref(), - AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(), - } - } - fn tokens_mut(&mut self) -> Option<&mut Option> { - Some(match &mut self.kind { - AttrKind::Normal(normal) => &mut normal.tokens, - AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(), - }) - } -} - /// A trait for AST nodes having (or not having) attributes. pub trait HasAttrs { /// This is `true` if this `HasAttrs` might support 'custom' (proc-macro) inner @@ -247,7 +232,7 @@ impl_has_attrs!( Variant, WherePredicate, ); -impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility); +impl_has_attrs_none!(Block, Pat, Path, Ty, Visibility); impl HasAttrs for WithTokens { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 40a1b4bd32218..2029899975006 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -7,21 +7,20 @@ use std::fmt::Debug; use std::sync::atomic::{AtomicU32, Ordering}; use rustc_index::bit_set::GrowableBitSet; -use rustc_span::{Ident, Span, Symbol, sym}; +use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::{ThinVec, thin_vec}; use crate::ast::{ AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs, - Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path, - PathSegment, Safety, SyntheticAttr, + Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, Path, PathSegment, + Safety, SyntheticAttr, }; use crate::token::{ - self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token, + self, CommentKind, Delimiter, DocFragmentKind, IdentIsRaw, InvisibleOrigin, MetaVarKind, Token, }; use crate::tokenstream::{ - AttrTokenStream, AttrTokenTree, DelimSpacing, DelimSpan, LazyAttrTokenStream, Spacing, - TokenStream, TokenStreamIter, TokenTree, + DelimSpacing, DelimSpan, Spacing, TokenStream, TokenStreamIter, TokenTree, }; use crate::util::comments; use crate::util::literal::escape_string_symbol; @@ -61,7 +60,7 @@ impl AttrIdGenerator { impl Attribute { pub fn get_normal_item(&self) -> &AttrItem { match &self.kind { - AttrKind::Normal(normal) => &normal.item, + AttrKind::Normal(normal) => &normal, AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(), } } @@ -83,7 +82,7 @@ impl AttributeExt for Attribute { fn value_span(&self) -> Option { match &self.kind { - AttrKind::Normal(normal) => match &normal.item.args { + AttrKind::Normal(normal) => match &normal.args { AttrArgs::Eq { expr, .. } => Some(expr.span), _ => None, }, @@ -105,7 +104,7 @@ impl AttributeExt for Attribute { fn name(&self) -> Option { use SyntheticAttr::*; match &self.kind { - AttrKind::Normal(normal) => normal.item.name(), + AttrKind::Normal(normal) => normal.name(), AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None, AttrKind::DocComment(..) => None, } @@ -115,7 +114,7 @@ impl AttributeExt for Attribute { use SyntheticAttr::*; match &self.kind { AttrKind::Normal(normal) => { - Some(normal.item.path.segments.iter().map(|i| i.ident.name).collect()) + Some(normal.path.segments.iter().map(|i| i.ident.name).collect()) } AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None, AttrKind::DocComment(_, _) => None, @@ -124,7 +123,7 @@ impl AttributeExt for Attribute { fn path_span(&self) -> Option { match &self.kind { - AttrKind::Normal(attr) => Some(attr.item.path.span), + AttrKind::Normal(attr) => Some(attr.path.span), AttrKind::Synthetic(..) => unreachable!(), AttrKind::DocComment(_, _) => None, } @@ -133,9 +132,8 @@ impl AttributeExt for Attribute { fn path_matches(&self, name: &[Symbol]) -> bool { match &self.kind { AttrKind::Normal(normal) => { - normal.item.path.segments.len() == name.len() + normal.path.segments.len() == name.len() && normal - .item .path .segments .iter() @@ -152,7 +150,7 @@ impl AttributeExt for Attribute { fn is_word(&self) -> bool { match &self.kind { - AttrKind::Normal(normal) => matches!(normal.item.args, AttrArgs::Empty), + AttrKind::Normal(normal) => matches!(normal.args, AttrArgs::Empty), AttrKind::Synthetic(..) => unreachable!(), AttrKind::DocComment(..) => false, } @@ -167,7 +165,7 @@ impl AttributeExt for Attribute { /// ``` fn meta_item_list(&self) -> Option> { match &self.kind { - AttrKind::Normal(normal) => normal.item.meta_item_list(), + AttrKind::Normal(normal) => normal.meta_item_list(), AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None, } } @@ -189,7 +187,7 @@ impl AttributeExt for Attribute { /// ``` fn value_str(&self) -> Option { match &self.kind { - AttrKind::Normal(normal) => normal.item.value_str(), + AttrKind::Normal(normal) => normal.value_str(), AttrKind::Synthetic(..) => unreachable!(), AttrKind::DocComment(..) => None, } @@ -204,9 +202,9 @@ impl AttributeExt for Attribute { match &self.kind { AttrKind::DocComment(kind, data) => Some((*data, DocFragmentKind::Sugared(*kind))), AttrKind::Normal(normal) - if normal.item.path == sym::doc - && let Some(value) = normal.item.value_str() - && let Some(value_span) = normal.item.value_span() => + if normal.path == sym::doc + && let Some(value) = normal.value_str() + && let Some(value_span) = normal.value_span() => { Some((value, DocFragmentKind::Raw(value_span))) } @@ -221,7 +219,7 @@ impl AttributeExt for Attribute { 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(), + AttrKind::Normal(normal) if normal.path == sym::doc => normal.value_str(), _ => None, } } @@ -229,9 +227,7 @@ impl AttributeExt for Attribute { fn doc_resolution_scope(&self) -> Option { match &self.kind { AttrKind::DocComment(..) => Some(self.style), - AttrKind::Normal(normal) - if normal.item.path == sym::doc && normal.item.value_str().is_some() => - { + AttrKind::Normal(normal) if normal.path == sym::doc && normal.value_str().is_some() => { Some(self.style) } _ => None, @@ -278,31 +274,77 @@ impl Attribute { /// Extracts the MetaItem from inside this Attribute. pub fn meta(&self) -> Option { match &self.kind { - AttrKind::Normal(normal) => normal.item.meta(self.span), + AttrKind::Normal(normal) => normal.meta(self.span), AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None, } } pub fn meta_kind(&self) -> Option { match &self.kind { - AttrKind::Normal(normal) => normal.item.meta_kind(), + AttrKind::Normal(normal) => normal.meta_kind(), AttrKind::Synthetic(..) => unreachable!(), AttrKind::DocComment(..) => None, } } + /// Synthesizes token trees for the attribute, rather than using an + /// `Option` as is done for many other AST nodes. This works well for + /// attributes because (a) they have a rigid micro-grammar, and (b) we record tokens for the + /// internal pieces in the interesting cases (e.g. in `AttrArgs` within `AttrItem::args`.) pub fn token_trees(&self) -> Vec { - match self.kind { - AttrKind::Normal(ref normal) => normal - .tokens - .as_ref() - .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}")) - .to_attr_token_stream() - .to_token_trees(), + match &self.kind { + AttrKind::Normal(normal) => { + let prefix_len: u32 = match self.style { + AttrStyle::Outer => 1, // `#` + AttrStyle::Inner => 2, // `#!` + }; + + // If the attribute is in the standard parsed `#[..]` or `#![..]` form we can use + // simple offset arithmetic to create precise sub-spans for the delimiter tokens. + // Otherwise, every token gets the same span, that of the entire attribute. + // + // See the code that sets `use_precise_delim_token_spans` in `parse_attribute` for + // more details. + let span = self.span; + let (pound_span, bang_span, dspan) = if normal.use_precise_delim_token_spans { + assert_ne!(span, DUMMY_SP); + let lo = span.lo(); + ( + span.with_hi(lo + BytePos(1)), + span.with_lo(lo + BytePos(1)).with_hi(lo + BytePos(2)), + DelimSpan::from_pair( + span.with_lo(lo + BytePos(prefix_len)) + .with_hi(lo + BytePos(prefix_len + 1)), + span.with_lo(span.hi() - BytePos(1)), + ), + ) + } else { + (span, span, DelimSpan::from_single(span)) + }; + + let delimited = TokenTree::Delimited( + dspan, + DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), + Delimiter::Bracket, + TokenStream::new(normal.token_trees()), + ); + + let trees = match self.style { + AttrStyle::Outer => { + vec![TokenTree::token_joint_hidden(token::Pound, pound_span), delimited] + } + AttrStyle::Inner => vec![ + TokenTree::token_joint(token::Pound, pound_span), + TokenTree::token_joint_hidden(token::Bang, bang_span), + delimited, + ], + }; + trees + } // Empty tokens here ensures synthetic attributes are invisible to proc macros. AttrKind::Synthetic(..) => vec![], AttrKind::DocComment(comment_kind, data) => vec![TokenTree::token_alone( - token::DocComment(comment_kind, self.style, data), + token::DocComment(*comment_kind, self.style, *data), self.span, )], } @@ -310,16 +352,14 @@ impl Attribute { pub fn deprecation_note(&self) -> Option { match &self.kind { - AttrKind::Normal(normal) if normal.item.path == sym::deprecated => { - let meta = &normal.item; - + AttrKind::Normal(normal) if normal.path == sym::deprecated => { // #[deprecated = "..."] - if let Some(s) = meta.value_str() { - return Some(Ident { name: s, span: meta.span }); + if let Some(s) = normal.value_str() { + return Some(Ident { name: s, span: normal.span }); } // #[deprecated(note = "...")] - if let Some(list) = meta.meta_item_list() { + if let Some(list) = normal.meta_item_list() { for nested in list { if let Some(mi) = nested.meta_item() && mi.path == sym::note @@ -338,6 +378,31 @@ impl Attribute { } impl AttrItem { + pub fn new(unsafety: Safety, path: Path, args: AttrArgs, span: Span) -> AttrItem { + AttrItem { + unsafety, + path, + args, + span, + from_cfg_attr: false, + use_precise_delim_token_spans: false, + } + } + + pub fn from_ident(ident: Ident) -> Self { + AttrItem::new(Safety::Default, Path::from_ident(ident), AttrArgs::Empty, ident.span) + } + + pub fn is_valid_for_outer_style(&self) -> bool { + self.path == sym::cfg_attr + || self.path == sym::cfg + || self.path == sym::forbid + || self.path == sym::warn + || self.path == sym::allow + || self.path == sym::deny + || self.path == sym::expect + } + pub fn name(&self) -> Option { if let [seg] = &*self.path.segments { Some(seg.ident.name) } else { None } } @@ -406,6 +471,82 @@ impl AttrItem { pub fn meta_kind(&self) -> Option { MetaItemKind::from_attr_args(&self.args) } + + /// Synthesizes token trees for the attr item. See `Attribute::token_trees` for more details. + pub fn token_trees(&self) -> Vec { + let mut trees = vec![]; + + let num_segs = self.path.segments.len(); + for (i, seg) in self.path.segments.iter().enumerate() { + if i > 0 { + // `::` separator spans are not recorded in the AST. Assume the separator span is + // the full space between path segments. In the rare case that whitespace is + // present (e.g. "rustfmt :: skip") the span will be slightly imprecise. + let sep_span = self.path.segments[i - 1].ident.span.between(seg.ident.span); + trees.push(TokenTree::token_joint_hidden(token::PathSep, sep_span)); + } + + // A leading `::` is represented by a `PathRoot` segment; the `PathSep` emitted above + // on the next loop iteration covers it. + if seg.ident.name != kw::PathRoot { + let spacing = if i + 1 < num_segs { + Spacing::Joint // followed by `::` + } else { + match &self.args { + AttrArgs::Delimited(_) => Spacing::JointHidden, // followed by `(`/`[`/`{` + AttrArgs::Empty | AttrArgs::Eq { .. } => Spacing::Alone, + } + }; + trees.push(TokenTree::Token(Token::from_ast_ident(seg.ident), spacing)); + } + } + + match &self.args { + AttrArgs::Empty => {} + AttrArgs::Delimited(DelimArgs { dspan, delim, tokens }) => { + trees.push(TokenTree::Delimited( + *dspan, + DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), + *delim, + tokens.clone(), + )); + } + AttrArgs::Eq { eq_span, expr } => { + trees.push(TokenTree::token_alone(token::Eq, *eq_span)); + if expr.tokens.is_some() { + // Parsed attributes, e.g. from `parse_attr_args` which force collects tokens. + trees.extend(TokenStream::from_ast(expr).iter().cloned()); + } else if let ExprKind::Lit(lit) = expr.kind { + // Builtin attributes, e.g. from `mk_attr_name_value_str`. + trees.push(TokenTree::token_alone(token::Literal(lit), expr.span)); + } else { + panic!("attribute value expression has no tokens: {expr:?}"); + } + } + } + + let safety_kw = match self.unsafety { + Safety::Default => None, + Safety::Unsafe(span) => Some((kw::Unsafe, span)), + Safety::Safe(span) => Some((kw::Safe, span)), + }; + if let Some((kw, kw_span)) = safety_kw { + vec![ + TokenTree::Token( + Token::new(token::Ident(kw, IdentIsRaw::No), kw_span), + Spacing::JointHidden, + ), + TokenTree::Delimited( + DelimSpan::from_single(self.span), + DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), + Delimiter::Parenthesis, + TokenStream::new(trees), + ), + ] + } else { + trees + } + } } impl MetaItem { @@ -734,40 +875,10 @@ pub fn mk_doc_comment( pub fn mk_attr_from_item( g: &AttrIdGenerator, item: AttrItem, - tokens: Option, style: AttrStyle, span: Span, ) -> Attribute { - Attribute { - kind: AttrKind::Normal(Box::new(NormalAttr { item, tokens })), - id: g.mk_attr_id(), - style, - span, - } -} - -fn mk_attr_tokens( - style: AttrStyle, - item_tokens: AttrTokenStream, - span: Span, -) -> LazyAttrTokenStream { - let mut tokens = match style { - AttrStyle::Outer => { - vec![AttrTokenTree::Token(Token::new(token::Pound, span), Spacing::JointHidden)] - } - AttrStyle::Inner => vec![ - AttrTokenTree::Token(Token::new(token::Pound, span), Spacing::Joint), - AttrTokenTree::Token(Token::new(token::Bang, span), Spacing::JointHidden), - ], - }; - tokens.push(AttrTokenTree::Delimited( - DelimSpan::from_single(span), - DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), - Delimiter::Bracket, - item_tokens, - )); - - LazyAttrTokenStream::new_direct(AttrTokenStream::new(tokens)) + Attribute { kind: AttrKind::Normal(Box::new(item)), id: g.mk_attr_id(), style, span } } // `span` is used for the `Attribute` and everything within it (except for any span within @@ -776,22 +887,7 @@ pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: S let path = Path::from_ident(Ident::new(name, span)); let args = AttrArgs::Empty; - let tokens = Some(mk_attr_tokens( - style, - AttrTokenStream::new(vec![AttrTokenTree::Token( - Token::from_ast_ident(Ident::new(name, span)), - Spacing::Alone, - )]), - span, - )); - - mk_attr_from_item( - g, - AttrItem { unsafety: Safety::Default, path, args, span }, - tokens, - style, - span, - ) + mk_attr_from_item(g, AttrItem::new(Safety::Default, path, args, span), style, span) } // `span` is used for the `Attribute` and everything within it (except for any span within @@ -815,30 +911,7 @@ pub fn mk_attr_nested_word( tokens: inner_tokens, }); - let tokens = Some(mk_attr_tokens( - style, - AttrTokenStream::new(vec![ - AttrTokenTree::Token(Token::from_ast_ident(Ident::new(outer, span)), Spacing::Alone), - AttrTokenTree::Delimited( - DelimSpan::from_single(span), - DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), - Delimiter::Parenthesis, - AttrTokenStream::new(vec![AttrTokenTree::Token( - Token::from_ast_ident(Ident::new(inner, span)), - Spacing::Alone, - )]), - ), - ]), - span, - )); - - mk_attr_from_item( - g, - AttrItem { unsafety: Safety::Default, path, args: attr_args, span }, - tokens, - style, - span, - ) + mk_attr_from_item(g, AttrItem::new(Safety::Default, path, attr_args, span), style, span) } // `span` is used for the `Attribute` and everything within it (except for any span within @@ -861,26 +934,7 @@ pub fn mk_attr_name_value_str( let path = Path::from_ident(Ident::new(name, span)); let args = AttrArgs::Eq { eq_span: span, expr }; - let tokens = Some(mk_attr_tokens( - style, - AttrTokenStream::new(vec![ - AttrTokenTree::Token(Token::from_ast_ident(Ident::new(name, span)), Spacing::Alone), - AttrTokenTree::Token(Token::new(token::Eq, span), Spacing::Alone), - AttrTokenTree::Token( - Token::new(token::TokenKind::lit(lit.kind, lit.symbol, lit.suffix), span), - Spacing::Alone, - ), - ]), - span, - )); - - mk_attr_from_item( - g, - AttrItem { unsafety: Safety::Default, path, args, span }, - tokens, - style, - span, - ) + mk_attr_from_item(g, AttrItem::new(Safety::Default, path, args, span), style, span) } pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator { diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 428f37b8af450..adaf9a68e486e 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -102,10 +102,6 @@ impl WithTokens { pub fn new(node: T) -> WithTokens { WithTokens { node, tokens: None } } - - pub fn map(self, f: impl FnOnce(T) -> U) -> WithTokens { - WithTokens { node: f(self.node), tokens: self.tokens } - } } /// A lazy version of [`AttrTokenStream`], which defers creation of an actual diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index a768935f38fc3..7f526c70612df 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -459,7 +459,6 @@ macro_rules! common_visitor_and_walkers { ModKind, ModSpans, MutTy, - NormalAttr, Parens, ParenthesizedArgs, PatFieldsRest, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 06673379a5e9f..05a11bf399dbf 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -922,7 +922,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_attrs( inner_hir_id, &[Attribute { - kind: AttrKind::Normal(Box::new(NormalAttr::from_ident(Ident::new( + kind: AttrKind::Normal(Box::new(AttrItem::from_ident(Ident::new( sym::track_caller, span, )))), diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 0e47424ba1aa4..0ee9d303293ca 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -523,7 +523,7 @@ impl<'a> AstValidator<'a> { AttrKind::Normal(normal) => { let arr = [sym::allow, sym::deny, sym::expect, sym::forbid, sym::splat, sym::warn]; - !attr.has_any_name(&arr) && rustc_attr_parsing::is_builtin_attr(&normal.item) + !attr.has_any_name(&arr) && rustc_attr_parsing::is_builtin_attr(normal) } AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => false, AttrKind::DocComment(..) => true, @@ -1282,7 +1282,7 @@ impl<'a> AstValidator<'a> { continue; } - let attr_name = pprust::path_to_string(&normal.item.path); + let attr_name = pprust::path_to_string(&normal.path); for eii_impl in eii_impls { self.dcx().emit_err(diagnostics::EiiImplAttributeNotSupported { attr_span: attr.span, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 3b0c90264e32d..2c4b76e3a3292 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -682,7 +682,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere ast::AttrStyle::Inner => self.word("#!["), ast::AttrStyle::Outer => self.word("#["), } - self.print_attr_item(&normal.item, attr.span); + self.print_attr_item(&normal, attr.span); self.word("]"); } ast::AttrKind::Synthetic(..) => unreachable!(), // due to early return above diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index ef37027f07b96..301f1c0f8b779 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -1,13 +1,13 @@ use std::convert::identity; use rustc_ast::token::Delimiter; -use rustc_ast::tokenstream::{DelimSpan, WithTokens}; +use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{AttrItem, Attribute, LitKind, ast, token}; use rustc_errors::{Applicability, Diagnostic, PResult, msg}; use rustc_feature::{Features, GatedCfg, find_gated_cfg}; use rustc_hir::attrs::{CfgEntry, RustcVersion}; use rustc_hir::{AttrPath, Target}; -use rustc_parse::parser::{ForceCollect, Parser, Recovery}; +use rustc_parse::parser::{Parser, Recovery}; use rustc_parse::{exp, parse_in}; use rustc_session::Session; use rustc_session::config::ExpectedValues; @@ -318,8 +318,9 @@ pub fn parse_cfg_attr( sess: &Session, features: Option<&Features>, lint_node_id: ast::NodeId, -) -> Option<(CfgEntry, Vec<(WithTokens, Span)>)> { - match &cfg_attr.get_normal_item().args { +) -> Option<(CfgEntry, Vec)> { + let item = cfg_attr.get_normal_item(); + match &item.args { ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, tokens }) if !tokens.is_empty() => { check_cfg_attr_bad_delim(&sess.psess, *dspan, *delim); match parse_in(&sess.psess, tokens.clone(), "`cfg_attr` input", |p| { @@ -329,11 +330,11 @@ pub fn parse_cfg_attr( Err(e) => { let suggestions = CFG_ATTR_TEMPLATE.suggestions( ParsedDescription::Attribute, - cfg_attr.get_normal_item().unsafety, + item.unsafety, sym::cfg_attr, ); e.with_span_suggestions( - cfg_attr.get_normal_item().span, + item.span, "must be of the form", suggestions, Applicability::HasPlaceholders, @@ -347,25 +348,24 @@ pub fn parse_cfg_attr( } } _ => { - let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) = - cfg_attr.get_normal_item().args - { - (dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument) - } else { - (cfg_attr.get_normal_item().span, AttributeParseErrorReason::ExpectedList) - }; + let (span, reason) = + if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) = item.args { + (dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument) + } else { + (item.span, AttributeParseErrorReason::ExpectedList) + }; sess.dcx().emit_err(AttributeParseError { span, - inner_span: cfg_attr.get_normal_item().span, + inner_span: item.span, template: CFG_ATTR_TEMPLATE, - path: AttrPath::from_ast(&cfg_attr.get_normal_item().path, identity), + path: AttrPath::from_ast(&item.path, identity), description: ParsedDescription::Attribute, reason, suggestions: session_diagnostics::AttributeParseErrorSuggestions::CreatedByTemplate( CFG_ATTR_TEMPLATE.suggestions( ParsedDescription::Attribute, - cfg_attr.get_normal_item().unsafety, + item.unsafety, sym::cfg_attr, ), ), @@ -392,7 +392,7 @@ fn parse_cfg_attr_internal<'a>( features: Option<&Features>, lint_node_id: ast::NodeId, attribute: &Attribute, -) -> PResult<'a, (CfgEntry, Vec<(WithTokens, Span)>)> { +) -> PResult<'a, (CfgEntry, Vec)> { // Parse cfg predicate let pred_start = parser.token.span; let meta = MetaItemOrLitParser::parse_single( @@ -402,13 +402,14 @@ fn parse_cfg_attr_internal<'a>( )?; let pred_span = pred_start.with_hi(parser.token.span.hi()); + let item = attribute.get_normal_item(); let cfg_predicate = AttributeParser::parse_single_args( sess, attribute.span, - attribute.get_normal_item().span, + item.span, attribute.style, AttrPath { segments: attribute.path().into_boxed_slice(), span: attribute.span }, - Some(attribute.get_normal_item().unsafety), + Some(item.unsafety), AttributeSafety::Normal, ParsedDescription::Attribute, pred_span, @@ -434,9 +435,8 @@ fn parse_cfg_attr_internal<'a>( // Presumably, the majority of the time there will only be one attr. let mut expanded_attrs = Vec::with_capacity(1); while parser.token != token::Eof { - let lo = parser.token.span; - let item = parser.parse_attr_item(ForceCollect::Yes)?; - expanded_attrs.push((item, lo.to(parser.prev_token.span))); + let item = parser.parse_attr_item()?; + expanded_attrs.push(item); if !parser.eat(exp!(Comma)) { break; } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 6254dd73f3263..e5b322ccf2c6c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -375,7 +375,6 @@ pub struct AcceptContext<'f, 'sess> { /// #[attribute(...)] /// ^^^^^^^^^^^^^^^^^ outer span /// ``` - /// For attributes in `cfg_attr`, the outer span and inner spans are equal. pub(crate) attr_span: Span, /// The inner span of the attribute currently being parsed. /// @@ -1135,6 +1134,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { pub(crate) fn suggestions(&self) -> Vec { self.template.suggestions(self.parsed_description, self.attr_safety, &self.attr_path) } + /// Error that a string literal was expected. /// You can optionally give the literal you did find (which you found not to be a string literal) /// which can make better errors. For example, if the literal was a byte string it will suggest diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index cea549e310476..8558d71fc4de6 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -363,17 +363,17 @@ impl<'sess> AttributeParser<'sess> { synthetic_attr_state.accept_synthetic_attr(attr_span, lower_span, synthetic); } ast::AttrKind::Normal(n) => { - attr_paths.push(PathParser(&n.item.path)); - let attr_path = AttrPath::from_ast(&n.item.path, lower_span); + attr_paths.push(PathParser(&n.path)); + let attr_path = AttrPath::from_ast(&n.path, lower_span); let parts = - n.item.path.segments.iter().map(|seg| seg.ident.name).collect::>(); - let inner_span = lower_span(n.item.span); + n.path.segments.iter().map(|seg| seg.ident.name).collect::>(); + let inner_span = lower_span(n.span); if let Some(accept) = ATTRIBUTE_PARSERS.accepters.get(parts.as_slice()) { self.check_attribute_safety( &attr_path, inner_span, - n.item.unsafety, + n.unsafety, accept.safety, &mut emit_lint, ); @@ -383,7 +383,7 @@ impl<'sess> AttributeParser<'sess> { } let Some(args) = ArgParser::from_attr_args( - &n.item.args, + &n.args, &parts, &self.sess.psess, self.should_emit, @@ -437,7 +437,7 @@ impl<'sess> AttributeParser<'sess> { attr_style: attr.style, parsed_description: ParsedDescription::Attribute, template: &accept.template, - attr_safety: n.item.unsafety, + attr_safety: n.unsafety, attr_path: attr_path.clone(), #[cfg(debug_assertions)] has_target_been_checked: false, @@ -454,7 +454,7 @@ impl<'sess> AttributeParser<'sess> { } else { let attr = AttrItem { path: attr_path.clone(), - args: self.lower_attr_args(&n.item.args, lower_span), + args: self.lower_attr_args(&n.args, lower_span), id: HashIgnoredAttrId { attr_id: attr.id }, style: attr.style, span: attr_span, @@ -463,7 +463,7 @@ impl<'sess> AttributeParser<'sess> { self.check_attribute_safety( &attr_path, inner_span, - n.item.unsafety, + n.unsafety, AttributeSafety::Normal, &mut emit_lint, ); diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 53fa0a2fb9293..bd61a6bd82ac2 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -287,7 +287,7 @@ pub(crate) struct UnknownVersionLiteral { #[diag("multiple `{$name}` attributes")] pub(crate) struct UnusedMultiple { #[primary_span] - #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "maybe-incorrect")] pub this: Span, #[note("attribute also specified here")] pub other: Span, @@ -1113,7 +1113,7 @@ pub(crate) struct AdditionalCommaSuggestion { #[derive(Diagnostic)] #[diag("unused attribute")] pub(crate) struct UnusedDuplicate { - #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "maybe-incorrect")] pub this: Span, #[note("attribute also specified here")] pub other: Span, diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 51ab44d8a03ef..ad9872c6b6711 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -27,7 +27,7 @@ mod llvm_enzyme { use crate::diagnostics; pub(crate) fn outer_normal_attr( - kind: &Box, + kind: &Box, id: rustc_ast::AttrId, span: Span, ) -> rustc_ast::Attribute { @@ -347,7 +347,7 @@ mod llvm_enzyme { eii_impls: ThinVec::new(), }); let mut rustc_ad_attr = - Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_autodiff))); + Box::new(ast::AttrItem::from_ident(Ident::with_dummy_span(sym::rustc_autodiff))); let ts2: Vec = vec![TokenTree::Token( Token::new(TokenKind::Ident(sym::never, false.into()), span), @@ -358,13 +358,12 @@ mod llvm_enzyme { delim: ast::token::Delimiter::Parenthesis, tokens: TokenStream::from_iter(ts2), }; - let inline_item = ast::AttrItem { - unsafety: ast::Safety::Default, - path: ast::Path::from_ident(Ident::with_dummy_span(sym::inline)), - args: ast::AttrArgs::Delimited(never_arg), - span: DUMMY_SP, - }; - let inline_never_attr = Box::new(ast::NormalAttr { item: inline_item, tokens: None }); + let inline_never_attr = Box::new(ast::AttrItem::new( + ast::Safety::Default, + ast::Path::from_ident(Ident::with_dummy_span(sym::inline)), + ast::AttrArgs::Delimited(never_arg), + DUMMY_SP, + )); let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id(); let attr = outer_normal_attr(&rustc_ad_attr, new_id, span); let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id(); @@ -374,8 +373,8 @@ mod llvm_enzyme { fn same_attribute(attr: &ast::AttrKind, item: &ast::AttrKind) -> bool { match (attr, item) { (ast::AttrKind::Normal(a), ast::AttrKind::Normal(b)) => { - let a = &a.item.path; - let b = &b.item.path; + let a = &a.path; + let b = &b.path; a.segments.iter().eq_by(&b.segments, |a, b| a.ident == b.ident) } _ => false, @@ -424,7 +423,7 @@ mod llvm_enzyme { } }; // Now update for d_fn - rustc_ad_attr.item.args = rustc_ast::AttrArgs::Delimited(rustc_ast::DelimArgs { + rustc_ad_attr.args = rustc_ast::AttrArgs::Delimited(rustc_ast::DelimArgs { dspan: DelimSpan::dummy(), delim: rustc_ast::token::Delimiter::Parenthesis, tokens: ts, diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index cc036fab83c9d..f81eb3f79e15d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -804,10 +804,10 @@ impl<'a> TraitDef<'a> { if self.is_const && self.is_staged_api_crate { attrs.push( cx.attr_nested( - rustc_ast::AttrItem { - unsafety: Safety::Default, - path: rustc_const_unstable, - args: AttrArgs::Delimited(DelimArgs { + rustc_ast::AttrItem::new( + Safety::Default, + rustc_const_unstable, + AttrArgs::Delimited(DelimArgs { dspan: DelimSpan::from_single(self.span), delim: rustc_ast::token::Delimiter::Parenthesis, tokens: [ @@ -825,8 +825,8 @@ impl<'a> TraitDef<'a> { }) .collect(), }), - span: self.span, - }, + self.span, + ), self.span, ), ) diff --git a/compiler/rustc_builtin_macros/src/offload.rs b/compiler/rustc_builtin_macros/src/offload.rs index cdb3ba22ec6c8..5fafc89819c71 100644 --- a/compiler/rustc_builtin_macros/src/offload.rs +++ b/compiler/rustc_builtin_macros/src/offload.rs @@ -12,7 +12,7 @@ fn compile_for_device(ecx: &mut ExtCtxt<'_>) -> bool { ecx.sess.opts.unstable_opts.offload.contains(&Offload::Device) } -fn outer_normal_attr(normal: &Box, id: ast::AttrId, span: Span) -> ast::Attribute { +fn outer_normal_attr(normal: &Box, id: ast::AttrId, span: Span) -> ast::Attribute { let style = ast::AttrStyle::Outer; let kind = ast::AttrKind::Normal(normal.clone()); ast::Attribute { kind, id, style, span } @@ -103,7 +103,7 @@ pub(crate) fn expand_kernel( // rustc_offload_kernel attr let rustc_offload_kernel_attr = - Box::new(ast::NormalAttr::from_ident(Ident::with_dummy_span(sym::rustc_offload_kernel))); + Box::new(ast::AttrItem::from_ident(Ident::with_dummy_span(sym::rustc_offload_kernel))); let rustc_offload_kernel = outer_normal_attr( &rustc_offload_kernel_attr, ecx.sess.psess.attr_id_generator.mk_attr_id(), @@ -111,14 +111,13 @@ pub(crate) fn expand_kernel( ); // unsafe(no_mangle) attr - let unsafe_item = AttrItem { - unsafety: ast::Safety::Unsafe(span), - path: ast::Path::from_ident(Ident::new(sym::no_mangle, span)), - args: ast::AttrArgs::Empty, + let no_mangle_attr = Box::new(AttrItem::new( + ast::Safety::Unsafe(span), + ast::Path::from_ident(Ident::new(sym::no_mangle, span)), + ast::AttrArgs::Empty, span, - }; + )); - let no_mangle_attr = Box::new(ast::NormalAttr { item: unsafe_item, tokens: None }); let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id(); let unsafe_no_mangle = outer_normal_attr(&no_mangle_attr, new_id, span); @@ -176,13 +175,12 @@ pub(crate) fn expand_kernel( tokens: TokenStream::from_iter(ts), }; - let inline_item = ast::AttrItem { - unsafety: ast::Safety::Default, - path: ast::Path::from_ident(Ident::with_dummy_span(sym::inline)), - args: ast::AttrArgs::Delimited(never_arg), - span: DUMMY_SP, - }; - let inline_never_attr = Box::new(ast::NormalAttr { item: inline_item, tokens: None }); + let inline_never_attr = Box::new(ast::AttrItem::new( + ast::Safety::Default, + ast::Path::from_ident(Ident::with_dummy_span(sym::inline)), + ast::AttrArgs::Delimited(never_arg), + DUMMY_SP, + )); let new_id = ecx.sess.psess.attr_id_generator.mk_attr_id(); let inline_never = outer_normal_attr(&inline_never_attr, new_id, span); diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index b87dfd0198efc..326ad6758da86 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -765,6 +765,6 @@ impl<'a> ExtCtxt<'a> { // Builds an attribute fully manually. pub fn attr_nested(&self, inner: AttrItem, span: Span) -> ast::Attribute { let g = &self.sess.psess.attr_id_generator; - attr::mk_attr_from_item(g, inner, None, ast::AttrStyle::Outer, span) + attr::mk_attr_from_item(g, inner, ast::AttrStyle::Outer, span) } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index af017bea67697..075baacf94c58 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -3,10 +3,8 @@ use std::iter; use rustc_ast::attr::data_structures::CfgEntry; -use rustc_ast::token::{Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::{ - AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, WithTokens, -}; +use rustc_ast::token::Token; +use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, LazyAttrTokenStream}; use rustc_ast::{ self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId, SyntheticAttr, @@ -29,7 +27,7 @@ use rustc_hir::{ use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::diagnostics::feature_err; -use rustc_span::{STDLIB_STABLE_CRATES, Span, Symbol, sym}; +use rustc_span::{STDLIB_STABLE_CRATES, Symbol, sym}; use tracing::instrument; use crate::diagnostics::{ @@ -301,58 +299,18 @@ impl<'a> StripUnconfigured<'a> { fn expand_cfg_attr_item( &self, cfg_attr: &Attribute, - (attr_item, attr_item_span): (WithTokens, Span), + mut attr_item: ast::AttrItem, ) -> Attribute { // Convert `#[cfg_attr(pred, attr)]` to `#[attr]`. - - // Use the `#` from `#[cfg_attr(pred, attr)]` in the result `#[attr]`. - let mut orig_trees = cfg_attr.token_trees().into_iter(); - let Some(TokenTree::Token(pound_token @ Token { kind: TokenKind::Pound, .. }, _)) = - orig_trees.next() - else { - panic!("Bad tokens for attribute {cfg_attr:?}"); - }; - - // For inner attributes, we do the same thing for the `!` in `#![attr]`. - let mut trees = if cfg_attr.style == AttrStyle::Inner { - let Some(TokenTree::Token(bang_token @ Token { kind: TokenKind::Bang, .. }, _)) = - orig_trees.next() - else { - panic!("Bad tokens for attribute {cfg_attr:?}"); - }; - vec![ - AttrTokenTree::Token(pound_token, Spacing::Joint), - AttrTokenTree::Token(bang_token, Spacing::JointHidden), - ] - } else { - vec![AttrTokenTree::Token(pound_token, Spacing::JointHidden)] - }; - - // And the same thing for the `[`/`]` delimiters in `#[attr]`. - let Some(TokenTree::Delimited(delim_span, delim_spacing, Delimiter::Bracket, _)) = - orig_trees.next() - else { - panic!("Bad tokens for attribute {cfg_attr:?}"); - }; - trees.push(AttrTokenTree::Delimited( - delim_span, - delim_spacing, - Delimiter::Bracket, - attr_item - .tokens - .as_ref() - .unwrap_or_else(|| panic!("Missing tokens for {:?}", attr_item.node)) - .to_attr_token_stream(), - )); - - let attr_item_path_span = attr_item.node.path.span; - let attr_tokens = Some(LazyAttrTokenStream::new_direct(AttrTokenStream::new(trees))); + attr_item.from_cfg_attr = true; + attr_item.use_precise_delim_token_spans = + cfg_attr.get_normal_item().use_precise_delim_token_spans; + let attr_item_path_span = attr_item.path.span; let attr = ast::attr::mk_attr_from_item( &self.sess.psess.attr_id_generator, - attr_item.node, - attr_tokens, + attr_item, cfg_attr.style, - attr_item_span, + cfg_attr.span, ); if attr.has_name(sym::crate_type) { self.sess.dcx().emit_err(CrateTypeInCfgAttr { span: attr_item_path_span }); diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 187dcea4f91a5..279ee7bd1ec00 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2253,7 +2253,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { match &attr.kind { AttrKind::Normal(normal) - if rustc_attr_parsing::is_builtin_attr(&normal.item) + if rustc_attr_parsing::is_builtin_attr(normal) && !AttributeParser::is_parsed_attribute(&attr.path()) => { let attr_name = attr.name().unwrap(); diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index eabec05cd66c6..a84422875ace5 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -545,11 +545,12 @@ fn transcribe_pnr<'tx>( mk_delimited(ty.node.span, MetaVarKind::Ty { is_path }, TokenStream::from_ast(ty)) } ParseNtResult::Meta(attr_item) => { - let has_meta_form = attr_item.node.meta_kind().is_some(); + // `AttrItem` is different: we synthesize tokens rather than collecting them. + let has_meta_form = attr_item.meta_kind().is_some(); mk_delimited( - attr_item.node.span, + attr_item.span, MetaVarKind::Meta { has_meta_form }, - TokenStream::from_ast(attr_item), + TokenStream::new(attr_item.token_trees()), ) } ParseNtResult::Path(path) => { diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 9b8398f755f64..e440e5bcf0039 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -754,7 +754,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & while let Some(attr) = attrs.next() { let (is_doc_comment, is_doc_attribute) = match &attr.kind { AttrKind::DocComment(..) => (true, false), - AttrKind::Normal(normal) if normal.item.path == sym::doc => (true, true), + AttrKind::Normal(normal) if normal.path == sym::doc => (true, true), _ => (false, false), }; if is_doc_comment { diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index 539c15f18a9a4..8fdc743015858 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -316,6 +316,14 @@ fn fake_token_stream_for_file_mod( let attr = attr_to_exclude.expect("file modules must have an attribute to exclude"); assert_eq!(attr.style, ast::AttrStyle::Inner); + // If `attr` is from a `cfg_attr`, cutting it out via `attr.span` will also cut out any sibling + // attrs expanded from the same `cfg_attr`. Bail out because that would be invalid. (It's also + // conceptually reasonable because once `cfg_attr` is involved the AST no longer exactly + // matches the original source code.) + if attr.get_normal_item().from_cfg_attr { + return None; + } + let mut body_tts = Vec::new(); body_tts.extend(lex_token_trees_for_span(psess, spans.inner_span.until(attr.span))?); body_tts.extend(lex_token_trees_for_span( diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index fae58c29954d0..d91dc6c4d37e7 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -1,6 +1,6 @@ use rustc_ast as ast; use rustc_ast::token::{self, MetaVarKind}; -use rustc_ast::tokenstream::{ParserRange, WithTokens}; +use rustc_ast::tokenstream::ParserRange; use rustc_ast::{Attribute, attr}; use rustc_errors::codes::*; use rustc_errors::{Diag, PResult, msg}; @@ -10,7 +10,6 @@ use tracing::debug; use super::{ AllowConstBlockItems, AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle, - Trailing, UsePreAttrPos, }; use crate::parser::FnContext; use crate::{diagnostics, exp}; @@ -122,60 +121,76 @@ impl<'a> Parser<'a> { inner_parse_policy, self.token ); let lo = self.token.span; - // Attributes can't have attributes of their own [Editor's note: not with that attitude] - self.collect_tokens_no_attrs(|this| { - let pound_hi = this.token.span.hi(); - assert!(this.eat(exp!(Pound)), "parse_attribute called in non-attribute position"); - - let not_lo = this.token.span.lo(); - let style = - if this.eat(exp!(Bang)) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer }; - - let mut bracket_res = this.expect(exp!(OpenBracket)); - // If `#!` is not followed by `[` - if let Err(err) = &mut bracket_res - && style == ast::AttrStyle::Inner - && pound_hi == not_lo - { - err.note( - "the token sequence `#!` here looks like the start of \ - a shebang interpreter directive but it is not", - ); - err.help( - "if you meant this to be a shebang interpreter directive, \ - move it to the very start of the file", - ); - } - bracket_res?; - - let attr_item = this.parse_attr_item(ForceCollect::No)?; - // `attr_item` will never have tokens: within `parse_attr_item`, `collect_tokens` - // attaches tokens only if: - // - `ForceCollect::Yes` is passed (not true), or - // - attributes on the parsed node require tokens (not true, because attr items can't - // have attributes of their own, hence the empty `HasAttrs` impl for `AttrItem`). - assert!(attr_item.tokens.is_none()); - - this.expect(exp!(CloseBracket))?; - let attr_sp = lo.to(this.prev_token.span); - - // Emit error if inner attribute is encountered and forbidden. - if style == ast::AttrStyle::Inner { - this.error_on_forbidden_inner_attr( - attr_sp, - inner_parse_policy, - attr_item.node.is_valid_for_outer_style(), - ); - } + assert!(self.eat(exp!(Pound)), "parse_attribute called in non-attribute position"); + + let not_lo = self.token.span.lo(); + let (style, bang_span) = if self.eat(exp!(Bang)) { + (ast::AttrStyle::Inner, Some(self.prev_token.span)) + } else { + (ast::AttrStyle::Outer, None) + }; + + let mut bracket_res = self.expect(exp!(OpenBracket)); + // If `#!` is not followed by `[` + if let Err(err) = &mut bracket_res + && style == ast::AttrStyle::Inner + && lo.hi() == not_lo + { + err.note( + "the token sequence `#!` here looks like the start of \ + a shebang interpreter directive but it is not", + ); + err.help( + "if you meant this to be a shebang interpreter directive, \ + move it to the very start of the file", + ); + } + bracket_res?; + let open_span = self.prev_token.span; + + let mut attr_item = self.parse_attr_item()?; + + self.expect(exp!(CloseBracket))?; + let close_span = self.prev_token.span; + let attr_sp = lo.to(self.prev_token.span); - Ok(attr::mk_attr_from_item( - &self.psess.attr_id_generator, - attr_item.node, - None, - style, + // Emit error if inner attribute is encountered and forbidden. + if style == ast::AttrStyle::Inner { + self.error_on_forbidden_inner_attr( attr_sp, - )) - }) + inner_parse_policy, + attr_item.is_valid_for_outer_style(), + ); + } + + // Determine if the parsed attribute has the "standard form" of `#[..]` or `#![..]`. + // - If so, `Attribute::token_trees` can perfectly reconstruct the spans of the + // `#`/`!`/`[`/`]` tokens with byte arithmetic on `Attribute::span`. + // - If not, `Attribute::token_trees` will give those tokens less precise spans. + // + // Cases that aren't "standard form" include the following. + // - Parsed attributes with any additional whitespace (e.g. `# ! [ foo ]`). + // - `doc` attributes desugared from doc comments in `macro_rules!` arguments, where every + // token gets the whole comment's span. + // - When the tokens have mismatched syntax contexts (e.g. `#[$meta]`). + // - Attributes produced by `cfg_attr` expansion. + // - Compiler-generated attributes. + let prefix_len: u32 = match style { + ast::AttrStyle::Outer => 1, // `#` + ast::AttrStyle::Inner => 2, // `#!` + }; + let attr_lo = attr_sp.lo(); + attr_item.use_precise_delim_token_spans = lo == attr_sp.with_hi(attr_lo + BytePos(1)) + && bang_span.is_none_or(|bang_span| { + bang_span == attr_sp.with_lo(attr_lo + BytePos(1)).with_hi(attr_lo + BytePos(2)) + }) + && open_span + == attr_sp + .with_lo(attr_lo + BytePos(prefix_len)) + .with_hi(attr_lo + BytePos(prefix_len + 1)) + && close_span == attr_sp.with_lo(attr_sp.hi() - BytePos(1)); + + Ok(attr::mk_attr_from_item(&self.psess.attr_id_generator, attr_item, style, attr_sp)) } fn annotate_following_item_if_applicable( @@ -316,41 +331,31 @@ impl<'a> Parser<'a> { /// PATH /// PATH `=` UNSUFFIXED_LIT /// The delimiters or `=` are still put into the resulting token stream. - pub fn parse_attr_item( - &mut self, - force_collect: ForceCollect, - ) -> PResult<'a, WithTokens> { + pub fn parse_attr_item(&mut self) -> PResult<'a, ast::AttrItem> { if let Some(item) = self.eat_metavar_seq_with_matcher( |mv_kind| matches!(mv_kind, MetaVarKind::Meta { .. }), - |this| this.parse_attr_item(force_collect), + |this| this.parse_attr_item(), ) { return Ok(item); } - // Attr items don't have attributes. - self.collect_tokens(None, AttrWrapper::empty(), force_collect, |this, _empty_attrs| { - let lo = this.token.span; - let is_unsafe = this.eat_keyword(exp!(Unsafe)); - let unsafety = if is_unsafe { - let unsafe_span = this.prev_token.span; - this.expect(exp!(OpenParen))?; - ast::Safety::Unsafe(unsafe_span) - } else { - ast::Safety::Default - }; + let lo = self.token.span; + let is_unsafe = self.eat_keyword(exp!(Unsafe)); + let unsafety = if is_unsafe { + let unsafe_span = self.prev_token.span; + self.expect(exp!(OpenParen))?; + ast::Safety::Unsafe(unsafe_span) + } else { + ast::Safety::Default + }; - let path = this.parse_path(PathStyle::Mod)?; - let args = this.parse_attr_args()?; - if is_unsafe { - this.expect(exp!(CloseParen))?; - } - let span = lo.to(this.prev_token.span); - Ok(( - WithTokens::new(ast::AttrItem { unsafety, path, args, span }), - Trailing::No, - UsePreAttrPos::No, - )) - }) + let path = self.parse_path(PathStyle::Mod)?; + let args = self.parse_attr_args()?; + if is_unsafe { + self.expect(exp!(CloseParen))?; + } + let span = lo.to(self.prev_token.span); + Ok(ast::AttrItem::new(unsafety, path, args, span)) } /// Parses attributes that appear after the opening of an item. These should @@ -434,10 +439,9 @@ impl<'a> Parser<'a> { return if has_meta_form { let attr_item = self .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| { - this.parse_attr_item(ForceCollect::No) + this.parse_attr_item() }) - .unwrap() - .node; + .unwrap(); Ok(attr_item.meta(attr_item.path.span).unwrap()) } else { self.unexpected_any() diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index fe34d9951dc5f..5b3f5d7b6b367 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -411,7 +411,7 @@ fn needs_tokens(attrs: &[ast::Attribute]) -> bool { // Tokens are needed if... attrs.iter().any(|attr| match &attr.kind { AttrKind::Normal(normal) => { - match normal.item.name() { + match normal.name() { // ... a multi-segment attribute is present, e.g. `rustfmt::skip`. None => true, // ... `cfg_attr` or a single-segment non-builtin attribute is present, e.g. diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 3064c0b22592d..bfeee3d680e12 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -859,9 +859,9 @@ impl<'a> Parser<'a> { let mut snapshot = self.create_snapshot_for_diagnostic(); if let [attr] = &expr.attrs[..] && let ast::AttrKind::Normal(attr_kind) = &attr.kind - && let [segment] = &attr_kind.item.path.segments[..] + && let [segment] = &attr_kind.path.segments[..] && segment.ident.name == sym::cfg - && let Some(args_span) = attr_kind.item.args.span() + && let Some(args_span) = attr_kind.args.span() && let next_attr = match snapshot.parse_attribute(InnerAttrPolicy::Forbidden(None)) { Ok(next_attr) => next_attr, @@ -871,8 +871,8 @@ impl<'a> Parser<'a> { } } && let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind - && let Some(next_attr_args_span) = next_attr_kind.item.args.span() - && let [next_segment] = &next_attr_kind.item.path.segments[..] + && let Some(next_attr_args_span) = next_attr_kind.args.span() + && let [next_segment] = &next_attr_kind.path.segments[..] && next_segment.ident.name == sym::cfg { let next_expr = match snapshot.parse_expr() { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index ef62316abf941..fb61be27c32e4 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -1828,8 +1828,7 @@ pub enum ParseNtResult { Expr(Box, NtExprKind), Literal(Box), Ty(WithTokens>), - // These tokens are for the attr item, e.g. just the `foo` within `#[foo]` or `#![foo]`. - Meta(WithTokens>), + Meta(Box), Path(WithTokens>), Vis(WithTokens>), Guard(Box), diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 9f9545c194082..254cba1e02343 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -189,9 +189,7 @@ impl<'a> Parser<'a> { this.parse_path(PathStyle::Type).map(|path| WithTokens::new(Box::new(path))) })?)) } - NonterminalKind::Meta => Ok(ParseNtResult::Meta( - self.parse_attr_item(ForceCollect::Yes)?.map(|item| Box::new(item)), - )), + NonterminalKind::Meta => Ok(ParseNtResult::Meta(Box::new(self.parse_attr_item()?))), NonterminalKind::Vis => { Ok(ParseNtResult::Vis(self.collect_tokens_no_attrs(|this| { this.parse_visibility(FollowedByType::Yes) diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 067101a6446a1..325a5b7fd932a 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -508,7 +508,7 @@ impl<'a> Parser<'a> { let (attrs, block) = self.parse_inner_attrs_and_block(None)?; if let [.., last] = &*attrs { let suggest_to_outer = match &last.kind { - ast::AttrKind::Normal(attr) => attr.item.is_valid_for_outer_style(), + ast::AttrKind::Normal(attr) => attr.is_valid_for_outer_style(), _ => false, }; self.error_on_forbidden_inner_attr( diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 63bff7a3f4498..360a0402701db 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -559,10 +559,8 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { let orig_in_attr = mem::replace(&mut self.invocation_parent.in_attr, true); match &attr.kind { AttrKind::Normal(normal) => { - if attr::is_builtin_attr(&normal.item) { - self.r - .builtin_attrs - .push((normal.item.path.segments[0].ident, self.parent_scope)); + if attr::is_builtin_attr(normal) { + self.r.builtin_attrs.push((normal.path.segments[0].ident, self.parent_scope)); } } AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => {} diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 851d605c36be5..e7d0c7eeac1e6 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -21,7 +21,6 @@ impl From<&AttrKind> for SimpleAttrKind { match value { AttrKind::Normal(attr) => { let path_symbols = attr - .item .path .segments .iter() diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 2833619814cc3..d761c5fa12fa0 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -615,7 +615,7 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { if attr.has_name(sym::ignore) && let AttrKind::Normal(normal_attr) = &attr.kind - && !matches!(normal_attr.item.args, AttrArgs::Eq { .. }) + && !matches!(normal_attr.args, AttrArgs::Eq { .. }) { span_lint_and_help( cx, 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 fd27e30a67f3b..1c916734efc2c 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 @@ -9,12 +9,12 @@ use rustc_span::sym; pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) { if let AttrKind::Normal(normal_attr) = &attr.kind { - if let AttrArgs::Eq { .. } = &normal_attr.item.args { + if let AttrArgs::Eq { .. } = &normal_attr.args { // `#[should_panic = ".."]` found, good return; } - if let AttrArgs::Delimited(args) = &normal_attr.item.args + if let AttrArgs::Delimited(args) = &normal_attr.args && let mut tt_iter = args.tokens.iter() && let Some(TokenTree::Token( Token { diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs index 509b345048c1e..3c7d477daf0ba 100644 --- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs +++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs @@ -73,7 +73,7 @@ impl EarlyLintPass for CrateInMacroDef { fn is_macro_export(attr: &Attribute) -> bool { if let AttrKind::Normal(normal) = &attr.kind - && let [segment] = normal.item.path.segments.as_slice() + && let [segment] = normal.path.segments.as_slice() { segment.ident.name == sym::macro_export } else { 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 0a4ef50f14bb3..2389b4eaa91b3 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 @@ -11,7 +11,7 @@ pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { if !attr.span.from_expansion() && let AttrKind::Normal(ref normal) = attr.kind && attr.doc_str().is_some() - && let AttrArgs::Eq { expr: meta, .. } = &normal.item.args + && let AttrArgs::Eq { expr: meta, .. } = &normal.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/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs index 3c35c2fc9c82d..bd65f4751cc1e 100644 --- a/src/tools/clippy/clippy_lints/src/large_include_file.rs +++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs @@ -92,7 +92,7 @@ impl EarlyLintPass for LargeIncludeFile { && let AttrKind::Normal(ref normal) = attr.kind && let Some(doc) = attr.doc_str() && doc.as_str().len() as u64 > self.max_file_size - && let AttrArgs::Eq { expr: meta, .. } = &normal.item.args + && let AttrArgs::Eq { expr: meta, .. } = &normal.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_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 3f0b99aa4d780..eb8868e6dbcc5 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -995,7 +995,7 @@ fn eq_attr(l: &Attribute, r: &Attribute) -> bool { l.style == r.style && match (&l.kind, &r.kind) { (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2, - (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_args(&l.item.args, &r.item.args), + (Normal(l), Normal(r)) => eq_path(&l.path, &r.path) && eq_attr_args(&l.args, &r.args), (Synthetic(..), _) | (_, Synthetic(..)) => unreachable!(), _ => false, } diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs index ac9ce2e87963e..c28fe58a76cd6 100644 --- a/src/tools/rustfmt/src/attr.rs +++ b/src/tools/rustfmt/src/attr.rs @@ -368,7 +368,7 @@ impl Rewrite for ast::Attribute { Ok(meta.rewrite_result(context, shape).map_or_else( |_| snippet.to_owned(), |rw| match &self.kind { - ast::AttrKind::Normal(normal_attr) => match normal_attr.item.unsafety { + ast::AttrKind::Normal(normal_attr) => match normal_attr.unsafety { // For #![feature(unsafe_attributes)] // See https://github.com/rust-lang/rust/issues/123757 ast::Safety::Unsafe(_) => format!("{}[unsafe({})]", prefix, rw), diff --git a/src/tools/rustfmt/src/parse/macros/cfg_select.rs b/src/tools/rustfmt/src/parse/macros/cfg_select.rs index 040447ff1898f..cccb66377a4e7 100644 --- a/src/tools/rustfmt/src/parse/macros/cfg_select.rs +++ b/src/tools/rustfmt/src/parse/macros/cfg_select.rs @@ -34,7 +34,7 @@ fn parse_cfg_select_inner<'a>( while parser.token.kind != TokenKind::Eof { if !parser.eat_keyword(exp!(Underscore)) { - parser.parse_attr_item(ForceCollect::No).map_err(|e| { + parser.parse_attr_item().map_err(|e| { e.cancel(); "Failed to parse attr item" })?; diff --git a/src/tools/rustfmt/src/skip.rs b/src/tools/rustfmt/src/skip.rs index cd3860d3d7f0a..4b69519e0513a 100644 --- a/src/tools/rustfmt/src/skip.rs +++ b/src/tools/rustfmt/src/skip.rs @@ -110,7 +110,7 @@ fn get_skip_names(kind: &str, attrs: &[ast::Attribute]) -> Vec { // rustc_ast::ast::Path is implemented partialEq // but it is designed for segments.len() == 1 if let ast::AttrKind::Normal(normal) = &attr.kind { - if pprust::path_to_string(&normal.item.path) != path { + if pprust::path_to_string(&normal.path) != path { continue; } } diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index 55f9a4d8c8b26..87ceb733e525d 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -882,7 +882,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } else { match &attr.kind { ast::AttrKind::Normal(ref normal) - if self.is_unknown_rustfmt_attr(&normal.item.path.segments) => + if self.is_unknown_rustfmt_attr(&normal.path.segments) => { let file_name = self.psess.span_to_filename(attr.span); self.report.append( diff --git a/tests/run-make/const-trait-stable-toolchain/const-super-trait-stable-enabled.stderr b/tests/run-make/const-trait-stable-toolchain/const-super-trait-stable-enabled.stderr index 263532f3a9400..b1602c183bc4b 100644 --- a/tests/run-make/const-trait-stable-toolchain/const-super-trait-stable-enabled.stderr +++ b/tests/run-make/const-trait-stable-toolchain/const-super-trait-stable-enabled.stderr @@ -11,10 +11,10 @@ note: this trait is not `const`, so it cannot have `[const]` trait bounds | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0554]: `#![feature]` may not be used on the NIGHTLY release channel - --> const-super-trait.rs:1:30 + --> const-super-trait.rs:1:1 | 1 | #![cfg_attr(feature_enabled, feature(const_trait_impl))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `[const]` can only be applied to `const` traits --> const-super-trait.rs:7:12 diff --git a/tests/ui-fulldeps/auxiliary/parser.rs b/tests/ui-fulldeps/auxiliary/parser.rs index 6ee39e5130f68..85161d4f1f930 100644 --- a/tests/ui-fulldeps/auxiliary/parser.rs +++ b/tests/ui-fulldeps/auxiliary/parser.rs @@ -51,33 +51,6 @@ impl MutVisitor for Normalize { fn visit_attribute(&mut self, attr: &mut Attribute) { attr.id = AttrId::from_u32(0); - if let AttrKind::Normal(normal_attr) = &mut attr.kind { - if let Some(tokens) = &mut normal_attr.tokens { - let mut stream = tokens.to_attr_token_stream(); - normalize_attr_token_stream(&mut stream); - *tokens = LazyAttrTokenStream::new_direct(stream); - } - } mut_visit::walk_attribute(self, attr); } } - -fn normalize_attr_token_stream(stream: &mut AttrTokenStream) { - Arc::make_mut(&mut stream.0) - .iter_mut() - .for_each(normalize_attr_token_tree); -} - -fn normalize_attr_token_tree(token: &mut AttrTokenTree) { - match token { - AttrTokenTree::Token(token, _spacing) => { - Normalize.visit_span(&mut token.span); - } - AttrTokenTree::Delimited(dspan, _spacing, _delim, stream) => { - normalize_attr_token_stream(stream); - Normalize.visit_span(&mut dspan.open); - Normalize.visit_span(&mut dspan.close); - } - AttrTokenTree::AttrsTarget(_) => unimplemented!("AttrTokenTree::AttrsTarget"), - } -} diff --git a/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.rs b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.rs new file mode 100644 index 0000000000000..30434569dbe73 --- /dev/null +++ b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.rs @@ -0,0 +1,34 @@ +// Test for handling of duplicated attributes within `cfg_attr`, in particular the suggestions of +// what to remove. + +#[inline] +#[inline] +//~^ WARN unused attribute +//~| WARN this was previously accepted +fn f1() {} + +#[deprecated] +#[deprecated] +//~^ ERROR multiple `deprecated` attributes +fn f2() {} + +#[inline] +#[cfg_attr(true, inline)] +//~^ WARN unused attribute +//~| WARN this was previously accepted +fn f3() {} + +#[deprecated] +#[cfg_attr(true, deprecated)] +//~^ ERROR multiple `deprecated` attributes +fn f4() {} + +#[inline] +#[deprecated] +#[cfg_attr(true, inline, deprecated)] +//~^ WARN unused attribute +//~| WARN this was previously accepted +//~| ERROR multiple `deprecated` attributes +fn f5() {} + +fn main() {} diff --git a/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.stderr b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.stderr new file mode 100644 index 0000000000000..a133a82d4d220 --- /dev/null +++ b/tests/ui/conditional-compilation/cfg-attr-duplicate-attrs.stderr @@ -0,0 +1,78 @@ +error: multiple `deprecated` attributes + --> $DIR/cfg-attr-duplicate-attrs.rs:11:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:10:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ + +error: multiple `deprecated` attributes + --> $DIR/cfg-attr-duplicate-attrs.rs:22:1 + | +LL | #[cfg_attr(true, deprecated)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:21:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ + +error: multiple `deprecated` attributes + --> $DIR/cfg-attr-duplicate-attrs.rs:28:1 + | +LL | #[cfg_attr(true, inline, deprecated)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:27:1 + | +LL | #[deprecated] + | ^^^^^^^^^^^^^ + +warning: unused attribute + --> $DIR/cfg-attr-duplicate-attrs.rs:5:1 + | +LL | #[inline] + | ^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:4:1 + | +LL | #[inline] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: requested on the command line with `-W unused-attributes` + +warning: unused attribute + --> $DIR/cfg-attr-duplicate-attrs.rs:16:1 + | +LL | #[cfg_attr(true, inline)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:15:1 + | +LL | #[inline] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +warning: unused attribute + --> $DIR/cfg-attr-duplicate-attrs.rs:28:1 + | +LL | #[cfg_attr(true, inline, deprecated)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/cfg-attr-duplicate-attrs.rs:26:1 + | +LL | #[inline] + | ^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: aborting due to 3 previous errors; 3 warnings emitted + diff --git a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs index 907e1b966766f..508b449358964 100644 --- a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs +++ b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.rs @@ -46,6 +46,9 @@ struct S12; //~| WARN previously accepted struct S13; +#[cfg_attr(true, cfg_attr(true, 3))] //~ ERROR expected identifier, found `3` +struct S14; + #[cfg_attr(true, inline())] //~ ERROR malformed `inline` attribute input fn f1() {} diff --git a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr index f21c791cdf7f4..f35d82ba5fd23 100644 --- a/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr +++ b/tests/ui/conditional-compilation/cfg_attr-attr-syntax-validation.stderr @@ -120,6 +120,19 @@ LL - #[cfg_attr(true)] LL + #[cfg_attr(predicate, attr1, attr2, ...)] | +error: expected identifier, found `3` + --> $DIR/cfg_attr-attr-syntax-validation.rs:49:33 + | +LL | #[cfg_attr(true, cfg_attr(true, 3))] + | ^ expected identifier + | + = note: for more information, visit +help: must be of the form + | +LL - #[cfg_attr(true, cfg_attr(true, 3))] +LL + #[cfg_attr(true, cfg_attr(predicate, attr1, attr2, ...))] + | + error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable --> $DIR/cfg_attr-attr-syntax-validation.rs:30:30 | @@ -156,7 +169,7 @@ LL | #[cfg_attr(true, link_section = "name")] | ++++++++ error[E0805]: malformed `inline` attribute input - --> $DIR/cfg_attr-attr-syntax-validation.rs:49:18 + --> $DIR/cfg_attr-attr-syntax-validation.rs:52:18 | LL | #[cfg_attr(true, inline())] | ^^^^^^-- @@ -184,7 +197,7 @@ LL | #[cfg_attr(true, link_section)] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: requested on the command line with `-W unused-attributes` -error: aborting due to 13 previous errors; 1 warning emitted +error: aborting due to 14 previous errors; 1 warning emitted Some errors have detailed explanations: E0539, E0565, E0805. For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/proc-macro/attr-complex-fn.rs b/tests/ui/proc-macro/attr-complex-fn.rs index bf100401a65fc..e7c7446928aee 100644 --- a/tests/ui/proc-macro/attr-complex-fn.rs +++ b/tests/ui/proc-macro/attr-complex-fn.rs @@ -20,7 +20,7 @@ fn foo>>() {} impl MyTrait for MyStruct<{true}> { #![print_attr] - #![rustc_dummy] + # ![rustc_dummy] // whitespace cause imprecise spans for `#`/`!`/`[`/`]` in token synthesis } fn main() {} diff --git a/tests/ui/proc-macro/attr-complex-fn.stdout b/tests/ui/proc-macro/attr-complex-fn.stdout index 9bbb746bb4d62..bbb7f3ff1cc40 100644 --- a/tests/ui/proc-macro/attr-complex-fn.stdout +++ b/tests/ui/proc-macro/attr-complex-fn.stdout @@ -151,22 +151,22 @@ PRINT-ATTR INPUT (DEBUG): TokenStream [ Punct { ch: '#', spacing: Joint, - span: $DIR/attr-complex-fn.rs:23:5: 23:6 (#0), + span: $DIR/attr-complex-fn.rs:23:5: 23:21 (#0), }, Punct { ch: '!', spacing: Alone, - span: $DIR/attr-complex-fn.rs:23:6: 23:7 (#0), + span: $DIR/attr-complex-fn.rs:23:5: 23:21 (#0), }, Group { delimiter: Bracket, stream: TokenStream [ Ident { ident: "rustc_dummy", - span: $DIR/attr-complex-fn.rs:23:8: 23:19 (#0), + span: $DIR/attr-complex-fn.rs:23:9: 23:20 (#0), }, ], - span: $DIR/attr-complex-fn.rs:23:7: 23:20 (#0), + span: $DIR/attr-complex-fn.rs:23:5: 23:21 (#0), }, ], span: $DIR/attr-complex-fn.rs:21:41: 24:2 (#0), diff --git a/tests/ui/proc-macro/capture-macro-rules-invoke.stdout b/tests/ui/proc-macro/capture-macro-rules-invoke.stdout index bd785f6f2919b..3e4844ca9364e 100644 --- a/tests/ui/proc-macro/capture-macro-rules-invoke.stdout +++ b/tests/ui/proc-macro/capture-macro-rules-invoke.stdout @@ -312,7 +312,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ span: $DIR/capture-macro-rules-invoke.rs:56:26: 56:35 (#0), }, ], - span: $DIR/capture-macro-rules-invoke.rs:56:25: 56:36 (#0), + span: $DIR/capture-macro-rules-invoke.rs:56:19: 56:36 (#0), }, ], span: $DIR/capture-macro-rules-invoke.rs:33:29: 33:34 (#11),