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