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