]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_attr/src/builtin.rs
Auto merge of #106646 - Amanieu:ilp32-object, r=Mark-Simulacrum
[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(meta @ MetaItem { kind: MetaItemKind::List(metas), .. }) = &meta {
281             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
282                 if item.is_some() {
283                     handle_errors(
284                         &sess.parse_sess,
285                         meta.span,
286                         AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
287                     );
288                     return false;
289                 }
290                 if let Some(v) = meta.value_str() {
291                     *item = Some(v);
292                     true
293                 } else {
294                     sess.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
295                     false
296                 }
297             };
298
299             let meta_name = meta.name_or_empty();
300             match meta_name {
301                 sym::rustc_const_unstable | sym::rustc_default_body_unstable | sym::unstable => {
302                     if meta_name == sym::unstable && stab.is_some() {
303                         handle_errors(
304                             &sess.parse_sess,
305                             attr.span,
306                             AttrError::MultipleStabilityLevels,
307                         );
308                         break;
309                     } else if meta_name == sym::rustc_const_unstable && const_stab.is_some() {
310                         handle_errors(
311                             &sess.parse_sess,
312                             attr.span,
313                             AttrError::MultipleStabilityLevels,
314                         );
315                         break;
316                     } else if meta_name == sym::rustc_default_body_unstable && body_stab.is_some() {
317                         handle_errors(
318                             &sess.parse_sess,
319                             attr.span,
320                             AttrError::MultipleStabilityLevels,
321                         );
322                         break;
323                     }
324
325                     let mut feature = None;
326                     let mut reason = None;
327                     let mut issue = None;
328                     let mut issue_num = None;
329                     let mut is_soft = false;
330                     let mut implied_by = None;
331                     for meta in metas {
332                         let Some(mi) = meta.meta_item() else {
333                             handle_errors(
334                                 &sess.parse_sess,
335                                 meta.span(),
336                                 AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
337                             );
338                             continue 'outer;
339                         };
340                         match mi.name_or_empty() {
341                             sym::feature => {
342                                 if !get(mi, &mut feature) {
343                                     continue 'outer;
344                                 }
345                             }
346                             sym::reason => {
347                                 if !get(mi, &mut reason) {
348                                     continue 'outer;
349                                 }
350                             }
351                             sym::issue => {
352                                 if !get(mi, &mut issue) {
353                                     continue 'outer;
354                                 }
355
356                                 // These unwraps are safe because `get` ensures the meta item
357                                 // is a name/value pair string literal.
358                                 issue_num = match issue.unwrap().as_str() {
359                                     "none" => None,
360                                     issue => match issue.parse::<NonZeroU32>() {
361                                         Ok(num) => Some(num),
362                                         Err(err) => {
363                                             sess.emit_err(
364                                                 session_diagnostics::InvalidIssueString {
365                                                     span: mi.span,
366                                                     cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
367                                                         mi.name_value_literal_span().unwrap(),
368                                                         err.kind(),
369                                                     ),
370                                                 },
371                                             );
372                                             continue 'outer;
373                                         }
374                                     },
375                                 };
376                             }
377                             sym::soft => {
378                                 if !mi.is_word() {
379                                     sess.emit_err(session_diagnostics::SoftNoArgs {
380                                         span: mi.span,
381                                     });
382                                 }
383                                 is_soft = true;
384                             }
385                             sym::implied_by => {
386                                 if !get(mi, &mut implied_by) {
387                                     continue 'outer;
388                                 }
389                             }
390                             _ => {
391                                 handle_errors(
392                                     &sess.parse_sess,
393                                     meta.span(),
394                                     AttrError::UnknownMetaItem(
395                                         pprust::path_to_string(&mi.path),
396                                         &["feature", "reason", "issue", "soft"],
397                                     ),
398                                 );
399                                 continue 'outer;
400                             }
401                         }
402                     }
403
404                     match (feature, reason, issue) {
405                         (Some(feature), reason, Some(_)) => {
406                             if !rustc_lexer::is_ident(feature.as_str()) {
407                                 handle_errors(
408                                     &sess.parse_sess,
409                                     attr.span,
410                                     AttrError::NonIdentFeature,
411                                 );
412                                 continue;
413                             }
414                             let level = Unstable {
415                                 reason: UnstableReason::from_opt_reason(reason),
416                                 issue: issue_num,
417                                 is_soft,
418                                 implied_by,
419                             };
420                             if sym::unstable == meta_name {
421                                 stab = Some((Stability { level, feature }, attr.span));
422                             } else if sym::rustc_const_unstable == meta_name {
423                                 const_stab = Some((
424                                     ConstStability { level, feature, promotable: false },
425                                     attr.span,
426                                 ));
427                             } else if sym::rustc_default_body_unstable == meta_name {
428                                 body_stab =
429                                     Some((DefaultBodyStability { level, feature }, attr.span));
430                             } else {
431                                 unreachable!("Unknown stability attribute {meta_name}");
432                             }
433                         }
434                         (None, _, _) => {
435                             handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
436                             continue;
437                         }
438                         _ => {
439                             sess.emit_err(session_diagnostics::MissingIssue { span: attr.span });
440                             continue;
441                         }
442                     }
443                 }
444                 sym::rustc_const_stable | sym::stable => {
445                     if meta_name == sym::stable && stab.is_some() {
446                         handle_errors(
447                             &sess.parse_sess,
448                             attr.span,
449                             AttrError::MultipleStabilityLevels,
450                         );
451                         break;
452                     } else if meta_name == sym::rustc_const_stable && const_stab.is_some() {
453                         handle_errors(
454                             &sess.parse_sess,
455                             attr.span,
456                             AttrError::MultipleStabilityLevels,
457                         );
458                         break;
459                     }
460
461                     let mut feature = None;
462                     let mut since = None;
463                     for meta in metas {
464                         match meta {
465                             NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() {
466                                 sym::feature => {
467                                     if !get(mi, &mut feature) {
468                                         continue 'outer;
469                                     }
470                                 }
471                                 sym::since => {
472                                     if !get(mi, &mut since) {
473                                         continue 'outer;
474                                     }
475                                 }
476                                 _ => {
477                                     handle_errors(
478                                         &sess.parse_sess,
479                                         meta.span(),
480                                         AttrError::UnknownMetaItem(
481                                             pprust::path_to_string(&mi.path),
482                                             &["feature", "since"],
483                                         ),
484                                     );
485                                     continue 'outer;
486                                 }
487                             },
488                             NestedMetaItem::Lit(lit) => {
489                                 handle_errors(
490                                     &sess.parse_sess,
491                                     lit.span,
492                                     AttrError::UnsupportedLiteral(
493                                         UnsupportedLiteralReason::Generic,
494                                         false,
495                                     ),
496                                 );
497                                 continue 'outer;
498                             }
499                         }
500                     }
501
502                     if let Some(s) = since && s.as_str() == VERSION_PLACEHOLDER {
503                         since = Some(rust_version_symbol());
504                     }
505
506                     match (feature, since) {
507                         (Some(feature), Some(since)) => {
508                             let level = Stable { since, allowed_through_unstable_modules: false };
509                             if sym::stable == meta_name {
510                                 stab = Some((Stability { level, feature }, attr.span));
511                             } else {
512                                 const_stab = Some((
513                                     ConstStability { level, feature, promotable: false },
514                                     attr.span,
515                                 ));
516                             }
517                         }
518                         (None, _) => {
519                             handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
520                             continue;
521                         }
522                         _ => {
523                             handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
524                             continue;
525                         }
526                     }
527                 }
528                 _ => unreachable!(),
529             }
530         }
531     }
532
533     // Merge the const-unstable info into the stability info
534     if promotable {
535         match &mut const_stab {
536             Some((stab, _)) => stab.promotable = promotable,
537             _ => _ = sess.emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp }),
538         }
539     }
540
541     if allowed_through_unstable_modules {
542         match &mut stab {
543             Some((
544                 Stability {
545                     level: StabilityLevel::Stable { allowed_through_unstable_modules, .. },
546                     ..
547                 },
548                 _,
549             )) => *allowed_through_unstable_modules = true,
550             _ => {
551                 sess.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
552             }
553         }
554     }
555
556     (stab, const_stab, body_stab)
557 }
558
559 pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option<Symbol> {
560     sess.first_attr_value_str_by_name(attrs, sym::crate_name)
561 }
562
563 #[derive(Clone, Debug)]
564 pub struct Condition {
565     pub name: Symbol,
566     pub name_span: Span,
567     pub value: Option<Symbol>,
568     pub value_span: Option<Span>,
569     pub span: Span,
570 }
571
572 /// Tests if a cfg-pattern matches the cfg set
573 pub fn cfg_matches(
574     cfg: &ast::MetaItem,
575     sess: &ParseSess,
576     lint_node_id: NodeId,
577     features: Option<&Features>,
578 ) -> bool {
579     eval_condition(cfg, sess, features, &mut |cfg| {
580         try_gate_cfg(cfg.name, cfg.span, sess, features);
581         if let Some(names_valid) = &sess.check_config.names_valid {
582             if !names_valid.contains(&cfg.name) {
583                 sess.buffer_lint_with_diagnostic(
584                     UNEXPECTED_CFGS,
585                     cfg.span,
586                     lint_node_id,
587                     "unexpected `cfg` condition name",
588                     BuiltinLintDiagnostics::UnexpectedCfg((cfg.name, cfg.name_span), None),
589                 );
590             }
591         }
592         if let Some(value) = cfg.value {
593             if let Some(values) = &sess.check_config.values_valid.get(&cfg.name) {
594                 if !values.contains(&value) {
595                     sess.buffer_lint_with_diagnostic(
596                         UNEXPECTED_CFGS,
597                         cfg.span,
598                         lint_node_id,
599                         "unexpected `cfg` condition value",
600                         BuiltinLintDiagnostics::UnexpectedCfg(
601                             (cfg.name, cfg.name_span),
602                             cfg.value_span.map(|vs| (value, vs)),
603                         ),
604                     );
605                 }
606             }
607         }
608         sess.config.contains(&(cfg.name, cfg.value))
609     })
610 }
611
612 fn try_gate_cfg(name: Symbol, span: Span, sess: &ParseSess, features: Option<&Features>) {
613     let gate = find_gated_cfg(|sym| sym == name);
614     if let (Some(feats), Some(gated_cfg)) = (features, gate) {
615         gate_cfg(&gated_cfg, span, sess, feats);
616     }
617 }
618
619 fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &Features) {
620     let (cfg, feature, has_feature) = gated_cfg;
621     if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
622         let explain = format!("`cfg({cfg})` is experimental and subject to change");
623         feature_err(sess, *feature, cfg_span, &explain).emit();
624     }
625 }
626
627 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
628 struct Version {
629     major: u16,
630     minor: u16,
631     patch: u16,
632 }
633
634 fn parse_version(s: &str, allow_appendix: bool) -> Option<Version> {
635     let mut components = s.split('-');
636     let d = components.next()?;
637     if !allow_appendix && components.next().is_some() {
638         return None;
639     }
640     let mut digits = d.splitn(3, '.');
641     let major = digits.next()?.parse().ok()?;
642     let minor = digits.next()?.parse().ok()?;
643     let patch = digits.next().unwrap_or("0").parse().ok()?;
644     Some(Version { major, minor, patch })
645 }
646
647 /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
648 /// evaluate individual items.
649 pub fn eval_condition(
650     cfg: &ast::MetaItem,
651     sess: &ParseSess,
652     features: Option<&Features>,
653     eval: &mut impl FnMut(Condition) -> bool,
654 ) -> bool {
655     match &cfg.kind {
656         ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
657             try_gate_cfg(sym::version, cfg.span, sess, features);
658             let (min_version, span) = match &mis[..] {
659                 [NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
660                     (sym, span)
661                 }
662                 [
663                     NestedMetaItem::Lit(MetaItemLit { span, .. })
664                     | NestedMetaItem::MetaItem(MetaItem { span, .. }),
665                 ] => {
666                     sess.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
667                     return false;
668                 }
669                 [..] => {
670                     sess.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
671                         span: cfg.span,
672                     });
673                     return false;
674                 }
675             };
676             let Some(min_version) = parse_version(min_version.as_str(), false) else {
677                 sess.emit_warning(session_diagnostics::UnknownVersionLiteral { span: *span });
678                 return false;
679             };
680             let rustc_version = parse_version(env!("CFG_RELEASE"), true).unwrap();
681
682             // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
683             if sess.assume_incomplete_release {
684                 rustc_version > min_version
685             } else {
686                 rustc_version >= min_version
687             }
688         }
689         ast::MetaItemKind::List(mis) => {
690             for mi in mis.iter() {
691                 if !mi.is_meta_item() {
692                     handle_errors(
693                         sess,
694                         mi.span(),
695                         AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
696                     );
697                     return false;
698                 }
699             }
700
701             // The unwraps below may look dangerous, but we've already asserted
702             // that they won't fail with the loop above.
703             match cfg.name_or_empty() {
704                 sym::any => mis
705                     .iter()
706                     // We don't use any() here, because we want to evaluate all cfg condition
707                     // as eval_condition can (and does) extra checks
708                     .fold(false, |res, mi| {
709                         res | eval_condition(mi.meta_item().unwrap(), sess, features, eval)
710                     }),
711                 sym::all => mis
712                     .iter()
713                     // We don't use all() here, because we want to evaluate all cfg condition
714                     // as eval_condition can (and does) extra checks
715                     .fold(true, |res, mi| {
716                         res & eval_condition(mi.meta_item().unwrap(), sess, features, eval)
717                     }),
718                 sym::not => {
719                     if mis.len() != 1 {
720                         sess.emit_err(session_diagnostics::ExpectedOneCfgPattern {
721                             span: cfg.span,
722                         });
723                         return false;
724                     }
725
726                     !eval_condition(mis[0].meta_item().unwrap(), sess, features, eval)
727                 }
728                 sym::target => {
729                     if let Some(features) = features && !features.cfg_target_compact {
730                         feature_err(
731                             sess,
732                             sym::cfg_target_compact,
733                             cfg.span,
734                             &"compact `cfg(target(..))` is experimental and subject to change"
735                         ).emit();
736                     }
737
738                     mis.iter().fold(true, |res, mi| {
739                         let mut mi = mi.meta_item().unwrap().clone();
740                         if let [seg, ..] = &mut mi.path.segments[..] {
741                             seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
742                         }
743
744                         res & eval_condition(&mi, sess, features, eval)
745                     })
746                 }
747                 _ => {
748                     sess.emit_err(session_diagnostics::InvalidPredicate {
749                         span: cfg.span,
750                         predicate: pprust::path_to_string(&cfg.path),
751                     });
752                     false
753                 }
754             }
755         }
756         ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
757             sess.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
758             true
759         }
760         MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
761             handle_errors(
762                 sess,
763                 lit.span,
764                 AttrError::UnsupportedLiteral(
765                     UnsupportedLiteralReason::CfgString,
766                     lit.kind.is_bytestr(),
767                 ),
768             );
769             true
770         }
771         ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
772             let ident = cfg.ident().expect("multi-segment cfg predicate");
773             eval(Condition {
774                 name: ident.name,
775                 name_span: ident.span,
776                 value: cfg.value_str(),
777                 value_span: cfg.name_value_literal_span(),
778                 span: cfg.span,
779             })
780         }
781     }
782 }
783
784 #[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
785 pub struct Deprecation {
786     pub since: Option<Symbol>,
787     /// The note to issue a reason.
788     pub note: Option<Symbol>,
789     /// A text snippet used to completely replace any use of the deprecated item in an expression.
790     ///
791     /// This is currently unstable.
792     pub suggestion: Option<Symbol>,
793
794     /// Whether to treat the since attribute as being a Rust version identifier
795     /// (rather than an opaque string).
796     pub is_since_rustc_version: bool,
797 }
798
799 /// Finds the deprecation attribute. `None` if none exists.
800 pub fn find_deprecation(sess: &Session, attrs: &[Attribute]) -> Option<(Deprecation, Span)> {
801     find_deprecation_generic(sess, attrs.iter())
802 }
803
804 fn find_deprecation_generic<'a, I>(sess: &Session, attrs_iter: I) -> Option<(Deprecation, Span)>
805 where
806     I: Iterator<Item = &'a Attribute>,
807 {
808     let mut depr: Option<(Deprecation, Span)> = None;
809     let is_rustc = sess.features_untracked().staged_api;
810
811     'outer: for attr in attrs_iter {
812         if !attr.has_name(sym::deprecated) {
813             continue;
814         }
815
816         let Some(meta) = attr.meta() else {
817             continue;
818         };
819         let mut since = None;
820         let mut note = None;
821         let mut suggestion = None;
822         match &meta.kind {
823             MetaItemKind::Word => {}
824             MetaItemKind::NameValue(..) => note = meta.value_str(),
825             MetaItemKind::List(list) => {
826                 let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
827                     if item.is_some() {
828                         handle_errors(
829                             &sess.parse_sess,
830                             meta.span,
831                             AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
832                         );
833                         return false;
834                     }
835                     if let Some(v) = meta.value_str() {
836                         *item = Some(v);
837                         true
838                     } else {
839                         if let Some(lit) = meta.name_value_literal() {
840                             handle_errors(
841                                 &sess.parse_sess,
842                                 lit.span,
843                                 AttrError::UnsupportedLiteral(
844                                     UnsupportedLiteralReason::DeprecatedString,
845                                     lit.kind.is_bytestr(),
846                                 ),
847                             );
848                         } else {
849                             sess.emit_err(session_diagnostics::IncorrectMetaItem2 {
850                                 span: meta.span,
851                             });
852                         }
853
854                         false
855                     }
856                 };
857
858                 for meta in list {
859                     match meta {
860                         NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() {
861                             sym::since => {
862                                 if !get(mi, &mut since) {
863                                     continue 'outer;
864                                 }
865                             }
866                             sym::note => {
867                                 if !get(mi, &mut note) {
868                                     continue 'outer;
869                                 }
870                             }
871                             sym::suggestion => {
872                                 if !sess.features_untracked().deprecated_suggestion {
873                                     sess.emit_err(session_diagnostics::DeprecatedItemSuggestion {
874                                         span: mi.span,
875                                         is_nightly: sess.is_nightly_build().then_some(()),
876                                         details: (),
877                                     });
878                                 }
879
880                                 if !get(mi, &mut suggestion) {
881                                     continue 'outer;
882                                 }
883                             }
884                             _ => {
885                                 handle_errors(
886                                     &sess.parse_sess,
887                                     meta.span(),
888                                     AttrError::UnknownMetaItem(
889                                         pprust::path_to_string(&mi.path),
890                                         if sess.features_untracked().deprecated_suggestion {
891                                             &["since", "note", "suggestion"]
892                                         } else {
893                                             &["since", "note"]
894                                         },
895                                     ),
896                                 );
897                                 continue 'outer;
898                             }
899                         },
900                         NestedMetaItem::Lit(lit) => {
901                             handle_errors(
902                                 &sess.parse_sess,
903                                 lit.span,
904                                 AttrError::UnsupportedLiteral(
905                                     UnsupportedLiteralReason::DeprecatedKvPair,
906                                     false,
907                                 ),
908                             );
909                             continue 'outer;
910                         }
911                     }
912                 }
913             }
914         }
915
916         if is_rustc {
917             if since.is_none() {
918                 handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
919                 continue;
920             }
921
922             if note.is_none() {
923                 sess.emit_err(session_diagnostics::MissingNote { span: attr.span });
924                 continue;
925             }
926         }
927
928         depr = Some((
929             Deprecation { since, note, suggestion, is_since_rustc_version: is_rustc },
930             attr.span,
931         ));
932     }
933
934     depr
935 }
936
937 #[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)]
938 pub enum ReprAttr {
939     ReprInt(IntType),
940     ReprC,
941     ReprPacked(u32),
942     ReprSimd,
943     ReprTransparent,
944     ReprAlign(u32),
945 }
946
947 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
948 #[derive(Encodable, Decodable, HashStable_Generic)]
949 pub enum IntType {
950     SignedInt(ast::IntTy),
951     UnsignedInt(ast::UintTy),
952 }
953
954 impl IntType {
955     #[inline]
956     pub fn is_signed(self) -> bool {
957         use IntType::*;
958
959         match self {
960             SignedInt(..) => true,
961             UnsignedInt(..) => false,
962         }
963     }
964 }
965
966 /// Parse #[repr(...)] forms.
967 ///
968 /// Valid repr contents: any of the primitive integral type names (see
969 /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
970 /// the same discriminant size that the corresponding C enum would or C
971 /// structure layout, `packed` to remove padding, and `transparent` to delegate representation
972 /// concerns to the only non-ZST field.
973 pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
974     if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() }
975 }
976
977 pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
978     assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
979     use ReprAttr::*;
980     let mut acc = Vec::new();
981     let diagnostic = &sess.parse_sess.span_diagnostic;
982
983     if let Some(items) = attr.meta_item_list() {
984         for item in items {
985             let mut recognised = false;
986             if item.is_word() {
987                 let hint = match item.name_or_empty() {
988                     sym::C => Some(ReprC),
989                     sym::packed => Some(ReprPacked(1)),
990                     sym::simd => Some(ReprSimd),
991                     sym::transparent => Some(ReprTransparent),
992                     sym::align => {
993                         sess.emit_err(session_diagnostics::InvalidReprAlignNeedArg {
994                             span: item.span(),
995                         });
996                         recognised = true;
997                         None
998                     }
999                     name => int_type_of_word(name).map(ReprInt),
1000                 };
1001
1002                 if let Some(h) = hint {
1003                     recognised = true;
1004                     acc.push(h);
1005                 }
1006             } else if let Some((name, value)) = item.name_value_literal() {
1007                 let mut literal_error = None;
1008                 if name == sym::align {
1009                     recognised = true;
1010                     match parse_alignment(&value.kind) {
1011                         Ok(literal) => acc.push(ReprAlign(literal)),
1012                         Err(message) => literal_error = Some(message),
1013                     };
1014                 } else if name == sym::packed {
1015                     recognised = true;
1016                     match parse_alignment(&value.kind) {
1017                         Ok(literal) => acc.push(ReprPacked(literal)),
1018                         Err(message) => literal_error = Some(message),
1019                     };
1020                 } else if matches!(name, sym::C | sym::simd | sym::transparent)
1021                     || int_type_of_word(name).is_some()
1022                 {
1023                     recognised = true;
1024                     sess.emit_err(session_diagnostics::InvalidReprHintNoParen {
1025                         span: item.span(),
1026                         name: name.to_ident_string(),
1027                     });
1028                 }
1029                 if let Some(literal_error) = literal_error {
1030                     sess.emit_err(session_diagnostics::InvalidReprGeneric {
1031                         span: item.span(),
1032                         repr_arg: name.to_ident_string(),
1033                         error_part: literal_error,
1034                     });
1035                 }
1036             } else if let Some(meta_item) = item.meta_item() {
1037                 match &meta_item.kind {
1038                     MetaItemKind::NameValue(value) => {
1039                         if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
1040                             let name = meta_item.name_or_empty().to_ident_string();
1041                             recognised = true;
1042                             sess.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
1043                                 span: item.span(),
1044                                 repr_arg: &name,
1045                                 cause: IncorrectReprFormatGenericCause::from_lit_kind(
1046                                     item.span(),
1047                                     &value.kind,
1048                                     &name,
1049                                 ),
1050                             });
1051                         } else if matches!(
1052                             meta_item.name_or_empty(),
1053                             sym::C | sym::simd | sym::transparent
1054                         ) || int_type_of_word(meta_item.name_or_empty()).is_some()
1055                         {
1056                             recognised = true;
1057                             sess.emit_err(session_diagnostics::InvalidReprHintNoValue {
1058                                 span: meta_item.span,
1059                                 name: meta_item.name_or_empty().to_ident_string(),
1060                             });
1061                         }
1062                     }
1063                     MetaItemKind::List(_) => {
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(
1072                                 session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
1073                                     span: meta_item.span,
1074                                 },
1075                             );
1076                         } else if matches!(
1077                             meta_item.name_or_empty(),
1078                             sym::C | sym::simd | sym::transparent
1079                         ) || int_type_of_word(meta_item.name_or_empty()).is_some()
1080                         {
1081                             recognised = true;
1082                             sess.emit_err(session_diagnostics::InvalidReprHintNoParen {
1083                                 span: meta_item.span,
1084                                 name: meta_item.name_or_empty().to_ident_string(),
1085                             });
1086                         }
1087                     }
1088                     _ => (),
1089                 }
1090             }
1091             if !recognised {
1092                 // Not a word we recognize. This will be caught and reported by
1093                 // the `check_mod_attrs` pass, but this pass doesn't always run
1094                 // (e.g. if we only pretty-print the source), so we have to gate
1095                 // the `delay_span_bug` call as follows:
1096                 if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) {
1097                     diagnostic.delay_span_bug(item.span(), "unrecognized representation hint");
1098                 }
1099             }
1100         }
1101     }
1102     acc
1103 }
1104
1105 fn int_type_of_word(s: Symbol) -> Option<IntType> {
1106     use IntType::*;
1107
1108     match s {
1109         sym::i8 => Some(SignedInt(ast::IntTy::I8)),
1110         sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
1111         sym::i16 => Some(SignedInt(ast::IntTy::I16)),
1112         sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
1113         sym::i32 => Some(SignedInt(ast::IntTy::I32)),
1114         sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
1115         sym::i64 => Some(SignedInt(ast::IntTy::I64)),
1116         sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
1117         sym::i128 => Some(SignedInt(ast::IntTy::I128)),
1118         sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
1119         sym::isize => Some(SignedInt(ast::IntTy::Isize)),
1120         sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
1121         _ => None,
1122     }
1123 }
1124
1125 pub enum TransparencyError {
1126     UnknownTransparency(Symbol, Span),
1127     MultipleTransparencyAttrs(Span, Span),
1128 }
1129
1130 pub fn find_transparency(
1131     attrs: &[Attribute],
1132     macro_rules: bool,
1133 ) -> (Transparency, Option<TransparencyError>) {
1134     let mut transparency = None;
1135     let mut error = None;
1136     for attr in attrs {
1137         if attr.has_name(sym::rustc_macro_transparency) {
1138             if let Some((_, old_span)) = transparency {
1139                 error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span));
1140                 break;
1141             } else if let Some(value) = attr.value_str() {
1142                 transparency = Some((
1143                     match value {
1144                         sym::transparent => Transparency::Transparent,
1145                         sym::semitransparent => Transparency::SemiTransparent,
1146                         sym::opaque => Transparency::Opaque,
1147                         _ => {
1148                             error = Some(TransparencyError::UnknownTransparency(value, attr.span));
1149                             continue;
1150                         }
1151                     },
1152                     attr.span,
1153                 ));
1154             }
1155         }
1156     }
1157     let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque };
1158     (transparency.map_or(fallback, |t| t.0), error)
1159 }
1160
1161 pub fn allow_internal_unstable<'a>(
1162     sess: &'a Session,
1163     attrs: &'a [Attribute],
1164 ) -> impl Iterator<Item = Symbol> + 'a {
1165     allow_unstable(sess, attrs, sym::allow_internal_unstable)
1166 }
1167
1168 pub fn rustc_allow_const_fn_unstable<'a>(
1169     sess: &'a Session,
1170     attrs: &'a [Attribute],
1171 ) -> impl Iterator<Item = Symbol> + 'a {
1172     allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
1173 }
1174
1175 fn allow_unstable<'a>(
1176     sess: &'a Session,
1177     attrs: &'a [Attribute],
1178     symbol: Symbol,
1179 ) -> impl Iterator<Item = Symbol> + 'a {
1180     let attrs = sess.filter_by_name(attrs, symbol);
1181     let list = attrs
1182         .filter_map(move |attr| {
1183             attr.meta_item_list().or_else(|| {
1184                 sess.emit_err(session_diagnostics::ExpectsFeatureList {
1185                     span: attr.span,
1186                     name: symbol.to_ident_string(),
1187                 });
1188                 None
1189             })
1190         })
1191         .flatten();
1192
1193     list.into_iter().filter_map(move |it| {
1194         let name = it.ident().map(|ident| ident.name);
1195         if name.is_none() {
1196             sess.emit_err(session_diagnostics::ExpectsFeatures {
1197                 span: it.span(),
1198                 name: symbol.to_ident_string(),
1199             });
1200         }
1201         name
1202     })
1203 }
1204
1205 pub fn parse_alignment(node: &ast::LitKind) -> Result<u32, &'static str> {
1206     if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
1207         if literal.is_power_of_two() {
1208             // rustc_middle::ty::layout::Align restricts align to <= 2^29
1209             if *literal <= 1 << 29 { Ok(*literal as u32) } else { Err("larger than 2^29") }
1210         } else {
1211             Err("not a power of two")
1212         }
1213     } else {
1214         Err("not an unsuffixed integer")
1215     }
1216 }