]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/attr.rs
Rollup merge of #40521 - TimNN:panic-free-shift, r=alexcrichton
[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, spanned, dummy_spanned, mk_sp};
22 use syntax_pos::{Span, BytePos, 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, lo: BytePos, hi: BytePos)
451                            -> Attribute {
452     let style = doc_comment_style(&text.as_str());
453     let lit = spanned(lo, hi, LitKind::Str(text, ast::StrStyle::Cooked));
454     Attribute {
455         id: id,
456         style: style,
457         path: ast::Path::from_ident(mk_sp(lo, hi), ast::Ident::from_str("doc")),
458         tokens: MetaItemKind::NameValue(lit).tokens(mk_sp(lo, hi)),
459         is_sugared_doc: true,
460         span: mk_sp(lo, hi),
461     }
462 }
463
464 pub fn list_contains_name(items: &[NestedMetaItem], name: &str) -> bool {
465     items.iter().any(|item| {
466         item.check_name(name)
467     })
468 }
469
470 pub fn contains_name(attrs: &[Attribute], name: &str) -> bool {
471     attrs.iter().any(|item| {
472         item.check_name(name)
473     })
474 }
475
476 pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str) -> Option<Symbol> {
477     attrs.iter()
478         .find(|at| at.check_name(name))
479         .and_then(|at| at.value_str())
480 }
481
482 /* Higher-level applications */
483
484 pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
485     first_attr_value_str_by_name(attrs, "crate_name")
486 }
487
488 /// Find the value of #[export_name=*] attribute and check its validity.
489 pub fn find_export_name_attr(diag: &Handler, attrs: &[Attribute]) -> Option<Symbol> {
490     attrs.iter().fold(None, |ia,attr| {
491         if attr.check_name("export_name") {
492             if let s@Some(_) = attr.value_str() {
493                 s
494             } else {
495                 struct_span_err!(diag, attr.span, E0558,
496                                  "export_name attribute has invalid format")
497                     .span_label(attr.span,
498                                 &format!("did you mean #[export_name=\"*\"]?"))
499                     .emit();
500                 None
501             }
502         } else {
503             ia
504         }
505     })
506 }
507
508 pub fn contains_extern_indicator(diag: &Handler, attrs: &[Attribute]) -> bool {
509     contains_name(attrs, "no_mangle") ||
510         find_export_name_attr(diag, attrs).is_some()
511 }
512
513 #[derive(Copy, Clone, PartialEq)]
514 pub enum InlineAttr {
515     None,
516     Hint,
517     Always,
518     Never,
519 }
520
521 /// Determine what `#[inline]` attribute is present in `attrs`, if any.
522 pub fn find_inline_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> InlineAttr {
523     attrs.iter().fold(InlineAttr::None, |ia, attr| {
524         if attr.path != "inline" {
525             return ia;
526         }
527         let meta = match attr.meta() {
528             Some(meta) => meta.node,
529             None => return ia,
530         };
531         match meta {
532             MetaItemKind::Word => {
533                 mark_used(attr);
534                 InlineAttr::Hint
535             }
536             MetaItemKind::List(ref items) => {
537                 mark_used(attr);
538                 if items.len() != 1 {
539                     diagnostic.map(|d|{ span_err!(d, attr.span, E0534, "expected one argument"); });
540                     InlineAttr::None
541                 } else if list_contains_name(&items[..], "always") {
542                     InlineAttr::Always
543                 } else if list_contains_name(&items[..], "never") {
544                     InlineAttr::Never
545                 } else {
546                     diagnostic.map(|d| {
547                         span_err!(d, items[0].span, E0535, "invalid argument");
548                     });
549
550                     InlineAttr::None
551                 }
552             }
553             _ => ia,
554         }
555     })
556 }
557
558 /// True if `#[inline]` or `#[inline(always)]` is present in `attrs`.
559 pub fn requests_inline(attrs: &[Attribute]) -> bool {
560     match find_inline_attr(None, attrs) {
561         InlineAttr::Hint | InlineAttr::Always => true,
562         InlineAttr::None | InlineAttr::Never => false,
563     }
564 }
565
566 /// Tests if a cfg-pattern matches the cfg set
567 pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
568     match cfg.node {
569         ast::MetaItemKind::List(ref mis) => {
570             for mi in mis.iter() {
571                 if !mi.is_meta_item() {
572                     handle_errors(&sess.span_diagnostic, mi.span, AttrError::UnsupportedLiteral);
573                     return false;
574                 }
575             }
576
577             // The unwraps below may look dangerous, but we've already asserted
578             // that they won't fail with the loop above.
579             match &*cfg.name.as_str() {
580                 "any" => mis.iter().any(|mi| {
581                     cfg_matches(mi.meta_item().unwrap(), sess, features)
582                 }),
583                 "all" => mis.iter().all(|mi| {
584                     cfg_matches(mi.meta_item().unwrap(), sess, features)
585                 }),
586                 "not" => {
587                     if mis.len() != 1 {
588                         span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
589                         return false;
590                     }
591
592                     !cfg_matches(mis[0].meta_item().unwrap(), sess, features)
593                 },
594                 p => {
595                     span_err!(sess.span_diagnostic, cfg.span, E0537, "invalid predicate `{}`", p);
596                     false
597                 }
598             }
599         },
600         ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
601             if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
602                 gated_cfg.check_and_emit(sess, feats);
603             }
604             sess.config.contains(&(cfg.name(), cfg.value_str()))
605         }
606     }
607 }
608
609 /// Represents the #[stable], #[unstable] and #[rustc_deprecated] attributes.
610 #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)]
611 pub struct Stability {
612     pub level: StabilityLevel,
613     pub feature: Symbol,
614     pub rustc_depr: Option<RustcDeprecation>,
615 }
616
617 /// The available stability levels.
618 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
619 pub enum StabilityLevel {
620     // Reason for the current stability level and the relevant rust-lang issue
621     Unstable { reason: Option<Symbol>, issue: u32 },
622     Stable { since: Symbol },
623 }
624
625 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
626 pub struct RustcDeprecation {
627     pub since: Symbol,
628     pub reason: Symbol,
629 }
630
631 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd, Clone, Debug, Eq, Hash)]
632 pub struct Deprecation {
633     pub since: Option<Symbol>,
634     pub note: Option<Symbol>,
635 }
636
637 impl StabilityLevel {
638     pub fn is_unstable(&self) -> bool { if let Unstable {..} = *self { true } else { false }}
639     pub fn is_stable(&self) -> bool { if let Stable {..} = *self { true } else { false }}
640 }
641
642 fn find_stability_generic<'a, I>(diagnostic: &Handler,
643                                  attrs_iter: I,
644                                  item_sp: Span)
645                                  -> Option<Stability>
646     where I: Iterator<Item = &'a Attribute>
647 {
648     let mut stab: Option<Stability> = None;
649     let mut rustc_depr: Option<RustcDeprecation> = None;
650
651     'outer: for attr in attrs_iter {
652         if attr.path != "rustc_deprecated" && attr.path != "unstable" && attr.path != "stable" {
653             continue // not a stability level
654         }
655
656         mark_used(attr);
657
658         let meta = attr.meta();
659         if let Some(MetaItem { node: MetaItemKind::List(ref metas), .. }) = meta {
660             let meta = meta.as_ref().unwrap();
661             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
662                 if item.is_some() {
663                     handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name()));
664                     return false
665                 }
666                 if let Some(v) = meta.value_str() {
667                     *item = Some(v);
668                     true
669                 } else {
670                     span_err!(diagnostic, meta.span, E0539, "incorrect meta item");
671                     false
672                 }
673             };
674
675             match &*meta.name.as_str() {
676                 "rustc_deprecated" => {
677                     if rustc_depr.is_some() {
678                         span_err!(diagnostic, item_sp, E0540,
679                                   "multiple rustc_deprecated attributes");
680                         break
681                     }
682
683                     let mut since = None;
684                     let mut reason = None;
685                     for meta in metas {
686                         if let Some(mi) = meta.meta_item() {
687                             match &*mi.name().as_str() {
688                                 "since" => if !get(mi, &mut since) { continue 'outer },
689                                 "reason" => if !get(mi, &mut reason) { continue 'outer },
690                                 _ => {
691                                     handle_errors(diagnostic, mi.span,
692                                                   AttrError::UnknownMetaItem(mi.name()));
693                                     continue 'outer
694                                 }
695                             }
696                         } else {
697                             handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
698                             continue 'outer
699                         }
700                     }
701
702                     match (since, reason) {
703                         (Some(since), Some(reason)) => {
704                             rustc_depr = Some(RustcDeprecation {
705                                 since: since,
706                                 reason: reason,
707                             })
708                         }
709                         (None, _) => {
710                             handle_errors(diagnostic, attr.span(), AttrError::MissingSince);
711                             continue
712                         }
713                         _ => {
714                             span_err!(diagnostic, attr.span(), E0543, "missing 'reason'");
715                             continue
716                         }
717                     }
718                 }
719                 "unstable" => {
720                     if stab.is_some() {
721                         handle_errors(diagnostic, attr.span(), AttrError::MultipleStabilityLevels);
722                         break
723                     }
724
725                     let mut feature = None;
726                     let mut reason = None;
727                     let mut issue = None;
728                     for meta in metas {
729                         if let Some(mi) = meta.meta_item() {
730                             match &*mi.name().as_str() {
731                                 "feature" => if !get(mi, &mut feature) { continue 'outer },
732                                 "reason" => if !get(mi, &mut reason) { continue 'outer },
733                                 "issue" => if !get(mi, &mut issue) { continue 'outer },
734                                 _ => {
735                                     handle_errors(diagnostic, meta.span,
736                                                   AttrError::UnknownMetaItem(mi.name()));
737                                     continue 'outer
738                                 }
739                             }
740                         } else {
741                             handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
742                             continue 'outer
743                         }
744                     }
745
746                     match (feature, reason, issue) {
747                         (Some(feature), reason, Some(issue)) => {
748                             stab = Some(Stability {
749                                 level: Unstable {
750                                     reason: reason,
751                                     issue: {
752                                         if let Ok(issue) = issue.as_str().parse() {
753                                             issue
754                                         } else {
755                                             span_err!(diagnostic, attr.span(), E0545,
756                                                       "incorrect 'issue'");
757                                             continue
758                                         }
759                                     }
760                                 },
761                                 feature: feature,
762                                 rustc_depr: None,
763                             })
764                         }
765                         (None, _, _) => {
766                             handle_errors(diagnostic, attr.span(), AttrError::MissingFeature);
767                             continue
768                         }
769                         _ => {
770                             span_err!(diagnostic, attr.span(), E0547, "missing 'issue'");
771                             continue
772                         }
773                     }
774                 }
775                 "stable" => {
776                     if stab.is_some() {
777                         handle_errors(diagnostic, attr.span(), AttrError::MultipleStabilityLevels);
778                         break
779                     }
780
781                     let mut feature = None;
782                     let mut since = None;
783                     for meta in metas {
784                         if let NestedMetaItemKind::MetaItem(ref mi) = meta.node {
785                             match &*mi.name().as_str() {
786                                 "feature" => if !get(mi, &mut feature) { continue 'outer },
787                                 "since" => if !get(mi, &mut since) { continue 'outer },
788                                 _ => {
789                                     handle_errors(diagnostic, meta.span,
790                                                   AttrError::UnknownMetaItem(mi.name()));
791                                     continue 'outer
792                                 }
793                             }
794                         } else {
795                             handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
796                             continue 'outer
797                         }
798                     }
799
800                     match (feature, since) {
801                         (Some(feature), Some(since)) => {
802                             stab = Some(Stability {
803                                 level: Stable {
804                                     since: since,
805                                 },
806                                 feature: feature,
807                                 rustc_depr: None,
808                             })
809                         }
810                         (None, _) => {
811                             handle_errors(diagnostic, attr.span(), AttrError::MissingFeature);
812                             continue
813                         }
814                         _ => {
815                             handle_errors(diagnostic, attr.span(), AttrError::MissingSince);
816                             continue
817                         }
818                     }
819                 }
820                 _ => unreachable!()
821             }
822         } else {
823             span_err!(diagnostic, attr.span(), E0548, "incorrect stability attribute type");
824             continue
825         }
826     }
827
828     // Merge the deprecation info into the stability info
829     if let Some(rustc_depr) = rustc_depr {
830         if let Some(ref mut stab) = stab {
831             stab.rustc_depr = Some(rustc_depr);
832         } else {
833             span_err!(diagnostic, item_sp, E0549,
834                       "rustc_deprecated attribute must be paired with \
835                        either stable or unstable attribute");
836         }
837     }
838
839     stab
840 }
841
842 fn find_deprecation_generic<'a, I>(diagnostic: &Handler,
843                                    attrs_iter: I,
844                                    item_sp: Span)
845                                    -> Option<Deprecation>
846     where I: Iterator<Item = &'a Attribute>
847 {
848     let mut depr: Option<Deprecation> = None;
849
850     'outer: for attr in attrs_iter {
851         if attr.path != "deprecated" {
852             continue
853         }
854
855         mark_used(attr);
856
857         if depr.is_some() {
858             span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes");
859             break
860         }
861
862         depr = if let Some(metas) = attr.meta_item_list() {
863             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
864                 if item.is_some() {
865                     handle_errors(diagnostic, meta.span, AttrError::MultipleItem(meta.name()));
866                     return false
867                 }
868                 if let Some(v) = meta.value_str() {
869                     *item = Some(v);
870                     true
871                 } else {
872                     span_err!(diagnostic, meta.span, E0551, "incorrect meta item");
873                     false
874                 }
875             };
876
877             let mut since = None;
878             let mut note = None;
879             for meta in metas {
880                 if let NestedMetaItemKind::MetaItem(ref mi) = meta.node {
881                     match &*mi.name().as_str() {
882                         "since" => if !get(mi, &mut since) { continue 'outer },
883                         "note" => if !get(mi, &mut note) { continue 'outer },
884                         _ => {
885                             handle_errors(diagnostic, meta.span,
886                                           AttrError::UnknownMetaItem(mi.name()));
887                             continue 'outer
888                         }
889                     }
890                 } else {
891                     handle_errors(diagnostic, meta.span, AttrError::UnsupportedLiteral);
892                     continue 'outer
893                 }
894             }
895
896             Some(Deprecation {since: since, note: note})
897         } else {
898             Some(Deprecation{since: None, note: None})
899         }
900     }
901
902     depr
903 }
904
905 /// Find the first stability attribute. `None` if none exists.
906 pub fn find_stability(diagnostic: &Handler, attrs: &[Attribute],
907                       item_sp: Span) -> Option<Stability> {
908     find_stability_generic(diagnostic, attrs.iter(), item_sp)
909 }
910
911 /// Find the deprecation attribute. `None` if none exists.
912 pub fn find_deprecation(diagnostic: &Handler, attrs: &[Attribute],
913                         item_sp: Span) -> Option<Deprecation> {
914     find_deprecation_generic(diagnostic, attrs.iter(), item_sp)
915 }
916
917
918 /// Parse #[repr(...)] forms.
919 ///
920 /// Valid repr contents: any of the primitive integral type names (see
921 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
922 /// the same discriminant size that the corresponding C enum would or C
923 /// structure layout, and `packed` to remove padding.
924 pub fn find_repr_attrs(diagnostic: &Handler, attr: &Attribute) -> Vec<ReprAttr> {
925     let mut acc = Vec::new();
926     if attr.path == "repr" {
927         if let Some(items) = attr.meta_item_list() {
928             mark_used(attr);
929             for item in items {
930                 if !item.is_meta_item() {
931                     handle_errors(diagnostic, item.span, AttrError::UnsupportedLiteral);
932                     continue
933                 }
934
935                 if let Some(mi) = item.word() {
936                     let word = &*mi.name().as_str();
937                     let hint = match word {
938                         // Can't use "extern" because it's not a lexical identifier.
939                         "C" => Some(ReprExtern),
940                         "packed" => Some(ReprPacked),
941                         "simd" => Some(ReprSimd),
942                         _ => match int_type_of_word(word) {
943                             Some(ity) => Some(ReprInt(ity)),
944                             None => {
945                                 // Not a word we recognize
946                                 span_err!(diagnostic, item.span, E0552,
947                                           "unrecognized representation hint");
948                                 None
949                             }
950                         }
951                     };
952
953                     if let Some(h) = hint {
954                         acc.push(h);
955                     }
956                 } else {
957                     span_err!(diagnostic, item.span, E0553,
958                               "unrecognized enum representation hint");
959                 }
960             }
961         }
962     }
963     acc
964 }
965
966 fn int_type_of_word(s: &str) -> Option<IntType> {
967     match s {
968         "i8" => Some(SignedInt(ast::IntTy::I8)),
969         "u8" => Some(UnsignedInt(ast::UintTy::U8)),
970         "i16" => Some(SignedInt(ast::IntTy::I16)),
971         "u16" => Some(UnsignedInt(ast::UintTy::U16)),
972         "i32" => Some(SignedInt(ast::IntTy::I32)),
973         "u32" => Some(UnsignedInt(ast::UintTy::U32)),
974         "i64" => Some(SignedInt(ast::IntTy::I64)),
975         "u64" => Some(UnsignedInt(ast::UintTy::U64)),
976         "i128" => Some(SignedInt(ast::IntTy::I128)),
977         "u128" => Some(UnsignedInt(ast::UintTy::U128)),
978         "isize" => Some(SignedInt(ast::IntTy::Is)),
979         "usize" => Some(UnsignedInt(ast::UintTy::Us)),
980         _ => None
981     }
982 }
983
984 #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
985 pub enum ReprAttr {
986     ReprInt(IntType),
987     ReprExtern,
988     ReprPacked,
989     ReprSimd,
990 }
991
992 #[derive(Eq, Hash, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
993 pub enum IntType {
994     SignedInt(ast::IntTy),
995     UnsignedInt(ast::UintTy)
996 }
997
998 impl IntType {
999     #[inline]
1000     pub fn is_signed(self) -> bool {
1001         match self {
1002             SignedInt(..) => true,
1003             UnsignedInt(..) => false
1004         }
1005     }
1006 }
1007
1008 impl MetaItem {
1009     fn tokens(&self) -> TokenStream {
1010         let ident = TokenTree::Token(self.span, Token::Ident(Ident::with_empty_ctxt(self.name)));
1011         TokenStream::concat(vec![ident.into(), self.node.tokens(self.span)])
1012     }
1013
1014     fn from_tokens<I>(tokens: &mut iter::Peekable<I>) -> Option<MetaItem>
1015         where I: Iterator<Item = TokenTree>,
1016     {
1017         let (mut span, name) = match tokens.next() {
1018             Some(TokenTree::Token(span, Token::Ident(ident))) => (span, ident.name),
1019             Some(TokenTree::Token(_, Token::Interpolated(ref nt))) => return match **nt {
1020                 token::Nonterminal::NtMeta(ref meta) => Some(meta.clone()),
1021                 _ => 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 }