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