blob: 6068c11590a23d257d3b5ece3ed4896f7ff7fe38 [file]
use std::fmt;
use rustc_ast::attr::AttributeExt;
use rustc_ast::token::DocFragmentKind;
use rustc_ast::{AttrStyle, DelimArgs, MetaItemInner, MetaItemLit, ast, join_path_idents};
use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher};
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
use rustc_macros::{Decodable, Encodable, StableHash};
use rustc_span::{AttrId, DUMMY_SP, Ident, Span, Symbol, sym};
use smallvec::SmallVec;
use thin_vec::ThinVec;
use crate::AttributeKind;
/// Arguments passed to an attribute macro.
#[derive(Clone, Debug, StableHash, Encodable, Decodable)]
pub enum AttrArgs {
/// No arguments: `#[attr]`.
Empty,
/// Delimited arguments: `#[attr()/[]/{}]`.
Delimited(DelimArgs),
/// Arguments of a key-value attribute: `#[attr = "value"]`.
Eq {
/// Span of the `=` token.
eq_span: Span,
/// The "value".
expr: MetaItemLit,
},
}
#[derive(Clone, Debug, StableHash, Encodable, Decodable)]
pub struct AttrPath {
pub segments: Box<[Symbol]>,
pub span: Span,
}
impl IntoDiagArg for AttrPath {
fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
self.to_string().into_diag_arg(path)
}
}
impl AttrPath {
pub fn from_ast(path: &ast::Path, lower_span: impl Copy + Fn(Span) -> Span) -> Self {
AttrPath {
segments: path
.segments
.iter()
.map(|i| i.ident.name)
.collect::<Vec<_>>()
.into_boxed_slice(),
span: lower_span(path.span),
}
}
}
impl fmt::Display for AttrPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
join_path_idents(self.segments.iter().map(|i| Ident { name: *i, span: DUMMY_SP }))
)
}
}
#[derive(Clone, Debug, StableHash, Encodable, Decodable)]
pub struct AttrItem {
// Not lowered to hir::Path because we have no NodeId to resolve to.
pub path: AttrPath,
pub args: AttrArgs,
pub id: HashIgnoredAttrId,
/// Denotes if the attribute decorates the following construct (outer)
/// or the construct this attribute is contained within (inner).
pub style: AttrStyle,
/// Span of the entire attribute
pub span: Span,
}
/// The derived implementation of [`StableHash`] on [`Attribute`]s shouldn't hash
/// [`AttrId`]s. By wrapping them in this, we make sure we never do.
#[derive(Copy, Debug, Encodable, Decodable, Clone)]
pub struct HashIgnoredAttrId {
pub attr_id: AttrId,
}
impl StableHash for HashIgnoredAttrId {
fn stable_hash<Hcx: StableHashCtxt>(&self, _hcx: &mut Hcx, _hasher: &mut StableHasher) {
/* we don't hash HashIgnoredAttrId, we ignore them */
}
}
/// Many functions on this type have their documentation in the [`AttributeExt`] trait,
/// since they defer their implementation directly to that trait.
#[derive(Clone, Debug, Encodable, Decodable, StableHash)]
pub enum Attribute {
/// A parsed built-in attribute.
///
/// Each attribute has a span connected to it. However, you must be somewhat careful using it.
/// That's because sometimes we merge multiple attributes together, like when an item has
/// multiple `repr` attributes. In this case the span might not be very useful.
Parsed(AttributeKind),
/// An attribute that could not be parsed, out of a token-like representation.
/// This is the case for custom tool attributes.
Unparsed(Box<AttrItem>),
}
impl Attribute {
pub fn get_normal_item(&self) -> &AttrItem {
match &self {
Attribute::Unparsed(normal) => &normal,
_ => panic!("unexpected parsed attribute"),
}
}
pub fn value_lit(&self) -> Option<&MetaItemLit> {
match &self {
Attribute::Unparsed(n) => match n.as_ref() {
AttrItem { args: AttrArgs::Eq { eq_span: _, expr }, .. } => Some(expr),
_ => None,
},
_ => None,
}
}
pub fn is_parsed_attr(&self) -> bool {
match self {
Attribute::Parsed(_) => true,
Attribute::Unparsed(_) => false,
}
}
pub fn is_prefix_attr_for_suggestions(&self) -> bool {
match self {
Attribute::Unparsed(attr) => attr.span.desugaring_kind().is_none(),
// Other parsed attributes that can appear on expressions originate from source and
// should make suggestions treat the expression like a prefixed form.
Attribute::Parsed(_) => true,
}
}
}
impl AttributeExt for Attribute {
#[inline]
fn id(&self) -> AttrId {
match &self {
Attribute::Unparsed(u) => u.id.attr_id,
_ => panic!(),
}
}
#[inline]
fn meta_item_list(&self) -> Option<ThinVec<ast::MetaItemInner>> {
match &self {
Attribute::Unparsed(n) => match n.as_ref() {
AttrItem { args: AttrArgs::Delimited(d), .. } => {
ast::MetaItemKind::list_from_tokens(d.tokens.clone())
}
_ => None,
},
_ => None,
}
}
#[inline]
fn value_str(&self) -> Option<Symbol> {
self.value_lit().and_then(|x| x.value_as_str())
}
#[inline]
fn value_span(&self) -> Option<Span> {
self.value_lit().map(|i| i.span)
}
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
#[inline]
fn name(&self) -> Option<Symbol> {
match &self {
Attribute::Unparsed(n) => {
if let [ident] = n.path.segments.as_ref() {
Some(*ident)
} else {
None
}
}
_ => None,
}
}
#[inline]
fn path_matches(&self, name: &[Symbol]) -> bool {
match &self {
Attribute::Unparsed(n) => n.path.segments.iter().eq(name),
_ => false,
}
}
#[inline]
fn is_doc_comment(&self) -> Option<Span> {
if let Attribute::Parsed(AttributeKind::DocComment { span, .. }) = self {
Some(*span)
} else {
None
}
}
#[inline]
fn span(&self) -> Span {
match &self {
Attribute::Unparsed(u) => u.span,
// FIXME: should not be needed anymore when all attrs are parsed
Attribute::Parsed(AttributeKind::DocComment { span, .. }) => *span,
Attribute::Parsed(AttributeKind::Deprecated { span, .. }) => *span,
Attribute::Parsed(AttributeKind::CfgTrace(cfgs)) => cfgs[0].1,
a => panic!("can't get the span of an arbitrary parsed attribute: {a:?}"),
}
}
#[inline]
fn is_word(&self) -> bool {
match &self {
Attribute::Unparsed(n) => {
matches!(n.args, AttrArgs::Empty)
}
_ => false,
}
}
#[inline]
fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>> {
match &self {
Attribute::Unparsed(n) => Some(n.path.segments.iter().copied().collect()),
_ => None,
}
}
fn path_span(&self) -> Option<Span> {
match &self {
Attribute::Unparsed(attr) => Some(attr.path.span),
Attribute::Parsed(_) => None,
}
}
#[inline]
fn doc_str(&self) -> Option<Symbol> {
match &self {
Attribute::Parsed(AttributeKind::DocComment { comment, .. }) => Some(*comment),
_ => None,
}
}
fn is_automatically_derived_attr(&self) -> bool {
matches!(self, Attribute::Parsed(AttributeKind::AutomaticallyDerived))
}
#[inline]
fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
match &self {
Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => {
Some((*comment, *kind))
}
_ => None,
}
}
fn doc_resolution_scope(&self) -> Option<AttrStyle> {
match self {
Attribute::Parsed(AttributeKind::DocComment { style, .. }) => Some(*style),
Attribute::Unparsed(attr) if self.has_name(sym::doc) && self.value_str().is_some() => {
Some(attr.style)
}
_ => None,
}
}
fn is_proc_macro_attr(&self) -> bool {
matches!(
self,
Attribute::Parsed(
AttributeKind::ProcMacro
| AttributeKind::ProcMacroAttribute
| AttributeKind::ProcMacroDerive { .. }
)
)
}
fn is_doc_hidden(&self) -> bool {
matches!(self, Attribute::Parsed(AttributeKind::Doc(d)) if d.hidden.is_some())
}
fn is_doc_keyword_or_attribute(&self) -> bool {
matches!(self, Attribute::Parsed(AttributeKind::Doc(d)) if d.attribute.is_some() || d.keyword.is_some())
}
fn is_rustc_doc_primitive(&self) -> bool {
matches!(self, Attribute::Parsed(AttributeKind::RustcDocPrimitive(..)))
}
}
// FIXME(fn_delegation): use function delegation instead of manually forwarding
impl Attribute {
#[inline]
pub fn id(&self) -> AttrId {
AttributeExt::id(self)
}
#[inline]
pub fn name(&self) -> Option<Symbol> {
AttributeExt::name(self)
}
#[inline]
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
AttributeExt::meta_item_list(self)
}
#[inline]
pub fn value_str(&self) -> Option<Symbol> {
AttributeExt::value_str(self)
}
#[inline]
pub fn value_span(&self) -> Option<Span> {
AttributeExt::value_span(self)
}
#[inline]
pub fn path_matches(&self, name: &[Symbol]) -> bool {
AttributeExt::path_matches(self, name)
}
#[inline]
pub fn is_doc_comment(&self) -> Option<Span> {
AttributeExt::is_doc_comment(self)
}
#[inline]
pub fn has_name(&self, name: Symbol) -> bool {
AttributeExt::has_name(self, name)
}
#[inline]
pub fn has_any_name(&self, names: &[Symbol]) -> bool {
AttributeExt::has_any_name(self, names)
}
#[inline]
pub fn span(&self) -> Span {
AttributeExt::span(self)
}
#[inline]
pub fn is_word(&self) -> bool {
AttributeExt::is_word(self)
}
#[inline]
pub fn path(&self) -> SmallVec<[Symbol; 1]> {
AttributeExt::path(self)
}
#[inline]
pub fn doc_str(&self) -> Option<Symbol> {
AttributeExt::doc_str(self)
}
#[inline]
pub fn is_proc_macro_attr(&self) -> bool {
AttributeExt::is_proc_macro_attr(self)
}
#[inline]
pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
AttributeExt::doc_str_and_fragment_kind(self)
}
}