Skip to content

Commit f23675f

Browse files
committed
Add sub-fn for unsatisfied ty or trait in FnCtxt::report_no_match_method_error
Currently this method is quiet long and complex, this commit refactors it and improves its readability by adding sub-fn
1 parent 117d676 commit f23675f

File tree

1 file changed

+132
-90
lines changed

1 file changed

+132
-90
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 132 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,120 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
772772
static_candidates
773773
}
774774

775+
fn suggest_unsatisfied_ty_or_trait(
776+
&self,
777+
err: &mut Diag<'_>,
778+
span: Span,
779+
rcvr_ty: Ty<'tcx>,
780+
item_ident: Ident,
781+
item_kind: &str,
782+
source: SelfSource<'tcx>,
783+
unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>,
784+
static_candidates: &[CandidateSource],
785+
) -> Result<(bool, bool, bool, bool, SortedMap<Span, Vec<String>>), ()> {
786+
let mut restrict_type_params = false;
787+
let mut suggested_derive = false;
788+
let mut unsatisfied_bounds = false;
789+
let mut custom_span_label = !static_candidates.is_empty();
790+
let mut bound_spans: SortedMap<Span, Vec<String>> = Default::default();
791+
let tcx = self.tcx;
792+
793+
if item_ident.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
794+
let msg = "consider using `len` instead";
795+
if let SelfSource::MethodCall(_expr) = source {
796+
err.span_suggestion_short(span, msg, "len", Applicability::MachineApplicable);
797+
} else {
798+
err.span_label(span, msg);
799+
}
800+
if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) {
801+
let iterator_trait = self.tcx.def_path_str(iterator_trait);
802+
err.note(format!(
803+
"`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
804+
));
805+
}
806+
} else if self.impl_into_iterator_should_be_iterator(rcvr_ty, span, unsatisfied_predicates)
807+
{
808+
err.span_label(span, format!("`{rcvr_ty}` is not an iterator"));
809+
if !span.in_external_macro(self.tcx.sess.source_map()) {
810+
err.multipart_suggestion_verbose(
811+
"call `.into_iter()` first",
812+
vec![(span.shrink_to_lo(), format!("into_iter()."))],
813+
Applicability::MaybeIncorrect,
814+
);
815+
}
816+
// Report to emit the diagnostic
817+
return Err(());
818+
} else if !unsatisfied_predicates.is_empty() {
819+
if matches!(rcvr_ty.kind(), ty::Param(_)) {
820+
// We special case the situation where we are looking for `_` in
821+
// `<TypeParam as _>::method` because otherwise the machinery will look for blanket
822+
// implementations that have unsatisfied trait bounds to suggest, leading us to claim
823+
// things like "we're looking for a trait with method `cmp`, both `Iterator` and `Ord`
824+
// have one, in order to implement `Ord` you need to restrict `TypeParam: FnPtr` so
825+
// that `impl<T: FnPtr> Ord for T` can apply", which is not what we want. We have a type
826+
// parameter, we want to directly say "`Ord::cmp` and `Iterator::cmp` exist, restrict
827+
// `TypeParam: Ord` or `TypeParam: Iterator`"". That is done further down when calling
828+
// `self.suggest_traits_to_import`, so we ignore the `unsatisfied_predicates`
829+
// suggestions.
830+
} else {
831+
self.handle_unsatisfied_predicates(
832+
err,
833+
rcvr_ty,
834+
item_ident,
835+
item_kind,
836+
span,
837+
unsatisfied_predicates,
838+
&mut restrict_type_params,
839+
&mut suggested_derive,
840+
&mut unsatisfied_bounds,
841+
&mut custom_span_label,
842+
&mut bound_spans,
843+
);
844+
}
845+
} else if let ty::Adt(def, targs) = rcvr_ty.kind()
846+
&& let SelfSource::MethodCall(rcvr_expr) = source
847+
{
848+
// This is useful for methods on arbitrary self types that might have a simple
849+
// mutability difference, like calling a method on `Pin<&mut Self>` that is on
850+
// `Pin<&Self>`.
851+
if targs.len() == 1 {
852+
let mut item_segment = hir::PathSegment::invalid();
853+
item_segment.ident = item_ident;
854+
for t in [Ty::new_mut_ref, Ty::new_imm_ref, |_, _, t| t] {
855+
let new_args =
856+
tcx.mk_args_from_iter(targs.iter().map(|arg| match arg.as_type() {
857+
Some(ty) => ty::GenericArg::from(t(
858+
tcx,
859+
tcx.lifetimes.re_erased,
860+
ty.peel_refs(),
861+
)),
862+
_ => arg,
863+
}));
864+
let rcvr_ty = Ty::new_adt(tcx, *def, new_args);
865+
if let Ok(method) = self.lookup_method_for_diagnostic(
866+
rcvr_ty,
867+
&item_segment,
868+
span,
869+
tcx.parent_hir_node(rcvr_expr.hir_id).expect_expr(),
870+
rcvr_expr,
871+
) {
872+
err.span_note(
873+
tcx.def_span(method.def_id),
874+
format!("{item_kind} is available for `{rcvr_ty}`"),
875+
);
876+
}
877+
}
878+
}
879+
}
880+
Ok((
881+
restrict_type_params,
882+
suggested_derive,
883+
unsatisfied_bounds,
884+
custom_span_label,
885+
bound_spans,
886+
))
887+
}
888+
775889
fn report_no_match_method_error(
776890
&self,
777891
mut span: Span,
@@ -874,12 +988,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
874988
sugg_span,
875989
&no_match_data,
876990
);
877-
let mut custom_span_label = !static_candidates.is_empty();
878991

