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        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(..) => 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(..) => 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(..) => 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            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<Symbol> {
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(s);
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(s);
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
301impl Attribute {
302    pub fn style(&self) -> AttrStyle {
303        self.style
304    }
305
306    pub fn may_have_doc_links(&self) -> bool {
307        self.doc_str().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
308            || self.deprecation_note().is_some_and(|s| comments::may_have_doc_links(s.as_str()))
309    }
310
311    /// Extracts the MetaItem from inside this Attribute.
312    pub fn meta(&self) -> Option<MetaItem> {
313        match &self.kind {
314            AttrKind::Normal(normal) => normal.item.meta(self.span),
315            AttrKind::DocComment(..) => None,
316        }
317    }
318
319    pub fn meta_kind(&self) -> Option<MetaItemKind> {
320        match &self.kind {
321            AttrKind::Normal(normal) => normal.item.meta_kind(),
322            AttrKind::DocComment(..) => None,
323        }
324    }
325
326    pub fn token_trees(&self) -> Vec<TokenTree> {
327        match self.kind {
328            AttrKind::Normal(ref normal) => normal
329                .tokens
330                .as_ref()
331                .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}"))
332                .to_attr_token_stream()
333                .to_token_trees(),
334            AttrKind::DocComment(comment_kind, data) => vec![TokenTree::token_alone(
335                token::DocComment(comment_kind, self.style, data),
336                self.span,
337            )],
338        }
339    }
340}
341
342impl AttrItem {
343    pub fn span(&self) -> Span {
344        self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
345    }
346
347    pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
348        match &self.args.unparsed_ref()? {
349            AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
350                MetaItemKind::list_from_tokens(args.tokens.clone())
351            }
352            AttrArgs::Delimited(_) | AttrArgs::Eq { .. } | AttrArgs::Empty => None,
353        }
354    }
355
356    /// Returns the string value in:
357    ///
358    /// ```text
359    /// #[attribute = "value"]
360    ///               ^^^^^^^
361    /// ```
362    ///
363    /// It returns `None` in any other cases like:
364    ///
365    /// ```text
366    /// #[attr("value")]
367    /// ```
368    fn value_str(&self) -> Option<Symbol> {
369        match &self.args.unparsed_ref()? {
370            AttrArgs::Eq { expr, .. } => match expr.kind {
371                ExprKind::Lit(token_lit) => {
372                    LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
373                }
374                _ => None,
375            },
376            AttrArgs::Delimited(_) | AttrArgs::Empty => None,
377        }
378    }
379
380    /// Returns the span in:
381    ///
382    /// ```text
383    /// #[attribute = "value"]
384    ///               ^^^^^^^
385    /// ```
386    ///
387    /// It returns `None` in any other cases like:
388    ///
389    /// ```text
390    /// #[attr("value")]
391    /// ```
392    fn value_span(&self) -> Option<Span> {
393        match &self.args.unparsed_ref()? {
394            AttrArgs::Eq { expr, .. } => Some(expr.span),
395            AttrArgs::Delimited(_) | AttrArgs::Empty => None,
396        }
397    }
398
399    pub fn meta(&self, span: Span) -> Option<MetaItem> {
400        Some(MetaItem {
401            unsafety: Safety::Default,
402            path: self.path.clone(),
403            kind: self.meta_kind()?,
404            span,
405        })
406    }
407
408    pub fn meta_kind(&self) -> Option<MetaItemKind> {
409        MetaItemKind::from_attr_args(self.args.unparsed_ref()?)
410    }
411}
412
413impl MetaItem {
414    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
415    pub fn ident(&self) -> Option<Ident> {
416        if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None }
417    }
418
419    pub fn name(&self) -> Option<Symbol> {
420        self.ident().map(|ident| ident.name)
421    }
422
423    pub fn has_name(&self, name: Symbol) -> bool {
424        self.path == name
425    }
426
427    pub fn is_word(&self) -> bool {
428        matches!(self.kind, MetaItemKind::Word)
429    }
430
431    pub fn meta_item_list(&self) -> Option<&[MetaItemInner]> {
432        match &self.kind {
433            MetaItemKind::List(l) => Some(&**l),
434            _ => None,
435        }
436    }
437
438    /// ```text
439    /// Example:
440    ///     #[attribute(name = "value")]
441    ///                 ^^^^^^^^^^^^^^
442    /// ```
443    pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
444        match &self.kind {
445            MetaItemKind::NameValue(v) => Some(v),
446            _ => None,
447        }
448    }
449
450    /// This is used in case you want the value span instead of the whole attribute. Example:
451    ///
452    /// ```text
453    /// #[doc(alias = "foo")]
454    /// ```
455    ///
456    /// In here, it'll return a span for `"foo"`.
457    pub fn name_value_literal_span(&self) -> Option<Span> {
458        Some(self.name_value_literal()?.span)
459    }
460
461    /// Returns the string value in:
462    ///
463    /// ```text
464    /// #[attribute = "value"]
465    ///               ^^^^^^^
466    /// ```
467    ///
468    /// It returns `None` in any other cases like:
469    ///
470    /// ```text
471    /// #[attr("value")]
472    /// ```
473    pub fn value_str(&self) -> Option<Symbol> {
474        match &self.kind {
475            MetaItemKind::NameValue(v) => v.kind.str(),
476            _ => None,
477        }
478    }
479
480    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItem> {
481        // FIXME: Share code with `parse_path`.
482        let tt = iter.next().map(|tt| TokenTree::uninterpolate(tt));
483        let path = match tt.as_deref() {
484            Some(&TokenTree::Token(
485                Token { kind: ref kind @ (token::Ident(..) | token::PathSep), span },
486                _,
487            )) => 'arm: {
488                let mut segments = if let &token::Ident(name, _) = kind {
489                    if let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) =
490                        iter.peek()
491                    {
492                        iter.next();
493                        thin_vec![PathSegment::from_ident(Ident::new(name, span))]
494                    } else {
495                        break 'arm Path::from_ident(Ident::new(name, span));
496                    }
497                } else {
498                    thin_vec![PathSegment::path_root(span)]
499                };
500                loop {
501                    let Some(&TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
502                        iter.next().map(|tt| TokenTree::uninterpolate(tt)).as_deref()
503                    else {
504                        return None;
505                    };
506                    segments.push(PathSegment::from_ident(Ident::new(name, span)));
507                    let Some(TokenTree::Token(Token { kind: token::PathSep, .. }, _)) = iter.peek()
508                    else {
509                        break;
510                    };
511                    iter.next();
512                }
513                let span = span.with_hi(segments.last().unwrap().ident.span.hi());
514                Path { span, segments, tokens: None }
515            }
516            Some(TokenTree::Delimited(
517                _span,
518                _spacing,
519                Delimiter::Invisible(InvisibleOrigin::MetaVar(
520                    MetaVarKind::Meta { .. } | MetaVarKind::Path,
521                )),
522                _stream,
523            )) => {
524                // This path is currently unreachable in the test suite.
525                unreachable!()
526            }
527            Some(TokenTree::Token(Token { kind, .. }, _)) if kind.is_delim() => {
528                panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tt);
529            }
530            _ => return None,
531        };
532        let list_closing_paren_pos = iter.peek().map(|tt| tt.span().hi());
533        let kind = MetaItemKind::from_tokens(iter)?;
534        let hi = match &kind {
535            MetaItemKind::NameValue(lit) => lit.span.hi(),
536            MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
537            _ => path.span.hi(),
538        };
539        let span = path.span.with_hi(hi);
540        // FIXME: This parses `unsafe()` not as unsafe attribute syntax in `MetaItem`,
541        // but as a parenthesized list. This (and likely `MetaItem`) should be changed in
542        // such a way that builtin macros don't accept extraneous `unsafe()`.
543        Some(MetaItem { unsafety: Safety::Default, path, kind, span })
544    }
545}
546
547impl MetaItemKind {
548    // public because it can be called in the hir
549    pub fn list_from_tokens(tokens: TokenStream) -> Option<ThinVec<MetaItemInner>> {
550        let mut iter = tokens.iter();
551        let mut result = ThinVec::new();
552        while iter.peek().is_some() {
553            let item = MetaItemInner::from_tokens(&mut iter)?;
554            result.push(item);
555            match iter.next() {
556                None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}
557                _ => return None,
558            }
559        }
560        Some(result)
561    }
562
563    fn name_value_from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {
564        match iter.next() {
565            Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
566                MetaItemKind::name_value_from_tokens(&mut inner_tokens.iter())
567            }
568            Some(TokenTree::Token(token, _)) => {
569                MetaItemLit::from_token(token).map(MetaItemKind::NameValue)
570            }
571            _ => None,
572        }
573    }
574
575    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemKind> {
576        match iter.peek() {
577            Some(TokenTree::Delimited(.., Delimiter::Parenthesis, inner_tokens)) => {
578                let inner_tokens = inner_tokens.clone();
579                iter.next();
580                MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
581            }
582            Some(TokenTree::Delimited(..)) => None,
583            Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
584                iter.next();
585                MetaItemKind::name_value_from_tokens(iter)
586            }
587            _ => Some(MetaItemKind::Word),
588        }
589    }
590
591    fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {
592        match args {
593            AttrArgs::Empty => Some(MetaItemKind::Word),
594            AttrArgs::Delimited(DelimArgs { dspan: _, delim: Delimiter::Parenthesis, tokens }) => {
595                MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List)
596            }
597            AttrArgs::Delimited(..) => None,
598            AttrArgs::Eq { expr, .. } => match expr.kind {
599                ExprKind::Lit(token_lit) => {
600                    // Turn failures to `None`, we'll get parse errors elsewhere.
601                    MetaItemLit::from_token_lit(token_lit, expr.span)
602                        .ok()
603                        .map(|lit| MetaItemKind::NameValue(lit))
604                }
605                _ => None,
606            },
607        }
608    }
609}
610
611impl MetaItemInner {
612    pub fn span(&self) -> Span {
613        match self {
614            MetaItemInner::MetaItem(item) => item.span,
615            MetaItemInner::Lit(lit) => lit.span,
616        }
617    }
618
619    /// For a single-segment meta item, returns its identifier; otherwise, returns `None`.
620    pub fn ident(&self) -> Option<Ident> {
621        self.meta_item().and_then(|meta_item| meta_item.ident())
622    }
623
624    /// For a single-segment meta item, returns its name; otherwise, returns `None`.
625    pub fn name(&self) -> Option<Symbol> {
626        self.ident().map(|ident| ident.name)
627    }
628
629    /// Returns `true` if this list item is a MetaItem with a name of `name`.
630    pub fn has_name(&self, name: Symbol) -> bool {
631        self.meta_item().is_some_and(|meta_item| meta_item.has_name(name))
632    }
633
634    /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
635    pub fn is_word(&self) -> bool {
636        self.meta_item().is_some_and(|meta_item| meta_item.is_word())
637    }
638
639    /// Gets a list of inner meta items from a list `MetaItem` type.
640    pub fn meta_item_list(&self) -> Option<&[MetaItemInner]> {
641        self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
642    }
643
644    /// If it's a singleton list of the form `foo(lit)`, returns the `foo` and
645    /// the `lit`.
646    pub fn singleton_lit_list(&self) -> Option<(Symbol, &MetaItemLit)> {
647        self.meta_item().and_then(|meta_item| {
648            meta_item.meta_item_list().and_then(|meta_item_list| {
649                if meta_item_list.len() == 1
650                    && let Some(ident) = meta_item.ident()
651                    && let Some(lit) = meta_item_list[0].lit()
652                {
653                    return Some((ident.name, lit));
654                }
655                None
656            })
657        })
658    }
659
660    /// See [`MetaItem::name_value_literal_span`].
661    pub fn name_value_literal_span(&self) -> Option<Span> {
662        self.meta_item()?.name_value_literal_span()
663    }
664
665    /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
666    /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
667    pub fn value_str(&self) -> Option<Symbol> {
668        self.meta_item().and_then(|meta_item| meta_item.value_str())
669    }
670
671    /// Returns the `MetaItemLit` if `self` is a `MetaItemInner::Literal`s.
672    pub fn lit(&self) -> Option<&MetaItemLit> {
673        match self {
674            MetaItemInner::Lit(lit) => Some(lit),
675            _ => None,
676        }
677    }
678
679    /// Returns the bool if `self` is a boolean `MetaItemInner::Literal`.
680    pub fn boolean_literal(&self) -> Option<bool> {
681        match self {
682            MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => Some(*b),
683            _ => None,
684        }
685    }
686
687    /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem` or if it's
688    /// `MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. })`.
689    pub fn meta_item_or_bool(&self) -> Option<&MetaItemInner> {
690        match self {
691            MetaItemInner::MetaItem(_item) => Some(self),
692            MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(_), .. }) => Some(self),
693            _ => None,
694        }
695    }
696
697    /// Returns the `MetaItem` if `self` is a `MetaItemInner::MetaItem`.
698    pub fn meta_item(&self) -> Option<&MetaItem> {
699        match self {
700            MetaItemInner::MetaItem(item) => Some(item),
701            _ => None,
702        }
703    }
704
705    /// Returns `true` if the variant is `MetaItem`.
706    pub fn is_meta_item(&self) -> bool {
707        self.meta_item().is_some()
708    }
709
710    fn from_tokens(iter: &mut TokenStreamIter<'_>) -> Option<MetaItemInner> {
711        match iter.peek() {
712            Some(TokenTree::Token(token, _)) if let Some(lit) = MetaItemLit::from_token(token) => {
713                iter.next();
714                return Some(MetaItemInner::Lit(lit));
715            }
716            Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => {
717                iter.next();
718                return MetaItemInner::from_tokens(&mut inner_tokens.iter());
719            }
720            _ => {}
721        }
722        MetaItem::from_tokens(iter).map(MetaItemInner::MetaItem)
723    }
724}
725
726pub fn mk_doc_comment(
727    g: &AttrIdGenerator,
728    comment_kind: CommentKind,
729    style: AttrStyle,
730    data: Symbol,
731    span: Span,
732) -> Attribute {
733    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
734}
735
736fn mk_attr(
737    g: &AttrIdGenerator,
738    style: AttrStyle,
739    unsafety: Safety,
740    path: Path,
741    args: AttrArgs,
742    span: Span,
743) -> Attribute {
744    mk_attr_from_item(
745        g,
746        AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args), tokens: None },
747        None,
748        style,
749        span,
750    )
751}
752
753pub fn mk_attr_from_item(
754    g: &AttrIdGenerator,
755    item: AttrItem,
756    tokens: Option<LazyAttrTokenStream>,
757    style: AttrStyle,
758    span: Span,
759) -> Attribute {
760    Attribute {
761        kind: AttrKind::Normal(Box::new(NormalAttr { item, tokens })),
762        id: g.mk_attr_id(),
763        style,
764        span,
765    }
766}
767
768pub fn mk_attr_word(
769    g: &AttrIdGenerator,
770    style: AttrStyle,
771    unsafety: Safety,
772    name: Symbol,
773    span: Span,
774) -> Attribute {
775    let path = Path::from_ident(Ident::new(name, span));
776    let args = AttrArgs::Empty;
777    mk_attr(g, style, unsafety, path, args, span)
778}
779
780pub fn mk_attr_nested_word(
781    g: &AttrIdGenerator,
782    style: AttrStyle,
783    unsafety: Safety,
784    outer: Symbol,
785    inner: Symbol,
786    span: Span,
787) -> Attribute {
788    let inner_tokens = TokenStream::new(vec![TokenTree::Token(
789        Token::from_ast_ident(Ident::new(inner, span)),
790        Spacing::Alone,
791    )]);
792    let outer_ident = Ident::new(outer, span);
793    let path = Path::from_ident(outer_ident);
794    let attr_args = AttrArgs::Delimited(DelimArgs {
795        dspan: DelimSpan::from_single(span),
796        delim: Delimiter::Parenthesis,
797        tokens: inner_tokens,
798    });
799    mk_attr(g, style, unsafety, path, attr_args, span)
800}
801
802pub fn mk_attr_name_value_str(
803    g: &AttrIdGenerator,
804    style: AttrStyle,
805    unsafety: Safety,
806    name: Symbol,
807    val: Symbol,
808    span: Span,
809) -> Attribute {
810    let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
811    let expr = Box::new(Expr {
812        id: DUMMY_NODE_ID,
813        kind: ExprKind::Lit(lit),
814        span,
815        attrs: AttrVec::new(),
816        tokens: None,
817    });
818    let path = Path::from_ident(Ident::new(name, span));
819    let args = AttrArgs::Eq { eq_span: span, expr };
820    mk_attr(g, style, unsafety, path, args, span)
821}
822
823pub fn filter_by_name<A: AttributeExt>(attrs: &[A], name: Symbol) -> impl Iterator<Item = &A> {
824    attrs.iter().filter(move |attr| attr.has_name(name))
825}
826
827pub fn find_by_name<A: AttributeExt>(attrs: &[A], name: Symbol) -> Option<&A> {
828    filter_by_name(attrs, name).next()
829}
830
831pub fn first_attr_value_str_by_name(attrs: &[impl AttributeExt], name: Symbol) -> Option<Symbol> {
832    find_by_name(attrs, name).and_then(|attr| attr.value_str())
833}
834
835pub fn contains_name(attrs: &[impl AttributeExt], name: Symbol) -> bool {
836    find_by_name(attrs, name).is_some()
837}
838
839pub fn list_contains_name(items: &[MetaItemInner], name: Symbol) -> bool {
840    items.iter().any(|item| item.has_name(name))
841}
842
843impl MetaItemLit {
844    pub fn value_str(&self) -> Option<Symbol> {
845        LitKind::from_token_lit(self.as_token_lit()).ok().and_then(|lit| lit.str())
846    }
847}
848
849pub trait AttributeExt: Debug {
850    fn id(&self) -> AttrId;
851
852    /// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`),
853    /// return the name of the attribute; otherwise, returns `None`.
854    fn name(&self) -> Option<Symbol>;
855
856    /// Get the meta item list, `#[attr(meta item list)]`
857    fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>>;
858
859    /// Gets the value literal, as string, when using `#[attr = value]`
860    fn value_str(&self) -> Option<Symbol>;
861
862    /// Gets the span of the value literal, as string, when using `#[attr = value]`
863    fn value_span(&self) -> Option<Span>;
864
865    /// Checks whether the path of this attribute matches the name.
866    ///
867    /// Matches one segment of the path to each element in `name`
868    fn path_matches(&self, name: &[Symbol]) -> bool;
869
870    /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
871    /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
872    /// a doc comment) will return `false`.
873    fn is_doc_comment(&self) -> Option<Span>;
874
875    #[inline]
876    fn has_name(&self, name: Symbol) -> bool {
877        self.name().map(|x| x == name).unwrap_or(false)
878    }
879
880    #[inline]
881    fn has_any_name(&self, names: &[Symbol]) -> bool {
882        names.iter().any(|&name| self.has_name(name))
883    }
884
885    /// get the span of the entire attribute
886    fn span(&self) -> Span;
887
888    fn is_word(&self) -> bool;
889
890    fn path(&self) -> SmallVec<[Symbol; 1]> {
891        self.symbol_path().unwrap_or(smallvec![sym::doc])
892    }
893
894    fn path_span(&self) -> Option<Span>;
895
896    /// Returns None for doc comments
897    fn symbol_path(&self) -> Option<SmallVec<[Symbol; 1]>>;
898
899    /// Returns the documentation if this is a doc comment or a sugared doc comment.
900    /// * `///doc` returns `Some("doc")`.
901    /// * `#[doc = "doc"]` returns `Some("doc")`.
902    /// * `#[doc(...)]` returns `None`.
903    fn doc_str(&self) -> Option<Symbol>;
904
905    /// Returns the deprecation note if this is deprecation attribute.
906    /// * `#[deprecated = "note"]` returns `Some("note")`.
907    /// * `#[deprecated(note = "note", ...)]` returns `Some("note")`.
908    fn deprecation_note(&self) -> Option<Symbol>;
909
910    fn is_proc_macro_attr(&self) -> bool {
911        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
912            .iter()
913            .any(|kind| self.has_name(*kind))
914    }
915    fn is_automatically_derived_attr(&self) -> bool;
916
917    /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
918    /// * `///doc` returns `Some(("doc", CommentKind::Line))`.
919    /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
920    /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
921    /// * `#[doc(...)]` returns `None`.
922    fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)>;
923
924    /// Returns outer or inner if this is a doc attribute or a sugared doc
925    /// comment, otherwise None.
926    ///
927    /// This is used in the case of doc comments on modules, to decide whether
928    /// to resolve intra-doc links against the symbols in scope within the
929    /// commented module (for inner doc) vs within its parent module (for outer
930    /// doc).
931    fn doc_resolution_scope(&self) -> Option<AttrStyle>;
932
933    /// Returns `true` if this attribute contains `doc(hidden)`.
934    fn is_doc_hidden(&self) -> bool;
935
936    /// Returns `true` is this attribute contains `doc(keyword)` or `doc(attribute)`.
937    fn is_doc_keyword_or_attribute(&self) -> bool;
938}
939
940// FIXME(fn_delegation): use function delegation instead of manually forwarding
941
942impl Attribute {
943    pub fn id(&self) -> AttrId {
944        AttributeExt::id(self)
945    }
946
947    pub fn name(&self) -> Option<Symbol> {
948        AttributeExt::name(self)
949    }
950
951    pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
952        AttributeExt::meta_item_list(self)
953    }
954
955    pub fn value_str(&self) -> Option<Symbol> {
956        AttributeExt::value_str(self)
957    }
958
959    pub fn value_span(&self) -> Option<Span> {
960        AttributeExt::value_span(self)
961    }
962
963    pub fn path_matches(&self, name: &[Symbol]) -> bool {
964        AttributeExt::path_matches(self, name)
965    }
966
967    // on ast attributes we return a bool since that's what most code already expects
968    pub fn is_doc_comment(&self) -> bool {
969        AttributeExt::is_doc_comment(self).is_some()
970    }
971
972    #[inline]
973    pub fn has_name(&self, name: Symbol) -> bool {
974        AttributeExt::has_name(self, name)
975    }
976
977    #[inline]
978    pub fn has_any_name(&self, names: &[Symbol]) -> bool {
979        AttributeExt::has_any_name(self, names)
980    }
981
982    pub fn span(&self) -> Span {
983        AttributeExt::span(self)
984    }
985
986    pub fn is_word(&self) -> bool {
987        AttributeExt::is_word(self)
988    }
989
990    pub fn path(&self) -> SmallVec<[Symbol; 1]> {
991        AttributeExt::path(self)
992    }
993
994    pub fn doc_str(&self) -> Option<Symbol> {
995        AttributeExt::doc_str(self)
996    }
997
998    pub fn is_proc_macro_attr(&self) -> bool {
999        AttributeExt::is_proc_macro_attr(self)
1000    }
1001
1002    pub fn doc_str_and_fragment_kind(&self) -> Option<(Symbol, DocFragmentKind)> {
1003        AttributeExt::doc_str_and_fragment_kind(self)
1004    }
1005}