about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_attr/src/builtin.rs34
-rw-r--r--compiler/rustc_interface/src/interface.rs25
-rw-r--r--compiler/rustc_lint/src/builtin.rs20
-rw-r--r--compiler/rustc_lint/src/context.rs14
-rw-r--r--compiler/rustc_session/src/config.rs233
5 files changed, 173 insertions, 153 deletions
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index d2d3792345f..70426d86075 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -5,6 +5,7 @@ use rustc_ast::{Attribute, LitKind, MetaItem, MetaItemKind, MetaItemLit, NestedM
 use rustc_ast_pretty::pprust;
 use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
 use rustc_macros::HashStable_Generic;
+use rustc_session::config::ExpectedValues;
 use rustc_session::lint::builtin::UNEXPECTED_CFGS;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::{feature_err, ParseSess};
@@ -581,8 +582,20 @@ pub fn cfg_matches(
 ) -> bool {
     eval_condition(cfg, sess, features, &mut |cfg| {
         try_gate_cfg(cfg.name, cfg.span, sess, features);
-        if let Some(names_valid) = &sess.check_config.names_valid {
-            if !names_valid.contains(&cfg.name) {
+        match sess.check_config.expecteds.get(&cfg.name) {
+            Some(ExpectedValues::Some(values)) if !values.contains(&cfg.value) => {
+                sess.buffer_lint_with_diagnostic(
+                    UNEXPECTED_CFGS,
+                    cfg.span,
+                    lint_node_id,
+                    "unexpected `cfg` condition value",
+                    BuiltinLintDiagnostics::UnexpectedCfg(
+                        (cfg.name, cfg.name_span),
+                        cfg.value_span.map(|vs| (cfg.value.unwrap(), vs)),
+                    ),
+                );
+            }
+            None if sess.check_config.exhaustive_names => {
                 sess.buffer_lint_with_diagnostic(
                     UNEXPECTED_CFGS,
                     cfg.span,
@@ -591,22 +604,7 @@ pub fn cfg_matches(
                     BuiltinLintDiagnostics::UnexpectedCfg((cfg.name, cfg.name_span), None),
                 );
             }
-        }
-        if let Some(value) = cfg.value {
-            if let Some(values) = &sess.check_config.values_valid.get(&cfg.name) {
-                if !values.contains(&value) {
-                    sess.buffer_lint_with_diagnostic(
-                        UNEXPECTED_CFGS,
-                        cfg.span,
-                        lint_node_id,
-                        "unexpected `cfg` condition value",
-                        BuiltinLintDiagnostics::UnexpectedCfg(
-                            (cfg.name, cfg.name_span),
-                            cfg.value_span.map(|vs| (value, vs)),
-                        ),
-                    );
-                }
-            }
+            _ => { /* not unexpected */ }
         }
         sess.config.contains(&(cfg.name, cfg.value))
     })
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index de24a25f133..9d9f4ee13f4 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -9,7 +9,7 @@ use rustc_data_structures::OnDrop;
 use rustc_errors::registry::Registry;
 use rustc_errors::{ErrorGuaranteed, Handler};
 use rustc_lint::LintStore;
-use rustc_middle::ty;
+use rustc_middle::{bug, ty};
 use rustc_parse::maybe_new_parser_from_source_str;
 use rustc_query_impl::QueryCtxt;
 use rustc_query_system::query::print_query_stack;
@@ -154,12 +154,14 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
                     Ok(meta_item) if parser.token == token::Eof => {
                         if let Some(args) = meta_item.meta_item_list() {
                             if meta_item.has_name(sym::names) {
-                                let names_valid =
-                                    check_cfg.names_valid.get_or_insert_with(|| FxHashSet::default());
+                                check_cfg.exhaustive_names = true;
                                 for arg in args {
                                     if arg.is_word() && arg.ident().is_some() {
                                         let ident = arg.ident().expect("multi-segment cfg key");
-                                        names_valid.insert(ident.name.to_string());
+                                        check_cfg
+                                            .expecteds
+                                            .entry(ident.name.to_string())
+                                            .or_insert(ExpectedValues::Any);
                                     } else {
                                         error!("`names()` arguments must be simple identifiers");
                                     }
@@ -168,8 +170,8 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
                                 if let Some((name, values)) = args.split_first() {
                                     if name.is_word() && name.ident().is_some() {
                                         let ident = name.ident().expect("multi-segment cfg key");
-                                        let ident_values = check_cfg
-                                            .values_valid
+                                        let expected_values = check_cfg
+                                            .expecteds
                                             .entry(ident.name.to_string())
                                             .or_insert_with(|| {
                                                 ExpectedValues::Some(FxHashSet::default())
@@ -183,20 +185,24 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
                                             if let Some(LitKind::Str(s, _)) =
                                                 val.lit().map(|lit| &lit.kind)
                                             {
-                                                ident_values.insert(s.to_string());
+                                                expected_values.insert(Some(s.to_string()));
                                             } else {
                                                 error!(
                                                     "`values()` arguments must be string literals"
                                                 );
                                             }
                                         }
+
+                                        if values.is_empty() {
+                                            expected_values.insert(None);
+                                        }
                                     } else {
                                         error!(
                                             "`values()` first argument must be a simple identifier"
                                         );
                                     }
                                 } else if args.is_empty() {
-                                    check_cfg.well_known_values = true;
+                                    check_cfg.exhaustive_values = true;
                                 } else {
                                     expected_error();
                                 }
@@ -220,9 +226,6 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
             }
         }
 
-        if let Some(names_valid) = &mut check_cfg.names_valid {
-            names_valid.extend(check_cfg.values_valid.keys().cloned());
-        }
         check_cfg
     })
 }
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index aeb791901bd..01052698850 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -63,6 +63,7 @@ use rustc_middle::ty::layout::{LayoutError, LayoutOf};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef};
+use rustc_session::config::ExpectedValues;
 use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
 use rustc_span::edition::Edition;
 use rustc_span::source_map::Spanned;
@@ -3306,16 +3307,15 @@ impl EarlyLintPass for UnexpectedCfgs {
         let cfg = &cx.sess().parse_sess.config;
         let check_cfg = &cx.sess().parse_sess.check_config;
         for &(name, value) in cfg {
-            if let Some(names_valid) = &check_cfg.names_valid && !names_valid.contains(&name){
-                cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName {
-                    name,
-                });
-            }
-            if let Some(value) = value && let Some(values) = check_cfg.values_valid.get(&name) && !values.contains(&value) {
-                cx.emit_lint(
-                    UNEXPECTED_CFGS,
-                    BuiltinUnexpectedCliConfigValue { name, value },
-                );
+            match check_cfg.expecteds.get(&name) {
+                Some(ExpectedValues::Some(values)) if !values.contains(&value) => {
+                    let value = value.unwrap_or(kw::Empty);
+                    cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigValue { name, value });
+                }
+                None if check_cfg.exhaustive_names => {
+                    cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName { name });
+                }
+                _ => { /* expected */ }
             }
         }
     }
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index d4898ffe883..f53a7bb0c19 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -769,10 +769,7 @@ pub trait LintContext: Sized {
                     db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information");
                 },
                 BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), None) => {
-                    let Some(names_valid) = &sess.parse_sess.check_config.names_valid else {
-                        bug!("it shouldn't be possible to have a diagnostic on a name if name checking is not enabled");
-                    };
-                    let possibilities: Vec<Symbol> = names_valid.iter().map(|s| *s).collect();
+                    let possibilities: Vec<Symbol> = sess.parse_sess.check_config.expecteds.keys().map(|s| *s).collect();
 
                     // Suggest the most probable if we found one
                     if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
@@ -780,10 +777,15 @@ pub trait LintContext: Sized {
                     }
                 },
                 BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), Some((value, value_span))) => {
-                    let Some(values) = &sess.parse_sess.check_config.values_valid.get(&name) else {
+                    let Some(rustc_session::config::ExpectedValues::Some(values)) = &sess.parse_sess.check_config.expecteds.get(&name) else {
                         bug!("it shouldn't be possible to have a diagnostic on a value whose name is not in values");
                     };
-                    let possibilities: Vec<Symbol> = values.iter().map(|&s| s).collect();
+                    let mut have_none_possibility = false;
+                    let possibilities: Vec<Symbol> = values.iter()
+                        .inspect(|a| have_none_possibility |= a.is_none())
+                        .copied()
+                        .filter_map(std::convert::identity)
+                        .collect();
 
                     // Show the full list if all possible values for a given name, but don't do it
                     // for names as the possibilities could be very long
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 51418c01eed..e731fde6c3c 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1056,37 +1056,76 @@ pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig
 
 /// The parsed `--check-cfg` options
 pub struct CheckCfg<T = String> {
-    /// The set of all `names()`, if None no name checking is performed
-    pub names_valid: Option<FxHashSet<T>>,
+    /// Is well known names activated
+    pub exhaustive_names: bool,
     /// Is well known values activated
-    pub well_known_values: bool,
-    /// The set of all `values()`
-    pub values_valid: FxHashMap<T, FxHashSet<T>>,
+    pub exhaustive_values: bool,
+    /// All the expected values for a config name
+    pub expecteds: FxHashMap<T, ExpectedValues<T>>,
 }
 
 impl<T> Default for CheckCfg<T> {
     fn default() -> Self {
         CheckCfg {
-            names_valid: Default::default(),
-            values_valid: Default::default(),
-            well_known_values: false,
+            exhaustive_names: false,
+            exhaustive_values: false,
+            expecteds: FxHashMap::default(),
         }
     }
 }
 
 impl<T> CheckCfg<T> {
-    fn map_data<O: Eq + Hash>(&self, f: impl Fn(&T) -> O) -> CheckCfg<O> {
+    fn map_data<O: Eq + Hash>(self, f: impl Fn(T) -> O) -> CheckCfg<O> {
         CheckCfg {
-            names_valid: self
-                .names_valid
-                .as_ref()
-                .map(|names_valid| names_valid.iter().map(|a| f(a)).collect()),
-            values_valid: self
-                .values_valid
-                .iter()
-                .map(|(a, b)| (f(a), b.iter().map(|b| f(b)).collect()))
+            exhaustive_names: self.exhaustive_names,
+            exhaustive_values: self.exhaustive_values,
+            expecteds: self
+                .expecteds
+                .into_iter()
+                .map(|(name, values)| {
+                    (
+                        f(name),
+                        match values {
+                            ExpectedValues::Some(values) => ExpectedValues::Some(
+                                values.into_iter().map(|b| b.map(|b| f(b))).collect(),
+                            ),
+                            ExpectedValues::Any => ExpectedValues::Any,
+                        },
+                    )
+                })
                 .collect(),
-            well_known_values: self.well_known_values,
+        }
+    }
+}
+
+pub enum ExpectedValues<T> {
+    Some(FxHashSet<Option<T>>),
+    Any,
+}
+
+impl<T: Eq + Hash> ExpectedValues<T> {
+    fn insert(&mut self, value: T) -> bool {
+        match self {
+            ExpectedValues::Some(expecteds) => expecteds.insert(Some(value)),
+            ExpectedValues::Any => false,
+        }
+    }
+}
+
+impl<T: Eq + Hash> Extend<T> for ExpectedValues<T> {
+    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
+        match self {
+            ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(Some)),
+            ExpectedValues::Any => {}
+        }
+    }
+}
+
+impl<'a, T: Eq + Hash + Copy + 'a> Extend<&'a T> for ExpectedValues<T> {
+    fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
+        match self {
+            ExpectedValues::Some(expecteds) => expecteds.extend(iter.into_iter().map(|a| Some(*a))),
+            ExpectedValues::Any => {}
         }
     }
 }
