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