1 //! Functions dealing with attributes and meta items.
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};
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};
19 #[cfg(debug_assertions)]
21 #[cfg(debug_assertions)]
22 use std::sync::atomic::{AtomicU32, Ordering};
23 use thin_vec::thin_vec;
25 pub struct MarkedAttrs(GrowableBitSet<AttrId>);
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())
34 pub fn mark(&mut self, attr: &Attribute) {
35 self.0.insert(attr.id);
38 pub fn is_marked(&self, attr: &Attribute) -> bool {
39 self.0.contains(attr.id)
44 /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
45 pub fn meta_item(&self) -> Option<&MetaItem> {
47 NestedMetaItem::MetaItem(item) => Some(item),
52 /// Returns the `MetaItemLit` if `self` is a `NestedMetaItem::Literal`s.
53 pub fn lit(&self) -> Option<&MetaItemLit> {
55 NestedMetaItem::Lit(lit) => Some(lit),
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))
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())
69 pub fn name_or_empty(&self) -> Symbol {
70 self.ident().unwrap_or_else(Ident::empty).name
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())
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()
87 return Some((ident.name, lit));
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())
99 /// Returns `true` if the variant is `MetaItem`.
100 pub fn is_meta_item(&self) -> bool {
101 self.meta_item().is_some()
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())
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()
117 pub fn has_name(&self, name: Symbol) -> bool {
119 AttrKind::Normal(normal) => normal.item.path == name,
120 AttrKind::DocComment(..) => false,
124 /// For a single-segment attribute, returns its name; otherwise, returns `None`.
125 pub fn ident(&self) -> Option<Ident> {
127 AttrKind::Normal(normal) => {
128 if let [ident] = &*normal.item.path.segments {
134 AttrKind::DocComment(..) => None,
137 pub fn name_or_empty(&self) -> Symbol {
138 self.ident().unwrap_or_else(Ident::empty).name
141 pub fn value_str(&self) -> Option<Symbol> {
143 AttrKind::Normal(normal) => normal.item.meta_kind().and_then(|kind| kind.value_str()),
144 AttrKind::DocComment(..) => None,
148 pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
150 AttrKind::Normal(normal) => match normal.item.meta_kind() {
151 Some(MetaItemKind::List(list)) => Some(list),
154 AttrKind::DocComment(..) => None,
158 pub fn is_word(&self) -> bool {
159 if let AttrKind::Normal(normal) = &self.kind {
160 matches!(normal.item.args, AttrArgs::Empty)
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 }
172 pub fn name_or_empty(&self) -> Symbol {
173 self.ident().unwrap_or_else(Ident::empty).name
178 /// #[attribute(name = "value")]
181 pub fn name_value_literal(&self) -> Option<&MetaItemLit> {
183 MetaItemKind::NameValue(v) => Some(v),
188 pub fn value_str(&self) -> Option<Symbol> {
189 self.kind.value_str()
192 pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
194 MetaItemKind::List(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> {
225 Some(MetaItem { path: self.path.clone(), kind: self.meta_kind()?, span })
228 pub fn meta_kind(&self) -> Option<MetaItemKind> {
229 MetaItemKind::from_attr_args(&self.args)
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 {
239 AttrKind::Normal(..) => false,
240 AttrKind::DocComment(..) => true,
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)> {
251 AttrKind::DocComment(kind, data) => Some((data, kind)),
252 AttrKind::Normal(ref normal) if normal.item.path == sym::doc => normal
255 .and_then(|kind| kind.value_str())
256 .map(|data| (data, CommentKind::Line)),
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> {
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())
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()))
279 pub fn get_normal_item(&self) -> &AttrItem {
281 AttrKind::Normal(normal) => &normal.item,
282 AttrKind::DocComment(..) => panic!("unexpected doc comment"),
286 pub fn unwrap_normal_item(self) -> AttrItem {
288 AttrKind::Normal(normal) => normal.into_inner().item,
289 AttrKind::DocComment(..) => panic!("unexpected doc comment"),
293 /// Extracts the MetaItem from inside this Attribute.
294 pub fn meta(&self) -> Option<MetaItem> {
296 AttrKind::Normal(normal) => normal.item.meta(self.span),
297 AttrKind::DocComment(..) => None,
301 pub fn meta_kind(&self) -> Option<MetaItemKind> {
303 AttrKind::Normal(normal) => normal.item.meta_kind(),
304 AttrKind::DocComment(..) => None,
308 pub fn tokens(&self) -> TokenStream {
310 AttrKind::Normal(normal) => normal
313 .unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self))
314 .to_attr_token_stream()
316 &AttrKind::DocComment(comment_kind, data) => TokenStream::new(vec![TokenTree::Token(
317 Token::new(token::DocComment(comment_kind, self.style, data), self.span),
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)
330 pub fn mk_name_value_item(ident: Ident, kind: LitKind, lit_span: Span) -> MetaItem {
331 let lit = MetaItemLit { token_lit: kind.to_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 }
336 pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
338 #[cfg(debug_assertions)]
339 static MAX_ATTR_ID: AtomicU32 = AtomicU32::new(u32::MAX);
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();
350 #[cfg(debug_assertions)]
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);
356 Cell::new(index.reverse_bits())
360 pub fn mk_attr_id(&self) -> AttrId {
361 let id = self.0.get();
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));
380 mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
383 pub fn mk_attr_from_item(
386 tokens: Option<LazyAttrTokenStream>,
391 kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
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)
404 pub fn mk_attr_name_value_str(
411 let lit = LitKind::Str(val, StrStyle::Cooked).to_token_lit();
414 kind: ExprKind::Lit(lit),
416 attrs: AttrVec::new(),
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)
424 pub fn mk_attr_nested_word(
431 let inner_tokens = TokenStream::new(vec![TokenTree::Token(
432 Token::from_ast_ident(Ident::new(inner, span)),
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,
442 mk_attr(g, style, path, attr_args, span)
445 pub fn mk_doc_comment(
447 comment_kind: CommentKind,
452 Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
455 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
456 items.iter().any(|item| item.has_name(name))
460 fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
462 I: Iterator<Item = TokenTree>,
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 },
470 let mut segments = if let token::Ident(name, _) = kind {
471 if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
475 thin_vec![PathSegment::from_ident(Ident::new(name, span))]
477 break 'arm Path::from_ident(Ident::new(name, span));
480 thin_vec![PathSegment::path_root(span)]
483 if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
484 tokens.next().map(TokenTree::uninterpolate)
486 segments.push(PathSegment::from_ident(Ident::new(name, span)));
490 if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
498 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
499 Path { span, segments, tokens: None }
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(),
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()),
515 let span = path.span.with_hi(hi);
516 Some(MetaItem { path, kind, span })
521 pub fn value_str(&self) -> Option<Symbol> {
523 MetaItemKind::NameValue(v) => match v.kind {
524 LitKind::Str(s, _) => Some(s),
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)?;
537 match tokens.next() {
538 None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}
542 Some(MetaItemKind::List(result))
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())
552 Some(TokenTree::Token(token, _)) => {
553 MetaItemLit::from_token(&token).map(MetaItemKind::NameValue)
559 fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {
561 AttrArgs::Empty => Some(MetaItemKind::Word),
562 AttrArgs::Delimited(DelimArgs {
564 delim: MacDelimiter::Parenthesis,
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)
573 .map(|lit| MetaItemKind::NameValue(lit))
577 AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
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();
588 MetaItemKind::list_from_tokens(inner_tokens)
590 Some(TokenTree::Delimited(..)) => None,
591 Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
593 MetaItemKind::name_value_from_tokens(tokens)
595 _ => Some(MetaItemKind::Word),
600 impl NestedMetaItem {
601 pub fn span(&self) -> Span {
603 NestedMetaItem::MetaItem(item) => item.span,
604 NestedMetaItem::Lit(lit) => lit.span,
608 fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
610 I: Iterator<Item = TokenTree>,
612 match tokens.peek() {
613 Some(TokenTree::Token(token, _))
614 if let Some(lit) = MetaItemLit::from_token(token) =>
617 return Some(NestedMetaItem::Lit(lit));
619 Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
620 let inner_tokens = inner_tokens.clone();
622 return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
626 MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)