Skip to main content

rustc_ast/attr/
mod.rs

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