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