Skip to content

Commit 572368f

Browse files
Move cfg -> cfg_old
1 parent 16a2739 commit 572368f

File tree

3 files changed

+249
-0
lines changed

3 files changed

+249
-0
lines changed
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NodeId};
2+
use rustc_ast_pretty::pprust;
3+
use rustc_attr_data_structures::RustcVersion;
4+
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
5+
use rustc_session::Session;
6+
use rustc_session::config::ExpectedValues;
7+
use rustc_session::lint::builtin::UNEXPECTED_CFGS;
8+
use rustc_session::lint::{BuiltinLintDiag, Lint};
9+
use rustc_session::parse::feature_err;
10+
use rustc_span::{Span, Symbol, sym};
11+
12+
use crate::session_diagnostics::{self, UnsupportedLiteralReason};
13+
use crate::{fluent_generated, parse_version};
14+
15+
/// Emitter of a builtin lint from `cfg_matches`.
16+
///
17+
/// Used to support emitting a lint (currently on check-cfg), either:
18+
/// - as an early buffered lint (in `rustc`)
19+
/// - or has a "normal" lint from HIR (in `rustdoc`)
20+
pub trait CfgMatchesLintEmitter {
21+
fn emit_span_lint(&self, sess: &Session, lint: &'static Lint, sp: Span, diag: BuiltinLintDiag);
22+
}
23+
24+
impl 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)]
31+
pub 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+
39+
/// Tests if a cfg-pattern matches the cfg set
40+
pub 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+
_ => { /* not unexpected */ }
72+
}
73+
sess.psess.config.contains(&(cfg.name, cfg.value))
74+
})
75+
}
76+
77+
pub fn 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)] // FIXME: make this translatable
85+
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+
93+
/// Evaluate a cfg-like condition (with `any` and `all`), using `eval` to
94+
/// evaluate individual items.
95+
pub 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+
// See https://github.com/rust-lang/rust/issues/64796#issuecomment-640851454 for details
146+
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+
// The unwraps below may look dangerous, but we've already asserted
166+
// that they won't fail with the loop above.
167+
match cfg.name() {
168+
Some(sym::any) => mis
169+
.iter()
170+
// We don't use any() here, because we want to evaluate all cfg condition
171+
// as eval_condition can (and does) extra checks
172+
.fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)),
173+
Some(sym::all) => mis
174+
.iter()
175+
// We don't use all() here, because we want to evaluate all cfg condition
176+
// as eval_condition can (and does) extra checks
177+
.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+
}

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use crate::session_diagnostics::UnusedMultiple;
2727

2828
pub(crate) mod allow_unstable;
2929
pub(crate) mod cfg;
30+
pub(crate) mod cfg_old;
3031
pub(crate) mod codegen_attrs;
3132
pub(crate) mod confusables;
3233
pub(crate) mod deprecation;

compiler/rustc_attr_parsing/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ pub mod parser;
9191
mod session_diagnostics;
9292

9393
pub use attributes::cfg::*;
94+
pub use attributes::cfg_old::*;
9495
pub use attributes::util::{
9596
find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
9697
};

0 commit comments

Comments
 (0)