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