compiler/rustc_ast/src/attr/mod.rs RUST 1,016 lines View on github.com → Search inside
1//! Functions dealing with attributes and meta items.23pub mod data_structures;4pub mod version;56use std::fmt::Debug;7use std::sync::atomic::{AtomicU32, Ordering};89use rustc_index::bit_set::GrowableBitSet;10use rustc_span::{Ident, Span, Symbol, sym};11use smallvec::{SmallVec, smallvec};12use thin_vec::{ThinVec, thin_vec};1314use crate::AttrItemKind;15use crate::ast::{16    AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs,17    Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,18    PathSegment, Safety,19};20use crate::token::{21    self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token,22};23use crate::tokenstream::{24    DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,25};26use crate::util::comments;27use crate::util::literal::escape_string_symbol;2829pub struct MarkedAttrs(GrowableBitSet<AttrId>);3031impl MarkedAttrs {32    pub fn new() -> Self {33        // We have no idea how many attributes there will be, so just34        // initiate the vectors with 0 bits. We'll grow them as necessary.35        MarkedAttrs(GrowableBitSet::new_empty())36    }3738    pub fn mark(&mut self, attr: &Attribute) {39        self.0.insert(attr.id);40    }4142    pub fn is_marked(&self, attr: &Attribute) -> bool {43        self.0.contains(attr.id)44    }45}4647pub struct AttrIdGenerator(AtomicU32);4849impl AttrIdGenerator {50    pub fn new() -> Self {51        AttrIdGenerator(AtomicU32::new(0))52    }5354    pub fn mk_attr_id(&self) -> AttrId {55        let id = self.0.fetch_add(1, Ordering::Relaxed);56        assert!(id != u32::MAX);57        AttrId::from_u32(id)58    }59}6061impl Attribute {62    pub fn get_normal_item(&self) -> &AttrItem {63        match &self.kind {64            AttrKind::Normal(normal) => &normal.item,65            AttrKind::DocComment(..) => panic!("unexpected doc comment"),66        }67    }6869    /// Replaces the arguments of this attribute with new arguments `AttrItemKind`.70    /// This is useful for making this attribute into a trace attribute, and should otherwise be avoided.71    pub fn replace_args(&mut self, new_args: AttrItemKind) {72        match &mut self.kind {73            AttrKind::Normal(normal) => normal.item.args = new_args,74            AttrKind::DocComment(..) => panic!("unexpected doc comment"),75        }76    }7778    pub fn unwrap_normal_item(self) -> AttrItem {79        match self.kind {80            AttrKind::Normal(normal) => normal.item,81            AttrKind::DocComment(..) => panic!("unexpected doc comment"),82        }83    }84}8586impl AttributeExt for Attribute {87    fn id(&self) -> AttrId {88        self.id89    }9091    fn value_span(&self) -> Option<Span> {92        match &self.kind {93            AttrKind::Normal(normal) => match &normal.item.args.unparsed_ref()? {94                AttrArgs::Eq { expr, .. } => Some(expr.span),95                _ => None,96            },97            AttrKind::DocComment(..) => None,98        }99    }100101    /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).102    /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not103    /// a doc comment) will return `false`.104    fn is_doc_comment(&self) -> Option<Span> {105        match self.kind {106            AttrKind::Normal(..) => None,107            AttrKind::DocComment(..) => Some(self.span),108        }109    }110111    /// For a single-segment attribute, returns its name; otherwise, returns `None`.112    fn name(&self) -> Option<Symbol> {113        match &self.kind {114            AttrKind::Normal(normal) => {115                if let [ident] = &*normal.item.path.segments {116                    Some(ident.ident.name)117                } else {118                    None119                }120            }121            AttrKind::DocComment(..) => None,122        }123    }124125    fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>> {126        match &self.kind {127            AttrKind::Normal(p) => {128                Some(p.item.path.segments.iter().map(|i| i.ident.name).collect())129            }130            AttrKind::DocComment(_, _) => None,131        }132    }133134    fn path_span(&self) -> Option<Span> {135        match &self.kind {136            AttrKind::Normal(attr) => Some(attr.item.path.span),137            AttrKind::DocComment(_, _) => None,138        }139    }140141    fn path_matches(&self, name: &[Symbol]) -> bool {142        match &self.kind {143            AttrKind::Normal(normal) => {144                normal.item.path.segments.len() == name.len()145                    && normal146                        .item147                        .path148                        .segments149                        .iter()150                        .zip(name)151                        .all(|(s, n)| s.args.is_none() && s.ident.name == *n)152            }153            AttrKind::DocComment(..) => false,154        }155    }156157    fn span(&self) -> Span {158        self.span159    }160161    fn is_word(&self) -> bool {162        if let AttrKind::Normal(normal) = &self.kind {163            matches!(normal.item.args, AttrItemKind::Unparsed(AttrArgs::Empty))164        } else {165            false166        }167    }168169    /// Returns a list of meta items if the attribute is delimited with parenthesis:170    ///171    /// ```text172    /// #[attr(a, b = "c")] // Returns `Some()`.173    /// #[attr = ""] // Returns `None`.174    /// #[attr] // Returns `None`.175    /// ```176    fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {177        match &self.kind {178            AttrKind::Normal(normal) => normal.item.meta_item_list(),179            AttrKind::DocComment(..) => None,180        }181    }182183    /// Returns the string value in:184    ///185    /// ```text186    /// #[attribute = "value"]187    ///               ^^^^^^^188    /// ```189    ///190    /// It returns `None` in any other cases, including doc comments if they191    /// are not under the form `#[doc = "..."]`.192    ///193    /// It also returns `None` for:194    ///195    /// ```text196    /// #[attr("value")]197    /// ```198    fn value_str(&self) -> Option<Symbol> {199        match &self.kind {200            AttrKind::Normal(normal) => normal.item.value_str(),201            AttrKind::DocComment(..) => None,202        }203    }204205    /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.206    /// * `///doc` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Line)))`.207    /// * `/** doc */` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Block)))`.208    /// * `#[doc = "doc"]` returns `Some(("doc", DocFragmentKind::Raw))`.209    /// * `#[doc(...)]` returns `None`.210    fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {211        match &self.kind {212            AttrKind::DocComment(kind, data) => Some((*data, DocFragmentKind::Sugared(*kind))),213            AttrKind::Normal(normal) if normal.item.path == sym::doc => {214                if let Some(value) = normal.item.value_str()215                    && let Some(value_span) = normal.item.value_span()216                {217                    Some((value, DocFragmentKind::Raw(value_span)))218                } else {219                    None220                }221            }222            _ => None,223        }224    }225226    /// Returns the documentation if this is a doc comment or a sugared doc comment.227    /// * `///doc` returns `Some("doc")`.228    /// * `#[doc = "doc"]` returns `Some("doc")`.229    /// * `#[doc(...)]` returns `None`.230    fn doc_str(&self) -> Option<Symbol> {231        match &self.kind {232            AttrKind::DocComment(.., data) => Some(*data),233            AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),234            _ => None,235        }236    }237238    fn doc_resolution_scope(&self) -> Option<AttrStyle> {239        match &self.kind {240            AttrKind::DocComment(..) => Some(self.style),241            AttrKind::Normal(normal)242                if normal.item.path == sym::doc && normal.item.value_str().is_some() =>243            {244                Some(self.style)245            }246            _ => None,247        }248    }249250    fn is_automatically_derived_attr(&self) -> bool {251        self.has_name(sym::automatically_derived)252    }253254    fn is_doc_hidden(&self) -> bool {255        self.has_name(sym::doc)256            && self.meta_item_list().is_some_and(|l| list_contains_name(&l, sym::hidden))257    }258259    fn is_doc_keyword_or_attribute(&self) -> bool {260        if self.has_name(sym::doc)261            && let Some(items) = self.meta_item_list()262        {263            for item in items {264                if item.has_name(sym::keyword) || item.has_name(sym::attribute) {265                    return true;266                }267            }268        }269        false270    }271272    fn is_rustc_doc_primitive(&self) -> bool {273        self.has_name(sym::rustc_doc_primitive)274    }275}276277impl Attribute {278    pub fn style(&self) -> AttrStyle {279        self.style280    }281282    pub fn may_have_doc_links(&self) -> bool {283        self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))284            || self.deprecation_note().is_some_and(|s| comments::may_have_doc_links(s.as_str()))285    }286287    /// Extracts the MetaItem from inside this Attribute.288    pub fn meta(&self) -> Option<MetaItem> {289        match &self.kind {290            AttrKind::Normal(normal) => normal.item.meta(self.span),291            AttrKind::DocComment(..) => None,292        }293    }294295    pub fn meta_kind(&self) -> Option<MetaItemKind> {296        match &self.kind {297            AttrKind::Normal(normal) => normal.item.meta_kind(),298            AttrKind::DocComment(..) => None,299        }300    }301302    pub fn token_trees(&self) -> Vec<TokenTree> {303        match self.kind {304            AttrKind::Normal(ref normal) => normal305                .tokens306                .as_ref()307                .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}"))308                .to_attr_token_stream()309                .to_token_trees(),310            AttrKind::DocComment(comment_kind, data) => vec![TokenTree::token_alone(311                token::DocComment(comment_kind, self.style, data),312                self.span,313            )],314        }315    }316317    pub fn deprecation_note(&self) -> Option<Ident> {318        match &self.kind {319            AttrKind::Normal(normal) if normal.item.path == sym::deprecated => {320                let meta = &normal.item;321322                // #[deprecated = "..."]323                if let Some(s) = meta.value_str() {324                    return Some(Ident { name: s, span: meta.span() });325                }326327                // #[deprecated(note = "...")]328                if let Some(list) = meta.meta_item_list() {329                    for nested in list {330                        if let Some(mi) = nested.meta_item()331                            && mi.path == sym::note332                            && let Some(s) = mi.value_str()333                        {334                            return Some(Ident { name: s, span: mi.span });335                        }336                    }337                }338339                None340            }341            _ => None,342        }343    }344}345346impl AttrItem {347    pub fn span(&self) -> Span {348        self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))349    }350351    pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {352        match &self.args.unparsed_ref()? {353            AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {354                MetaItemKind::list_from_tokens(args.tokens.clone())355            }356            AttrArgs::Delimited(_) | AttrArgs::Eq { .. } | AttrArgs::Empty => None,357        }358    }359360    /// Returns the string value in:361    ///362    /// ```text363    /// #[attribute = "value"]364    ///               ^^^^^^^365    /// ```366    ///367    /// It returns `None` in any other cases like:368    ///369    /// ```text370    /// #[attr("value")]371    /// ```372    fn value_str(&self) -> Option<Symbol> {373        match &self.args.unparsed_ref()? {374            AttrArgs::Eq { expr, .. } => match expr.kind {375                ExprKind::Lit(token_lit) => {376                    LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())377                }378                _ => None,379            },380            AttrArgs::Delimited(_) | AttrArgs::Empty => None,381        }382    }383384    /// Returns the span in:385    ///386    /// ```text387    /// #[attribute = "value"]388    ///               ^^^^^^^389    /// ```390    ///391    /// It returns `None` in any other cases like:392    ///393    /// ```text394    /// #[attr("value")]395    /// ```396    fn value_span(&self) -> Option<Span> {397        match &self.args.unparsed_ref()? {398            AttrArgs::Eq { expr, .. } => Some(expr.span),399            AttrArgs::Delimited(_) | AttrArgs::Empty => None,400        }401    }402403    pub fn meta(&self, span: Span) -> Option<MetaItem> {404        Some(MetaItem {405            unsafety: Safety::Default,406            path: self.path.clone(),407            kind: self.meta_kind()?,408            span,409        })410    }411412    pub fn meta_kind(&self) -> Option<MetaItemKind> {413        MetaItemKind::from_attr_args(self.args.unparsed_ref()?)414    }415}416417impl MetaItem {418    /// For a single-segment meta item, returns its name; otherwise, returns `None`.419    pub fn ident(&self) -> Option<Ident> {420        if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None }421    }422423    pub fn name(&self) -> Option<Symbol> {424        self.ident().map(|ident| ident.name)425    }426427    pub fn has_name(&self, name: Symbol) -> bool {428        self.path == name429    }430431    pub fn is_word(&self) -> bool {432        matches!(self.kind, MetaItemKind::Word)433    }434435    pub fn meta_item_list(&self) -> Option<&[MetaItemInner]> {436        match &self.kind {437            MetaItemKind::List(l) => Some(&**l),438            _ => None,439        }440    }441442    /// ```text443    /// Example:444    ///     #[attribute(name = "value")]445    ///                 ^^^^^^^^^^^^^^446    /// ```447    pub fn name_value_literal(&self) -> Option<&MetaItemLit> {448        match &self.kind {449            MetaItemKind::NameValue(v) => Some(v),450            _ => None,451        }452    }453454    /// This is used in case you want the value span instead of the whole attribute. Example:455    ///456    /// ```text457    /// #[doc(alias = "foo")]458    /// ```459    ///460    /// In here, it'll return a span for `"foo"`.461    pub fn name_value_literal_span(&self) -> Option<Span> {462        Some(self.name_value_literal()?.span)463    }464465    /// Returns the string value in:466    ///467    /// ```text468    /// #[attribute = "value"]469    ///               ^^^^^^^470    /// ```471    ///472    /// It returns `None` in any other cases like:473    ///474    /// ```text475    /// #[attr("value")]476    /// ```477    pub fn value_str(&self) -> Option<Symbol> {478        match &self.kind {479            MetaItemKind::NameValue(v) => v.kind.str(),480            _ => None,481        }482    }483484    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItem> {485        // FIXME: Share code with `parse_path`.486        let tt = iter.next().map(|tt| TokenTree::uninterpolate(tt));487        let path = match tt.as_deref() {488            Some(&TokenTree::Token(489                Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },490                _,491            )) => 'arm: {492                let mut segments = if let &token::Ident(name, _) = kind {493                    if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =494                        iter.peek()495                    {496                        iter.next();497                        thin_vec![PathSegment::from_ident(Ident::new(name, span))]498                    } else {499                        break 'arm Path::from_ident(Ident::new(name, span));500                    }501                } else {502                    thin_vec![PathSegment::path_root(span)]503                };504                loop {505                    let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =506                        iter.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()507                    else {508                        return None;509                    };510                    segments.push(PathSegment::from_ident(Ident::new(name, span)));511                    let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = iter.peek()512                    else {513                        break;514                    };515                    iter.next();516                }517                let span = span.with_hi(segments.last().unwrap().ident.span.hi());518                Path { span, segments, tokens: None }519            }520            Some(TokenTree::Delimited(521                _span,522                _spacing,523                Delimiter::Invisible(InvisibleOrigin::MetaVar(524                    MetaVarKind::Meta { .. } | MetaVarKind::Path,525                )),526                _stream,527            )) => {528                // This path is currently unreachable in the test suite.529                unreachable!()530            }531            Some(TokenTree::Token(Token { kind, .. }, _)) if kind.is_delim() => {532                panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tt);533            }534            _ => return None,535        };536        let list_closing_paren_pos = iter.peek().map(|tt| tt.span().hi());537        let kind = MetaItemKind::from_tokens(iter)?;538        let hi = match &kind {539            MetaItemKind::NameValue(lit) => lit.span.hi(),540            MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),541            _ => path.span.hi(),542        };543        let span = path.span.with_hi(hi);544        // FIXME: This parses `unsafe()` not as unsafe attribute syntax in `MetaItem`,545        // but as a parenthesized list. This (and likely `MetaItem`) should be changed in546        // such a way that builtin macros don't accept extraneous `unsafe()`.547        Some(MetaItem { unsafety: Safety::Default, path, kind, span })548    }549}550551impl MetaItemKind {552    // public because it can be called in the hir553    pub fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> {554        let mut iter = tokens.iter();555        let mut result = ThinVec::new();556        while iter.peek().is_some() {557            let item = MetaItemInner::from_tokens(&mut iter)?;558            result.push(item);559            match iter.next() {560                None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}561                _ => return None,562            }563        }564        Some(result)565    }566567    fn name_value_from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {568        match iter.next() {569            Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {570                MetaItemKind::name_value_from_tokens(&mut inner_tokens.iter())571            }572            Some(TokenTree::Token(token, _)) => {573                MetaItemLit::from_token(token).map(MetaItemKind::NameValue)574            }575            _ => None,576        }577    }578579    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {580        match iter.peek() {581            Some(TokenTree::Delimited(.., Delimiter::Parenthesis, inner_tokens)) => {582                let inner_tokens = inner_tokens.clone();583                iter.next();584                MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)585            }586            Some(TokenTree::Delimited(..)) => None,587            Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {588                iter.next();589                MetaItemKind::name_value_from_tokens(iter)590            }591            _ => Some(MetaItemKind::Word),592        }593    }594595    fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {596        match args {597            AttrArgs::Empty => Some(MetaItemKind::Word),598            AttrArgs::Delimited(DelimArgs { dspan: _, delim: Delimiter::Parenthesis, tokens }) => {599                MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List)600            }601            AttrArgs::Delimited(..) => None,602            AttrArgs::Eq { expr, .. } => match expr.kind {603                ExprKind::Lit(token_lit) => {604                    // Turn failures to `None`, we'll get parse errors elsewhere.605                    MetaItemLit::from_token_lit(token_lit, expr.span)606                        .ok()607                        .map(|lit| MetaItemKind::NameValue(lit))608                }609                _ => None,610            },611        }612    }613}614615impl MetaItemInner {616    pub fn span(&self) -> Span {617        match self {618            MetaItemInner::MetaItem(item) => item.span,619            MetaItemInner::Lit(lit) => lit.span,620        }621    }622623    /// For a single-segment meta item, returns its identifier; otherwise, returns `None`.624    pub fn ident(&self) -> Option<Ident> {625        self.meta_item().and_then(|meta_item| meta_item.ident())626    }627628    /// For a single-segment meta item, returns its name; otherwise, returns `None`.629    pub fn name(&self) -> Option<Symbol> {630        self.ident().map(|ident| ident.name)631    }632633    /// Returns `true` if this list item is a MetaItem with a name of `name`.634    pub fn has_name(&self, name: Symbol) -> bool {635        self.meta_item().is_some_and(|meta_item| meta_item.has_name(name))636    }637638    /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.639    pub fn is_word(&self) -> bool {640        self.meta_item().is_some_and(|meta_item| meta_item.is_word())641    }642643    /// Gets a list of inner meta items from a list `MetaItem` type.644    pub fn meta_item_list(&self) -> Option<&[MetaItemInner]> {645        self.meta_item().and_then(|meta_item| meta_item.meta_item_list())646    }647648    /// If it's a singleton list of the form `foo(lit)`, returns the `foo` and649    /// the `lit`.650    pub fn singleton_lit_list(&self) -> Option<(Symbol, &MetaItemLit)> {651        self.meta_item().and_then(|meta_item| {652            meta_item.meta_item_list().and_then(|meta_item_list| {653                if meta_item_list.len() == 1654                    && let Some(ident) = meta_item.ident()655                    && let Some(lit) = meta_item_list[0].lit()656                {657                    return Some((ident.name, lit));658                }659                None660            })661        })662    }663664    /// See [`MetaItem::name_value_literal_span`].665    pub fn name_value_literal_span(&self) -> Option<Span> {666        self.meta_item()?.name_value_literal_span()667    }668669    /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a670    /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.671    pub fn value_str(&self) -> Option<Symbol> {672        self.meta_item().and_then(|meta_item| meta_item.value_str())673    }674675    /// Returns the `MetaItemLit` if `self` is a `MetaItemInner::Literal`s.676    pub fn lit(&self) -> Option<&MetaItemLit> {677        match self {678            MetaItemInner::Lit(lit) => Some(lit),679            _ => None,680        }681    }682683    /// Returns the bool if `self` is a boolean `MetaItemInner::Literal`.684    pub fn boolean_literal(&self) -> Option<bool> {685        match self {686            MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => Some(*b),687            _ => None,688        }689    }690691    /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem` or if it's692    /// `MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`.693    pub fn meta_item_or_bool(&self) -> Option<&MetaItemInner> {694        match self {695            MetaItemInner::MetaItem(_item) => Some(self),696            MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. }) => Some(self),697            _ => None,698        }699    }700701    /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem`.702    pub fn meta_item(&self) -> Option<&MetaItem> {703        match self {704            MetaItemInner::MetaItem(item) => Some(item),705            _ => None,706        }707    }708709    /// Returns `true` if the variant is `MetaItem`.710    pub fn is_meta_item(&self) -> bool {711        self.meta_item().is_some()712    }713714    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemInner> {715        match iter.peek() {716            Some(TokenTree::Token(token, _)) if let Some(lit) = MetaItemLit::from_token(token) => {717                iter.next();718                return Some(MetaItemInner::Lit(lit));719            }720            Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {721                iter.next();722                return MetaItemInner::from_tokens(&mut inner_tokens.iter());723            }724            _ => {}725        }726        MetaItem::from_tokens(iter).map(MetaItemInner::MetaItem)727    }728}729730pub fn mk_doc_comment(731    g: &AttrIdGenerator,732    comment_kind: CommentKind,733    style: AttrStyle,734    data: Symbol,735    span: Span,736) -> Attribute {737    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }738}739740fn mk_attr(741    g: &AttrIdGenerator,742    style: AttrStyle,743    unsafety: Safety,744    path: Path,745    args: AttrArgs,746    span: Span,747) -> Attribute {748    mk_attr_from_item(749        g,750        AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args), tokens: None },751        None,752        style,753        span,754    )755}756757pub fn mk_attr_from_item(758    g: &AttrIdGenerator,759    item: AttrItem,760    tokens: Option<LazyAttrTokenStream>,761    style: AttrStyle,762    span: Span,763) -> Attribute {764    Attribute {765        kind: AttrKind::Normal(Box::new(NormalAttr { item, tokens })),766        id: g.mk_attr_id(),767        style,768        span,769    }770}771772pub fn mk_attr_word(773    g: &AttrIdGenerator,774    style: AttrStyle,775    unsafety: Safety,776    name: Symbol,777    span: Span,778) -> Attribute {779    let path = Path::from_ident(Ident::new(name, span));780    let args = AttrArgs::Empty;781    mk_attr(g, style, unsafety, path, args, span)782}783784pub fn mk_attr_nested_word(785    g: &AttrIdGenerator,786    style: AttrStyle,787    unsafety: Safety,788    outer: Symbol,789    inner: Symbol,790    span: Span,791) -> Attribute {792    let inner_tokens = TokenStream::new(vec![TokenTree::Token(793        Token::from_ast_ident(Ident::new(inner, span)),794        Spacing::Alone,795    )]);796    let outer_ident = Ident::new(outer, span);797    let path = Path::from_ident(outer_ident);798    let attr_args = AttrArgs::Delimited(DelimArgs {799        dspan: DelimSpan::from_single(span),800        delim: Delimiter::Parenthesis,801        tokens: inner_tokens,802    });803    mk_attr(g, style, unsafety, path, attr_args, span)804}805806pub fn mk_attr_name_value_str(807    g: &AttrIdGenerator,808    style: AttrStyle,809    unsafety: Safety,810    name: Symbol,811    val: Symbol,812    span: Span,813) -> Attribute {814    let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);815    let expr = Box::new(Expr {816        id: DUMMY_NODE_ID,817        kind: ExprKind::Lit(lit),818        span,819        attrs: AttrVec::new(),820        tokens: None,821    });822    let path = Path::from_ident(Ident::new(name, span));823    let args = AttrArgs::Eq { eq_span: span, expr };824    mk_attr(g, style, unsafety, path, args, span)825}826827pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> {828    attrs.iter().filter(move |attr| attr.has_name(name))829}830831pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {832    filter_by_name(attrs, name).next()833}834835pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> {836    find_by_name(attrs, name).and_then(|attr| attr.value_str())837}838839pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {840    find_by_name(attrs, name).is_some()841}842843pub fn list_contains_name(items: &[MetaItemInner], name: Symbol) -> bool {844    items.iter().any(|item| item.has_name(name))845}846847impl MetaItemLit {848    pub fn value_str(&self) -> Option<Symbol> {849        LitKind::from_token_lit(self.as_token_lit()).ok().and_then(|lit| lit.str())850    }851}852853pub trait AttributeExt: Debug {854    fn id(&self) -> AttrId;855856    /// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`),857    /// return the name of the attribute; otherwise, returns `None`.858    fn name(&self) -> Option<Symbol>;859860    /// Get the meta item list, `#[attr(meta item list)]`861    fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>>;862863    /// Gets the value literal, as string, when using `#[attr = value]`864    fn value_str(&self) -> Option<Symbol>;865866    /// Gets the span of the value literal, as string, when using `#[attr = value]`867    fn value_span(&self) -> Option<Span>;868869    /// Checks whether the path of this attribute matches the name.870    ///871    /// Matches one segment of the path to each element in `name`872    fn path_matches(&self, name: &[Symbol]) -> bool;873874    /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).875    /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not876    /// a doc comment) will return `false`.877    fn is_doc_comment(&self) -> Option<Span>;878879    /// Returns true if the attribute's first *and only* path segment is equal to the passed-in880    /// symbol.881    #[inline]882    fn has_name(&self, name: Symbol) -> bool {883        self.name().map(|x| x == name).unwrap_or(false)884    }885886    /// Returns true if the attribute's first *and only* path segment is any of the passed-in887    /// symbols.888    #[inline]889    fn has_any_name(&self, names: &[Symbol]) -> bool {890        names.iter().any(|&name| self.has_name(name))891    }892893    /// get the span of the entire attribute894    fn span(&self) -> Span;895896    /// Returns whether the attribute is a path, without any arguments.897    fn is_word(&self) -> bool;898899    fn path(&self) -> SmallVec<[Symbol; 1]> {900        self.symbol_path().unwrap_or(smallvec![sym::doc])901    }902903    fn path_span(&self) -> Option<Span>;904905    /// Returns None for doc comments906    fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>>;907908    /// Returns the documentation if this is a doc comment or a sugared doc comment.909    /// * `///doc` returns `Some("doc")`.910    /// * `#[doc = "doc"]` returns `Some("doc")`.911    /// * `#[doc(...)]` returns `None`.912    fn doc_str(&self) -> Option<Symbol>;913914    /// Returns whether this attribute is any of the proc macro attributes.915    /// i.e. `proc_macro`, `proc_macro_attribute` or `proc_macro_derive`.916    fn is_proc_macro_attr(&self) -> bool {917        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]918            .iter()919            .any(|kind| self.has_name(*kind))920    }921    /// Returns true if this attribute is `#[automatically_deived]`.922    fn is_automatically_derived_attr(&self) -> bool;923924    /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.925    /// * `///doc` returns `Some(("doc", CommentKind::Line))`.926    /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.927    /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.928    /// * `#[doc(...)]` returns `None`.929    fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)>;930931    /// Returns outer or inner if this is a doc attribute or a sugared doc932    /// comment, otherwise None.933    ///934    /// This is used in the case of doc comments on modules, to decide whether935    /// to resolve intra-doc links against the symbols in scope within the936    /// commented module (for inner doc) vs within its parent module (for outer937    /// doc).938    fn doc_resolution_scope(&self) -> Option<AttrStyle>;939940    /// Returns `true` if this attribute contains `doc(hidden)`.941    fn is_doc_hidden(&self) -> bool;942943    /// Returns `true` is this attribute contains `doc(keyword)` or `doc(attribute)`.944    fn is_doc_keyword_or_attribute(&self) -> bool;945946    /// Returns `true` if this is a `#[rustc_doc_primitive]` attribute.947    fn is_rustc_doc_primitive(&self) -> bool;948}949950// FIXME(fn_delegation): use function delegation instead of manually forwarding951952impl Attribute {953    pub fn id(&self) -> AttrId {954        AttributeExt::id(self)955    }956957    pub fn name(&self) -> Option<Symbol> {958        AttributeExt::name(self)959    }960961    pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {962        AttributeExt::meta_item_list(self)963    }964965    pub fn value_str(&self) -> Option<Symbol> {966        AttributeExt::value_str(self)967    }968969    pub fn value_span(&self) -> Option<Span> {970        AttributeExt::value_span(self)971    }972973    pub fn path_matches(&self, name: &[Symbol]) -> bool {974        AttributeExt::path_matches(self, name)975    }976977    // on ast attributes we return a bool since that's what most code already expects978    pub fn is_doc_comment(&self) -> bool {979        AttributeExt::is_doc_comment(self).is_some()980    }981982    #[inline]983    pub fn has_name(&self, name: Symbol) -> bool {984        AttributeExt::has_name(self, name)985    }986987    #[inline]988    pub fn has_any_name(&self, names: &[Symbol]) -> bool {989        AttributeExt::has_any_name(self, names)990    }991992    pub fn span(&self) -> Span {993        AttributeExt::span(self)994    }995996    pub fn is_word(&self) -> bool {997        AttributeExt::is_word(self)998    }9991000    pub fn path(&self) -> SmallVec<[Symbol; 1]> {1001        AttributeExt::path(self)1002    }10031004    pub fn doc_str(&self) -> Option<Symbol> {1005        AttributeExt::doc_str(self)1006    }10071008    pub fn is_proc_macro_attr(&self) -> bool {1009        AttributeExt::is_proc_macro_attr(self)1010    }10111012    pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {1013        AttributeExt::doc_str_and_fragment_kind(self)1014    }1015}

Code quality findings 27

Critical: Use of 'unsafe' keyword bypasses Rust's safety guarantees. Requires careful auditing, clear justification (FFI, specific optimizations), and minimal scope.
error safety unsafe-block
// FIXME: This parses `unsafe()` not as unsafe attribute syntax in `MetaItem`,
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
if let [ident] = &*normal.item.path.segments {
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None }
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let span = span.with_hi(segments.last().unwrap().ident.span.hi());
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
&& let Some(lit) = meta_item_list[0].lit()
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning correctness unchecked-indexing
/// See [`MetaItem::name_value_literal_span`].
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &self.kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
AttrKind::Normal(normal) => match &normal.item.args.unparsed_ref()? {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &self.kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &self.kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &self.args.unparsed_ref()? {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
AttrArgs::Eq { expr, .. } => match expr.kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &self.kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &self.kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match &self.kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let path = match tt.as_deref() {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
segments.push(PathSegment::from_ident(Ident::new(name, span)));
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let hi = match &kind {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
result.push(item);
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match iter.next() {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match iter.next() {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
AttrArgs::Eq { expr, .. } => match expr.kind {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match self {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match self {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match self {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match self {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
match iter.peek() {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.