]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/attr.rs
Use `Ident` instead of `Name` in `MetaItem`
[rust.git] / src / libsyntax / attr.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 // Functions dealing with attributes and meta items
12
13 pub use self::StabilityLevel::*;
14 pub use self::ReprAttr::*;
15 pub use self::IntType::*;
16
17 use ast;
18 use ast::{AttrId, Attribute, Name, Ident};
19 use ast::{MetaItem, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
20 use ast::{Lit, LitKind, Expr, ExprKind, Item, Local, Stmt, StmtKind};
21 use codemap::{Spanned, respan, dummy_spanned};
22 use syntax_pos::Span;
23 use errors::Handler;
24 use feature_gate::{Features, GatedCfg};
25 use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
26 use parse::parser::Parser;
27 use parse::{self, ParseSess, PResult};
28 use parse::token::{self, Token};
29 use ptr::P;
30 use symbol::Symbol;
31 use tokenstream::{TokenStream, TokenTree, Delimited};
32 use util::ThinVec;
33 use GLOBALS;
34
35 use std::iter;
36
37 enum AttrError {
38     MultipleItem(Name),
39     UnknownMetaItem(Name),
40     MissingSince,
41     MissingFeature,
42     MultipleStabilityLevels,
43     UnsupportedLiteral
44 }
45
46 fn handle_errors(diag: &Handler, span: Span, error: AttrError) {
47     match error {
48         AttrError::MultipleItem(item) => span_err!(diag, span, E0538,
49                                                    "multiple '{}' items", item),
50         AttrError::UnknownMetaItem(item) => span_err!(diag, span, E0541,
51                                                       "unknown meta item '{}'", item),
52         AttrError::MissingSince => span_err!(diag, span, E0542, "missing 'since'"),
53         AttrError::MissingFeature => span_err!(diag, span, E0546, "missing 'feature'"),
54         AttrError::MultipleStabilityLevels => span_err!(diag, span, E0544,
55                                                         "multiple stability levels"),
56         AttrError::UnsupportedLiteral => span_err!(diag, span, E0565, "unsupported literal"),
57     }
58 }
59
60 pub fn mark_used(attr: &Attribute) {
61     debug!("Marking {:?} as used.", attr);
62     let AttrId(id) = attr.id;
63     GLOBALS.with(|globals| {
64         let mut slot = globals.used_attrs.lock();
65         let idx = (id / 64) as usize;
66         let shift = id % 64;
67         if slot.len() <= idx {
68             slot.resize(idx + 1, 0);
69         }
70         slot[idx] |= 1 << shift;
71     });
72 }
73
74 pub fn is_used(attr: &Attribute) -> bool {
75     let AttrId(id) = attr.id;
76     GLOBALS.with(|globals| {
77         let slot = globals.used_attrs.lock();
78         let idx = (id / 64) as usize;
79         let shift = id % 64;
80         slot.get(idx).map(|bits| bits & (1 << shift) != 0)
81             .unwrap_or(false)
82     })
83 }
84
85 pub fn mark_known(attr: &Attribute) {
86     debug!("Marking {:?} as known.", attr);
87     let AttrId(id) = attr.id;
88     GLOBALS.with(|globals| {
89         let mut slot = globals.known_attrs.lock();
90         let idx = (id / 64) as usize;
91         let shift = id % 64;
92         if slot.len() <= idx {
93             slot.resize(idx + 1, 0);
94         }
95         slot[idx] |= 1 << shift;
96     });
97 }
98
99 pub fn is_known(attr: &Attribute) -> bool {
100     let AttrId(id) = attr.id;
101     GLOBALS.with(|globals| {
102         let slot = globals.known_attrs.lock();
103         let idx = (id / 64) as usize;
104         let shift = id % 64;
105         slot.get(idx).map(|bits| bits & (1 << shift) != 0)
106             .unwrap_or(false)
107     })
108 }
109
110 impl NestedMetaItem {
111     /// Returns the MetaItem if self is a NestedMetaItemKind::MetaItem.
112     pub fn meta_item(&self) -> Option<&MetaItem> {
113         match self.node {
114             NestedMetaItemKind::MetaItem(ref item) => Some(item),
115             _ => None
116         }
117     }
118
119     /// Returns the Lit if self is a NestedMetaItemKind::Literal.
120     pub fn literal(&self) -> Option<&Lit> {
121         match self.node {
122             NestedMetaItemKind::Literal(ref lit) => Some(lit),
123             _ => None
124         }
125     }
126
127     /// Returns the Span for `self`.
128     pub fn span(&self) -> Span {
129         self.span
130     }
131
132     /// Returns true if this list item is a MetaItem with a name of `name`.
133     pub fn check_name(&self, name: &str) -> bool {
134         self.meta_item().map_or(false, |meta_item| meta_item.check_name(name))
135     }
136
137     /// Returns the name of the meta item, e.g. `foo` in `#[foo]`,
138     /// `#[foo="bar"]` and `#[foo(bar)]`, if self is a MetaItem
139     pub fn name(&self) -> Option<Name> {
140         self.meta_item().and_then(|meta_item| Some(meta_item.ident.name))
141     }
142
143     /// Gets the string value if self is a MetaItem and the MetaItem is a
144     /// MetaItemKind::NameValue variant containing a string, otherwise None.
145     pub fn value_str(&self) -> Option<Symbol> {
146         self.meta_item().and_then(|meta_item| meta_item.value_str())
147     }
148
149     /// Returns a name and single literal value tuple of the MetaItem.
150     pub fn name_value_literal(&self) -> Option<(Name, &Lit)> {
151         self.meta_item().and_then(
152             |meta_item| meta_item.meta_item_list().and_then(
153                 |meta_item_list| {
154                     if meta_item_list.len() == 1 {
155                         let nested_item = &meta_item_list[0];
156                         if nested_item.is_literal() {
157                             Some((meta_item.ident.name, nested_item.literal().unwrap()))
158                         } else {
159                             None
160                         }
161                     }
162                     else {
163                         None
164                     }}))
165     }
166
167     /// Returns a MetaItem if self is a MetaItem with Kind Word.
168     pub fn word(&self) -> Option<&MetaItem> {
169         self.meta_item().and_then(|meta_item| if meta_item.is_word() {
170             Some(meta_item)
171         } else {
172             None
173         })
174     }
175
176     /// Gets a list of inner meta items from a list MetaItem type.
177     pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
178         self.meta_item().and_then(|meta_item| meta_item.meta_item_list())
179     }
180
181     /// Returns `true` if the variant is MetaItem.
182     pub fn is_meta_item(&self) -> bool {
183         self.meta_item().is_some()
184     }
185
186     /// Returns `true` if the variant is Literal.
187     pub fn is_literal(&self) -> bool {
188         self.literal().is_some()
189     }
190
191     /// Returns `true` if self is a MetaItem and the meta item is a word.
192     pub fn is_word(&self) -> bool {
193         self.word().is_some()
194     }
195
196     /// Returns `true` if self is a MetaItem and the meta item is a ValueString.
197     pub fn is_value_str(&self) -> bool {
198         self.value_str().is_some()
199     }
200
201     /// Returns `true` if self is a MetaItem and the meta item is a list.
202     pub fn is_meta_item_list(&self) -> bool {
203         self.meta_item_list().is_some()
204     }
205 }
206
207 impl Attribute {
208     pub fn check_name(&self, name: &str) -> bool {
209         let matches = self.path == name;
210         if matches {
211             mark_used(self);
212         }
213         matches
214     }
215
216     pub fn name(&self) -> Option<Name> {
217         match self.path.segments.len() {
218             1 => Some(self.path.segments[0].ident.name),
219             _ => None,
220         }
221     }
222
223     pub fn value_str(&self) -> Option<Symbol> {
224         self.meta().and_then(|meta| meta.value_str())
225     }
226
227     pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
228         match self.meta() {
229             Some(MetaItem { node: MetaItemKind::List(list), .. }) => Some(list),
230             _ => None
231         }
232     }
233
234     pub fn is_word(&self) -> bool {
235         self.path.segments.len() == 1 && self.tokens.is_empty()
236     }
237
238     pub fn span(&self) -> Span {
239         self.span
240     }
241
242     pub fn is_meta_item_list(&self) -> bool {
243         self.meta_item_list().is_some()
244     }
245
246     /// Indicates if the attribute is a Value String.
247     pub fn is_value_str(&self) -> bool {
248         self.value_str().is_some()
249     }
250 }
251
252 impl MetaItem {
253     pub fn value_str(&self) -> Option<Symbol> {
254         match self.node {
255             MetaItemKind::NameValue(ref v) => {
256                 match v.node {
257                     LitKind::Str(ref s, _) => Some(*s),
258                     _ => None,
259                 }
260             },
261             _ => None
262         }
263     }
264
265     pub fn meta_item_list(&self) -> Option<&[NestedMetaItem]> {
266         match self.node {
267             MetaItemKind::List(ref l) => Some(&l[..]),
268             _ => None
269         }
270     }
271
272     pub fn is_word(&self) -> bool {
273         match self.node {
274             MetaItemKind::Word => true,
275             _ => false,
276         }
277     }
278
279     pub fn span(&self) -> Span { self.span }
280
281     pub fn check_name(&self, name: &str) -> bool {
282         self.ident.name == name
283     }
284
285     pub fn is_value_str(&self) -> bool {
286         self.value_str().is_some()
287     }
288
289     pub fn is_meta_item_list(&self) -> bool {
290         self.meta_item_list().is_some()
291     }
292 }
293
294 impl Attribute {
295     /// Extract the MetaItem from inside this Attribute.
296     pub fn meta(&self) -> Option<MetaItem> {
297         let mut tokens = self.tokens.trees().peekable();
298         Some(MetaItem {
299             ident: match self.path.segments.len() {
300                 1 => self.path.segments[0].ident,
301                 _ => return None,
302             },
303             node: if let Some(node) = MetaItemKind::from_tokens(&mut tokens) {
304                 if tokens.peek().is_some() {
305                     return None;
306                 }
307                 node
308             } else {
309                 return None;
310             },
311             span: self.span,
312         })
313     }
314
315     pub fn parse<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, T>
316         where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
317     {
318         let mut parser = Parser::new(sess, self.tokens.clone(), None, false, false);
319         let result = f(&mut parser)?;
320         if parser.token != token::Eof {
321             parser.unexpected()?;
322         }
323         Ok(result)
324     }
325
326     pub fn parse_list<'a, T, F>(&self, sess: &'a ParseSess, mut f: F) -> PResult<'a, Vec<T>>
327         where F: FnMut(&mut Parser<'a>) -> PResult<'a, T>,
328     {
329         if self.tokens.is_empty() {
330             return Ok(Vec::new());
331         }
332         self.parse(sess, |parser| {
333             parser.expect(&token::OpenDelim(token::Paren))?;
334             let mut list = Vec::new();
335             while !parser.eat(&token::CloseDelim(token::Paren)) {
336                 list.push(f(parser)?);
337                 if !parser.eat(&token::Comma) {
338                    parser.expect(&token::CloseDelim(token::Paren))?;
339                     break
340                 }
341             }
342             Ok(list)
343         })
344     }
345
346     pub fn parse_meta<'a>(&self, sess: &'a ParseSess) -> PResult<'a, MetaItem> {
347         if self.path.segments.len() > 1 {
348             sess.span_diagnostic.span_err(self.path.span, "expected ident, found path");
349         }
350
351         Ok(MetaItem {
352             ident: self.path.segments.last().unwrap().ident,
353             node: self.parse(sess, |parser| parser.parse_meta_item_kind())?,
354             span: self.span,
355         })
356     }
357
358     /// Convert self to a normal #[doc="foo"] comment, if it is a
359     /// comment like `///` or `/** */`. (Returns self unchanged for
360     /// non-sugared doc attributes.)
361     pub fn with_desugared_doc<T, F>(&self, f: F) -> T where
362         F: FnOnce(&Attribute) -> T,
363     {
364         if self.is_sugared_doc {
365             let comment = self.value_str().unwrap();
366             let meta = mk_name_value_item_str(
367                 Ident::from_str("doc"),
368                 dummy_spanned(Symbol::intern(&strip_doc_comment_decoration(&comment.as_str()))));
369             let mut attr = if self.style == ast::AttrStyle::Outer {
370                 mk_attr_outer(self.span, self.id, meta)
371             } else {
372                 mk_attr_inner(self.span, self.id, meta)
373             };
374             attr.is_sugared_doc = true;
375             f(&attr)
376         } else {
377             f(self)
378         }
379     }
380 }
381
382 /* Constructors */
383
384 pub fn mk_name_value_item_str(ident: Ident, value: Spanned<Symbol>) -> MetaItem {
385     let value = respan(value.span, LitKind::Str(value.node, ast::StrStyle::Cooked));
386     mk_name_value_item(ident.span.to(value.span), ident, value)
387 }
388
389 pub fn mk_name_value_item(span: Span, ident: Ident, value: ast::Lit) -> MetaItem {
390     MetaItem { ident, span, node: MetaItemKind::NameValue(value) }
391 }
392
393 pub fn mk_list_item(span: Span, ident: Ident, items: Vec<NestedMetaItem>) -> MetaItem {
394     MetaItem { ident, span, node: MetaItemKind::List(items) }
395 }
396
397 pub fn mk_word_item(ident: Ident) -> MetaItem {
398     MetaItem { ident, span: ident.span, node: MetaItemKind::Word }
399 }
400 pub fn mk_nested_word_item(ident: Ident) -> NestedMetaItem {
401     respan(ident.span, NestedMetaItemKind::MetaItem(mk_word_item(ident)))
402 }
403
404 pub fn mk_attr_id() -> AttrId {
405     use std::sync::atomic::AtomicUsize;
406     use std::sync::atomic::Ordering;
407
408     static NEXT_ATTR_ID: AtomicUsize = AtomicUsize::new(0);
409
410     let id = NEXT_ATTR_ID.fetch_add(1, Ordering::SeqCst);
411     assert!(id != ::std::usize::MAX);
412     AttrId(id)
413 }
414
415 /// Returns an inner attribute with the given value.
416 pub fn mk_attr_inner(span: Span, id: AttrId, item: MetaItem) -> Attribute {
417     mk_spanned_attr_inner(span, id, item)
418 }
419
420 /// Returns an inner attribute with the given value and span.
421 pub fn mk_spanned_attr_inner(sp: Span, id: AttrId, item: MetaItem) -> Attribute {
422     Attribute {
423         id,
424         style: ast::AttrStyle::Inner,
425         path: ast::Path::from_ident(item.ident),
426         tokens: item.node.tokens(item.span),
427         is_sugared_doc: false,
428         span: sp,
429     }
430 }
431
432
433 /// Returns an outer attribute with the given value.
434 pub fn mk_attr_outer(span: Span, id: AttrId, item: MetaItem) -> Attribute {
435     mk_spanned_attr_outer(span, id, item)
436 }
437
438 /// Returns an outer attribute with the given value and span.
439 pub fn mk_spanned_attr_outer(sp: Span, id: AttrId, item: MetaItem) -> Attribute {
440     Attribute {
441         id,
442         style: ast::AttrStyle::Outer,
443         path: ast::Path::from_ident(item.ident),
444         tokens: item.node.tokens(item.span),
445         is_sugared_doc: false,
446         span: sp,
447     }
448 }
449
450 pub fn mk_sugared_doc_attr(id: AttrId, text: Symbol, span: Span) -> Attribute {
451     let style = doc_comment_style(&text.as_str());
452     let lit = respan(span, LitKind::Str(text, ast::StrStyle::Cooked));
453     Attribute {
454         id,
455         style,
456         path: ast::Path::from_ident(Ident::from_str("doc").with_span_pos(span)),
457         tokens: MetaItemKind::NameValue(lit).tokens(span),
458         is_sugared_doc: true,
459         span,
460     }
461 }
462
463 pub fn list_contains_name(items: &[NestedMetaItem], name: &str) -> bool {
464     items.iter().any(|item| {
465         item.check_name(name)
466     })
467 }
468
469 pub fn contains_name(attrs: &[Attribute], name: &str) -> bool {
470     attrs.iter().any(|item| {
471         item.check_name(name)
472     })
473 }
474
475 pub fn find_by_name<'a>(attrs: &'a [Attribute], name: &str) -> Option<&'a Attribute> {
476     attrs.iter().find(|attr| attr.check_name(name))
477 }
478
479 pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str) -> Option<Symbol> {
480     attrs.iter()
481         .find(|at| at.check_name(name))
482         .and_then(|at| at.value_str())
483 }
484
485 /// Check if `attrs` contains an attribute like `#![feature(feature_name)]`.
486 /// This will not perform any "sanity checks" on the form of the attributes.
487 pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
488     attrs.iter().any(|item| {
489         item.check_name("feature") &&
490         item.meta_item_list().map(|list| {
491             list.iter().any(|mi| {
492                 mi.word().map(|w| w.ident.name == feature_name)
493                          .unwrap_or(false)
494             })
495         }).unwrap_or(false)
496     })
497 }
498
499 /* Higher-level applications */
500
501 pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
502     first_attr_value_str_by_name(attrs, "crate_name")
503 }
504
505 #[derive(Copy, Clone, Hash, PartialEq, RustcEncodable, RustcDecodable)]
506 pub enum InlineAttr {
507     None,
508     Hint,
509     Always,
510     Never,
511 }
512
513 #[derive(Copy, Clone, PartialEq)]
514 pub enum UnwindAttr {
515     Allowed,
516     Aborts,
517 }
518
519 /// Determine what `#[unwind]` attribute is present in `attrs`, if any.
520 pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option<UnwindAttr> {
521     let syntax_error = |attr: &Attribute| {
522         mark_used(attr);
523         diagnostic.map(|d| {
524             span_err!(d, attr.span, E0633, "malformed `#[unwind]` attribute");
525         });
526         None
527     };
528
529     attrs.iter().fold(None, |ia, attr| {
530         if attr.path != "unwind" {
531             return ia;
532         }
533         let meta = match attr.meta() {
534             Some(meta) => meta.node,
535             None => return ia,
536         };
537         match meta {
538             MetaItemKind::Word => {
539                 syntax_error(attr)
540             }
541             MetaItemKind::List(ref items) => {
542                 mark_used(attr);
543                 if items.len() != 1 {
544                     syntax_error(attr)
545                 } else if list_contains_name(&items[..], "allowed") {
546                     Some(UnwindAttr::Allowed)
547                 } else if list_contains_name(&items[..], "aborts") {
548                     Some(UnwindAttr::Aborts)
549                 } else {
550                     syntax_error(attr)
551                 }
552             }
553             _ => ia,
554         }
555     })
556 }
557
558
559 /// Tests if a cfg-pattern matches the cfg set
560 pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
561     eval_condition(cfg, sess, &mut |cfg| {
562         if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
563             gated_cfg.check_and_emit(sess, feats);
564         }
565         sess.config.contains(&(cfg.ident.name, cfg.value_str()))
566     })
567 }
568
569 /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
570 /// evaluate individual items.
571 pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
572                          -> bool
573     where F: FnMut(&ast::MetaItem) -> bool
574 {
575     match cfg.node {
576         ast::MetaItemKind::List(ref mis) => {
577             for mi in mis.iter() {
578                 if !mi.is_meta_item() {
579                     handle_errors(&sess.span_diagnostic, mi.span, AttrError::UnsupportedLiteral);
580                     return false;
581                 }
582             }
583
584             // The unwraps below may look dangerous, but we've already asserted
585             // that they won't fail with the loop above.
586             match &*cfg.ident.name.as_str() {
587                 "any" => mis.iter().any(|mi| {
588                     eval_condition(mi.meta_item().unwrap(), sess, eval)
589                 }),
590                 "all" => mis.iter().all(|mi| {
591                     eval_condition(mi.meta_item().unwrap(), sess, eval)
592                 }),
593                 "not" => {
594                     if mis.len() != 1 {
595                         span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
596                         return false;
597                     }
598
599                     !eval_condition(mis[0].meta_item().unwrap(), sess, eval)
600                 },
601                 p => {
602                     span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", p);
603                     false
604                 }
605             }
606         },
607         ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
608             eval(cfg)
609         }
610     }
611 }
612
613 /// Represents the #[stable], #[unstable], #[rustc_{deprecated,const_unstable}] attributes.
614 #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)]
615 pub struct Stability {
616     pub level: StabilityLevel,
617     pub feature: Symbol,
618     pub rustc_depr: Option<RustcDeprecation>,
619     pub rustc_const_unstable: Option<RustcConstUnstable>,
620 }
621
622 /// The available stability levels.
623 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
624 pub enum StabilityLevel {
625     // Reason for the current stability level and the relevant rust-lang issue
626     Unstable { reason: Option<Symbol>, issue: u32 },
627     Stable { since: Symbol },
628 }
629
630 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
631 pub struct RustcDeprecation {
632     pub since: Symbol,
633     pub reason: Symbol,
634 }
635
636 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
637 pub struct RustcConstUnstable {
638     pub feature: Symbol,
639 }
640
641 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
642 pub struct Deprecation {
643     pub since: Option<Symbol>,
644     pub note: Option<Symbol>,
645 }
646
647 impl StabilityLevel {
648     pub fn is_unstable(&self) -> bool { if let Unstable {..} = *self { true } else { false }}
649     pub fn is_stable(&self) -> bool { if let Stable {..} = *self { true } else { false }}
650 }
651
652 fn find_stability_generic<'a, I>(diagnostic: &Handler,
653                                  attrs_iter: I,
654                                  item_sp: Span)
655                                  -> Option<Stability>
656     where I: Iterator<Item = &'a Attribute>
657 {
658     let mut stab: Option<Stability> = None;
659     let mut rustc_depr: Option<RustcDeprecation> = None;
660     let mut rustc_const_unstable: Option<RustcConstUnstable> = None;
661
662     'outer: for attr in attrs_iter {
663         if ![
664             "rustc_deprecated",
665             "rustc_const_unstable",
666             "unstable",
667             "stable",
668         ].iter().any(|&s| attr.path == s) {
669             continue // not a stability level
670         }
671
672         mark_used(attr);
673
674         let meta = attr.meta();
675         if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta {
676             let meta = meta.as_ref().unwrap();
677             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
678                 if item.is_some() {
679                     handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.ident.name));
680                     return false
681                 }
682                 if let Some(v) = meta.value_str() {
683                     *item = Some(v);
684                     true
685                 } else {
686                     span_err!(diagnostic, meta.span, E0539, "incorrect meta item");
687                     false
688                 }
689             };
690
691             macro_rules! get_meta {
692                 ($($name:ident),+) => {
693                     $(
694                         let mut $name = None;
695                     )+
696                     for meta in metas {
697                         if let Some(mi) = meta.meta_item() {
698                             match &*mi.ident.name.as_str() {
699                                 $(
700                                     stringify!($name)
701                                         => if !get(mi, &mut $name) { continue 'outer },
702                                 )+
703                                 _ => {
704                                     handle_errors(diagnostic, mi.span,
705                                                   AttrError::UnknownMetaItem(mi.ident.name));
706                                     continue 'outer
707                                 }
708                             }
709                         } else {
710                             handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
711                             continue 'outer
712                         }
713                     }
714                 }
715             }
716
717             match &*meta.ident.name.as_str() {
718                 "rustc_deprecated" => {
719                     if rustc_depr.is_some() {
720                         span_err!(diagnostic, item_sp, E0540,
721                                   "multiple rustc_deprecated attributes");
722                         continue 'outer
723                     }
724
725                     get_meta!(since, reason);
726
727                     match (since, reason) {
728                         (Some(since), Some(reason)) => {
729                             rustc_depr = Some(RustcDeprecation {
730                                 since,
731                                 reason,
732                             })
733                         }
734                         (None, _) => {
735                             handle_errors(diagnostic, attr.span(), AttrError::MissingSince);
736                             continue
737                         }
738                         _ => {
739                             span_err!(diagnostic, attr.span(), E0543, "missing 'reason'");
740                             continue
741                         }
742                     }
743                 }
744                 "rustc_const_unstable" => {
745                     if rustc_const_unstable.is_some() {
746                         span_err!(diagnostic, item_sp, E0553,
747                                   "multiple rustc_const_unstable attributes");
748                         continue 'outer
749                     }
750
751                     get_meta!(feature);
752                     if let Some(feature) = feature {
753                         rustc_const_unstable = Some(RustcConstUnstable {
754                             feature
755                         });
756                     } else {
757                         span_err!(diagnostic, attr.span(), E0629, "missing 'feature'");
758                         continue
759                     }
760                 }
761                 "unstable" => {
762                     if stab.is_some() {
763                         handle_errors(diagnostic, attr.span(), AttrError::MultipleStabilityLevels);
764                         break
765                     }
766
767                     let mut feature = None;
768                     let mut reason = None;
769                     let mut issue = None;
770                     for meta in metas {
771                         if let Some(mi) = meta.meta_item() {
772                             match &*mi.ident.name.as_str() {
773                                 "feature" => if !get(mi, &mut feature) { continue 'outer },
774                                 "reason" => if !get(mi, &mut reason) { continue 'outer },
775                                 "issue" => if !get(mi, &mut issue) { continue 'outer },
776                                 _ => {
777                                     handle_errors(diagnostic, meta.span,
778                                                   AttrError::UnknownMetaItem(mi.ident.name));
779                                     continue 'outer
780                                 }
781                             }
782                         } else {
783                             handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
784                             continue 'outer
785                         }
786                     }
787
788                     match (feature, reason, issue) {
789                         (Some(feature), reason, Some(issue)) => {
790                             stab = Some(Stability {
791                                 level: Unstable {
792                                     reason,
793                                     issue: {
794                                         if let Ok(issue) = issue.as_str().parse() {
795                                             issue
796                                         } else {
797                                             span_err!(diagnostic, attr.span(), E0545,
798                                                       "incorrect 'issue'");
799                                             continue
800                                         }
801                                     }
802                                 },
803                                 feature,
804                                 rustc_depr: None,
805                                 rustc_const_unstable: None,
806                             })
807                         }
808                         (None, _, _) => {
809                             handle_errors(diagnostic, attr.span(), AttrError::MissingFeature);
810                             continue
811                         }
812                         _ => {
813                             span_err!(diagnostic, attr.span(), E0547, "missing 'issue'");
814                             continue
815                         }
816                     }
817                 }
818                 "stable" => {
819                     if stab.is_some() {
820                         handle_errors(diagnostic, attr.span(), AttrError::MultipleStabilityLevels);
821                         break
822                     }
823
824                     let mut feature = None;
825                     let mut since = None;
826                     for meta in metas {
827                         if let NestedMetaItemKind::MetaItem(ref mi) = meta.node {
828                             match &*mi.ident.name.as_str() {
829                                 "feature" => if !get(mi, &mut feature) { continue 'outer },
830                                 "since" => if !get(mi, &mut since) { continue 'outer },
831                                 _ => {
832                                     handle_errors(diagnostic, meta.span,
833                                                   AttrError::UnknownMetaItem(mi.ident.name));
834                                     continue 'outer
835                                 }
836                             }
837                         } else {
838                             handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
839                             continue 'outer
840                         }
841                     }
842
843                     match (feature, since) {
844                         (Some(feature), Some(since)) => {
845                             stab = Some(Stability {
846                                 level: Stable {
847                                     since,
848                                 },
849                                 feature,
850                                 rustc_depr: None,
851                                 rustc_const_unstable: None,
852                             })
853                         }
854                         (None, _) => {
855                             handle_errors(diagnostic, attr.span(), AttrError::MissingFeature);
856                             continue
857                         }
858                         _ => {
859                             handle_errors(diagnostic, attr.span(), AttrError::MissingSince);
860                             continue
861                         }
862                     }
863                 }
864                 _ => unreachable!()
865             }
866         } else {
867             span_err!(diagnostic, attr.span(), E0548, "incorrect stability attribute type");
868             continue
869         }
870     }
871
872     // Merge the deprecation info into the stability info
873     if let Some(rustc_depr) = rustc_depr {
874         if let Some(ref mut stab) = stab {
875             stab.rustc_depr = Some(rustc_depr);
876         } else {
877             span_err!(diagnostic, item_sp, E0549,
878                       "rustc_deprecated attribute must be paired with \
879                        either stable or unstable attribute");
880         }
881     }
882
883     // Merge the const-unstable info into the stability info
884     if let Some(rustc_const_unstable) = rustc_const_unstable {
885         if let Some(ref mut stab) = stab {
886             stab.rustc_const_unstable = Some(rustc_const_unstable);
887         } else {
888             span_err!(diagnostic, item_sp, E0630,
889                       "rustc_const_unstable attribute must be paired with \
890                        either stable or unstable attribute");
891         }
892     }
893
894     stab
895 }
896
897 fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
898                                    attrs_iter: I,
899                                    item_sp: Span)
900                                    -> Option<Deprecation>
901     where I: Iterator<Item = &'a Attribute>
902 {
903     let mut depr: Option<Deprecation> = None;
904
905     'outer: for attr in attrs_iter {
906         if attr.path != "deprecated" {
907             continue
908         }
909
910         mark_used(attr);
911
912         if depr.is_some() {
913             span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes");
914             break
915         }
916
917         depr = if let Some(metas) = attr.meta_item_list() {
918             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
919                 if item.is_some() {
920                     handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.ident.name));
921                     return false
922                 }
923                 if let Some(v) = meta.value_str() {
924                     *item = Some(v);
925                     true
926                 } else {
927                     span_err!(diagnostic, meta.span, E0551, "incorrect meta item");
928                     false
929                 }
930             };
931
932             let mut since = None;
933             let mut note = None;
934             for meta in metas {
935                 if let NestedMetaItemKind::MetaItem(ref mi) = meta.node {
936                     match &*mi.ident.name.as_str() {
937                         "since" => if !get(mi, &mut since) { continue 'outer },
938                         "note" => if !get(mi, &mut note) { continue 'outer },
939                         _ => {
940                             handle_errors(diagnostic, meta.span,
941                                           AttrError::UnknownMetaItem(mi.ident.name));
942                             continue 'outer
943                         }
944                     }
945                 } else {
946                     handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
947                     continue 'outer
948                 }
949             }
950
951             Some(Deprecation {since: since, note: note})
952         } else {
953             Some(Deprecation{since: None, note: None})
954         }
955     }
956
957     depr
958 }
959
960 /// Find the first stability attribute. `None` if none exists.
961 pub fn find_stability(diagnostic: &Handler, attrs: &[Attribute],
962                       item_sp: Span) -> Option<Stability> {
963     find_stability_generic(diagnostic, attrs.iter(), item_sp)
964 }
965
966 /// Find the deprecation attribute. `None` if none exists.
967 pub fn find_deprecation(diagnostic: &Handler, attrs: &[Attribute],
968                         item_sp: Span) -> Option<Deprecation> {
969     find_deprecation_generic(diagnostic, attrs.iter(), item_sp)
970 }
971
972
973 /// Parse #[repr(...)] forms.
974 ///
975 /// Valid repr contents: any of the primitive integral type names (see
976 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
977 /// the same discriminant size that the corresponding C enum would or C
978 /// structure layout, `packed` to remove padding, and `transparent` to elegate representation
979 /// concerns to the only non-ZST field.
980 pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> {
981     let mut acc = Vec::new();
982     if attr.path == "repr" {
983         if let Some(items) = attr.meta_item_list() {
984             mark_used(attr);
985             for item in items {
986                 if !item.is_meta_item() {
987                     handle_errors(diagnostic, item.span, AttrError::UnsupportedLiteral);
988                     continue
989                 }
990
991                 let mut recognised = false;
992                 if let Some(mi) = item.word() {
993                     let word = &*mi.ident.name.as_str();
994                     let hint = match word {
995                         "C" => Some(ReprC),
996                         "packed" => Some(ReprPacked),
997                         "simd" => Some(ReprSimd),
998                         "transparent" => Some(ReprTransparent),
999                         _ => match int_type_of_word(word) {
1000                             Some(ity) => Some(ReprInt(ity)),
1001                             None => {
1002                                 None
1003                             }
1004                         }
1005                     };
1006
1007                     if let Some(h) = hint {
1008                         recognised = true;
1009                         acc.push(h);
1010                     }
1011                 } else if let Some((name, value)) = item.name_value_literal() {
1012                     if name == "align" {
1013                         recognised = true;
1014                         let mut align_error = None;
1015                         if let ast::LitKind::Int(align, ast::LitIntType::Unsuffixed) = value.node {
1016                             if align.is_power_of_two() {
1017                                 // rustc::ty::layout::Align restricts align to <= 2147483647
1018                                 if align <= 2147483647 {
1019                                     acc.push(ReprAlign(align as u32));
1020                                 } else {
1021                                     align_error = Some("larger than 2147483647");
1022                                 }
1023                             } else {
1024                                 align_error = Some("not a power of two");
1025                             }
1026                         } else {
1027                             align_error = Some("not an unsuffixed integer");
1028                         }
1029                         if let Some(align_error) = align_error {
1030                             span_err!(diagnostic, item.span, E0589,
1031                                       "invalid `repr(align)` attribute: {}", align_error);
1032                         }
1033                     }
1034                 }
1035                 if !recognised {
1036                     // Not a word we recognize
1037                     span_err!(diagnostic, item.span, E0552,
1038                               "unrecognized representation hint");
1039                 }
1040             }
1041         }
1042     }
1043     acc
1044 }
1045
1046 fn int_type_of_word(s: &str) -> Option<IntType> {
1047     match s {
1048         "i8" => Some(SignedInt(ast::IntTy::I8)),
1049         "u8" => Some(UnsignedInt(ast::UintTy::U8)),
1050         "i16" => Some(SignedInt(ast::IntTy::I16)),
1051         "u16" => Some(UnsignedInt(ast::UintTy::U16)),
1052         "i32" => Some(SignedInt(ast::IntTy::I32)),
1053         "u32" => Some(UnsignedInt(ast::UintTy::U32)),
1054         "i64" => Some(SignedInt(ast::IntTy::I64)),
1055         "u64" => Some(UnsignedInt(ast::UintTy::U64)),
1056         "i128" => Some(SignedInt(ast::IntTy::I128)),
1057         "u128" => Some(UnsignedInt(ast::UintTy::U128)),
1058         "isize" => Some(SignedInt(ast::IntTy::Isize)),
1059         "usize" => Some(UnsignedInt(ast::UintTy::Usize)),
1060         _ => None
1061     }
1062 }
1063
1064 #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
1065 pub enum ReprAttr {
1066     ReprInt(IntType),
1067     ReprC,
1068     ReprPacked,
1069     ReprSimd,
1070     ReprTransparent,
1071     ReprAlign(u32),
1072 }
1073
1074 #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
1075 pub enum IntType {
1076     SignedInt(ast::IntTy),
1077     UnsignedInt(ast::UintTy)
1078 }
1079
1080 impl IntType {
1081     #[inline]
1082     pub fn is_signed(self) -> bool {
1083         match self {
1084             SignedInt(..) => true,
1085             UnsignedInt(..) => false
1086         }
1087     }
1088 }
1089
1090 impl MetaItem {
1091     fn tokens(&self) -> TokenStream {
1092         let ident = TokenTree::Token(self.span, Token::from_ast_ident(self.ident));
1093         TokenStream::concat(vec![ident.into(), self.node.tokens(self.span)])
1094     }
1095
1096     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
1097         where I: Iterator<Item = TokenTree>,
1098     {
1099         let (span, ident) = match tokens.next() {
1100             Some(TokenTree::Token(span, Token::Ident(ident, _))) => (span, ident),
1101             Some(TokenTree::Token(_, Token::Interpolated(ref nt))) => match nt.0 {
1102                 token::Nonterminal::NtIdent(ident, _) => (ident.span, ident),
1103                 token::Nonterminal::NtMeta(ref meta) => return Some(meta.clone()),
1104                 _ => return None,
1105             },
1106             _ => return None,
1107         };
1108         let list_closing_paren_pos = tokens.peek().map(|tt| tt.span().hi());
1109         let node = MetaItemKind::from_tokens(tokens)?;
1110         let hi = match node {
1111             MetaItemKind::NameValue(ref lit) => lit.span.hi(),
1112             MetaItemKind::List(..) => list_closing_paren_pos.unwrap_or(span.hi()),
1113             _ => span.hi(),
1114         };
1115         Some(MetaItem { ident, node, span: span.with_hi(hi) })
1116     }
1117 }
1118
1119 impl MetaItemKind {
1120     pub fn tokens(&self, span: Span) -> TokenStream {
1121         match *self {
1122             MetaItemKind::Word => TokenStream::empty(),
1123             MetaItemKind::NameValue(ref lit) => {
1124                 TokenStream::concat(vec![TokenTree::Token(span, Token::Eq).into(), lit.tokens()])
1125             }
1126             MetaItemKind::List(ref list) => {
1127                 let mut tokens = Vec::new();
1128                 for (i, item) in list.iter().enumerate() {
1129                     if i > 0 {
1130                         tokens.push(TokenTree::Token(span, Token::Comma).into());
1131                     }
1132                     tokens.push(item.node.tokens());
1133                 }
1134                 TokenTree::Delimited(span, Delimited {
1135                     delim: token::Paren,
1136                     tts: TokenStream::concat(tokens).into(),
1137                 }).into()
1138             }
1139         }
1140     }
1141
1142     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItemKind>
1143         where I: Iterator<Item = TokenTree>,
1144     {
1145         let delimited = match tokens.peek().cloned() {
1146             Some(TokenTree::Token(_, token::Eq)) => {
1147                 tokens.next();
1148                 return if let Some(TokenTree::Token(span, token)) = tokens.next() {
1149                     LitKind::from_token(token)
1150                         .map(|lit| MetaItemKind::NameValue(Spanned { node: lit, span: span }))
1151                 } else {
1152                     None
1153                 };
1154             }
1155             Some(TokenTree::Delimited(_, ref delimited)) if delimited.delim == token::Paren => {
1156                 tokens.next();
1157                 delimited.stream()
1158             }
1159             _ => return Some(MetaItemKind::Word),
1160         };
1161
1162         let mut tokens = delimited.into_trees().peekable();
1163         let mut result = Vec::new();
1164         while let Some(..) = tokens.peek() {
1165             let item = NestedMetaItemKind::from_tokens(&mut tokens)?;
1166             result.push(respan(item.span(), item));
1167             match tokens.next() {
1168                 None | Some(TokenTree::Token(_, Token::Comma)) => {}
1169                 _ => return None,
1170             }
1171         }
1172         Some(MetaItemKind::List(result))
1173     }
1174 }
1175
1176 impl NestedMetaItemKind {
1177     fn span(&self) -> Span {
1178         match *self {
1179             NestedMetaItemKind::MetaItem(ref item) => item.span,
1180             NestedMetaItemKind::Literal(ref lit) => lit.span,
1181         }
1182     }
1183
1184     fn tokens(&self) -> TokenStream {
1185         match *self {
1186             NestedMetaItemKind::MetaItem(ref item) => item.tokens(),
1187             NestedMetaItemKind::Literal(ref lit) => lit.tokens(),
1188         }
1189     }
1190
1191     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<NestedMetaItemKind>
1192         where I: Iterator<Item = TokenTree>,
1193     {
1194         if let Some(TokenTree::Token(span, token)) = tokens.peek().cloned() {
1195             if let Some(node) = LitKind::from_token(token) {
1196                 tokens.next();
1197                 return Some(NestedMetaItemKind::Literal(respan(span, node)));
1198             }
1199         }
1200
1201         MetaItem::from_tokens(tokens).map(NestedMetaItemKind::MetaItem)
1202     }
1203 }
1204
1205 impl Lit {
1206     fn tokens(&self) -> TokenStream {
1207         TokenTree::Token(self.span, self.node.token()).into()
1208     }
1209 }
1210
1211 impl LitKind {
1212     fn token(&self) -> Token {
1213         use std::ascii;
1214
1215         match *self {
1216             LitKind::Str(string, ast::StrStyle::Cooked) => {
1217                 let mut escaped = String::new();
1218                 for ch in string.as_str().chars() {
1219                     escaped.extend(ch.escape_unicode());
1220                 }
1221                 Token::Literal(token::Lit::Str_(Symbol::intern(&escaped)), None)
1222             }
1223             LitKind::Str(string, ast::StrStyle::Raw(n)) => {
1224                 Token::Literal(token::Lit::StrRaw(string, n), None)
1225             }
1226             LitKind::ByteStr(ref bytes) => {
1227                 let string = bytes.iter().cloned().flat_map(ascii::escape_default)
1228                     .map(Into::<char>::into).collect::<String>();
1229                 Token::Literal(token::Lit::ByteStr(Symbol::intern(&string)), None)
1230             }
1231             LitKind::Byte(byte) => {
1232                 let string: String = ascii::escape_default(byte).map(Into::<char>::into).collect();
1233                 Token::Literal(token::Lit::Byte(Symbol::intern(&string)), None)
1234             }
1235             LitKind::Char(ch) => {
1236                 let string: String = ch.escape_default().map(Into::<char>::into).collect();
1237                 Token::Literal(token::Lit::Char(Symbol::intern(&string)), None)
1238             }
1239             LitKind::Int(n, ty) => {
1240                 let suffix = match ty {
1241                     ast::LitIntType::Unsigned(ty) => Some(Symbol::intern(ty.ty_to_string())),
1242                     ast::LitIntType::Signed(ty) => Some(Symbol::intern(ty.ty_to_string())),
1243                     ast::LitIntType::Unsuffixed => None,
1244                 };
1245                 Token::Literal(token::Lit::Integer(Symbol::intern(&n.to_string())), suffix)
1246             }
1247             LitKind::Float(symbol, ty) => {
1248                 Token::Literal(token::Lit::Float(symbol), Some(Symbol::intern(ty.ty_to_string())))
1249             }
1250             LitKind::FloatUnsuffixed(symbol) => Token::Literal(token::Lit::Float(symbol), None),
1251             LitKind::Bool(value) => Token::Ident(Ident::with_empty_ctxt(Symbol::intern(if value {
1252                 "true"
1253             } else {
1254                 "false"
1255             })), false),
1256         }
1257     }
1258
1259     fn from_token(token: Token) -> Option<LitKind> {
1260         match token {
1261             Token::Ident(ident, false) if ident.name == "true" => Some(LitKind::Bool(true)),
1262             Token::Ident(ident, false) if ident.name == "false" => Some(LitKind::Bool(false)),
1263             Token::Interpolated(ref nt) => match nt.0 {
1264                 token::NtExpr(ref v) => match v.node {
1265                     ExprKind::Lit(ref lit) => Some(lit.node.clone()),
1266                     _ => None,
1267                 },
1268                 _ => None,
1269             },
1270             Token::Literal(lit, suf) => {
1271                 let (suffix_illegal, result) = parse::lit_token(lit, suf, None);
1272                 if suffix_illegal && suf.is_some() {
1273                     return None;
1274                 }
1275                 result
1276             }
1277             _ => None,
1278         }
1279     }
1280 }
1281
1282 pub trait HasAttrs: Sized {
1283     fn attrs(&self) -> &[ast::Attribute];
1284     fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self;
1285 }
1286
1287 impl<T: HasAttrs> HasAttrs for Spanned<T> {
1288     fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() }
1289     fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
1290         respan(self.span, self.node.map_attrs(f))
1291     }
1292 }
1293
1294 impl HasAttrs for Vec<Attribute> {
1295     fn attrs(&self) -> &[Attribute] {
1296         self
1297     }
1298     fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
1299         f(self)
1300     }
1301 }
1302
1303 impl HasAttrs for ThinVec<Attribute> {
1304     fn attrs(&self) -> &[Attribute] {
1305         self
1306     }
1307     fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
1308         f(self.into()).into()
1309     }
1310 }
1311
1312 impl<T: HasAttrs + 'static> HasAttrs for P<T> {
1313     fn attrs(&self) -> &[Attribute] {
1314         (**self).attrs()
1315     }
1316     fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
1317         self.map(|t| t.map_attrs(f))
1318     }
1319 }
1320
1321 impl HasAttrs for StmtKind {
1322     fn attrs(&self) -> &[Attribute] {
1323         match *self {
1324             StmtKind::Local(ref local) => local.attrs(),
1325             StmtKind::Item(..) => &[],
1326             StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
1327             StmtKind::Mac(ref mac) => {
1328                 let (_, _, ref attrs) = **mac;
1329                 attrs.attrs()
1330             }
1331         }
1332     }
1333
1334     fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
1335         match self {
1336             StmtKind::Local(local) => StmtKind::Local(local.map_attrs(f)),
1337             StmtKind::Item(..) => self,
1338             StmtKind::Expr(expr) => StmtKind::Expr(expr.map_attrs(f)),
1339             StmtKind::Semi(expr) => StmtKind::Semi(expr.map_attrs(f)),
1340             StmtKind::Mac(mac) => StmtKind::Mac(mac.map(|(mac, style, attrs)| {
1341                 (mac, style, attrs.map_attrs(f))
1342             })),
1343         }
1344     }
1345 }
1346
1347 impl HasAttrs for Stmt {
1348     fn attrs(&self) -> &[ast::Attribute] { self.node.attrs() }
1349     fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
1350         Stmt { id: self.id, node: self.node.map_attrs(f), span: self.span }
1351     }
1352 }
1353
1354 macro_rules! derive_has_attrs {
1355     ($($ty:path),*) => { $(
1356         impl HasAttrs for $ty {
1357             fn attrs(&self) -> &[Attribute] {
1358                 &self.attrs
1359             }
1360
1361             fn map_attrs<F>(mut self, f: F) -> Self
1362                 where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>,
1363             {
1364                 self.attrs = self.attrs.map_attrs(f);
1365                 self
1366             }
1367         }
1368     )* }
1369 }
1370
1371 derive_has_attrs! {
1372     Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm,
1373     ast::Field, ast::FieldPat, ast::Variant_
1374 }