]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_ast/src/attr/mod.rs
1ba4691467586bd6edfa817770173761e70aae6c
[rust.git] / compiler / rustc_ast / src / attr / mod.rs
1 //! Functions dealing with attributes and meta items.
2
3 use crate::ast;
4 use crate::ast::{AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute};
5 use crate::ast::{DelimArgs, Expr, ExprKind, LitKind, MetaItemLit};
6 use crate::ast::{MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem, NormalAttr};
7 use crate::ast::{Path, PathSegment, StrStyle, DUMMY_NODE_ID};
8 use crate::ptr::P;
9 use crate::token::{self, CommentKind, Delimiter, Token};
10 use crate::tokenstream::{DelimSpan, Spacing, TokenTree};
11 use crate::tokenstream::{LazyAttrTokenStream, TokenStream};
12 use crate::util::comments;
13 use rustc_data_structures::sync::WorkerLocal;
14 use rustc_index::bit_set::GrowableBitSet;
15 use rustc_span::symbol::{sym, Ident, Symbol};
16 use rustc_span::Span;
17 use std::cell::Cell;
18 use std::iter;
19 #[cfg(debug_assertions)]
20 use std::ops::BitXor;
21 #[cfg(debug_assertions)]
22 use std::sync::atomic::{AtomicU32, Ordering};
23 use thin_vec::thin_vec;
24
25 pub struct MarkedAttrs(GrowableBitSet<AttrId>);
26
27 impl 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
43 impl NestedMetaItem {
44     /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
45     pub fn meta_item(&self) -> Option<&MetaItem> {
46         match self {
47             NestedMetaItem::MetaItem(item) => Some(item),
48             _ => None,
49         }
50     }
51
52     /// Returns the `MetaItemLit` if `self` is a `NestedMetaItem::Literal`s.
53     pub fn lit(&self) -> Option<&MetaItemLit> {
54         match self {
55             NestedMetaItem::Lit(lit) => Some(lit),
56             _ => None,
57         }
58     }
59
60     /// Returns `true` if this list item is a MetaItem with a name of `name`.
61     pub fn has_name(&self, name: Symbol) -> bool {
62         self.meta_item().map_or(false, |meta_item| meta_item.has_name(name))
63     }
64
65     /// For a single-segment meta item, returns its name; otherwise, returns `None`.
66     pub fn ident(&self) -> Option<Ident> {
67         self.meta_item().and_then(|meta_item| meta_item.ident())
68     }
69     pub fn name_or_empty(&self) -> Symbol {
70         self.ident().unwrap_or_else(Ident::empty).name
71     }
72
73     /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
74     /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
75     pub fn value_str(&self) -> Option<Symbol> {
76         self.meta_item().and_then(|meta_item| meta_item.value_str())
77     }
78
79     /// Returns a name and single literal value tuple of the `MetaItem`.
80     pub fn name_value_literal(&self) -> Option<(Symbol, &MetaItemLit)> {
81         self.meta_item().and_then(|meta_item| {
82             meta_item.meta_item_list().and_then(|meta_item_list| {
83                 if meta_item_list.len() == 1
84                     && let Some(ident) = meta_item.ident()
85                     && let Some(lit) = meta_item_list[0].lit()
86                 {
87                     return Some((ident.name, lit));
88                 }
89                 None
90             })
91         })
92     }
93
94     /// Gets a list of inner meta items from a list `MetaItem` type.
95     pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
96         self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
97     }
98
99     /// Returns `true` if the variant is `MetaItem`.
100     pub fn is_meta_item(&self) -> bool {
101         self.meta_item().is_some()
102     }
103
104     /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
105     pub fn is_word(&self) -> bool {
106         self.meta_item().map_or(false, |meta_item| meta_item.is_word())
107     }
108
109     /// See [`MetaItem::name_value_literal_span`].
110     pub fn name_value_literal_span(&self) -> Option<Span> {
111         self.meta_item()?.name_value_literal_span()
112     }
113 }
114
115 impl Attribute {
116     #[inline]
117     pub fn has_name(&self, name: Symbol) -> bool {
118         match &self.kind {
119             AttrKind::Normal(normal) => normal.item.path == name,
120             AttrKind::DocComment(..) => false,
121         }
122     }
123
124     /// For a single-segment attribute, returns its name; otherwise, returns `None`.
125     pub fn ident(&self) -> Option<Ident> {
126         match &self.kind {
127             AttrKind::Normal(normal) => {
128                 if let [ident] = &*normal.item.path.segments {
129                     Some(ident.ident)
130                 } else {
131                     None
132                 }
133             }
134             AttrKind::DocComment(..) => None,
135         }
136     }
137     pub fn name_or_empty(&self) -> Symbol {
138         self.ident().unwrap_or_else(Ident::empty).name
139     }
140
141     pub fn value_str(&self) -> Option<Symbol> {
142         match &self.kind {
143             AttrKind::Normal(normal) => normal.item.meta_kind().and_then(|kind| kind.value_str()),
144             AttrKind::DocComment(..) => None,
145         }
146     }
147
148     pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
149         match &self.kind {
150             AttrKind::Normal(normal) => match normal.item.meta_kind() {
151                 Some(MetaItemKind::List(list)) => Some(list),
152                 _ => None,
153             },
154             AttrKind::DocComment(..) => None,
155         }
156     }
157
158     pub fn is_word(&self) -> bool {
159         if let AttrKind::Normal(normal) = &self.kind {
160             matches!(normal.item.args, AttrArgs::Empty)
161         } else {
162             false
163         }
164     }
165 }
166
167 impl MetaItem {
168     /// For a single-segment meta item, returns its name; otherwise, returns `None`.
169     pub fn ident(&self) -> Option<Ident> {
170         if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
171     }
172     pub fn name_or_empty(&self) -> Symbol {
173         self.ident().unwrap_or_else(Ident::empty).name
174     }
175
176     /// ```text
177     /// Example:
178     ///     #[attribute(name = "value")]
179     ///                 ^^^^^^^^^^^^^^
180     /// ```
181     pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
182         match &self.kind {
183             MetaItemKind::NameValue(v) => Some(v),
184             _ => None,
185         }
186     }
187
188     pub fn value_str(&self) -> Option<Symbol> {
189         self.kind.value_str()
190     }
191
192     pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
193         match &self.kind {
194             MetaItemKind::List(l) => Some(&**l),
195             _ => None,
196         }
197     }
198
199     pub fn is_word(&self) -> bool {
200         matches!(self.kind, MetaItemKind::Word)
201     }
202
203     pub fn has_name(&self, name: Symbol) -> bool {
204         self.path == name
205     }
206
207     /// This is used in case you want the value span instead of the whole attribute. Example:
208     ///
209     /// ```text
210     /// #[doc(alias = "foo")]
211     /// ```
212     ///
213     /// In here, it'll return a span for `"foo"`.
214     pub fn name_value_literal_span(&self) -> Option<Span> {
215         Some(self.name_value_literal()?.span)
216     }
217 }
218
219 impl AttrItem {
220     pub fn span(&self) -> Span {
221         self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
222     }
223
224     pub fn meta(&self, span: Span) -> Option<MetaItem> {
225         Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
226     }
227
228     pub fn meta_kind(&self) -> Option<MetaItemKind> {
229         MetaItemKind::from_attr_args(&self.args)
230     }
231 }
232
233 impl Attribute {
234     /// Returns `true` if it is a sugared doc comment (`///` or `//!` for example).
235     /// So `#[doc = "doc"]` (which is a doc comment) and `#[doc(...)]` (which is not
236     /// a doc comment) will return `false`.
237     pub fn is_doc_comment(&self) -> bool {
238         match self.kind {
239             AttrKind::Normal(..) => false,
240             AttrKind::DocComment(..) => true,
241         }
242     }
243
244     /// Returns the documentation and its kind if this is a doc comment or a sugared doc comment.
245     /// * `///doc` returns `Some(("doc", CommentKind::Line))`.
246     /// * `/** doc */` returns `Some(("doc", CommentKind::Block))`.
247     /// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
248     /// * `#[doc(...)]` returns `None`.
249     pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
250         match self.kind {
251             AttrKind::DocComment(kind, data) => Some((data, kind)),
252             AttrKind::Normal(ref normal) if normal.item.path == sym::doc => normal
253                 .item
254                 .meta_kind()
255                 .and_then(|kind| kind.value_str())
256                 .map(|data| (data, CommentKind::Line)),
257             _ => None,
258         }
259     }
260
261     /// Returns the documentation if this is a doc comment or a sugared doc comment.
262     /// * `///doc` returns `Some("doc")`.
263     /// * `#[doc = "doc"]` returns `Some("doc")`.
264     /// * `#[doc(...)]` returns `None`.
265     pub fn doc_str(&self) -> Option<Symbol> {
266         match &self.kind {
267             AttrKind::DocComment(.., data) => Some(*data),
268             AttrKind::Normal(normal) if normal.item.path == sym::doc => {
269                 normal.item.meta_kind().and_then(|kind| kind.value_str())
270             }
271             _ => None,
272         }
273     }
274
275     pub fn may_have_doc_links(&self) -> bool {
276         self.doc_str().map_or(false, |s| comments::may_have_doc_links(s.as_str()))
277     }
278
279     pub fn get_normal_item(&self) -> &AttrItem {
280         match &self.kind {
281             AttrKind::Normal(normal) => &normal.item,
282             AttrKind::DocComment(..) => panic!("unexpected doc comment"),
283         }
284     }
285
286     pub fn unwrap_normal_item(self) -> AttrItem {
287         match self.kind {
288             AttrKind::Normal(normal) => normal.into_inner().item,
289             AttrKind::DocComment(..) => panic!("unexpected doc comment"),
290         }
291     }
292
293     /// Extracts the MetaItem from inside this Attribute.
294     pub fn meta(&self) -> Option<MetaItem> {
295         match &self.kind {
296             AttrKind::Normal(normal) => normal.item.meta(self.span),
297             AttrKind::DocComment(..) => None,
298         }
299     }
300
301     pub fn meta_kind(&self) -> Option<MetaItemKind> {
302         match &self.kind {
303             AttrKind::Normal(normal) => normal.item.meta_kind(),
304             AttrKind::DocComment(..) => None,
305         }
306     }
307
308     pub fn tokens(&self) -> TokenStream {
309         match &self.kind {
310             AttrKind::Normal(normal) => normal
311                 .tokens
312                 .as_ref()
313                 .unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self))
314                 .to_attr_token_stream()
315                 .to_tokenstream(),
316             &AttrKind::DocComment(comment_kind, data) => TokenStream::new(vec![TokenTree::Token(
317                 Token::new(token::DocComment(comment_kind, self.style, data), self.span),
318                 Spacing::Alone,
319             )]),
320         }
321     }
322 }
323
324 /* Constructors */
325
326 pub fn mk_name_value_item_str(ident: Ident, str: Symbol, str_span: Span) -> MetaItem {
327     mk_name_value_item(ident, LitKind::Str(str, ast::StrStyle::Cooked), str_span)
328 }
329
330 pub fn mk_name_value_item(ident: Ident, kind: LitKind, lit_span: Span) -> MetaItem {
331     let lit = MetaItemLit { token_lit: kind.synthesize_token_lit(), kind, span: lit_span };
332     let span = ident.span.to(lit_span);
333     MetaItem { path: Path::from_ident(ident), kind: MetaItemKind::NameValue(lit), span }
334 }
335
336 pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
337
338 #[cfg(debug_assertions)]
339 static MAX_ATTR_ID: AtomicU32 = AtomicU32::new(u32::MAX);
340
341 impl AttrIdGenerator {
342     pub fn new() -> Self {
343         // We use `(index as u32).reverse_bits()` to initialize the
344         // starting value of AttrId in each worker thread.
345         // The `index` is the index of the worker thread.
346         // This ensures that the AttrId generated in each thread is unique.
347         AttrIdGenerator(WorkerLocal::new(|index| {
348             let index: u32 = index.try_into().unwrap();
349
350             #[cfg(debug_assertions)]
351             {
352                 let max_id = ((index + 1).next_power_of_two() - 1).bitxor(u32::MAX).reverse_bits();
353                 MAX_ATTR_ID.fetch_min(max_id, Ordering::Release);
354             }
355
356             Cell::new(index.reverse_bits())
357         }))
358     }
359
360     pub fn mk_attr_id(&self) -> AttrId {
361         let id = self.0.get();
362
363         // Ensure the assigned attr_id does not overlap the bits
364         // representing the number of threads.
365         #[cfg(debug_assertions)]
366         assert!(id <= MAX_ATTR_ID.load(Ordering::Acquire));
367
368         self.0.set(id + 1);
369         AttrId::from_u32(id)
370     }
371 }
372
373 pub fn mk_attr(
374     g: &AttrIdGenerator,
375     style: AttrStyle,
376     path: Path,
377     args: AttrArgs,
378     span: Span,
379 ) -> Attribute {
380     mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
381 }
382
383 pub fn mk_attr_from_item(
384     g: &AttrIdGenerator,
385     item: AttrItem,
386     tokens: Option<LazyAttrTokenStream>,
387     style: AttrStyle,
388     span: Span,
389 ) -> Attribute {
390     Attribute {
391         kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
392         id: g.mk_attr_id(),
393         style,
394         span,
395     }
396 }
397
398 pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
399     let path = Path::from_ident(Ident::new(name, span));
400     let args = AttrArgs::Empty;
401     mk_attr(g, style, path, args, span)
402 }
403
404 pub fn mk_attr_name_value_str(
405     g: &AttrIdGenerator,
406     style: AttrStyle,
407     name: Symbol,
408     val: Symbol,
409     span: Span,
410 ) -> Attribute {
411     let lit = LitKind::Str(val, StrStyle::Cooked).synthesize_token_lit();
412     let expr = P(Expr {
413         id: DUMMY_NODE_ID,
414         kind: ExprKind::Lit(lit),
415         span,
416         attrs: AttrVec::new(),
417         tokens: None,
418     });
419     let path = Path::from_ident(Ident::new(name, span));
420     let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
421     mk_attr(g, style, path, args, span)
422 }
423
424 pub fn mk_attr_nested_word(
425     g: &AttrIdGenerator,
426     style: AttrStyle,
427     outer: Symbol,
428     inner: Symbol,
429     span: Span,
430 ) -> Attribute {
431     let inner_tokens = TokenStream::new(vec![TokenTree::Token(
432         Token::from_ast_ident(Ident::new(inner, span)),
433         Spacing::Alone,
434     )]);
435     let outer_ident = Ident::new(outer, span);
436     let path = Path::from_ident(outer_ident);
437     let attr_args = AttrArgs::Delimited(DelimArgs {
438         dspan: DelimSpan::from_single(span),
439         delim: MacDelimiter::Parenthesis,
440         tokens: inner_tokens,
441     });
442     mk_attr(g, style, path, attr_args, span)
443 }
444
445 pub fn mk_doc_comment(
446     g: &AttrIdGenerator,
447     comment_kind: CommentKind,
448     style: AttrStyle,
449     data: Symbol,
450     span: Span,
451 ) -> Attribute {
452     Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
453 }
454
455 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
456     items.iter().any(|item| item.has_name(name))
457 }
458
459 impl MetaItem {
460     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
461     where
462         I: Iterator<Item = TokenTree>,
463     {
464         // FIXME: Share code with `parse_path`.
465         let path = match tokens.next().map(TokenTree::uninterpolate) {
466             Some(TokenTree::Token(
467                 Token { kind: kind @ (token::Ident(..) | token::ModSep), span },
468                 _,
469             )) => 'arm: {
470                 let mut segments = if let token::Ident(name, _) = kind {
471                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
472                         tokens.peek()
473                     {
474                         tokens.next();
475                         thin_vec![PathSegment::from_ident(Ident::new(name, span))]
476                     } else {
477                         break 'arm Path::from_ident(Ident::new(name, span));
478                     }
479                 } else {
480                     thin_vec![PathSegment::path_root(span)]
481                 };
482                 loop {
483                     if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
484                         tokens.next().map(TokenTree::uninterpolate)
485                     {
486                         segments.push(PathSegment::from_ident(Ident::new(name, span)));
487                     } else {
488                         return None;
489                     }
490                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
491                         tokens.peek()
492                     {
493                         tokens.next();
494                     } else {
495                         break;
496                     }
497                 }
498                 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
499                 Path { span, segments, tokens: None }
500             }
501             Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &*nt {
502                 token::Nonterminal::NtMeta(item) => return item.meta(item.path.span),
503                 token::Nonterminal::NtPath(path) => (**path).clone(),
504                 _ => return None,
505             },
506             _ => return None,
507         };
508         let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
509         let kind = MetaItemKind::from_tokens(tokens)?;
510         let hi = match &kind {
511             MetaItemKind::NameValue(lit) => lit.span.hi(),
512             MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
513             _ => path.span.hi(),
514         };
515         let span = path.span.with_hi(hi);
516         Some(MetaItem { path, kind, span })
517     }
518 }
519
520 impl MetaItemKind {
521     pub fn value_str(&self) -> Option<Symbol> {
522         match self {
523             MetaItemKind::NameValue(v) => match v.kind {
524                 LitKind::Str(s, _) => Some(s),
525                 _ => None,
526             },
527             _ => None,
528         }
529     }
530
531     fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
532         let mut tokens = tokens.into_trees().peekable();
533         let mut result = Vec::new();
534         while tokens.peek().is_some() {
535             let item = NestedMetaItem::from_tokens(&mut tokens)?;
536             result.push(item);
537             match tokens.next() {
538                 None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}
539                 _ => return None,
540             }
541         }
542         Some(MetaItemKind::List(result))
543     }
544
545     fn name_value_from_tokens(
546         tokens: &mut impl Iterator<Item = TokenTree>,
547     ) -> Option<MetaItemKind> {
548         match tokens.next() {
549             Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
550                 MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees())
551             }
552             Some(TokenTree::Token(token, _)) => {
553                 MetaItemLit::from_token(&token).map(MetaItemKind::NameValue)
554             }
555             _ => None,
556         }
557     }
558
559     fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {
560         match args {
561             AttrArgs::Empty => Some(MetaItemKind::Word),
562             AttrArgs::Delimited(DelimArgs {
563                 dspan: _,
564                 delim: MacDelimiter::Parenthesis,
565                 tokens,
566             }) => MetaItemKind::list_from_tokens(tokens.clone()),
567             AttrArgs::Delimited(..) => None,
568             AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind {
569                 ExprKind::Lit(token_lit) => {
570                     // Turn failures to `None`, we'll get parse errors elsewhere.
571                     MetaItemLit::from_token_lit(token_lit, expr.span)
572                         .ok()
573                         .map(|lit| MetaItemKind::NameValue(lit))
574                 }
575                 _ => None,
576             },
577             AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
578         }
579     }
580
581     fn from_tokens(
582         tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
583     ) -> Option<MetaItemKind> {
584         match tokens.peek() {
585             Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
586                 let inner_tokens = inner_tokens.clone();
587                 tokens.next();
588                 MetaItemKind::list_from_tokens(inner_tokens)
589             }
590             Some(TokenTree::Delimited(..)) => None,
591             Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
592                 tokens.next();
593                 MetaItemKind::name_value_from_tokens(tokens)
594             }
595             _ => Some(MetaItemKind::Word),
596         }
597     }
598 }
599
600 impl NestedMetaItem {
601     pub fn span(&self) -> Span {
602         match self {
603             NestedMetaItem::MetaItem(item) => item.span,
604             NestedMetaItem::Lit(lit) => lit.span,
605         }
606     }
607
608     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
609     where
610         I: Iterator<Item = TokenTree>,
611     {
612         match tokens.peek() {
613             Some(TokenTree::Token(token, _))
614                 if let Some(lit) = MetaItemLit::from_token(token) =>
615             {
616                 tokens.next();
617                 return Some(NestedMetaItem::Lit(lit));
618             }
619             Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
620                 let inner_tokens = inner_tokens.clone();
621                 tokens.next();
622                 return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
623             }
624             _ => {}
625         }
626         MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
627     }
628 }