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