1 //! Functions dealing with attributes and meta items.
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};
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};
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),
324 pub struct AttrIdGenerator(WorkerLocal<Cell<u32>>);
326 #[cfg(debug_assertions)]
327 static MAX_ATTR_ID: AtomicU32 = AtomicU32::new(u32::MAX);
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();
338 #[cfg(debug_assertions)]
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);
344 Cell::new(index.reverse_bits())
348 pub fn mk_attr_id(&self) -> AttrId {
349 let id = self.0.get();
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));
368 mk_attr_from_item(g, AttrItem { path, args, tokens: None }, None, style, span)
371 pub fn mk_attr_from_item(
374 tokens: Option<LazyAttrTokenStream>,
379 kind: AttrKind::Normal(P(NormalAttr { item, tokens })),
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)
392 pub fn mk_attr_name_value_str(
399 let lit = token::Lit::new(token::Str, escape_string_symbol(val), None);
402 kind: ExprKind::Lit(lit),
404 attrs: AttrVec::new(),
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)
412 pub fn mk_attr_nested_word(
419 let inner_tokens = TokenStream::new(vec![TokenTree::Token(
420 Token::from_ast_ident(Ident::new(inner, span)),
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,
430 mk_attr(g, style, path, attr_args, span)
433 pub fn mk_doc_comment(
435 comment_kind: CommentKind,
440 Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span }
443 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
444 items.iter().any(|item| item.has_name(name))
448 fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
450 I: Iterator<Item = TokenTree>,
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 },
458 let mut segments = if let token::Ident(name, _) = kind {
459 if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
463 thin_vec![PathSegment::from_ident(Ident::new(name, span))]
465 break 'arm Path::from_ident(Ident::new(name, span));
468 thin_vec![PathSegment::path_root(span)]
471 if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }, _)) =
472 tokens.next().map(TokenTree::uninterpolate)
474 segments.push(PathSegment::from_ident(Ident::new(name, span)));
478 if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }, _)) =
486 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
487 Path { span, segments, tokens: None }
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(),
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()),
503 let span = path.span.with_hi(hi);
504 Some(MetaItem { path, kind, span })
509 pub fn value_str(&self) -> Option<Symbol> {
511 MetaItemKind::NameValue(v) => match v.kind {
512 LitKind::Str(s, _) => Some(s),
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)?;
525 match tokens.next() {
526 None | Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) => {}
530 Some(MetaItemKind::List(result))
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())
540 Some(TokenTree::Token(token, _)) => {
541 MetaItemLit::from_token(&token).map(MetaItemKind::NameValue)
547 fn from_attr_args(args: &AttrArgs) -> Option<MetaItemKind> {
549 AttrArgs::Empty => Some(MetaItemKind::Word),
550 AttrArgs::Delimited(DelimArgs {
552 delim: MacDelimiter::Parenthesis,
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)
561 .map(|lit| MetaItemKind::NameValue(lit))
565 AttrArgs::Eq(_, AttrArgsEq::Hir(lit)) => Some(MetaItemKind::NameValue(lit.clone())),
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();
576 MetaItemKind::list_from_tokens(inner_tokens)
578 Some(TokenTree::Delimited(..)) => None,
579 Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
581 MetaItemKind::name_value_from_tokens(tokens)
583 _ => Some(MetaItemKind::Word),
588 impl NestedMetaItem {
589 pub fn span(&self) -> Span {
591 NestedMetaItem::MetaItem(item) => item.span,
592 NestedMetaItem::Lit(lit) => lit.span,
596 fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
598 I: Iterator<Item = TokenTree>,
600 match tokens.peek() {
601 Some(TokenTree::Token(token, _))
602 if let Some(lit) = MetaItemLit::from_token(token) =>
605 return Some(NestedMetaItem::Lit(lit));
607 Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
608 let inner_tokens = inner_tokens.clone();
610 return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
614 MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)