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::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;
28
29pub struct MarkedAttrs(GrowableBitSet<AttrId>);
30
31impl MarkedAttrs {
32    pub fn new() -> Self {
33        // We have no idea how many attributes there will be, so just
34        // initiate the vectors with 0 bits. We'll grow them as necessary.
35        MarkedAttrs(GrowableBitSet::new_empty())
36    }
37
38    pub fn mark(&mut self, attr: &Attribute) {
39        self.0.insert(attr.id);
40    }
41
42    pub fn is_marked(&self, attr: &Attribute) -> bool {
43        self.0.contains(attr.id)
44    }
45}
46
47pub struct AttrIdGenerator(AtomicU32);
48
49impl AttrIdGenerator {
50    pub fn new() -> Self {
51        AttrIdGenerator(AtomicU32::new(0))
52    }
53
54    pub fn mk_attr_id(&self) -> AttrId {
55        let id = self.0.fetch_add(1, Ordering::Relaxed);
56        if !(id != u32::MAX) {
    ::core::panicking::panic("assertion failed: id != u32::MAX")
};assert!(id != u32::MAX);
57        AttrId::from_u32(id)
58    }
59}
60
61impl Attribute {
62    pub fn get_normal_item(&self) -> &AttrItem {
63        match &self.kind {
64            AttrKind::Normal(normal) => &normal.item,
65            AttrKind::DocComment(..) => { ::core::panicking::panic_fmt(format_args!("unexpected doc comment")); }panic!("unexpected doc comment"),
66        }
67    }
68
69    /// 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(..) => { ::core::panicking::panic_fmt(format_args!("unexpected doc comment")); }panic!("unexpected doc comment"),
75        }
76    }
77
78    pub fn unwrap_normal_item(self) -> AttrItem {
79        match self.kind {
80            AttrKind::Normal(normal) => normal.item,
81            AttrKind::DocComment(..) => { ::core::panicking::panic_fmt(format_args!("unexpected doc comment")); }panic!("unexpected doc comment"),
82        }
83    }
84}
85
86impl AttributeExt for Attribute {
87    fn id(&self) -> AttrId {
88        self.id
89    }
90
91    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    }
100
101    /// 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 not
103    /// 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    }
110
111    /// 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                    None
119                }
120            }
121            AttrKind::DocComment(..) => None,
122        }
123    }
124
125    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    }
133
134    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    }
140
141    fn path_matches(&self, name: &[Symbol]) -> bool {
142        match &self.kind {
143            AttrKind::Normal(normal) => {
144                normal.item.path.segments.len() == name.len()
145                    && normal
146                        .item
147                        .path
148                        .segments
149                        .iter()
150                        .zip(name)
151                        .all(|(s, n)| s.args.is_none() && s.ident.name == *n)
152            }
153            AttrKind::DocComment(..) => false,
154        }
155    }
156
157    fn span(&self) -> Span {
158        self.span
159    }
160
161    fn is_word(&self) -> bool {
162        if let AttrKind::Normal(normal) = &self.kind {
163            #[allow(non_exhaustive_omitted_patterns)] match normal.item.args {
    AttrItemKind::Unparsed(AttrArgs::Empty) => true,
    _ => false,
}matches!(normal.item.args, AttrItemKind::Unparsed(AttrArgs::Empty))
164        } else {
165            false
166        }
167    }
168
169    /// Returns a list of meta items if the attribute is delimited with parenthesis:
170    ///
171    /// ```text
172    /// #[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    }
182
183    /// Returns the string value in:
184    ///
185    /// ```text
186    /// #[attribute = "value"]
187    ///               ^^^^^^^
188    /// ```
189    ///
190    /// It returns `None` in any other cases, including doc comments if they
191    /// are not under the form `#[doc = "..."]`.
192    ///
193    /// It also returns `None` for:
194    ///
195    /// ```text
196    /// #[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    }
204
205    /// 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                    None
220                }
221            }
222            _ => None,
223        }
224    }
225
226    /// 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    }
237
238    fn deprecation_note(&self) -> Option<Ident> {
239        match &self.kind {
240            AttrKind::Normal(normal) if normal.item.path == sym::deprecated => {
241                let meta = &normal.item;
242
243                // #[deprecated = "..."]
244                if let Some(s) = meta.value_str() {
245                    return Some(Ident { name: s, span: meta.span() });
246                }
247
248                // #[deprecated(note = "...")]
249                if let Some(list) = meta.meta_item_list() {
250                    for nested in list {
251                        if let Some(mi) = nested.meta_item()
252                            && mi.path == sym::note
253                            && let Some(s) = mi.value_str()
254                        {
255                            return Some(Ident { name: s, span: mi.span });
256                        }
257                    }
258                }
259
260                None
261            }
262            _ => None,
263        }
264    }
265
266    fn doc_resolution_scope(&self) -> Option<AttrStyle> {
267        match &self.kind {
268            AttrKind::DocComment(..) => Some(self.style),
269            AttrKind::Normal(normal)
270                if normal.item.path == sym::doc && normal.item.value_str().is_some() =>
271            {
272                Some(self.style)
273            }
274            _ => None,
275        }
276    }
277
278    fn is_automatically_derived_attr(&self) -> bool {
279        self.has_name(sym::automatically_derived)
280    }
281
282    fn is_doc_hidden(&self) -> bool {
283        self.has_name(sym::doc)
284            && self.meta_item_list().is_some_and(|l| list_contains_name(&l, sym::hidden))
285    }
286
287    fn is_doc_keyword_or_attribute(&self) -> bool {
288        if self.has_name(sym::doc)
289            && let Some(items) = self.meta_item_list()
290        {
291            for item in items {
292                if item.has_name(sym::keyword) || item.has_name(sym::attribute) {
293                    return true;
294                }
295            }
296        }
297        false
298    }
299
300    fn is_rustc_doc_primitive(&self) -> bool {
301        self.has_name(sym::rustc_doc_primitive)
302    }
303}
304
305impl Attribute {
306    pub fn style(&self) -> AttrStyle {
307        self.style
308    }
309
310    pub fn may_have_doc_links(&self) -> bool {
311        self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
312            || self.deprecation_note().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
313    }
314
315    /// Extracts the MetaItem from inside this Attribute.
316    pub fn meta(&self) -> Option<MetaItem> {
317        match &self.kind {
318            AttrKind::Normal(normal) => normal.item.meta(self.span),
319            AttrKind::DocComment(..) => None,
320        }
321    }
322
323    pub fn meta_kind(&self) -> Option<MetaItemKind> {
324        match &self.kind {
325            AttrKind::Normal(normal) => normal.item.meta_kind(),
326            AttrKind::DocComment(..) => None,
327        }
328    }
329
330    pub fn token_trees(&self) -> Vec<TokenTree> {
331        match self.kind {
332            AttrKind::Normal(ref normal) => normal
333                .tokens
334                .as_ref()
335                .unwrap_or_else(|| {
    ::core::panicking::panic_fmt(format_args!("attribute is missing tokens: {0:?}",
            self));
}panic!("attribute is missing tokens: {self:?}"))
336                .to_attr_token_stream()
337                .to_token_trees(),
338            AttrKind::DocComment(comment_kind, data) => ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [TokenTree::token_alone(token::DocComment(comment_kind, self.style,
                        data), self.span)]))vec![TokenTree::token_alone(
339                token::DocComment(comment_kind, self.style, data),
340                self.span,
341            )],
342        }
343    }
344}
345
346impl 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    }
350
351    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    }
359
360    /// Returns the string value in:
361    ///
362    /// ```text
363    /// #[attribute = "value"]
364    ///               ^^^^^^^
365    /// ```
366    ///
367    /// It returns `None` in any other cases like:
368    ///
369    /// ```text
370    /// #[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    }
383
384    /// Returns the span in:
385    ///
386    /// ```text
387    /// #[attribute = "value"]
388    ///               ^^^^^^^
389    /// ```
390    ///
391    /// It returns `None` in any other cases like:
392    ///
393    /// ```text
394    /// #[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    }
402
403    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    }
411
412    pub fn meta_kind(&self) -> Option<MetaItemKind> {
413        MetaItemKind::from_attr_args(self.args.unparsed_ref()?)
414    }
415}
416
417impl 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    }
422
423    pub fn name(&self) -> Option<Symbol> {
424        self.ident().map(|ident| ident.name)
425    }
426
427    pub fn has_name(&self, name: Symbol) -> bool {
428        self.path == name
429    }
430
431    pub fn is_word(&self) -> bool {
432        #[allow(non_exhaustive_omitted_patterns)] match self.kind {
    MetaItemKind::Word => true,
    _ => false,
}matches!(self.kind, MetaItemKind::Word)
433    }
434
435    pub fn meta_item_list(&self) -> Option<&[MetaItemInner]> {
436        match &self.kind {
437            MetaItemKind::List(l) => Some(&**l),
438            _ => None,
439        }
440    }
441
442    /// ```text
443    /// 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    }
453
454    /// This is used in case you want the value span instead of the whole attribute. Example:
455    ///
456    /// ```text
457    /// #[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    }
464
465    /// Returns the string value in:
466    ///
467    /// ```text
468    /// #[attribute = "value"]
469    ///               ^^^^^^^
470    /// ```
471    ///
472    /// It returns `None` in any other cases like:
473    ///
474    /// ```text
475    /// #[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    }
483
484    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                        {
    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))]
