Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 144 additions & 4 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//! - [`MacroDef`], [`MacStmtStyle`], [`MacCall`]: Macro definition and invocation.
//! - [`Attribute`]: Metadata associated with item.
//! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators.
// ignore-tidy-file-filelength

use std::borrow::{Borrow, Cow};
use std::{cmp, fmt};
Expand All @@ -33,12 +34,13 @@ use rustc_span::{
ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, LocalExpnId, Span, Spanned, Symbol, kw, respan,
sym,
};
use smallvec::{SmallVec, smallvec};
use thin_vec::{ThinVec, thin_vec};

use crate::attr::data_structures::CfgEntry;
pub use crate::format::*;
use crate::token::{self, CommentKind, Delimiter};
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream, TokenTree};
use crate::util::parser::{ExprPrecedence, Fixity};
use crate::visit::{AssocCtxt, BoundKind, LifetimeCtxt};

Expand Down Expand Up @@ -3464,13 +3466,151 @@ pub enum AttrKind {
/// A normal attribute.
Normal(Box<NormalAttr>),

/// A synthetic attribute inserted by the compiler.
Synthetic(Box<SyntheticAttr>),

/// 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(CommentKind, Symbol),

/// A synthetic attribute inserted by the compiler.
///
/// See [`SyntheticAttr`] for the semantics of "synthetic" attributes.
Synthetic(Box<SyntheticAttr>),

/// A macro generated attribute inserted by a builtin macro.
///
/// See [`GeneratedAttr`] for the semantics of "generated" attributes.
Generated(GeneratedAttr),
}

/// An attribute created by a macro expansion.
///
/// This attribute has no tokens, which means it cannot be allowed to re-enter the parser. This
/// means it can only be added to items that cannot later be observed by other macros.
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub enum GeneratedAttr {
AutomaticallyDerived,
RustcConstUnstableDerived,
Inline,
DocHidden,
CoverageOff,
RustcTestMarker(Symbol),
}

impl GeneratedAttr {
pub fn descr(&self) -> String {
match self {
GeneratedAttr::AutomaticallyDerived => "#[automatically_derived]".into(),
GeneratedAttr::RustcConstUnstableDerived => {
"#[rustc_const_unstable(feature = \"derive_const\", issue = \"143874\")]".into()
}
GeneratedAttr::Inline => "#[inline]".into(),
GeneratedAttr::DocHidden => "#[doc(hidden)]".into(),
GeneratedAttr::CoverageOff => "#[coverage(off)]".into(),
GeneratedAttr::RustcTestMarker(test_path) => {
format!("#[rustc_test_marker = \"{test_path}\"]")
}
}
}
pub fn value_span(&self) -> Option<Span> {
match self {
GeneratedAttr::AutomaticallyDerived => None,
GeneratedAttr::RustcConstUnstableDerived => None,
GeneratedAttr::Inline => None,
GeneratedAttr::DocHidden => None,
GeneratedAttr::CoverageOff => None,
GeneratedAttr::RustcTestMarker(_) => None,
}
}

pub fn is_doc_comment(&self) -> Option<Span> {
match self {
GeneratedAttr::AutomaticallyDerived => None,
GeneratedAttr::RustcConstUnstableDerived => None,
GeneratedAttr::Inline => None,
GeneratedAttr::DocHidden => None,
GeneratedAttr::CoverageOff => None,
GeneratedAttr::RustcTestMarker(_) => None,
}
}

pub fn name(&self) -> Option<Symbol> {
match self {
GeneratedAttr::AutomaticallyDerived => Some(sym::automatically_derived),
GeneratedAttr::RustcConstUnstableDerived => Some(sym::rustc_const_unstable),
GeneratedAttr::Inline => Some(sym::inline),
GeneratedAttr::DocHidden => Some(sym::doc),
GeneratedAttr::CoverageOff => Some(sym::coverage),
GeneratedAttr::RustcTestMarker(_) => Some(sym::rustc_test_marker),
}
}

pub fn path_matches(&self, name: &[Symbol]) -> bool {
let path = match self {
GeneratedAttr::AutomaticallyDerived => &[sym::automatically_derived],
GeneratedAttr::RustcConstUnstableDerived => &[sym::rustc_const_unstable],
GeneratedAttr::Inline => &[sym::inline],
GeneratedAttr::DocHidden => &[sym::doc],
GeneratedAttr::CoverageOff => &[sym::coverage],
GeneratedAttr::RustcTestMarker(_) => &[sym::rustc_test_marker],
};

path == name
}

pub fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>> {
self.name().map(|n| smallvec![n])
}

pub fn path_span(&self) -> Option<Span> {
match self {
GeneratedAttr::AutomaticallyDerived => None,
GeneratedAttr::RustcConstUnstableDerived => None,
GeneratedAttr::Inline => None,
GeneratedAttr::DocHidden => None,
GeneratedAttr::CoverageOff => None,
GeneratedAttr::RustcTestMarker(_) => None,
}
}

pub fn is_word(&self) -> bool {
match self {
GeneratedAttr::AutomaticallyDerived => true,
GeneratedAttr::RustcConstUnstableDerived => false,
GeneratedAttr::Inline => true,
GeneratedAttr::DocHidden => false,
GeneratedAttr::CoverageOff => false,
GeneratedAttr::RustcTestMarker(_) => false,
}
}
pub fn value_str(&self) -> Option<Symbol> {
match self {
GeneratedAttr::AutomaticallyDerived => None,
GeneratedAttr::RustcConstUnstableDerived => None,
GeneratedAttr::Inline => None,
GeneratedAttr::DocHidden => None,
GeneratedAttr::CoverageOff => None,
GeneratedAttr::RustcTestMarker(test_path) => Some(*test_path),
}
}

pub fn doc_str_and_fragment_kind(&self) -> Option<!> {
match self {
GeneratedAttr::AutomaticallyDerived => None,
GeneratedAttr::RustcConstUnstableDerived => None,
GeneratedAttr::Inline => None,
GeneratedAttr::DocHidden => None,
GeneratedAttr::CoverageOff => None,
GeneratedAttr::RustcTestMarker(_) => None,
}
}

pub fn token_trees(&self) -> Vec<TokenTree> {
unimplemented!(
"generated attribute `{}` cannot re-enter the parser at this time",
self.descr()
)
}
}

