diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 41337fc21a7b7..e0afcd51b7f41 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -8,7 +8,8 @@ use std::process::ExitStatus; use rustc_errors::codes::*; use rustc_errors::{ - Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, msg, + Diag, DiagArgValue, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, IntoDiagArg, + Level, msg, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::layout::LayoutError; @@ -1249,20 +1250,29 @@ pub(crate) struct FeatureNotValid<'a> { #[label("`{$feature}` is not valid for this target")] pub span: Span, #[subdiagnostic] - pub plus_hint: Option>, + pub hint: FeatureNotValidHint<'a>, } #[derive(Subdiagnostic)] -#[suggestion( - "consider removing the leading `+` in the feature name", - code = "enable = \"{stripped}\"", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub struct RemovePlusFromFeatureName<'a> { - #[primary_span] - pub span: Span, - pub stripped: &'a str, +pub(crate) enum FeatureNotValidHint<'a> { + #[suggestion( + "consider removing the leading `+` in the feature name", + code = "enable = \"{stripped}\"", + applicability = "maybe-incorrect", + style = "verbose" + )] + RemovePlusFromFeatureName { + #[primary_span] + span: Span, + stripped: &'a str, + }, + #[help( + "valid names are: {$possibilities}{$and_more -> + [0] {\"\"} + *[other] {\" \"}and {$and_more} more + }" + )] + ValidFeatureNames { possibilities: DiagSymbolList<&'a str>, and_more: usize }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 08a02831312c7..8ac3f0555db27 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -9,12 +9,12 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON; use rustc_session::parse::feature_err; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, Symbol, edit_distance, sym}; use rustc_target::spec::Arch; use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability}; use smallvec::SmallVec; -use crate::errors::{FeatureNotValid, RemovePlusFromFeatureName}; +use crate::errors::{FeatureNotValid, FeatureNotValidHint}; use crate::{errors, target_features}; /// Compute the enabled target features from the `#[target_feature]` function attribute. @@ -32,15 +32,25 @@ pub(crate) fn from_target_feature_attr( for &(feature, feature_span) in features { let feature_str = feature.as_str(); let Some(stability) = rust_target_features.get(feature_str) else { - let plus_hint = feature_str - .strip_prefix('+') - .filter(|stripped| rust_target_features.contains_key(*stripped)) - .map(|stripped| RemovePlusFromFeatureName { span: feature_span, stripped }); - tcx.dcx().emit_err(FeatureNotValid { - feature: feature_str, - span: feature_span, - plus_hint, - }); + let hint = if let Some(stripped) = feature_str.strip_prefix('+') + && rust_target_features.contains_key(stripped) + { + FeatureNotValidHint::RemovePlusFromFeatureName { span: feature_span, stripped } + } else { + // Show the 5 feature names that are most similar to the input. + let mut valid_names: Vec<_> = + rust_target_features.keys().map(|name| name.as_str()).into_sorted_stable_ord(); + valid_names.sort_by_key(|name| { + edit_distance::edit_distance(name, feature.as_str(), 5).unwrap_or(usize::MAX) + }); + valid_names.truncate(5); + + FeatureNotValidHint::ValidFeatureNames { + possibilities: valid_names.into(), + and_more: rust_target_features.len().saturating_sub(5), + } + }; + tcx.dcx().emit_err(FeatureNotValid { feature: feature_str, span: feature_span, hint }); continue; }; diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index 47068fbb25d49..1022929118bd6 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -119,7 +119,12 @@ fn main() { //~| NOTE `+sse2` is not valid for this target unsafe fn hey() {} -#[target_feature(enable = "+sse5")] -//~^ ERROR `+sse5` is not valid for this target -//~| NOTE `+sse5` is not valid for this target -unsafe fn typo() {} +#[target_feature(enable = "sse5")] +//~^ ERROR `sse5` is not valid for this target +//~| NOTE `sse5` is not valid for this target +unsafe fn typo_sse() {} + +#[target_feature(enable = "avx512")] +//~^ ERROR `avx512` is not valid for this target +//~| NOTE `avx512` is not valid for this target +unsafe fn typo_avx512() {} diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index fbc35cfdc325e..8b91381a32b38 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -160,6 +160,8 @@ error: the feature named `foo` is not valid for this target | LL | #[target_feature(enable = "foo")] | ^^^^^^^^^^^^^^ `foo` is not valid for this target + | + = help: valid names are: `fma`, `xop`, `adx`, `aes`, and `avx` and 74 more error[E0046]: not all trait items implemented, missing: `foo` --> $DIR/invalid-attribute.rs:81:1 @@ -205,13 +207,23 @@ LL - #[target_feature(enable = "+sse2")] LL + #[target_feature(enable = "sse2")] | -error: the feature named `+sse5` is not valid for this target +error: the feature named `sse5` is not valid for this target --> $DIR/invalid-attribute.rs:122:18 | -LL | #[target_feature(enable = "+sse5")] - | ^^^^^^^^^^^^^^^^ `+sse5` is not valid for this target +LL | #[target_feature(enable = "sse5")] + | ^^^^^^^^^^^^^^^ `sse5` is not valid for this target + | + = help: valid names are: `sse`, `sse2`, `sse3`, `sse4a`, and `ssse3` and 74 more + +error: the feature named `avx512` is not valid for this target + --> $DIR/invalid-attribute.rs:127:18 + | +LL | #[target_feature(enable = "avx512")] + | ^^^^^^^^^^^^^^^^^ `avx512` is not valid for this target + | + = help: valid names are: `avx512f`, `avx2`, `avx512bw`, `avx512cd`, and `avx512dq` and 74 more -error: aborting due to 25 previous errors +error: aborting due to 26 previous errors Some errors have detailed explanations: E0046, E0053, E0539, E0658. For more information about an error, try `rustc --explain E0046`.