]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/attr/mod.rs
bump smallvec to 1.0
[rust.git] / src / libsyntax / attr / mod.rs
1 //! Functions dealing with attributes and meta items.
2
3 mod builtin;
4
5 pub use builtin::*;
6 pub use IntType::*;
7 pub use ReprAttr::*;
8 pub use StabilityLevel::*;
9 pub use crate::ast::Attribute;
10
11 use crate::ast;
12 use crate::ast::{AttrItem, 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};
17 use crate::parse::lexer::comments::doc_comment_style;
18 use crate::parse;
19 use crate::parse::PResult;
20 use crate::parse::token::{self, Token};
21 use crate::ptr::P;
22 use crate::sess::ParseSess;
23 use crate::symbol::{sym, Symbol};
24 use crate::ThinVec;
25 use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
26 use crate::GLOBALS;
27
28 use log::debug;
29 use syntax_pos::Span;
30
31 use std::iter;
32 use std::ops::DerefMut;
33
34 pub fn mark_used(attr: &Attribute) {
35     debug!("marking {:?} as used", attr);
36     GLOBALS.with(|globals| {
37         globals.used_attrs.lock().insert(attr.id);
38     });
39 }
40
41 pub fn is_used(attr: &Attribute) -> bool {
42     GLOBALS.with(|globals| {
43         globals.used_attrs.lock().contains(attr.id)
44     })
45 }
46
47 pub fn mark_known(attr: &Attribute) {
48     debug!("marking {:?} as known", attr);
49     GLOBALS.with(|globals| {
50         globals.known_attrs.lock().insert(attr.id);
51     });
52 }
53
54 pub fn is_known(attr: &Attribute) -> bool {
55     GLOBALS.with(|globals| {
56         globals.known_attrs.lock().contains(attr.id)
57     })
58 }
59
60 pub fn is_known_lint_tool(m_item: Ident) -> bool {
61     [sym::clippy, sym::rustc].contains(&m_item.name)
62 }
63
64 impl NestedMetaItem {
65     /// Returns the `MetaItem` if `self` is a `NestedMetaItem::MetaItem`.
66     pub fn meta_item(&self) -> Option<&MetaItem> {
67         match *self {
68             NestedMetaItem::MetaItem(ref item) => Some(item),
69             _ => None
70         }
71     }
72
73     /// Returns the `Lit` if `self` is a `NestedMetaItem::Literal`s.
74     pub fn literal(&self) -> Option<&Lit> {
75         match *self {
76             NestedMetaItem::Literal(ref lit) => Some(lit),
77             _ => None
78         }
79     }
80
81     /// Returns `true` if this list item is a MetaItem with a name of `name`.
82     pub fn check_name(&self, name: Symbol) -> bool {
83         self.meta_item().map_or(false, |meta_item| meta_item.check_name(name))
84     }
85
86     /// For a single-segment meta item, returns its name; otherwise, returns `None`.
87     pub fn ident(&self) -> Option<Ident> {
88         self.meta_item().and_then(|meta_item| meta_item.ident())
89     }
90     pub fn name_or_empty(&self) -> Symbol {
91         self.ident().unwrap_or(Ident::invalid()).name
92     }
93
94     /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
95     /// `MetaItemKind::NameValue` variant containing a string, otherwise `None`.
96     pub fn value_str(&self) -> Option<Symbol> {
97         self.meta_item().and_then(|meta_item| meta_item.value_str())
98     }
99
100     /// Returns a name and single literal value tuple of the `MetaItem`.
101     pub fn name_value_literal(&self) -> Option<(Name, &Lit)> {
102         self.meta_item().and_then(
103             |meta_item| meta_item.meta_item_list().and_then(
104                 |meta_item_list| {
105                     if meta_item_list.len() == 1 {
106                         if let Some(ident) = meta_item.ident() {
107                             if let Some(lit) = meta_item_list[0].literal() {
108                                 return Some((ident.name, lit));
109                             }
110                         }
111                     }
112                     None
113                 }))
114     }
115
116     /// Gets a list of inner meta items from a list `MetaItem` type.
117     pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
118         self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
119     }
120
121     /// Returns `true` if the variant is `MetaItem`.
122     pub fn is_meta_item(&self) -> bool {
123         self.meta_item().is_some()
124     }
125
126     /// Returns `true` if the variant is `Literal`.
127     pub fn is_literal(&self) -> bool {
128         self.literal().is_some()
129     }
130
131     /// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
132     pub fn is_word(&self) -> bool {
133         self.meta_item().map_or(false, |meta_item| meta_item.is_word())
134     }
135
136     /// Returns `true` if `self` is a `MetaItem` and the meta item is a `ValueString`.
137     pub fn is_value_str(&self) -> bool {
138         self.value_str().is_some()
139     }
140
141     /// Returns `true` if `self` is a `MetaItem` and the meta item is a list.
142     pub fn is_meta_item_list(&self) -> bool {
143         self.meta_item_list().is_some()
144     }
145 }
146
147 impl Attribute {
148     /// Returns `true` if the attribute's path matches the argument. If it matches, then the
149     /// attribute is marked as used.
150     ///
151     /// To check the attribute name without marking it used, use the `path` field directly.
152     pub fn check_name(&self, name: Symbol) -> bool {
153         let matches = self.path == name;
154         if matches {
155             mark_used(self);
156         }
157         matches
158     }
159
160     /// For a single-segment attribute, returns its name; otherwise, returns `None`.
161     pub fn ident(&self) -> Option<Ident> {
162         if self.path.segments.len() == 1 {
163             Some(self.path.segments[0].ident)
164         } else {
165             None
166         }
167     }
168     pub fn name_or_empty(&self) -> Symbol {
169         self.ident().unwrap_or(Ident::invalid()).name
170     }
171
172     pub fn value_str(&self) -> Option<Symbol> {
173         self.meta().and_then(|meta| meta.value_str())
174     }
175
176     pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
177         match self.meta() {
178             Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list),
179             _ => None
180         }
181     }
182
183     pub fn is_word(&self) -> bool {
184         self.tokens.is_empty()
185     }
186
187     pub fn is_meta_item_list(&self) -> bool {
188         self.meta_item_list().is_some()
189     }
190
191     /// Indicates if the attribute is a `ValueString`.
192     pub fn is_value_str(&self) -> bool {
193         self.value_str().is_some()
194     }
195 }
196
197 impl MetaItem {
198     /// For a single-segment meta item, returns its name; otherwise, returns `None`.
199     pub fn ident(&self) -> Option<Ident> {
200         if self.path.segments.len() == 1 {
201             Some(self.path.segments[0].ident)
202         } else {
203             None
204         }
205     }
206     pub fn name_or_empty(&self) -> Symbol {
207         self.ident().unwrap_or(Ident::invalid()).name
208     }
209
210     // Example:
211     //     #[attribute(name = "value")]
212     //                 ^^^^^^^^^^^^^^
213     pub fn name_value_literal(&self) -> Option<&Lit> {
214         match &self.kind {
215             MetaItemKind::NameValue(v) => Some(v),
216             _ => None,
217         }
218     }
219
220     pub fn value_str(&self) -> Option<Symbol> {
221         match self.kind {
222             MetaItemKind::NameValue(ref v) => {
223                 match v.kind {
224                     LitKind::Str(ref s, _) => Some(*s),
225                     _ => None,
226                 }
227             },
228             _ => None
229         }
230     }
231
232     pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
233         match self.kind {
234             MetaItemKind::List(ref l) => Some(&l[..]),
235             _ => None
236         }
237     }
238
239     pub fn is_word(&self) -> bool {
240         match self.kind {
241             MetaItemKind::Word => true,
242             _ => false,
243         }
244     }
245
246     pub fn check_name(&self, name: Symbol) -> bool {
247         self.path == name
248     }
249
250     pub fn is_value_str(&self) -> bool {
251         self.value_str().is_some()
252     }
253
254     pub fn is_meta_item_list(&self) -> bool {
255         self.meta_item_list().is_some()
256     }
257 }
258
259 impl AttrItem {
260     crate fn meta(&self, span: Span) -> Option<MetaItem> {
261         let mut tokens = self.tokens.trees().peekable();
262         Some(MetaItem {
263             path: self.path.clone(),
264             kind: if let Some(kind) = MetaItemKind::from_tokens(&mut tokens) {
265                 if tokens.peek().is_some() {
266                     return None;
267                 }
268                 kind
269             } else {
270                 return None;
271             },
272             span,
273         })
274     }
275 }
276
277 impl Attribute {
278     /// Extracts the MetaItem from inside this Attribute.
279     pub fn meta(&self) -> Option<MetaItem> {
280         self.item.meta(self.span)
281     }
282
283     pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> {
284         Ok(MetaItem {
285             path: self.path.clone(),
286             kind: parse::parse_in_attr(sess, self, |p| p.parse_meta_item_kind())?,
287             span: self.span,
288         })
289     }
290 }
291
292 /* Constructors */
293
294 pub fn mk_name_value_item_str(ident: Ident, str: Symbol, str_span: Span) -> MetaItem {
295     let lit_kind = LitKind::Str(str, ast::StrStyle::Cooked);
296     mk_name_value_item(ident, lit_kind, str_span)
297 }
298
299 pub fn mk_name_value_item(ident: Ident, lit_kind: LitKind, lit_span: Span) -> MetaItem {
300     let lit = Lit::from_lit_kind(lit_kind, lit_span);
301     let span = ident.span.to(lit_span);
302     MetaItem { path: Path::from_ident(ident), span, kind: MetaItemKind::NameValue(lit) }
303 }
304
305 pub fn mk_list_item(ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem {
306     MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::List(items) }
307 }
308
309 pub fn mk_word_item(ident: Ident) -> MetaItem {
310     MetaItem { path: Path::from_ident(ident), span: ident.span, kind: MetaItemKind::Word }
311 }
312
313 pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
314     NestedMetaItem::MetaItem(mk_word_item(ident))
315 }
316
317 crate fn mk_attr_id() -> AttrId {
318     use std::sync::atomic::AtomicUsize;
319     use std::sync::atomic::Ordering;
320
321     static NEXT_ATTR_ID: AtomicUsize = AtomicUsize::new(0);
322
323     let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst);
324     assert!(id != ::std::usize::MAX);
325     AttrId(id)
326 }
327
328 pub fn mk_attr(style: AttrStyle, path: Path, tokens: TokenStream, span: Span) -> Attribute {
329     Attribute {
330         item: AttrItem { path, tokens },
331         id: mk_attr_id(),
332         style,
333         is_sugared_doc: false,
334         span,
335     }
336 }
337
338 /// Returns an inner attribute with the given value and span.
339 pub fn mk_attr_inner(item: MetaItem) -> Attribute {
340     mk_attr(AttrStyle::Inner, item.path, item.kind.tokens(item.span), item.span)
341 }
342
343 /// Returns an outer attribute with the given value and span.
344 pub fn mk_attr_outer(item: MetaItem) -> Attribute {
345     mk_attr(AttrStyle::Outer, item.path, item.kind.tokens(item.span), item.span)
346 }
347
348 pub fn mk_sugared_doc_attr(text: Symbol, span: Span) -> Attribute {
349     let style = doc_comment_style(&text.as_str());
350     let lit_kind = LitKind::Str(text, ast::StrStyle::Cooked);
351     let lit = Lit::from_lit_kind(lit_kind, span);
352     Attribute {
353         item: AttrItem {
354             path: Path::from_ident(Ident::with_dummy_span(sym::doc).with_span_pos(span)),
355             tokens: MetaItemKind::NameValue(lit).tokens(span),
356         },
357         id: mk_attr_id(),
358         style,
359         is_sugared_doc: true,
360         span,
361     }
362 }
363
364 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
365     items.iter().any(|item| {
366         item.check_name(name)
367     })
368 }
369
370 pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {
371     attrs.iter().any(|item| {
372         item.check_name(name)
373     })
374 }
375
376 pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {
377     attrs.iter().find(|attr| attr.check_name(name))
378 }
379
380 pub fn allow_internal_unstable<'a>(
381     attrs: &[Attribute],
382     span_diagnostic: &'a errors::Handler,
383 ) -> Option<impl Iterator<Item = Symbol> + 'a> {
384     find_by_name(attrs, sym::allow_internal_unstable).and_then(|attr| {
385         attr.meta_item_list().or_else(|| {
386             span_diagnostic.span_err(
387                 attr.span,
388                 "allow_internal_unstable expects list of feature names"
389             );
390             None
391         }).map(|features| features.into_iter().filter_map(move |it| {
392             let name = it.ident().map(|ident| ident.name);
393             if name.is_none() {
394                 span_diagnostic.span_err(
395                     it.span(),
396                     "`allow_internal_unstable` expects feature names",
397                 )
398             }
399             name
400         }))
401     })
402 }
403
404 pub fn filter_by_name(attrs: &[Attribute], name: Symbol)
405                       -> impl Iterator<Item=&Attribute> {
406     attrs.iter().filter(move |attr| attr.check_name(name))
407 }
408
409 pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> {
410     attrs.iter()
411         .find(|at| at.check_name(name))
412         .and_then(|at| at.value_str())
413 }
414
415 impl MetaItem {
416     fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> {
417         let mut idents = vec![];
418         let mut last_pos = BytePos(0 as u32);
419         for (i, segment) in self.path.segments.iter().enumerate() {
420             let is_first = i == 0;
421             if !is_first {
422                 let mod_sep_span = Span::new(last_pos,
423                                              segment.ident.span.lo(),
424                                              segment.ident.span.ctxt());
425                 idents.push(TokenTree::token(token::ModSep, mod_sep_span).into());
426             }
427             idents.push(TokenTree::Token(Token::from_ast_ident(segment.ident)).into());
428             last_pos = segment.ident.span.hi();
429         }
430         idents.extend(self.kind.token_trees_and_joints(self.span));
431         idents
432     }
433
434     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
435         where I: Iterator<Item = TokenTree>,
436     {
437         // FIXME: Share code with `parse_path`.
438         let path = match tokens.next() {
439             Some(TokenTree::Token(Token { kind: kind @ token::Ident(..), span })) |
440             Some(TokenTree::Token(Token { kind: kind @ token::ModSep, span })) => 'arm: {
441                 let mut segments = if let token::Ident(name, _) = kind {
442                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }))
443                             = tokens.peek() {
444                         tokens.next();
445                         vec![PathSegment::from_ident(Ident::new(name, span))]
446                     } else {
447                         break 'arm Path::from_ident(Ident::new(name, span));
448                     }
449                 } else {
450                     vec![PathSegment::path_root(span)]
451                 };
452                 loop {
453                     if let Some(TokenTree::Token(Token { kind: token::Ident(name, _), span }))
454                             = tokens.next() {
455                         segments.push(PathSegment::from_ident(Ident::new(name, span)));
456                     } else {
457                         return None;
458                     }
459                     if let Some(TokenTree::Token(Token { kind: token::ModSep, .. }))
460                             = tokens.peek() {
461                         tokens.next();
462                     } else {
463                         break;
464                     }
465                 }
466                 let span = span.with_hi(segments.last().unwrap().ident.span.hi());
467                 Path { span, segments }
468             }
469             Some(TokenTree::Token(Token { kind: token::Interpolated(nt), .. })) => match *nt {
470                 token::Nonterminal::NtIdent(ident, _) => Path::from_ident(ident),
471                 token::Nonterminal::NtMeta(ref item) => return item.meta(item.path.span),
472                 token::Nonterminal::NtPath(ref path) => path.clone(),
473                 _ => return None,
474             },
475             _ => return None,
476         };
477         let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
478         let kind = MetaItemKind::from_tokens(tokens)?;
479         let hi = match kind {
480             MetaItemKind::NameValue(ref lit) => lit.span.hi(),
481             MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(path.span.hi()),
482             _ => path.span.hi(),
483         };
484         let span = path.span.with_hi(hi);
485         Some(MetaItem { path, kind, span })
486     }
487 }
488
489 impl MetaItemKind {
490     pub fn token_trees_and_joints(&self, span: Span) -> Vec<TreeAndJoint> {
491         match *self {
492             MetaItemKind::Word => vec![],
493             MetaItemKind::NameValue(ref lit) => {
494                 vec![
495                     TokenTree::token(token::Eq, span).into(),
496                     lit.token_tree().into(),
497                 ]
498             }
499             MetaItemKind::List(ref list) => {
500                 let mut tokens = Vec::new();
501                 for (i, item) in list.iter().enumerate() {
502                     if i > 0 {
503                         tokens.push(TokenTree::token(token::Comma, span).into());
504                     }
505                     tokens.extend(item.token_trees_and_joints())
506                 }
507                 vec![
508                     TokenTree::Delimited(
509                         DelimSpan::from_single(span),
510                         token::Paren,
511                         TokenStream::new(tokens).into(),
512                     ).into()
513                 ]
514             }
515         }
516     }
517
518     // Premature conversions of `TokenTree`s to `TokenStream`s can hurt
519     // performance. Do not use this function if `token_trees_and_joints()` can
520     // be used instead.
521     pub fn tokens(&self, span: Span) -> TokenStream {
522         TokenStream::new(self.token_trees_and_joints(span))
523     }
524
525     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemKind>
526         where I: Iterator<Item = TokenTree>,
527     {
528         let delimited = match tokens.peek().cloned() {
529             Some(TokenTree::Token(token)) if token == token::Eq => {
530                 tokens.next();
531                 return if let Some(TokenTree::Token(token)) = tokens.next() {
532                     Lit::from_token(&token).ok().map(MetaItemKind::NameValue)
533                 } else {
534                     None
535                 };
536             }
537             Some(TokenTree::Delimited(_, delim, ref tts)) if delim == token::Paren => {
538                 tokens.next();
539                 tts.clone()
540             }
541             _ => return Some(MetaItemKind::Word),
542         };
543
544         let mut tokens = delimited.into_trees().peekable();
545         let mut result = Vec::new();
546         while let Some(..) = tokens.peek() {
547             let item = NestedMetaItem::from_tokens(&mut tokens)?;
548             result.push(item);
549             match tokens.next() {
550                 None | Some(TokenTree::Token(Token { kind: token::Comma, .. })) => {}
551                 _ => return None,
552             }
553         }
554         Some(MetaItemKind::List(result))
555     }
556 }
557
558 impl NestedMetaItem {
559     pub fn span(&self) -> Span {
560         match *self {
561             NestedMetaItem::MetaItem(ref item) => item.span,
562             NestedMetaItem::Literal(ref lit) => lit.span,
563         }
564     }
565
566     fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> {
567         match *self {
568             NestedMetaItem::MetaItem(ref item) => item.token_trees_and_joints(),
569             NestedMetaItem::Literal(ref lit) => vec![lit.token_tree().into()],
570         }
571     }
572
573     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItem>
574         where I: Iterator<Item = TokenTree>,
575     {
576         if let Some(TokenTree::Token(token)) = tokens.peek() {
577             if let Ok(lit) = Lit::from_token(token) {
578                 tokens.next();
579                 return Some(NestedMetaItem::Literal(lit));
580             }
581         }
582
583         MetaItem::from_tokens(tokens).map(NestedMetaItem::MetaItem)
584     }
585 }
586
587 pub trait HasAttrs: Sized {
588     fn attrs(&self) -> &[ast::Attribute];
589     fn visit_attrs<F: FnOnce(&mut Vec<ast::Attribute>)>(&mut self, f: F);
590 }
591
592 impl<T: HasAttrs> HasAttrs for Spanned<T> {
593     fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() }
594     fn visit_attrs<F: FnOnce(&mut Vec<ast::Attribute>)>(&mut self, f: F) {
595         self.node.visit_attrs(f);
596     }
597 }
598
599 impl HasAttrs for Vec<Attribute> {
600     fn attrs(&self) -> &[Attribute] {
601         self
602     }
603     fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
604         f(self)
605     }
606 }
607
608 impl HasAttrs for ThinVec<Attribute> {
609     fn attrs(&self) -> &[Attribute] {
610         self
611     }
612     fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
613         visit_clobber(self, |this| {
614             let mut vec = this.into();
615             f(&mut vec);
616             vec.into()
617         });
618     }
619 }
620
621 impl<T: HasAttrs + 'static> HasAttrs for P<T> {
622     fn attrs(&self) -> &[Attribute] {
623         (**self).attrs()
624     }
625     fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
626         (**self).visit_attrs(f);
627     }
628 }
629
630 impl HasAttrs for StmtKind {
631     fn attrs(&self) -> &[Attribute] {
632         match *self {
633             StmtKind::Local(ref local) => local.attrs(),
634             StmtKind::Item(..) => &[],
635             StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
636             StmtKind::Mac(ref mac) => {
637                 let (_, _, ref attrs) = **mac;
638                 attrs.attrs()
639             }
640         }
641     }
642
643     fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
644         match self {
645             StmtKind::Local(local) => local.visit_attrs(f),
646             StmtKind::Item(..) => {}
647             StmtKind::Expr(expr) => expr.visit_attrs(f),
648             StmtKind::Semi(expr) => expr.visit_attrs(f),
649             StmtKind::Mac(mac) => {
650                 let (_mac, _style, attrs) = mac.deref_mut();
651                 attrs.visit_attrs(f);
652             }
653         }
654     }
655 }
656
657 impl HasAttrs for Stmt {
658     fn attrs(&self) -> &[ast::Attribute] {
659         self.kind.attrs()
660     }
661
662     fn visit_attrs<F: FnOnce(&mut Vec<ast::Attribute>)>(&mut self, f: F) {
663         self.kind.visit_attrs(f);
664     }
665 }
666
667 impl HasAttrs for GenericParam {
668     fn attrs(&self) -> &[ast::Attribute] {
669         &self.attrs
670     }
671
672     fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
673         self.attrs.visit_attrs(f);
674     }
675 }
676
677 macro_rules! derive_has_attrs {
678     ($($ty:path),*) => { $(
679         impl HasAttrs for $ty {
680             fn attrs(&self) -> &[Attribute] {
681                 &self.attrs
682             }
683
684             fn visit_attrs<F: FnOnce(&mut Vec<Attribute>)>(&mut self, f: F) {
685                 self.attrs.visit_attrs(f);
686             }
687         }
688     )* }
689 }
690
691 derive_has_attrs! {
692     Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm,
693     ast::Field, ast::FieldPat, ast::Variant, ast::Param
694 }