@@ -1095,58 +1134,27 @@ impl<T> CheckCfg<T> {
 /// `rustc_interface::interface::Config` accepts this in the compiler configuration,
 /// but the symbol interner is not yet set up then, so we must convert it later.
 pub fn to_crate_check_config(cfg: CheckCfg) -> CrateCheckConfig {
-    cfg.map_data(|s| Symbol::intern(s))
+    cfg.map_data(|s| Symbol::intern(&s))
 }
 
 impl CrateCheckConfig {
-    /// Fills a `CrateCheckConfig` with well-known configuration names.
-    fn fill_well_known_names(&mut self) {
-        // NOTE: This should be kept in sync with `default_configuration` and
-        // `fill_well_known_values`
-        const WELL_KNOWN_NAMES: &[Symbol] = &[
-            // rustc
-            sym::unix,
-            sym::windows,
-            sym::target_os,
-            sym::target_family,
-            sym::target_arch,
-            sym::target_endian,
-            sym::target_pointer_width,
-            sym::target_env,
-            sym::target_abi,
-            sym::target_vendor,
-            sym::target_thread_local,
-            sym::target_has_atomic_load_store,
-            sym::target_has_atomic,
-            sym::target_has_atomic_equal_alignment,
-            sym::target_feature,
-            sym::panic,
-            sym::sanitize,
-            sym::debug_assertions,
-            sym::proc_macro,
-            sym::test,
-            sym::feature,
-            // rustdoc
-            sym::doc,
-            sym::doctest,
-            // miri
-            sym::miri,
-        ];
-
-        // We only insert well-known names if `names()` was activated
-        if let Some(names_valid) = &mut self.names_valid {
-            names_valid.extend(WELL_KNOWN_NAMES);
-        }
-    }
-
-    /// Fills a `CrateCheckConfig` with well-known configuration values.
-    fn fill_well_known_values(&mut self, current_target: &Target) {
-        if !self.well_known_values {
+    pub fn fill_well_known(&mut self, current_target: &Target) {
+        if !self.exhaustive_values && !self.exhaustive_names {
             return;
         }
 
-        // NOTE: This should be kept in sync with `default_configuration` and
-        // `fill_well_known_names`
+        let no_values = || {
+            let mut values = FxHashSet::default();
+            values.insert(None);
+            ExpectedValues::Some(values)
+        };
+
+        let empty_values = || {
+            let values = FxHashSet::default();
+            ExpectedValues::Some(values)
+        };
+
+        // NOTE: This should be kept in sync with `default_configuration`
 
         let panic_values = &PanicStrategy::all();
 
@@ -1166,6 +1174,9 @@ impl CrateCheckConfig {
         // Unknown possible values:
         //  - `feature`
         //  - `target_feature`
+        for name in [sym::feature, sym::target_feature] {
+            self.expecteds.entry(name).or_insert(ExpectedValues::Any);
+        }
 
         // No-values
         for name in [
@@ -1179,20 +1190,23 @@ impl CrateCheckConfig {
             sym::debug_assertions,
             sym::target_thread_local,
         ] {
-            self.values_valid.entry(name).or_default();
+            self.expecteds.entry(name).or_insert_with(no_values);
         }
 
         // Pre-defined values
-        self.values_valid.entry(sym::panic).or_default().extend(panic_values);
-        self.values_valid.entry(sym::sanitize).or_default().extend(sanitize_values);
-        self.values_valid.entry(sym::target_has_atomic).or_default().extend(atomic_values);
-        self.values_valid
+        self.expecteds.entry(sym::panic).or_insert_with(empty_values).extend(panic_values);
+        self.expecteds.entry(sym::sanitize).or_insert_with(empty_values).extend(sanitize_values);
+        self.expecteds
+            .entry(sym::target_has_atomic)
+            .or_insert_with(no_values)
+            .extend(atomic_values);
+        self.expecteds
             .entry(sym::target_has_atomic_load_store)
-            .or_default()
+            .or_insert_with(no_values)
             .extend(atomic_values);
-        self.values_valid
+        self.expecteds
             .entry(sym::target_has_atomic_equal_alignment)
-            .or_default()
+            .or_insert_with(no_values)
             .extend(atomic_values);
 
         // Target specific values
@@ -1210,47 +1224,50 @@ impl CrateCheckConfig {
 
             // Initialize (if not already initialized)
             for &e in VALUES {
-                self.values_valid.entry(e).or_default();
+                let entry = self.expecteds.entry(e);
+                if !self.exhaustive_values {
+                    entry.or_insert(ExpectedValues::Any);
+                } else {
+                    entry.or_insert_with(empty_values);
+                }
             }
 
-            // Get all values map at once otherwise it would be costly.
-            // (8 values * 220 targets ~= 1760 times, at the time of writing this comment).
-            let [
-                values_target_os,
-                values_target_family,
-                values_target_arch,
-                values_target_endian,
-                values_target_env,
-                values_target_abi,
-                values_target_vendor,
-                values_target_pointer_width,
-            ] = self
-                .values_valid
-                .get_many_mut(VALUES)
-                .expect("unable to get all the check-cfg values buckets");
-
-            for target in TARGETS
-                .iter()
-                .map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
-                .chain(iter::once(current_target.clone()))
-            {
-                values_target_os.insert(Symbol::intern(&target.options.os));
-                values_target_family
-                    .extend(target.options.families.iter().map(|family| Symbol::intern(family)));
-                values_target_arch.insert(Symbol::intern(&target.arch));
-                values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
-                values_target_env.insert(Symbol::intern(&target.options.env));
-                values_target_abi.insert(Symbol::intern(&target.options.abi));
-                values_target_vendor.insert(Symbol::intern(&target.options.vendor));
-                values_target_pointer_width.insert(sym::integer(target.pointer_width));
+            if self.exhaustive_values {
+                // Get all values map at once otherwise it would be costly.
+                // (8 values * 220 targets ~= 1760 times, at the time of writing this comment).
+                let [
+                    values_target_os,
+                    values_target_family,
+                    values_target_arch,
+                    values_target_endian,
+                    values_target_env,
+                    values_target_abi,
+                    values_target_vendor,
+                    values_target_pointer_width,
+                ] = self
+                    .expecteds
+                    .get_many_mut(VALUES)
+                    .expect("unable to get all the check-cfg values buckets");
+
+                for target in TARGETS
+                    .iter()
+                    .map(|target| Target::expect_builtin(&TargetTriple::from_triple(target)))
+                    .chain(iter::once(current_target.clone()))
+                {
+                    values_target_os.insert(Symbol::intern(&target.options.os));
+                    values_target_family.extend(
+                        target.options.families.iter().map(|family| Symbol::intern(family)),
+                    );
+                    values_target_arch.insert(Symbol::intern(&target.arch));
+                    values_target_endian.insert(Symbol::intern(target.options.endian.as_str()));
+                    values_target_env.insert(Symbol::intern(&target.options.env));
+                    values_target_abi.insert(Symbol::intern(&target.options.abi));
+                    values_target_vendor.insert(Symbol::intern(&target.options.vendor));
+                    values_target_pointer_width.insert(sym::integer(target.pointer_width));
+                }
             }
         }
     }
-
-    pub fn fill_well_known(&mut self, current_target: &Target) {
-        self.fill_well_known_names();
-        self.fill_well_known_values(current_target);
-    }
 }
 
 pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateConfig {