diff options
| author | bors <bors@rust-lang.org> | 2023-06-09 03:20:22 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-06-09 03:20:22 +0000 |
| commit | 9c843d9fa322596c7d525c78fa89731ecf7afbfe (patch) | |
| tree | bce25222317f45213741940b2e11552009a763b2 /compiler | |
| parent | 68c8fdaac071432c0a5c149ece5c094449fbe8e0 (diff) | |
| parent | a9188226a87618f98ae039682e7616a50bcf6c23 (diff) | |
Auto merge of #112116 - compiler-errors:misc-hir-typeck-mismatch-tweaks, r=WaffleLapkin
Misc HIR typeck type mismatch tweaks These are all intended to improve #112104, but I couldn't get it to actually suggest adding `as_ref` to the LHS of the equality expr without some hacks that I may play around with some more. Each commit's title should explain what it's doing except for perhaps the last one, which addresses the bogus suggestion on #112104 itself.
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_hir_typeck/messages.ftl | 2 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/demand.rs | 60 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/errors.rs | 16 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 12 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 235 | ||||
| -rw-r--r-- | compiler/rustc_infer/messages.ftl | 3 | ||||
| -rw-r--r-- | compiler/rustc_infer/src/errors/mod.rs | 24 | ||||
| -rw-r--r-- | compiler/rustc_infer/src/infer/error_reporting/mod.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_infer/src/infer/error_reporting/suggest.rs | 27 |
10 files changed, 245 insertions, 141 deletions
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index aab432eee57..4728edd837a 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -25,6 +25,8 @@ hir_typeck_const_select_must_be_fn = this argument must be a function item hir_typeck_convert_to_str = try converting the passed type into a `&str` +hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to `{$expected}` + hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private hir_typeck_expected_default_return_type = expected `()` because of default return type diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index b50630e636b..b62f689ec6b 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -3,7 +3,7 @@ use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::MultiSpan; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_hir::def::CtorKind; +use rustc_hir::def::{CtorKind, Res}; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; @@ -91,6 +91,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_wrong_return_ty_due_to_generic_arg(err, expr, expr_ty); } + /// Really hacky heuristic to remap an `assert_eq!` error to the user + /// expressions provided to the macro. + fn adjust_expr_for_assert_eq_macro( + &self, + found_expr: &mut &'tcx hir::Expr<'tcx>, + expected_expr: &mut Option<&'tcx hir::Expr<'tcx>>, + ) { + let Some(expected_expr) = expected_expr else { return; }; + + if !found_expr.span.eq_ctxt(expected_expr.span) { + return; + } + + if !found_expr + .span + .ctxt() + .outer_expn_data() + .macro_def_id + .is_some_and(|def_id| self.tcx.is_diagnostic_item(sym::assert_eq_macro, def_id)) + { + return; + } + + let hir::ExprKind::Unary( + hir::UnOp::Deref, + hir::Expr { kind: hir::ExprKind::Path(found_path), .. }, + ) = found_expr.kind else { return; }; + let hir::ExprKind::Unary( + hir::UnOp::Deref, + hir::Expr { kind: hir::ExprKind::Path(expected_path), .. }, + ) = expected_expr.kind else { return; }; + + for (path, name, idx, var) in [ + (expected_path, "left_val", 0, expected_expr), + (found_path, "right_val", 1, found_expr), + ] { + if let hir::QPath::Resolved(_, path) = path + && let [segment] = path.segments + && segment.ident.name.as_str() == name + && let Res::Local(hir_id) = path.res + && let Some((_, hir::Node::Expr(match_expr))) = self.tcx.hir().parent_iter(hir_id).nth(2) + && let hir::ExprKind::Match(scrutinee, _, _) = match_expr.kind + && let hir::ExprKind::Tup(exprs) = scrutinee.kind + && let hir::ExprKind::AddrOf(_, _, macro_arg) = exprs[idx].kind + { + *var = macro_arg; + } + } + } + /// Requires that the two types unify, and prints an error message if /// they don't. pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { @@ -156,7 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn demand_coerce( &self, - expr: &hir::Expr<'tcx>, + expr: &'tcx hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, @@ -177,10 +227,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] pub fn demand_coerce_diag( &self, - expr: &hir::Expr<'tcx>, + mut expr: &'tcx hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + mut expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, allow_two_phase: AllowTwoPhase, ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>) { let expected = self.resolve_vars_with_obligations(expected); @@ -190,6 +240,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(e) => e, }; + self.adjust_expr_for_assert_eq_macro(&mut expr, &mut expected_ty_expr); + self.set_tainted_by_errors(self.tcx.sess.delay_span_bug( expr.span, "`TypeError` when attempting coercion but no error emitted", diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 4222205c841..5161a366ae7 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -327,3 +327,19 @@ pub struct CtorIsPrivate { pub span: Span, pub def: String, } + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + hir_typeck_convert_using_method, + applicability = "machine-applicable", + style = "verbose" +)] +pub struct SuggestConvertViaMethod<'tcx> { + #[suggestion_part(code = "{sugg}")] + pub span: Span, + #[suggestion_part(code = "")] + pub borrow_removal_span: Option<Span>, + pub sugg: &'static str, + pub expected: Ty<'tcx>, + pub found: Ty<'tcx>, +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 30a543aab50..48c3d6f08de 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1404,7 +1404,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // type of the place it is referencing, and not some // supertype thereof. let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); - self.demand_eqtype(init.span, local_ty, init_ty); + if let Some(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) { + self.emit_type_mismatch_suggestions( + &mut diag, + init.peel_drop_temps(), + init_ty, + local_ty, + None, + None, + ); + diag.emit(); + } init_ty } else { self.check_expr_coercible_to_type(init, local_ty, None) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index e19b0664461..3a4fe334f88 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1,6 +1,8 @@ use super::FnCtxt; -use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing}; +use crate::errors::{ + AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod, +}; use crate::fluent_generated as fluent; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; @@ -275,6 +277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> bool { let expr = expr.peel_blocks(); + let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); + if let Some((suggestion, msg, applicability, verbose, annotation)) = self.suggest_deref_or_ref(expr, found, expected) { @@ -325,9 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } return true; - } else if self.suggest_else_fn_with_closure(err, expr, found, expected) { + } + + if self.suggest_else_fn_with_closure(err, expr, found, expected) { return true; - } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) + } + + if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) && let ty::FnDef(def_id, ..) = *found.kind() && let Some(sp) = self.tcx.hir().span_if_local(def_id) { @@ -343,97 +351,156 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(sp, format!("{descr} `{name}` defined here")); } return true; - } else if self.suggest_cast(err, expr, found, expected, expected_ty_expr) { + } + + if self.suggest_cast(err, expr, found, expected, expected_ty_expr) { return true; - } else { - let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); - if !methods.is_empty() { - let mut suggestions = methods.iter() - .filter_map(|conversion_method| { - let receiver_method_ident = expr.method_ident(); - if let Some(method_ident) = receiver_method_ident - && method_ident.name == conversion_method.name - { - return None // do not suggest code that is already there (#53348) - } + } - let method_call_list = [sym::to_vec, sym::to_string]; - let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind - && receiver_method.ident.name == sym::clone - && method_call_list.contains(&conversion_method.name) - // If receiver is `.clone()` and found type has one of those methods, - // we guess that the user wants to convert from a slice type (`&[]` or `&str`) - // to an owned type (`Vec` or `String`). These conversions clone internally, - // so we remove the user's `clone` call. - { - vec![( - receiver_method.ident.span, - conversion_method.name.to_string() - )] - } else if expr.precedence().order() - < ExprPrecedence::MethodCall.order() - { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), - ] - } else { - vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] - }; - let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); - if let Some(name) = struct_pat_shorthand_field { - sugg.insert( - 0, - (expr.span.shrink_to_lo(), format!("{}: ", name)), - ); - } - Some(sugg) - }) - .peekable(); - if suggestions.peek().is_some() { - err.multipart_suggestions( - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); - return true; - } - } else if let ty::Adt(found_adt, found_substs) = found.kind() - && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) - && let ty::Adt(expected_adt, expected_substs) = expected.kind() - && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) - && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind() - && inner_ty.is_str() - { - let ty = found_substs.type_at(0); - let mut peeled = ty; - let mut ref_cnt = 0; - while let ty::Ref(_, inner, _) = peeled.kind() { - peeled = *inner; - ref_cnt += 1; - } - if let ty::Adt(adt, _) = peeled.kind() - && Some(adt.did()) == self.tcx.lang_items().string() - { - let sugg = if ref_cnt == 0 { - ".as_deref()" + if !methods.is_empty() { + let mut suggestions = methods + .iter() + .filter_map(|conversion_method| { + let receiver_method_ident = expr.method_ident(); + if let Some(method_ident) = receiver_method_ident + && method_ident.name == conversion_method.name + { + return None // do not suggest code that is already there (#53348) + } + + let method_call_list = [sym::to_vec, sym::to_string]; + let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind + && receiver_method.ident.name == sym::clone + && method_call_list.contains(&conversion_method.name) + // If receiver is `.clone()` and found type has one of those methods, + // we guess that the user wants to convert from a slice type (`&[]` or `&str`) + // to an owned type (`Vec` or `String`). These conversions clone internally, + // so we remove the user's `clone` call. + { + vec![( + receiver_method.ident.span, + conversion_method.name.to_string() + )] + } else if expr.precedence().order() + < ExprPrecedence::MethodCall.order() + { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), + ] } else { - ".map(|x| x.as_str())" + vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] }; - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - fluent::hir_typeck_convert_to_str, - sugg, - Applicability::MachineApplicable, - ); - return true; - } + let struct_pat_shorthand_field = + self.maybe_get_struct_pattern_shorthand_field(expr); + if let Some(name) = struct_pat_shorthand_field { + sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name))); + } + Some(sugg) + }) + .peekable(); + if suggestions.peek().is_some() { + err.multipart_suggestions( + "try using a conversion method", + suggestions, + Applicability::MaybeIncorrect, + ); + return true; + } + } + + if let Some((found_ty_inner, expected_ty_inner, error_tys)) = + self.deconstruct_option_or_result(found, expected) + && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind() + { + // Suggest removing any stray borrows (unless there's macro shenanigans involved). + let inner_expr = expr.peel_borrows(); + if !inner_expr.span.eq_ctxt(expr.span) { + return false; + } + let borrow_removal_span = if inner_expr.hir_id == expr.hir_id { + None + } else { + Some(expr.span.shrink_to_lo().until(inner_expr.span)) + }; + // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for + // `as_ref` and `as_deref` compatibility. + let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| { + self.can_eq(self.param_env, self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, found), expected) + }); + // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`, + // but those checks need to be a bit more delicate and the benefit is diminishing. + if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref { + err.subdiagnostic(SuggestConvertViaMethod { + span: expr.span.shrink_to_hi(), + sugg: ".as_ref()", + expected, + found, + borrow_removal_span, + }); + return true; + } else if let Some((deref_ty, _)) = + self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1) + && self.can_eq(self.param_env, deref_ty, peeled) + && error_tys_equate_as_ref + { + err.subdiagnostic(SuggestConvertViaMethod { + span: expr.span.shrink_to_hi(), + sugg: ".as_deref()", + expected, + found, + borrow_removal_span, + }); + return true; + } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind() + && Some(adt.did()) == self.tcx.lang_items().string() + && peeled.is_str() + // `Result::map`, conversely, does not take ref of the error type. + && error_tys.map_or(true, |(found, expected)| { + self.can_eq(self.param_env, found, expected) + }) + { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + fluent::hir_typeck_convert_to_str, + ".map(|x| x.as_str())", + Applicability::MachineApplicable, + ); + return true; } } false } + fn deconstruct_option_or_result( + &self, + found_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> { + let ty::Adt(found_adt, found_substs) = found_ty.peel_refs().kind() else { + return None; + }; + let ty::Adt(expected_adt, expected_substs) = expected_ty.kind() else { + return None; + }; + if self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) + && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) + { + Some((found_substs.type_at(0), expected_substs.type_at(0), None)) + } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did()) + && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did()) + { + Some(( + found_substs.type_at(0), + expected_substs.type_at(0), + Some((found_substs.type_at(1), expected_substs.type_at(1))), + )) + } else { + None + } + } + /// When encountering the expected boxed value allocated in the stack, suggest allocating it /// in the heap by calling `Box::new()`. pub(in super::super) fn suggest_boxing_when_appropriate( diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index f44c4a7c1e3..4d0e7706367 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -278,9 +278,6 @@ infer_ril_introduced_by = requirement introduced by this return type infer_ril_introduced_here = `'static` requirement introduced here infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type -infer_sarwa_option = you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()` -infer_sarwa_result = you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()` - infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index b1e819e83f1..7e1fa08f23a 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1247,30 +1247,6 @@ pub struct FnConsiderCasting { } #[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, - }, -} - -#[derive(Subdiagnostic)] pub enum SuggestAccessingField<'a> { #[suggestion( infer_suggest_accessing_field, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index f8a253c8949..ce5b5882e3b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1897,7 +1897,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if should_suggest_fixes { self.suggest_tuple_pattern(cause, &exp_found, diag); - self.suggest_as_ref_where_appropriate(span, &exp_found, diag); self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); self.suggest_function_pointers(cause, span, &exp_found, diag); diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index 9a8fac0fc1b..a723dc3f079 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -374,12 +374,18 @@ impl<T> Trait<T> for X { ) { let tcx = self.tcx; + // Don't suggest constraining a projection to something containing itself + if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) { + return; + } + let msg = || { format!( "consider constraining the associated type `{}` to `{}`", values.expected, values.found ) }; + let body_owner = tcx.hir().get_if_local(body_owner_def_id); let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index d885d040707..1422bdc9ea2 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -13,9 +13,9 @@ use rustc_span::{sym, BytePos, Span}; use crate::errors::{ ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, - FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate, - SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, - SuggestTuplePatternOne, TypeErrorAdditionalDiags, + FunctionPointerSuggestion, SuggestAccessingField, SuggestBoxingForReturnImplTrait, + SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, + TypeErrorAdditionalDiags, }; use super::TypeErrCtxt; @@ -289,27 +289,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate, - /// suggests it. - pub(super) fn suggest_as_ref_where_appropriate( - &self, - span: Span, - exp_found: &ty::error::ExpectedFound<Ty<'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_kind(exp_found.expected, exp_found.found) - { - // HACK: fix issue# 100605, suggesting convert from &Option<T> 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); - } - } - pub(super) fn suggest_function_pointers( &self, cause: &ObligationCause<'tcx>, |
