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
::{DelimSpan, LazyTokenStream, TokenStream, TokenTree, TreeAndSpacing}
;
11 use rustc_index
::bit_set
::GrowableBitSet
;
12 use rustc_span
::source_map
::BytePos
;
13 use rustc_span
::symbol
::{sym, Ident, Symbol}
;
18 pub struct MarkedAttrs(GrowableBitSet
<AttrId
>);
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())
27 pub fn mark(&mut self, attr
: &Attribute
) {
28 self.0.insert
(attr
.id
);
31 pub fn is_marked(&self, attr
: &Attribute
) -> bool
{
32 self.0.contains(attr
.id
)
37 /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
38 pub fn meta_item(&self) -> Option
<&MetaItem
> {
40 NestedMetaItem
::MetaItem(ref item
) => Some(item
),
45 /// Returns the `Lit` if `self` is a `NestedMetaItem::Literal`s.
46 pub fn literal(&self) -> Option
<&Lit
> {
48 NestedMetaItem
::Literal(ref lit
) => Some(lit
),
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
))
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())
62 pub fn name_or_empty(&self) -> Symbol
{
63 self.ident().unwrap_or_else(Ident
::invalid
).name
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())
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
));
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())
93 /// Returns `true` if the variant is `MetaItem`.
94 pub fn is_meta_item(&self) -> bool
{
95 self.meta_item().is_some()
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())
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()
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()
113 pub fn name_value_literal_span(&self) -> Option
<Span
> {
114 self.meta_item()?
.name_value_literal_span()
120 pub fn has_name(&self, name
: Symbol
) -> bool
{
122 AttrKind
::Normal(ref item
, _
) => item
.path
== name
,
123 AttrKind
::DocComment(..) => false,
127 /// For a single-segment attribute, returns its name; otherwise, returns `None`.
128 pub fn ident(&self) -> Option
<Ident
> {
130 AttrKind
::Normal(ref item
, _
) => {
131 if item
.path
.segments
.len() == 1 {
132 Some(item
.path
.segments
[0].ident
)
137 AttrKind
::DocComment(..) => None
,
140 pub fn name_or_empty(&self) -> Symbol
{
141 self.ident().unwrap_or_else(Ident
::invalid
).name
144 pub fn value_str(&self) -> Option
<Symbol
> {
146 AttrKind
::Normal(ref item
, _
) => item
.meta(self.span
).and_then(|meta
| meta
.value_str()),
147 AttrKind
::DocComment(..) => None
,
151 pub fn meta_item_list(&self) -> Option
<Vec
<NestedMetaItem
>> {
153 AttrKind
::Normal(ref item
, _
) => match item
.meta(self.span
) {
154 Some(MetaItem { kind: MetaItemKind::List(list), .. }
) => Some(list
),
157 AttrKind
::DocComment(..) => None
,
161 pub fn is_word(&self) -> bool
{
162 if let AttrKind
::Normal(item
, _
) = &self.kind
{
163 matches
!(item
.args
, MacArgs
::Empty
)
169 pub fn is_meta_item_list(&self) -> bool
{
170 self.meta_item_list().is_some()
173 /// Indicates if the attribute is a `ValueString`.
174 pub fn is_value_str(&self) -> bool
{
175 self.value_str().is_some()
178 /// This is used in case you want the value span instead of the whole attribute. Example:
181 /// #[doc(alias = "foo")]
184 /// In here, it'll return a span for `"foo"`.
185 pub fn name_value_literal_span(&self) -> Option
<Span
> {
187 AttrKind
::Normal(ref item
, _
) => {
188 item
.meta(self.span
).and_then(|meta
| meta
.name_value_literal_span())
190 AttrKind
::DocComment(..) => None
,
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 }
200 pub fn name_or_empty(&self) -> Symbol
{
201 self.ident().unwrap_or_else(Ident
::invalid
).name
205 // #[attribute(name = "value")]
207 pub fn name_value_literal(&self) -> Option
<&Lit
> {
209 MetaItemKind
::NameValue(v
) => Some(v
),
214 pub fn value_str(&self) -> Option
<Symbol
> {
216 MetaItemKind
::NameValue(ref v
) => match v
.kind
{
217 LitKind
::Str(ref s
, _
) => Some(*s
),
224 pub fn meta_item_list(&self) -> Option
<&[NestedMetaItem
]> {
226 MetaItemKind
::List(ref l
) => Some(&l
[..]),
231 pub fn is_word(&self) -> bool
{
232 matches
!(self.kind
, MetaItemKind
::Word
)
235 pub fn has_name(&self, name
: Symbol
) -> bool
{
239 pub fn is_value_str(&self) -> bool
{
240 self.value_str().is_some()
243 /// This is used in case you want the value span instead of the whole attribute. Example:
246 /// #[doc(alias = "foo")]
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
)
256 pub fn span(&self) -> Span
{
257 self.args
.span().map_or(self.path
.span
, |args_span
| self.path
.span
.to(args_span
))
260 pub fn meta(&self, span
: Span
) -> Option
<MetaItem
> {
262 path
: self.path
.clone(),
263 kind
: MetaItemKind
::from_mac_args(&self.args
)?
,
270 pub fn is_doc_comment(&self) -> bool
{
272 AttrKind
::Normal(..) => false,
273 AttrKind
::DocComment(..) => true,
277 pub fn doc_str(&self) -> Option
<Symbol
> {
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())
287 pub fn get_normal_item(&self) -> &AttrItem
{
289 AttrKind
::Normal(ref item
, _
) => item
,
290 AttrKind
::DocComment(..) => panic
!("unexpected doc comment"),
294 pub fn unwrap_normal_item(self) -> AttrItem
{
296 AttrKind
::Normal(item
, _
) => item
,
297 AttrKind
::DocComment(..) => panic
!("unexpected doc comment"),
301 /// Extracts the MetaItem from inside this Attribute.
302 pub fn meta(&self) -> Option
<MetaItem
> {
304 AttrKind
::Normal(ref item
, _
) => item
.meta(self.span
),
305 AttrKind
::DocComment(..) => None
,
309 pub fn tokens(&self) -> TokenStream
{
311 AttrKind
::Normal(_
, ref tokens
) => tokens
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
),
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
)
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) }
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) }
339 pub fn mk_word_item(ident
: Ident
) -> MetaItem
{
340 MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::Word }
343 pub fn mk_nested_word_item(ident
: Ident
) -> NestedMetaItem
{
344 NestedMetaItem
::MetaItem(mk_word_item(ident
))
347 crate fn mk_attr_id() -> AttrId
{
348 use std
::sync
::atomic
::AtomicU32
;
349 use std
::sync
::atomic
::Ordering
;
351 static NEXT_ATTR_ID
: AtomicU32
= AtomicU32
::new(0);
353 let id
= NEXT_ATTR_ID
.fetch_add(1, Ordering
::SeqCst
);
354 assert
!(id
!= u32::MAX
);
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
)
362 pub fn mk_attr_from_item(
364 tokens
: Option
<LazyTokenStream
>,
368 Attribute { kind: AttrKind::Normal(item, tokens), id: mk_attr_id(), style, span }
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
)
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
)
381 pub fn mk_doc_comment(
382 comment_kind
: CommentKind
,
387 Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span }
390 pub fn list_contains_name(items
: &[NestedMetaItem
], name
: Symbol
) -> bool
{
391 items
.iter().any(|item
| item
.has_name(name
))
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;
402 Span
::new(last_pos
, segment
.ident
.span
.lo(), segment
.ident
.span
.ctxt());
403 idents
.push(TokenTree
::token(token
::ModSep
, mod_sep_span
).into());
405 idents
.push(TokenTree
::Token(Token
::from_ast_ident(segment
.ident
)).into());
406 last_pos
= segment
.ident
.span
.hi();
408 idents
.extend(self.kind
.token_trees_and_spacings(self.span
));
412 fn from_tokens
<I
>(tokens
: &mut iter
::Peekable
<I
>) -> Option
<MetaItem
>
414 I
: Iterator
<Item
= TokenTree
>,
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
),
422 let mut segments
= if let token
::Ident(name
, _
) = kind
{
423 if let Some(TokenTree
::Token(Token { kind: token::ModSep, .. }
)) = tokens
.peek()
426 vec
![PathSegment
::from_ident(Ident
::new(name
, span
))]
428 break 'arm Path
::from_ident(Ident
::new(name
, span
));
431 vec
![PathSegment
::path_root(span
)]
434 if let Some(TokenTree
::Token(Token { kind: token::Ident(name, _), span }
)) =
435 tokens
.next().map(TokenTree
::uninterpolate
)
437 segments
.push(PathSegment
::from_ident(Ident
::new(name
, span
)));
441 if let Some(TokenTree
::Token(Token { kind: token::ModSep, .. }
)) = tokens
.peek()
448 let span
= span
.with_hi(segments
.last().unwrap().ident
.span
.hi());
449 Path { span, segments, tokens: None }
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(),
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()),
465 let span
= path
.span
.with_hi(hi
);
466 Some(MetaItem { path, kind, span }
)
471 pub fn mac_args(&self, span
: Span
) -> MacArgs
{
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() {
479 tts
.push(TokenTree
::token(token
::Comma
, span
).into());
481 tts
.extend(item
.token_trees_and_spacings())
484 DelimSpan
::from_single(span
),
485 MacDelimiter
::Parenthesis
,
486 TokenStream
::new(tts
),
492 fn token_trees_and_spacings(&self, span
: Span
) -> Vec
<TreeAndSpacing
> {
494 MetaItemKind
::Word
=> vec
![],
495 MetaItemKind
::NameValue(ref lit
) => {
497 TokenTree
::token(token
::Eq
, span
).into(),
498 TokenTree
::Token(lit
.to_token()).into(),
501 MetaItemKind
::List(ref list
) => {
502 let mut tokens
= Vec
::new();
503 for (i
, item
) in list
.iter().enumerate() {
505 tokens
.push(TokenTree
::token(token
::Comma
, span
).into());
507 tokens
.extend(item
.token_trees_and_spacings())
510 TokenTree
::Delimited(
511 DelimSpan
::from_single(span
),
513 TokenStream
::new(tokens
),
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
)?
;
527 match tokens
.next() {
528 None
| Some(TokenTree
::Token(Token { kind: token::Comma, .. }
)) => {}
532 Some(MetaItemKind
::List(result
))
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())
542 Some(TokenTree
::Token(token
)) => {
543 Lit
::from_token(&token
).ok().map(MetaItemKind
::NameValue
)
549 fn from_mac_args(args
: &MacArgs
) -> Option
<MetaItemKind
> {
551 MacArgs
::Delimited(_
, MacDelimiter
::Parenthesis
, tokens
) => {
552 MetaItemKind
::list_from_tokens(tokens
.clone())
554 MacArgs
::Delimited(..) => None
,
555 MacArgs
::Eq(_
, token
) => Lit
::from_token(token
).ok().map(MetaItemKind
::NameValue
),
556 MacArgs
::Empty
=> Some(MetaItemKind
::Word
),
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();
567 MetaItemKind
::list_from_tokens(inner_tokens
)
569 Some(TokenTree
::Delimited(..)) => None
,
570 Some(TokenTree
::Token(Token { kind: token::Eq, .. }
)) => {
572 MetaItemKind
::name_value_from_tokens(tokens
)
574 _
=> Some(MetaItemKind
::Word
),
579 impl NestedMetaItem
{
580 pub fn span(&self) -> Span
{
582 NestedMetaItem
::MetaItem(ref item
) => item
.span
,
583 NestedMetaItem
::Literal(ref lit
) => lit
.span
,
587 fn token_trees_and_spacings(&self) -> Vec
<TreeAndSpacing
> {
589 NestedMetaItem
::MetaItem(ref item
) => item
.token_trees_and_spacings(),
590 NestedMetaItem
::Literal(ref lit
) => vec
![TokenTree
::Token(lit
.to_token()).into()],
594 fn from_tokens
<I
>(tokens
: &mut iter
::Peekable
<I
>) -> Option
<NestedMetaItem
>
596 I
: Iterator
<Item
= TokenTree
>,
598 match tokens
.peek() {
599 Some(TokenTree
::Token(token
)) => {
600 if let Ok(lit
) = Lit
::from_token(token
) {
602 return Some(NestedMetaItem
::Literal(lit
));
605 Some(TokenTree
::Delimited(_
, token
::NoDelim
, inner_tokens
)) => {
606 let inner_tokens
= inner_tokens
.clone();
608 return NestedMetaItem
::from_tokens(&mut inner_tokens
.into_trees().peekable());
612 MetaItem
::from_tokens(tokens
).map(NestedMetaItem
::MetaItem
)