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