From a0db06bdebfef83b4424415bd6e64cfb5bdcc978 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 19 Jul 2024 14:21:30 +0000 Subject: [PATCH] Use structured suggestions for unconstrained generic parameters on impl blocks --- compiler/rustc_hir_analysis/messages.ftl | 5 ++ compiler/rustc_hir_analysis/src/errors.rs | 14 ++++ .../rustc_hir_analysis/src/impl_wf_check.rs | 76 +++++-------------- 3 files changed, 39 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index b43559f422533..2fcf43c77d126 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -519,6 +519,11 @@ hir_analysis_typeof_reserved_keyword_used = .suggestion = consider replacing `typeof(...)` with an actual type .label = reserved keyword +hir_analysis_unconstrained_generic_parameter = the {$param_def_kind} `{$param_name}` is not constrained by the impl trait, self type, or predicates + .label = unconstrained {$param_def_kind} + .const_param_note = expressions using a const parameter must map each value to a distinct output value + .const_param_note2 = proving the result of expressions other than the parameter are unique is not supported + hir_analysis_unconstrained_opaque_type = unconstrained opaque type .note = `{$name}` must be used in combination with a concrete type within the same {$what} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 03311fed39615..8e88f33a07968 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1604,6 +1604,20 @@ pub(crate) enum UnusedGenericParameterHelp { TyAlias { param_name: Ident }, } +#[derive(Diagnostic)] +#[diag(hir_analysis_unconstrained_generic_parameter)] +pub(crate) struct UnconstrainedGenericParameter { + #[primary_span] + #[label] + pub span: Span, + pub param_name: Symbol, + pub param_def_kind: &'static str, + #[note(hir_analysis_const_param_note)] + pub const_param_note: Option<()>, + #[note(hir_analysis_const_param_note2)] + pub const_param_note2: Option<()>, +} + #[derive(Diagnostic)] pub enum UnnamedFieldsRepr<'a> { #[diag(hir_analysis_unnamed_fields_repr_missing_repr_c)] diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 5cc1ec71757be..f0fcbd5528f4e 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -8,15 +8,15 @@ //! specialization errors. These things can (and probably should) be //! fixed, but for the moment it's easier to do these checks early. -use crate::constrained_generic_params as cgp; +use crate::{constrained_generic_params as cgp, errors::UnconstrainedGenericParameter}; use min_specialization::check_min_specialization; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{codes::*, struct_span_code_err}; +use rustc_errors::codes::*; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::ErrorGuaranteed; mod min_specialization; @@ -117,43 +117,34 @@ fn enforce_impl_params_are_constrained( let mut res = Ok(()); for param in &impl_generics.own_params { - match param.kind { + let err = match param.kind { // Disallow ANY unconstrained type parameters. ty::GenericParamDefKind::Type { .. } => { let param_ty = ty::ParamTy::for_def(param); - if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { - res = Err(report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "type", - param_ty.name, - )); - } + !input_parameters.contains(&cgp::Parameter::from(param_ty)) } ty::GenericParamDefKind::Lifetime => { let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); - if lifetimes_in_associated_types.contains(¶m_lt) && // (*) + lifetimes_in_associated_types.contains(¶m_lt) && // (*) !input_parameters.contains(¶m_lt) - { - res = Err(report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "lifetime", - param.name, - )); - } } ty::GenericParamDefKind::Const { .. } => { let param_ct = ty::ParamConst::for_def(param); - if !input_parameters.contains(&cgp::Parameter::from(param_ct)) { - res = Err(report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "const", - param_ct.name, - )); - } + !input_parameters.contains(&cgp::Parameter::from(param_ct)) } + }; + if err { + let const_param_note = + matches!(param.kind, ty::GenericParamDefKind::Const { .. }).then_some(()); + let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { + span: tcx.def_span(param.def_id), + param_name: param.name, + param_def_kind: tcx.def_descr(param.def_id), + const_param_note, + const_param_note2: const_param_note, + }); + diag.code(E0207); + res = Err(diag.emit()); } } res @@ -177,30 +168,3 @@ fn enforce_impl_params_are_constrained( // associated types. I believe this is sound, because lifetimes // used elsewhere are not projected back out. } - -fn report_unused_parameter( - tcx: TyCtxt<'_>, - span: Span, - kind: &str, - name: Symbol, -) -> ErrorGuaranteed { - let mut err = struct_span_code_err!( - tcx.dcx(), - span, - E0207, - "the {} parameter `{}` is not constrained by the \ - impl trait, self type, or predicates", - kind, - name - ); - err.span_label(span, format!("unconstrained {kind} parameter")); - if kind == "const" { - err.note( - "expressions using a const parameter must map each value to a distinct output value", - ); - err.note( - "proving the result of expressions other than the parameter are unique is not supported", - ); - } - err.emit() -}