1use std::hash::Hash;
24use std::iter;
25
26use rustc_abi::Align;
27use rustc_ast::ast;
28use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
29use rustc_lint_defs::builtin::EXPLICIT_BUILTIN_CFGS_IN_FLAGS;
30use rustc_span::{Symbol, sym};
31use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, Target};
32
33use crate::config::{CrateType, FmtDebug};
34use crate::{Session, errors};
35
36pub type Cfg = FxIndexSet<(Symbol, Option<Symbol>)>;
42
43#[derive(Default)]
45pub struct CheckCfg {
46 pub exhaustive_names: bool,
48 pub exhaustive_values: bool,
50 pub expecteds: FxHashMap<Symbol, ExpectedValues<Symbol>>,
52 pub well_known_names: FxHashSet<Symbol>,
54}
55
56pub enum ExpectedValues<T> {
57 Some(FxHashSet<Option<T>>),
58 Any,
59}
60
61impl<T: Eq + Hash> ExpectedValues<T> {
62 fn insert(&mut self, value: T) -> bool {
63 match self {
64 ExpectedValues::Some(expecteds) => expecteds.insert(Some(value)),
65 ExpectedValues::Any => false,
66 }
67 }
68}
69
70impl<T: Eq + Hash> Extend<T> for ExpectedValues<T> {
71 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
72 match self {
73 ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(Some)),
74 ExpectedValues::Any => {}
75 }
76 }
77}
78
79impl<'a, T: Eq + Hash + Copy + 'a> Extend<&'a T> for ExpectedValues<T> {
80 fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
81 match self {
82 ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(|a| Some(*a))),
83 ExpectedValues::Any => {}
84 }
85 }
86}
87
88pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) {
90 let disallow = |cfg: &(Symbol, Option<Symbol>), controlled_by| {
91 let cfg_name = cfg.0;
92 let cfg = if let Some(value) = cfg.1 {
93 format!(r#"{}="{}""#, cfg_name, value)
94 } else {
95 format!("{}", cfg_name)
96 };
97 sess.psess.opt_span_buffer_lint(
98 EXPLICIT_BUILTIN_CFGS_IN_FLAGS,
99 None,
100 ast::CRATE_NODE_ID,
101 errors::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.into(),
102 )
103 };
104
105 for cfg in user_cfgs {
117 match cfg {
118 (sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"),
119 (sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"),
120 (sym::ub_checks, None) => disallow(cfg, "-Z ub-checks"),
121 (sym::contract_checks, None) => disallow(cfg, "-Z contract-checks"),
122 (sym::sanitize, None | Some(_)) => disallow(cfg, "-Z sanitizer"),
123 (
124 sym::sanitizer_cfi_generalize_pointers | sym::sanitizer_cfi_normalize_integers,
125 None | Some(_),
126 ) => disallow(cfg, "-Z sanitizer=cfi"),
127 (sym::proc_macro, None) => disallow(cfg, "--crate-type proc-macro"),
128 (sym::panic, Some(sym::abort | sym::unwind | sym::immediate_abort)) => {
129 disallow(cfg, "-C panic")
130 }
131 (sym::target_feature, Some(_)) => disallow(cfg, "-C target-feature"),
132 (sym::unix, None)
133 | (sym::windows, None)
134 | (sym::relocation_model, Some(_))
135 | (sym::target_abi, None | Some(_))
136 | (sym::target_arch, Some(_))
137 | (sym::target_endian, Some(_))
138 | (sym::target_env, None | Some(_))
139 | (sym::target_family, Some(_))
140 | (sym::target_os, Some(_))
141 | (sym::target_pointer_width, Some(_))
142 | (sym::target_vendor, None | Some(_))
143 | (sym::target_has_atomic, Some(_))
144 | (sym::target_has_atomic_equal_alignment, Some(_))
145 | (sym::target_has_atomic_load_store, Some(_))
146 | (sym::target_has_reliable_f16, None | Some(_))
147 | (sym::target_has_reliable_f16_math, None | Some(_))
148 | (sym::target_has_reliable_f128, None | Some(_))
149 | (sym::target_has_reliable_f128_math, None | Some(_))
150 | (sym::target_thread_local, None) => disallow(cfg, "--target"),
151 (sym::fmt_debug, None | Some(_)) => disallow(cfg, "-Z fmt-debug"),
152 (sym::emscripten_wasm_eh, None | Some(_)) => disallow(cfg, "-Z emscripten_wasm_eh"),
153 _ => {}
154 }
155 }
156}
157
158pub(crate) fn default_configuration(sess: &Session) -> Cfg {
160 let mut ret = Cfg::default();
161
162 macro_rules! ins_none {
163 ($key:expr) => {
164 ret.insert(($key, None));
165 };
166 }
167 macro_rules! ins_str {
168 ($key:expr, $val_str:expr) => {
169 ret.insert(($key, Some(Symbol::intern($val_str))));
170 };
171 }
172 macro_rules! ins_sym {
173 ($key:expr, $val_sym:expr) => {
174 ret.insert(($key, Some($val_sym)));
175 };
176 }
177
178 if sess.opts.debug_assertions {
187 ins_none!(sym::debug_assertions);
188 }
189
190 if sess.is_nightly_build() {
191 match sess.opts.unstable_opts.fmt_debug {
192 FmtDebug::Full => {
193 ins_sym!(sym::fmt_debug, sym::full);
194 }
195 FmtDebug::Shallow => {
196 ins_sym!(sym::fmt_debug, sym::shallow);
197 }
198 FmtDebug::None => {
199 ins_sym!(sym::fmt_debug, sym::none);
200 }
201 }
202 }
203
204 if sess.overflow_checks() {
205 ins_none!(sym::overflow_checks);
206 }
207
208 ins_sym!(sym::panic, sess.panic_strategy().desc_symbol());
213 if sess.panic_strategy() == PanicStrategy::ImmediateAbort {
214 ins_sym!(sym::panic, PanicStrategy::Abort.desc_symbol());
215 }
216
217 #[allow(rustc::bad_opt_access)]
219 if sess.opts.crate_types.contains(&CrateType::ProcMacro) {
220 ins_none!(sym::proc_macro);
221 }
222
223 if sess.is_nightly_build() {
224 ins_sym!(sym::relocation_model, sess.target.relocation_model.desc_symbol());
225 }
226
227 for mut s in sess.opts.unstable_opts.sanitizer {
228 if s == SanitizerSet::KERNELADDRESS {
230 s = SanitizerSet::ADDRESS;
231 }
232 ins_str!(sym::sanitize, &s.to_string());
233 }
234
235 if sess.is_sanitizer_cfi_generalize_pointers_enabled() {
236 ins_none!(sym::sanitizer_cfi_generalize_pointers);
237 }
238 if sess.is_sanitizer_cfi_normalize_integers_enabled() {
239 ins_none!(sym::sanitizer_cfi_normalize_integers);
240 }
241
242 ins_str!(sym::target_abi, &sess.target.abi);
243 ins_str!(sym::target_arch, &sess.target.arch);
244 ins_str!(sym::target_endian, sess.target.endian.as_str());
245 ins_str!(sym::target_env, &sess.target.env);
246
247 for family in sess.target.families.as_ref() {
248 ins_str!(sym::target_family, family);
249 if family == "windows" {
250 ins_none!(sym::windows);
251 } else if family == "unix" {
252 ins_none!(sym::unix);
253 }
254 }
255
256 let layout = sess.target.parse_data_layout().unwrap_or_else(|err| {
258 sess.dcx().emit_fatal(err);
259 });
260 let mut has_atomic = false;
261 for (i, align) in [
262 (8, layout.i8_align),
263 (16, layout.i16_align),
264 (32, layout.i32_align),
265 (64, layout.i64_align),
266 (128, layout.i128_align),
267 ] {
268 if i >= sess.target.min_atomic_width() && i <= sess.target.max_atomic_width() {
269 if !has_atomic {
270 has_atomic = true;
271 if sess.is_nightly_build() {
272 if sess.target.atomic_cas {
273 ins_none!(sym::target_has_atomic);
274 }
275 ins_none!(sym::target_has_atomic_load_store);
276 }
277 }
278 let mut insert_atomic = |sym, align: Align| {
279 if sess.target.atomic_cas {
280 ins_sym!(sym::target_has_atomic, sym);
281 }
282 if align.bits() == i {
283 ins_sym!(sym::target_has_atomic_equal_alignment, sym);
284 }
285 ins_sym!(sym::target_has_atomic_load_store, sym);
286 };
287 insert_atomic(sym::integer(i), align);
288 if sess.target.pointer_width as u64 == i {
289 insert_atomic(sym::ptr, layout.pointer_align().abi);
290 }
291 }
292 }
293
294 ins_str!(sym::target_os, &sess.target.os);
295 ins_sym!(sym::target_pointer_width, sym::integer(sess.target.pointer_width));
296
297 if sess.opts.unstable_opts.has_thread_local.unwrap_or(sess.target.has_thread_local) {
298 ins_none!(sym::target_thread_local);
299 }
300
301 ins_str!(sym::target_vendor, &sess.target.vendor);
302
303 if sess.is_test_crate() {
305 ins_none!(sym::test);
306 }
307
308 if sess.ub_checks() {
309 ins_none!(sym::ub_checks);
310 }
311
312 if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh {
314 ins_none!(sym::emscripten_wasm_eh);
315 }
316
317 if sess.contract_checks() {
318 ins_none!(sym::contract_checks);
319 }
320
321 ret
322}
323
324impl CheckCfg {
325 pub fn fill_well_known(&mut self, current_target: &Target) {
327 if !self.exhaustive_values && !self.exhaustive_names {
328 return;
329 }
330
331 let no_values = || {
333 let mut values = FxHashSet::default();
334 values.insert(None);
335 ExpectedValues::Some(values)
336 };
337
338 let empty_values = || {
340 let values = FxHashSet::default();
341 ExpectedValues::Some(values)
342 };
343
344 macro_rules! ins {
345 ($name:expr, $values:expr) => {{
346 self.well_known_names.insert($name);
347 self.expecteds.entry($name).or_insert_with($values)
348 }};
349 }
350
351 ins!(sym::debug_assertions, no_values);
371
372 ins!(sym::fmt_debug, empty_values).extend(FmtDebug::all());
373
374 ins!(sym::clippy, no_values);
379 ins!(sym::doc, no_values);
380 ins!(sym::doctest, no_values);
381 ins!(sym::miri, no_values);
382 ins!(sym::rustfmt, no_values);
383
384 ins!(sym::overflow_checks, no_values);
385
386 ins!(sym::panic, empty_values)
387 .extend(PanicStrategy::ALL.iter().map(PanicStrategy::desc_symbol));
388
389 ins!(sym::proc_macro, no_values);
390
391 ins!(sym::relocation_model, empty_values)
392 .extend(RelocModel::ALL.iter().map(RelocModel::desc_symbol));
393
394 let sanitize_values = SanitizerSet::all()
395 .into_iter()
396 .map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap()));
397 ins!(sym::sanitize, empty_values).extend(sanitize_values);
398
399 ins!(sym::sanitizer_cfi_generalize_pointers, no_values);
400 ins!(sym::sanitizer_cfi_normalize_integers, no_values);
401
402 ins!(sym::target_feature, empty_values).extend(
403 rustc_target::target_features::all_rust_features()
404 .filter(|(_, s)| s.in_cfg())
405 .map(|(f, _s)| f)
406 .chain(rustc_target::target_features::RUSTC_SPECIFIC_FEATURES.iter().cloned())
407 .map(Symbol::intern),
408 );
409
410 {
412 const VALUES: [&Symbol; 8] = [
413 &sym::target_abi,
414 &sym::target_arch,
415 &sym::target_endian,
416 &sym::target_env,
417 &sym::target_family,
418 &sym::target_os,
419 &sym::target_pointer_width,
420 &sym::target_vendor,
421 ];
422
423 for &e in VALUES {
425 if !self.exhaustive_values {
426 ins!(e, || ExpectedValues::Any);
427 } else {
428 ins!(e, empty_values);
429 }
430 }
431
432 if self.exhaustive_values {
433 let [
436 Some(values_target_abi),
437 Some(values_target_arch),
438 Some(values_target_endian),
439 Some(values_target_env),
440 Some(values_target_family),
441 Some(values_target_os),
442 Some(values_target_pointer_width),
443 Some(values_target_vendor),
444 ] = self.expecteds.get_disjoint_mut(VALUES)
445 else {
446 panic!("unable to get all the check-cfg values buckets");
447 };
448
449 for target in Target::builtins().chain(iter::once(current_target.clone())) {
450 values_target_abi.insert(Symbol::intern(&target.options.abi));
451 values_target_arch.insert(Symbol::intern(&target.arch));
452 values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
453 values_target_env.insert(Symbol::intern(&target.options.env));
454 values_target_family.extend(
455 target.options.families.iter().map(|family| Symbol::intern(family)),
456 );
457 values_target_os.insert(Symbol::intern(&target.options.os));
458 values_target_pointer_width.insert(sym::integer(target.pointer_width));
459 values_target_vendor.insert(Symbol::intern(&target.options.vendor));
460 }
461 }
462 }
463
464 let atomic_values = &[
465 sym::ptr,
466 sym::integer(8usize),
467 sym::integer(16usize),
468 sym::integer(32usize),
469 sym::integer(64usize),
470 sym::integer(128usize),
471 ];
472 for sym in [
473 sym::target_has_atomic,
474 sym::target_has_atomic_equal_alignment,
475 sym::target_has_atomic_load_store,
476 ] {
477 ins!(sym, no_values).extend(atomic_values);
478 }
479
480 ins!(sym::target_thread_local, no_values);
481
482 ins!(sym::ub_checks, no_values);
483 ins!(sym::contract_checks, no_values);
484
485 ins!(sym::unix, no_values);
486 ins!(sym::windows, no_values);
487 }
488}