]> git.lizzy.rs Git - rust.git/blob - src/librustc_attr/builtin.rs
Merge branch 'E0637' of https://github.com/jwhite927/rust into E0637
[rust.git] / src / librustc_attr / builtin.rs
1 //! Parsing and validation of builtin attributes
2
3 use super::{find_by_name, mark_used};
4
5 use rustc_ast_pretty::pprust;
6 use rustc_errors::{struct_span_err, Applicability, Handler};
7 use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
8 use rustc_macros::HashStable_Generic;
9 use rustc_session::parse::{feature_err, ParseSess};
10 use rustc_span::hygiene::Transparency;
11 use rustc_span::{symbol::sym, symbol::Symbol, Span};
12 use std::num::NonZeroU32;
13 use syntax::ast::{self, Attribute, MetaItem, MetaItemKind, NestedMetaItem};
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 issue_num = None;
375                     let mut is_soft = false;
376                     for meta in metas {
377                         if let Some(mi) = meta.meta_item() {
378                             match mi.name_or_empty() {
379                                 sym::feature => {
380                                     if !get(mi, &mut feature) {
381                                         continue 'outer;
382                                     }
383                                 }
384                                 sym::reason => {
385                                     if !get(mi, &mut reason) {
386                                         continue 'outer;
387                                     }
388                                 }
389                                 sym::issue => {
390                                     if !get(mi, &mut issue) {
391                                         continue 'outer;
392                                     }
393
394                                     // These unwraps are safe because `get` ensures the meta item
395                                     // is a name/value pair string literal.
396                                     issue_num = match &*issue.unwrap().as_str() {
397                                         "none" => None,
398                                         issue => {
399                                             let emit_diag = |msg: &str| {
400                                                 struct_span_err!(
401                                                     diagnostic,
402                                                     mi.span,
403                                                     E0545,
404                                                     "`issue` must be a non-zero numeric string \
405                                                     or \"none\"",
406                                                 )
407                                                 .span_label(
408                                                     mi.name_value_literal().unwrap().span,
409                                                     msg,
410                                                 )
411                                                 .emit();
412                                             };
413                                             match issue.parse() {
414                                                 Ok(num) if num == 0 => {
415                                                     emit_diag(
416                                                         "`issue` must not be \"0\", \
417                                                         use \"none\" instead",
418                                                     );
419                                                     continue 'outer;
420                                                 }
421                                                 Ok(num) => NonZeroU32::new(num),
422                                                 Err(err) => {
423                                                     emit_diag(&err.to_string());
424                                                     continue 'outer;
425                                                 }
426                                             }
427                                         }
428                                     };
429                                 }
430                                 sym::soft => {
431                                     if !mi.is_word() {
432                                         let msg = "`soft` should not have any arguments";
433                                         sess.span_diagnostic.span_err(mi.span, msg);
434                                     }
435                                     is_soft = true;
436                                 }
437                                 _ => {
438                                     handle_errors(
439                                         sess,
440                                         meta.span(),
441                                         AttrError::UnknownMetaItem(
442                                             pprust::path_to_string(&mi.path),
443                                             &["feature", "reason", "issue", "soft"],
444                                         ),
445                                     );
446                                     continue 'outer;
447                                 }
448                             }
449                         } else {
450                             handle_errors(
451                                 sess,
452                                 meta.span(),
453                                 AttrError::UnsupportedLiteral("unsupported literal", false),
454                             );
455                             continue 'outer;
456                         }
457                     }
458
459                     match (feature, reason, issue) {
460                         (Some(feature), reason, Some(_)) => {
461                             let level = Unstable { reason, issue: issue_num, is_soft };
462                             if sym::unstable == meta_name {
463                                 stab = Some(Stability { level, feature, rustc_depr: None });
464                             } else {
465                                 const_stab = Some(ConstStability {
466                                     level,
467                                     feature,
468                                     promotable: false,
469                                     allow_const_fn_ptr: false,
470                                 });
471                             }
472                         }
473                         (None, _, _) => {
474                             handle_errors(sess, attr.span, AttrError::MissingFeature);
475                             continue;
476                         }
477                         _ => {
478                             struct_span_err!(diagnostic, attr.span, E0547, "missing 'issue'")
479                                 .emit();
480                             continue;
481                         }
482                     }
483                 }
484                 sym::rustc_const_stable | sym::stable => {
485                     if meta_name == sym::stable && stab.is_some() {
486                         handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
487                         break;
488                     } else if meta_name == sym::rustc_const_stable && const_stab.is_some() {
489                         handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
490                         break;
491                     }
492
493                     let mut feature = None;
494                     let mut since = None;
495                     for meta in metas {
496                         match meta {
497                             NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() {
498                                 sym::feature => {
499                                     if !get(mi, &mut feature) {
500                                         continue 'outer;
501                                     }
502                                 }
503                                 sym::since => {
504                                     if !get(mi, &mut since) {
505                                         continue 'outer;
506                                     }
507                                 }
508                                 _ => {
509                                     handle_errors(
510                                         sess,
511                                         meta.span(),
512                                         AttrError::UnknownMetaItem(
513                                             pprust::path_to_string(&mi.path),
514                                             &["since", "note"],
515                                         ),
516                                     );
517                                     continue 'outer;
518                                 }
519                             },
520                             NestedMetaItem::Literal(lit) => {
521                                 handle_errors(
522                                     sess,
523                                     lit.span,
524                                     AttrError::UnsupportedLiteral("unsupported literal", false),
525                                 );
526                                 continue 'outer;
527                             }
528                         }
529                     }
530
531                     match (feature, since) {
532                         (Some(feature), Some(since)) => {
533                             let level = Stable { since };
534                             if sym::stable == meta_name {
535                                 stab = Some(Stability { level, feature, rustc_depr: None });
536                             } else {
537                                 const_stab = Some(ConstStability {
538                                     level,
539                                     feature,
540                                     promotable: false,
541                                     allow_const_fn_ptr: false,
542                                 });
543                             }
544                         }
545                         (None, _) => {
546                             handle_errors(sess, attr.span, AttrError::MissingFeature);
547                             continue;
548                         }
549                         _ => {
550                             handle_errors(sess, attr.span, AttrError::MissingSince);
551                             continue;
552                         }
553                     }
554                 }
555                 _ => unreachable!(),
556             }
557         }
558     }
559
560     // Merge the deprecation info into the stability info
561     if let Some(rustc_depr) = rustc_depr {
562         if let Some(ref mut stab) = stab {
563             stab.rustc_depr = Some(rustc_depr);
564         } else {
565             struct_span_err!(
566                 diagnostic,
567                 item_sp,
568                 E0549,
569                 "rustc_deprecated attribute must be paired with \
570                        either stable or unstable attribute"
571             )
572             .emit();
573         }
574     }
575
576     // Merge the const-unstable info into the stability info
577     if promotable || allow_const_fn_ptr {
578         if let Some(ref mut stab) = const_stab {
579             stab.promotable = promotable;
580             stab.allow_const_fn_ptr = allow_const_fn_ptr;
581         } else {
582             struct_span_err!(
583                 diagnostic,
584                 item_sp,
585                 E0717,
586                 "rustc_promotable and rustc_allow_const_fn_ptr attributes \
587                       must be paired with either a rustc_const_unstable or a rustc_const_stable \
588                       attribute"
589             )
590             .emit();
591         }
592     }
593
594     (stab, const_stab)
595 }
596
597 pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
598     super::first_attr_value_str_by_name(attrs, sym::crate_name)
599 }
600
601 /// Tests if a cfg-pattern matches the cfg set
602 pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) -> bool {
603     eval_condition(cfg, sess, &mut |cfg| {
604         let gate = find_gated_cfg(|sym| cfg.check_name(sym));
605         if let (Some(feats), Some(gated_cfg)) = (features, gate) {
606             gate_cfg(&gated_cfg, cfg.span, sess, feats);
607         }
608         let error = |span, msg| {
609             sess.span_diagnostic.span_err(span, msg);
610             true
611         };
612         if cfg.path.segments.len() != 1 {
613             return error(cfg.path.span, "`cfg` predicate key must be an identifier");
614         }
615         match &cfg.kind {
616             MetaItemKind::List(..) => {
617                 error(cfg.span, "unexpected parentheses after `cfg` predicate key")
618             }
619             MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
620                 handle_errors(
621                     sess,
622                     lit.span,
623                     AttrError::UnsupportedLiteral(
624                         "literal in `cfg` predicate value must be a string",
625                         lit.kind.is_bytestr(),
626                     ),
627                 );
628                 true
629             }
630             MetaItemKind::NameValue(..) | MetaItemKind::Word => {
631                 let ident = cfg.ident().expect("multi-segment cfg predicate");
632                 sess.config.contains(&(ident.name, cfg.value_str()))
633             }
634         }
635     })
636 }
637
638 fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &Features) {
639     let (cfg, feature, has_feature) = gated_cfg;
640     if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
641         let explain = format!("`cfg({})` is experimental and subject to change", cfg);
642         feature_err(sess, *feature, cfg_span, &explain).emit()
643     }
644 }
645
646 /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
647 /// evaluate individual items.
648 pub fn eval_condition(
649     cfg: &ast::MetaItem,
650     sess: &ParseSess,
651     eval: &mut impl FnMut(&ast::MetaItem) -> bool,
652 ) -> bool {
653     match cfg.kind {
654         ast::MetaItemKind::List(ref mis) => {
655             for mi in mis.iter() {
656                 if !mi.is_meta_item() {
657                     handle_errors(
658                         sess,
659                         mi.span(),
660                         AttrError::UnsupportedLiteral("unsupported literal", false),
661                     );
662                     return false;
663                 }
664             }
665
666             // The unwraps below may look dangerous, but we've already asserted
667             // that they won't fail with the loop above.
668             match cfg.name_or_empty() {
669                 sym::any => {
670                     mis.iter().any(|mi| eval_condition(mi.meta_item().unwrap(), sess, eval))
671                 }
672                 sym::all => {
673                     mis.iter().all(|mi| eval_condition(mi.meta_item().unwrap(), sess, eval))
674                 }
675                 sym::not => {
676                     if mis.len() != 1 {
677                         struct_span_err!(
678                             sess.span_diagnostic,
679                             cfg.span,
680                             E0536,
681                             "expected 1 cfg-pattern"
682                         )
683                         .emit();
684                         return false;
685                     }
686
687                     !eval_condition(mis[0].meta_item().unwrap(), sess, eval)
688                 }
689                 _ => {
690                     struct_span_err!(
691                         sess.span_diagnostic,
692                         cfg.span,
693                         E0537,
694                         "invalid predicate `{}`",
695                         pprust::path_to_string(&cfg.path)
696                     )
697                     .emit();
698                     false
699                 }
700             }
701         }
702         ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => eval(cfg),
703     }
704 }
705
706 #[derive(RustcEncodable, RustcDecodable, Clone, HashStable_Generic)]
707 pub struct Deprecation {
708     pub since: Option<Symbol>,
709     pub note: Option<Symbol>,
710 }
711
712 /// Finds the deprecation attribute. `None` if none exists.
713 pub fn find_deprecation(
714     sess: &ParseSess,
715     attrs: &[Attribute],
716     item_sp: Span,
717 ) -> Option<Deprecation> {
718     find_deprecation_generic(sess, attrs.iter(), item_sp)
719 }
720
721 fn find_deprecation_generic<'a, I>(
722     sess: &ParseSess,
723     attrs_iter: I,
724     item_sp: Span,
725 ) -> Option<Deprecation>
726 where
727     I: Iterator<Item = &'a Attribute>,
728 {
729     let mut depr: Option<Deprecation> = None;
730     let diagnostic = &sess.span_diagnostic;
731
732     'outer: for attr in attrs_iter {
733         if !attr.check_name(sym::deprecated) {
734             continue;
735         }
736
737         if depr.is_some() {
738             struct_span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes").emit();
739             break;
740         }
741
742         let meta = match attr.meta() {
743             Some(meta) => meta,
744             None => continue,
745         };
746         depr = match &meta.kind {
747             MetaItemKind::Word => Some(Deprecation { since: None, note: None }),
748             MetaItemKind::NameValue(..) => {
749                 meta.value_str().map(|note| Deprecation { since: None, note: Some(note) })
750             }
751             MetaItemKind::List(list) => {
752                 let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
753                     if item.is_some() {
754                         handle_errors(
755                             sess,
756                             meta.span,
757                             AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
758                         );
759                         return false;
760                     }
761                     if let Some(v) = meta.value_str() {
762                         *item = Some(v);
763                         true
764                     } else {
765                         if let Some(lit) = meta.name_value_literal() {
766                             handle_errors(
767                                 sess,
768                                 lit.span,
769                                 AttrError::UnsupportedLiteral(
770                                     "literal in `deprecated` \
771                                     value must be a string",
772                                     lit.kind.is_bytestr(),
773                                 ),
774                             );
775                         } else {
776                             struct_span_err!(diagnostic, meta.span, E0551, "incorrect meta item")
777                                 .emit();
778                         }
779
780                         false
781                     }
782                 };
783
784                 let mut since = None;
785                 let mut note = None;
786                 for meta in list {
787                     match meta {
788                         NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() {
789                             sym::since => {
790                                 if !get(mi, &mut since) {
791                                     continue 'outer;
792                                 }
793                             }
794                             sym::note => {
795                                 if !get(mi, &mut note) {
796                                     continue 'outer;
797                                 }
798                             }
799                             _ => {
800                                 handle_errors(
801                                     sess,
802                                     meta.span(),
803                                     AttrError::UnknownMetaItem(
804                                         pprust::path_to_string(&mi.path),
805                                         &["since", "note"],
806                                     ),
807                                 );
808                                 continue 'outer;
809                             }
810                         },
811                         NestedMetaItem::Literal(lit) => {
812                             handle_errors(
813                                 sess,
814                                 lit.span,
815                                 AttrError::UnsupportedLiteral(
816                                     "item in `deprecated` must be a key/value pair",
817                                     false,
818                                 ),
819                             );
820                             continue 'outer;
821                         }
822                     }
823                 }
824
825                 Some(Deprecation { since, note })
826             }
827         };
828     }
829
830     depr
831 }
832
833 #[derive(PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone)]
834 pub enum ReprAttr {
835     ReprInt(IntType),
836     ReprC,
837     ReprPacked(u32),
838     ReprSimd,
839     ReprTransparent,
840     ReprAlign(u32),
841 }
842
843 #[derive(Eq, PartialEq, Debug, RustcEncodable, RustcDecodable, Copy, Clone, HashStable_Generic)]
844 pub enum IntType {
845     SignedInt(ast::IntTy),
846     UnsignedInt(ast::UintTy),
847 }
848
849 impl IntType {
850     #[inline]
851     pub fn is_signed(self) -> bool {
852         use IntType::*;
853
854         match self {
855             SignedInt(..) => true,
856             UnsignedInt(..) => false,
857         }
858     }
859 }
860
861 /// Parse #[repr(...)] forms.
862 ///
863 /// Valid repr contents: any of the primitive integral type names (see
864 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
865 /// the same discriminant size that the corresponding C enum would or C
866 /// structure layout, `packed` to remove padding, and `transparent` to elegate representation
867 /// concerns to the only non-ZST field.
868 pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
869     use ReprAttr::*;
870
871     let mut acc = Vec::new();
872     let diagnostic = &sess.span_diagnostic;
873     if attr.has_name(sym::repr) {
874         if let Some(items) = attr.meta_item_list() {
875             mark_used(attr);
876             for item in items {
877                 if !item.is_meta_item() {
878                     handle_errors(
879                         sess,
880                         item.span(),
881                         AttrError::UnsupportedLiteral(
882                             "meta item in `repr` must be an identifier",
883                             false,
884                         ),
885                     );
886                     continue;
887                 }
888
889                 let mut recognised = false;
890                 if item.is_word() {
891                     let hint = match item.name_or_empty() {
892                         sym::C => Some(ReprC),
893                         sym::packed => Some(ReprPacked(1)),
894                         sym::simd => Some(ReprSimd),
895                         sym::transparent => Some(ReprTransparent),
896                         name => int_type_of_word(name).map(ReprInt),
897                     };
898
899                     if let Some(h) = hint {
900                         recognised = true;
901                         acc.push(h);
902                     }
903                 } else if let Some((name, value)) = item.name_value_literal() {
904                     let parse_alignment = |node: &ast::LitKind| -> Result<u32, &'static str> {
905                         if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
906                             if literal.is_power_of_two() {
907                                 // rustc::ty::layout::Align restricts align to <= 2^29
908                                 if *literal <= 1 << 29 {
909                                     Ok(*literal as u32)
910                                 } else {
911                                     Err("larger than 2^29")
912                                 }
913                             } else {
914                                 Err("not a power of two")
915                             }
916                         } else {
917                             Err("not an unsuffixed integer")
918                         }
919                     };
920
921                     let mut literal_error = None;
922                     if name == sym::align {
923                         recognised = true;
924                         match parse_alignment(&value.kind) {
925                             Ok(literal) => acc.push(ReprAlign(literal)),
926                             Err(message) => literal_error = Some(message),
927                         };
928                     } else if name == sym::packed {
929                         recognised = true;
930                         match parse_alignment(&value.kind) {
931                             Ok(literal) => acc.push(ReprPacked(literal)),
932                             Err(message) => literal_error = Some(message),
933                         };
934                     }
935                     if let Some(literal_error) = literal_error {
936                         struct_span_err!(
937                             diagnostic,
938                             item.span(),
939                             E0589,
940                             "invalid `repr(align)` attribute: {}",
941                             literal_error
942                         )
943                         .emit();
944                     }
945                 } else {
946                     if let Some(meta_item) = item.meta_item() {
947                         if meta_item.check_name(sym::align) {
948                             if let MetaItemKind::NameValue(ref value) = meta_item.kind {
949                                 recognised = true;
950                                 let mut err = struct_span_err!(
951                                     diagnostic,
952                                     item.span(),
953                                     E0693,
954                                     "incorrect `repr(align)` attribute format"
955                                 );
956                                 match value.kind {
957                                     ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
958                                         err.span_suggestion(
959                                             item.span(),
960                                             "use parentheses instead",
961                                             format!("align({})", int),
962                                             Applicability::MachineApplicable,
963                                         );
964                                     }
965                                     ast::LitKind::Str(s, _) => {
966                                         err.span_suggestion(
967                                             item.span(),
968                                             "use parentheses instead",
969                                             format!("align({})", s),
970                                             Applicability::MachineApplicable,
971                                         );
972                                     }
973                                     _ => {}
974                                 }
975                                 err.emit();
976                             }
977                         }
978                     }
979                 }
980                 if !recognised {
981                     // Not a word we recognize
982                     struct_span_err!(
983                         diagnostic,
984                         item.span(),
985                         E0552,
986                         "unrecognized representation hint"
987                     )
988                     .emit();
989                 }
990             }
991         }
992     }
993     acc
994 }
995
996 fn int_type_of_word(s: Symbol) -> Option<IntType> {
997     use IntType::*;
998
999     match s {
1000         sym::i8 => Some(SignedInt(ast::IntTy::I8)),
1001         sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
1002         sym::i16 => Some(SignedInt(ast::IntTy::I16)),
1003         sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
1004         sym::i32 => Some(SignedInt(ast::IntTy::I32)),
1005         sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
1006         sym::i64 => Some(SignedInt(ast::IntTy::I64)),
1007         sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
1008         sym::i128 => Some(SignedInt(ast::IntTy::I128)),
1009         sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
1010         sym::isize => Some(SignedInt(ast::IntTy::Isize)),
1011         sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
1012         _ => None,
1013     }
1014 }
1015
1016 pub enum TransparencyError {
1017     UnknownTransparency(Symbol, Span),
1018     MultipleTransparencyAttrs(Span, Span),
1019 }
1020
1021 pub fn find_transparency(
1022     attrs: &[Attribute],
1023     is_legacy: bool,
1024 ) -> (Transparency, Option<TransparencyError>) {
1025     let mut transparency = None;
1026     let mut error = None;
1027     for attr in attrs {
1028         if attr.check_name(sym::rustc_macro_transparency) {
1029             if let Some((_, old_span)) = transparency {
1030                 error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span));
1031                 break;
1032             } else if let Some(value) = attr.value_str() {
1033                 transparency = Some((
1034                     match &*value.as_str() {
1035                         "transparent" => Transparency::Transparent,
1036                         "semitransparent" => Transparency::SemiTransparent,
1037                         "opaque" => Transparency::Opaque,
1038                         _ => {
1039                             error = Some(TransparencyError::UnknownTransparency(value, attr.span));
1040                             continue;
1041                         }
1042                     },
1043                     attr.span,
1044                 ));
1045             }
1046         }
1047     }
1048     let fallback = if is_legacy { Transparency::SemiTransparent } else { Transparency::Opaque };
1049     (transparency.map_or(fallback, |t| t.0), error)
1050 }
1051
1052 pub fn allow_internal_unstable<'a>(
1053     attrs: &[Attribute],
1054     diag: &'a rustc_errors::Handler,
1055 ) -> Option<impl Iterator<Item = Symbol> + 'a> {
1056     let attr = find_by_name(attrs, sym::allow_internal_unstable)?;
1057     let list = attr.meta_item_list().or_else(|| {
1058         diag.span_err(attr.span, "allow_internal_unstable expects list of feature names");
1059         None
1060     })?;
1061     Some(list.into_iter().filter_map(move |it| {
1062         let name = it.ident().map(|ident| ident.name);
1063         if name.is_none() {
1064             diag.span_err(it.span(), "`allow_internal_unstable` expects feature names");
1065         }
1066         name
1067     }))
1068 }