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}