#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_ast/src/ast_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,17 @@ impl HasTokens for Attribute {
fn tokens(&self) -> Option<&LazyAttrTokenStream> {
match &self.kind {
AttrKind::Normal(normal) => normal.tokens.as_ref(),
AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(),
AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => {
unreachable!()
}
}
}
fn tokens_mut(&mut self) -> Option<&mut Option<LazyAttrTokenStream>> {
Some(match &mut self.kind {
AttrKind::Normal(normal) => &mut normal.tokens,
AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(),
AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => {
unreachable!()
}
})
}
}
Expand Down
60 changes: 44 additions & 16 deletions compiler/rustc_ast/src/attr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use rustc_span::{Ident, Span, Symbol, sym};
use smallvec::{SmallVec, smallvec};
use thin_vec::{ThinVec, thin_vec};

use crate::GeneratedAttr;
use crate::ast::{
AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs,
Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,
Expand Down Expand Up @@ -62,7 +63,9 @@ impl Attribute {
pub fn get_normal_item(&self) -> &AttrItem {
match &self.kind {
AttrKind::Normal(normal) => &normal.item,
AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(),
AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => {
unreachable!()
}
}
}

Expand All @@ -71,7 +74,9 @@ impl Attribute {
AttrKind::Normal(..) => {
Attribute { kind: AttrKind::Synthetic(Box::new(synthetic_attr)), ..self }
}
AttrKind::Synthetic(..) | AttrKind::DocComment(..) => unreachable!(),
AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => {
unreachable!()
}
}
}
}
Expand All @@ -84,16 +89,18 @@ impl AttributeExt for Attribute {
_ => None,
},
AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None,
AttrKind::Generated(generated) => generated.value_span(),
}
}

/// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
/// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
/// a doc comment) will return `false`.
fn is_doc_comment(&self) -> Option<Span> {
match self.kind {
match &self.kind {
AttrKind::Normal(..) | AttrKind::Synthetic(..) => None,
AttrKind::DocComment(..) => Some(self.span),
AttrKind::Generated(generated) => generated.is_doc_comment(),
}
}

Expand All @@ -104,6 +111,7 @@ impl AttributeExt for Attribute {
AttrKind::Normal(normal) => normal.item.name(),
AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None,
AttrKind::DocComment(..) => None,
AttrKind::Generated(generated) => generated.name(),
}
}

Expand All @@ -115,6 +123,7 @@ impl AttributeExt for Attribute {
}
AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None,
AttrKind::DocComment(_, _) => None,
AttrKind::Generated(generated) => generated.symbol_path(),
}
}

