1 //! Functions dealing with attributes and meta items
8 pub use StabilityLevel::*;
9 pub use crate::ast::Attribute;
12 use crate::ast::{AttrId, AttrStyle, Name, Ident, Path, PathSegment};
13 use crate::ast::{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, dummy_spanned};
17 use crate::parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
18 use crate::parse::parser::Parser;
19 use crate::parse::{self, ParseSess, PResult};
20 use crate::parse::token::{self, Token};
22 use crate::symbol::{sym, Symbol};
24 use crate::tokenstream::{TokenStream, TokenTree, DelimSpan};
28 use syntax_pos::{FileName, Span};
31 use std::ops::DerefMut;
33 pub fn mark_used(attr: &Attribute) {
34 debug!("marking {:?} as used", attr);
35 GLOBALS.with(|globals| {
36 globals.used_attrs.lock().insert(attr.id);
40 pub fn is_used(attr: &Attribute) -> bool {
41 GLOBALS.with(|globals| {
42 globals.used_attrs.lock().contains(attr.id)
46 pub fn mark_known(attr: &Attribute) {
47 debug!("marking {:?} as known", attr);
48 GLOBALS.with(|globals| {
49 globals.known_attrs.lock().insert(attr.id);
53 pub fn is_known(attr: &Attribute) -> bool {
54 GLOBALS.with(|globals| {
55 globals.known_attrs.lock().contains(attr.id)
59 pub fn is_known_lint_tool(m_item: Ident) -> bool {
60 [sym::clippy, sym::rustc].contains(&m_item.name)
64 /// Returns the MetaItem if self is a NestedMetaItem::MetaItem.
65 pub fn meta_item(&self) -> Option<&MetaItem> {
67 NestedMetaItem::MetaItem(ref item) => Some(item),
72 /// Returns the Lit if self is a NestedMetaItem::Literal.
73 pub fn literal(&self) -> Option<&Lit> {
75 NestedMetaItem::Literal(ref lit) => Some(lit),
80 /// Returns `true` if this list item is a MetaItem with a name of `name`.
81 pub fn check_name(&self, name: Symbol) -> bool {
82 self.meta_item().map_or(false, |meta_item| meta_item.check_name(name))
85 /// For a single-segment meta-item returns its name, otherwise returns `None`.
86 pub fn ident(&self) -> Option<Ident> {
87 self.meta_item().and_then(|meta_item| meta_item.ident())
89 pub fn name_or_empty(&self) -> Symbol {
90 self.ident().unwrap_or(Ident::invalid()).name
93 /// Gets the string value if self is a MetaItem and the MetaItem is a
94 /// MetaItemKind::NameValue variant containing a string, otherwise None.
95 pub fn value_str(&self) -> Option<Symbol> {
96 self.meta_item().and_then(|meta_item| meta_item.value_str())
99 /// Returns a name and single literal value tuple of the MetaItem.
100 pub fn name_value_literal(&self) -> Option<(Name, &Lit)> {
101 self.meta_item().and_then(
102 |meta_item| meta_item.meta_item_list().and_then(
104 if meta_item_list.len() == 1 {
105 if let Some(ident) = meta_item.ident() {
106 if let Some(lit) = meta_item_list[0].literal() {
107 return Some((ident.name, lit));
115 /// Gets a list of inner meta items from a list MetaItem type.
116 pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
117 self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
120 /// Returns `true` if the variant is MetaItem.
121 pub fn is_meta_item(&self) -> bool {
122 self.meta_item().is_some()
125 /// Returns `true` if the variant is Literal.
126 pub fn is_literal(&self) -> bool {
127 self.literal().is_some()
130 /// Returns `true` if self is a MetaItem and the meta item is a word.
131 pub fn is_word(&self) -> bool {
132 self.meta_item().map_or(false, |meta_item| meta_item.is_word())
135 /// Returns `true` if self is a MetaItem and the meta item is a ValueString.
136 pub fn is_value_str(&self) -> bool {
137 self.value_str().is_some()
140 /// Returns `true` if self is a MetaItem and the meta item is a list.
141 pub fn is_meta_item_list(&self) -> bool {
142 self.meta_item_list().is_some()
147 /// Returns `true` if the attribute's path matches the argument. If it matches, then the
148 /// attribute is marked as used.
150 /// To check the attribute name without marking it used, use the `path` field directly.
151 pub fn check_name(&self, name: Symbol) -> bool {
152 let matches = self.path == name;
159 /// For a single-segment attribute returns its name, otherwise returns `None`.
160 pub fn ident(&self) -> Option<Ident> {
161 if self.path.segments.len() == 1 {
162 Some(self.path.segments[0].ident)
167 pub fn name_or_empty(&self) -> Symbol {
168 self.ident().unwrap_or(Ident::invalid()).name
171 pub fn value_str(&self) -> Option<Symbol> {
172 self.meta().and_then(|meta| meta.value_str())
175 pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
177 Some(MetaItem { node: MetaItemKind::List(list), .. }) => Some(list),
182 pub fn is_word(&self) -> bool {
183 self.tokens.is_empty()
186 pub fn is_meta_item_list(&self) -> bool {
187 self.meta_item_list().is_some()
190 /// Indicates if the attribute is a Value String.
191 pub fn is_value_str(&self) -> bool {
192 self.value_str().is_some()
197 /// For a single-segment meta-item returns its name, otherwise returns `None`.
198 pub fn ident(&self) -> Option<Ident> {
199 if self.path.segments.len() == 1 {
200 Some(self.path.segments[0].ident)
205 pub fn name_or_empty(&self) -> Symbol {
206 self.ident().unwrap_or(Ident::invalid()).name
209 // #[attribute(name = "value")]
211 pub fn name_value_literal(&self) -> Option<&Lit> {
213 MetaItemKind::NameValue(v) => Some(v),
218 pub fn value_str(&self) -> Option<Symbol> {
220 MetaItemKind::NameValue(ref v) => {
222 LitKind::Str(ref s, _) => Some(*s),
230 pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
232 MetaItemKind::List(ref l) => Some(&l[..]),
237 pub fn is_word(&self) -> bool {
239 MetaItemKind::Word => true,
244 pub fn check_name(&self, name: Symbol) -> bool {
248 pub fn is_value_str(&self) -> bool {
249 self.value_str().is_some()
252 pub fn is_meta_item_list(&self) -> bool {
253 self.meta_item_list().is_some()
258 /// Extracts the MetaItem from inside this Attribute.
259 pub fn meta(&self) -> Option<MetaItem> {
260 let mut tokens = self.tokens.trees().peekable();
262 path: self.path.clone(),
263 node: if let Some(node) = MetaItemKind::from_tokens(&mut tokens) {
264 if tokens.peek().is_some() {
275 pub fn parse<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, T>
276 where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
278 let mut parser = Parser::new(
286 let result = f(&mut parser)?;
287 if parser.token != token::Eof {
288 parser.unexpected()?;
293 pub fn parse_list<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, Vec<T>>
294 where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
296 if self.tokens.is_empty() {
297 return Ok(Vec::new());
299 self.parse(sess, |parser| {
300 parser.expect(&token::OpenDelim(token::Paren))?;
301 let mut list = Vec::new();
302 while !parser.eat(&token::CloseDelim(token::Paren)) {
303 list.push(f(parser)?);
304 if !parser.eat(&token::Comma) {
305 parser.expect(&token::CloseDelim(token::Paren))?;
313 pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> {
315 path: self.path.clone(),
316 node: self.parse(sess, |parser| parser.parse_meta_item_kind())?,
321 /// Converts self to a normal #[doc="foo"] comment, if it is a
322 /// comment like `///` or `/** */`. (Returns self unchanged for
323 /// non-sugared doc attributes.)
324 pub fn with_desugared_doc<T, F>(&self, f: F) -> T where
325 F: FnOnce(&Attribute) -> T,
327 if self.is_sugared_doc {
328 let comment = self.value_str().unwrap();
329 let meta = mk_name_value_item_str(
330 Ident::with_empty_ctxt(sym::doc),
331 dummy_spanned(Symbol::intern(&strip_doc_comment_decoration(&comment.as_str()))));
336 tokens: meta.node.tokens(meta.span),
337 is_sugared_doc: true,
348 pub fn mk_name_value_item_str(ident: Ident, value: Spanned<Symbol>) -> MetaItem {
349 let lit_kind = LitKind::Str(value.node, ast::StrStyle::Cooked);
350 mk_name_value_item(ident.span.to(value.span), ident, lit_kind, value.span)
353 pub fn mk_name_value_item(span: Span, ident: Ident, lit_kind: LitKind, lit_span: Span) -> MetaItem {
354 let lit = Lit::from_lit_kind(lit_kind, lit_span);
355 MetaItem { path: Path::from_ident(ident), span, node: MetaItemKind::NameValue(lit) }
358 pub fn mk_list_item(span: Span, ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem {
359 MetaItem { path: Path::from_ident(ident), span, node: MetaItemKind::List(items) }
362 pub fn mk_word_item(ident: Ident) -> MetaItem {
363 MetaItem { path: Path::from_ident(ident), span: ident.span, node: MetaItemKind::Word }
366 pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
367 NestedMetaItem::MetaItem(mk_word_item(ident))
370 pub fn mk_attr_id() -> AttrId {
371 use std::sync::atomic::AtomicUsize;
372 use std::sync::atomic::Ordering;
374 static NEXT_ATTR_ID: AtomicUsize = AtomicUsize::new(0);
376 let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst);
377 assert!(id != ::std::usize::MAX);
381 /// Returns an inner attribute with the given value and span.
382 pub fn mk_attr_inner(span: Span, item: MetaItem) -> Attribute {
385 style: ast::AttrStyle::Inner,
387 tokens: item.node.tokens(item.span),
388 is_sugared_doc: false,
393 /// Returns an outer attribute with the given value and span.
394 pub fn mk_attr_outer(span: Span, item: MetaItem) -> Attribute {
397 style: ast::AttrStyle::Outer,
399 tokens: item.node.tokens(item.span),
400 is_sugared_doc: false,
405 pub fn mk_sugared_doc_attr(text: Symbol, span: Span) -> Attribute {
406 let style = doc_comment_style(&text.as_str());
407 let lit_kind = LitKind::Str(text, ast::StrStyle::Cooked);
408 let lit = Lit::from_lit_kind(lit_kind, span);
412 path: Path::from_ident(Ident::with_empty_ctxt(sym::doc).with_span_pos(span)),
413 tokens: MetaItemKind::NameValue(lit).tokens(span),
414 is_sugared_doc: true,
419 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
420 items.iter().any(|item| {
421 item.check_name(name)
425 pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {
426 attrs.iter().any(|item| {
427 item.check_name(name)
431 pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {
432 attrs.iter().find(|attr| attr.check_name(name))
435 pub fn filter_by_name(attrs: &[Attribute], name: Symbol)
436 -> impl Iterator<Item=&Attribute> {
437 attrs.iter().filter(move |attr| attr.check_name(name))
440 pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> {
442 .find(|at| at.check_name(name))
443 .and_then(|at| at.value_str())
447 fn tokens(&self) -> TokenStream {
448 let mut idents = vec![];
449 let mut last_pos = BytePos(0 as u32);
450 for (i, segment) in self.path.segments.iter().enumerate() {
451 let is_first = i == 0;
453 let mod_sep_span = Span::new(last_pos,
454 segment.ident.span.lo(),
455 segment.ident.span.ctxt());
456 idents.push(TokenTree::token(token::ModSep, mod_sep_span).into());
458 idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into());
459 last_pos = segment.ident.span.hi();
461 self.node.tokens(self.span).append_to_tree_and_joint_vec(&mut idents);
462 TokenStream::new(idents)
465 fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
466 where I: Iterator<Item = TokenTree>,
468 // FIXME: Share code with `parse_path`.
469 let path = match tokens.next() {
470 Some(TokenTree::Token(Token { kind: kind @ token::Ident(..), span })) |
471 Some(TokenTree::Token(Token { kind: kind @ token::ModSep, span })) => 'arm: {
472 let mut segments = if let token::Ident(name, _) = kind {
473 if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }))
476 vec![PathSegment::from_ident(Ident::new(name, span))]
478 break 'arm Path::from_ident(Ident::new(name, span));
481 vec![PathSegment::path_root(span)]
484 if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }))
486 segments.push(PathSegment::from_ident(Ident::new(name, span)));
490 if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }))
497 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
498 Path { span, segments }
500 Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt {
501 token::Nonterminal::NtIdent(ident, _) => Path::from_ident(ident),
502 token::Nonterminal::NtMeta(ref meta) => return Some(meta.clone()),
503 token::Nonterminal::NtPath(ref path) => path.clone(),
508 let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
509 let node = MetaItemKind::from_tokens(tokens)?;
510 let hi = match node {
511 MetaItemKind::NameValue(ref 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, node, span })
521 pub fn tokens(&self, span: Span) -> TokenStream {
523 MetaItemKind::Word => TokenStream::empty(),
524 MetaItemKind::NameValue(ref lit) => {
525 let mut vec = vec![TokenTree::token(token::Eq, span).into()];
526 lit.tokens().append_to_tree_and_joint_vec(&mut vec);
527 TokenStream::new(vec)
529 MetaItemKind::List(ref list) => {
530 let mut tokens = Vec::new();
531 for (i, item) in list.iter().enumerate() {
533 tokens.push(TokenTree::token(token::Comma, span).into());
535 item.tokens().append_to_tree_and_joint_vec(&mut tokens);
537 TokenTree::Delimited(
538 DelimSpan::from_single(span),
540 TokenStream::new(tokens).into(),
546 fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemKind>
547 where I: Iterator<Item = TokenTree>,
549 let delimited = match tokens.peek().cloned() {
550 Some(TokenTree::Token(token)) if token == token::Eq => {
552 return if let Some(TokenTree::Token(token)) = tokens.next() {
553 Lit::from_token(&token).ok().map(MetaItemKind::NameValue)
558 Some(TokenTree::Delimited(_, delim, ref tts)) if delim == token::Paren => {
562 _ => return Some(MetaItemKind::Word),
565 let mut tokens = delimited.into_trees().peekable();
566 let mut result = Vec::new();
567 while let Some(..) = tokens.peek() {
568 let item = NestedMetaItem::from_tokens(&mut tokens)?;
570 match tokens.next() {
571 None | Some(TokenTree::Token(Token { kind: token::Comma, .. })) => {}
575 Some(MetaItemKind::List(result))
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 tokens(&self) -> TokenStream {
589 NestedMetaItem::MetaItem(ref item) => item.tokens(),
590 NestedMetaItem::Literal(ref lit) => lit.tokens(),
594 fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
595 where I: Iterator<Item = TokenTree>,
597 if let Some(TokenTree::Token(token)) = tokens.peek() {
598 if let Ok(lit) = Lit::from_token(token) {
600 return Some(NestedMetaItem::Literal(lit));
604 MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
608 pub trait HasAttrs: Sized {
609 fn attrs(&self) -> &[ast::Attribute];
610 fn visit_attrs<F: FnOnce(&mut Vec<ast::Attribute>)>(&mut self, f: F);
613 impl<T: HasAttrs> HasAttrs for Spanned<T> {
614 fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() }
615 fn visit_attrs<F: FnOnce(&mut Vec<ast::Attribute>)>(&mut self, f: F) {
616 self.node.visit_attrs(f);
620 impl HasAttrs for Vec<Attribute> {
621 fn attrs(&self) -> &[Attribute] {
624 fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
629 impl HasAttrs for ThinVec<Attribute> {
630 fn attrs(&self) -> &[Attribute] {
633 fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
634 visit_clobber(self, |this| {
635 let mut vec = this.into();
642 impl<T: HasAttrs + 'static> HasAttrs for P<T> {
643 fn attrs(&self) -> &[Attribute] {
646 fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
647 (**self).visit_attrs(f);
651 impl HasAttrs for StmtKind {
652 fn attrs(&self) -> &[Attribute] {
654 StmtKind::Local(ref local) => local.attrs(),
655 StmtKind::Item(..) => &[],
656 StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
657 StmtKind::Mac(ref mac) => {
658 let (_, _, ref attrs) = **mac;
664 fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
666 StmtKind::Local(local) => local.visit_attrs(f),
667 StmtKind::Item(..) => {}
668 StmtKind::Expr(expr) => expr.visit_attrs(f),
669 StmtKind::Semi(expr) => expr.visit_attrs(f),
670 StmtKind::Mac(mac) => {
671 let (_mac, _style, attrs) = mac.deref_mut();
672 attrs.visit_attrs(f);
678 impl HasAttrs for Stmt {
679 fn attrs(&self) -> &[ast::Attribute] {
683 fn visit_attrs<F: FnOnce(&mut Vec<ast::Attribute>)>(&mut self, f: F) {
684 self.node.visit_attrs(f);
688 impl HasAttrs for GenericParam {
689 fn attrs(&self) -> &[ast::Attribute] {
693 fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
694 self.attrs.visit_attrs(f);
698 macro_rules! derive_has_attrs {
699 ($($ty:path),*) => { $(
700 impl HasAttrs for $ty {
701 fn attrs(&self) -> &[Attribute] {
705 fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
706 self.attrs.visit_attrs(f);
713 Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm,
714 ast::Field, ast::FieldPat, ast::Variant_, ast::Arg
717 pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -> ast::Crate {
718 for raw_attr in attrs {
719 let mut parser = parse::new_parser_from_source_str(
721 FileName::cli_crate_attr_source_code(&raw_attr),
725 let start_span = parser.token.span;
726 let (path, tokens) = panictry!(parser.parse_meta_item_unrestricted());
727 let end_span = parser.token.span;
728 if parser.token != token::Eof {
729 parse_sess.span_diagnostic
730 .span_err(start_span.to(end_span), "invalid crate attribute");
734 krate.attrs.push(Attribute {
736 style: AttrStyle::Inner,
739 is_sugared_doc: false,
740 span: start_span.to(end_span),