1 //! Functions dealing with attributes and meta items.
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};
13 use rustc_index::bit_set::GrowableBitSet;
14 use rustc_span::source_map::BytePos;
15 use rustc_span::symbol::{sym, Ident, Symbol};
20 pub struct MarkedAttrs(GrowableBitSet<AttrId>);
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())
29 pub fn mark(&mut self, attr: &Attribute) {
30 self.0.insert(attr.id);
33 pub fn is_marked(&self, attr: &Attribute) -> bool {
34 self.0.contains(attr.id)
39 /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
40 pub fn meta_item(&self) -> Option<&MetaItem> {
42 NestedMetaItem::MetaItem(ref item) => Some(item),
47 /// Returns the `Lit` if `self` is a `NestedMetaItem::Literal`s.
48 pub fn literal(&self) -> Option<&Lit> {
50 NestedMetaItem::Literal(ref lit) => Some(lit),
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))
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())
64 pub fn name_or_empty(&self) -> Symbol {
65 self.ident().unwrap_or_else(Ident::invalid).name
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())
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));
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())
95 /// Returns `true` if the variant is `MetaItem`.
96 pub fn is_meta_item(&self) -> bool {
97 self.meta_item().is_some()
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())
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()
113 pub fn has_name(&self, name: Symbol) -> bool {
115 AttrKind::Normal(ref item, _) => item.path == name,
116 AttrKind::DocComment(..) => false,
120 /// For a single-segment attribute, returns its name; otherwise, returns `None`.
121 pub fn ident(&self) -> Option<Ident> {
123 AttrKind::Normal(ref item, _) => {
124 if item.path.segments.len() == 1 {
125 Some(item.path.segments[0].ident)
130 AttrKind::DocComment(..) => None,
133 pub fn name_or_empty(&self) -> Symbol {
134 self.ident().unwrap_or_else(Ident::invalid).name
137 pub fn value_str(&self) -> Option<Symbol> {
139 AttrKind::Normal(ref item, _) => item.meta(self.span).and_then(|meta| meta.value_str()),
140 AttrKind::DocComment(..) => None,
144 pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
146 AttrKind::Normal(ref item, _) => match item.meta(self.span) {
147 Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list),
150 AttrKind::DocComment(..) => None,
154 pub fn is_word(&self) -> bool {
155 if let AttrKind::Normal(item, _) = &self.kind {
156 matches!(item.args, MacArgs::Empty)
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 }
168 pub fn name_or_empty(&self) -> Symbol {
169 self.ident().unwrap_or_else(Ident::invalid).name
173 // #[attribute(name = "value")]
175 pub fn name_value_literal(&self) -> Option<&Lit> {
177 MetaItemKind::NameValue(v) => Some(v),
182 pub fn value_str(&self) -> Option<Symbol> {
184 MetaItemKind::NameValue(ref v) => match v.kind {
185 LitKind::Str(ref s, _) => Some(*s),
192 pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
194 MetaItemKind::List(ref l) => Some(&l[..]),
199 pub fn is_word(&self) -> bool {
200 matches!(self.kind, MetaItemKind::Word)
203 pub fn has_name(&self, name: Symbol) -> bool {
207 /// This is used in case you want the value span instead of the whole attribute. Example:
210 /// #[doc(alias = "foo")]
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)
220 pub fn span(&self) -> Span {
221 self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
224 pub fn meta(&self, span: Span) -> Option<MetaItem> {
226 path: self.path.clone(),
227 kind: MetaItemKind::from_mac_args(&self.args)?,
234 pub fn is_doc_comment(&self) -> bool {
236 AttrKind::Normal(..) => false,
237 AttrKind::DocComment(..) => true,
241 pub fn doc_str(&self) -> Option<Symbol> {
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())
251 pub fn get_normal_item(&self) -> &AttrItem {
253 AttrKind::Normal(ref item, _) => item,
254 AttrKind::DocComment(..) => panic!("unexpected doc comment"),
258 pub fn unwrap_normal_item(self) -> AttrItem {
260 AttrKind::Normal(item, _) => item,
261 AttrKind::DocComment(..) => panic!("unexpected doc comment"),
265 /// Extracts the MetaItem from inside this Attribute.
266 pub fn meta(&self) -> Option<MetaItem> {
268 AttrKind::Normal(ref item, _) => item.meta(self.span),
269 AttrKind::DocComment(..) => None,
273 pub fn tokens(&self) -> AttrAnnotatedTokenStream {
275 AttrKind::Normal(_, ref tokens) => tokens
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),
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)
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) }
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) }
307 pub fn mk_word_item(ident: Ident) -> MetaItem {
308 MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::Word }
311 pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
312 NestedMetaItem::MetaItem(mk_word_item(ident))
315 crate fn mk_attr_id() -> AttrId {
316 use std::sync::atomic::AtomicU32;
317 use std::sync::atomic::Ordering;
319 static NEXT_ATTR_ID: AtomicU32 = AtomicU32::new(0);
321 let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst);
322 assert!(id != u32::MAX);
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)
330 pub fn mk_attr_from_item(
332 tokens: Option<LazyTokenStream>,
336 Attribute { kind: AttrKind::Normal(item, tokens), id: mk_attr_id(), style, span }
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)
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)
349 pub fn mk_doc_comment(
350 comment_kind: CommentKind,
355 Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span }
358 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
359 items.iter().any(|item| item.has_name(name))
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;
370 Span::new(last_pos, segment.ident.span.lo(), segment.ident.span.ctxt());
371 idents.push(TokenTree::token(token::ModSep, mod_sep_span).into());
373 idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into());
374 last_pos = segment.ident.span.hi();
376 idents.extend(self.kind.token_trees_and_spacings(self.span));
380 fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
382 I: Iterator<Item = TokenTree>,
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),
390 let mut segments = if let token::Ident(name, _) = kind {
391 if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek()
394 vec![PathSegment::from_ident(Ident::new(name, span))]
396 break 'arm Path::from_ident(Ident::new(name, span));
399 vec![PathSegment::path_root(span)]
402 if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span })) =
403 tokens.next().map(TokenTree::uninterpolate)
405 segments.push(PathSegment::from_ident(Ident::new(name, span)));
409 if let Some(TokenTree::Token(Token { kind: token::ModSep, .. })) = tokens.peek()
416 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
417 Path { span, segments, tokens: None }
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(),
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()),
433 let span = path.span.with_hi(hi);
434 Some(MetaItem { path, kind, span })
439 pub fn mac_args(&self, span: Span) -> MacArgs {
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() {
447 tts.push(TokenTree::token(token::Comma, span).into());
449 tts.extend(item.token_trees_and_spacings())
452 DelimSpan::from_single(span),
453 MacDelimiter::Parenthesis,
454 TokenStream::new(tts),
460 fn token_trees_and_spacings(&self, span: Span) -> Vec<TreeAndSpacing> {
462 MetaItemKind::Word => vec![],
463 MetaItemKind::NameValue(ref lit) => {
465 TokenTree::token(token::Eq, span).into(),
466 TokenTree::Token(lit.to_token()).into(),
469 MetaItemKind::List(ref list) => {
470 let mut tokens = Vec::new();
471 for (i, item) in list.iter().enumerate() {
473 tokens.push(TokenTree::token(token::Comma, span).into());
475 tokens.extend(item.token_trees_and_spacings())
478 TokenTree::Delimited(
479 DelimSpan::from_single(span),
481 TokenStream::new(tokens),
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)?;
495 match tokens.next() {
496 None | Some(TokenTree::Token(Token { kind: token::Comma, .. })) => {}
500 Some(MetaItemKind::List(result))
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())
510 Some(TokenTree::Token(token)) => {
511 Lit::from_token(&token).ok().map(MetaItemKind::NameValue)
517 fn from_mac_args(args: &MacArgs) -> Option<MetaItemKind> {
519 MacArgs::Delimited(_, MacDelimiter::Parenthesis, tokens) => {
520 MetaItemKind::list_from_tokens(tokens.clone())
522 MacArgs::Delimited(..) => None,
523 MacArgs::Eq(_, token) => Lit::from_token(token).ok().map(MetaItemKind::NameValue),
524 MacArgs::Empty => Some(MetaItemKind::Word),
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();
535 MetaItemKind::list_from_tokens(inner_tokens)
537 Some(TokenTree::Delimited(..)) => None,
538 Some(TokenTree::Token(Token { kind: token::Eq, .. })) => {
540 MetaItemKind::name_value_from_tokens(tokens)
542 _ => Some(MetaItemKind::Word),
547 impl NestedMetaItem {
548 pub fn span(&self) -> Span {
550 NestedMetaItem::MetaItem(ref item) => item.span,
551 NestedMetaItem::Literal(ref lit) => lit.span,
555 fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> {
557 NestedMetaItem::MetaItem(ref item) => item.token_trees_and_spacings(),
558 NestedMetaItem::Literal(ref lit) => vec![TokenTree::Token(lit.to_token()).into()],
562 fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
564 I: Iterator<Item = TokenTree>,
566 match tokens.peek() {
567 Some(TokenTree::Token(token)) => {
568 if let Ok(lit) = Lit::from_token(token) {
570 return Some(NestedMetaItem::Literal(lit));
573 Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => {
574 let inner_tokens = inner_tokens.clone();
576 return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
580 MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)