mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-31 04:57:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1202 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			1202 lines
		
	
	
		
			44 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Parsing and validation of builtin attributes
 | |
| 
 | |
| use rustc_ast::{self as ast, attr};
 | |
| use rustc_ast::{Attribute, LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedMetaItem, NodeId};
 | |
| use rustc_ast_pretty::pprust;
 | |
| use rustc_errors::ErrorGuaranteed;
 | |
| use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
 | |
| use rustc_macros::HashStable_Generic;
 | |
| use rustc_session::config::ExpectedValues;
 | |
| use rustc_session::lint::builtin::UNEXPECTED_CFGS;
 | |
| use rustc_session::lint::BuiltinLintDiagnostics;
 | |
| use rustc_session::parse::{feature_err, ParseSess};
 | |
| use rustc_session::{RustcVersion, Session};
 | |
| use rustc_span::hygiene::Transparency;
 | |
| use rustc_span::{symbol::sym, symbol::Symbol, Span};
 | |
| use std::num::NonZeroU32;
 | |
| 
 | |
| use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
 | |
| 
 | |
| /// The version placeholder that recently stabilized features contain inside the
 | |
| /// `since` field of the `#[stable]` attribute.
 | |
| ///
 | |
| /// For more, see [this pull request](https://github.com/rust-lang/rust/pull/100591).
 | |
| pub const VERSION_PLACEHOLDER: &str = "CURRENT_RUSTC_VERSION";
 | |
| 
 | |
| pub fn is_builtin_attr(attr: &Attribute) -> bool {
 | |
|     attr.is_doc_comment() || attr.ident().is_some_and(|ident| is_builtin_attr_name(ident.name))
 | |
| }
 | |
| 
 | |
| pub(crate) enum UnsupportedLiteralReason {
 | |
|     Generic,
 | |
|     CfgString,
 | |
|     DeprecatedString,
 | |
|     DeprecatedKvPair,
 | |
| }
 | |
| 
 | |
| #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
 | |
| pub enum InlineAttr {
 | |
|     None,
 | |
|     Hint,
 | |
|     Always,
 | |
|     Never,
 | |
| }
 | |
| 
 | |
| #[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic)]
 | |
| pub enum InstructionSetAttr {
 | |
|     ArmA32,
 | |
|     ArmT32,
 | |
| }
 | |
| 
 | |
| #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
 | |
| pub enum OptimizeAttr {
 | |
|     None,
 | |
|     Speed,
 | |
|     Size,
 | |
| }
 | |
| 
 | |
| /// Represents the following attributes:
 | |
| ///
 | |
| /// - `#[stable]`
 | |
| /// - `#[unstable]`
 | |
| #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
 | |
| #[derive(HashStable_Generic)]
 | |
| pub struct Stability {
 | |
|     pub level: StabilityLevel,
 | |
|     pub feature: Symbol,
 | |
| }
 | |
| 
 | |
