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