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