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