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_thread_local, None) => disallow(cfg, "--target"),
146 (sym::fmt_debug, None | Some(_)) => disallow(cfg, "-Z fmt-debug"),
147 (sym::emscripten_wasm_eh, None | Some(_)) => disallow(cfg, "-Z emscripten_wasm_eh"),
148 _ => {}
149 }
150 }
151}
152
153pub(crate) fn default_configuration(sess: &Session) -> Cfg {
155 let mut ret = Cfg::default();
156
157 macro_rules! ins_none {
158 ($key:expr) => {
159 ret.insert(($key, None));
160 };
161 }
162 macro_rules! ins_str {
163 ($key:expr, $val_str:expr) => {
164 ret.insert(($key, Some(Symbol::intern($val_str))));
165 };
166 }
167 macro_rules! ins_sym {
168 ($key:expr, $val_sym:expr) => {
169 ret.insert(($key, Some($val_sym)));
170 };
171 }
172
173 if sess.opts.debug_assertions {
182 ins_none!(sym::debug_assertions);
183 }
184
185 if sess.is_nightly_build() {
186 match sess.opts.unstable_opts.fmt_debug {
187 FmtDebug::Full => {
188 ins_sym!(sym::fmt_debug, sym::full);
189 }
190 FmtDebug::Shallow => {
191 ins_sym!(sym::fmt_debug, sym::shallow);
192 }
193 FmtDebug::None => {
194 ins_sym!(sym::fmt_debug, sym::none);
195 }
196 }
197 }
198
199 if sess.overflow_checks() {
200 ins_none!(sym::overflow_checks);
201 }
202
203 ins_sym!(sym::panic, sess.panic_strategy().desc_symbol());
204
205 #[allow(rustc::bad_opt_access)]
207 if sess.opts.crate_types.contains(&CrateType::ProcMacro) {
208 ins_none!(sym::proc_macro);
209 }
210
211 if sess.is_nightly_build() {
212 ins_sym!(sym::relocation_model, sess.target.relocation_model.desc_symbol());
213 }
214
215 for mut s in sess.opts.unstable_opts.sanitizer {
216 if s == SanitizerSet::KERNELADDRESS {
218 s = SanitizerSet::ADDRESS;
219 }
220 ins_str!(sym::sanitize, &s.to_string());
221 }
222
223 if sess.is_sanitizer_cfi_generalize_pointers_enabled() {
224 ins_none!(sym::sanitizer_cfi_generalize_pointers);
225 }
226 if sess.is_sanitizer_cfi_normalize_integers_enabled() {
227 ins_none!(sym::sanitizer_cfi_normalize_integers);
228 }
229
230 ins_str!(sym::target_abi, &sess.target.abi);
231 ins_str!(sym::target_arch, &sess.target.arch);
232 ins_str!(sym::target_endian, sess.target.endian.as_str());
233 ins_str!(sym::target_env, &sess.target.env);
234
235 for family in sess.target.families.as_ref() {
236 ins_str!(sym::target_family, family);
237 if family == "windows" {
238 ins_none!(sym::windows);
239 } else if family == "unix" {
240 ins_none!(sym::unix);
241 }
242 }
243
244 let layout = sess.target.parse_data_layout().unwrap_or_else(|err| {
246 sess.dcx().emit_fatal(err);
247 });
248 let mut has_atomic = false;
249 for (i, align) in [
250 (8, layout.i8_align.abi),
251 (16, layout.i16_align.abi),
252 (32, layout.i32_align.abi),
253 (64, layout.i64_align.abi),
254 (128, layout.i128_align.abi),
255 ] {
256 if i >= sess.target.min_atomic_width() && i <= sess.target.max_atomic_width() {
257 if !has_atomic {
258 has_atomic = true;
259 if sess.is_nightly_build() {
260 if sess.target.atomic_cas {
261 ins_none!(sym::target_has_atomic);
262 }
263 ins_none!(sym::target_has_atomic_load_store);
264 }
265 }
266 let mut insert_atomic = |sym, align: Align| {
267 if sess.target.atomic_cas {
268 ins_sym!(sym::target_has_atomic, sym);
269 }
270 if align.bits() == i {
271 ins_sym!(sym::target_has_atomic_equal_alignment, sym);
272 }
273 ins_sym!(sym::target_has_atomic_load_store, sym);
274 };
275 insert_atomic(sym::integer(i), align);
276 if sess.target.pointer_width as u64 == i {
277 insert_atomic(sym::ptr, layout.pointer_align.abi);
278 }
279 }
280 }
281
282 ins_str!(sym::target_os, &sess.target.os);
283 ins_sym!(sym::target_pointer_width, sym::integer(sess.target.pointer_width));
284
285 if sess.opts.unstable_opts.has_thread_local.unwrap_or(sess.target.has_thread_local) {
286 ins_none!(sym::target_thread_local);
287 }
288
289 ins_str!(sym::target_vendor, &sess.target.vendor);
290
291 if sess.is_test_crate() {
293 ins_none!(sym::test);
294 }
295
296 if sess.ub_checks() {
297 ins_none!(sym::ub_checks);
298 }
299
300 if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh {
302 ins_none!(sym::emscripten_wasm_eh);
303 }
304
305 if sess.contract_checks() {
306 ins_none!(sym::contract_checks);
307 }
308
309 ret
310}
311
312impl CheckCfg {
313 pub fn fill_well_known(&mut self, current_target: &Target) {
315 if !self.exhaustive_values && !self.exhaustive_names {
316 return;
317 }
318
319 let no_values = || {
321 let mut values = FxHashSet::default();
322 values.insert(None);
323 ExpectedValues::Some(values)
324 };
325
326 let empty_values = || {
328 let values = FxHashSet::default();
329 ExpectedValues::Some(values)
330 };
331
332 macro_rules! ins {
333 ($name:expr, $values:expr) => {{
334 self.well_known_names.insert($name);
335 self.expecteds.entry($name).or_insert_with($values)
336 }};
337 }
338
339 ins!(sym::debug_assertions, no_values);
359
360 ins!(sym::fmt_debug, empty_values).extend(FmtDebug::all());
361
362 ins!(sym::clippy, no_values);
367 ins!(sym::doc, no_values);
368 ins!(sym::doctest, no_values);
369 ins!(sym::miri, no_values);
370 ins!(sym::rustfmt, no_values);
371
372 ins!(sym::overflow_checks, no_values);
373
374 ins!(sym::panic, empty_values).extend(&PanicStrategy::all());
375
376 ins!(sym::proc_macro, no_values);
377
378 ins!(sym::relocation_model, empty_values).extend(RelocModel::all());
379
380 let sanitize_values = SanitizerSet::all()
381 .into_iter()
382 .map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap()));
383 ins!(sym::sanitize, empty_values).extend(sanitize_values);
384
385 ins!(sym::sanitizer_cfi_generalize_pointers, no_values);
386 ins!(sym::sanitizer_cfi_normalize_integers, no_values);
387
388 ins!(sym::target_feature, empty_values).extend(
389 rustc_target::target_features::all_rust_features()
390 .filter(|(_, s)| s.in_cfg())
391 .map(|(f, _s)| f)
392 .chain(rustc_target::target_features::RUSTC_SPECIFIC_FEATURES.iter().cloned())
393 .map(Symbol::intern),
394 );
395
396 {
398 const VALUES: [&Symbol; 8] = [
399 &sym::target_abi,
400 &sym::target_arch,
401 &sym::target_endian,
402 &sym::target_env,
403 &sym::target_family,
404 &sym::target_os,
405 &sym::target_pointer_width,
406 &sym::target_vendor,
407 ];
408
409 for &e in VALUES {
411 if !self.exhaustive_values {
412 ins!(e, || ExpectedValues::Any);
413 } else {
414 ins!(e, empty_values);
415 }
416 }
417
418 if self.exhaustive_values {
419 let [
422 Some(values_target_abi),
423 Some(values_target_arch),
424 Some(values_target_endian),
425 Some(values_target_env),
426 Some(values_target_family),
427 Some(values_target_os),
428 Some(values_target_pointer_width),
429 Some(values_target_vendor),
430 ] = self.expecteds.get_disjoint_mut(VALUES)
431 else {
432 panic!("unable to get all the check-cfg values buckets");
433 };
434
435 for target in Target::builtins().chain(iter::once(current_target.clone())) {
436 values_target_abi.insert(Symbol::intern(&target.options.abi));
437 values_target_arch.insert(Symbol::intern(&target.arch));
438 values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
439 values_target_env.insert(Symbol::intern(&target.options.env));
440 values_target_family.extend(
441 target.options.families.iter().map(|family| Symbol::intern(family)),
442 );
443 values_target_os.insert(Symbol::intern(&target.options.os));
444 values_target_pointer_width.insert(sym::integer(target.pointer_width));
445 values_target_vendor.insert(Symbol::intern(&target.options.vendor));
446 }
447 }
448 }
449
450 let atomic_values = &[
451 sym::ptr,
452 sym::integer(8usize),
453 sym::integer(16usize),
454 sym::integer(32usize),
455 sym::integer(64usize),
456 sym::integer(128usize),
457 ];
458 for sym in [
459 sym::target_has_atomic,
460 sym::target_has_atomic_equal_alignment,
461 sym::target_has_atomic_load_store,
462 ] {
463 ins!(sym, no_values).extend(atomic_values);
464 }
465
466 ins!(sym::target_thread_local, no_values);
467
468 ins!(sym::ub_checks, no_values);
469 ins!(sym::contract_checks, no_values);
470
471 ins!(sym::unix, no_values);
472 ins!(sym::windows, no_values);
473 }
474}