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::symbol::kw;
11use rustc_span::{Span, Symbol, sym};
12
13use crate::session_diagnostics::{self, UnsupportedLiteralReason};
14use crate::{fluent_generated, parse_version};
15
16#[derive(Clone, Debug)]
17pub struct Condition {
18 pub name: Symbol,
19 pub name_span: Span,
20 pub value: Option<Symbol>,
21 pub value_span: Option<Span>,
22 pub span: Span,
23}
24
25pub fn cfg_matches(
27 cfg: &MetaItemInner,
28 sess: &Session,
29 lint_node_id: NodeId,
30 features: Option<&Features>,
31) -> bool {
32 eval_condition(cfg, sess, features, &mut |cfg| {
33 try_gate_cfg(cfg.name, cfg.span, sess, features);
34 match sess.psess.check_config.expecteds.get(&cfg.name) {
35 Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
36 sess.psess.buffer_lint(
37 UNEXPECTED_CFGS,
38 cfg.span,
39 lint_node_id,
40 BuiltinLintDiag::UnexpectedCfgValue(
41 (cfg.name, cfg.name_span),
42 cfg.value.map(|v| (v, cfg.value_span.unwrap())),
43 ),
44 );
45 }
46 None if sess.psess.check_config.exhaustive_names => {
47 sess.psess.buffer_lint(
48 UNEXPECTED_CFGS,
49 cfg.span,
50 lint_node_id,
51 BuiltinLintDiag::UnexpectedCfgName(
52 (cfg.name, cfg.name_span),
53 cfg.value.map(|v| (v, cfg.value_span.unwrap())),
54 ),
55 );
56 }
57 _ => { }
58 }
59 sess.psess.config.contains(&(cfg.name, cfg.value))
60 })
61}
62
63fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Features>) {
64 let gate = find_gated_cfg(|sym| sym == name);
65 if let (Some(feats), Some(gated_cfg)) = (features, gate) {
66 gate_cfg(gated_cfg, span, sess, feats);
67 }
68}
69
70#[allow(rustc::untranslatable_diagnostic)] fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
72 let (cfg, feature, has_feature) = gated_cfg;
73 if !has_feature(features) && !cfg_span.allows_unstable(*feature) {
74 let explain = format!("`cfg({cfg})` is experimental and subject to change");
75 feature_err(sess, *feature, cfg_span, explain).emit();
76 }
77}
78
79pub fn eval_condition(
82 cfg: &MetaItemInner,
83 sess: &Session,
84 features: Option<&Features>,
85 eval: &mut impl FnMut(Condition) -> bool,
86) -> bool {
87 let dcx = sess.dcx();
88
89 let cfg = match cfg {
90 MetaItemInner::MetaItem(meta_item) => meta_item,
91 MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => {
92 if let Some(features) = features {
93 gate_cfg(
96 &(
97 if *b { kw::True } else { kw::False },
98 sym::cfg_boolean_literals,
99 |features: &Features| features.cfg_boolean_literals(),
100 ),
101 cfg.span(),
102 sess,
103 features,
104 );
105 }
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.name_or_empty() == 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 > min_version
148 } else {
149 RustcVersion::CURRENT >= 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_or_empty() {
168 sym::any => mis
169 .iter()
170 .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
173 sym::all => mis
174 .iter()
175 .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)),
178 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 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}