879-
let mut bound_spans: SortedMap<Span, Vec<String>> = Default::default();
880-
let mut restrict_type_params = false;
881-
let mut suggested_derive = false;
882-
let mut unsatisfied_bounds = false;
883992
let mut ty_span = match rcvr_ty.kind() {
884993
ty::Param(param_type) => {
885994
Some(param_type.span_from_generics(self.tcx, self.body_id.to_def_id()))
@@ -888,92 +997,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
888997
_ => None,
889998
};
890999

891-
if item_ident.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
892-
let msg = "consider using `len` instead";
893-
if let SelfSource::MethodCall(_expr) = source {
894-
err.span_suggestion_short(span, msg, "len", Applicability::MachineApplicable);
895-
} else {
896-
err.span_label(span, msg);
897-
}
898-
if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) {
899-
let iterator_trait = self.tcx.def_path_str(iterator_trait);
900-
err.note(format!(
901-
"`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
902-
));
903-
}
904-
} else if self.impl_into_iterator_should_be_iterator(rcvr_ty, span, unsatisfied_predicates)
905-
{
906-
err.span_label(span, format!("`{rcvr_ty}` is not an iterator"));
907-
if !span.in_external_macro(self.tcx.sess.source_map()) {
908-
err.multipart_suggestion_verbose(
909-
"call `.into_iter()` first",
910-
vec![(span.shrink_to_lo(), format!("into_iter()."))],
911-
Applicability::MaybeIncorrect,
912-
);
913-
}
1000+
let Ok((
1001+
restrict_type_params,
1002+
suggested_derive,
1003+
unsatisfied_bounds,
1004+
custom_span_label,
1005+
bound_spans,
1006+
)) = self.suggest_unsatisfied_ty_or_trait(
1007+
&mut err,
1008+
span,
1009+
rcvr_ty,
1010+
item_ident,
1011+
item_kind,
1012+
source,
1013+
unsatisfied_predicates,
1014+
&static_candidates,
1015+
)
1016+
else {
9141017
return err.emit();
915-
} else if !unsatisfied_predicates.is_empty() {
916-
if matches!(rcvr_ty.kind(), ty::Param(_)) {
917-
// We special case the situation where we are looking for `_` in
918-
// `<TypeParam as _>::method` because otherwise the machinery will look for blanket
919-
// implementations that have unsatisfied trait bounds to suggest, leading us to claim
920-
// things like "we're looking for a trait with method `cmp`, both `Iterator` and `Ord`
921-
// have one, in order to implement `Ord` you need to restrict `TypeParam: FnPtr` so
922-
// that `impl<T: FnPtr> Ord for T` can apply", which is not what we want. We have a type
923-
// parameter, we want to directly say "`Ord::cmp` and `Iterator::cmp` exist, restrict
924-
// `TypeParam: Ord` or `TypeParam: Iterator`"". That is done further down when calling
925-
// `self.suggest_traits_to_import`, so we ignore the `unsatisfied_predicates`
926-
// suggestions.
927-
} else {
928-
self.handle_unsatisfied_predicates(
929-
&mut err,
930-
rcvr_ty,
931-
item_ident,
932-
item_kind,
933-
span,
934-
unsatisfied_predicates,
935-
&mut restrict_type_params,
936-
&mut suggested_derive,
937-
&mut unsatisfied_bounds,
938-
&mut custom_span_label,
939-
&mut bound_spans,
940-
);
941-
}
942-
} else if let ty::Adt(def, targs) = rcvr_ty.kind()
943-
&& let SelfSource::MethodCall(rcvr_expr) = source
944-
{
945-
// This is useful for methods on arbitrary self types that might have a simple
946-
// mutability difference, like calling a method on `Pin<&mut Self>` that is on
947-
// `Pin<&Self>`.
948-
if targs.len() == 1 {
949-
let mut item_segment = hir::PathSegment::invalid();
950-
item_segment.ident = item_ident;
951-
for t in [Ty::new_mut_ref, Ty::new_imm_ref, |_, _, t| t] {
952-
let new_args =
953-
tcx.mk_args_from_iter(targs.iter().map(|arg| match arg.as_type() {
954-
Some(ty) => ty::GenericArg::from(t(
955-
tcx,
956-
tcx.lifetimes.re_erased,
957-
ty.peel_refs(),
958-
)),
959-
_ => arg,
960-
}));
961-
let rcvr_ty = Ty::new_adt(tcx, *def, new_args);
962-
if let Ok(method) = self.lookup_method_for_diagnostic(
963-
rcvr_ty,
964-
&item_segment,
965-
span,
966-
tcx.parent_hir_node(rcvr_expr.hir_id).expect_expr(),
967-
rcvr_expr,
968-
) {
969-
err.span_note(
970-
tcx.def_span(method.def_id),
971-
format!("{item_kind} is available for `{rcvr_ty}`"),
972-
);
973-
}
974-
}
975-
}
976-
}
1018+
};
9771019

9781020
let mut find_candidate_for_method = false;
9791021
let should_label_not_found = match source {

0 commit comments

Comments
 (0)