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