]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/attr/builtin.rs
c10541c8c7e75d367eaf8055937d46a4c5ec7c68
[rust.git] / src / libsyntax / attr / builtin.rs
1 //! Parsing and validation of builtin attributes
2
3 use crate::ast::{self, Attribute, MetaItem, NestedMetaItem};
4 use crate::feature_gate::{Features, GatedCfg};
5 use crate::print::pprust;
6 use crate::sess::ParseSess;
7
8 use errors::{Applicability, Handler};
9 use std::num::NonZeroU32;
10 use syntax_pos::hygiene::Transparency;
11 use syntax_pos::{symbol::Symbol, symbol::sym, Span};
12 use rustc_macros::HashStable_Generic;
13
14 use super::{mark_used, MetaItemKind};
15
16 use rustc_error_codes::*;
17
18 enum AttrError {
19     MultipleItem(String),
20     UnknownMetaItem(String, &'static [&'static str]),
21     MissingSince,
22     MissingFeature,
23     MultipleStabilityLevels,
24     UnsupportedLiteral(&'static str, /* is_bytestr */ bool),
25 }
26
27 /// A template that the attribute input must match.
28 /// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
29 #[derive(Clone, Copy)]
30 pub struct AttributeTemplate {
31     pub word: bool,
32     pub list: Option<&'static str>,
33     pub name_value_str: Option<&'static str>,
34 }
35
36 impl AttributeTemplate {
37     pub fn only_word() -> Self {
38         Self { word: true, list: None, name_value_str: None }
39     }
40
41     /// Checks that the given meta-item is compatible with this template.
42     pub fn compatible(&self, meta_item_kind: &ast::MetaItemKind) -> bool {
43         match meta_item_kind {
44             ast::MetaItemKind::Word => self.word,
45             ast::MetaItemKind::List(..) => self.list.is_some(),
46             ast::MetaItemKind::NameValue(lit) if lit.kind.is_str() => self.name_value_str.is_some(),
47             ast::MetaItemKind::NameValue(..) => false,
48         }
49     }
50 }
51
52 fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
53     let diag = &sess.span_diagnostic;
54     match error {
55         AttrError::MultipleItem(item) => span_err!(diag, span, E0538,
56                                                    "multiple '{}' items", item),
57         AttrError::UnknownMetaItem(item, expected) => {
58             let expected = expected
59                 .iter()
60                 .map(|name| format!("`{}`", name))
61                 .collect::<Vec<_>>();
62             struct_span_err!(diag, span, E0541, "unknown meta item '{}'", item)
63                 .span_label(span, format!("expected one of {}", expected.join(", ")))
64                 .emit();
65         }
66         AttrError::MissingSince => span_err!(diag, span, E0542, "missing 'since'"),
67         AttrError::MissingFeature => span_err!(diag, span, E0546, "missing 'feature'"),
68         AttrError::MultipleStabilityLevels => span_err!(diag, span, E0544,
69                                                         "multiple stability levels"),
70         AttrError::UnsupportedLiteral(
71             msg,
72             is_bytestr,
73         ) => {
74             let mut err = struct_span_err!(diag, span, E0565, "{}", msg);
75             if is_bytestr {
76                 if let Ok(lint_str) = sess.source_map().span_to_snippet(span) {
77                     err.span_suggestion(
78                         span,
79                         "consider removing the prefix",
80                         format!("{}", &lint_str[1..]),
81                         Applicability::MaybeIncorrect,
82                     );
83                 }
84             }
85             err.emit();
86         }
87     }
88 }
89
90 #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
91 pub enum InlineAttr {
92     None,
93     Hint,
94     Always,
95     Never,
96 }
97
98 #[derive(Clone, RustcEncodable, RustcDecodable)]
99 pub enum OptimizeAttr {
100     None,
101     Speed,
102     Size,
103 }
104
105 #[derive(Copy, Clone, PartialEq)]
106 pub enum UnwindAttr {
107     Allowed,
108     Aborts,
109 }
110
111 /// Determine what `#[unwind]` attribute is present in `attrs`, if any.
112 pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option<UnwindAttr> {
113     attrs.iter().fold(None, |ia, attr| {
114         if attr.check_name(sym::unwind) {
115             if let Some(meta) = attr.meta() {
116                 if let MetaItemKind::List(items) = meta.kind {
117                     if items.len() == 1 {
118                         if items[0].check_name(sym::allowed) {
119                             return Some(UnwindAttr::Allowed);
120                         } else if items[0].check_name(sym::aborts) {
121                             return Some(UnwindAttr::Aborts);
122                         }
123                     }
124
125                     diagnostic.map(|d| {
126                         struct_span_err!(d, attr.span, E0633, "malformed `unwind` attribute input")
127                             .span_label(attr.span, "invalid argument")
128                             .span_suggestions(
129                                 attr.span,
130                                 "the allowed arguments are `allowed` and `aborts`",
131                                 (vec!["allowed", "aborts"]).into_iter()
132                                     .map(|s| format!("#[unwind({})]", s)),
133                                 Applicability::MachineApplicable,
134                             ).emit();
135                     });
136                 }
137             }
138         }
139
140         ia
141     })
142 }
143
144 /// Represents the #[stable], #[unstable], #[rustc_{deprecated,const_unstable}] attributes.
145 #[derive(RustcEncodable, RustcDecodable, Copy, Clone, Debug,
146          PartialEq, Eq, Hash, HashStable_Generic)]
147 pub struct Stability {
148     pub level: StabilityLevel,
149     pub feature: Symbol,
150     pub rustc_depr: Option<RustcDeprecation>,
151     /// `None` means the function is stable but needs to be a stable const fn, too
152     /// `Some` contains the feature gate required to be able to use the function
153     /// as const fn
154     pub const_stability: Option<Symbol>,
155     /// whether the function has a `#[rustc_promotable]` attribute
156     pub promotable: bool,
157     /// whether the function has a `#[rustc_allow_const_fn_ptr]` attribute
158     pub allow_const_fn_ptr: bool,
159 }
160
161 /// The available stability levels.
162 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd,
163          Copy, Clone, Debug, Eq, Hash, HashStable_Generic)]
164 pub enum StabilityLevel {
165     // Reason for the current stability level and the relevant rust-lang issue
166     Unstable { reason: Option<Symbol>, issue: Option<NonZeroU32>, is_soft: bool },
167     Stable { since: Symbol },
168 }
169
170 impl StabilityLevel {
171     pub fn is_unstable(&self) -> bool {
172         if let StabilityLevel::Unstable {..} = *self {
173             true
174         } else {
175             false
176         }
177     }
178     pub fn is_stable(&self) -> bool {
179         if let StabilityLevel::Stable {..} = *self {
180             true
181         } else {
182             false
183         }
184     }
185 }
186
187 #[derive(RustcEncodable, RustcDecodable, PartialEq, PartialOrd,
188          Copy, Clone, Debug, Eq, Hash, HashStable_Generic)]
189 pub struct RustcDeprecation {
190     pub since: Symbol,
191     pub reason: Symbol,
192     /// A text snippet used to completely replace any use of the deprecated item in an expression.
193     pub suggestion: Option<Symbol>,
194 }
195
196 /// Checks if `attrs` contains an attribute like `#![feature(feature_name)]`.
197 /// This will not perform any "sanity checks" on the form of the attributes.
198 pub fn contains_feature_attr(attrs: &[Attribute], feature_name: Symbol) -> bool {
199     attrs.iter().any(|item| {
200         item.check_name(sym::feature) &&
201         item.meta_item_list().map(|list| {
202             list.iter().any(|mi| mi.is_word() && mi.check_name(feature_name))
203         }).unwrap_or(false)
204     })
205 }
206
207 /// Collects stability info from all stability attributes in `attrs`.
208 /// Returns `None` if no stability attributes are found.
209 pub fn find_stability(sess: &ParseSess, attrs: &[Attribute],
210                       item_sp: Span) -> Option<Stability> {
211     find_stability_generic(sess, attrs.iter(), item_sp)
212 }
213
214 fn find_stability_generic<'a, I>(sess: &ParseSess,
215                                  attrs_iter: I,
216                                  item_sp: Span)
217                                  -> Option<Stability>
218     where I: Iterator<Item = &'a Attribute>
219 {
220     use StabilityLevel::*;
221
222     let mut stab: Option<Stability> = None;
223     let mut rustc_depr: Option<RustcDeprecation> = None;
224     let mut rustc_const_unstable: Option<Symbol> = None;
225     let mut promotable = false;
226     let mut allow_const_fn_ptr = false;
227     let diagnostic = &sess.span_diagnostic;
228
229     'outer: for attr in attrs_iter {
230         if ![
231             sym::rustc_deprecated,
232             sym::rustc_const_unstable,
233             sym::unstable,
234             sym::stable,
235             sym::rustc_promotable,
236             sym::rustc_allow_const_fn_ptr,
237         ].iter().any(|&s| attr.has_name(s)) {
238             continue // not a stability level
239         }
240
241         mark_used(attr);
242
243         let meta = attr.meta();
244
245         if attr.has_name(sym::rustc_promotable) {
246             promotable = true;
247         }
248         if attr.has_name(sym::rustc_allow_const_fn_ptr) {
249             allow_const_fn_ptr = true;
250         }
251         // attributes with data
252         else if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta {
253             let meta = meta.as_ref().unwrap();
254             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
255                 if item.is_some() {
256                     handle_errors(
257                         sess,
258                         meta.span,
259                         AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
260                     );
261                     return false
262                 }
263                 if let Some(v) = meta.value_str() {
264                     *item = Some(v);
265                     true
266                 } else {
267                     span_err!(diagnostic, meta.span, E0539, "incorrect meta item");
268                     false
269                 }
270             };
271
272             macro_rules! get_meta {
273                 ($($name:ident),+) => {
274                     $(
275                         let mut $name = None;
276                     )+
277                     for meta in metas {
278                         if let Some(mi) = meta.meta_item() {
279                             match mi.name_or_empty() {
280                                 $(
281                                     sym::$name => if !get(mi, &mut $name) { continue 'outer },
282                                 )+
283                                 _ => {
284                                     let expected = &[ $( stringify!($name) ),+ ];
285                                     handle_errors(
286                                         sess,
287                                         mi.span,
288                                         AttrError::UnknownMetaItem(
289                                             pprust::path_to_string(&mi.path),
290                                             expected,
291                                         ),
292                                     );
293                                     continue 'outer
294                                 }
295                             }
296                         } else {
297                             handle_errors(
298                                 sess,
299                                 meta.span(),
300                                 AttrError::UnsupportedLiteral(
301                                     "unsupported literal",
302                                     false,
303                                 ),
304                             );
305                             continue 'outer
306                         }
307                     }
308                 }
309             }
310
311             match meta.name_or_empty() {
312                 sym::rustc_deprecated => {
313                     if rustc_depr.is_some() {
314                         span_err!(diagnostic, item_sp, E0540,
315                                   "multiple rustc_deprecated attributes");
316                         continue 'outer
317                     }
318
319                     get_meta!(since, reason, suggestion);
320
321                     match (since, reason) {
322                         (Some(since), Some(reason)) => {
323                             rustc_depr = Some(RustcDeprecation {
324                                 since,
325                                 reason,
326                                 suggestion,
327                             })
328                         }
329                         (None, _) => {
330                             handle_errors(sess, attr.span, AttrError::MissingSince);
331                             continue
332                         }
333                         _ => {
334                             span_err!(diagnostic, attr.span, E0543, "missing 'reason'");
335                             continue
336                         }
337                     }
338                 }
339                 sym::rustc_const_unstable => {
340                     if rustc_const_unstable.is_some() {
341                         span_err!(diagnostic, item_sp, E0553,
342                                   "multiple rustc_const_unstable attributes");
343                         continue 'outer
344                     }
345
346                     get_meta!(feature);
347                     if let Some(feature) = feature {
348                         rustc_const_unstable = Some(feature);
349                     } else {
350                         span_err!(diagnostic, attr.span, E0629, "missing 'feature'");
351                         continue
352                     }
353                 }
354                 sym::unstable => {
355                     if stab.is_some() {
356                         handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
357                         break
358                     }
359
360                     let mut feature = None;
361                     let mut reason = None;
362                     let mut issue = None;
363                     let mut is_soft = false;
364                     for meta in metas {
365                         if let Some(mi) = meta.meta_item() {
366                             match mi.name_or_empty() {
367                                 sym::feature => if !get(mi, &mut feature) { continue 'outer },
368                                 sym::reason => if !get(mi, &mut reason) { continue 'outer },
369                                 sym::issue => if !get(mi, &mut issue) { continue 'outer },
370                                 sym::soft => {
371                                     if !mi.is_word() {
372                                         let msg = "`soft` should not have any arguments";
373                                         sess.span_diagnostic.span_err(mi.span, msg);
374                                     }
375                                     is_soft = true;
376                                 }
377                                 _ => {
378                                     handle_errors(
379                                         sess,
380                                         meta.span(),
381                                         AttrError::UnknownMetaItem(
382                                             pprust::path_to_string(&mi.path),
383                                             &["feature", "reason", "issue", "soft"]
384                                         ),
385                                     );
386                                     continue 'outer
387                                 }
388                             }
389                         } else {
390                             handle_errors(
391                                 sess,
392                                 meta.span(),
393                                 AttrError::UnsupportedLiteral(
394                                     "unsupported literal",
395                                     false,
396                                 ),
397                             );
398                             continue 'outer
399                         }
400                     }
401
402                     match (feature, reason, issue) {
403                         (Some(feature), reason, Some(issue)) => {
404                             let issue = match &*issue.as_str() {
405                                 // FIXME(rossmacarthur): remove "0" because "none" should be used
406                                 // See #41260
407                                 "none" | "0" => None,
408                                 issue => {
409                                     if let Ok(num) = issue.parse() {
410                                         NonZeroU32::new(num)
411                                     } else {
412                                         span_err!(
413                                             diagnostic,
414                                             attr.span,
415                                             E0545,
416                                             "incorrect 'issue'"
417                                         );
418                                         continue
419                                     }
420                                 }
421                             };
422                             stab = Some(Stability {
423                                 level: Unstable {
424                                     reason,
425                                     issue,
426                                     is_soft,
427                                 },
428                                 feature,
429                                 rustc_depr: None,
430                                 const_stability: None,
431                                 promotable: false,
432                                 allow_const_fn_ptr: false,
433                             })
434                         }
435                         (None, _, _) => {
436                             handle_errors(sess, attr.span, AttrError::MissingFeature);
437                             continue
438                         }
439                         _ => {
440                             span_err!(diagnostic, attr.span, E0547, "missing 'issue'");
441                             continue
442                         }
443                     }
444                 }
445                 sym::stable => {
446                     if stab.is_some() {
447                         handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
448                         break
449                     }
450
451                     let mut feature = None;
452                     let mut since = None;
453                     for meta in metas {
454                         match meta {
455                             NestedMetaItem::MetaItem(mi) => {
456                                 match mi.name_or_empty() {
457                                     sym::feature => if !get(mi, &mut feature) { continue 'outer },
458                                     sym::since => if !get(mi, &mut since) { continue 'outer },
459                                     _ => {
460                                         handle_errors(
461                                             sess,
462                                             meta.span(),
463                                             AttrError::UnknownMetaItem(
464                                                 pprust::path_to_string(&mi.path),
465                                                 &["since", "note"],
466                                             ),
467                                         );
468                                         continue 'outer
469                                     }
470                                 }
471                             },
472                             NestedMetaItem::Literal(lit) => {
473                                 handle_errors(
474                                     sess,
475                                     lit.span,
476                                     AttrError::UnsupportedLiteral(
477                                         "unsupported literal",
478                                         false,
479                                     ),
480                                 );
481                                 continue 'outer
482                             }
483                         }
484                     }
485
486                     match (feature, since) {
487                         (Some(feature), Some(since)) => {
488                             stab = Some(Stability {
489                                 level: Stable {
490                                     since,
491                                 },
492                                 feature,
493                                 rustc_depr: None,
494                                 const_stability: None,
495                                 promotable: false,
496                                 allow_const_fn_ptr: false,
497                             })
498                         }
499                         (None, _) => {
500                             handle_errors(sess, attr.span, AttrError::MissingFeature);
501                             continue
502                         }
503                         _ => {
504                             handle_errors(sess, attr.span, AttrError::MissingSince);
505                             continue
506                         }
507                     }
508                 }
509                 _ => unreachable!()
510             }
511         }
512     }
513
514     // Merge the deprecation info into the stability info
515     if let Some(rustc_depr) = rustc_depr {
516         if let Some(ref mut stab) = stab {
517             stab.rustc_depr = Some(rustc_depr);
518         } else {
519             span_err!(diagnostic, item_sp, E0549,
520                       "rustc_deprecated attribute must be paired with \
521                        either stable or unstable attribute");
522         }
523     }
524
525     // Merge the const-unstable info into the stability info
526     if let Some(feature) = rustc_const_unstable {
527         if let Some(ref mut stab) = stab {
528             stab.const_stability = Some(feature);
529         } else {
530             span_err!(diagnostic, item_sp, E0630,
531                       "rustc_const_unstable attribute must be paired with \
532                        either stable or unstable attribute");
533         }
534     }
535
536     // Merge the const-unstable info into the stability info
537     if promotable || allow_const_fn_ptr {
538         if let Some(ref mut stab) = stab {
539             stab.promotable = promotable;
540             stab.allow_const_fn_ptr = allow_const_fn_ptr;
541         } else {
542             span_err!(diagnostic, item_sp, E0717,
543                       "rustc_promotable and rustc_allow_const_fn_ptr attributes \
544                       must be paired with either stable or unstable attribute");
545         }
546     }
547
548     stab
549 }
550
551 pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
552     super::first_attr_value_str_by_name(attrs, sym::crate_name)
553 }
554
555 /// Tests if a cfg-pattern matches the cfg set
556 pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
557     eval_condition(cfg, sess, &mut |cfg| {
558         if let (Some(feats), Some(gated_cfg)) = (features, GatedCfg::gate(cfg)) {
559             gated_cfg.check_and_emit(sess, feats);
560         }
561         let error = |span, msg| { sess.span_diagnostic.span_err(span, msg); true };
562         if cfg.path.segments.len() != 1 {
563             return error(cfg.path.span, "`cfg` predicate key must be an identifier");
564         }
565         match &cfg.kind {
566             MetaItemKind::List(..) => {
567                 error(cfg.span, "unexpected parentheses after `cfg` predicate key")
568             }
569             MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
570                 handle_errors(
571                     sess,
572                     lit.span,
573                     AttrError::UnsupportedLiteral(
574                         "literal in `cfg` predicate value must be a string",
575                         lit.kind.is_bytestr()
576                     ),
577                 );
578                 true
579             }
580             MetaItemKind::NameValue(..) | MetaItemKind::Word => {
581                 let ident = cfg.ident().expect("multi-segment cfg predicate");
582                 sess.config.contains(&(ident.name, cfg.value_str()))
583             }
584         }
585     })
586 }
587
588 /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
589 /// evaluate individual items.
590 pub fn eval_condition<F>(cfg: &ast::MetaItem, sess: &ParseSess, eval: &mut F)
591                          -> bool
592     where F: FnMut(&ast::MetaItem) -> bool
593 {
594     match cfg.kind {
595         ast::MetaItemKind::List(ref mis) => {
596             for mi in mis.iter() {
597                 if !mi.is_meta_item() {
598                     handle_errors(
599                         sess,
600                         mi.span(),
601                         AttrError::UnsupportedLiteral(
602                             "unsupported literal",
603                             false
604                         ),
605                     );
606                     return false;
607                 }
608             }
609
610             // The unwraps below may look dangerous, but we've already asserted
611             // that they won't fail with the loop above.
612             match cfg.name_or_empty() {
613                 sym::any => mis.iter().any(|mi| {
614                     eval_condition(mi.meta_item().unwrap(), sess, eval)
615                 }),
616                 sym::all => mis.iter().all(|mi| {
617                     eval_condition(mi.meta_item().unwrap(), sess, eval)
618                 }),
619                 sym::not => {
620                     if mis.len() != 1 {
621                         span_err!(sess.span_diagnostic, cfg.span, E0536, "expected 1 cfg-pattern");
622                         return false;
623                     }
624
625                     !eval_condition(mis[0].meta_item().unwrap(), sess, eval)
626                 },
627                 _ => {
628                     span_err!(
629                         sess.span_diagnostic, cfg.span, E0537,
630                         "invalid predicate `{}`",
631                         pprust::path_to_string(&cfg.path)
632                     );
633                     false
634                 }
635             }
636         },
637         ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
638             eval(cfg)
639         }
640     }
641 }
642
643 #[derive(RustcEncodable, RustcDecodable, Clone, HashStable_Generic)]
644 pub struct Deprecation {
645     pub since: Option<Symbol>,
646     pub note: Option<Symbol>,
647 }
648
649 /// Finds the deprecation attribute. `None` if none exists.
650 pub fn find_deprecation(sess: &ParseSess, attrs: &[Attribute],
651                         item_sp: Span) -> Option<Deprecation> {
652     find_deprecation_generic(sess, attrs.iter(), item_sp)
653 }
654
655 fn find_deprecation_generic<'a, I>(sess: &ParseSess,
656                                    attrs_iter: I,
657                                    item_sp: Span)
658                                    -> Option<Deprecation>
659     where I: Iterator<Item = &'a Attribute>
660 {
661     let mut depr: Option<Deprecation> = None;
662     let diagnostic = &sess.span_diagnostic;
663
664     'outer: for attr in attrs_iter {
665         if !attr.check_name(sym::deprecated) {
666             continue;
667         }
668
669         if depr.is_some() {
670             span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes");
671             break
672         }
673
674         let meta = match attr.meta() {
675             Some(meta) => meta,
676             None => continue,
677         };
678         depr = match &meta.kind {
679             MetaItemKind::Word => Some(Deprecation { since: None, note: None }),
680             MetaItemKind::NameValue(..) => {
681                 meta.value_str().map(|note| {
682                     Deprecation { since: None, note: Some(note) }
683                 })
684             }
685             MetaItemKind::List(list) => {
686                 let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
687                     if item.is_some() {
688                         handle_errors(
689                             sess,
690                             meta.span,
691                             AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
692                         );
693                         return false
694                     }
695                     if let Some(v) = meta.value_str() {
696                         *item = Some(v);
697                         true
698                     } else {
699                         if let Some(lit) = meta.name_value_literal() {
700                             handle_errors(
701                                 sess,
702                                 lit.span,
703                                 AttrError::UnsupportedLiteral(
704                                     "literal in `deprecated` \
705                                     value must be a string",
706                                     lit.kind.is_bytestr()
707                                 ),
708                             );
709                         } else {
710                             span_err!(diagnostic, meta.span, E0551, "incorrect meta item");
711                         }
712
713                         false
714                     }
715                 };
716
717                 let mut since = None;
718                 let mut note = None;
719                 for meta in list {
720                     match meta {
721                         NestedMetaItem::MetaItem(mi) => {
722                             match mi.name_or_empty() {
723                                 sym::since => if !get(mi, &mut since) { continue 'outer },
724                                 sym::note => if !get(mi, &mut note) { continue 'outer },
725                                 _ => {
726                                     handle_errors(
727                                         sess,
728                                         meta.span(),
729                                         AttrError::UnknownMetaItem(
730                                             pprust::path_to_string(&mi.path),
731                                             &["since", "note"],
732                                         ),
733                                     );
734                                     continue 'outer
735                                 }
736                             }
737                         }
738                         NestedMetaItem::Literal(lit) => {
739                             handle_errors(
740                                 sess,
741                                 lit.span,
742                                 AttrError::UnsupportedLiteral(
743                                     "item in `deprecated` must be a key/value pair",
744                                     false,
745                                 ),
746                             );
747                             continue 'outer
748                         }
749                     }
750                 }
751
752                 Some(Deprecation { since, note })
753             }
754         };
755     }
756
757     depr
758 }
759
760 #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
761 pub enum ReprAttr {
762     ReprInt(IntType),
763     ReprC,
764     ReprPacked(u32),
765     ReprSimd,
766     ReprTransparent,
767     ReprAlign(u32),
768 }
769
770 #[derive(Eq, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone, HashStable_Generic)]
771 pub enum IntType {
772     SignedInt(ast::IntTy),
773     UnsignedInt(ast::UintTy)
774 }
775
776 impl IntType {
777     #[inline]
778     pub fn is_signed(self) -> bool {
779         use IntType::*;
780
781         match self {
782             SignedInt(..) => true,
783             UnsignedInt(..) => false
784         }
785     }
786 }
787
788 /// Parse #[repr(...)] forms.
789 ///
790 /// Valid repr contents: any of the primitive integral type names (see
791 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
792 /// the same discriminant size that the corresponding C enum would or C
793 /// structure layout, `packed` to remove padding, and `transparent` to elegate representation
794 /// concerns to the only non-ZST field.
795 pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
796     use ReprAttr::*;
797
798     let mut acc = Vec::new();
799     let diagnostic = &sess.span_diagnostic;
800     if attr.has_name(sym::repr) {
801         if let Some(items) = attr.meta_item_list() {
802             mark_used(attr);
803             for item in items {
804                 if !item.is_meta_item() {
805                     handle_errors(
806                         sess,
807                         item.span(),
808                         AttrError::UnsupportedLiteral(
809                             "meta item in `repr` must be an identifier",
810                             false,
811                         ),
812                     );
813                     continue
814                 }
815
816                 let mut recognised = false;
817                 if item.is_word() {
818                     let hint = match item.name_or_empty() {
819                         sym::C => Some(ReprC),
820                         sym::packed => Some(ReprPacked(1)),
821                         sym::simd => Some(ReprSimd),
822                         sym::transparent => Some(ReprTransparent),
823                         name => int_type_of_word(name).map(ReprInt),
824                     };
825
826                     if let Some(h) = hint {
827                         recognised = true;
828                         acc.push(h);
829                     }
830                 } else if let Some((name, value)) = item.name_value_literal() {
831                     let parse_alignment = |node: &ast::LitKind| -> Result<u32, &'static str> {
832                         if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
833                             if literal.is_power_of_two() {
834                                 // rustc::ty::layout::Align restricts align to <= 2^29
835                                 if *literal <= 1 << 29 {
836                                     Ok(*literal as u32)
837                                 } else {
838                                     Err("larger than 2^29")
839                                 }
840                             } else {
841                                 Err("not a power of two")
842                             }
843                         } else {
844                             Err("not an unsuffixed integer")
845                         }
846                     };
847
848                     let mut literal_error = None;
849                     if name == sym::align {
850                         recognised = true;
851                         match parse_alignment(&value.kind) {
852                             Ok(literal) => acc.push(ReprAlign(literal)),
853                             Err(message) => literal_error = Some(message)
854                         };
855                     }
856                     else if name == sym::packed {
857                         recognised = true;
858                         match parse_alignment(&value.kind) {
859                             Ok(literal) => acc.push(ReprPacked(literal)),
860                             Err(message) => literal_error = Some(message)
861                         };
862                     }
863                     if let Some(literal_error) = literal_error {
864                         span_err!(diagnostic, item.span(), E0589,
865                                   "invalid `repr(align)` attribute: {}", literal_error);
866                     }
867                 } else {
868                     if let Some(meta_item) = item.meta_item() {
869                         if meta_item.check_name(sym::align) {
870                             if let MetaItemKind::NameValue(ref value) = meta_item.kind {
871                                 recognised = true;
872                                 let mut err = struct_span_err!(diagnostic, item.span(), E0693,
873                                     "incorrect `repr(align)` attribute format");
874                                 match value.kind {
875                                     ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
876                                         err.span_suggestion(
877                                             item.span(),
878                                             "use parentheses instead",
879                                             format!("align({})", int),
880                                             Applicability::MachineApplicable
881                                         );
882                                     }
883                                     ast::LitKind::Str(s, _) => {
884                                         err.span_suggestion(
885                                             item.span(),
886                                             "use parentheses instead",
887                                             format!("align({})", s),
888                                             Applicability::MachineApplicable
889                                         );
890                                     }
891                                     _ => {}
892                                 }
893                                 err.emit();
894                             }
895                         }
896                     }
897                 }
898                 if !recognised {
899                     // Not a word we recognize
900                     span_err!(diagnostic, item.span(), E0552,
901                               "unrecognized representation hint");
902                 }
903             }
904         }
905     }
906     acc
907 }
908
909 fn int_type_of_word(s: Symbol) -> Option<IntType> {
910     use IntType::*;
911
912     match s {
913         sym::i8 => Some(SignedInt(ast::IntTy::I8)),
914         sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
915         sym::i16 => Some(SignedInt(ast::IntTy::I16)),
916         sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
917         sym::i32 => Some(SignedInt(ast::IntTy::I32)),
918         sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
919         sym::i64 => Some(SignedInt(ast::IntTy::I64)),
920         sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
921         sym::i128 => Some(SignedInt(ast::IntTy::I128)),
922         sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
923         sym::isize => Some(SignedInt(ast::IntTy::Isize)),
924         sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
925         _ => None
926     }
927 }
928
929 pub enum TransparencyError {
930     UnknownTransparency(Symbol, Span),
931     MultipleTransparencyAttrs(Span, Span),
932 }
933
934 pub fn find_transparency(
935     attrs: &[Attribute], is_legacy: bool
936 ) -> (Transparency, Option<TransparencyError>) {
937     let mut transparency = None;
938     let mut error = None;
939     for attr in attrs {
940         if attr.check_name(sym::rustc_macro_transparency) {
941             if let Some((_, old_span)) = transparency {
942                 error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span));
943                 break;
944             } else if let Some(value) = attr.value_str() {
945                 transparency = Some((match &*value.as_str() {
946                     "transparent" => Transparency::Transparent,
947                     "semitransparent" => Transparency::SemiTransparent,
948                     "opaque" => Transparency::Opaque,
949                     _ => {
950                         error = Some(TransparencyError::UnknownTransparency(value, attr.span));
951                         continue;
952                     }
953                 }, attr.span));
954             }
955         }
956     }
957     let fallback = if is_legacy { Transparency::SemiTransparent } else { Transparency::Opaque };
958     (transparency.map_or(fallback, |t| t.0), error)
959 }