498                    } else {
499                        break 'arm Path::from_ident(Ident::new(name, span));
500                    }
501                } else {
502                    {
    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)]
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                ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
530            }
531            Some(TokenTree::Token(Token { kind, .. }, _)) if kind.is_delim() => {
532                {
    ::core::panicking::panic_fmt(format_args!("Should be `AttrTokenTree::Delimited`, not delim tokens: {0:?}",
            tt));
};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 in
546        // such a way that builtin macros don't accept extraneous `unsafe()`.
547        Some(MetaItem { unsafety: Safety::Default, path, kind, span })
548    }
549}
550
551impl MetaItemKind {
552    // public because it can be called in the hir
553    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    }
566
567    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    }
578
579    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    }
594
595    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}
614
615impl MetaItemInner {
616    pub fn span(&self) -> Span {
617        match self {
618            MetaItemInner::MetaItem(item) => item.span,
619            MetaItemInner::Lit(lit) => lit.span,
620        }
621    }
622
623    /// 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    }
627
628    /// 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    }
632
633    /// 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    }
637
638    /// 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    }
642
643    /// 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    }
647
648    /// If it's a singleton list of the form `foo(lit)`, returns the `foo` and
649    /// 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() == 1
654                    && let Some(ident) = meta_item.ident()
655                    && let Some(lit) = meta_item_list[0].lit()
656                {
657                    return Some((ident.name, lit));
658                }
659                None
660            })
661        })
662    }
663
664    /// 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    }
668
669    /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
670    /// `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    }
674
675    /// 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    }
682
683    /// 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    }
690
691    /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem` or if it's
692    /// `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    }
700
701    /// 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    }
708
709    /// Returns `true` if the variant is `MetaItem`.
710    pub fn is_meta_item(&self) -> bool {
711        self.meta_item().is_some()
712    }
713
714    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}
729
730pub 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}
739
740fn 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}
756
757pub 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}
771
772pub 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}
783
784pub 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(::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(
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}
805
806pub 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}
826
827pub fn filter_by_name<A: AttributeExt>(attrs: &[A], name: Symbol) -> impl Iterator<Item = &A> {
828    attrs.iter().filter(move |attr| attr.has_name(name))
829}
830
831pub fn find_by_name<A: AttributeExt>(attrs: &[A], name: Symbol) -> Option<&A> {
832    filter_by_name(attrs, name).next()
833}
834
835pub fn first_attr_value_str_by_name(attrs: &[impl AttributeExt], name: Symbol) -> Option<Symbol> {
836    find_by_name(attrs, name).and_then(|attr| attr.value_str())
837}
838
839pub fn contains_name(attrs: &[impl AttributeExt], name: Symbol) -> bool {
840    find_by_name(attrs, name).is_some()
841}
842
843pub fn list_contains_name(items: &[MetaItemInner], name: Symbol) -> bool {
844    items.iter().any(|item| item.has_name(name))
845}
846
847impl 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}
852
853pub trait AttributeExt: Debug {
854    fn id(&self) -> AttrId;
855
856    /// 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>;
859
860    /// Get the meta item list, `#[attr(meta item list)]`
861    fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>>;
862
863    /// Gets the value literal, as string, when using `#[attr = value]`
864    fn value_str(&self) -> Option<Symbol>;
865
866    /// Gets the span of the value literal, as string, when using `#[attr = value]`
867    fn value_span(&self) -> Option<Span>;
868
869    /// 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;
873
874    /// 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 not
876    /// a doc comment) will return `false`.
877    fn is_doc_comment(&self) -> Option<Span>;
878
879    #[inline]
880    fn has_name(&self, name: Symbol) -> bool {
881        self.name().map(|x| x == name).unwrap_or(false)
882    }
883
884    #[inline]
885    fn has_any_name(&self, names: &[Symbol]) -> bool {
886        names.iter().any(|&name| self.has_name(name))
887    }
888
889    /// get the span of the entire attribute
890    fn span(&self) -> Span;
891
892    fn is_word(&self) -> bool;
893
894    fn path(&self) -> SmallVec<[Symbol; 1]> {
895        self.symbol_path().unwrap_or({
    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])
896    }
897
898    fn path_span(&self) -> Option<Span>;
899
900    /// Returns None for doc comments
901    fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>>;
902
903    /// Returns the documentation if this is a doc comment or a sugared doc comment.
904    /// * `///doc` returns `Some("doc")`.
905    /// * `#[doc = "doc"]` returns `Some("doc")`.
906    /// * `#[doc(...)]` returns `None`.
907    fn doc_str(&self) -> Option<Symbol>;
908
909    /// Returns the deprecation note if this is deprecation attribute.
910    /// * `#[deprecated = "note"]` returns `Some("note")`.
911    /// * `#[deprecated(note = "note", ...)]` returns `Some("note")`.
912    fn deprecation_note(&self) -> Option<Ident>;
913
914    fn is_proc_macro_attr(&self) -> bool {
915        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
916            .iter()
917            .any(|kind| self.has_name(*kind))
918    }
919    fn is_automatically_derived_attr(&self) -> bool;
920
921    /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
922    /// * `///doc` returns `Some(("doc", CommentKind::Line))`.
923    /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
924    /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
925    /// * `#[doc(...)]` returns `None`.
926    fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)>;
927
928    /// Returns outer or inner if this is a doc attribute or a sugared doc
929    /// comment, otherwise None.
930    ///
931    /// This is used in the case of doc comments on modules, to decide whether
932    /// to resolve intra-doc links against the symbols in scope within the
933    /// commented module (for inner doc) vs within its parent module (for outer
934    /// doc).
935    fn doc_resolution_scope(&self) -> Option<AttrStyle>;
936
937    /// Returns `true` if this attribute contains `doc(hidden)`.
938    fn is_doc_hidden(&self) -> bool;
939
940    /// Returns `true` is this attribute contains `doc(keyword)` or `doc(attribute)`.
941    fn is_doc_keyword_or_attribute(&self) -> bool;
942
943    /// Returns `true` if this is a `#[rustc_doc_primitive]` attribute.
944    fn is_rustc_doc_primitive(&self) -> bool;
945}
946
947// FIXME(fn_delegation): use function delegation instead of manually forwarding
948
949impl Attribute {
950    pub fn id(&self) -> AttrId {
951        AttributeExt::id(self)
952    }
953
954    pub fn name(&self) -> Option<Symbol> {
955        AttributeExt::name(self)
956    }
957
958    pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
959        AttributeExt::meta_item_list(self)
960    }
961
962    pub fn value_str(&self) -> Option<Symbol> {
963        AttributeExt::value_str(self)
964    }
965
966    pub fn value_span(&self) -> Option<Span> {
967        AttributeExt::value_span(self)
968    }
969
970    pub fn path_matches(&self, name: &[Symbol]) -> bool {
971        AttributeExt::path_matches(self, name)
972    }
973
974    // on ast attributes we return a bool since that's what most code already expects
975    pub fn is_doc_comment(&self) -> bool {
976        AttributeExt::is_doc_comment(self).is_some()
977    }
978
979    #[inline]
980    pub fn has_name(&self, name: Symbol) -> bool {
981        AttributeExt::has_name(self, name)
982    }
983
984    #[inline]
985    pub fn has_any_name(&self, names: &[Symbol]) -> bool {
986        AttributeExt::has_any_name(self, names)
987    }
988
989    pub fn span(&self) -> Span {
990        AttributeExt::span(self)
991    }
992
993    pub fn is_word(&self) -> bool {
994        AttributeExt::is_word(self)
995    }
996
997    pub fn path(&self) -> SmallVec<[Symbol; 1]> {
998        AttributeExt::path(self)
999    }
1000
1001    pub fn doc_str(&self) -> Option<Symbol> {
1002        AttributeExt::doc_str(self)
1003    }
1004
1005    pub fn is_proc_macro_attr(&self) -> bool {
1006        AttributeExt::is_proc_macro_attr(self)
1007    }
1008
1009    pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
1010        AttributeExt::doc_str_and_fragment_kind(self)
1011    }
1012}