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