rustc_ast/attr/
mod.rs

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