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