]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_ast/src/attr/mod.rs
Rollup merge of #105128 - Sp00ph:vec_vec_deque_conversion, r=dtolnay
[rust.git] / compiler / rustc_ast / src / attr / mod.rs
1 //! Functions dealing with attributes and meta items.
2
3 use crate::ast::{AttrArgs, AttrArgsEq, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute};
4 use crate::ast::{DelimArgs, Expr, ExprKind, LitKind, MetaItemLit};
5 use crate::ast::{MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem, NormalAttr};
6 use crate::ast::{Path, PathSegment, DUMMY_NODE_ID};
7 use crate::ptr::P;
8 use crate::token::{self, CommentKind, Delimiter, Token};
9 use crate::tokenstream::{DelimSpan, Spacing, TokenTree};
10 use crate::tokenstream::{LazyAttrTokenStream, TokenStream};
11 use crate::util::comments;
12 use crate::util::literal::escape_string_symbol;
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 pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
325
326 #[cfg(debug_assertions)]
327 static MAX_ATTR_ID: AtomicU32 = AtomicU32::new(u32::MAX);
328
329 impl AttrIdGenerator {
330     pub fn new() -> Self {
331         // We use `(index as u32).reverse_bits()` to initialize the
332         // starting value of AttrId in each worker thread.
333         // The `index` is the index of the worker thread.
334         // This ensures that the AttrId generated in each thread is unique.
335         AttrIdGenerator(WorkerLocal::new(|index| {
336             let index: u32 = index.try_into().unwrap();
337
338             #[cfg(debug_assertions)]
339             {
340                 let max_id = ((index + 1).next_power_of_two() - 1).bitxor(u32::MAX).reverse_bits();
341                 MAX_ATTR_ID.fetch_min(max_id, Ordering::Release);
342             }
343
344             Cell::new(index.reverse_bits())
345         }))
346     }
347
348     pub fn mk_attr_id(&self) -> AttrId {
349         let id = self.0.get();
350
351         // Ensure the assigned attr_id does not overlap the bits
352         // representing the number of threads.
353         #[cfg(debug_assertions)]
354         assert!(id <= MAX_ATTR_ID.load(Ordering::Acquire));
355
356         self.0.set(id + 1);
357         AttrId::from_u32(id)
358     }
359 }
360
361 pub fn mk_attr(
362     g: &AttrIdGenerator,
363     style: AttrStyle,
364     path: Path,
365     args: AttrArgs,
366     span: Span,
367 ) -> Attribute {
368     mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
369 }
370
371 pub fn mk_attr_from_item(
372     g: &AttrIdGenerator,
373     item: AttrItem,
374     tokens: Option<LazyAttrTokenStream>,
375     style: AttrStyle,
376     span: Span,
377 ) -> Attribute {
378     Attribute {
379         kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
380         id: g.mk_attr_id(),
381         style,
382         span,
383     }
384 }
385
386 pub fn mk_attr_word(g: &AttrIdGenerator, style: AttrStyle, name: Symbol, span: Span) -> Attribute {
387     let path = Path::from_ident(Ident::new(name, span));
388     let args = AttrArgs::Empty;
389     mk_attr(g, style, path, args, span)
390 }
391
392 pub fn mk_attr_name_value_str(
393     g: &AttrIdGenerator,
394     style: AttrStyle,
395     name: Symbol,
396     val: Symbol,
397     span: Span,
398 ) -> Attribute {
399     let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
400     let expr = P(Expr {
401         id: DUMMY_NODE_ID,
402         kind: ExprKind::Lit(lit),
403         span,
404         attrs: AttrVec::new(),
405         tokens: None,
406     });
407     let path = Path::from_ident(Ident::new(name, span));
408     let args = AttrArgs::Eq(span, AttrArgsEq::Ast(expr));
409     mk_attr(g, style, path, args, span)
410 }
411
412 pub fn mk_attr_nested_word(
413     g: &AttrIdGenerator,
414     style: AttrStyle,
415     outer: Symbol,
416     inner: Symbol,
417     span: Span,
418 ) -> Attribute {
419     let inner_tokens = TokenStream::new(vec![TokenTree::Token(
420         Token::from_ast_ident(Ident::new(inner, span)),
421         Spacing::Alone,
422     )]);
423     let outer_ident = Ident::new(outer, span);
424     let path = Path::from_ident(outer_ident);
425     let attr_args = AttrArgs::Delimited(DelimArgs {
426         dspan: DelimSpan::from_single(span),
427         delim: MacDelimiter::Parenthesis,
428         tokens: inner_tokens,
429     });
430     mk_attr(g, style, path, attr_args, span)
431 }
432
433 pub fn mk_doc_comment(
434     g: &AttrIdGenerator,
435     comment_kind: CommentKind,
436     style: AttrStyle,
437     data: Symbol,
438     span: Span,
439 ) -> Attribute {
440     Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
441 }
442
443 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
444     items.iter().any(|item| item.has_name(name))
445 }
446
447 impl MetaItem {
448     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
449     where
450         I: Iterator<Item = TokenTree>,
451     {
452         // FIXME: Share code with `parse_path`.
453         let path = match tokens.next().map(TokenTree::uninterpolate) {
454             Some(TokenTree::Token(
455                 Token { kind: kind @ (token::Ident(..) | token::ModSep), span },
456                 _,
457             )) => 'arm: {
458                 let mut segments = if let token::Ident(name, _) = kind {
459                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
460                         tokens.peek()
461                     {
462                         tokens.next();
463                         thin_vec![PathSegment::from_ident(Ident::new(name, span))]
464                     } else {
465                         break 'arm Path::from_ident(Ident::new(name, span));
466                     }
467                 } else {
468                     thin_vec![PathSegment::path_root(span)]
469                 };
470                 loop {
471                     if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
472                         tokens.next().map(TokenTree::uninterpolate)
473                     {
474                         segments.push(PathSegment::from_ident(Ident::new(name, span)));
475                     } else {
476                         return None;
477                     }
478                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
479                         tokens.peek()
480                     {
481                         tokens.next();
482                     } else {
483                         break;
484                     }
485                 }
486                 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
487                 Path { span, segments, tokens: None }
488             }
489             Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. }, _)) => match &*nt {
490                 token::Nonterminal::NtMeta(item) => return item.meta(item.path.span),
491                 token::Nonterminal::NtPath(path) => (**path).clone(),
492                 _ => return None,
493             },
494             _ => return None,
495         };
496         let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
497         let kind = MetaItemKind::from_tokens(tokens)?;
498         let hi = match &kind {
499             MetaItemKind::NameValue(lit) => lit.span.hi(),
500             MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
501             _ => path.span.hi(),
502         };
503         let span = path.span.with_hi(hi);
504         Some(MetaItem { path, kind, span })
505     }
506 }
507
508 impl MetaItemKind {
509     pub fn value_str(&self) -> Option<Symbol> {
510         match self {
511             MetaItemKind::NameValue(v) => match v.kind {
512                 LitKind::Str(s, _) => Some(s),
513                 _ => None,
514             },
515             _ => None,
516         }
517     }
518
519     fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
520         let mut tokens = tokens.into_trees().peekable();
521         let mut result = Vec::new();
522         while tokens.peek().is_some() {
523             let item = NestedMetaItem::from_tokens(&mut tokens)?;
524             result.push(item);
525             match tokens.next() {
526                 None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}
527                 _ => return None,
528             }
529         }
530         Some(MetaItemKind::List(result))
531     }
532
533     fn name_value_from_tokens(
534         tokens: &mut impl Iterator<Item = TokenTree>,
535     ) -> Option<MetaItemKind> {
536         match tokens.next() {
537             Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
538                 MetaItemKind::name_value_from_tokens(&mut inner_tokens.into_trees())
539             }
540             Some(TokenTree::Token(token, _)) => {
541                 MetaItemLit::from_token(&token).map(MetaItemKind::NameValue)
542             }
543             _ => None,
544         }
545     }
546
547     fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {
548         match args {
549             AttrArgs::Empty => Some(MetaItemKind::Word),
550             AttrArgs::Delimited(DelimArgs {
551                 dspan: _,
552                 delim: MacDelimiter::Parenthesis,
553                 tokens,
554             }) => MetaItemKind::list_from_tokens(tokens.clone()),
555             AttrArgs::Delimited(..) => None,
556             AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind {
557                 ExprKind::Lit(token_lit) => {
558                     // Turn failures to `None`, we'll get parse errors elsewhere.
559                     MetaItemLit::from_token_lit(token_lit, expr.span)
560                         .ok()
561                         .map(|lit| MetaItemKind::NameValue(lit))
562                 }
563                 _ => None,
564             },
565             AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
566         }
567     }
568
569     fn from_tokens(
570         tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
571     ) -> Option<MetaItemKind> {
572         match tokens.peek() {
573             Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
574                 let inner_tokens = inner_tokens.clone();
575                 tokens.next();
576                 MetaItemKind::list_from_tokens(inner_tokens)
577             }
578             Some(TokenTree::Delimited(..)) => None,
579             Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
580                 tokens.next();
581                 MetaItemKind::name_value_from_tokens(tokens)
582             }
583             _ => Some(MetaItemKind::Word),
584         }
585     }
586 }
587
588 impl NestedMetaItem {
589     pub fn span(&self) -> Span {
590         match self {
591             NestedMetaItem::MetaItem(item) => item.span,
592             NestedMetaItem::Lit(lit) => lit.span,
593         }
594     }
595
596     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
597     where
598         I: Iterator<Item = TokenTree>,
599     {
600         match tokens.peek() {
601             Some(TokenTree::Token(token, _))
602                 if let Some(lit) = MetaItemLit::from_token(token) =>
603             {
604                 tokens.next();
605                 return Some(NestedMetaItem::Lit(lit));
606             }
607             Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
608                 let inner_tokens = inner_tokens.clone();
609                 tokens.next();
610                 return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
611             }
612             _ => {}
613         }
614         MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
615     }
616 }