| impl Stability {
 | |
|     pub fn is_unstable(&self) -> bool {
 | |
|         self.level.is_unstable()
 | |
|     }
 | |
| 
 | |
|     pub fn is_stable(&self) -> bool {
 | |
|         self.level.is_stable()
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Represents the `#[rustc_const_unstable]` and `#[rustc_const_stable]` attributes.
 | |
| #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
 | |
| #[derive(HashStable_Generic)]
 | |
| pub struct ConstStability {
 | |
|     pub level: StabilityLevel,
 | |
|     pub feature: Symbol,
 | |
|     /// whether the function has a `#[rustc_promotable]` attribute
 | |
|     pub promotable: bool,
 | |
| }
 | |
| 
 | |
| impl ConstStability {
 | |
|     pub fn is_const_unstable(&self) -> bool {
 | |
|         self.level.is_unstable()
 | |
|     }
 | |
| 
 | |
|     pub fn is_const_stable(&self) -> bool {
 | |
|         self.level.is_stable()
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Represents the `#[rustc_default_body_unstable]` attribute.
 | |
| #[derive(Encodable, Decodable, Copy, Clone, Debug, PartialEq, Eq, Hash)]
 | |
| #[derive(HashStable_Generic)]
 | |
| pub struct DefaultBodyStability {
 | |
|     pub level: StabilityLevel,
 | |
|     pub feature: Symbol,
 | |
| }
 | |
| 
 | |
| /// The available stability levels.
 | |
| #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
 | |
| #[derive(HashStable_Generic)]
 | |
| pub enum StabilityLevel {
 | |
|     /// `#[unstable]`
 | |
|     Unstable {
 | |
|         /// Reason for the current stability level.
 | |
|         reason: UnstableReason,
 | |
|         /// Relevant `rust-lang/rust` issue.
 | |
|         issue: Option<NonZeroU32>,
 | |
|         is_soft: bool,
 | |
|         /// If part of a feature is stabilized and a new feature is added for the remaining parts,
 | |
|         /// then the `implied_by` attribute is used to indicate which now-stable feature previously
 | |
|         /// contained an item.
 | |
|         ///
 | |
|         /// ```pseudo-Rust
 | |
|         /// #[unstable(feature = "foo", issue = "...")]
 | |
|         /// fn foo() {}
 | |
|         /// #[unstable(feature = "foo", issue = "...")]
 | |
|         /// fn foobar() {}
 | |
|         /// ```
 | |
|         ///
 | |
|         /// ...becomes...
 | |
|         ///
 | |
|         /// ```pseudo-Rust
 | |
|         /// #[stable(feature = "foo", since = "1.XX.X")]
 | |
|         /// fn foo() {}
 | |
|         /// #[unstable(feature = "foobar", issue = "...", implied_by = "foo")]
 | |
|         /// fn foobar() {}
 | |
|         /// ```
 | |
|         implied_by: Option<Symbol>,
 | |
|     },
 | |
|     /// `#[stable]`
 | |
|     Stable {
 | |
|         /// Rust release which stabilized this feature.
 | |
|         since: StableSince,
 | |
|         /// Is this item allowed to be referred to on stable, despite being contained in unstable
 | |
|         /// modules?
 | |
|         allowed_through_unstable_modules: bool,
 | |
|     },
 | |
| }
 | |
| 
 | |
| /// Rust release in which a feature is stabilized.
 | |
| #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
 | |
| #[derive(HashStable_Generic)]
 | |
| pub enum StableSince {
 | |
|     Version(RustcVersion),
 | |
|     /// Stabilized in the upcoming version, whatever number that is.
 | |
|     Current,
 | |
|     /// Failed to parse a stabilization version.
 | |
|     Err,
 | |
| }
 | |
| 
 | |
| impl StabilityLevel {
 | |
|     pub fn is_unstable(&self) -> bool {
 | |
|         matches!(self, StabilityLevel::Unstable { .. })
 | |
|     }
 | |
|     pub fn is_stable(&self) -> bool {
 | |
|         matches!(self, StabilityLevel::Stable { .. })
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
 | |
| #[derive(HashStable_Generic)]
 | |
| pub enum UnstableReason {
 | |
|     None,
 | |
|     Default,
 | |
|     Some(Symbol),
 | |
| }
 | |
| 
 | |
| impl UnstableReason {
 | |
|     fn from_opt_reason(reason: Option<Symbol>) -> Self {
 | |
|         // UnstableReason::Default constructed manually
 | |
|         match reason {
 | |
|             Some(r) => Self::Some(r),
 | |
|             None => Self::None,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub fn to_opt_reason(&self) -> Option<Symbol> {
 | |
|         match self {
 | |
|             Self::None => None,
 | |
|             Self::Default => Some(sym::unstable_location_reason_default),
 | |
|             Self::Some(r) => Some(*r),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Collects stability info from `stable`/`unstable`/`rustc_allowed_through_unstable_modules`
 | |
| /// attributes in `attrs`. Returns `None` if no stability attributes are found.
 | |
| pub fn find_stability(
 | |
|     sess: &Session,
 | |
|     attrs: &[Attribute],
 | |
|     item_sp: Span,
 | |
| ) -> Option<(Stability, Span)> {
 | |
|     let mut stab: Option<(Stability, Span)> = None;
 | |
|     let mut allowed_through_unstable_modules = false;
 | |
| 
 | |
|     for attr in attrs {
 | |
|         match attr.name_or_empty() {
 | |
|             sym::rustc_allowed_through_unstable_modules => allowed_through_unstable_modules = true,
 | |
|             sym::unstable => {
 | |
|                 if stab.is_some() {
 | |
|                     sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
 | |
|                     break;
 | |
|                 }
 | |
| 
 | |
|                 if let Some((feature, level)) = parse_unstability(sess, attr) {
 | |
|                     stab = Some((Stability { level, feature }, attr.span));
 | |
|                 }
 | |
|             }
 | |
|             sym::stable => {
 | |
|                 if stab.is_some() {
 | |
|                     sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
 | |
|                     break;
 | |
|                 }
 | |
|                 if let Some((feature, level)) = parse_stability(sess, attr) {
 | |
|                     stab = Some((Stability { level, feature }, attr.span));
 | |
|                 }
 | |
|             }
 | |
|             _ => {}
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if allowed_through_unstable_modules {
 | |
|         match &mut stab {
 | |
|             Some((
 | |
|                 Stability {
 | |
|                     level: StabilityLevel::Stable { allowed_through_unstable_modules, .. },
 | |
|                     ..
 | |
|                 },
 | |
|                 _,
 | |
|             )) => *allowed_through_unstable_modules = true,
 | |
|             _ => {
 | |
|                 sess.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     stab
 | |
| }
 | |
| 
 | |
| /// Collects stability info from `rustc_const_stable`/`rustc_const_unstable`/`rustc_promotable`
 | |
| /// attributes in `attrs`. Returns `None` if no stability attributes are found.
 | |
| pub fn find_const_stability(
 | |
|     sess: &Session,
 | |
|     attrs: &[Attribute],
 | |
|     item_sp: Span,
 | |
| ) -> Option<(ConstStability, Span)> {
 | |
|     let mut const_stab: Option<(ConstStability, Span)> = None;
 | |
|     let mut promotable = false;
 | |
| 
 | |
|     for attr in attrs {
 | |
|         match attr.name_or_empty() {
 | |
|             sym::rustc_promotable => promotable = true,
 | |
|             sym::rustc_const_unstable => {
 | |
|                 if const_stab.is_some() {
 | |
|                     sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
 | |
|                     break;
 | |
|                 }
 | |
| 
 | |
|                 if let Some((feature, level)) = parse_unstability(sess, attr) {
 | |
|                     const_stab =
 | |
|                         Some((ConstStability { level, feature, promotable: false }, attr.span));
 | |
|                 }
 | |
|             }
 | |
|             sym::rustc_const_stable => {
 | |
|                 if const_stab.is_some() {
 | |
|                     sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
 | |
|                     break;
 | |
|                 }
 | |
|                 if let Some((feature, level)) = parse_stability(sess, attr) {
 | |
|                     const_stab =
 | |
|                         Some((ConstStability { level, feature, promotable: false }, attr.span));
 | |
|                 }
 | |
|             }
 | |
|             _ => {}
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Merge the const-unstable info into the stability info
 | |
|     if promotable {
 | |
|         match &mut const_stab {
 | |
|             Some((stab, _)) => stab.promotable = promotable,
 | |
|             _ => _ = sess.emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp }),
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     const_stab
 | |
| }
 | |
| 
 | |
| /// Collects stability info from `rustc_default_body_unstable` attributes in `attrs`.
 | |
| /// Returns `None` if no stability attributes are found.
 | |
| pub fn find_body_stability(
 | |
|     sess: &Session,
 | |
|     attrs: &[Attribute],
 | |
| ) -> Option<(DefaultBodyStability, Span)> {
 | |
|     let mut body_stab: Option<(DefaultBodyStability, Span)> = None;
 | |
| 
 | |
|     for attr in attrs {
 | |
|         if attr.has_name(sym::rustc_default_body_unstable) {
 | |
|             if body_stab.is_some() {
 | |
|                 sess.emit_err(session_diagnostics::MultipleStabilityLevels { span: attr.span });
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             if let Some((feature, level)) = parse_unstability(sess, attr) {
 | |
|                 body_stab = Some((DefaultBodyStability { level, feature }, attr.span));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     body_stab
 | |
| }
 | |
| 
 | |
| fn insert_or_error(sess: &Session, meta: &MetaItem, item: &mut Option<Symbol>) -> Option<()> {
 | |
|     if item.is_some() {
 | |
|         sess.emit_err(session_diagnostics::MultipleItem {
 | |
|             span: meta.span,
 | |
|             item: pprust::path_to_string(&meta.path),
 | |
|         });
 | |
|         None
 | |
|     } else if let Some(v) = meta.value_str() {
 | |
|         *item = Some(v);
 | |
|         Some(())
 | |
|     } else {
 | |
|         sess.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
 | |
|         None
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Read the content of a `stable`/`rustc_const_stable` attribute, and return the feature name and
 | |
| /// its stability information.
 | |
| fn parse_stability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> {
 | |
|     let meta = attr.meta()?;
 | |
|     let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None };
 | |
| 
 | |
|     let mut feature = None;
 | |
|     let mut since = None;
 | |
|     for meta in metas {
 | |
|         let Some(mi) = meta.meta_item() else {
 | |
|             sess.emit_err(session_diagnostics::UnsupportedLiteral {
 | |
|                 span: meta.span(),
 | |
|                 reason: UnsupportedLiteralReason::Generic,
 | |
|                 is_bytestr: false,
 | |
|                 start_point_span: sess.source_map().start_point(meta.span()),
 | |
|             });
 | |
|             return None;
 | |
|         };
 | |
| 
 | |
|         match mi.name_or_empty() {
 | |
|             sym::feature => insert_or_error(sess, mi, &mut feature)?,
 | |
|             sym::since => insert_or_error(sess, mi, &mut since)?,
 | |
|             _ => {
 | |
|                 sess.emit_err(session_diagnostics::UnknownMetaItem {
 | |
|                     span: meta.span(),
 | |
|                     item: pprust::path_to_string(&mi.path),
 | |
|                     expected: &["feature", "since"],
 | |
|                 });
 | |
|                 return None;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     let feature = match feature {
 | |
|         Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
 | |
|         Some(_bad_feature) => {
 | |
|             Err(sess.emit_err(session_diagnostics::NonIdentFeature { span: attr.span }))
 | |
|         }
 | |
|         None => Err(sess.emit_err(session_diagnostics::MissingFeature { span: attr.span })),
 | |
|     };
 | |
| 
 | |
|     let since = if let Some(since) = since {
 | |
|         if since.as_str() == VERSION_PLACEHOLDER {
 | |
|             StableSince::Current
 | |
|         } else if let Some(version) = parse_version(since) {
 | |
|             StableSince::Version(version)
 | |
|         } else {
 | |
|             sess.emit_err(session_diagnostics::InvalidSince { span: attr.span });
 | |
|             StableSince::Err
 | |
|         }
 | |
|     } else {
 | |
|         sess.emit_err(session_diagnostics::MissingSince { span: attr.span });
 | |
|         StableSince::Err
 | |
|     };
 | |
| 
 | |
|     match feature {
 | |
|         Ok(feature) => {
 | |
|             let level = StabilityLevel::Stable { since, allowed_through_unstable_modules: false };
 | |
|             Some((feature, level))
 | |
|         }
 | |
|         Err(ErrorGuaranteed { .. }) => None,
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable`
 | |
| /// attribute, and return the feature name and its stability information.
 | |
| fn parse_unstability(sess: &Session, attr: &Attribute) -> Option<(Symbol, StabilityLevel)> {
 | |
|     let meta = attr.meta()?;
 | |
|     let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None };
 | |
| 
 | |
|     let mut feature = None;
 | |
|     let mut reason = None;
 | |
|     let mut issue = None;
 | |
|     let mut issue_num = None;
 | |
|     let mut is_soft = false;
 | |
|     let mut implied_by = None;
 | |
|     for meta in metas {
 | |
|         let Some(mi) = meta.meta_item() else {
 | |
|             sess.emit_err(session_diagnostics::UnsupportedLiteral {
 | |
|                 span: meta.span(),
 | |
|                 reason: UnsupportedLiteralReason::Generic,
 | |
|                 is_bytestr: false,
 | |
|                 start_point_span: sess.source_map().start_point(meta.span()),
 | |
|             });
 | |
|             return None;
 | |
|         };
 | |
| 
 | |
|         match mi.name_or_empty() {
 | |
|             sym::feature => insert_or_error(sess, mi, &mut feature)?,
 | |
|             sym::reason => insert_or_error(sess, mi, &mut reason)?,
 | |
|             sym::issue => {
 | |
|                 insert_or_error(sess, mi, &mut issue)?;
 | |
| 
 | |
|                 // These unwraps are safe because `insert_or_error` ensures the meta item
 | |
|                 // is a name/value pair string literal.
 | |
|                 issue_num = match issue.unwrap().as_str() {
 | |
|                     "none" => None,
 | |
|                     issue => match issue.parse::<NonZeroU32>() {
 | |
|                         Ok(num) => Some(num),
 | |
|                         Err(err) => {
 | |
|                             sess.emit_err(
 | |
|                                 session_diagnostics::InvalidIssueString {
 | |
|                                     span: mi.span,
 | |
|                                     cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
 | |
|                                         mi.name_value_literal_span().unwrap(),
 | |
|                                         err.kind(),
 | |
|                                     ),
 | |
|                                 },
 | |
|                             );
 | |
|                             return None;
 | |
|                         }
 | |
|                     },
 | |
|                 };
 | |
|             }
 | |
|             sym::soft => {
 | |
|                 if !mi.is_word() {
 | |
|                     sess.emit_err(session_diagnostics::SoftNoArgs { span: mi.span });
 | |
|                 }
 | |
|                 is_soft = true;
 | |
|             }
 | |
|             sym::implied_by => insert_or_error(sess, mi, &mut implied_by)?,
 | |
|             _ => {
 | |
|                 sess.emit_err(session_diagnostics::UnknownMetaItem {
 | |
|                     span: meta.span(),
 | |
|                     item: pprust::path_to_string(&mi.path),
 | |
|                     expected: &["feature", "reason", "issue", "soft", "implied_by"],
 | |
|                 });
 | |
|                 return None;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     let feature = match feature {
 | |
|         Some(feature) if rustc_lexer::is_ident(feature.as_str()) => Ok(feature),
 | |
|         Some(_bad_feature) => {
 | |
|             Err(sess.emit_err(session_diagnostics::NonIdentFeature { span: attr.span }))
 | |
|         }
 | |
|         None => Err(sess.emit_err(session_diagnostics::MissingFeature { span: attr.span })),
 | |
|     };
 | |
| 
 | |
|     let issue =
 | |
|         issue.ok_or_else(|| sess.emit_err(session_diagnostics::MissingIssue { span: attr.span }));
 | |
| 
 | |
|     match (feature, issue) {
 | |
|         (Ok(feature), Ok(_)) => {
 | |
|             let level = StabilityLevel::Unstable {
 | |
|                 reason: UnstableReason::from_opt_reason(reason),
 | |
|                 issue: issue_num,
 | |
|                 is_soft,
 | |
|                 implied_by,
 | |
|             };
 | |
|             Some((feature, level))
 | |
|         }
 | |
|         (Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None,
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
 | |
|     attr::first_attr_value_str_by_name(attrs, sym::crate_name)
 | |
| }
 | |
| 
 | |
| #[derive(Clone, Debug)]
 | |
| pub struct Condition {
 | |
|     pub name: Symbol,
 | |
|     pub name_span: Span,
 | |
|     pub value: Option<Symbol>,
 | |
|     pub value_span: Option<Span>,
 | |
|     pub span: Span,
 | |
| }
 | |
| 
 | |
| /// Tests if a cfg-pattern matches the cfg set
 | |
| pub fn cfg_matches(
 | |
|     cfg: &ast::MetaItem,
 | |
|     sess: &ParseSess,
 | |
|     lint_node_id: NodeId,
 | |
|     features: Option<&Features>,
 | |
| ) -> bool {
 | |
|     eval_condition(cfg, sess, features, &mut |cfg| {
 | |
|         try_gate_cfg(cfg.name, cfg.span, sess, features);
 | |
|         match sess.check_config.expecteds.get(&cfg.name) {
 | |
|             Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
 | |
|                 sess.buffer_lint_with_diagnostic(
 | |
|                     UNEXPECTED_CFGS,
 | |
|                     cfg.span,
 | |
|                     lint_node_id,
 | |
|                     if let Some(value) = cfg.value {
 | |
|                         format!("unexpected `cfg` condition value: `{value}`")
 | |
|                     } else {
 | |
|                         format!("unexpected `cfg` condition value: (none)")
 | |
|                     },
 | |
|                     BuiltinLintDiagnostics::UnexpectedCfgValue(
 | |
|                         (cfg.name, cfg.name_span),
 | |
|                         cfg.value.map(|v| (v, cfg.value_span.unwrap())),
 | |
|                     ),
 | |
|                 );
 | |
|             }
 | |
|             None if sess.check_config.exhaustive_names => {
 | |
|                 sess.buffer_lint_with_diagnostic(
 | |
|                     UNEXPECTED_CFGS,
 | |
|                     cfg.span,
 | |
|                     lint_node_id,
 | |
|                     format!("unexpected `cfg` condition name: `{}`", cfg.name),
 | |
|                     BuiltinLintDiagnostics::UnexpectedCfgName(
 | |
|                         (cfg.name, cfg.name_span),
 | |
|                         cfg.value.map(|v| (v, cfg.value_span.unwrap())),
 | |
|                     ),
 | |
|                 );
 | |
|             }
 | |
|             _ => { /* not unexpected */ }
 | |
|         }
 | |
|         sess.config.contains(&(cfg.name, cfg.value))
 | |
|     })
 | |
| }
 | |
| 
 | |
| fn try_gate_cfg(name: Symbol, span: Span, sess: &ParseSess, features: Option<&Features>) {
 | |
|     let gate = find_gated_cfg(|sym| sym == name);
 | |
|     if let (Some(feats), Some(gated_cfg)) = (features, gate) {
 | |
|         gate_cfg(gated_cfg, span, sess, feats);
 | |
|     }
 | |
| }
 | |
| 
 | |
| fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &Features) {
 | |
|     let (cfg, feature, has_feature) = gated_cfg;
 | |
|     if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
 | |
|         let explain = format!("`cfg({cfg})` is experimental and subject to change");
 | |
|         feature_err(sess, *feature, cfg_span, explain).emit();
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Parse a rustc version number written inside string literal in an attribute,
 | |
| /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
 | |
| /// not accepted in this position, unlike when parsing CFG_RELEASE.
 | |
| fn parse_version(s: Symbol) -> Option<RustcVersion> {
 | |
|     let mut components = s.as_str().split('-');
 | |
|     let d = components.next()?;
 | |
|     if components.next().is_some() {
 | |
|         return None;
 | |
|     }
 | |
|     let mut digits = d.splitn(3, '.');
 | |
|     let major = digits.next()?.parse().ok()?;
 | |
|     let minor = digits.next()?.parse().ok()?;
 | |
|     let patch = digits.next().unwrap_or("0").parse().ok()?;
 | |
|     Some(RustcVersion { major, minor, patch })
 | |
| }
 | |
| 
 | |
| /// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
 | |
| /// evaluate individual items.
 | |
| pub fn eval_condition(
 | |
|     cfg: &ast::MetaItem,
 | |
|     sess: &ParseSess,
 | |
|     features: Option<&Features>,
 | |
|     eval: &mut impl FnMut(Condition) -> bool,
 | |
| ) -> bool {
 | |
|     match &cfg.kind {
 | |
|         ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
 | |
|             try_gate_cfg(sym::version, cfg.span, sess, features);
 | |
|             let (min_version, span) = match &mis[..] {
 | |
|                 [NestedMetaItem::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
 | |
|                     (sym, span)
 | |
|                 }
 | |
|                 [
 | |
|                     NestedMetaItem::Lit(MetaItemLit { span, .. })
 | |
|                     | NestedMetaItem::MetaItem(MetaItem { span, .. }),
 | |
|                 ] => {
 | |
|                     sess.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
 | |
|                     return false;
 | |
|                 }
 | |
|                 [..] => {
 | |
|                     sess.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
 | |
|                         span: cfg.span,
 | |
|                     });
 | |
|                     return false;
 | |
|                 }
 | |
|             };
 | |
|             let Some(min_version) = parse_version(*min_version) else {
 | |
|                 sess.emit_warning(session_diagnostics::UnknownVersionLiteral { span: *span });
 | |
|                 return false;
 | |
|             };
 | |
| 
 | |
|             // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
 | |
|             if sess.assume_incomplete_release {
 | |
|                 RustcVersion::CURRENT > min_version
 | |
|             } else {
 | |
|                 RustcVersion::CURRENT >= min_version
 | |
|             }
 | |
|         }
 | |
|         ast::MetaItemKind::List(mis) => {
 | |
|             for mi in mis.iter() {
 | |
|                 if !mi.is_meta_item() {
 | |
|                     sess.emit_err(session_diagnostics::UnsupportedLiteral {
 | |
|                         span: mi.span(),
 | |
|                         reason: UnsupportedLiteralReason::Generic,
 | |
|                         is_bytestr: false,
 | |
|                         start_point_span: sess.source_map().start_point(mi.span()),
 | |
|                     });
 | |
|                     return false;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // The unwraps below may look dangerous, but we've already asserted
 | |
|             // that they won't fail with the loop above.
 | |
|             match cfg.name_or_empty() {
 | |
|                 sym::any => mis
 | |
|                     .iter()
 | |
|                     // We don't use any() here, because we want to evaluate all cfg condition
 | |
|                     // as eval_condition can (and does) extra checks
 | |
|                     .fold(false, |res, mi| {
 | |
|                         res | eval_condition(mi.meta_item().unwrap(), sess, features, eval)
 | |
|                     }),
 | |
|                 sym::all => mis
 | |
|                     .iter()
 | |
|                     // We don't use all() here, because we want to evaluate all cfg condition
 | |
|                     // as eval_condition can (and does) extra checks
 | |
|                     .fold(true, |res, mi| {
 | |
|                         res & eval_condition(mi.meta_item().unwrap(), sess, features, eval)
 | |
|                     }),
 | |
|                 sym::not => {
 | |
|                     if mis.len() != 1 {
 | |
|                         sess.emit_err(session_diagnostics::ExpectedOneCfgPattern {
 | |
|                             span: cfg.span,
 | |
|                         });
 | |
|                         return false;
 | |
|                     }
 | |
| 
 | |
|                     !eval_condition(mis[0].meta_item().unwrap(), sess, features, eval)
 | |
|                 }
 | |
|                 sym::target => {
 | |
|                     if let Some(features) = features
 | |
|                         && !features.cfg_target_compact
 | |
|                     {
 | |
|                         feature_err(
 | |
|                             sess,
 | |
|                             sym::cfg_target_compact,
 | |
|                             cfg.span,
 | |
|                             "compact `cfg(target(..))` is experimental and subject to change",
 | |
|                         )
 | |
|                         .emit();
 | |
|                     }
 | |
| 
 | |
|                     mis.iter().fold(true, |res, mi| {
 | |
|                         let mut mi = mi.meta_item().unwrap().clone();
 | |
|                         if let [seg, ..] = &mut mi.path.segments[..] {
 | |
|                             seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
 | |
|                         }
 | |
| 
 | |
|                         res & eval_condition(&mi, sess, features, eval)
 | |
|                     })
 | |
|                 }
 | |
|                 _ => {
 | |
|                     sess.emit_err(session_diagnostics::InvalidPredicate {
 | |
|                         span: cfg.span,
 | |
|                         predicate: pprust::path_to_string(&cfg.path),
 | |
|                     });
 | |
|                     false
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
 | |
|             sess.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
 | |
|             true
 | |
|         }
 | |
|         MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
 | |
|             sess.emit_err(session_diagnostics::UnsupportedLiteral {
 | |
|                 span: lit.span,
 | |
|                 reason: UnsupportedLiteralReason::CfgString,
 | |
|                 is_bytestr: lit.kind.is_bytestr(),
 | |
|                 start_point_span: sess.source_map().start_point(lit.span),
 | |
|             });
 | |
|             true
 | |
|         }
 | |
|         ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => {
 | |
|             let ident = cfg.ident().expect("multi-segment cfg predicate");
 | |
|             eval(Condition {
 | |
|                 name: ident.name,
 | |
|                 name_span: ident.span,
 | |
|                 value: cfg.value_str(),
 | |
|                 value_span: cfg.name_value_literal_span(),
 | |
|                 span: cfg.span,
 | |
|             })
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
 | |
| pub struct Deprecation {
 | |
|     pub since: DeprecatedSince,
 | |
|     /// The note to issue a reason.
 | |
|     pub note: Option<Symbol>,
 | |
|     /// A text snippet used to completely replace any use of the deprecated item in an expression.
 | |
|     ///
 | |
|     /// This is currently unstable.
 | |
|     pub suggestion: Option<Symbol>,
 | |
| }
 | |
| 
 | |
| /// Release in which an API is deprecated.
 | |
| #[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
 | |
| pub enum DeprecatedSince {
 | |
|     RustcVersion(RustcVersion),
 | |
|     /// Deprecated in the future ("to be determined").
 | |
|     Future,
 | |
|     /// `feature(staged_api)` is off. Deprecation versions outside the standard
 | |
|     /// library are allowed to be arbitrary strings, for better or worse.
 | |
|     NonStandard(Symbol),
 | |
|     /// Deprecation version is unspecified but optional.
 | |
|     Unspecified,
 | |
|     /// Failed to parse a deprecation version, or the deprecation version is
 | |
|     /// unspecified and required. An error has already been emitted.
 | |
|     Err,
 | |
| }
 | |
| 
 | |
| impl Deprecation {
 | |
|     /// Whether an item marked with #[deprecated(since = "X")] is currently
 | |
|     /// deprecated (i.e., whether X is not greater than the current rustc
 | |
|     /// version).
 | |
|     pub fn is_in_effect(&self) -> bool {
 | |
|         match self.since {
 | |
|             DeprecatedSince::RustcVersion(since) => since <= RustcVersion::CURRENT,
 | |
|             DeprecatedSince::Future => false,
 | |
|             // The `since` field doesn't have semantic purpose without `#![staged_api]`.
 | |
|             DeprecatedSince::NonStandard(_) => true,
 | |
|             // Assume deprecation is in effect if "since" field is absent or invalid.
 | |
|             DeprecatedSince::Unspecified | DeprecatedSince::Err => true,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub fn is_since_rustc_version(&self) -> bool {
 | |
|         matches!(self.since, DeprecatedSince::RustcVersion(_))
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Finds the deprecation attribute. `None` if none exists.
 | |
| pub fn find_deprecation(
 | |
|     sess: &Session,
 | |
|     features: &Features,
 | |
|     attrs: &[Attribute],
 | |
| ) -> Option<(Deprecation, Span)> {
 | |
|     let mut depr: Option<(Deprecation, Span)> = None;
 | |
|     let is_rustc = features.staged_api;
 | |
| 
 | |
|     'outer: for attr in attrs {
 | |
|         if !attr.has_name(sym::deprecated) {
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         let Some(meta) = attr.meta() else {
 | |
|             continue;
 | |
|         };
 | |
|         let mut since = None;
 | |
|         let mut note = None;
 | |
|         let mut suggestion = None;
 | |
|         match &meta.kind {
 | |
|             MetaItemKind::Word => {}
 | |
|             MetaItemKind::NameValue(..) => note = meta.value_str(),
 | |
|             MetaItemKind::List(list) => {
 | |
|                 let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
 | |
|                     if item.is_some() {
 | |
|                         sess.emit_err(session_diagnostics::MultipleItem {
 | |
|                             span: meta.span,
 | |
|                             item: pprust::path_to_string(&meta.path),
 | |
|                         });
 | |
|                         return false;
 | |
|                     }
 | |
|                     if let Some(v) = meta.value_str() {
 | |
|                         *item = Some(v);
 | |
|                         true
 | |
|                     } else {
 | |
|                         if let Some(lit) = meta.name_value_literal() {
 | |
|                             sess.emit_err(session_diagnostics::UnsupportedLiteral {
 | |
|                                 span: lit.span,
 | |
|                                 reason: UnsupportedLiteralReason::DeprecatedString,
 | |
|                                 is_bytestr: lit.kind.is_bytestr(),
 | |
|                                 start_point_span: sess.source_map().start_point(lit.span),
 | |
|                             });
 | |
|                         } else {
 | |
|                             sess.emit_err(session_diagnostics::IncorrectMetaItem {
 | |
|                                 span: meta.span,
 | |
|                             });
 | |
|                         }
 | |
| 
 | |
|                         false
 | |
|                     }
 | |
|                 };
 | |
| 
 | |
|                 for meta in list {
 | |
|                     match meta {
 | |
|                         NestedMetaItem::MetaItem(mi) => match mi.name_or_empty() {
 | |
|                             sym::since => {
 | |
|                                 if !get(mi, &mut since) {
 | |
|                                     continue 'outer;
 | |
|                                 }
 | |
|                             }
 | |
|                             sym::note => {
 | |
|                                 if !get(mi, &mut note) {
 | |
|                                     continue 'outer;
 | |
|                                 }
 | |
|                             }
 | |
|                             sym::suggestion => {
 | |
|                                 if !features.deprecated_suggestion {
 | |
|                                     sess.emit_err(session_diagnostics::DeprecatedItemSuggestion {
 | |
|                                         span: mi.span,
 | |
|                                         is_nightly: sess.is_nightly_build().then_some(()),
 | |
|                                         details: (),
 | |
|                                     });
 | |
|                                 }
 | |
| 
 | |
|                                 if !get(mi, &mut suggestion) {
 | |
|                                     continue 'outer;
 | |
|                                 }
 | |
|                             }
 | |
|                             _ => {
 | |
|                                 sess.emit_err(session_diagnostics::UnknownMetaItem {
 | |
|                                     span: meta.span(),
 | |
|                                     item: pprust::path_to_string(&mi.path),
 | |
|                                     expected: if features.deprecated_suggestion {
 | |
|                                         &["since", "note", "suggestion"]
 | |
|                                     } else {
 | |
|                                         &["since", "note"]
 | |
|                                     },
 | |
|                                 });
 | |
|                                 continue 'outer;
 | |
|                             }
 | |
|                         },
 | |
|                         NestedMetaItem::Lit(lit) => {
 | |
|                             sess.emit_err(session_diagnostics::UnsupportedLiteral {
 | |
|                                 span: lit.span,
 | |
|                                 reason: UnsupportedLiteralReason::DeprecatedKvPair,
 | |
|                                 is_bytestr: false,
 | |
|                                 start_point_span: sess.source_map().start_point(lit.span),
 | |
|                             });
 | |
|                             continue 'outer;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         let since = if let Some(since) = since {
 | |
|             if since.as_str() == "TBD" {
 | |
|                 DeprecatedSince::Future
 | |
|             } else if !is_rustc {
 | |
|                 DeprecatedSince::NonStandard(since)
 | |
|             } else if let Some(version) = parse_version(since) {
 | |
|                 DeprecatedSince::RustcVersion(version)
 | |
|             } else {
 | |
|                 sess.emit_err(session_diagnostics::InvalidSince { span: attr.span });
 | |
|                 DeprecatedSince::Err
 | |
|             }
 | |
|         } else if is_rustc {
 | |
|             sess.emit_err(session_diagnostics::MissingSince { span: attr.span });
 | |
|             DeprecatedSince::Err
 | |
|         } else {
 | |
|             DeprecatedSince::Unspecified
 | |
|         };
 | |
| 
 | |
|         if is_rustc && note.is_none() {
 | |
|             sess.emit_err(session_diagnostics::MissingNote { span: attr.span });
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         depr = Some((Deprecation { since, note, suggestion }, attr.span));
 | |
|     }
 | |
| 
 | |
|     depr
 | |
| }
 | |
| 
 | |
| #[derive(PartialEq, Debug, Encodable, Decodable, Copy, Clone)]
 | |
| pub enum ReprAttr {
 | |
|     ReprInt(IntType),
 | |
|     ReprRust,
 | |
|     ReprC,
 | |
|     ReprPacked(u32),
 | |
|     ReprSimd,
 | |
|     ReprTransparent,
 | |
|     ReprAlign(u32),
 | |
| }
 | |
| 
 | |
| #[derive(Eq, PartialEq, Debug, Copy, Clone)]
 | |
| #[derive(Encodable, Decodable, HashStable_Generic)]
 | |
| pub enum IntType {
 | |
|     SignedInt(ast::IntTy),
 | |
|     UnsignedInt(ast::UintTy),
 | |
| }
 | |
| 
 | |
| impl IntType {
 | |
|     #[inline]
 | |
|     pub fn is_signed(self) -> bool {
 | |
|         use IntType::*;
 | |
| 
 | |
|         match self {
 | |
|             SignedInt(..) => true,
 | |
|             UnsignedInt(..) => false,
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Parse #[repr(...)] forms.
 | |
| ///
 | |
| /// Valid repr contents: any of the primitive integral type names (see
 | |
| /// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
 | |
| /// the same discriminant size that the corresponding C enum would or C
 | |
| /// structure layout, `packed` to remove padding, and `transparent` to delegate representation
 | |
| /// concerns to the only non-ZST field.
 | |
| pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
 | |
|     if attr.has_name(sym::repr) { parse_repr_attr(sess, attr) } else { Vec::new() }
 | |
| }
 | |
| 
 | |
| pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
 | |
|     assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}");
 | |
|     use ReprAttr::*;
 | |
|     let mut acc = Vec::new();
 | |
|     let diagnostic = sess.diagnostic();
 | |
| 
 | |
|     if let Some(items) = attr.meta_item_list() {
 | |
|         for item in items {
 | |
|             let mut recognised = false;
 | |
|             if item.is_word() {
 | |
|                 let hint = match item.name_or_empty() {
 | |
|                     sym::Rust => Some(ReprRust),
 | |
|                     sym::C => Some(ReprC),
 | |
|                     sym::packed => Some(ReprPacked(1)),
 | |
|                     sym::simd => Some(ReprSimd),
 | |
|                     sym::transparent => Some(ReprTransparent),
 | |
|                     sym::align => {
 | |
|                         sess.emit_err(session_diagnostics::InvalidReprAlignNeedArg {
 | |
|                             span: item.span(),
 | |
|                         });
 | |
|                         recognised = true;
 | |
|                         None
 | |
|                     }
 | |
|                     name => int_type_of_word(name).map(ReprInt),
 | |
|                 };
 | |
| 
 | |
|                 if let Some(h) = hint {
 | |
|                     recognised = true;
 | |
|                     acc.push(h);
 | |
|                 }
 | |
|             } else if let Some((name, value)) = item.name_value_literal() {
 | |
|                 let mut literal_error = None;
 | |
|                 if name == sym::align {
 | |
|                     recognised = true;
 | |
|                     match parse_alignment(&value.kind) {
 | |
|                         Ok(literal) => acc.push(ReprAlign(literal)),
 | |
|                         Err(message) => literal_error = Some(message),
 | |
|                     };
 | |
|                 } else if name == sym::packed {
 | |
|                     recognised = true;
 | |
|                     match parse_alignment(&value.kind) {
 | |
|                         Ok(literal) => acc.push(ReprPacked(literal)),
 | |
|                         Err(message) => literal_error = Some(message),
 | |
|                     };
 | |
|                 } else if matches!(name, sym::Rust | sym::C | sym::simd | sym::transparent)
 | |
|                     || int_type_of_word(name).is_some()
 | |
|                 {
 | |
|                     recognised = true;
 | |
|                     sess.emit_err(session_diagnostics::InvalidReprHintNoParen {
 | |
|                         span: item.span(),
 | |
|                         name: name.to_ident_string(),
 | |
|                     });
 | |
|                 }
 | |
|                 if let Some(literal_error) = literal_error {
 | |
|                     sess.emit_err(session_diagnostics::InvalidReprGeneric {
 | |
|                         span: item.span(),
 | |
|                         repr_arg: name.to_ident_string(),
 | |
|                         error_part: literal_error,
 | |
|                     });
 | |
|                 }
 | |
|             } else if let Some(meta_item) = item.meta_item() {
 | |
|                 match &meta_item.kind {
 | |
|                     MetaItemKind::NameValue(value) => {
 | |
|                         if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
 | |
|                             let name = meta_item.name_or_empty().to_ident_string();
 | |
|                             recognised = true;
 | |
|                             sess.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
 | |
|                                 span: item.span(),
 | |
|                                 repr_arg: &name,
 | |
|                                 cause: IncorrectReprFormatGenericCause::from_lit_kind(
 | |
|                                     item.span(),
 | |
|                                     &value.kind,
 | |
|                                     &name,
 | |
|                                 ),
 | |
|                             });
 | |
|                         } else if matches!(
 | |
|                             meta_item.name_or_empty(),
 | |
|                             sym::Rust | sym::C | sym::simd | sym::transparent
 | |
|                         ) || int_type_of_word(meta_item.name_or_empty()).is_some()
 | |
|                         {
 | |
|                             recognised = true;
 | |
|                             sess.emit_err(session_diagnostics::InvalidReprHintNoValue {
 | |
|                                 span: meta_item.span,
 | |
|                                 name: meta_item.name_or_empty().to_ident_string(),
 | |
|                             });
 | |
|                         }
 | |
|                     }
 | |
|                     MetaItemKind::List(_) => {
 | |
|                         if meta_item.has_name(sym::align) {
 | |
|                             recognised = true;
 | |
|                             sess.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
 | |
|                                 span: meta_item.span,
 | |
|                             });
 | |
|                         } else if meta_item.has_name(sym::packed) {
 | |
|                             recognised = true;
 | |
|                             sess.emit_err(
 | |
|                                 session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
 | |
|                                     span: meta_item.span,
 | |
|                                 },
 | |
|                             );
 | |
|                         } else if matches!(
 | |
|                             meta_item.name_or_empty(),
 | |
|                             sym::Rust | sym::C | sym::simd | sym::transparent
 | |
|                         ) || int_type_of_word(meta_item.name_or_empty()).is_some()
 | |
|                         {
 | |
|                             recognised = true;
 | |
|                             sess.emit_err(session_diagnostics::InvalidReprHintNoParen {
 | |
|                                 span: meta_item.span,
 | |
|                                 name: meta_item.name_or_empty().to_ident_string(),
 | |
|                             });
 | |
|                         }
 | |
|                     }
 | |
|                     _ => (),
 | |
|                 }
 | |
|             }
 | |
|             if !recognised {
 | |
|                 // Not a word we recognize. This will be caught and reported by
 | |
|                 // the `check_mod_attrs` pass, but this pass doesn't always run
 | |
|                 // (e.g. if we only pretty-print the source), so we have to gate
 | |
|                 // the `span_delayed_bug` call as follows:
 | |
|                 if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) {
 | |
|                     diagnostic.span_delayed_bug(item.span(), "unrecognized representation hint");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     acc
 | |
| }
 | |
| 
 | |
| fn int_type_of_word(s: Symbol) -> Option<IntType> {
 | |
|     use IntType::*;
 | |
| 
 | |
|     match s {
 | |
|         sym::i8 => Some(SignedInt(ast::IntTy::I8)),
 | |
|         sym::u8 => Some(UnsignedInt(ast::UintTy::U8)),
 | |
|         sym::i16 => Some(SignedInt(ast::IntTy::I16)),
 | |
|         sym::u16 => Some(UnsignedInt(ast::UintTy::U16)),
 | |
|         sym::i32 => Some(SignedInt(ast::IntTy::I32)),
 | |
|         sym::u32 => Some(UnsignedInt(ast::UintTy::U32)),
 | |
|         sym::i64 => Some(SignedInt(ast::IntTy::I64)),
 | |
|         sym::u64 => Some(UnsignedInt(ast::UintTy::U64)),
 | |
|         sym::i128 => Some(SignedInt(ast::IntTy::I128)),
 | |
|         sym::u128 => Some(UnsignedInt(ast::UintTy::U128)),
 | |
|         sym::isize => Some(SignedInt(ast::IntTy::Isize)),
 | |
|         sym::usize => Some(UnsignedInt(ast::UintTy::Usize)),
 | |
|         _ => None,
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub enum TransparencyError {
 | |
|     UnknownTransparency(Symbol, Span),
 | |
|     MultipleTransparencyAttrs(Span, Span),
 | |
| }
 | |
| 
 | |
| pub fn find_transparency(
 | |
|     attrs: &[Attribute],
 | |
|     macro_rules: bool,
 | |
| ) -> (Transparency, Option<TransparencyError>) {
 | |
|     let mut transparency = None;
 | |
|     let mut error = None;
 | |
|     for attr in attrs {
 | |
|         if attr.has_name(sym::rustc_macro_transparency) {
 | |
|             if let Some((_, old_span)) = transparency {
 | |
|                 error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span));
 | |
|                 break;
 | |
|             } else if let Some(value) = attr.value_str() {
 | |
|                 transparency = Some((
 | |
|                     match value {
 | |
|                         sym::transparent => Transparency::Transparent,
 | |
|                         sym::semitransparent => Transparency::SemiTransparent,
 | |
|                         sym::opaque => Transparency::Opaque,
 | |
|                         _ => {
 | |
|                             error = Some(TransparencyError::UnknownTransparency(value, attr.span));
 | |
|                             continue;
 | |
|                         }
 | |
|                     },
 | |
|                     attr.span,
 | |
|                 ));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     let fallback = if macro_rules { Transparency::SemiTransparent } else { Transparency::Opaque };
 | |
|     (transparency.map_or(fallback, |t| t.0), error)
 | |
| }
 | |
| 
 | |
| pub fn allow_internal_unstable<'a>(
 | |
|     sess: &'a Session,
 | |
|     attrs: &'a [Attribute],
 | |
| ) -> impl Iterator<Item = Symbol> + 'a {
 | |
|     allow_unstable(sess, attrs, sym::allow_internal_unstable)
 | |
| }
 | |
| 
 | |
| pub fn rustc_allow_const_fn_unstable<'a>(
 | |
|     sess: &'a Session,
 | |
|     attrs: &'a [Attribute],
 | |
| ) -> impl Iterator<Item = Symbol> + 'a {
 | |
|     allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
 | |
| }
 | |
| 
 | |
| fn allow_unstable<'a>(
 | |
|     sess: &'a Session,
 | |
|     attrs: &'a [Attribute],
 | |
|     symbol: Symbol,
 | |
| ) -> impl Iterator<Item = Symbol> + 'a {
 | |
|     let attrs = attr::filter_by_name(attrs, symbol);
 | |
|     let list = attrs
 | |
|         .filter_map(move |attr| {
 | |
|             attr.meta_item_list().or_else(|| {
 | |
|                 sess.emit_err(session_diagnostics::ExpectsFeatureList {
 | |
|                     span: attr.span,
 | |
|                     name: symbol.to_ident_string(),
 | |
|                 });
 | |
|                 None
 | |
|             })
 | |
|         })
 | |
|         .flatten();
 | |
| 
 | |
|     list.into_iter().filter_map(move |it| {
 | |
|         let name = it.ident().map(|ident| ident.name);
 | |
|         if name.is_none() {
 | |
|             sess.emit_err(session_diagnostics::ExpectsFeatures {
 | |
|                 span: it.span(),
 | |
|                 name: symbol.to_ident_string(),
 | |
|             });
 | |
|         }
 | |
|         name
 | |
|     })
 | |
| }
 | |
| 
 | |
| pub fn parse_alignment(node: &ast::LitKind) -> Result<u32, &'static str> {
 | |
|     if let ast::LitKind::Int(literal, ast::LitIntType::Unsuffixed) = node {
 | |
|         if literal.is_power_of_two() {
 | |
|             // rustc_middle::ty::layout::Align restricts align to <= 2^29
 | |
|             if *literal <= 1 << 29 { Ok(*literal as u32) } else { Err("larger than 2^29") }
 | |
|         } else {
 | |
|             Err("not a power of two")
 | |
|         }
 | |
|     } else {
 | |
|         Err("not an unsuffixed integer")
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Read the content of a `rustc_confusables` attribute, and return the list of candidate names.
 | |
| pub fn parse_confusables(attr: &Attribute) -> Option<Vec<Symbol>> {
 | |
|     let meta = attr.meta()?;
 | |
|     let MetaItem { kind: MetaItemKind::List(ref metas), .. } = meta else { return None };
 | |
| 
 | |
|     let mut candidates = Vec::new();
 | |
| 
 | |
|     for meta in metas {
 | |
|         let NestedMetaItem::Lit(meta_lit) = meta else {
 | |
|             return None;
 | |
|         };
 | |
|         candidates.push(meta_lit.symbol);
 | |
|     }
 | |
| 
 | |
|     return Some(candidates);
 | |
| }
 | 
