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