diff options
| -rw-r--r-- | compiler/rustc_attr/src/builtin.rs | 34 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/interface.rs | 25 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/builtin.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/context.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_session/src/config.rs | 233 |
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 { |
