diff --git a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs index fab0e9e863dc5..a211a1aaa6bdf 100644 --- a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs @@ -316,9 +316,27 @@ pub(super) fn unexpected_cfg_value( let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); let is_from_external_macro = name_span.in_external_macro(sess.source_map()); - // 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 - let code_sugg = if !possibilities.is_empty() { + let code_sugg = if let Some((value, _)) = value + && sess.psess.check_config.well_known_names.contains(&name) + && let valid_names = possible_well_known_names_for_cfg_value(sess, value) + && !valid_names.is_empty() + { + // Suggest changing the name to something for which `value` is an expected value. + let max_suggestions = 3; + let suggestions = valid_names + .iter() + .take(max_suggestions) + .copied() + .map(|name| lints::unexpected_cfg_value::ChangeNameSuggestion { + span: name_span, + name, + value, + }) + .collect::>(); + lints::unexpected_cfg_value::CodeSuggestion::ChangeName { suggestions } + } else if !possibilities.is_empty() { + // 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 let expected_values = { let (possibilities, and_more) = sort_and_truncate_possibilities( sess, @@ -419,3 +437,22 @@ pub(super) fn unexpected_cfg_value( value: value.map_or_else(String::new, |(v, _span)| v.to_string()), } } + +/// Ordering of the output is not stable, use this only in diagnostic code. +fn possible_well_known_names_for_cfg_value(sess: &Session, value: Symbol) -> Vec { + #[allow(rustc::potential_query_instability)] + sess.psess + .check_config + .well_known_names + .iter() + .filter(|name| { + sess.psess + .check_config + .expecteds + .get(*name) + .map(|expected_values| expected_values.contains(&Some(value))) + .unwrap_or_default() + }) + .copied() + .collect() +} diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index b77225db4f0eb..1220290d46891 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2904,6 +2904,10 @@ pub(crate) mod unexpected_cfg_value { name: Symbol, }, + ChangeName { + #[subdiagnostic] + suggestions: Vec, + }, } #[derive(Subdiagnostic)] @@ -2961,6 +2965,20 @@ pub(crate) mod unexpected_cfg_value { pub and_more: usize, } + #[derive(Subdiagnostic)] + #[suggestion( + "`{$value}` is an expected value for `{$name}`", + code = "{name}", + applicability = "maybe-incorrect", + style = "verbose" + )] + pub(crate) struct ChangeNameSuggestion { + #[primary_span] + pub span: Span, + pub name: Symbol, + pub value: Symbol, + } + #[derive(Subdiagnostic)] pub(crate) enum InvocationHelp { #[note( diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index a7b54e68a071d..1d5287cfd80ac 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -65,6 +65,13 @@ impl ExpectedValues { ExpectedValues::Any => false, } } + + pub fn contains(&self, value: &Option) -> bool { + match self { + ExpectedValues::Some(expecteds) => expecteds.contains(value), + ExpectedValues::Any => false, + } + } } impl Extend for ExpectedValues { diff --git a/tests/ui/cfg/suggest-alternative-name-on-target.rs b/tests/ui/cfg/suggest-alternative-name-on-target.rs new file mode 100644 index 0000000000000..e3810dbbc9c36 --- /dev/null +++ b/tests/ui/cfg/suggest-alternative-name-on-target.rs @@ -0,0 +1,39 @@ +#![deny(unexpected_cfgs)] +//~^ NOTE lint level is defined here + +// target arch used in `target_abi` +#[cfg(target_abi = "arm")] +//~^ ERROR unexpected `cfg` condition value: +//~| NOTE see $DIR/suggest-alternative-name-on-target.rs:5:7 + | +LL | #[cfg(target_abi = "arm")] + | ^^^^^^^^^^^^^^^^^^ + | + = note: see for more information about checking conditional configuration +note: the lint level is defined here + --> $DIR/suggest-alternative-name-on-target.rs:1:9 + | +LL | #![deny(unexpected_cfgs)] + | ^^^^^^^^^^^^^^^ +help: `arm` is an expected value for `target_arch` + | +LL - #[cfg(target_abi = "arm")] +LL + #[cfg(target_arch = "arm")] + | + +error: unexpected `cfg` condition value: `gnu` + --> $DIR/suggest-alternative-name-on-target.rs:12:7 + | +LL | #[cfg(target_arch = "gnu")] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see for more information about checking conditional configuration +help: `gnu` is an expected value for `target_env` + | +LL - #[cfg(target_arch = "gnu")] +LL + #[cfg(target_env = "gnu")] + | + +error: unexpected `cfg` condition value: `openbsd` + --> $DIR/suggest-alternative-name-on-target.rs:19:7 + | +LL | #[cfg(target_env = "openbsd")] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see for more information about checking conditional configuration +help: `openbsd` is an expected value for `target_os` + | +LL - #[cfg(target_env = "openbsd")] +LL + #[cfg(target_os = "openbsd")] + | + +error: unexpected `cfg` condition value: `eabi` + --> $DIR/suggest-alternative-name-on-target.rs:26:7 + | +LL | #[cfg(target_os = "eabi")] + | ^^^^^^^^^^^^^^^^^^ + | + = note: see for more information about checking conditional configuration +help: `eabi` is an expected value for `target_abi` + | +LL - #[cfg(target_os = "eabi")] +LL + #[cfg(target_abi = "eabi")] + | + +error: unexpected `cfg` condition value: `windows` + --> $DIR/suggest-alternative-name-on-target.rs:32:7 + | +LL | #[cfg(target_abi = "windows")] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see for more information about checking conditional configuration +help: `windows` is an expected value for `target_os` + | +LL - #[cfg(target_abi = "windows")] +LL + #[cfg(target_os = "windows")] + | +help: `windows` is an expected value for `target_family` + | +LL - #[cfg(target_abi = "windows")] +LL + #[cfg(target_family = "windows")] + | + +error: aborting due to 5 previous errors +