1 //! Functions dealing with attributes and meta items.
8 pub use StabilityLevel
::*;
9 pub use crate::ast
::Attribute
;
12 use crate::ast
::{AttrItem, AttrId, AttrKind, AttrStyle, Name, Ident, Path, PathSegment}
;
13 use crate::ast
::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem}
;
14 use crate::ast
::{Lit, LitKind, Expr, Item, Local, Stmt, StmtKind, GenericParam}
;
15 use crate::mut_visit
::visit_clobber
;
16 use crate::source_map
::{BytePos, Spanned}
;
17 use crate::token
::{self, Token}
;
19 use crate::symbol
::{sym, Symbol}
;
21 use crate::tokenstream
::{DelimSpan, TokenStream, TokenTree, TreeAndJoint}
;
28 use std
::ops
::DerefMut
;
30 pub fn mark_used(attr
: &Attribute
) {
31 debug
!("marking {:?} as used", attr
);
32 GLOBALS
.with(|globals
| {
33 globals
.used_attrs
.lock().insert(attr
.id
);
37 pub fn is_used(attr
: &Attribute
) -> bool
{
38 GLOBALS
.with(|globals
| {
39 globals
.used_attrs
.lock().contains(attr
.id
)
43 pub fn mark_known(attr
: &Attribute
) {
44 debug
!("marking {:?} as known", attr
);
45 GLOBALS
.with(|globals
| {
46 globals
.known_attrs
.lock().insert(attr
.id
);
50 pub fn is_known(attr
: &Attribute
) -> bool
{
51 GLOBALS
.with(|globals
| {
52 globals
.known_attrs
.lock().contains(attr
.id
)
56 pub fn is_known_lint_tool(m_item
: Ident
) -> bool
{
57 [sym
::clippy
, sym
::rustc
].contains(&m_item
.name
)
61 /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
62 pub fn meta_item(&self) -> Option
<&MetaItem
> {
64 NestedMetaItem
::MetaItem(ref item
) => Some(item
),
69 /// Returns the `Lit` if `self` is a `NestedMetaItem::Literal`s.
70 pub fn literal(&self) -> Option
<&Lit
> {
72 NestedMetaItem
::Literal(ref lit
) => Some(lit
),
77 /// Returns `true` if this list item is a MetaItem with a name of `name`.
78 pub fn check_name(&self, name
: Symbol
) -> bool
{
79 self.meta_item().map_or(false, |meta_item
| meta_item
.check_name(name
))
82 /// For a single-segment meta item, returns its name; otherwise, returns `None`.
83 pub fn ident(&self) -> Option
<Ident
> {
84 self.meta_item().and_then(|meta_item
| meta_item
.ident())
86 pub fn name_or_empty(&self) -> Symbol
{
87 self.ident().unwrap_or(Ident
::invalid()).name
90 /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
91 /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
92 pub fn value_str(&self) -> Option
<Symbol
> {
93 self.meta_item().and_then(|meta_item
| meta_item
.value_str())
96 /// Returns a name and single literal value tuple of the `MetaItem`.
97 pub fn name_value_literal(&self) -> Option
<(Name
, &Lit
)> {
98 self.meta_item().and_then(
99 |meta_item
| meta_item
.meta_item_list().and_then(
101 if meta_item_list
.len() == 1 {
102 if let Some(ident
) = meta_item
.ident() {
103 if let Some(lit
) = meta_item_list
[0].literal() {
104 return Some((ident
.name
, lit
));
112 /// Gets a list of inner meta items from a list `MetaItem` type.
113 pub fn meta_item_list(&self) -> Option
<&[NestedMetaItem
]> {
114 self.meta_item().and_then(|meta_item
| meta_item
.meta_item_list())
117 /// Returns `true` if the variant is `MetaItem`.
118 pub fn is_meta_item(&self) -> bool
{
119 self.meta_item().is_some()
122 /// Returns `true` if the variant is `Literal`.
123 pub fn is_literal(&self) -> bool
{
124 self.literal().is_some()
127 /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
128 pub fn is_word(&self) -> bool
{
129 self.meta_item().map_or(false, |meta_item
| meta_item
.is_word())
132 /// Returns `true` if `self` is a `MetaItem` and the meta item is a `ValueString`.
133 pub fn is_value_str(&self) -> bool
{
134 self.value_str().is_some()
137 /// Returns `true` if `self` is a `MetaItem` and the meta item is a list.
138 pub fn is_meta_item_list(&self) -> bool
{
139 self.meta_item_list().is_some()
144 pub fn has_name(&self, name
: Symbol
) -> bool
{
146 AttrKind
::Normal(ref item
) => item
.path
== name
,
147 AttrKind
::DocComment(_
) => name
== sym
::doc
,
151 /// Returns `true` if the attribute's path matches the argument. If it matches, then the
152 /// attribute is marked as used.
153 pub fn check_name(&self, name
: Symbol
) -> bool
{
154 let matches
= self.has_name(name
);
161 /// For a single-segment attribute, returns its name; otherwise, returns `None`.
162 pub fn ident(&self) -> Option
<Ident
> {
164 AttrKind
::Normal(ref item
) => {
165 if item
.path
.segments
.len() == 1 {
166 Some(item
.path
.segments
[0].ident
)
171 AttrKind
::DocComment(_
) => Some(Ident
::new(sym
::doc
, self.span
)),
174 pub fn name_or_empty(&self) -> Symbol
{
175 self.ident().unwrap_or(Ident
::invalid()).name
178 pub fn value_str(&self) -> Option
<Symbol
> {
180 AttrKind
::Normal(ref item
) => {
181 item
.meta(self.span
).and_then(|meta
| meta
.value_str())
183 AttrKind
::DocComment(comment
) => Some(comment
),
187 pub fn meta_item_list(&self) -> Option
<Vec
<NestedMetaItem
>> {
189 AttrKind
::Normal(ref item
) => {
190 match item
.meta(self.span
) {
191 Some(MetaItem { kind: MetaItemKind::List(list), .. }
) => Some(list
),
195 AttrKind
::DocComment(_
) => None
,
199 pub fn is_word(&self) -> bool
{
200 if let AttrKind
::Normal(item
) = &self.kind
{
201 matches
!(item
.args
, MacArgs
::Empty
)
207 pub fn is_meta_item_list(&self) -> bool
{
208 self.meta_item_list().is_some()
211 /// Indicates if the attribute is a `ValueString`.
212 pub fn is_value_str(&self) -> bool
{
213 self.value_str().is_some()
218 /// For a single-segment meta item, returns its name; otherwise, returns `None`.
219 pub fn ident(&self) -> Option
<Ident
> {
220 if self.path
.segments
.len() == 1 {
221 Some(self.path
.segments
[0].ident
)
226 pub fn name_or_empty(&self) -> Symbol
{
227 self.ident().unwrap_or(Ident
::invalid()).name
231 // #[attribute(name = "value")]
233 pub fn name_value_literal(&self) -> Option
<&Lit
> {
235 MetaItemKind
::NameValue(v
) => Some(v
),
240 pub fn value_str(&self) -> Option
<Symbol
> {
242 MetaItemKind
::NameValue(ref v
) => {
244 LitKind
::Str(ref s
, _
) => Some(*s
),
252 pub fn meta_item_list(&self) -> Option
<&[NestedMetaItem
]> {
254 MetaItemKind
::List(ref l
) => Some(&l
[..]),
259 pub fn is_word(&self) -> bool
{
261 MetaItemKind
::Word
=> true,
266 pub fn check_name(&self, name
: Symbol
) -> bool
{
270 pub fn is_value_str(&self) -> bool
{
271 self.value_str().is_some()
274 pub fn is_meta_item_list(&self) -> bool
{
275 self.meta_item_list().is_some()
280 pub fn meta(&self, span
: Span
) -> Option
<MetaItem
> {
282 path
: self.path
.clone(),
283 kind
: MetaItemKind
::from_mac_args(&self.args
)?
,
290 pub fn is_doc_comment(&self) -> bool
{
292 AttrKind
::Normal(_
) => false,
293 AttrKind
::DocComment(_
) => true,
297 pub fn get_normal_item(&self) -> &AttrItem
{
299 AttrKind
::Normal(ref item
) => item
,
300 AttrKind
::DocComment(_
) => panic
!("unexpected sugared doc"),
304 pub fn unwrap_normal_item(self) -> AttrItem
{
306 AttrKind
::Normal(item
) => item
,
307 AttrKind
::DocComment(_
) => panic
!("unexpected sugared doc"),
311 /// Extracts the MetaItem from inside this Attribute.
312 pub fn meta(&self) -> Option
<MetaItem
> {
314 AttrKind
::Normal(ref item
) => item
.meta(self.span
),
315 AttrKind
::DocComment(comment
) =>
316 Some(mk_name_value_item_str(Ident
::new(sym
::doc
, self.span
), comment
, self.span
)),
323 pub fn mk_name_value_item_str(ident
: Ident
, str: Symbol
, str_span
: Span
) -> MetaItem
{
324 let lit_kind
= LitKind
::Str(str, ast
::StrStyle
::Cooked
);
325 mk_name_value_item(ident
, lit_kind
, str_span
)
328 pub fn mk_name_value_item(ident
: Ident
, lit_kind
: LitKind
, lit_span
: Span
) -> MetaItem
{
329 let lit
= Lit
::from_lit_kind(lit_kind
, lit_span
);
330 let span
= ident
.span
.to(lit_span
);
331 MetaItem { path: Path::from_ident(ident), span, kind: MetaItemKind::NameValue(lit) }
334 pub fn mk_list_item(ident
: Ident
, items
: Vec
<NestedMetaItem
>) -> MetaItem
{
335 MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::List(items) }
338 pub fn mk_word_item(ident
: Ident
) -> MetaItem
{
339 MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::Word }
342 pub fn mk_nested_word_item(ident
: Ident
) -> NestedMetaItem
{
343 NestedMetaItem
::MetaItem(mk_word_item(ident
))
346 crate fn mk_attr_id() -> AttrId
{
347 use std
::sync
::atomic
::AtomicUsize
;
348 use std
::sync
::atomic
::Ordering
;
350 static NEXT_ATTR_ID
: AtomicUsize
= AtomicUsize
::new(0);
352 let id
= NEXT_ATTR_ID
.fetch_add(1, Ordering
::SeqCst
);
353 assert
!(id
!= ::std
::usize::MAX
);
357 pub fn mk_attr(style
: AttrStyle
, path
: Path
, args
: MacArgs
, span
: Span
) -> Attribute
{
358 mk_attr_from_item(style
, AttrItem { path, args }
, span
)
361 pub fn mk_attr_from_item(style
: AttrStyle
, item
: AttrItem
, span
: Span
) -> Attribute
{
363 kind
: AttrKind
::Normal(item
),
370 /// Returns an inner attribute with the given value and span.
371 pub fn mk_attr_inner(item
: MetaItem
) -> Attribute
{
372 mk_attr(AttrStyle
::Inner
, item
.path
, item
.kind
.mac_args(item
.span
), item
.span
)
375 /// Returns an outer attribute with the given value and span.
376 pub fn mk_attr_outer(item
: MetaItem
) -> Attribute
{
377 mk_attr(AttrStyle
::Outer
, item
.path
, item
.kind
.mac_args(item
.span
), item
.span
)
380 pub fn mk_doc_comment(style
: AttrStyle
, comment
: Symbol
, span
: Span
) -> Attribute
{
382 kind
: AttrKind
::DocComment(comment
),
389 pub fn list_contains_name(items
: &[NestedMetaItem
], name
: Symbol
) -> bool
{
390 items
.iter().any(|item
| {
391 item
.check_name(name
)
395 pub fn contains_name(attrs
: &[Attribute
], name
: Symbol
) -> bool
{
396 attrs
.iter().any(|item
| {
397 item
.check_name(name
)
401 pub fn find_by_name(attrs
: &[Attribute
], name
: Symbol
) -> Option
<&Attribute
> {
402 attrs
.iter().find(|attr
| attr
.check_name(name
))
405 pub fn allow_internal_unstable
<'a
>(
407 span_diagnostic
: &'a errors
::Handler
,
408 ) -> Option
<impl Iterator
<Item
= Symbol
> + 'a
> {
409 find_by_name(attrs
, sym
::allow_internal_unstable
).and_then(|attr
| {
410 attr
.meta_item_list().or_else(|| {
411 span_diagnostic
.span_err(
413 "allow_internal_unstable expects list of feature names"
416 }).map(|features
| features
.into_iter().filter_map(move |it
| {
417 let name
= it
.ident().map(|ident
| ident
.name
);
419 span_diagnostic
.span_err(
421 "`allow_internal_unstable` expects feature names",
429 pub fn filter_by_name(attrs
: &[Attribute
], name
: Symbol
)
430 -> impl Iterator
<Item
=&Attribute
> {
431 attrs
.iter().filter(move |attr
| attr
.check_name(name
))
434 pub fn first_attr_value_str_by_name(attrs
: &[Attribute
], name
: Symbol
) -> Option
<Symbol
> {
436 .find(|at
| at
.check_name(name
))
437 .and_then(|at
| at
.value_str())
441 fn token_trees_and_joints(&self) -> Vec
<TreeAndJoint
> {
442 let mut idents
= vec
![];
443 let mut last_pos
= BytePos(0 as u32);
444 for (i
, segment
) in self.path
.segments
.iter().enumerate() {
445 let is_first
= i
== 0;
447 let mod_sep_span
= Span
::new(last_pos
,
448 segment
.ident
.span
.lo(),
449 segment
.ident
.span
.ctxt());
450 idents
.push(TokenTree
::token(token
::ModSep
, mod_sep_span
).into());
452 idents
.push(TokenTree
::Token(Token
::from_ast_ident(segment
.ident
)).into());
453 last_pos
= segment
.ident
.span
.hi();
455 idents
.extend(self.kind
.token_trees_and_joints(self.span
));
459 fn from_tokens
<I
>(tokens
: &mut iter
::Peekable
<I
>) -> Option
<MetaItem
>
460 where I
: Iterator
<Item
= TokenTree
>,
462 // FIXME: Share code with `parse_path`.
463 let path
= match tokens
.next() {
464 Some(TokenTree
::Token(Token { kind: kind @ token::Ident(..), span }
)) |
465 Some(TokenTree
::Token(Token { kind: kind @ token::ModSep, span }
)) => 'arm
: {
466 let mut segments
= if let token
::Ident(name
, _
) = kind
{
467 if let Some(TokenTree
::Token(Token { kind: token::ModSep, .. }
))
470 vec
![PathSegment
::from_ident(Ident
::new(name
, span
))]
472 break 'arm Path
::from_ident(Ident
::new(name
, span
));
475 vec
![PathSegment
::path_root(span
)]
478 if let Some(TokenTree
::Token(Token { kind: token::Ident(name, _), span }
))
480 segments
.push(PathSegment
::from_ident(Ident
::new(name
, span
)));
484 if let Some(TokenTree
::Token(Token { kind: token::ModSep, .. }
))
491 let span
= span
.with_hi(segments
.last().unwrap().ident
.span
.hi());
492 Path { span, segments }
494 Some(TokenTree
::Token(Token { kind: token::Interpolated(nt), .. }
)) => match *nt
{
495 token
::Nonterminal
::NtIdent(ident
, _
) => Path
::from_ident(ident
),
496 token
::Nonterminal
::NtMeta(ref item
) => return item
.meta(item
.path
.span
),
497 token
::Nonterminal
::NtPath(ref path
) => path
.clone(),
502 let list_closing_paren_pos
= tokens
.peek().map(|tt
| tt
.span().hi());
503 let kind
= MetaItemKind
::from_tokens(tokens
)?
;
504 let hi
= match kind
{
505 MetaItemKind
::NameValue(ref lit
) => lit
.span
.hi(),
506 MetaItemKind
::List(..) => list_closing_paren_pos
.unwrap_or(path
.span
.hi()),
509 let span
= path
.span
.with_hi(hi
);
510 Some(MetaItem { path, kind, span }
)
515 pub fn mac_args(&self, span
: Span
) -> MacArgs
{
517 MetaItemKind
::Word
=> MacArgs
::Empty
,
518 MetaItemKind
::NameValue(lit
) => MacArgs
::Eq(span
, lit
.token_tree().into()),
519 MetaItemKind
::List(list
) => {
520 let mut tts
= Vec
::new();
521 for (i
, item
) in list
.iter().enumerate() {
523 tts
.push(TokenTree
::token(token
::Comma
, span
).into());
525 tts
.extend(item
.token_trees_and_joints())
528 DelimSpan
::from_single(span
), MacDelimiter
::Parenthesis
, TokenStream
::new(tts
)
534 fn token_trees_and_joints(&self, span
: Span
) -> Vec
<TreeAndJoint
> {
536 MetaItemKind
::Word
=> vec
![],
537 MetaItemKind
::NameValue(ref lit
) => {
539 TokenTree
::token(token
::Eq
, span
).into(),
540 lit
.token_tree().into(),
543 MetaItemKind
::List(ref list
) => {
544 let mut tokens
= Vec
::new();
545 for (i
, item
) in list
.iter().enumerate() {
547 tokens
.push(TokenTree
::token(token
::Comma
, span
).into());
549 tokens
.extend(item
.token_trees_and_joints())
552 TokenTree
::Delimited(
553 DelimSpan
::from_single(span
),
555 TokenStream
::new(tokens
).into(),
562 fn list_from_tokens(tokens
: TokenStream
) -> Option
<MetaItemKind
> {
563 let mut tokens
= tokens
.into_trees().peekable();
564 let mut result
= Vec
::new();
565 while let Some(..) = tokens
.peek() {
566 let item
= NestedMetaItem
::from_tokens(&mut tokens
)?
;
568 match tokens
.next() {
569 None
| Some(TokenTree
::Token(Token { kind: token::Comma, .. }
)) => {}
573 Some(MetaItemKind
::List(result
))
576 fn name_value_from_tokens(
577 tokens
: &mut impl Iterator
<Item
= TokenTree
>,
578 ) -> Option
<MetaItemKind
> {
579 match tokens
.next() {
580 Some(TokenTree
::Token(token
)) =>
581 Lit
::from_token(&token
).ok().map(MetaItemKind
::NameValue
),
586 fn from_mac_args(args
: &MacArgs
) -> Option
<MetaItemKind
> {
588 MacArgs
::Delimited(_
, MacDelimiter
::Parenthesis
, tokens
) =>
589 MetaItemKind
::list_from_tokens(tokens
.clone()),
590 MacArgs
::Delimited(..) => None
,
591 MacArgs
::Eq(_
, tokens
) => {
592 assert
!(tokens
.len() == 1);
593 MetaItemKind
::name_value_from_tokens(&mut tokens
.trees())
595 MacArgs
::Empty
=> Some(MetaItemKind
::Word
),
600 tokens
: &mut iter
::Peekable
<impl Iterator
<Item
= TokenTree
>>,
601 ) -> Option
<MetaItemKind
> {
602 match tokens
.peek() {
603 Some(TokenTree
::Delimited(_
, token
::Paren
, inner_tokens
)) => {
604 let inner_tokens
= inner_tokens
.clone();
606 MetaItemKind
::list_from_tokens(inner_tokens
)
608 Some(TokenTree
::Delimited(..)) => None
,
609 Some(TokenTree
::Token(Token { kind: token::Eq, .. }
)) => {
611 MetaItemKind
::name_value_from_tokens(tokens
)
613 _
=> Some(MetaItemKind
::Word
),
618 impl NestedMetaItem
{
619 pub fn span(&self) -> Span
{
621 NestedMetaItem
::MetaItem(ref item
) => item
.span
,
622 NestedMetaItem
::Literal(ref lit
) => lit
.span
,
626 fn token_trees_and_joints(&self) -> Vec
<TreeAndJoint
> {
628 NestedMetaItem
::MetaItem(ref item
) => item
.token_trees_and_joints(),
629 NestedMetaItem
::Literal(ref lit
) => vec
![lit
.token_tree().into()],
633 fn from_tokens
<I
>(tokens
: &mut iter
::Peekable
<I
>) -> Option
<NestedMetaItem
>
634 where I
: Iterator
<Item
= TokenTree
>,
636 if let Some(TokenTree
::Token(token
)) = tokens
.peek() {
637 if let Ok(lit
) = Lit
::from_token(token
) {
639 return Some(NestedMetaItem
::Literal(lit
));
643 MetaItem
::from_tokens(tokens
).map(NestedMetaItem
::MetaItem
)
647 pub trait HasAttrs
: Sized
{
648 fn attrs(&self) -> &[ast
::Attribute
];
649 fn visit_attrs
<F
: FnOnce(&mut Vec
<ast
::Attribute
>)>(&mut self, f
: F
);
652 impl<T
: HasAttrs
> HasAttrs
for Spanned
<T
> {
653 fn attrs(&self) -> &[ast
::Attribute
] { self.node.attrs() }
654 fn visit_attrs
<F
: FnOnce(&mut Vec
<ast
::Attribute
>)>(&mut self, f
: F
) {
655 self.node
.visit_attrs(f
);
659 impl HasAttrs
for Vec
<Attribute
> {
660 fn attrs(&self) -> &[Attribute
] {
663 fn visit_attrs
<F
: FnOnce(&mut Vec
<Attribute
>)>(&mut self, f
: F
) {
668 impl HasAttrs
for ThinVec
<Attribute
> {
669 fn attrs(&self) -> &[Attribute
] {
672 fn visit_attrs
<F
: FnOnce(&mut Vec
<Attribute
>)>(&mut self, f
: F
) {
673 visit_clobber(self, |this
| {
674 let mut vec
= this
.into();
681 impl<T
: HasAttrs
+ '
static> HasAttrs
for P
<T
> {
682 fn attrs(&self) -> &[Attribute
] {
685 fn visit_attrs
<F
: FnOnce(&mut Vec
<Attribute
>)>(&mut self, f
: F
) {
686 (**self).visit_attrs(f
);
690 impl HasAttrs
for StmtKind
{
691 fn attrs(&self) -> &[Attribute
] {
693 StmtKind
::Local(ref local
) => local
.attrs(),
694 StmtKind
::Item(..) => &[],
695 StmtKind
::Expr(ref expr
) | StmtKind
::Semi(ref expr
) => expr
.attrs(),
696 StmtKind
::Mac(ref mac
) => {
697 let (_
, _
, ref attrs
) = **mac
;
703 fn visit_attrs
<F
: FnOnce(&mut Vec
<Attribute
>)>(&mut self, f
: F
) {
705 StmtKind
::Local(local
) => local
.visit_attrs(f
),
706 StmtKind
::Item(..) => {}
707 StmtKind
::Expr(expr
) => expr
.visit_attrs(f
),
708 StmtKind
::Semi(expr
) => expr
.visit_attrs(f
),
709 StmtKind
::Mac(mac
) => {
710 let (_mac
, _style
, attrs
) = mac
.deref_mut();
711 attrs
.visit_attrs(f
);
717 impl HasAttrs
for Stmt
{
718 fn attrs(&self) -> &[ast
::Attribute
] {
722 fn visit_attrs
<F
: FnOnce(&mut Vec
<ast
::Attribute
>)>(&mut self, f
: F
) {
723 self.kind
.visit_attrs(f
);
727 impl HasAttrs
for GenericParam
{
728 fn attrs(&self) -> &[ast
::Attribute
] {
732 fn visit_attrs
<F
: FnOnce(&mut Vec
<Attribute
>)>(&mut self, f
: F
) {
733 self.attrs
.visit_attrs(f
);
737 macro_rules
! derive_has_attrs
{
738 ($
($ty
:path
),*) => { $
(
739 impl HasAttrs
for $ty
{
740 fn attrs(&self) -> &[Attribute
] {
744 fn visit_attrs
<F
: FnOnce(&mut Vec
<Attribute
>)>(&mut self, f
: F
) {
745 self.attrs
.visit_attrs(f
);
752 Item
, Expr
, Local
, ast
::ForeignItem
, ast
::StructField
, ast
::ImplItem
, ast
::TraitItem
, ast
::Arm
,
753 ast
::Field
, ast
::FieldPat
, ast
::Variant
, ast
::Param