rustc_attr_parsing/attributes/
cfg.rs1use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
2use rustc_ast_pretty::pprust;
3use rustc_attr_data_structures::RustcVersion;
4use rustc_feature::{Features, GatedCfg, find_gated_cfg};
5use rustc_session::Session;
6use rustc_session::config::ExpectedValues;
7use rustc_session::lint::BuiltinLintDiag;
8use rustc_session::lint::builtin::UNEXPECTED_CFGS;
9use rustc_session::parse::feature_err;
10use rustc_span::{Span, Symbol, sym};
11
12use crate::session_diagnostics::{self, UnsupportedLiteralReason};
13use crate::{fluent_generated, parse_version};
14
15#[derive(Clone, Debug)]
16pub struct Condition {
17 pub name: Symbol,
18 pub name_span: Span,
19 pub value: Option<Symbol>,
20 pub value_span: Option<Span>,
21 pub span: Span,
22}
23
24pub fn cfg_matches(
26 cfg: &MetaItemInner,
27 sess: &Session,
28 lint_node_id: NodeId,
29 features: Option<&Features>,
30) -> bool {
31 eval_condition(cfg, sess, features, &mut |cfg| {
32 try_gate_cfg(cfg.name, cfg.span, sess, features);
33 match sess.psess.check_config.expecteds.get(&cfg.name) {
34 Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
35 sess.psess.buffer_lint(
36 UNEXPECTED_CFGS,
37 cfg.span,
38 lint_node_id,
39 BuiltinLintDiag::UnexpectedCfgValue(
40 (cfg.name, cfg.name_span),
41 cfg.value.map(|v| (v, cfg.value_span.unwrap())),
42 ),
43 );
44 }
45 None if sess.psess.check_config.exhaustive_names => {
46 sess.psess.buffer_lint(
47 UNEXPECTED_CFGS,
48 cfg.span,
49 lint_node_id,
50 BuiltinLintDiag::UnexpectedCfgName(
51 (cfg.name, cfg.name_span),
52 cfg.value.map(|v| (v, cfg.value_span.unwrap())),
53 ),
54 );
55 }
56 _ => { }
57 }
58 sess.psess.config.contains(&(cfg.name, cfg.value))
59 })
60}
61
62fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
63 let gate = find_gated_cfg(|sym| sym == name);
64 if let (Some(feats), Some(gated_cfg)) = (features, gate) {
65 gate_cfg(gated_cfg, span, sess, feats);
66 }
67}
68
69#[allow(rustc::untranslatable_diagnostic)] fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
71 let (cfg, feature, has_feature) = gated_cfg;
72 if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
73 let explain = format!("`cfg({cfg})` is experimental and subject to change");
74 feature_err(sess, *feature, cfg_span, explain).emit();
75 }
76}
77
78pub fn eval_condition(
81 cfg: &MetaItemInner,
82 sess: &Session,
83 features: Option<&Features>,
84 eval: &mut impl FnMut(Condition) -> bool,
85) -> bool {
86 let dcx = sess.dcx();
87
88 let cfg = match cfg {
89 MetaItemInner::MetaItem(meta_item) => meta_item,
90 MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
91 return *b;
92 }
93 _ => {
94 dcx.emit_err(session_diagnostics::UnsupportedLiteral {
95 span: cfg.span(),
96 reason: UnsupportedLiteralReason::CfgBoolean,
97 is_bytestr: false,
98 start_point_span: sess.source_map().start_point(cfg.span()),
99 });
100 return false;
101 }
102 };
103
104 match &cfg.kind {
105 MetaItemKind::List(mis) if cfg.has_name(sym::version) => {
106 try_gate_cfg(sym::version, cfg.span, sess, features);
107 let (min_version, span) = match &mis[..] {
108 [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => {
109 (sym, span)
110 }
111 [
112 MetaItemInner::Lit(MetaItemLit { span, .. })
113 | MetaItemInner::MetaItem(MetaItem { span, .. }),
114 ] => {
115 dcx.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
116 return false;
117 }
118 [..] => {
119 dcx.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
120 span: cfg.span,
121 });
122 return false;
123 }
124 };
125 let Some(min_version) = parse_version(*min_version) else {
126 dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span });
127 return false;
128 };
129
130 if sess.psess.assume_incomplete_release {
132 RustcVersion::CURRENT > min_version
133 } else {
134 RustcVersion::CURRENT >= min_version
135 }
136 }
137 MetaItemKind::List(mis) => {
138 for mi in mis.iter() {
139 if mi.meta_item_or_bool().is_none() {
140 dcx.emit_err(session_diagnostics::UnsupportedLiteral {
141 span: mi.span(),
142 reason: UnsupportedLiteralReason::Generic,
143 is_bytestr: false,
144 start_point_span: sess.source_map().start_point(mi.span()),
145 });
146 return false;
147 }
148 }
149
150 match cfg.name() {
153 Some(sym::any) => mis
154 .iter()
155 .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
158 Some(sym::all) => mis
159 .iter()
160 .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)),
163 Some(sym::not) => {
164 let [mi] = mis.as_slice() else {
165 dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span });
166 return false;
167 };
168
169 !eval_condition(mi, sess, features, eval)
170 }
171 Some(sym::target) => {
172 if let Some(features) = features
173 && !features.cfg_target_compact()
174 {
175 feature_err(
176 sess,
177 sym::cfg_target_compact,
178 cfg.span,
179 fluent_generated::attr_parsing_unstable_cfg_target_compact,
180 )
181 .emit();
182 }
183
184 mis.iter().fold(true, |res, mi| {
185 let Some(mut mi) = mi.meta_item().cloned() else {
186 dcx.emit_err(session_diagnostics::CfgPredicateIdentifier {
187 span: mi.span(),
188 });
189 return false;
190 };
191
192 if let [seg, ..] = &mut mi.path.segments[..] {
193 seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name));
194 }
195
196 res & eval_condition(&MetaItemInner::MetaItem(mi), sess, features, eval)
197 })
198 }
199 _ => {
200 dcx.emit_err(session_diagnostics::InvalidPredicate {
201 span: cfg.span,
202 predicate: pprust::path_to_string(&cfg.path),
203 });
204 false
205 }
206 }
207 }
208 MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
209 dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
210 true
211 }
212 MetaItemKind::NameValue(lit) if !lit.kind.is_str() => {
213 dcx.emit_err(session_diagnostics::UnsupportedLiteral {
214 span: lit.span,
215 reason: UnsupportedLiteralReason::CfgString,
216 is_bytestr: lit.kind.is_bytestr(),
217 start_point_span: sess.source_map().start_point(lit.span),
218 });
219 true
220 }
221 MetaItemKind::Word | MetaItemKind::NameValue(..) => {
222 let ident = cfg.ident().expect("multi-segment cfg predicate");
223 eval(Condition {
224 name: ident.name,
225 name_span: ident.span,
226 value: cfg.value_str(),
227 value_span: cfg.name_value_literal_span(),
228 span: cfg.span,
229 })
230 }
231 }
232}