Skip to content
36 changes: 9 additions & 27 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ use rustc_span::{
ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, LocalExpnId, Span, Spanned, Symbol, kw, respan,
sym,
};
use smallvec::{SmallVec, smallvec};
use thin_vec::{ThinVec, thin_vec};

use crate::attr::data_structures::CfgEntry;
Expand Down Expand Up @@ -3523,43 +3522,26 @@ impl GeneratedAttr {
}
}

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

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

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> {
Expand Down
229 changes: 82 additions & 147 deletions compiler/rustc_ast/src/attr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,57 +80,17 @@ impl Attribute {
}

impl AttributeExt for Attribute {
fn value_span(&self) -> Option<Span> {
match &self.kind {
AttrKind::Normal(normal) => match &normal.item.args {
AttrArgs::Eq { expr, .. } => Some(expr.span),
_ => None,
},
AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None,
AttrKind::Generated(generated) => generated.value_span(),
}
}

/// 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 {
AttrKind::Normal(..) | AttrKind::Synthetic(..) => None,
AttrKind::DocComment(..) => Some(self.span),
AttrKind::Generated(generated) => generated.is_doc_comment(),
}
}

/// For a single-segment attribute, returns its name; otherwise, returns `None`.
fn name(&self) -> Option<Symbol> {
use SyntheticAttr::*;
match &self.kind {
AttrKind::Normal(normal) => normal.item.name(),
AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None,
AttrKind::DocComment(..) => None,
AttrKind::Generated(generated) => generated.name(),
}
}

fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>> {
use SyntheticAttr::*;
match &self.kind {
AttrKind::Normal(normal) => {
Some(normal.item.path.segments.iter().map(|i| i.ident.name).collect())
}
AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None,
AttrKind::DocComment(_, _) => None,
AttrKind::Generated(generated) => generated.symbol_path(),
}
}

fn path_span(&self) -> Option<Span> {
match &self.kind {
AttrKind::Normal(attr) => Some(attr.item.path.span),
AttrKind::Synthetic(..) => unreachable!(),
AttrKind::DocComment(_, _) => None,
AttrKind::Generated(generated) => generated.path_span(),
AttrKind::Generated(generated) => match generated.path() {
[name] => Some(*name),
_ => None,
},
}
}

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

fn is_word(&self) -> bool {
match &self.kind {
AttrKind::Normal(normal) => matches!(normal.item.args, AttrArgs::Empty),
AttrKind::Synthetic(..) => unreachable!(),
AttrKind::DocComment(..) => false,
AttrKind::Generated(generated) => generated.is_word(),
}
}

/// Returns a list of meta items if the attribute is delimited with parenthesis:
///
/// ```text
/// #[attr(a, b = "c")] // Returns `Some()`.
/// #[attr = ""] // Returns `None`.
/// #[attr] // Returns `None`.
/// ```
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
match &self.kind {
AttrKind::Normal(normal) => normal.item.meta_item_list(),
AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => None,
AttrKind::Generated(generated) => generated.path() == name,
}
}

Expand Down Expand Up @@ -222,18 +159,6 @@ impl AttributeExt for Attribute {
}
}

/// Returns the documentation if this is a doc comment or a sugared doc comment.
/// * `///doc` returns `Some("doc")`.
/// * `#[doc = "doc"]` returns `Some("doc")`.
/// * `#[doc(...)]` returns `None`.
fn doc_str(&self) -> Option<Symbol> {
match &self.kind {
AttrKind::DocComment(.., data) => Some(*data),
AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
_ => None,
}
}

fn doc_resolution_scope(&self) -> Option<AttrStyle> {
match &self.kind {
AttrKind::DocComment(..) => Some(self.style),
Expand All @@ -245,14 +170,6 @@ impl AttributeExt for Attribute {
_ => None,
}
}

/// Returns whether this attribute is any of the proc macro attributes.
/// i.e. `proc_macro`, `proc_macro_attribute` or `proc_macro_derive`.
fn is_proc_macro_attr(&self) -> bool {
[sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
.iter()
.any(|kind| self.has_name(*kind))
}
}

impl Attribute {
Expand Down Expand Up @@ -326,6 +243,29 @@ impl Attribute {
_ => None,
}
}

/// Returns a list of meta items if the attribute is delimited with parenthesis:
///
/// ```text
/// #[attr(a, b = "c")] // Returns `Some()`.
/// #[attr = ""] // Returns `None`.
/// #[attr] // Returns `None`.
/// ```
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
match &self.kind {
AttrKind::Normal(normal) => normal.item.meta_item_list(),
AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => None,
}
}

pub fn path_span(&self) -> Option<Span> {
match &self.kind {
AttrKind::Normal(attr) => Some(attr.item.path.span),
AttrKind::Synthetic(..) => unreachable!(),
AttrKind::DocComment(_, _) => None,
AttrKind::Generated(generated) => generated.path_span(),
}
}
}

