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