]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_ast/src/attr/mod.rs
Rollup merge of #84390 - m-ou-se:make-debug-non-exhaustive-without-fields-a-little...
[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::{AttrId, AttrItem, AttrKind, AttrStyle, Attribute};
5 use crate::ast::{Lit, LitKind};
6 use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
7 use crate::ast::{Path, PathSegment};
8 use crate::token::{self, CommentKind, Token};
9 use crate::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
10 use crate::tokenstream::{DelimSpan, Spacing, TokenTree, TreeAndSpacing};
11 use crate::tokenstream::{LazyTokenStream, TokenStream};
12
13 use rustc_index::bit_set::GrowableBitSet;
14 use rustc_span::source_map::BytePos;
15 use rustc_span::symbol::{sym, Ident, Symbol};
16 use rustc_span::Span;
17
18 use std::iter;
19
20 pub struct MarkedAttrs(GrowableBitSet<AttrId>);
21
22 impl MarkedAttrs {
23     // We have no idea how many attributes there will be, so just
24     // initiate the vectors with 0 bits. We'll grow them as necessary.
25     pub fn new() -> Self {
26         MarkedAttrs(GrowableBitSet::new_empty())
27     }
28
29     pub fn mark(&mut self, attr: &Attribute) {
30         self.0.insert(attr.id);
31     }
32
33     pub fn is_marked(&self, attr: &Attribute) -> bool {
34         self.0.contains(attr.id)
35     }
36 }
37
38 impl NestedMetaItem {
39     /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
40     pub fn meta_item(&self) -> Option<&MetaItem> {
41         match *self {
42             NestedMetaItem::MetaItem(ref item) => Some(item),
43             _ => None,
44         }
45     }
46
47     /// Returns the `Lit` if `self` is a `NestedMetaItem::Literal`s.
48     pub fn literal(&self) -> Option<&Lit> {
49         match *self {
50             NestedMetaItem::Literal(ref lit) => Some(lit),
51             _ => None,
52         }
53     }
54
55     /// Returns `true` if this list item is a MetaItem with a name of `name`.
56     pub fn has_name(&self, name: Symbol) -> bool {
57         self.meta_item().map_or(false, |meta_item| meta_item.has_name(name))
58     }
59
60     /// For a single-segment meta item, returns its name; otherwise, returns `None`.
61     pub fn ident(&self) -> Option<Ident> {
62         self.meta_item().and_then(|meta_item| meta_item.ident())
63     }
64     pub fn name_or_empty(&self) -> Symbol {
65         self.ident().unwrap_or_else(Ident::invalid).name
66     }
67
68     /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
69     /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
70     pub fn value_str(&self) -> Option<Symbol> {
71         self.meta_item().and_then(|meta_item| meta_item.value_str())
72     }
73
74     /// Returns a name and single literal value tuple of the `MetaItem`.
75     pub fn name_value_literal(&self) -> Option<(Symbol, &Lit)> {
76         self.meta_item().and_then(|meta_item| {
77             meta_item.meta_item_list().and_then(|meta_item_list| {
78                 if meta_item_list.len() == 1 {
79                     if let Some(ident) = meta_item.ident() {
80                         if let Some(lit) = meta_item_list[0].literal() {
81                             return Some((ident.name, lit));
82                         }
83                     }
84                 }
85                 None
86             })
87         })
88     }
89
90     /// Gets a list of inner meta items from a list `MetaItem` type.
91     pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
92         self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
93     }
94
95     /// Returns `true` if the variant is `MetaItem`.
96     pub fn is_meta_item(&self) -> bool {
97         self.meta_item().is_some()
98     }
99
100     /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
101     pub fn is_word(&self) -> bool {
102         self.meta_item().map_or(false, |meta_item| meta_item.is_word())
103     }
104
105     /// See [`MetaItem::name_value_literal_span`].
106     pub fn name_value_literal_span(&self) -> Option<Span> {
107         self.meta_item()?.name_value_literal_span()
108     }
109 }
110
111 impl Attribute {
112     #[inline]
113     pub fn has_name(&self, name: Symbol) -> bool {
114         match self.kind {
115             AttrKind::Normal(ref item, _) => item.path == name,
116             AttrKind::DocComment(..) => false,
117         }
118     }
119
120     /// For a single-segment attribute, returns its name; otherwise, returns `None`.
121     pub fn ident(&self) -> Option<Ident> {
122         match self.kind {
123             AttrKind::Normal(ref item, _) => {
124                 if item.path.segments.len() == 1 {
125                     Some(item.path.segments[0].ident)
126                 } else {
127                     None
128                 }
129             }
130             AttrKind::DocComment(..) => None,
131         }
132     }
133     pub fn name_or_empty(&self) -> Symbol {
134         self.ident().unwrap_or_else(Ident::invalid).name
135     }
136
137     pub fn value_str(&self) -> Option<Symbol> {
138         match self.kind {
139             AttrKind::Normal(ref item, _) => item.meta(self.span).and_then(|meta| meta.value_str()),
140             AttrKind::DocComment(..) => None,
141         }
142     }
143
144     pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
145         match self.kind {
146             AttrKind::Normal(ref item, _) => match item.meta(self.span) {
147                 Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list),
148                 _ => None,
149             },
150             AttrKind::DocComment(..) => None,
151         }
152     }
153
154     pub fn is_word(&self) -> bool {
155         if let AttrKind::Normal(item, _) = &self.kind {
156             matches!(item.args, MacArgs::Empty)
157         } else {
158             false
159         }
160     }
161 }
162
163 impl MetaItem {
164     /// For a single-segment meta item, returns its name; otherwise, returns `None`.
165     pub fn ident(&self) -> Option<Ident> {
166         if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
167     }
168     pub fn name_or_empty(&self) -> Symbol {
169         self.ident().unwrap_or_else(Ident::invalid).name
170     }
171
172     // Example:
173     //     #[attribute(name = "value")]
174     //                 ^^^^^^^^^^^^^^
175     pub fn name_value_literal(&self) -> Option<&Lit> {
176         match &self.kind {
177             MetaItemKind::NameValue(v) => Some(v),
178             _ => None,
179         }
180     }
181
182     pub fn value_str(&self) -> Option<Symbol> {
183         match self.kind {
184             MetaItemKind::NameValue(ref v) => match v.kind {
185                 LitKind::Str(ref s, _) => Some(*s),
186                 _ => None,
187             },
188             _ => None,
189         }
190     }
191
192     pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
193         match self.kind {
194             MetaItemKind::List(ref 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 {
226             path: self.path.clone(),
227             kind: MetaItemKind::from_mac_args(&self.args)?,
228             span,
229         })
230     }
231 }
232
233 impl Attribute {
234     pub fn is_doc_comment(&self) -> bool {
235         match self.kind {
236             AttrKind::Normal(..) => false,
237             AttrKind::DocComment(..) => true,
238         }
239     }
240
241     pub fn doc_str(&self) -> Option<Symbol> {
242         match self.kind {
243             AttrKind::DocComment(.., data) => Some(data),
244             AttrKind::Normal(ref item, _) if item.path == sym::doc => {
245                 item.meta(self.span).and_then(|meta| meta.value_str())
246             }
247             _ => None,
248         }
249     }
250
251     pub fn get_normal_item(&self) -> &AttrItem {
252         match self.kind {
253             AttrKind::Normal(ref item, _) => item,
254             AttrKind::DocComment(..) => panic!("unexpected doc comment"),
255         }
256     }
257
258     pub fn unwrap_normal_item(self) -> AttrItem {
259         match self.kind {
260             AttrKind::Normal(item, _) => item,
261             AttrKind::DocComment(..) => panic!("unexpected doc comment"),
262         }
263     }
264
265     /// Extracts the MetaItem from inside this Attribute.
266     pub fn meta(&self) -> Option<MetaItem> {
267         match self.kind {
268             AttrKind::Normal(ref item, _) => item.meta(self.span),
269             AttrKind::DocComment(..) => None,
270         }
271     }
272
273     pub fn tokens(&self) -> AttrAnnotatedTokenStream {
274         match self.kind {
275             AttrKind::Normal(_, ref tokens) => tokens
276                 .as_ref()
277                 .unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self))
278                 .create_token_stream(),
279             AttrKind::DocComment(comment_kind, data) => AttrAnnotatedTokenStream::from((
280                 AttrAnnotatedTokenTree::Token(Token::new(
281                     token::DocComment(comment_kind, self.style, data),
282                     self.span,
283                 )),
284                 Spacing::Alone,
285             )),
286         }
287     }
288 }
289
290 /* Constructors */
291
292 pub fn mk_name_value_item_str(ident: Ident, str: Symbol, str_span: Span) -> MetaItem {
293     let lit_kind = LitKind::Str(str, ast::StrStyle::Cooked);
294     mk_name_value_item(ident, lit_kind, str_span)
295 }
296
297 pub fn mk_name_value_item(ident: Ident, lit_kind: LitKind, lit_span: Span) -> MetaItem {
298     let lit = Lit::from_lit_kind(lit_kind, lit_span);
299     let span = ident.span.to(lit_span);
300     MetaItem { path: Path::from_ident(ident), span, kind: MetaItemKind::NameValue(lit) }
301 }
302
303 pub fn mk_list_item(ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem {
304     MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::List(items) }
305 }
306
307 pub fn mk_word_item(ident: Ident) -> MetaItem {
308     MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::Word }
309 }
310
311 pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
312     NestedMetaItem::MetaItem(mk_word_item(ident))
313 }
314
315 crate fn mk_attr_id() -> AttrId {
316     use std::sync::atomic::AtomicU32;
317     use std::sync::atomic::Ordering;
318
319     static NEXT_ATTR_ID: AtomicU32 = AtomicU32::new(0);
320
321     let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst);
322     assert!(id != u32::MAX);
323     AttrId::from_u32(id)
324 }
325
326 pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute {
327     mk_attr_from_item(AttrItem { path, args, tokens: None }, None, style, span)
328 }
329
330 pub fn mk_attr_from_item(
331     item: AttrItem,
332     tokens: Option<LazyTokenStream>,
333     style: AttrStyle,
334     span: Span,
335 ) -> Attribute {
336     Attribute { kind: AttrKind::Normal(item, tokens), id: mk_attr_id(), style, span }
337 }
338
339 /// Returns an inner attribute with the given value and span.
340 pub fn mk_attr_inner(item: MetaItem) -> Attribute {
341     mk_attr(AttrStyle::Inner, item.path, item.kind.mac_args(item.span), item.span)
342 }
343
344 /// Returns an outer attribute with the given value and span.
345 pub fn mk_attr_outer(item: MetaItem) -> Attribute {
346     mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span)
347 }
348
349 pub fn mk_doc_comment(
350     comment_kind: CommentKind,
351     style: AttrStyle,
352     data: Symbol,
353     span: Span,
354 ) -> Attribute {
355     Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span }
356 }
357
358 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
359     items.iter().any(|item| item.has_name(name))
360 }
361
362 impl MetaItem {
363     fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> {
364         let mut idents = vec![];
365         let mut last_pos = BytePos(0_u32);
366         for (i, segment) in self.path.segments.iter().enumerate() {
367             let is_first = i == 0;
368             if !is_first {
369                 let mod_sep_span =
370                     Span::new(last_pos, segment.ident.span.lo(), segment.ident.span.ctxt());
371                 idents.push(TokenTree::token(token::ModSep, mod_sep_span).into());
372             }
373             idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into());
374             last_pos = segment.ident.span.hi();
375         }
376         idents.extend(self.kind.token_trees_and_spacings(self.span));
377         idents
378     }
379
380     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
381     where
382         I: Iterator<Item = TokenTree>,
383     {
384         // FIXME: Share code with `parse_path`.
385         let path = match tokens.next().map(TokenTree::uninterpolate) {
386             Some(TokenTree::Token(Token {
387                 kind: kind @ (token::Ident(..) | token::ModSep),
388                 span,
389             })) => 'arm: {
390                 let mut segments = if let token::Ident(name, _) = kind {
391                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek()
392                     {
393                         tokens.next();
394                         vec![PathSegment::from_ident(Ident::new(name, span))]
395                     } else {
396                         break 'arm Path::from_ident(Ident::new(name, span));
397                     }
398                 } else {
399                     vec![PathSegment::path_root(span)]
400                 };
401                 loop {
402                     if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span })) =
403                         tokens.next().map(TokenTree::uninterpolate)
404                     {
405                         segments.push(PathSegment::from_ident(Ident::new(name, span)));
406                     } else {
407                         return None;
408                     }
409                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek()
410                     {
411                         tokens.next();
412                     } else {
413                         break;
414                     }
415                 }
416                 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
417                 Path { span, segments, tokens: None }
418             }
419             Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt {
420                 token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span),
421                 token::Nonterminal::NtPath(ref path) => path.clone(),
422                 _ => return None,
423             },
424             _ => return None,
425         };
426         let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
427         let kind = MetaItemKind::from_tokens(tokens)?;
428         let hi = match kind {
429             MetaItemKind::NameValue(ref lit) => lit.span.hi(),
430             MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
431             _ => path.span.hi(),
432         };
433         let span = path.span.with_hi(hi);
434         Some(MetaItem { path, kind, span })
435     }
436 }
437
438 impl MetaItemKind {
439     pub fn mac_args(&self, span: Span) -> MacArgs {
440         match self {
441             MetaItemKind::Word => MacArgs::Empty,
442             MetaItemKind::NameValue(lit) => MacArgs::Eq(span, lit.to_token()),
443             MetaItemKind::List(list) => {
444                 let mut tts = Vec::new();
445                 for (i, item) in list.iter().enumerate() {
446                     if i > 0 {
447                         tts.push(TokenTree::token(token::Comma, span).into());
448                     }
449                     tts.extend(item.token_trees_and_spacings())
450                 }
451                 MacArgs::Delimited(
452                     DelimSpan::from_single(span),
453                     MacDelimiter::Parenthesis,
454                     TokenStream::new(tts),
455                 )
456             }
457         }
458     }
459
460     fn token_trees_and_spacings(&self, span: Span) -> Vec<TreeAndSpacing> {
461         match *self {
462             MetaItemKind::Word => vec![],
463             MetaItemKind::NameValue(ref lit) => {
464                 vec![
465                     TokenTree::token(token::Eq, span).into(),
466                     TokenTree::Token(lit.to_token()).into(),
467                 ]
468             }
469             MetaItemKind::List(ref list) => {
470                 let mut tokens = Vec::new();
471                 for (i, item) in list.iter().enumerate() {
472                     if i > 0 {
473                         tokens.push(TokenTree::token(token::Comma, span).into());
474                     }
475                     tokens.extend(item.token_trees_and_spacings())
476                 }
477                 vec![
478                     TokenTree::Delimited(
479                         DelimSpan::from_single(span),
480                         token::Paren,
481                         TokenStream::new(tokens),
482                     )
483                     .into(),
484                 ]
485             }
486         }
487     }
488
489     fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
490         let mut tokens = tokens.into_trees().peekable();
491         let mut result = Vec::new();
492         while tokens.peek().is_some() {
493             let item = NestedMetaItem::from_tokens(&mut tokens)?;
494             result.push(item);
495             match tokens.next() {
496                 None | Some(TokenTree::Token(Token { kind: token::Comma, .. })) => {}
497                 _ => return None,
498             }
499         }
500         Some(MetaItemKind::List(result))
501     }
502
503     fn name_value_from_tokens(
504         tokens: &mut impl Iterator<Item = TokenTree>,
505     ) -> Option<MetaItemKind> {
506         match tokens.next() {
507             Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => {
508                 MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees())
509             }
510             Some(TokenTree::Token(token)) => {
511                 Lit::from_token(&token).ok().map(MetaItemKind::NameValue)
512             }
513             _ => None,
514         }
515     }
516
517     fn from_mac_args(args: &MacArgs) -> Option<MetaItemKind> {
518         match args {
519             MacArgs::Delimited(_, MacDelimiter::Parenthesis, tokens) => {
520                 MetaItemKind::list_from_tokens(tokens.clone())
521             }
522             MacArgs::Delimited(..) => None,
523             MacArgs::Eq(_, token) => Lit::from_token(token).ok().map(MetaItemKind::NameValue),
524             MacArgs::Empty => Some(MetaItemKind::Word),
525         }
526     }
527
528     fn from_tokens(
529         tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
530     ) -> Option<MetaItemKind> {
531         match tokens.peek() {
532             Some(TokenTree::Delimited(_, token::Paren, inner_tokens)) => {
533                 let inner_tokens = inner_tokens.clone();
534                 tokens.next();
535                 MetaItemKind::list_from_tokens(inner_tokens)
536             }
537             Some(TokenTree::Delimited(..)) => None,
538             Some(TokenTree::Token(Token { kind: token::Eq, .. })) => {
539                 tokens.next();
540                 MetaItemKind::name_value_from_tokens(tokens)
541             }
542             _ => Some(MetaItemKind::Word),
543         }
544     }
545 }
546
547 impl NestedMetaItem {
548     pub fn span(&self) -> Span {
549         match *self {
550             NestedMetaItem::MetaItem(ref item) => item.span,
551             NestedMetaItem::Literal(ref lit) => lit.span,
552         }
553     }
554
555     fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> {
556         match *self {
557             NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(),
558             NestedMetaItem::Literal(ref lit) => vec![TokenTree::Token(lit.to_token()).into()],
559         }
560     }
561
562     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
563     where
564         I: Iterator<Item = TokenTree>,
565     {
566         match tokens.peek() {
567             Some(TokenTree::Token(token)) => {
568                 if let Ok(lit) = Lit::from_token(token) {
569                     tokens.next();
570                     return Some(NestedMetaItem::Literal(lit));
571                 }
572             }
573             Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => {
574                 let inner_tokens = inner_tokens.clone();
575                 tokens.next();
576                 return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
577             }
578             _ => {}
579         }
580         MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
581     }
582 }