Expand All @@ -123,6 +132,7 @@ impl AttributeExt for Attribute {
AttrKind::Normal(attr) => Some(attr.item.path.span),
AttrKind::Synthetic(..) => unreachable!(),
AttrKind::DocComment(_, _) => None,
AttrKind::Generated(generated) => generated.path_span(),
}
}

Expand All @@ -139,6 +149,7 @@ impl AttributeExt for Attribute {
.all(|(s, n)| s.args.is_none() && s.ident.name == *n)
}
AttrKind::Synthetic(..) | AttrKind::DocComment(..) => false,
AttrKind::Generated(generated) => generated.path_matches(name),
}
}

Expand All @@ -147,6 +158,7 @@ impl AttributeExt for Attribute {
AttrKind::Normal(normal) => matches!(normal.item.args, AttrArgs::Empty),
AttrKind::Synthetic(..) => unreachable!(),
AttrKind::DocComment(..) => false,
AttrKind::Generated(generated) => generated.is_word(),
}
}

Expand All @@ -160,7 +172,7 @@ impl AttributeExt for Attribute {
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
match &self.kind {
AttrKind::Normal(normal) => normal.item.meta_item_list(),
AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None,
AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => None,
}
}

Expand All @@ -184,6 +196,7 @@ impl AttributeExt for Attribute {
AttrKind::Normal(normal) => normal.item.value_str(),
AttrKind::Synthetic(..) => unreachable!(),
AttrKind::DocComment(..) => None,
AttrKind::Generated(generated) => generated.value_str(),
}
}

Expand All @@ -207,6 +220,7 @@ impl AttributeExt for Attribute {
Some((value, DocFragmentKind::Raw(value_span), self.span))
}
AttrKind::Normal(..) | AttrKind::Synthetic(..) => None,
AttrKind::Generated(generated) => generated.doc_str_and_fragment_kind()?,
}
}

Expand Down Expand Up @@ -239,20 +253,32 @@ impl AttributeExt for Attribute {
}

fn is_doc_hidden(&self) -> bool {
self.has_name(sym::doc)
&& self.meta_item_list().is_some_and(|l| list_contains_name(&l, sym::hidden))
match &self.kind {
AttrKind::Normal(_) => {
self.has_name(sym::doc)
&& self.meta_item_list().is_some_and(|l| list_contains_name(&l, sym::hidden))
}
AttrKind::Generated(GeneratedAttr::DocHidden) => true,
_ => false,
}
}

fn is_doc_keyword_or_attribute(&self) -> bool {
if self.has_name(sym::doc)
&& let Some(items) = self.meta_item_list()
{
for item in items {
if item.has_name(sym::keyword) || item.has_name(sym::attribute) {
return true;
match &self.kind {
AttrKind::Normal(_) => {
if self.has_name(sym::doc)
&& let Some(items) = self.meta_item_list()
{
for item in items {
if item.has_name(sym::keyword) || item.has_name(sym::attribute) {
return true;
}
}
}
}
_ => {}
}

false
}

Expand Down Expand Up @@ -283,7 +309,7 @@ impl Attribute {
pub fn meta(&self) -> Option<MetaItem> {
match &self.kind {
AttrKind::Normal(normal) => normal.item.meta(self.span),
AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None,
AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => None,
}
}

Expand All @@ -292,21 +318,23 @@ impl Attribute {
AttrKind::Normal(normal) => normal.item.meta_kind(),
AttrKind::Synthetic(..) => unreachable!(),
AttrKind::DocComment(..) => None,
AttrKind::Generated(..) => None,
}
}

pub fn token_trees(&self) -> Vec<TokenTree> {
match self.kind {
AttrKind::Normal(ref normal) => normal
match &self.kind {
AttrKind::Normal(normal) => normal
.tokens
.as_ref()
.unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}"))
.to_attr_token_stream()
.to_token_trees(),
// Empty tokens here ensures synthetic attributes are invisible to proc macros.
AttrKind::Synthetic(..) => vec![],
AttrKind::Generated(generated) => generated.token_trees(),
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,
)],
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ macro_rules! common_visitor_and_walkers {
impl_visitable_noop! {
// tidy-alphabetical-start
AttrId,
GeneratedAttr,
Movability,
Mutability,
Pinnedness,
Expand Down
Loading
Loading