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