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