|  | use rustc_ast::{LitKind, NodeId}; | 
|  | use rustc_attr_data_structures::{CfgEntry, RustcVersion}; | 
|  | use rustc_feature::{AttributeTemplate, Features, template}; | 
|  | use rustc_session::Session; | 
|  | use rustc_session::config::ExpectedValues; | 
|  | use rustc_session::lint::BuiltinLintDiag; | 
|  | use rustc_session::lint::builtin::UNEXPECTED_CFGS; | 
|  | use rustc_session::parse::feature_err; | 
|  | use rustc_span::{Span, Symbol, sym}; | 
|  | use thin_vec::ThinVec; | 
|  |  | 
|  | use crate::context::{AcceptContext, ShouldEmit, Stage}; | 
|  | use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser}; | 
|  | use crate::{ | 
|  | CfgMatchesLintEmitter, fluent_generated, parse_version, session_diagnostics, try_gate_cfg, | 
|  | }; | 
|  |  | 
|  | pub const CFG_TEMPLATE: AttributeTemplate = template!(List: "predicate"); | 
|  |  | 
|  | pub fn parse_cfg_attr<'c, S: Stage>( | 
|  | cx: &'c mut AcceptContext<'_, '_, S>, | 
|  | args: &'c ArgParser<'_>, | 
|  | ) -> Option<CfgEntry> { | 
|  | let ArgParser::List(list) = args else { | 
|  | cx.expected_list(cx.attr_span); | 
|  | return None; | 
|  | }; | 
|  | let Some(single) = list.single() else { | 
|  | cx.expected_single_argument(list.span); | 
|  | return None; | 
|  | }; | 
|  | parse_cfg_entry(cx, single) | 
|  | } | 
|  |  | 
|  | fn parse_cfg_entry<S: Stage>( | 
|  | cx: &mut AcceptContext<'_, '_, S>, | 
|  | item: &MetaItemOrLitParser<'_>, | 
|  | ) -> Option<CfgEntry> { | 
|  | Some(match item { | 
|  | MetaItemOrLitParser::MetaItemParser(meta) => match meta.args() { | 
|  | ArgParser::List(list) => match meta.path().word_sym() { | 
|  | Some(sym::not) => { | 
|  | let Some(single) = list.single() else { | 
|  | cx.expected_single_argument(list.span); | 
|  | return None; | 
|  | }; | 
|  | CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span) | 
|  | } | 
|  | Some(sym::any) => CfgEntry::Any( | 
|  | list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(), | 
|  | list.span, | 
|  | ), | 
|  | Some(sym::all) => CfgEntry::All( | 
|  | list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(), | 
|  | list.span, | 
|  | ), | 
|  | Some(sym::target) => parse_cfg_entry_target(cx, list, meta.span())?, | 
|  | Some(sym::version) => parse_cfg_entry_version(cx, list, meta.span())?, | 
|  | _ => { | 
|  | cx.emit_err(session_diagnostics::InvalidPredicate { | 
|  | span: meta.span(), | 
|  | predicate: meta.path().to_string(), | 
|  | }); | 
|  | return None; | 
|  | } | 
|  | }, | 
|  | a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { | 
|  | let Some(name) = meta.path().word_sym() else { | 
|  | cx.emit_err(session_diagnostics::CfgPredicateIdentifier { | 
|  | span: meta.path().span(), | 
|  | }); | 
|  | return None; | 
|  | }; | 
|  | parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)? | 
|  | } | 
|  | }, | 
|  | MetaItemOrLitParser::Lit(lit) => match lit.kind { | 
|  | LitKind::Bool(b) => CfgEntry::Bool(b, lit.span), | 
|  | _ => { | 
|  | cx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: lit.span }); | 
|  | return None; | 
|  | } | 
|  | }, | 
|  | MetaItemOrLitParser::Err(_, _) => return None, | 
|  | }) | 
|  | } | 
|  |  | 
|  | fn parse_cfg_entry_version<S: Stage>( | 
|  | cx: &mut AcceptContext<'_, '_, S>, | 
|  | list: &MetaItemListParser<'_>, | 
|  | meta_span: Span, | 
|  | ) -> Option<CfgEntry> { | 
|  | try_gate_cfg(sym::version, meta_span, cx.sess(), cx.features_option()); | 
|  | let Some(version) = list.single() else { | 
|  | cx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral { span: list.span }); | 
|  | return None; | 
|  | }; | 
|  | let Some(version_lit) = version.lit() else { | 
|  | cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version.span() }); | 
|  | return None; | 
|  | }; | 
|  | let Some(version_str) = version_lit.value_str() else { | 
|  | cx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: version_lit.span }); | 
|  | return None; | 
|  | }; | 
|  |  | 
|  | let min_version = parse_version(version_str).or_else(|| { | 
|  | cx.sess() | 
|  | .dcx() | 
|  | .emit_warn(session_diagnostics::UnknownVersionLiteral { span: version_lit.span }); | 
|  | None | 
|  | }); | 
|  |  | 
|  | Some(CfgEntry::Version(min_version, list.span)) | 
|  | } | 
|  |  | 
|  | fn parse_cfg_entry_target<S: Stage>( | 
|  | cx: &mut AcceptContext<'_, '_, S>, | 
|  | list: &MetaItemListParser<'_>, | 
|  | meta_span: Span, | 
|  | ) -> Option<CfgEntry> { | 
|  | if let Some(features) = cx.features_option() | 
|  | && !features.cfg_target_compact() | 
|  | { | 
|  | feature_err( | 
|  | cx.sess(), | 
|  | sym::cfg_target_compact, | 
|  | meta_span, | 
|  | fluent_generated::attr_parsing_unstable_cfg_target_compact, | 
|  | ) | 
|  | .emit(); | 
|  | } | 
|  |  | 
|  | let mut result = ThinVec::new(); | 
|  | for sub_item in list.mixed() { | 
|  | // First, validate that this is a NameValue item | 
|  | let Some(sub_item) = sub_item.meta_item() else { | 
|  | cx.expected_name_value(sub_item.span(), None); | 
|  | continue; | 
|  | }; | 
|  | let Some(nv) = sub_item.args().name_value() else { | 
|  | cx.expected_name_value(sub_item.span(), None); | 
|  | continue; | 
|  | }; | 
|  |  | 
|  | // Then, parse it as a name-value item | 
|  | let Some(name) = sub_item.path().word_sym() else { | 
|  | cx.emit_err(session_diagnostics::CfgPredicateIdentifier { | 
|  | span: sub_item.path().span(), | 
|  | }); | 
|  | return None; | 
|  | }; | 
|  | let name = Symbol::intern(&format!("target_{name}")); | 
|  | if let Some(cfg) = | 
|  | parse_name_value(name, sub_item.path().span(), Some(nv), sub_item.span(), cx) | 
|  | { | 
|  | result.push(cfg); | 
|  | } | 
|  | } | 
|  | Some(CfgEntry::All(result, list.span)) | 
|  | } | 
|  |  | 
|  | fn parse_name_value<S: Stage>( | 
|  | name: Symbol, | 
|  | name_span: Span, | 
|  | value: Option<&NameValueParser>, | 
|  | span: Span, | 
|  | cx: &mut AcceptContext<'_, '_, S>, | 
|  | ) -> Option<CfgEntry> { | 
|  | try_gate_cfg(name, span, cx.sess(), cx.features_option()); | 
|  |  | 
|  | let value = match value { | 
|  | None => None, | 
|  | Some(value) => { | 
|  | let Some(value_str) = value.value_as_str() else { | 
|  | cx.expected_string_literal(value.value_span, Some(value.value_as_lit())); | 
|  | return None; | 
|  | }; | 
|  | Some((value_str, value.value_span)) | 
|  | } | 
|  | }; | 
|  |  | 
|  | Some(CfgEntry::NameValue { name, name_span, value, span }) | 
|  | } | 
|  |  | 
|  | pub fn eval_config_entry( | 
|  | sess: &Session, | 
|  | cfg_entry: &CfgEntry, | 
|  | id: NodeId, | 
|  | features: Option<&Features>, | 
|  | emit_lints: ShouldEmit, | 
|  | ) -> EvalConfigResult { | 
|  | match cfg_entry { | 
|  | CfgEntry::All(subs, ..) => { | 
|  | let mut all = None; | 
|  | for sub in subs { | 
|  | let res = eval_config_entry(sess, sub, id, features, emit_lints); | 
|  | // We cannot short-circuit because `eval_config_entry` emits some lints | 
|  | if !res.as_bool() { | 
|  | all.get_or_insert(res); | 
|  | } | 
|  | } | 
|  | all.unwrap_or_else(|| EvalConfigResult::True) | 
|  | } | 
|  | CfgEntry::Any(subs, span) => { | 
|  | let mut any = None; | 
|  | for sub in subs { | 
|  | let res = eval_config_entry(sess, sub, id, features, emit_lints); | 
|  | // We cannot short-circuit because `eval_config_entry` emits some lints | 
|  | if res.as_bool() { | 
|  | any.get_or_insert(res); | 
|  | } | 
|  | } | 
|  | any.unwrap_or_else(|| EvalConfigResult::False { | 
|  | reason: cfg_entry.clone(), | 
|  | reason_span: *span, | 
|  | }) | 
|  | } | 
|  | CfgEntry::Not(sub, span) => { | 
|  | if eval_config_entry(sess, sub, id, features, emit_lints).as_bool() { | 
|  | EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } | 
|  | } else { | 
|  | EvalConfigResult::True | 
|  | } | 
|  | } | 
|  | CfgEntry::Bool(b, span) => { | 
|  | if *b { | 
|  | EvalConfigResult::True | 
|  | } else { | 
|  | EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } | 
|  | } | 
|  | } | 
|  | CfgEntry::NameValue { name, name_span, value, span } => { | 
|  | if let ShouldEmit::ErrorsAndLints = emit_lints { | 
|  | match sess.psess.check_config.expecteds.get(name) { | 
|  | Some(ExpectedValues::Some(values)) | 
|  | if !values.contains(&value.map(|(v, _)| v)) => | 
|  | { | 
|  | id.emit_span_lint( | 
|  | sess, | 
|  | UNEXPECTED_CFGS, | 
|  | *span, | 
|  | BuiltinLintDiag::UnexpectedCfgValue((*name, *name_span), *value), | 
|  | ); | 
|  | } | 
|  | None if sess.psess.check_config.exhaustive_names => { | 
|  | id.emit_span_lint( | 
|  | sess, | 
|  | UNEXPECTED_CFGS, | 
|  | *span, | 
|  | BuiltinLintDiag::UnexpectedCfgName((*name, *name_span), *value), | 
|  | ); | 
|  | } | 
|  | _ => { /* not unexpected */ } | 
|  | } | 
|  | } | 
|  |  | 
|  | if sess.psess.config.contains(&(*name, value.map(|(v, _)| v))) { | 
|  | EvalConfigResult::True | 
|  | } else { | 
|  | EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *span } | 
|  | } | 
|  | } | 
|  | CfgEntry::Version(min_version, version_span) => { | 
|  | let Some(min_version) = min_version else { | 
|  | return EvalConfigResult::False { | 
|  | reason: cfg_entry.clone(), | 
|  | reason_span: *version_span, | 
|  | }; | 
|  | }; | 
|  | // See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details | 
|  | let min_version_ok = if sess.psess.assume_incomplete_release { | 
|  | RustcVersion::current_overridable() > *min_version | 
|  | } else { | 
|  | RustcVersion::current_overridable() >= *min_version | 
|  | }; | 
|  | if min_version_ok { | 
|  | EvalConfigResult::True | 
|  | } else { | 
|  | EvalConfigResult::False { reason: cfg_entry.clone(), reason_span: *version_span } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | pub enum EvalConfigResult { | 
|  | True, | 
|  | False { reason: CfgEntry, reason_span: Span }, | 
|  | } | 
|  |  | 
|  | impl EvalConfigResult { | 
|  | pub fn as_bool(&self) -> bool { | 
|  | match self { | 
|  | EvalConfigResult::True => true, | 
|  | EvalConfigResult::False { .. } => false, | 
|  | } | 
|  | } | 
|  | } |