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