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