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