From b36abea285e7615f12c079c40d3a68c9eee15c8d Mon Sep 17 00:00:00 2001 From: IQuant Date: Fri, 3 Mar 2023 15:41:22 +0300 Subject: [PATCH] Migrate SuggestAsRefWhereAppropriate --- compiler/rustc_infer/messages.ftl | 7 ++ compiler/rustc_infer/src/errors/mod.rs | 72 +++++++++++++++++ .../src/infer/error_reporting/suggest.rs | 81 ++++++++++++------- 3 files changed, 131 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 9d5933d5ab5..2cdc513d73c 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -353,3 +353,10 @@ infer_fps_use_ref = consider using a reference infer_fps_remove_ref = consider removing the reference infer_fps_cast = consider casting to a fn pointer infer_fps_items_are_distinct = fn items are distinct from fn pointers +infer_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}` + +infer_fn_uniq_types = different fn items have unique types, even if their signatures are the same +infer_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}` + +infer_sarwa_option = you can convert from `&Option` to `Option<&T>` using `.as_ref()` +infer_sarwa_result = you can convert from `&Result` to `Result<&T, &E>` using `.as_ref()` diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 8c4f44a5b80..481199d99ea 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1158,6 +1158,14 @@ pub struct OpaqueCapturesLifetime<'tcx> { pub opaque_ty: Ty<'tcx>, } +pub struct DiagArg(pub T); + +impl IntoDiagnosticArg for DiagArg { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + self.0.to_string().into_diagnostic_arg() + } +} + #[derive(Subdiagnostic)] pub enum FunctionPointerSuggestion<'a> { #[suggestion( @@ -1212,8 +1220,72 @@ pub enum FunctionPointerSuggestion<'a> { #[skip_arg] sig: Binder<'a, FnSig<'a>>, }, + #[suggestion( + infer_fps_cast_both, + code = "{fn_name} as {found_sig}", + style = "hidden", + applicability = "maybe-incorrect" + )] + CastBoth { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + #[skip_arg] + found_sig: Binder<'a, FnSig<'a>>, + expected_sig: DiagArg>>, + }, + #[suggestion( + infer_fps_cast_both, + code = "&({fn_name} as {found_sig})", + style = "hidden", + applicability = "maybe-incorrect" + )] + CastBothRef { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + #[skip_arg] + found_sig: Binder<'a, FnSig<'a>>, + expected_sig: DiagArg>>, + }, } #[derive(Subdiagnostic)] #[note(infer_fps_items_are_distinct)] pub struct FnItemsAreDistinct; + +#[derive(Subdiagnostic)] +#[note(infer_fn_uniq_types)] +pub struct FnUniqTypes; + +#[derive(Subdiagnostic)] +#[help(infer_fn_uniq_types)] +pub struct FnConsiderCasting { + pub casting: String, +} + +#[derive(Subdiagnostic)] +pub enum SuggestAsRefWhereAppropriate<'a> { + #[suggestion( + infer_sarwa_option, + code = "{snippet}.as_ref()", + applicability = "machine-applicable" + )] + Option { + #[primary_span] + span: Span, + snippet: &'a str, + }, + #[suggestion( + infer_sarwa_result, + code = "{snippet}.as_ref()", + applicability = "machine-applicable" + )] + Result { + #[primary_span] + span: Span, + snippet: &'a str, + }, +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index e4b3cf6905d..2d173e9d577 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -13,12 +13,19 @@ use rustc_span::{sym, BytePos, Span}; use rustc_target::abi::FieldIdx; use crate::errors::{ - ConsiderAddingAwait, FnItemsAreDistinct, FunctionPointerSuggestion, SuggAddLetForLetChains, + ConsiderAddingAwait, DiagArg, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, + FunctionPointerSuggestion, SuggAddLetForLetChains, SuggestAsRefWhereAppropriate, SuggestRemoveSemiOrReturnBinding, }; use super::TypeErrCtxt; +#[derive(Clone, Copy)] +pub enum SuggestAsRefKind { + Option, + Result, +} + impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub(super) fn suggest_remove_semi_or_return_binding( &self, @@ -322,15 +329,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { diag: &mut Diagnostic, ) { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) - && let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found) + && let Some(msg) = self.should_suggest_as_ref_kind(exp_found.expected, exp_found.found) { - diag.span_suggestion( - span, - msg, - // HACK: fix issue# 100605, suggesting convert from &Option to Option<&T>, remove the extra `&` - format!("{}.as_ref()", snippet.trim_start_matches('&')), - Applicability::MachineApplicable, - ); + // HACK: fix issue# 100605, suggesting convert from &Option to Option<&T>, remove the extra `&` + let snippet = snippet.trim_start_matches('&'); + let subdiag = match msg { + SuggestAsRefKind::Option => SuggestAsRefWhereAppropriate::Option { span, snippet }, + SuggestAsRefKind::Result => SuggestAsRefWhereAppropriate::Result { span, snippet }, + }; + diag.subdiagnostic(subdiag); } } @@ -384,7 +391,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).subst(self.tcx, substs2)); if self.same_type_modulo_infer(*expected_sig, *found_sig) { - diag.note("different fn items have unique types, even if their signatures are the same"); + diag.subdiagnostic(FnUniqTypes); } if !self.same_type_modulo_infer(*found_sig, *expected_sig) @@ -398,16 +405,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2); let sug = if found.is_ref() { - format!("&({fn_name} as {found_sig})") + FunctionPointerSuggestion::CastBothRef { + span, + fn_name, + found_sig: *found_sig, + expected_sig: DiagArg(*expected_sig), + } } else { - format!("{fn_name} as {found_sig}") + FunctionPointerSuggestion::CastBoth { + span, + fn_name, + found_sig: *found_sig, + expected_sig: DiagArg(*expected_sig), + } }; - let msg = format!( - "consider casting both fn items to fn pointers using `as {expected_sig}`" - ); - - diag.span_suggestion_hidden(span, msg, sug, Applicability::MaybeIncorrect); + diag.subdiagnostic(sug); } (ty::FnDef(did, substs), ty::FnPtr(sig)) => { let expected_sig = @@ -426,7 +439,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { format!("{fn_name} as {found_sig}") }; - diag.help(&format!("consider casting the fn item to a fn pointer: `{}`", casting)); + diag.subdiagnostic(FnConsiderCasting { casting }); } _ => { return; @@ -434,23 +447,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; } - pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> { + pub fn should_suggest_as_ref_kind( + &self, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> Option { if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) = (expected.kind(), found.kind()) { if let ty::Adt(found_def, found_substs) = *found_ty.kind() { if exp_def == &found_def { let have_as_ref = &[ - ( - sym::Option, - "you can convert from `&Option` to `Option<&T>` using \ - `.as_ref()`", - ), - ( - sym::Result, - "you can convert from `&Result` to \ - `Result<&T, &E>` using `.as_ref()`", - ), + (sym::Option, SuggestAsRefKind::Option), + (sym::Result, SuggestAsRefKind::Result), ]; if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| { self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg) @@ -484,6 +493,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { None } + // FIXME: Remove once rustc_hir_typeck is migrated to Diagnostics + pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> { + match self.should_suggest_as_ref_kind(expected, found) { + Some(SuggestAsRefKind::Option) => Some( + "you can convert from `&Option` to `Option<&T>` using \ + `.as_ref()`", + ), + Some(SuggestAsRefKind::Result) => Some( + "you can convert from `&Result` to \ + `Result<&T, &E>` using `.as_ref()`", + ), + None => None, + } + } /// Try to find code with pattern `if Some(..) = expr` /// use a `visitor` to mark the `if` which its span contains given error span, /// and then try to find a assignment in the `cond` part, which span is equal with error span