impl AttrItem {
Expand Down Expand Up @@ -901,60 +841,14 @@ pub trait AttributeExt: Debug + 'static {
/// return the name of the attribute; otherwise, returns `None`.
fn name(&self) -> Option<Symbol>;

/// Get the meta item list, `#[attr(meta item list)]`
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>>;

/// Gets the value literal, as string, when using `#[attr = value]`
fn value_str(&self) -> Option<Symbol>;

/// Gets the span of the value literal, as string, when using `#[attr = value]`
fn value_span(&self) -> Option<Span>;

/// 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) -> Option<Span>;

/// Returns true if the attribute's first *and only* path segment is equal to the passed-in
/// symbol.
#[inline]
fn has_name(&self, name: Symbol) -> bool {
self.name().map(|x| x == name).unwrap_or(false)
}

/// Returns true if the attribute's first *and only* path segment is any of the passed-in
/// symbols.
#[inline]
fn has_any_name(&self, names: &[Symbol]) -> bool {
names.iter().any(|&name| self.has_name(name))
}
/// Returns whether the attribute is a path, without any arguments.
fn is_word(&self) -> bool;

fn path(&self) -> SmallVec<[Symbol; 1]> {
self.symbol_path().unwrap_or(smallvec![sym::doc])
}

fn path_span(&self) -> Option<Span>;

/// Returns None for doc comments
fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>>;

/// 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<Symbol>;

/// Returns whether this attribute is any of the proc macro attributes.
/// i.e. `proc_macro`, `proc_macro_attribute` or `proc_macro_derive`.
fn is_proc_macro_attr(&self) -> bool;

/// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
/// * `///doc` returns `Some(("doc", CommentKind::Line))`.
/// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
Expand All @@ -979,51 +873,92 @@ impl Attribute {
AttributeExt::name(self)
}

pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
AttributeExt::meta_item_list(self)
}

pub fn value_str(&self) -> Option<Symbol> {
AttributeExt::value_str(self)
}

/// Gets the span of the value literal, as string, when using `#[attr = value]`
pub fn value_span(&self) -> Option<Span> {
AttributeExt::value_span(self)
match &self.kind {
AttrKind::Normal(normal) => match &normal.item.args {
AttrArgs::Eq { expr, .. } => Some(expr.span),
_ => None,
},
AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None,
AttrKind::Generated(generated) => generated.value_span(),
}
}

pub fn path_matches(&self, name: &[Symbol]) -> bool {
AttributeExt::path_matches(self, name)
}

// on ast attributes we return a bool since that's what most code already expects
/// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
/// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
/// a doc comment) will return `false`.
pub fn is_doc_comment(&self) -> bool {
AttributeExt::is_doc_comment(self).is_some()
match &self.kind {
AttrKind::Normal(..) | AttrKind::Synthetic(..) => false,
AttrKind::DocComment(..) => true,
AttrKind::Generated(generated) => generated.is_doc_comment(),
}
}

/// Returns true if the attribute's first *and only* path segment is equal to the passed-in
/// symbol.
#[inline]
pub fn has_name(&self, name: Symbol) -> bool {
AttributeExt::has_name(self, name)
self.name().map(|x| x == name).unwrap_or(false)
}

/// Returns true if the attribute's first *and only* path segment is any of the passed-in
/// symbols.
#[inline]
pub fn has_any_name(&self, names: &[Symbol]) -> bool {
AttributeExt::has_any_name(self, names)
names.iter().any(|&name| self.has_name(name))
}

/// Returns whether the attribute is a path, without any arguments.
pub fn is_word(&self) -> bool {
AttributeExt::is_word(self)
match &self.kind {
AttrKind::Normal(normal) => matches!(normal.item.args, AttrArgs::Empty),
AttrKind::Synthetic(..) => unreachable!(),
AttrKind::DocComment(..) => false,
AttrKind::Generated(generated) => generated.is_word(),
}
}

pub fn path(&self) -> SmallVec<[Symbol; 1]> {
AttributeExt::path(self)
use SyntheticAttr::*;
match &self.kind {
AttrKind::Normal(normal) => {
normal.item.path.segments.iter().map(|i| i.ident.name).collect()
}
AttrKind::Synthetic(CfgTrace(_)) => smallvec![sym::cfg],
AttrKind::Synthetic(CfgAttrTrace(_)) => smallvec![sym::cfg_attr],
AttrKind::DocComment(_, _) => smallvec![sym::doc],
AttrKind::Generated(generated) => generated.path().iter().copied().collect(),
}
}

/// Returns the documentation if this is a doc comment or a sugared doc comment.
/// * `///doc` returns `Some("doc")`.
/// * `#[doc = "doc"]` returns `Some("doc")`.
/// * `#[doc(...)]` returns `None`.
pub fn doc_str(&self) -> Option<Symbol> {
AttributeExt::doc_str(self)
match &self.kind {
AttrKind::DocComment(.., data) => Some(*data),
AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
_ => None,
}
}

/// Returns whether this attribute is any of the proc macro attributes.
/// i.e. `proc_macro`, `proc_macro_attribute` or `proc_macro_derive`.
pub fn is_proc_macro_attr(&self) -> bool {
AttributeExt::is_proc_macro_attr(self)
[sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
.iter()
.any(|kind| self.has_name(*kind))
}

pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind, Span)> {
Expand Down
Loading
Loading