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