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