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