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