From 37f55691f46741d783ba482d42f1cf5ef60593a9 Mon Sep 17 00:00:00 2001 From: IQuant Date: Thu, 23 Feb 2023 16:38:12 +0300 Subject: [PATCH 01/16] Ported FunctionPointerSuggestion --- compiler/rustc_infer/messages.ftl | 5 ++ compiler/rustc_infer/src/errors/mod.rs | 60 +++++++++++++++++++ .../src/infer/error_reporting/suggest.rs | 31 ++++------ 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 15780898dc6..9d5933d5ab5 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -348,3 +348,8 @@ infer_prlf_known_limitation = this is a known limitation that will be removed in infer_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds .label = opaque type defined here + +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 diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 6bbd3fd3e6e..8c4f44a5b80 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1157,3 +1157,63 @@ pub struct OpaqueCapturesLifetime<'tcx> { pub opaque_ty_span: Span, pub opaque_ty: Ty<'tcx>, } + +#[derive(Subdiagnostic)] +pub enum FunctionPointerSuggestion<'a> { + #[suggestion( + infer_fps_use_ref, + code = "&{fn_name}", + style = "verbose", + applicability = "maybe-incorrect" + )] + UseRef { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + }, + #[suggestion( + infer_fps_remove_ref, + code = "{fn_name}", + style = "verbose", + applicability = "maybe-incorrect" + )] + RemoveRef { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + }, + #[suggestion( + infer_fps_cast, + code = "&({fn_name} as {sig})", + style = "verbose", + applicability = "maybe-incorrect" + )] + CastRef { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + #[skip_arg] + sig: Binder<'a, FnSig<'a>>, + }, + #[suggestion( + infer_fps_cast, + code = "{fn_name} as {sig}", + style = "verbose", + applicability = "maybe-incorrect" + )] + Cast { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + #[skip_arg] + sig: Binder<'a, FnSig<'a>>, + }, +} + +#[derive(Subdiagnostic)] +#[note(infer_fps_items_are_distinct)] +pub struct FnItemsAreDistinct; diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 8ad143247e8..e4b3cf6905d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -13,7 +13,8 @@ use rustc_target::abi::FieldIdx; use crate::errors::{ - ConsiderAddingAwait, SuggAddLetForLetChains, SuggestRemoveSemiOrReturnBinding, + ConsiderAddingAwait, FnItemsAreDistinct, FunctionPointerSuggestion, SuggAddLetForLetChains, + SuggestRemoveSemiOrReturnBinding, }; use super::TypeErrCtxt; @@ -362,31 +363,19 @@ pub(super) fn suggest_function_pointers( return; } - let (msg, sug) = match (expected.is_ref(), found.is_ref()) { - (true, false) => { - let msg = "consider using a reference"; - let sug = format!("&{fn_name}"); - (msg, sug) - } - (false, true) => { - let msg = "consider removing the reference"; - let sug = format!("{fn_name}"); - (msg, sug) - } + let sugg = match (expected.is_ref(), found.is_ref()) { + (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name }, + (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name }, (true, true) => { - diag.note("fn items are distinct from fn pointers"); - let msg = "consider casting to a fn pointer"; - let sug = format!("&({fn_name} as {sig})"); - (msg, sug) + diag.subdiagnostic(FnItemsAreDistinct); + FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig } } (false, false) => { - diag.note("fn items are distinct from fn pointers"); - let msg = "consider casting to a fn pointer"; - let sug = format!("{fn_name} as {sig}"); - (msg, sug) + diag.subdiagnostic(FnItemsAreDistinct); + FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig } } }; - diag.span_suggestion_verbose(span, msg, sug, Applicability::MaybeIncorrect); + diag.subdiagnostic(sugg); } (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { let expected_sig = From b36abea285e7615f12c079c40d3a68c9eee15c8d Mon Sep 17 00:00:00 2001 From: IQuant Date: Fri, 3 Mar 2023 15:41:22 +0300 Subject: [PATCH 02/16] 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_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 @@ pub(super) fn suggest_as_ref_where_appropriate( 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 @@ pub(super) fn suggest_function_pointers( &(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 @@ pub(super) fn suggest_function_pointers( 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 @@ pub(super) fn suggest_function_pointers( 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 @@ pub(super) fn suggest_function_pointers( }; } - 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 @@ pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Opti 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 From d56b304bc8bd603d5f4298793f33f1cbb6c9d0d1 Mon Sep 17 00:00:00 2001 From: IQuant Date: Fri, 3 Mar 2023 16:17:53 +0300 Subject: [PATCH 03/16] Migrate SuggestAccessingField --- compiler/rustc_infer/messages.ftl | 2 ++ compiler/rustc_infer/src/errors/mod.rs | 28 +++++++++++++++++++ .../src/infer/error_reporting/suggest.rs | 27 ++++-------------- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 2cdc513d73c..17de77b74a8 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -360,3 +360,5 @@ infer_fn_consider_casting = consider casting the fn item to a fn pointer: `{$cas 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()` + +infer_suggest_accessing_field = you might have meant to use field `{$name}` whose type is `{$ty}` diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 481199d99ea..25c4e9a55f8 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1289,3 +1289,31 @@ pub enum SuggestAsRefWhereAppropriate<'a> { snippet: &'a str, }, } + +#[derive(Subdiagnostic)] +pub enum SuggestAccessingField<'a> { + #[suggestion( + infer_suggest_accessing_field, + code = "{snippet}.{name}", + applicability = "maybe-incorrect" + )] + Safe { + #[primary_span] + span: Span, + snippet: String, + name: Symbol, + ty: Ty<'a>, + }, + #[suggestion( + infer_suggest_accessing_field, + code = "unsafe {{ {snippet}.{name} }}", + applicability = "maybe-incorrect" + )] + Unsafe { + #[primary_span] + span: Span, + snippet: String, + name: Symbol, + ty: Ty<'a>, + }, +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 2d173e9d577..88d7cab0e4c 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -14,8 +14,8 @@ use crate::errors::{ ConsiderAddingAwait, DiagArg, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, - FunctionPointerSuggestion, SuggAddLetForLetChains, SuggestAsRefWhereAppropriate, - SuggestRemoveSemiOrReturnBinding, + FunctionPointerSuggestion, SuggAddLetForLetChains, SuggestAccessingField, + SuggestAsRefWhereAppropriate, SuggestRemoveSemiOrReturnBinding, }; use super::TypeErrCtxt; @@ -264,15 +264,6 @@ pub(super) fn suggest_await_on_expect_found( } } - pub fn suggest_await_on_future(&self, diag: &mut Diagnostic, sp: Span) { - diag.span_suggestion_verbose( - sp.shrink_to_hi(), - "consider `await`ing on the `Future`", - ".await", - Applicability::MaybeIncorrect, - ); - } - pub(super) fn suggest_accessing_field_where_appropriate( &self, cause: &ObligationCause<'tcx>, @@ -299,21 +290,13 @@ pub(super) fn suggest_accessing_field_where_appropriate( if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { let suggestion = if expected_def.is_struct() { - format!("{}.{}", snippet, name) + SuggestAccessingField::Safe { span, snippet, name, ty } } else if expected_def.is_union() { - format!("unsafe {{ {}.{} }}", snippet, name) + SuggestAccessingField::Unsafe { span, snippet, name, ty } } else { return; }; - diag.span_suggestion( - span, - &format!( - "you might have meant to use field `{}` whose type is `{}`", - name, ty - ), - suggestion, - Applicability::MaybeIncorrect, - ); + diag.subdiagnostic(suggestion); } } } From 23b8567a78bd3899dd963e83ed2d698c044c877f Mon Sep 17 00:00:00 2001 From: IQuant Date: Fri, 3 Mar 2023 17:49:51 +0300 Subject: [PATCH 04/16] Migrate SuggestBoxingForReturnImplTrait, Fix typo in infer_fn_consider_casting --- compiler/rustc_infer/messages.ftl | 3 ++ compiler/rustc_infer/src/errors/mod.rs | 20 ++++++++++- .../src/infer/error_reporting/suggest.rs | 36 +++++++++---------- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 17de77b74a8..2534c1cbdab 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -362,3 +362,6 @@ infer_sarwa_option = you can convert from `&Option` to `Option<&T>` using `.a infer_sarwa_result = you can convert from `&Result` to `Result<&T, &E>` using `.as_ref()` infer_suggest_accessing_field = you might have meant to use field `{$name}` whose type is `{$ty}` + +infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object +infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions \ No newline at end of file diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 25c4e9a55f8..5d72ad6fa05 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1261,7 +1261,7 @@ pub enum FunctionPointerSuggestion<'a> { pub struct FnUniqTypes; #[derive(Subdiagnostic)] -#[help(infer_fn_uniq_types)] +#[help(infer_fn_consider_casting)] pub struct FnConsiderCasting { pub casting: String, } @@ -1317,3 +1317,21 @@ pub enum SuggestAccessingField<'a> { ty: Ty<'a>, }, } + +#[derive(Subdiagnostic)] +pub enum SuggestBoxingForReturnImplTrait { + #[multipart_suggestion(infer_sbfrit_change_return_type, applicability = "maybe-incorrect")] + ChangeReturnType { + #[suggestion_part(code = "Box, + #[suggestion_part(code = ")")] + ends: Vec, + }, +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 88d7cab0e4c..82ee365f3a0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -15,7 +15,8 @@ use crate::errors::{ ConsiderAddingAwait, DiagArg, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, FunctionPointerSuggestion, SuggAddLetForLetChains, SuggestAccessingField, - SuggestAsRefWhereAppropriate, SuggestRemoveSemiOrReturnBinding, + SuggestAsRefWhereAppropriate, SuggestBoxingForReturnImplTrait, + SuggestRemoveSemiOrReturnBinding, }; use super::TypeErrCtxt; @@ -80,25 +81,20 @@ pub(super) fn suggest_boxing_for_return_impl_trait( return_sp: Span, arm_spans: impl Iterator, ) { - err.multipart_suggestion( - "you could change the return type to be a boxed trait object", - vec![ - (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box".to_string()), - ], - Applicability::MaybeIncorrect, - ); - let sugg = arm_spans - .flat_map(|sp| { - [(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())] - .into_iter() - }) - .collect::>(); - err.multipart_suggestion( - "if you change the return type to expect trait objects, box the returned expressions", - sugg, - Applicability::MaybeIncorrect, - ); + let sugg = SuggestBoxingForReturnImplTrait::ChangeReturnType { + start_sp: return_sp.with_hi(return_sp.lo() + BytePos(4)), + end_sp: return_sp.shrink_to_hi(), + }; + err.subdiagnostic(sugg); + + let mut starts = Vec::new(); + let mut ends = Vec::new(); + for span in arm_spans { + starts.push(span.shrink_to_lo()); + ends.push(span.shrink_to_hi()); + } + let sugg = SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends }; + err.subdiagnostic(sugg); } pub(super) fn suggest_tuple_pattern( From d18adb7d560ce39c836b98a702c14ef2ae3a28a1 Mon Sep 17 00:00:00 2001 From: IQuant Date: Fri, 3 Mar 2023 18:56:45 +0300 Subject: [PATCH 05/16] Migrate SuggestTuplePattern --- compiler/rustc_infer/messages.ftl | 5 ++- compiler/rustc_infer/src/errors/mod.rs | 36 ++++++++++++++++++ .../src/infer/error_reporting/suggest.rs | 37 +++++++------------ 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 2534c1cbdab..cde0f8e35d0 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -364,4 +364,7 @@ infer_sarwa_result = you can convert from `&Result` to `Result<&T, &E>` us infer_suggest_accessing_field = you might have meant to use field `{$name}` whose type is `{$ty}` infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object -infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions \ No newline at end of file +infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions + +infer_stp_wrap_one = try wrapping the pattern in `{$variant}` +infer_stp_wrap_many = try wrapping the pattern in a variant of `{$path}` diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 5d72ad6fa05..439d8a381e4 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1335,3 +1335,39 @@ pub enum SuggestBoxingForReturnImplTrait { ends: Vec, }, } + +#[derive(Subdiagnostic)] +#[multipart_suggestion(infer_stp_wrap_one, applicability = "maybe-incorrect")] +pub struct SuggestTuplePatternOne { + pub variant: String, + #[suggestion_part(code = "{variant}(")] + pub span_low: Span, + #[suggestion_part(code = ")")] + pub span_high: Span, +} + +pub struct SuggestTuplePatternMany { + pub path: String, + pub cause_span: Span, + pub compatible_variants: Vec, +} + +impl AddToDiagnostic for SuggestTuplePatternMany { + fn add_to_diagnostic_with(self, diag: &mut rustc_errors::Diagnostic, f: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + diag.set_arg("path", self.path); + let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into()); + diag.multipart_suggestions( + message, + self.compatible_variants.into_iter().map(|variant| { + vec![ + (self.cause_span.shrink_to_lo(), format!("{}(", variant)), + (self.cause_span.shrink_to_hi(), ")".to_string()), + ] + }), + rustc_errors::Applicability::MaybeIncorrect, + ); + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 82ee365f3a0..e7f5fdaef74 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -1,7 +1,7 @@ use hir::def::CtorKind; use hir::intravisit::{walk_expr, walk_stmt, Visitor}; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_middle::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, @@ -16,7 +16,7 @@ ConsiderAddingAwait, DiagArg, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, FunctionPointerSuggestion, SuggAddLetForLetChains, SuggestAccessingField, SuggestAsRefWhereAppropriate, SuggestBoxingForReturnImplTrait, - SuggestRemoveSemiOrReturnBinding, + SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, }; use super::TypeErrCtxt; @@ -134,30 +134,21 @@ pub(super) fn suggest_tuple_pattern( match &compatible_variants[..] { [] => {} [variant] => { - diag.multipart_suggestion_verbose( - &format!("try wrapping the pattern in `{}`", variant), - vec![ - (cause.span.shrink_to_lo(), format!("{}(", variant)), - (cause.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MaybeIncorrect, - ); + let sugg = SuggestTuplePatternOne { + variant: variant.to_owned(), + span_low: cause.span.shrink_to_lo(), + span_high: cause.span.shrink_to_hi(), + }; + diag.subdiagnostic(sugg); } _ => { // More than one matching variant. - diag.multipart_suggestions( - &format!( - "try wrapping the pattern in a variant of `{}`", - self.tcx.def_path_str(expected_adt.did()) - ), - compatible_variants.into_iter().map(|variant| { - vec![ - (cause.span.shrink_to_lo(), format!("{}(", variant)), - (cause.span.shrink_to_hi(), ")".to_string()), - ] - }), - Applicability::MaybeIncorrect, - ); + let sugg = SuggestTuplePatternMany { + path: self.tcx.def_path_str(expected_adt.did()), + cause_span: cause.span, + compatible_variants, + }; + diag.subdiagnostic(sugg); } } } From b610ce72cec5e450410c50418117a608c5d2ef6a Mon Sep 17 00:00:00 2001 From: IQuant Date: Fri, 3 Mar 2023 22:03:12 +0300 Subject: [PATCH 06/16] Migrate TupleTrailingCommaSuggestion --- compiler/rustc_infer/messages.ftl | 2 ++ compiler/rustc_infer/src/errors/mod.rs | 16 ++++++++++++ .../src/infer/error_reporting/mod.rs | 25 ++++++++----------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index cde0f8e35d0..0f33660e50c 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -368,3 +368,5 @@ infer_sbfrit_box_return_expr = if you change the return type to expect trait obj infer_stp_wrap_one = try wrapping the pattern in `{$variant}` infer_stp_wrap_many = try wrapping the pattern in a variant of `{$path}` + +infer_tuple_trailing_comma = use a trailing comma to create a tuple with one element diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 439d8a381e4..6432ae7d3f8 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1371,3 +1371,19 @@ fn add_to_diagnostic_with(self, diag: &mut rustc_errors::Diagnostic, f: F) ); } } + +#[derive(Subdiagnostic)] +pub enum TupleTrailingCommaSuggestion { + #[suggestion(infer_tuple_trailing_comma, code = ",", applicability = "machine-applicable")] + OnlyComma { + #[primary_span] + span: Span, + }, + #[multipart_suggestion(infer_tuple_trailing_comma, applicability = "machine-applicable")] + AlsoParentheses { + #[suggestion_part(code = "(")] + span_low: Span, + #[suggestion_part(code = ",)")] + span_high: Span, + }, +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index c9956b60a56..a4345d3ab21 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -50,6 +50,7 @@ use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; use crate::errors; +use crate::errors::TupleTrailingCommaSuggestion; use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; @@ -2110,22 +2111,18 @@ fn emit_tuple_wrap_err( let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) else { return }; - let msg = "use a trailing comma to create a tuple with one element"; - if code.starts_with('(') && code.ends_with(')') { + let sugg = if code.starts_with('(') && code.ends_with(')') { let before_close = span.hi() - BytePos::from_u32(1); - err.span_suggestion( - span.with_hi(before_close).shrink_to_hi(), - msg, - ",", - Applicability::MachineApplicable, - ); + TupleTrailingCommaSuggestion::OnlyComma { + span: span.with_hi(before_close).shrink_to_hi(), + } } else { - err.multipart_suggestion( - msg, - vec![(span.shrink_to_lo(), "(".into()), (span.shrink_to_hi(), ",)".into())], - Applicability::MachineApplicable, - ); - } + TupleTrailingCommaSuggestion::AlsoParentheses { + span_low: span.shrink_to_lo(), + span_high: span.shrink_to_hi(), + } + }; + err.subdiagnostic(sugg); } fn values_str( From ab11b4389e9236bec5f8fa679900ca7156567a7f Mon Sep 17 00:00:00 2001 From: IQuant Date: Fri, 3 Mar 2023 23:33:39 +0300 Subject: [PATCH 07/16] FailureCode::Error0038 is unreachable, so can be removed --- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index a4345d3ab21..de9aa45c315 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -54,7 +54,6 @@ use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; -use crate::traits::error_reporting::report_object_safety_error; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, PredicateObligation, @@ -1912,10 +1911,6 @@ pub fn report_and_explain_type_error( let span = trace.cause.span(); let failure_code = trace.cause.as_failure_code(terr); let mut diag = match failure_code { - FailureCode::Error0038(did) => { - let violations = self.tcx.object_safety_violations(did); - report_object_safety_error(self.tcx, span, did, violations) - } FailureCode::Error0317(failure_str) => { struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str) } @@ -2825,7 +2820,6 @@ fn report_inference_failure( } pub enum FailureCode { - Error0038(DefId), Error0317(&'static str), Error0580(&'static str), Error0308(&'static str), From 1f09bc77c1b72ccd0a037cb0d5729749d3acb304 Mon Sep 17 00:00:00 2001 From: IQuant Date: Sat, 4 Mar 2023 13:35:30 +0300 Subject: [PATCH 08/16] Migrate (most of) report_and_explain_type_error --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 2 +- compiler/rustc_infer/messages.ftl | 22 + compiler/rustc_infer/src/errors/mod.rs | 173 ++++++- .../src/infer/error_reporting/mod.rs | 437 +++++++++--------- .../src/infer/error_reporting/suggest.rs | 14 +- 5 files changed, 408 insertions(+), 240 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 61338ac613a..9bdce1ee071 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -768,7 +768,7 @@ fn has_error_or_infer<'tcx>(tys: impl IntoIterator>) -> bool { let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; let trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); - if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { + if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); return true; } diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index 0f33660e50c..163992658e1 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -370,3 +370,25 @@ infer_stp_wrap_one = try wrapping the pattern in `{$variant}` infer_stp_wrap_many = try wrapping the pattern in a variant of `{$path}` infer_tuple_trailing_comma = use a trailing comma to create a tuple with one element + +infer_oc_method_compat = method not compatible with trait +infer_oc_type_compat = type not compatible with trait +infer_oc_const_compat = const not compatible with trait +infer_oc_try_compat = `?` operator has incompatible types +infer_oc_match_compat = `match` arms have incompatible types +infer_oc_if_else_different = `if` and `else` have incompatible types +infer_oc_no_else = `if` may be missing an `else` clause +infer_oc_no_diverge = `else` clause of `let...else` does not diverge +infer_oc_fn_main_correct_type = `main` function has wrong type +infer_oc_fn_start_correct_type = `#[start]` function has wrong type +infer_oc_intristic_correct_type = intrinsic has wrong type +infer_oc_method_correct_type = mismatched `self` parameter type +infer_oc_closure_selfref = closure/generator type that references itself +infer_oc_cant_coerce = cannot coerce intrinsics to function pointers +infer_oc_generic = mismatched types + +infer_meant_byte_literal = if you meant to write a byte literal, prefix with `b` +infer_meant_char_literal = if you meant to write a `char` literal, use single quotes +infer_meant_str_literal = if you meant to write a `str` literal, use double quotes +infer_consider_specifying_length = consider specifying the actual array length +infer_try_cannot_convert = `?` operator cannot convert from `{$found}` to `{$expected}` diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 6432ae7d3f8..f3bed305f84 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -184,18 +184,6 @@ pub enum SourceKindMultiSuggestion<'a> { }, } -#[derive(Subdiagnostic)] -#[suggestion( - infer_suggest_add_let_for_letchains, - style = "verbose", - applicability = "machine-applicable", - code = "let " -)] -pub(crate) struct SuggAddLetForLetChains { - #[primary_span] - pub span: Span, -} - impl<'a> SourceKindMultiSuggestion<'a> { pub fn new_fully_qualified( span: Span, @@ -1373,17 +1361,172 @@ fn add_to_diagnostic_with(self, diag: &mut rustc_errors::Diagnostic, f: F) } #[derive(Subdiagnostic)] -pub enum TupleTrailingCommaSuggestion { +pub enum Error0308Subdiags { + #[suggestion( + infer_meant_byte_literal, + code = "b'{code}'", + applicability = "machine-applicable" + )] + MeantByteLiteral { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + infer_meant_char_literal, + code = "'{code}'", + applicability = "machine-applicable" + )] + MeantCharLiteral { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + infer_meant_str_literal, + code = "\"{code}\"", + applicability = "machine-applicable" + )] + MeantStrLiteral { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + infer_consider_specifying_length, + code = "{length}", + applicability = "maybe-incorrect" + )] + ConsiderSpecifyingLength { + #[primary_span] + span: Span, + length: u64, + }, + #[note(infer_try_cannot_convert)] + TryCannotConvert { found: String, expected: String }, #[suggestion(infer_tuple_trailing_comma, code = ",", applicability = "machine-applicable")] - OnlyComma { + TupleOnlyComma { #[primary_span] span: Span, }, #[multipart_suggestion(infer_tuple_trailing_comma, applicability = "machine-applicable")] - AlsoParentheses { + TupleAlsoParentheses { #[suggestion_part(code = "(")] span_low: Span, #[suggestion_part(code = ",)")] span_high: Span, }, + #[suggestion( + infer_suggest_add_let_for_letchains, + style = "verbose", + applicability = "machine-applicable", + code = "let " + )] + AddLetForLetChains { + #[primary_span] + span: Span, + }, +} + +#[derive(Diagnostic)] +pub enum FailureCodeDiagnostics { + #[diag(infer_oc_method_compat, code = "E0308")] + MethodCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_type_compat, code = "E0308")] + TypeCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_const_compat, code = "E0308")] + ConstCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_try_compat, code = "E0308")] + TryCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_match_compat, code = "E0308")] + MatchCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_if_else_different, code = "E0308")] + IfElseDifferent { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_no_else, code = "E0317")] + NoElse { + #[primary_span] + span: Span, + }, + #[diag(infer_oc_no_diverge, code = "E0308")] + NoDiverge { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_fn_main_correct_type, code = "E0580")] + FnMainCorrectType { + #[primary_span] + span: Span, + }, + #[diag(infer_oc_fn_start_correct_type, code = "E0308")] + FnStartCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_intristic_correct_type, code = "E0308")] + IntristicCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_method_correct_type, code = "E0308")] + MethodCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_closure_selfref, code = "E0644")] + ClosureSelfref { + #[primary_span] + span: Span, + }, + #[diag(infer_oc_cant_coerce, code = "E0308")] + CantCoerce { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, + #[diag(infer_oc_generic, code = "E0308")] + Generic { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec, + }, } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index de9aa45c315..4b7f4fe1aaa 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -49,8 +49,7 @@ use super::region_constraints::GenericKind; use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; -use crate::errors; -use crate::errors::TupleTrailingCommaSuggestion; +use crate::errors::{self, Error0308Subdiags, FailureCodeDiagnostics}; use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; @@ -1899,225 +1898,196 @@ enum Similar<'tcx> { debug!(?diag); } + pub fn type_error_additional_suggestions( + &self, + trace: &TypeTrace<'tcx>, + terr: TypeError<'tcx>, + ) -> Vec { + use crate::traits::ObligationCauseCode::MatchExpressionArm; + fn escape_literal(s: &str) -> String { + let mut escaped = String::with_capacity(s.len()); + let mut chrs = s.chars().peekable(); + while let Some(first) = chrs.next() { + match (first, chrs.peek()) { + ('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => { + escaped.push('\\'); + escaped.push(delim); + chrs.next(); + } + ('"' | '\'', _) => { + escaped.push('\\'); + escaped.push(first) + } + (c, _) => escaped.push(c), + }; + } + escaped + } + let mut suggestions = Vec::new(); + let span = trace.cause.span(); + if let Some((expected, found)) = trace.values.ty() { + match (expected.kind(), found.kind()) { + (ty::Tuple(_), ty::Tuple(_)) => {} + // If a tuple of length one was expected and the found expression has + // parentheses around it, perhaps the user meant to write `(expr,)` to + // build a tuple (issue #86100) + (ty::Tuple(fields), _) => { + suggestions.extend(self.tuple_wrap_err_subdiag( span, found, fields)) + } + // If a byte was expected and the found expression is a char literal + // containing a single ASCII character, perhaps the user meant to write `b'c'` to + // specify a byte literal + (ty::Uint(ty::UintTy::U8), ty::Char) => { + if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) + && let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) + && !code.starts_with("\\u") // forbid all Unicode escapes + && code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII + { + suggestions.push(Error0308Subdiags::MeantByteLiteral { span, code: escape_literal(code) }) + } + } + // If a character was expected and the found expression is a string literal + // containing a single character, perhaps the user meant to write `'c'` to + // specify a character literal (issue #92479) + (ty::Char, ty::Ref(_, r, _)) if r.is_str() => { + if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) + && let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"')) + && code.chars().count() == 1 + { + suggestions.push(Error0308Subdiags::MeantCharLiteral { span, code: escape_literal(code) }) + } + } + // If a string was expected and the found expression is a character literal, + // perhaps the user meant to write `"s"` to specify a string literal. + (ty::Ref(_, r, _), ty::Char) if r.is_str() => { + if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { + if let Some(code) = + code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) + { + suggestions.push(Error0308Subdiags::MeantStrLiteral { span, code: escape_literal(code) }) + } + } + } + // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`, + // we try to suggest to add the missing `let` for `if let Some(..) = expr` + (ty::Bool, ty::Tuple(list)) => if list.len() == 0 { + suggestions.extend(self.suggest_let_for_letchains(&trace.cause, span)); + } + (ty::Array(_, _), ty::Array(_, _)) => 'block: { + let hir = self.tcx.hir(); + let TypeError::FixedArraySize(sz) = terr else { + break 'block; + }; + let tykind = match hir.find_by_def_id(trace.cause.body_id) { + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(_, _, body_id), + .. + })) => { + let body = hir.body(*body_id); + struct LetVisitor<'v> { + span: Span, + result: Option<&'v hir::Ty<'v>>, + } + impl<'v> Visitor<'v> for LetVisitor<'v> { + fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { + if self.result.is_some() { + return; + } + // Find a local statement where the initializer has + // the same span as the error and the type is specified. + if let hir::Stmt { + kind: hir::StmtKind::Local(hir::Local { + init: Some(hir::Expr { + span: init_span, + .. + }), + ty: Some(array_ty), + .. + }), + .. + } = s + && init_span == &self.span { + self.result = Some(*array_ty); + } + } + } + let mut visitor = LetVisitor {span, result: None}; + visitor.visit_body(body); + visitor.result.map(|r| &r.peel_refs().kind) + } + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Const(ty, _), + .. + })) => { + Some(&ty.peel_refs().kind) + } + _ => None + }; + + if let Some(tykind) = tykind + && let hir::TyKind::Array(_, length) = tykind + && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length + && let Some(span) = self.tcx.hir().opt_span(*hir_id) + { + suggestions.push(Error0308Subdiags::ConsiderSpecifyingLength { span, length: sz.found }); + } + } + _ => {} + } + } + let code = trace.cause.code(); + if let &MatchExpressionArm(box MatchExpressionArmCause { source, .. }) = code + && let hir::MatchSource::TryDesugar = source + && let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values) + { + suggestions.push(Error0308Subdiags::TryCannotConvert { found: found_ty.content(), expected: expected_ty.content() }); + } + suggestions + } + pub fn report_and_explain_type_error( &self, trace: TypeTrace<'tcx>, terr: TypeError<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - use crate::traits::ObligationCauseCode::MatchExpressionArm; - debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr); let span = trace.cause.span(); - let failure_code = trace.cause.as_failure_code(terr); - let mut diag = match failure_code { - FailureCode::Error0317(failure_str) => { - struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str) - } - FailureCode::Error0580(failure_str) => { - struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str) - } - FailureCode::Error0308(failure_str) => { - fn escape_literal(s: &str) -> String { - let mut escaped = String::with_capacity(s.len()); - let mut chrs = s.chars().peekable(); - while let Some(first) = chrs.next() { - match (first, chrs.peek()) { - ('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => { - escaped.push('\\'); - escaped.push(delim); - chrs.next(); - } - ('"' | '\'', _) => { - escaped.push('\\'); - escaped.push(first) - } - (c, _) => escaped.push(c), - }; - } - escaped - } - let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str); - let values = self.resolve_vars_if_possible(trace.values); - if let Some((expected, found)) = values.ty() { - match (expected.kind(), found.kind()) { - (ty::Tuple(_), ty::Tuple(_)) => {} - // If a tuple of length one was expected and the found expression has - // parentheses around it, perhaps the user meant to write `(expr,)` to - // build a tuple (issue #86100) - (ty::Tuple(fields), _) => { - self.emit_tuple_wrap_err(&mut err, span, found, fields) - } - // If a byte was expected and the found expression is a char literal - // containing a single ASCII character, perhaps the user meant to write `b'c'` to - // specify a byte literal - (ty::Uint(ty::UintTy::U8), ty::Char) => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) - && let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) - && !code.starts_with("\\u") // forbid all Unicode escapes - && code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII - { - err.span_suggestion( - span, - "if you meant to write a byte literal, prefix with `b`", - format!("b'{}'", escape_literal(code)), - Applicability::MachineApplicable, - ); - } - } - // If a character was expected and the found expression is a string literal - // containing a single character, perhaps the user meant to write `'c'` to - // specify a character literal (issue #92479) - (ty::Char, ty::Ref(_, r, _)) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) - && let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"')) - && code.chars().count() == 1 - { - err.span_suggestion( - span, - "if you meant to write a `char` literal, use single quotes", - format!("'{}'", escape_literal(code)), - Applicability::MachineApplicable, - ); - } - } - // If a string was expected and the found expression is a character literal, - // perhaps the user meant to write `"s"` to specify a string literal. - (ty::Ref(_, r, _), ty::Char) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { - if let Some(code) = - code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) - { - err.span_suggestion( - span, - "if you meant to write a `str` literal, use double quotes", - format!("\"{}\"", escape_literal(code)), - Applicability::MachineApplicable, - ); - } - } - } - // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`, - // we try to suggest to add the missing `let` for `if let Some(..) = expr` - (ty::Bool, ty::Tuple(list)) => if list.len() == 0 { - self.suggest_let_for_letchains(&mut err, &trace.cause, span); - } - (ty::Array(_, _), ty::Array(_, _)) => 'block: { - let hir = self.tcx.hir(); - let TypeError::FixedArraySize(sz) = terr else { - break 'block; - }; - let tykind = match hir.find_by_def_id(trace.cause.body_id) { - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(_, _, body_id), - .. - })) => { - let body = hir.body(*body_id); - struct LetVisitor<'v> { - span: Span, - result: Option<&'v hir::Ty<'v>>, - } - impl<'v> Visitor<'v> for LetVisitor<'v> { - fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { - if self.result.is_some() { - return; - } - // Find a local statement where the initializer has - // the same span as the error and the type is specified. - if let hir::Stmt { - kind: hir::StmtKind::Local(hir::Local { - init: Some(hir::Expr { - span: init_span, - .. - }), - ty: Some(array_ty), - .. - }), - .. - } = s - && init_span == &self.span { - self.result = Some(*array_ty); - } - } - } - let mut visitor = LetVisitor {span, result: None}; - visitor.visit_body(body); - visitor.result.map(|r| &r.peel_refs().kind) - } - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Const(ty, _), - .. - })) => { - Some(&ty.peel_refs().kind) - } - _ => None - }; - - if let Some(tykind) = tykind - && let hir::TyKind::Array(_, length) = tykind - && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length - && let Some(span) = self.tcx.hir().opt_span(*hir_id) - { - err.span_suggestion( - span, - "consider specifying the actual array length", - sz.found, - Applicability::MaybeIncorrect, - ); - } - } - _ => {} - } - } - let code = trace.cause.code(); - if let &MatchExpressionArm(box MatchExpressionArmCause { source, .. }) = code - && let hir::MatchSource::TryDesugar = source - && let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values) - { - err.note(&format!( - "`?` operator cannot convert from `{}` to `{}`", - found_ty.content(), - expected_ty.content(), - )); - } - err - } - FailureCode::Error0644(failure_str) => { - struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str) - } - }; + let failure_code = trace.cause.as_failure_code_diag( + terr, + span, + self.type_error_additional_suggestions(&trace, terr), + ); + let mut diag = self.tcx.sess.create_err(failure_code); self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false); diag } - fn emit_tuple_wrap_err( + fn tuple_wrap_err_subdiag( &self, - err: &mut Diagnostic, span: Span, found: Ty<'tcx>, expected_fields: &List>, - ) { - let [expected_tup_elem] = expected_fields[..] else { return }; + ) -> Option { + let [expected_tup_elem] = expected_fields[..] else { return None}; if !self.same_type_modulo_infer(expected_tup_elem, found) { - return; + return None; } let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) - else { return }; + else { return None }; let sugg = if code.starts_with('(') && code.ends_with(')') { let before_close = span.hi() - BytePos::from_u32(1); - TupleTrailingCommaSuggestion::OnlyComma { - span: span.with_hi(before_close).shrink_to_hi(), - } + Error0308Subdiags::TupleOnlyComma { span: span.with_hi(before_close).shrink_to_hi() } } else { - TupleTrailingCommaSuggestion::AlsoParentheses { + Error0308Subdiags::TupleAlsoParentheses { span_low: span.shrink_to_lo(), span_high: span.shrink_to_hi(), } }; - err.subdiagnostic(sugg); + Some(sugg) } fn values_str( @@ -2820,56 +2790,89 @@ fn report_inference_failure( } pub enum FailureCode { - Error0317(&'static str), - Error0580(&'static str), - Error0308(&'static str), - Error0644(&'static str), + Error0317, + Error0580, + Error0308, + Error0644, } pub trait ObligationCauseExt<'tcx> { fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode; + + fn as_failure_code_diag( + &self, + terr: TypeError<'tcx>, + span: Span, + subdiags: Vec, + ) -> FailureCodeDiagnostics; fn as_requirement_str(&self) -> &'static str; } impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode { use self::FailureCode::*; + use crate::traits::ObligationCauseCode::*; + match self.code() { + IfExpressionWithNoElse => Error0317, + MainFunctionType => Error0580, + CompareImplItemObligation { .. } + | MatchExpressionArm(_) + | IfExpression { .. } + | LetElse + | StartFunctionType + | IntrinsicType + | MethodReceiver => Error0308, + + // In the case where we have no more specific thing to + // say, also take a look at the error code, maybe we can + // tailor to that. + _ => match terr { + TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => Error0644, + TypeError::IntrinsicCast => Error0308, + _ => Error0308, + }, + } + } + fn as_failure_code_diag( + &self, + terr: TypeError<'tcx>, + span: Span, + subdiags: Vec, + ) -> FailureCodeDiagnostics { use crate::traits::ObligationCauseCode::*; match self.code() { CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => { - Error0308("method not compatible with trait") + FailureCodeDiagnostics::MethodCompat { span, subdiags } } CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => { - Error0308("type not compatible with trait") + FailureCodeDiagnostics::TypeCompat { span, subdiags } } CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => { - Error0308("const not compatible with trait") + FailureCodeDiagnostics::ConstCompat { span, subdiags } } - MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => { - Error0308(match source { - hir::MatchSource::TryDesugar => "`?` operator has incompatible types", - _ => "`match` arms have incompatible types", - }) - } - IfExpression { .. } => Error0308("`if` and `else` have incompatible types"), - IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"), - LetElse => Error0308("`else` clause of `let...else` does not diverge"), - MainFunctionType => Error0580("`main` function has wrong type"), - StartFunctionType => Error0308("`#[start]` function has wrong type"), - IntrinsicType => Error0308("intrinsic has wrong type"), - MethodReceiver => Error0308("mismatched `self` parameter type"), + MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source { + hir::MatchSource::TryDesugar => { + FailureCodeDiagnostics::TryCompat { span, subdiags } + } + _ => FailureCodeDiagnostics::MatchCompat { span, subdiags }, + }, + IfExpression { .. } => FailureCodeDiagnostics::IfElseDifferent { span, subdiags }, + IfExpressionWithNoElse => FailureCodeDiagnostics::NoElse { span }, + LetElse => FailureCodeDiagnostics::NoDiverge { span, subdiags }, + MainFunctionType => FailureCodeDiagnostics::FnMainCorrectType { span }, + StartFunctionType => FailureCodeDiagnostics::FnStartCorrectType { span, subdiags }, + IntrinsicType => FailureCodeDiagnostics::IntristicCorrectType { span, subdiags }, + MethodReceiver => FailureCodeDiagnostics::MethodCorrectType { span, subdiags }, // In the case where we have no more specific thing to // say, also take a look at the error code, maybe we can // tailor to that. _ => match terr { TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => { - Error0644("closure/generator type that references itself") + FailureCodeDiagnostics::ClosureSelfref { span } } - TypeError::IntrinsicCast => { - Error0308("cannot coerce intrinsics to function pointers") - } - _ => Error0308("mismatched types"), + TypeError::IntrinsicCast => FailureCodeDiagnostics::CantCoerce { span, subdiags }, + _ => FailureCodeDiagnostics::Generic { span, subdiags }, }, } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index e7f5fdaef74..fecfc53c86a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -13,10 +13,10 @@ use rustc_target::abi::FieldIdx; use crate::errors::{ - ConsiderAddingAwait, DiagArg, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, - FunctionPointerSuggestion, SuggAddLetForLetChains, SuggestAccessingField, - SuggestAsRefWhereAppropriate, SuggestBoxingForReturnImplTrait, - SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, + ConsiderAddingAwait, DiagArg, Error0308Subdiags, FnConsiderCasting, FnItemsAreDistinct, + FnUniqTypes, FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate, + SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, + SuggestTuplePatternOne, }; use super::TypeErrCtxt; @@ -482,10 +482,9 @@ pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Opti /// and then try to find a assignment in the `cond` part, which span is equal with error span pub(super) fn suggest_let_for_letchains( &self, - err: &mut Diagnostic, cause: &ObligationCause<'_>, span: Span, - ) { + ) -> Option { let hir = self.tcx.hir(); if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) && let hir::Node::Item(hir::Item { @@ -532,9 +531,10 @@ fn visit_body(&mut self, body: &'v hir::Body<'v>) { let mut visitor = IfVisitor { err_span: span, found_if: false, result: false }; visitor.visit_body(&body); if visitor.result { - err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()}); + return Some(Error0308Subdiags::AddLetForLetChains{span: span.shrink_to_lo()}); } } + None } } From e813b6d6e1ef76a94a68f98316d76517d7ce0719 Mon Sep 17 00:00:00 2001 From: IQuant Date: Thu, 9 Mar 2023 19:47:30 +0300 Subject: [PATCH 09/16] Update compiler/rustc_infer/src/infer/error_reporting/suggest.rs Co-authored-by: David Wood --- compiler/rustc_infer/src/infer/error_reporting/suggest.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index fecfc53c86a..a13c9651834 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -463,7 +463,7 @@ pub fn should_suggest_as_ref_kind( None } - // FIXME: Remove once rustc_hir_typeck is migrated to Diagnostics + // FIXME: Remove once `rustc_hir_typeck` is migrated to diagnostic structs 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( From aa33a6fca27f0b9183146e191ba565e37e7644eb Mon Sep 17 00:00:00 2001 From: IQuant Date: Thu, 9 Mar 2023 21:06:44 +0300 Subject: [PATCH 10/16] Move and document escape_literal function --- .../src/infer/error_reporting/mod.rs | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 4b7f4fe1aaa..212fea47437 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -89,6 +89,28 @@ pub mod nice_region_error; +/// Makes a valid string literal from a string by escaping special characters (" and \), +/// unless they are already escaped. +fn escape_literal(s: &str) -> String { + let mut escaped = String::with_capacity(s.len()); + let mut chrs = s.chars().peekable(); + while let Some(first) = chrs.next() { + match (first, chrs.peek()) { + ('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => { + escaped.push('\\'); + escaped.push(delim); + chrs.next(); + } + ('"' | '\'', _) => { + escaped.push('\\'); + escaped.push(first) + } + (c, _) => escaped.push(c), + }; + } + escaped +} + /// A helper for building type related errors. The `typeck_results` /// field is only populated during an in-progress typeck. /// Get an instance by calling `InferCtxt::err` or `FnCtxt::infer_err`. @@ -1904,25 +1926,6 @@ pub fn type_error_additional_suggestions( terr: TypeError<'tcx>, ) -> Vec { use crate::traits::ObligationCauseCode::MatchExpressionArm; - fn escape_literal(s: &str) -> String { - let mut escaped = String::with_capacity(s.len()); - let mut chrs = s.chars().peekable(); - while let Some(first) = chrs.next() { - match (first, chrs.peek()) { - ('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => { - escaped.push('\\'); - escaped.push(delim); - chrs.next(); - } - ('"' | '\'', _) => { - escaped.push('\\'); - escaped.push(first) - } - (c, _) => escaped.push(c), - }; - } - escaped - } let mut suggestions = Vec::new(); let span = trace.cause.span(); if let Some((expected, found)) = trace.values.ty() { From fd18d9a584230ad696302157dac1a1a38f1bda31 Mon Sep 17 00:00:00 2001 From: IQuant Date: Thu, 9 Mar 2023 21:27:42 +0300 Subject: [PATCH 11/16] Renamed TypeErrorAdditionalDiags (was Error0308Subdiags) and ObligationCauseFailureCode (was FailureCodeDiagnostics) --- compiler/rustc_infer/src/errors/mod.rs | 28 ++++----- .../src/infer/error_reporting/mod.rs | 62 ++++++++++--------- .../src/infer/error_reporting/suggest.rs | 10 +-- 3 files changed, 52 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index f3bed305f84..2a5d3516c28 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1361,7 +1361,7 @@ fn add_to_diagnostic_with(self, diag: &mut rustc_errors::Diagnostic, f: F) } #[derive(Subdiagnostic)] -pub enum Error0308Subdiags { +pub enum TypeErrorAdditionalDiags { #[suggestion( infer_meant_byte_literal, code = "b'{code}'", @@ -1429,48 +1429,48 @@ pub enum Error0308Subdiags { } #[derive(Diagnostic)] -pub enum FailureCodeDiagnostics { +pub enum ObligationCauseFailureCode { #[diag(infer_oc_method_compat, code = "E0308")] MethodCompat { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_type_compat, code = "E0308")] TypeCompat { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_const_compat, code = "E0308")] ConstCompat { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_try_compat, code = "E0308")] TryCompat { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_match_compat, code = "E0308")] MatchCompat { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_if_else_different, code = "E0308")] IfElseDifferent { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_no_else, code = "E0317")] NoElse { @@ -1482,7 +1482,7 @@ pub enum FailureCodeDiagnostics { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_fn_main_correct_type, code = "E0580")] FnMainCorrectType { @@ -1494,21 +1494,21 @@ pub enum FailureCodeDiagnostics { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_intristic_correct_type, code = "E0308")] IntristicCorrectType { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_method_correct_type, code = "E0308")] MethodCorrectType { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_closure_selfref, code = "E0644")] ClosureSelfref { @@ -1520,13 +1520,13 @@ pub enum FailureCodeDiagnostics { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, #[diag(infer_oc_generic, code = "E0308")] Generic { #[primary_span] span: Span, #[subdiagnostic] - subdiags: Vec, + subdiags: Vec, }, } diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 212fea47437..1cb901be4e9 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -49,7 +49,7 @@ use super::region_constraints::GenericKind; use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; -use crate::errors::{self, Error0308Subdiags, FailureCodeDiagnostics}; +use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags}; use crate::infer; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; @@ -1924,7 +1924,7 @@ pub fn type_error_additional_suggestions( &self, trace: &TypeTrace<'tcx>, terr: TypeError<'tcx>, - ) -> Vec { + ) -> Vec { use crate::traits::ObligationCauseCode::MatchExpressionArm; let mut suggestions = Vec::new(); let span = trace.cause.span(); @@ -1946,7 +1946,7 @@ pub fn type_error_additional_suggestions( && !code.starts_with("\\u") // forbid all Unicode escapes && code.chars().next().map_or(false, |c| c.is_ascii()) // forbids literal Unicode characters beyond ASCII { - suggestions.push(Error0308Subdiags::MeantByteLiteral { span, code: escape_literal(code) }) + suggestions.push(TypeErrorAdditionalDiags::MeantByteLiteral { span, code: escape_literal(code) }) } } // If a character was expected and the found expression is a string literal @@ -1957,7 +1957,7 @@ pub fn type_error_additional_suggestions( && let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"')) && code.chars().count() == 1 { - suggestions.push(Error0308Subdiags::MeantCharLiteral { span, code: escape_literal(code) }) + suggestions.push(TypeErrorAdditionalDiags::MeantCharLiteral { span, code: escape_literal(code) }) } } // If a string was expected and the found expression is a character literal, @@ -1967,7 +1967,7 @@ pub fn type_error_additional_suggestions( if let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) { - suggestions.push(Error0308Subdiags::MeantStrLiteral { span, code: escape_literal(code) }) + suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { span, code: escape_literal(code) }) } } } @@ -2032,7 +2032,7 @@ fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length && let Some(span) = self.tcx.hir().opt_span(*hir_id) { - suggestions.push(Error0308Subdiags::ConsiderSpecifyingLength { span, length: sz.found }); + suggestions.push(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found }); } } _ => {} @@ -2043,7 +2043,7 @@ fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { && let hir::MatchSource::TryDesugar = source && let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values) { - suggestions.push(Error0308Subdiags::TryCannotConvert { found: found_ty.content(), expected: expected_ty.content() }); + suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert { found: found_ty.content(), expected: expected_ty.content() }); } suggestions } @@ -2071,7 +2071,7 @@ fn tuple_wrap_err_subdiag( span: Span, found: Ty<'tcx>, expected_fields: &List>, - ) -> Option { + ) -> Option { let [expected_tup_elem] = expected_fields[..] else { return None}; if !self.same_type_modulo_infer(expected_tup_elem, found) { @@ -2083,9 +2083,11 @@ fn tuple_wrap_err_subdiag( let sugg = if code.starts_with('(') && code.ends_with(')') { let before_close = span.hi() - BytePos::from_u32(1); - Error0308Subdiags::TupleOnlyComma { span: span.with_hi(before_close).shrink_to_hi() } + TypeErrorAdditionalDiags::TupleOnlyComma { + span: span.with_hi(before_close).shrink_to_hi(), + } } else { - Error0308Subdiags::TupleAlsoParentheses { + TypeErrorAdditionalDiags::TupleAlsoParentheses { span_low: span.shrink_to_lo(), span_high: span.shrink_to_hi(), } @@ -2806,8 +2808,8 @@ fn as_failure_code_diag( &self, terr: TypeError<'tcx>, span: Span, - subdiags: Vec, - ) -> FailureCodeDiagnostics; + subdiags: Vec, + ) -> ObligationCauseFailureCode; fn as_requirement_str(&self) -> &'static str; } @@ -2840,42 +2842,44 @@ fn as_failure_code_diag( &self, terr: TypeError<'tcx>, span: Span, - subdiags: Vec, - ) -> FailureCodeDiagnostics { + subdiags: Vec, + ) -> ObligationCauseFailureCode { use crate::traits::ObligationCauseCode::*; match self.code() { CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => { - FailureCodeDiagnostics::MethodCompat { span, subdiags } + ObligationCauseFailureCode::MethodCompat { span, subdiags } } CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => { - FailureCodeDiagnostics::TypeCompat { span, subdiags } + ObligationCauseFailureCode::TypeCompat { span, subdiags } } CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => { - FailureCodeDiagnostics::ConstCompat { span, subdiags } + ObligationCauseFailureCode::ConstCompat { span, subdiags } } MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source { hir::MatchSource::TryDesugar => { - FailureCodeDiagnostics::TryCompat { span, subdiags } + ObligationCauseFailureCode::TryCompat { span, subdiags } } - _ => FailureCodeDiagnostics::MatchCompat { span, subdiags }, + _ => ObligationCauseFailureCode::MatchCompat { span, subdiags }, }, - IfExpression { .. } => FailureCodeDiagnostics::IfElseDifferent { span, subdiags }, - IfExpressionWithNoElse => FailureCodeDiagnostics::NoElse { span }, - LetElse => FailureCodeDiagnostics::NoDiverge { span, subdiags }, - MainFunctionType => FailureCodeDiagnostics::FnMainCorrectType { span }, - StartFunctionType => FailureCodeDiagnostics::FnStartCorrectType { span, subdiags }, - IntrinsicType => FailureCodeDiagnostics::IntristicCorrectType { span, subdiags }, - MethodReceiver => FailureCodeDiagnostics::MethodCorrectType { span, subdiags }, + IfExpression { .. } => ObligationCauseFailureCode::IfElseDifferent { span, subdiags }, + IfExpressionWithNoElse => ObligationCauseFailureCode::NoElse { span }, + LetElse => ObligationCauseFailureCode::NoDiverge { span, subdiags }, + MainFunctionType => ObligationCauseFailureCode::FnMainCorrectType { span }, + StartFunctionType => ObligationCauseFailureCode::FnStartCorrectType { span, subdiags }, + IntrinsicType => ObligationCauseFailureCode::IntristicCorrectType { span, subdiags }, + MethodReceiver => ObligationCauseFailureCode::MethodCorrectType { span, subdiags }, // In the case where we have no more specific thing to // say, also take a look at the error code, maybe we can // tailor to that. _ => match terr { TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => { - FailureCodeDiagnostics::ClosureSelfref { span } + ObligationCauseFailureCode::ClosureSelfref { span } } - TypeError::IntrinsicCast => FailureCodeDiagnostics::CantCoerce { span, subdiags }, - _ => FailureCodeDiagnostics::Generic { span, subdiags }, + TypeError::IntrinsicCast => { + ObligationCauseFailureCode::CantCoerce { span, subdiags } + } + _ => ObligationCauseFailureCode::Generic { span, subdiags }, }, } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index a13c9651834..82cd05e3d07 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -13,10 +13,10 @@ use rustc_target::abi::FieldIdx; use crate::errors::{ - ConsiderAddingAwait, DiagArg, Error0308Subdiags, FnConsiderCasting, FnItemsAreDistinct, - FnUniqTypes, FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate, + ConsiderAddingAwait, DiagArg, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, + FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate, SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, - SuggestTuplePatternOne, + SuggestTuplePatternOne, TypeErrorAdditionalDiags, }; use super::TypeErrCtxt; @@ -484,7 +484,7 @@ pub(super) fn suggest_let_for_letchains( &self, cause: &ObligationCause<'_>, span: Span, - ) -> Option { + ) -> Option { let hir = self.tcx.hir(); if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) && let hir::Node::Item(hir::Item { @@ -531,7 +531,7 @@ fn visit_body(&mut self, body: &'v hir::Body<'v>) { let mut visitor = IfVisitor { err_span: span, found_if: false, result: false }; visitor.visit_body(&body); if visitor.result { - return Some(Error0308Subdiags::AddLetForLetChains{span: span.shrink_to_lo()}); + return Some(TypeErrorAdditionalDiags::AddLetForLetChains{span: span.shrink_to_lo()}); } } None From 6a05cd85adf99445a9064a0bbc71b507fcb3856e Mon Sep 17 00:00:00 2001 From: IQuant Date: Thu, 9 Mar 2023 21:59:40 +0300 Subject: [PATCH 12/16] A more general implementation of `IntoDiagnosticArg` for `Binder` (Also removes `DiagArg`, as it's no longer necessary) --- compiler/rustc_infer/src/errors/mod.rs | 12 ++-------- .../src/infer/error_reporting/suggest.rs | 6 ++--- compiler/rustc_middle/src/ty/sty.rs | 24 +++++++++---------- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 2a5d3516c28..b129621130d 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1146,14 +1146,6 @@ 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( @@ -1221,7 +1213,7 @@ pub enum FunctionPointerSuggestion<'a> { fn_name: String, #[skip_arg] found_sig: Binder<'a, FnSig<'a>>, - expected_sig: DiagArg>>, + expected_sig: Binder<'a, FnSig<'a>>, }, #[suggestion( infer_fps_cast_both, @@ -1236,7 +1228,7 @@ pub enum FunctionPointerSuggestion<'a> { fn_name: String, #[skip_arg] found_sig: Binder<'a, FnSig<'a>>, - expected_sig: DiagArg>>, + expected_sig: Binder<'a, FnSig<'a>>, }, } diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 82cd05e3d07..b5aeca12a1f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -13,7 +13,7 @@ use rustc_target::abi::FieldIdx; use crate::errors::{ - ConsiderAddingAwait, DiagArg, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, + ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate, SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags, @@ -379,14 +379,14 @@ pub(super) fn suggest_function_pointers( span, fn_name, found_sig: *found_sig, - expected_sig: DiagArg(*expected_sig), + expected_sig: *expected_sig, } } else { FunctionPointerSuggestion::CastBoth { span, fn_name, found_sig: *found_sig, - expected_sig: DiagArg(*expected_sig), + expected_sig: *expected_sig, } }; diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 5ea77833af2..061f26384bd 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -15,6 +15,7 @@ use polonius_engine::Atom; use rustc_data_structures::captures::Captures; use rustc_data_structures::intern::Interned; +use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::LangItem; @@ -26,7 +27,7 @@ use rustc_target::spec::abi::{self, Abi}; use std::borrow::Cow; use std::cmp::Ordering; -use std::fmt; +use std::fmt::{self, Display}; use std::marker::PhantomData; use std::ops::{ControlFlow, Deref, Range}; use ty::util::IntTypeExt; @@ -877,12 +878,6 @@ pub fn def_id(&self) -> DefId { } } -impl rustc_errors::IntoDiagnosticArg for PolyTraitRef<'_> { - fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { - self.to_string().into_diagnostic_arg() - } -} - /// An existential reference to a trait, where `Self` is erased. /// For example, the trait object `Trait<'a, 'b, X, Y>` is: /// ```ignore (illustrative) @@ -939,12 +934,6 @@ pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::PolyTrai } } -impl rustc_errors::IntoDiagnosticArg for PolyExistentialTraitRef<'_> { - fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { - self.to_string().into_diagnostic_arg() - } -} - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable)] pub enum BoundVariableKind { @@ -1159,6 +1148,15 @@ pub fn iter(self) -> impl Iterator> { } } +impl<'tcx, T> IntoDiagnosticArg for Binder<'tcx, T> +where + Binder<'tcx, T>: Display, +{ + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } +} + struct SkipBindersAt<'tcx> { tcx: TyCtxt<'tcx>, index: ty::DebruijnIndex, From 21d5bedd5f7564d62096e485f2fcb6a8956f1dec Mon Sep 17 00:00:00 2001 From: IQuant Date: Fri, 10 Mar 2023 19:49:44 +0300 Subject: [PATCH 13/16] Extract suggest_specify_actual_length into a separate function --- .../src/infer/error_reporting/mod.rs | 120 +++++++++--------- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 1cb901be4e9..3bb4e1c6b29 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1976,65 +1976,7 @@ pub fn type_error_additional_suggestions( (ty::Bool, ty::Tuple(list)) => if list.len() == 0 { suggestions.extend(self.suggest_let_for_letchains(&trace.cause, span)); } - (ty::Array(_, _), ty::Array(_, _)) => 'block: { - let hir = self.tcx.hir(); - let TypeError::FixedArraySize(sz) = terr else { - break 'block; - }; - let tykind = match hir.find_by_def_id(trace.cause.body_id) { - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(_, _, body_id), - .. - })) => { - let body = hir.body(*body_id); - struct LetVisitor<'v> { - span: Span, - result: Option<&'v hir::Ty<'v>>, - } - impl<'v> Visitor<'v> for LetVisitor<'v> { - fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { - if self.result.is_some() { - return; - } - // Find a local statement where the initializer has - // the same span as the error and the type is specified. - if let hir::Stmt { - kind: hir::StmtKind::Local(hir::Local { - init: Some(hir::Expr { - span: init_span, - .. - }), - ty: Some(array_ty), - .. - }), - .. - } = s - && init_span == &self.span { - self.result = Some(*array_ty); - } - } - } - let mut visitor = LetVisitor {span, result: None}; - visitor.visit_body(body); - visitor.result.map(|r| &r.peel_refs().kind) - } - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Const(ty, _), - .. - })) => { - Some(&ty.peel_refs().kind) - } - _ => None - }; - - if let Some(tykind) = tykind - && let hir::TyKind::Array(_, length) = tykind - && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length - && let Some(span) = self.tcx.hir().opt_span(*hir_id) - { - suggestions.push(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found }); - } - } + (ty::Array(_, _), ty::Array(_, _)) => suggestions.extend(self.specify_actual_length(terr, trace, span)), _ => {} } } @@ -2048,6 +1990,66 @@ fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { suggestions } + fn specify_actual_length( + &self, + terr: TypeError<'_>, + trace: &TypeTrace<'_>, + span: Span, + ) -> Option { + let hir = self.tcx.hir(); + let TypeError::FixedArraySize(sz) = terr else { + return None; + }; + let tykind = match hir.find_by_def_id(trace.cause.body_id) { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => { + let body = hir.body(*body_id); + struct LetVisitor<'v> { + span: Span, + result: Option<&'v hir::Ty<'v>>, + } + impl<'v> Visitor<'v> for LetVisitor<'v> { + fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { + if self.result.is_some() { + return; + } + // Find a local statement where the initializer has + // the same span as the error and the type is specified. + if let hir::Stmt { + kind: hir::StmtKind::Local(hir::Local { + init: Some(hir::Expr { + span: init_span, + .. + }), + ty: Some(array_ty), + .. + }), + .. + } = s + && init_span == &self.span { + self.result = Some(*array_ty); + } + } + } + let mut visitor = LetVisitor { span, result: None }; + visitor.visit_body(body); + visitor.result.map(|r| &r.peel_refs().kind) + } + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. })) => { + Some(&ty.peel_refs().kind) + } + _ => None, + }; + if let Some(tykind) = tykind + && let hir::TyKind::Array(_, length) = tykind + && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length + && let Some(span) = self.tcx.hir().opt_span(*hir_id) + { + Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found }) + } else { + None + } + } + pub fn report_and_explain_type_error( &self, trace: TypeTrace<'tcx>, From 467d367df75121dd305626ea2fb4459af588d29c Mon Sep 17 00:00:00 2001 From: IQuant Date: Sat, 11 Mar 2023 17:39:14 +0300 Subject: [PATCH 14/16] Rename tuple_wrap_err_subdiag to suggest_wrap_to_build_a_tuple, making it more consistent with similar functions --- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 3bb4e1c6b29..ca4f1c20b89 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1935,7 +1935,7 @@ pub fn type_error_additional_suggestions( // parentheses around it, perhaps the user meant to write `(expr,)` to // build a tuple (issue #86100) (ty::Tuple(fields), _) => { - suggestions.extend(self.tuple_wrap_err_subdiag( span, found, fields)) + suggestions.extend(self.suggest_wrap_to_build_a_tuple( span, found, fields)) } // If a byte was expected and the found expression is a char literal // containing a single ASCII character, perhaps the user meant to write `b'c'` to @@ -1976,7 +1976,7 @@ pub fn type_error_additional_suggestions( (ty::Bool, ty::Tuple(list)) => if list.len() == 0 { suggestions.extend(self.suggest_let_for_letchains(&trace.cause, span)); } - (ty::Array(_, _), ty::Array(_, _)) => suggestions.extend(self.specify_actual_length(terr, trace, span)), + (ty::Array(_, _), ty::Array(_, _)) => suggestions.extend(self.suggest_specify_actual_length(terr, trace, span)), _ => {} } } @@ -1990,7 +1990,7 @@ pub fn type_error_additional_suggestions( suggestions } - fn specify_actual_length( + fn suggest_specify_actual_length( &self, terr: TypeError<'_>, trace: &TypeTrace<'_>, @@ -2068,7 +2068,7 @@ pub fn report_and_explain_type_error( diag } - fn tuple_wrap_err_subdiag( + fn suggest_wrap_to_build_a_tuple( &self, span: Span, found: Ty<'tcx>, From 3fb6d6b2f422943cea592f64e4ae8984d6b86f1f Mon Sep 17 00:00:00 2001 From: IQuant Date: Tue, 4 Apr 2023 19:18:34 +0300 Subject: [PATCH 15/16] Now passes tests --- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index ca4f1c20b89..34dfcea81d1 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1928,7 +1928,8 @@ pub fn type_error_additional_suggestions( use crate::traits::ObligationCauseCode::MatchExpressionArm; let mut suggestions = Vec::new(); let span = trace.cause.span(); - if let Some((expected, found)) = trace.values.ty() { + let values = self.resolve_vars_if_possible(trace.values); + if let Some((expected, found)) = values.ty() { match (expected.kind(), found.kind()) { (ty::Tuple(_), ty::Tuple(_)) => {} // If a tuple of length one was expected and the found expression has From d07b1cd4a8e0f88b2b93090172ec4d257210d172 Mon Sep 17 00:00:00 2001 From: IQuant Date: Mon, 10 Apr 2023 18:08:35 +0300 Subject: [PATCH 16/16] Call `into_diagnostic_arg` on Binder's contained value directly. --- compiler/rustc_middle/src/ty/sty.rs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 061f26384bd..7b84c4e41d6 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -27,7 +27,7 @@ use rustc_target::spec::abi::{self, Abi}; use std::borrow::Cow; use std::cmp::Ordering; -use std::fmt::{self, Display}; +use std::fmt; use std::marker::PhantomData; use std::ops::{ControlFlow, Deref, Range}; use ty::util::IntTypeExt; @@ -878,6 +878,12 @@ pub fn def_id(&self) -> DefId { } } +impl<'tcx> IntoDiagnosticArg for TraitRef<'tcx> { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } +} + /// An existential reference to a trait, where `Self` is erased. /// For example, the trait object `Trait<'a, 'b, X, Y>` is: /// ```ignore (illustrative) @@ -918,6 +924,12 @@ pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::TraitRef } } +impl<'tcx> IntoDiagnosticArg for ExistentialTraitRef<'tcx> { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } +} + pub type PolyExistentialTraitRef<'tcx> = Binder<'tcx, ExistentialTraitRef<'tcx>>; impl<'tcx> PolyExistentialTraitRef<'tcx> { @@ -1150,10 +1162,10 @@ pub fn iter(self) -> impl Iterator> { impl<'tcx, T> IntoDiagnosticArg for Binder<'tcx, T> where - Binder<'tcx, T>: Display, + T: IntoDiagnosticArg, { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - self.to_string().into_diagnostic_arg() + self.0.into_diagnostic_arg() } } @@ -1373,6 +1385,12 @@ fn fake() -> FnSig<'tcx> { } } +impl<'tcx> IntoDiagnosticArg for FnSig<'tcx> { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + self.to_string().into_diagnostic_arg() + } +} + pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>; impl<'tcx> PolyFnSig<'tcx> {