Skip to main content

rustc_ast/attr/
mod.rs

1//! Functions dealing with attributes and meta items.
2
3pub mod data_structures;
4pub mod version;
5
6use std::fmt::Debug;
7use std::sync::atomic::{AtomicU32, Ordering};
8
9use rustc_index::bit_set::GrowableBitSet;
10use rustc_span::{Ident, Span, Symbol, sym};
11use smallvec::{SmallVec, smallvec};
12use thin_vec::{ThinVec, thin_vec};
13
14use crate::ast::{
15    AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs,
16    Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,
17    PathSegment, Safety, SyntheticAttr,
18};
19use crate::token::{
20    self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token,
21};
22use crate::tokenstream::{
23    DelimSpacing, DelimSpan, LazyTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree,
24};
25use crate::util::comments;
26use crate::util::literal::escape_string_symbol;
27
28pub struct MarkedAttrs(GrowableBitSet<AttrId>);
29
30impl MarkedAttrs {
31    pub fn new() -> Self {
32        // We have no idea how many attributes there will be, so just
33        // initiate the vectors with 0 bits. We'll grow them as necessary.
34        MarkedAttrs(GrowableBitSet::new_empty())
35    }
36
37    pub fn mark(&mut self, attr: &Attribute) {
38        self.0.insert(attr.id);
39    }
40
41    pub fn is_marked(&self, attr: &Attribute) -> bool {
42        self.0.contains(attr.id)
43    }
44}
45
46pub struct AttrIdGenerator(AtomicU32);
47
48impl AttrIdGenerator {
49    pub fn new() -> Self {
50        AttrIdGenerator(AtomicU32::new(0))
51    }
52
53    pub fn mk_attr_id(&self) -> AttrId {
54        let id = self.0.fetch_add(1, Ordering::Relaxed);
55        if !(id != u32::MAX) {
    ::core::panicking::panic("assertion failed: id != u32::MAX")
};assert!(id != u32::MAX);
56        AttrId::from_u32(id)
57    }
58}
59
60impl Attribute {
61    pub fn get_normal_item(&self) -> &AttrItem {
62        match &self.kind {
63            AttrKind::Normal(normal) => &normal.item,
64            AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => {
65                ::core::panicking::unreachable_default_msg()unreachable!()
66            }
67        }
68    }
69
70    pub fn convert_normal_to_synthetic(self, synthetic_attr: SyntheticAttr) -> Attribute {
71        match self.kind {
72            AttrKind::Normal(..) => {
73                Attribute { kind: AttrKind::Synthetic(Box::new(synthetic_attr)), ..self }
74            }
75            AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => {
76                ::core::panicking::unreachable_default_msg()unreachable!()
77            }
78        }
79    }
80}
81
82impl AttributeExt for Attribute {
83    /// For a single-segment attribute, returns its name; otherwise, returns `None`.
84    fn name(&self) -> Option<Symbol> {
85        use SyntheticAttr::*;
86        match &self.kind {
87            AttrKind::Normal(normal) => normal.item.name(),
88            AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => None,
89            AttrKind::DocComment(..) => None,
90            AttrKind::Generated(generated) => match generated.path() {
91                [name] => Some(*name),
92                _ => None,
93            },
94        }
95    }
96
97    fn path_matches(&self, name: &[Symbol]) -> bool {
98        match &self.kind {
99            AttrKind::Normal(normal) => {
100                normal.item.path.segments.len() == name.len()
101                    && normal
102                        .item
103                        .path
104                        .segments
105                        .iter()
106                        .zip(name)
107                        .all(|(s, n)| s.args.is_none() && s.ident.name == *n)
108            }
109            AttrKind::Synthetic(..) | AttrKind::DocComment(..) => false,
110            AttrKind::Generated(generated) => generated.path() == name,
111        }
112    }
113
114    /// Returns the string value in:
115    ///
116    /// ```text
117    /// #[attribute = "value"]
118    ///               ^^^^^^^
119    /// ```
120    ///
121    /// It returns `None` in any other cases, including doc comments if they
122    /// are not under the form `#[doc = "..."]`.
123    ///
124    /// It also returns `None` for:
125    ///
126    /// ```text
127    /// #[attr("value")]
128    /// ```
129    fn value_str(&self) -> Option<Symbol> {
130        match &self.kind {
131            AttrKind::Normal(normal) => normal.item.value_str(),
132            AttrKind::Synthetic(..) => ::core::panicking::unreachable_default_msg()unreachable!(),
133            AttrKind::DocComment(..) => None,
134            AttrKind::Generated(generated) => generated.value_str(),
135        }
136    }
137
138    /// Returns the documentation, its kind if this is a doc comment or a sugared doc comment
139    /// and the span of the entire comment (or the entire attribute if raw - see the span in
140    /// [`DocFragmentKind`] for the literal span.)
141    /// * `///doc` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Line)), _)`.
142    /// * `/** doc */` returns `Some(("doc", DocFragmentKind::Sugared(CommentKind::Block), _))`.
143    /// * `#[doc = "doc"]` returns `Some(("doc", DocFragmentKind::Raw), _)`.
144    /// * `#[doc(...)]` returns `None`.
145    fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind, Span)> {
146        match &self.kind {
147            AttrKind::DocComment(kind, data) => {
148                Some((*data, DocFragmentKind::Sugared(*kind), self.span))
149            }
150            AttrKind::Normal(normal)
151                if normal.item.path == sym::doc
152                    && let Some(value) = normal.item.value_str()
153                    && let Some(value_span) = normal.item.value_span() =>
154            {
155                Some((value, DocFragmentKind::Raw(value_span), self.span))
156            }
157            AttrKind::Normal(..) | AttrKind::Synthetic(..) => None,
158            AttrKind::Generated(generated) => generated.doc_str_and_fragment_kind()?,
159        }
160    }
161
162    fn doc_resolution_scope(&self) -> Option<AttrStyle> {
163        match &self.kind {
164            AttrKind::DocComment(..) => Some(self.style),
165            AttrKind::Normal(normal)
166                if normal.item.path == sym::doc && normal.item.value_str().is_some() =>
167            {
168                Some(self.style)
169            }
170            _ => None,
171        }
172    }
173}
174
175impl Attribute {
176    pub fn style(&self) -> AttrStyle {
177        self.style
178    }
179
180    pub fn may_have_doc_links(&self) -> bool {
181        self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
182            || self.deprecation_note().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
183    }
184
185    /// Extracts the MetaItem from inside this Attribute.
186    pub fn meta(&self) -> Option<MetaItem> {
187        match &self.kind {
188            AttrKind::Normal(normal) => normal.item.meta(self.span),
189            AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => None,
190        }
191    }
192
193    pub fn meta_kind(&self) -> Option<MetaItemKind> {
194        match &self.kind {
195            AttrKind::Normal(normal) => normal.item.meta_kind(),
196            AttrKind::Synthetic(..) => ::core::panicking::unreachable_default_msg()unreachable!(),
197            AttrKind::DocComment(..) => None,
198            AttrKind::Generated(..) => None,
199        }
200    }
201
202    pub fn append_token_trees_to(&self, res: &mut Vec<TokenTree>) {
203        match &self.kind {
204            AttrKind::Normal(normal) => normal
205                .tokens
206                .as_ref()
207                .unwrap_or_else(|| {
    ::core::panicking::panic_fmt(format_args!("attribute is missing tokens: {0:?}",
            self));
}panic!("attribute is missing tokens: {self:?}"))
208                .append_token_trees_to(res),
209            // Empty tokens here ensures synthetic attributes are invisible to proc macros.
210            AttrKind::Synthetic(..) => {}
211            AttrKind::Generated(generated) => generated.append_token_trees_to(res),
212            AttrKind::DocComment(comment_kind, data) => res.push(TokenTree::token_alone(
213                token::DocComment(*comment_kind, self.style, *data),
214                self.span,
215            )),
216        }
217    }
218
219    pub fn deprecation_note(&self) -> Option<Ident> {
220        match &self.kind {
221            AttrKind::Normal(normal) if normal.item.path == sym::deprecated => {
222                let meta = &normal.item;
223
224                // #[deprecated = "..."]
225                if let Some(s) = meta.value_str() {
226                    return Some(Ident { name: s, span: meta.span });
227                }
228
229                // #[deprecated(note = "...")]
230                if let Some(list) = meta.meta_item_list() {
231                    for nested in list {
232                        if let Some(mi) = nested.meta_item()
233                            && mi.path == sym::note
234                            && let Some(s) = mi.value_str()
235                        {
236                            return Some(Ident { name: s, span: mi.span });
237                        }
238                    }
239                }
240
241                None
242            }
243            _ => None,
244        }
245    }
246
247    /// Returns a list of meta items if the attribute is delimited with parenthesis:
248    ///
249    /// ```text
250    /// #[attr(a, b = "c")] // Returns `Some()`.
251    /// #[attr = ""] // Returns `None`.
252    /// #[attr] // Returns `None`.
253    /// ```
254    pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
255        match &self.kind {
256            AttrKind::Normal(normal) => normal.item.meta_item_list(),
257            AttrKind::Synthetic(..) | AttrKind::DocComment(..) | AttrKind::Generated(..) => None,
258        }
259    }
260
261    pub fn path_span(&self) -> Option<Span> {
262        match &self.kind {
263            AttrKind::Normal(attr) => Some(attr.item.path.span),
264            AttrKind::Synthetic(..) => ::core::panicking::unreachable_default_msg()unreachable!(),
265            AttrKind::DocComment(_, _) => None,
266            AttrKind::Generated(generated) => generated.path_span(),
267        }
268    }
269}
270
271impl AttrItem {
272    pub fn name(&self) -> Option<Symbol> {
273        if let [seg] = &*self.path.segments { Some(seg.ident.name) } else { None }
274    }
275
276    pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
277        match &self.args {
278            AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
279                MetaItemKind::list_from_tokens(args.tokens.clone())
280            }
281            AttrArgs::Delimited(_) | AttrArgs::Eq { .. } | AttrArgs::Empty => None,
282        }
283    }
284
285    /// Returns the string value in:
286    ///
287    /// ```text
288    /// #[attribute = "value"]
289    ///               ^^^^^^^
290    /// ```
291    ///
292    /// It returns `None` in any other cases like:
293    ///
294    /// ```text
295    /// #[attr("value")]
296    /// ```
297    fn value_str(&self) -> Option<Symbol> {
298        match &self.args {
299            AttrArgs::Eq { expr, .. } => match expr.kind {
300                ExprKind::Lit(token_lit) => {
301                    LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
302                }
303                _ => None,
304            },
305            AttrArgs::Delimited(_) | AttrArgs::Empty => None,
306        }
307    }
308
309    /// Returns the span in:
310    ///
311    /// ```text
312    /// #[attribute = "value"]
313    ///               ^^^^^^^
314    /// ```
315    ///
316    /// It returns `None` in any other cases like:
317    ///
318    /// ```text
319    /// #[attr("value")]
320    /// ```
321    fn value_span(&self) -> Option<Span> {
322        match &self.args {
323            AttrArgs::Eq { expr, .. } => Some(expr.span),
324            AttrArgs::Delimited(_) | AttrArgs::Empty => None,
325        }
326    }
327
328    pub fn meta(&self, span: Span) -> Option<MetaItem> {
329        Some(MetaItem {
330            unsafety: Safety::Default,
331            path: self.path.clone(),
332            kind: self.meta_kind()?,
333            span,
334        })
335    }
336
337    pub fn meta_kind(&self) -> Option<MetaItemKind> {
338        MetaItemKind::from_attr_args(&self.args)
339    }
340}
341
342impl MetaItem {
343    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
344    pub fn ident(&self) -> Option<Ident> {
345        if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None }
346    }
347
348    pub fn name(&self) -> Option<Symbol> {
349        self.ident().map(|ident| ident.name)
350    }
351
352    pub fn has_name(&self, name: Symbol) -> bool {
353        self.path == name
354    }
355
356    pub fn is_word(&self) -> bool {
357        #[allow(non_exhaustive_omitted_patterns)] match self.kind {
    MetaItemKind::Word => true,
    _ => false,
}matches!(self.kind, MetaItemKind::Word)
358    }
359
360    pub fn meta_item_list(&self) -> Option<&[MetaItemInner]> {
361        match &self.kind {
362            MetaItemKind::List(l) => Some(&**l),
363            _ => None,
364        }
365    }
366
367    /// ```text
368    /// Example:
369    ///     #[attribute(name = "value")]
370    ///                 ^^^^^^^^^^^^^^
371    /// ```
372    pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
373        match &self.kind {
374            MetaItemKind::NameValue(v) => Some(v),
375            _ => None,
376        }
377    }
378
379    /// This is used in case you want the value span instead of the whole attribute. Example:
380    ///
381    /// ```text
382    /// #[doc(alias = "foo")]
383    /// ```
384    ///
385    /// In here, it'll return a span for `"foo"`.
386    pub fn name_value_literal_span(&self) -> Option<Span> {
387        Some(self.name_value_literal()?.span)
388    }
389
390    /// Returns the string value in:
391    ///
392    /// ```text
393    /// #[attribute = "value"]
394    ///               ^^^^^^^
395    /// ```
396    ///
397    /// It returns `None` in any other cases like:
398    ///
399    /// ```text
400    /// #[attr("value")]
401    /// ```
402    pub fn value_str(&self) -> Option<Symbol> {
403        match &self.kind {
404            MetaItemKind::NameValue(v) => v.kind.str(),
405            _ => None,
406        }
407    }
408
409    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItem> {
410        // FIXME: Share code with `parse_path`.
411        let tt = iter.next().map(|tt| TokenTree::uninterpolate(tt));
412        let path = match tt.as_deref() {
413            Some(&TokenTree::Token(
414                Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
415                _,
416            )) => 'arm: {
417                let mut segments = if let &token::Ident(name, _) = kind {
418                    if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
419                        iter.peek()
420                    {
421                        iter.next();
422                        {
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(PathSegment::from_ident(Ident::new(name, span)));
    vec
}thin_vec![PathSegment::from_ident(Ident::new(name, span))]
423                    } else {
424                        break 'arm Path::from_ident(Ident::new(name, span));
425                    }
426                } else {
427                    {
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(PathSegment::path_root(span));
    vec
}thin_vec![PathSegment::path_root(span)]
428                };
429                loop {
430                    let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
431                        iter.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()
432                    else {
433                        return None;
434                    };
435                    segments.push(PathSegment::from_ident(Ident::new(name, span)));
436                    let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = iter.peek()
437                    else {
438                        break;
439                    };
440                    iter.next();
441                }
442                let span = span.with_hi(segments.last().unwrap().ident.span.hi());
443                Path { span, segments }
444            }
445            Some(TokenTree::Delimited(
446                _span,
447                _spacing,
448                Delimiter::Invisible(InvisibleOrigin::MetaVar(
449                    MetaVarKind::Meta { .. } | MetaVarKind::Path,
450                )),
451                _stream,
452            )) => {
453                // This path is currently unreachable in the test suite.
454                ::core::panicking::unreachable_default_msg()unreachable!()
455            }
456            Some(TokenTree::Token(Token { kind, .. }, _)) if kind.is_delim() => {
457                {
    ::core::panicking::panic_fmt(format_args!("Should be `TokenTree::Delimited`, not delim tokens: {0:?}",
            tt));
};panic!("Should be `TokenTree::Delimited`, not delim tokens: {:?}", tt);
458            }
459            _ => return None,
460        };
461        let list_closing_paren_pos = iter.peek().map(|tt| tt.span().hi());
462        let kind = MetaItemKind::from_tokens(iter)?;
463        let hi = match &kind {
464            MetaItemKind::NameValue(lit) => lit.span.hi(),
465            MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
466            _ => path.span.hi(),
467        };
468        let span = path.span.with_hi(hi);
469        // FIXME: This parses `unsafe()` not as unsafe attribute syntax in `MetaItem`,
470        // but as a parenthesized list. This (and likely `MetaItem`) should be changed in
471        // such a way that builtin macros don't accept extraneous `unsafe()`.
472        Some(MetaItem { unsafety: Safety::Default, path, kind, span })
473    }
474}
475
476impl MetaItemKind {
477    // public because it can be called in the hir
478    pub fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> {
479        let mut iter = tokens.iter();
480        let mut result = ThinVec::new();
481        while iter.peek().is_some() {
482            let item = MetaItemInner::from_tokens(&mut iter)?;
483            result.push(item);
484            match iter.next() {
485                None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}
486                _ => return None,
487            }
488        }
489        Some(result)
490    }
491
492    fn name_value_from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {
493        match iter.next() {
494            Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
495                MetaItemKind::name_value_from_tokens(&mut inner_tokens.iter())
496            }
497            Some(TokenTree::Token(token, _)) => {
498                MetaItemLit::from_token(token).map(MetaItemKind::NameValue)
499            }
500            _ => None,
501        }
502    }
503
504    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {
505        match iter.peek() {
506            Some(TokenTree::Delimited(.., Delimiter::Parenthesis, inner_tokens)) => {
507                let inner_tokens = inner_tokens.clone();
508                iter.next();
509                MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
510            }
511            Some(TokenTree::Delimited(..)) => None,
512            Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
513                iter.next();
514                MetaItemKind::name_value_from_tokens(iter)
515            }
516            _ => Some(MetaItemKind::Word),
517        }
518    }
519
520    fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {
521        match args {
522            AttrArgs::Empty => Some(MetaItemKind::Word),
523            AttrArgs::Delimited(DelimArgs { dspan: _, delim: Delimiter::Parenthesis, tokens }) => {
524                MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List)
525            }
526            AttrArgs::Delimited(..) => None,
527            AttrArgs::Eq { expr, .. } => match expr.kind {
528                ExprKind::Lit(token_lit) => {
529                    // Turn failures to `None`, we'll get parse errors elsewhere.
530                    MetaItemLit::from_token_lit(token_lit, expr.span)
531                        .ok()
532                        .map(|lit| MetaItemKind::NameValue(lit))
533                }
534                _ => None,
535            },
536        }
537    }
538}
539
540impl MetaItemInner {
541    pub fn span(&self) -> Span {
542        match self {
543            MetaItemInner::MetaItem(item) => item.span,
544            MetaItemInner::Lit(lit) => lit.span,
545        }
546    }
547
548    /// For a single-segment meta item, returns its identifier; otherwise, returns `None`.
549    pub fn ident(&self) -> Option<Ident> {
550        self.meta_item().and_then(|meta_item| meta_item.ident())
551    }
552
553    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
554    pub fn name(&self) -> Option<Symbol> {
555        self.ident().map(|ident| ident.name)
556    }
557
558    /// Returns `true` if this list item is a MetaItem with a name of `name`.
559    pub fn has_name(&self, name: Symbol) -> bool {
560        self.meta_item().is_some_and(|meta_item| meta_item.has_name(name))
561    }
562
563    /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
564    pub fn is_word(&self) -> bool {
565        self.meta_item().is_some_and(|meta_item| meta_item.is_word())
566    }
567
568    /// Gets a list of inner meta items from a list `MetaItem` type.
569    pub fn meta_item_list(&self) -> Option<&[MetaItemInner]> {
570        self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
571    }
572
573    /// If it's a singleton list of the form `foo(lit)`, returns the `foo` and
574    /// the `lit`.
575    pub fn singleton_lit_list(&self) -> Option<(Symbol, &MetaItemLit)> {
576        self.meta_item().and_then(|meta_item| {
577            meta_item.meta_item_list().and_then(|meta_item_list| {
578                if meta_item_list.len() == 1
579                    && let Some(ident) = meta_item.ident()
580                    && let Some(lit) = meta_item_list[0].lit()
581                {
582                    return Some((ident.name, lit));
583                }
584                None
585            })
586        })
587    }
588
589    /// See [`MetaItem::name_value_literal_span`].
590    pub fn name_value_literal_span(&self) -> Option<Span> {
591        self.meta_item()?.name_value_literal_span()
592    }
593
594    /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
595    /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
596    pub fn value_str(&self) -> Option<Symbol> {
597        self.meta_item().and_then(|meta_item| meta_item.value_str())
598    }
599
600    /// Returns the `MetaItemLit` if `self` is a `MetaItemInner::Literal`s.
601    pub fn lit(&self) -> Option<&MetaItemLit> {
602        match self {
603            MetaItemInner::Lit(lit) => Some(lit),
604            _ => None,
605        }
606    }
607
608    /// Returns the bool if `self` is a boolean `MetaItemInner::Literal`.
609    pub fn boolean_literal(&self) -> Option<bool> {
610        match self {
611            MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => Some(*b),
612            _ => None,
613        }
614    }
615
616    /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem` or if it's
617    /// `MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`.
618    pub fn meta_item_or_bool(&self) -> Option<&MetaItemInner> {
619        match self {
620            MetaItemInner::MetaItem(_item) => Some(self),
621            MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. }) => Some(self),
622            _ => None,
623        }
624    }
625
626    /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem`.
627    pub fn meta_item(&self) -> Option<&MetaItem> {
628        match self {
629            MetaItemInner::MetaItem(item) => Some(item),
630            _ => None,
631        }
632    }
633
634    /// Returns `true` if the variant is `MetaItem`.
635    pub fn is_meta_item(&self) -> bool {
636        self.meta_item().is_some()
637    }
638
639    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemInner> {
640        match iter.peek() {
641            Some(TokenTree::Token(token, _)) if let Some(lit) = MetaItemLit::from_token(token) => {
642                iter.next();
643                return Some(MetaItemInner::Lit(lit));
644            }
645            Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
646                iter.next();
647                return MetaItemInner::from_tokens(&mut inner_tokens.iter());
648            }
649            _ => {}
650        }
651        MetaItem::from_tokens(iter).map(MetaItemInner::MetaItem)
652    }
653}
654
655pub fn mk_doc_comment(
656    g: &AttrIdGenerator,
657    comment_kind: CommentKind,
658    style: AttrStyle,
659    data: Symbol,
660    span: Span,
661) -> Attribute {
662    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
663}
664
665pub fn mk_attr_from_item(
666    g: &AttrIdGenerator,
667    item: AttrItem,
668    tokens: Option<LazyTokenStream>,
669    style: AttrStyle,
670    span: Span,
671) -> Attribute {
672    Attribute {
673        kind: AttrKind::Normal(Box::new(NormalAttr { item, tokens })),
674        id: g.mk_attr_id(),
675        style,
676        span,
677    }
678}
679
680fn mk_attr_tokens(style: AttrStyle, item_tokens: TokenStream, span: Span) -> LazyTokenStream {
681    let mut tokens = match style {
682        AttrStyle::Outer => {
683            ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [TokenTree::Token(Token::new(token::Pound, span),
                    Spacing::JointHidden)]))vec![TokenTree::Token(Token::new(token::Pound, span), Spacing::JointHidden)]
684        }
685        AttrStyle::Inner => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [TokenTree::Token(Token::new(token::Pound, span), Spacing::Joint),
                TokenTree::Token(Token::new(token::Bang, span),
                    Spacing::JointHidden)]))vec![
686            TokenTree::Token(Token::new(token::Pound, span), Spacing::Joint),
687            TokenTree::Token(Token::new(token::Bang, span), Spacing::JointHidden),
688        ],
689    };
690    tokens.push(TokenTree::Delimited(
691        DelimSpan::from_single(span),
692        DelimSpacing::new(Spacing::JointHidden, Spacing::Alone),
693        Delimiter::Bracket,
694        item_tokens,
695    ));
696
697    LazyTokenStream::new_direct(TokenStream::new(tokens))
698}
699
700// `span` is used for the `Attribute` and everything within it (except for any span within
701// `unsafety`).
702pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
703    let path = Path::from_ident(Ident::new(name, span));
704    let args = AttrArgs::Empty;
705
706    let tokens = Some(mk_attr_tokens(
707        style,
708        TokenStream::new(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [TokenTree::Token(Token::from_ast_ident(Ident::new(name, span)),
                    Spacing::Alone)]))vec![TokenTree::Token(
709            Token::from_ast_ident(Ident::new(name, span)),
710            Spacing::Alone,
711        )]),
712        span,
713    ));
714
715    mk_attr_from_item(
716        g,
717        AttrItem { unsafety: Safety::Default, path, args, span },
718        tokens,
719        style,
720        span,
721    )
722}
723
724// `span` is used for the `Attribute` and everything within it (except for any span within
725// `unsafety`).
726pub fn mk_attr_nested_word(
727    g: &AttrIdGenerator,
728    style: AttrStyle,
729    outer: Symbol,
730    inner: Symbol,
731    span: Span,
732) -> Attribute {
733    let inner_tokens = TokenStream::new(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [TokenTree::Token(Token::from_ast_ident(Ident::new(inner, span)),
                    Spacing::Alone)]))vec![TokenTree::Token(
734        Token::from_ast_ident(Ident::new(inner, span)),
735        Spacing::Alone,
736    )]);
737    let outer_ident = Ident::new(outer, span);
738    let path = Path::from_ident(outer_ident);
739    let attr_args = AttrArgs::Delimited(DelimArgs {
740        dspan: DelimSpan::from_single(span),
741        delim: Delimiter::Parenthesis,
742        tokens: inner_tokens,
743    });
744
745    let tokens = Some(mk_attr_tokens(
746        style,
747        TokenStream::new(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [TokenTree::Token(Token::from_ast_ident(Ident::new(outer, span)),
                    Spacing::Alone),
                TokenTree::Delimited(DelimSpan::from_single(span),
                    DelimSpacing::new(Spacing::JointHidden, Spacing::Alone),
                    Delimiter::Parenthesis,
                    TokenStream::new(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                                [TokenTree::Token(Token::from_ast_ident(Ident::new(inner,
                                                    span)), Spacing::Alone)]))))]))vec![
748            TokenTree::Token(Token::from_ast_ident(Ident::new(outer, span)), Spacing::Alone),
749            TokenTree::Delimited(
750                DelimSpan::from_single(span),
751                DelimSpacing::new(Spacing::JointHidden, Spacing::Alone),
752                Delimiter::Parenthesis,
753                TokenStream::new(vec![TokenTree::Token(
754                    Token::from_ast_ident(Ident::new(inner, span)),
755                    Spacing::Alone,
756                )]),
757            ),
758        ]),
759        span,
760    ));
761
762    mk_attr_from_item(
763        g,
764        AttrItem { unsafety: Safety::Default, path, args: attr_args, span },
765        tokens,
766        style,
767        span,
768    )
769}
770
771// `span` is used for the `Attribute` and everything within it (except for any span within
772// `unsafety`).
773pub fn mk_attr_name_value_str(
774    g: &AttrIdGenerator,
775    style: AttrStyle,
776    name: Symbol,
777    val: Symbol,
778    span: Span,
779) -> Attribute {
780    let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
781    let expr = Box::new(Expr {
782        id: DUMMY_NODE_ID,
783        kind: ExprKind::Lit(lit),
784        span,
785        attrs: AttrVec::new(),
786        tokens: None,
787    });
788    let path = Path::from_ident(Ident::new(name, span));
789    let args = AttrArgs::Eq { eq_span: span, expr };
790
791    let tokens = Some(mk_attr_tokens(
792        style,
793        TokenStream::new(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [TokenTree::Token(Token::from_ast_ident(Ident::new(name, span)),
                    Spacing::Alone),
                TokenTree::Token(Token::new(token::Eq, span), Spacing::Alone),
                TokenTree::Token(Token::new(token::TokenKind::lit(lit.kind,
                            lit.symbol, lit.suffix), span), Spacing::Alone)]))vec![
794            TokenTree::Token(Token::from_ast_ident(Ident::new(name, span)), Spacing::Alone),
795            TokenTree::Token(Token::new(token::Eq, span), Spacing::Alone),
796            TokenTree::Token(
797                Token::new(token::TokenKind::lit(lit.kind, lit.symbol, lit.suffix), span),
798                Spacing::Alone,
799            ),
800        ]),
801        span,
802    ));
803
804    mk_attr_from_item(
805        g,
806        AttrItem { unsafety: Safety::Default, path, args, span },
807        tokens,
808        style,
809        span,
810    )
811}
812
813pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> {
814    attrs.iter().filter(move |attr| attr.has_name(name))
815}
816
817pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {
818    filter_by_name(attrs, name).next()
819}
820
821pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> {
822    find_by_name(attrs, name).and_then(|attr| attr.value_str())
823}
824
825pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {
826    find_by_name(attrs, name).is_some()
827}
828
829pub fn list_contains_name(items: &[MetaItemInner], name: Symbol) -> bool {
830    items.iter().any(|item| item.has_name(name))
831}
832
833impl MetaItemLit {
834    pub fn value_as_str(&self) -> Option<Symbol> {
835        LitKind::from_token_lit(self.as_token_lit()).ok().and_then(|lit| lit.str())
836    }
837}
838
839pub trait AttributeExt: Debug + 'static {
840    /// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`),
841    /// return the name of the attribute; otherwise, returns `None`.
842    fn name(&self) -> Option<Symbol>;
843
844    /// Gets the value literal, as string, when using `#[attr = value]`
845    fn value_str(&self) -> Option<Symbol>;
846
847    /// Checks whether the path of this attribute matches the name.
848    ///
849    /// Matches one segment of the path to each element in `name`
850    fn path_matches(&self, name: &[Symbol]) -> bool;
851
852    /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
853    /// * `///doc` returns `Some(("doc", CommentKind::Line))`.
854    /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
855    /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
856    /// * `#[doc(...)]` returns `None`.
857    fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind, Span)>;
858
859    /// Returns outer or inner if this is a doc attribute or a sugared doc
860    /// comment, otherwise None.
861    ///
862    /// This is used in the case of doc comments on modules, to decide whether
863    /// to resolve intra-doc links against the symbols in scope within the
864    /// commented module (for inner doc) vs within its parent module (for outer
865    /// doc).
866    fn doc_resolution_scope(&self) -> Option<AttrStyle>;
867}
868
869// FIXME(fn_delegation): use function delegation instead of manually forwarding
870
871impl Attribute {
872    pub fn name(&self) -> Option<Symbol> {
873        AttributeExt::name(self)
874    }
875
876    pub fn value_str(&self) -> Option<Symbol> {
877        AttributeExt::value_str(self)
878    }
879
880    /// Gets the span of the value literal, as string, when using `#[attr = value]`
881    pub fn value_span(&self) -> Option<Span> {
882        match &self.kind {
883            AttrKind::Normal(normal) => match &normal.item.args {
884                AttrArgs::Eq { expr, .. } => Some(expr.span),
885                _ => None,
886            },
887            AttrKind::Synthetic(..) | AttrKind::DocComment(..) => None,
888            AttrKind::Generated(generated) => generated.value_span(),
889        }
890    }
891
892    pub fn path_matches(&self, name: &[Symbol]) -> bool {
893        AttributeExt::path_matches(self, name)
894    }
895
896    /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
897    /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
898    /// a doc comment) will return `false`.
899    pub fn is_doc_comment(&self) -> bool {
900        match &self.kind {
901            AttrKind::Normal(..) | AttrKind::Synthetic(..) => false,
902            AttrKind::DocComment(..) => true,
903            AttrKind::Generated(generated) => generated.is_doc_comment(),
904        }
905    }
906
907    /// Returns true if the attribute's first *and only* path segment is equal to the passed-in
908    /// symbol.
909    #[inline]
910    pub fn has_name(&self, name: Symbol) -> bool {
911        self.name().map(|x| x == name).unwrap_or(false)
912    }
913
914    /// Returns true if the attribute's first *and only* path segment is any of the passed-in
915    /// symbols.
916    #[inline]
917    pub fn has_any_name(&self, names: &[Symbol]) -> bool {
918        names.iter().any(|&name| self.has_name(name))
919    }
920
921    /// Returns whether the attribute is a path, without any arguments.
922    pub fn is_word(&self) -> bool {
923        match &self.kind {
924            AttrKind::Normal(normal) => #[allow(non_exhaustive_omitted_patterns)] match normal.item.args {
    AttrArgs::Empty => true,
    _ => false,
}matches!(normal.item.args, AttrArgs::Empty),
925            AttrKind::Synthetic(..) => ::core::panicking::unreachable_default_msg()unreachable!(),
926            AttrKind::DocComment(..) => false,
927            AttrKind::Generated(generated) => generated.is_word(),
928        }
929    }
930
931    pub fn path(&self) -> SmallVec<[Symbol; 1]> {
932        use SyntheticAttr::*;
933        match &self.kind {
934            AttrKind::Normal(normal) => {
935                normal.item.path.segments.iter().map(|i| i.ident.name).collect()
936            }
937            AttrKind::Synthetic(CfgTrace(_)) => {
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(sym::cfg);
        vec
    } else {
        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [sym::cfg])))
    }
}smallvec![sym::cfg],
938            AttrKind::Synthetic(CfgAttrTrace(_)) => {
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(sym::cfg_attr);
        vec
    } else {
        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [sym::cfg_attr])))
    }
}smallvec![sym::cfg_attr],
939            AttrKind::DocComment(_, _) => {
    let count = 0usize + 1usize;
    let mut vec = ::smallvec::SmallVec::new();
    if count <= vec.inline_size() {
        vec.push(sym::doc);
        vec
    } else {
        ::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
                    [sym::doc])))
    }
}smallvec![sym::doc],
940            AttrKind::Generated(generated) => generated.path().iter().copied().collect(),
941        }
942    }
943
944    /// Returns the documentation if this is a doc comment or a sugared doc comment.
945    /// * `///doc` returns `Some("doc")`.
946    /// * `#[doc = "doc"]` returns `Some("doc")`.
947    /// * `#[doc(...)]` returns `None`.
948    pub fn doc_str(&self) -> Option<Symbol> {
949        match &self.kind {
950            AttrKind::DocComment(.., data) => Some(*data),
951            AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
952            _ => None,
953        }
954    }
955
956    /// Returns whether this attribute is any of the proc macro attributes.
957    /// i.e. `proc_macro`, `proc_macro_attribute` or `proc_macro_derive`.
958    pub fn is_proc_macro_attr(&self) -> bool {
959        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
960            .iter()
961            .any(|kind| self.has_name(*kind))
962    }
963
964    pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind, Span)> {
965        AttributeExt::doc_str_and_fragment_kind(self)
966    }
967}