Rollup merge of #154939 - chenyukang:yukang-refactor-fn-pointer-cast-suggestion, r=davidtwco

Refactor: simplify report_selection_error

Split out `suggest_cast_to_fn_pointer` for better readability.
This commit is contained in:
Jacob Pratt
2026-04-13 20:12:05 -04:00
committed by GitHub
2 changed files with 66 additions and 48 deletions
@@ -39,9 +39,7 @@
use tracing::{debug, instrument};
use super::suggestions::get_explanation_based_on_obligation;
use super::{
ArgKind, CandidateSimilarity, FindExprBySpan, GetSafeTransmuteErrorAndReason, ImplCandidate,
};
use super::{ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate};
use crate::error_reporting::TypeErrCtxt;
use crate::error_reporting::infer::TyCategory;
use crate::error_reporting::traits::report_dyn_incompatibility;
@@ -452,50 +450,13 @@ pub fn report_selection_error(
self.suggest_dereferencing_index(&obligation, &mut err, leaf_trait_predicate);
suggested |= self.suggest_dereferences(&obligation, &mut err, leaf_trait_predicate);
suggested |= self.suggest_fn_call(&obligation, &mut err, leaf_trait_predicate);
let impl_candidates = self.find_similar_impl_candidates(leaf_trait_predicate);
suggested = if let &[cand] = &impl_candidates[..] {
let cand = cand.trait_ref;
if let (ty::FnPtr(..), ty::FnDef(..)) =
(cand.self_ty().kind(), main_trait_predicate.self_ty().skip_binder().kind())
{
// Wrap method receivers and `&`-references in parens
let suggestion = if self.tcx.sess.source_map().span_followed_by(span, ".").is_some() {
vec![
(span.shrink_to_lo(), format!("(")),
(span.shrink_to_hi(), format!(" as {})", cand.self_ty())),
]
} else if let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) {
let mut expr_finder = FindExprBySpan::new(span, self.tcx);
expr_finder.visit_expr(body.value);
if let Some(expr) = expr_finder.result &&
let hir::ExprKind::AddrOf(_, _, expr) = expr.kind {
vec![
(expr.span.shrink_to_lo(), format!("(")),
(expr.span.shrink_to_hi(), format!(" as {})", cand.self_ty())),
]
} else {
vec![(span.shrink_to_hi(), format!(" as {}", cand.self_ty()))]
}
} else {
vec![(span.shrink_to_hi(), format!(" as {}", cand.self_ty()))]
};
let trait_ = self.tcx.short_string(cand.print_trait_sugared(), err.long_ty_path());
let ty = self.tcx.short_string(cand.self_ty(), err.long_ty_path());
err.multipart_suggestion(
format!(
"the trait `{trait_}` is implemented for fn pointer \
`{ty}`, try casting using `as`",
),
suggestion,
Applicability::MaybeIncorrect,
);
true
} else {
false
}
} else {
false
} || suggested;
suggested |= self.suggest_cast_to_fn_pointer(
&obligation,
&mut err,
leaf_trait_predicate,
main_trait_predicate,
span,
);
suggested |=
self.suggest_remove_reference(&obligation, &mut err, leaf_trait_predicate);
suggested |= self.suggest_semicolon_removal(
@@ -29,7 +29,8 @@
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::print::{
PrintPolyTraitPredicateExt as _, PrintPolyTraitRefExt, PrintTraitPredicateExt as _,
with_forced_trimmed_paths, with_no_trimmed_paths, with_types_for_suggestion,
PrintTraitRefExt as _, with_forced_trimmed_paths, with_no_trimmed_paths,
with_types_for_suggestion,
};
use rustc_middle::ty::{
self, AdtKind, GenericArgs, InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder,
@@ -1142,6 +1143,62 @@ pub(super) fn suggest_fn_call(
true
}
pub(super) fn suggest_cast_to_fn_pointer(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diag<'_>,
leaf_trait_predicate: ty::PolyTraitPredicate<'tcx>,
main_trait_predicate: ty::PolyTraitPredicate<'tcx>,
span: Span,
) -> bool {
let &[candidate] = &self.find_similar_impl_candidates(leaf_trait_predicate)[..] else {
return false;
};
let candidate = candidate.trait_ref;
if !matches!(
(candidate.self_ty().kind(), main_trait_predicate.self_ty().skip_binder().kind(),),
(ty::FnPtr(..), ty::FnDef(..))
) {
return false;
}
let parenthesized_cast = |span: Span| {
vec![
(span.shrink_to_lo(), "(".to_string()),
(span.shrink_to_hi(), format!(" as {})", candidate.self_ty())),
]
};
// Wrap method receivers and `&`-references in parens.
let suggestion = if self.tcx.sess.source_map().span_followed_by(span, ".").is_some() {
parenthesized_cast(span)
} else if let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) {
let mut expr_finder = FindExprBySpan::new(span, self.tcx);
expr_finder.visit_expr(body.value);
if let Some(expr) = expr_finder.result
&& let hir::ExprKind::AddrOf(_, _, expr) = expr.kind
{
parenthesized_cast(expr.span)
} else {
vec![(span.shrink_to_hi(), format!(" as {}", candidate.self_ty()))]
}
} else {
vec![(span.shrink_to_hi(), format!(" as {}", candidate.self_ty()))]
};
let trait_ = self.tcx.short_string(candidate.print_trait_sugared(), err.long_ty_path());
let self_ty = self.tcx.short_string(candidate.self_ty(), err.long_ty_path());
err.multipart_suggestion(
format!(
"the trait `{trait_}` is implemented for fn pointer \
`{self_ty}`, try casting using `as`",
),
suggestion,
Applicability::MaybeIncorrect,
);
true
}
pub(super) fn check_for_binding_assigned_block_without_tail_expression(
&self,
obligation: &PredicateObligation<'tcx>,