Skip to content

Commit eeeae60

Browse files
committed
Move repeated logic to own method
1 parent 94ca92c commit eeeae60

File tree

1 file changed

+110
-219
lines changed

1 file changed

+110
-219
lines changed

compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs

+110-219
Original file line numberDiff line numberDiff line change
@@ -455,124 +455,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
455455
InferSourceKind::LetBinding { insert_span, pattern_name, ty, def_id, hir_id } => {
456456
let mut paths = vec![];
457457
if let Some(def_id) = def_id
458-
&& let name = self.infcx.tcx.item_name(def_id)
459458
&& let Some(hir_id) = hir_id
460459
&& let expr = self.infcx.tcx.hir().expect_expr(hir_id)
461460
&& let hir::ExprKind::MethodCall(_, rcvr, _, _) = expr.kind
462461
&& let Some(ty) = typeck_results.node_type_opt(rcvr.hir_id)
463462
{
464-
// Look for all the possible implementations to suggest, otherwise we'll show
465-
// just suggest the syntax for the fully qualified path with placeholders.
466-
self.infcx.tcx.for_each_relevant_impl(
467-
self.infcx.tcx.parent(def_id), // Trait `DefId`
468-
ty, // `Self` type
469-
|impl_def_id| {
470-
let impl_args = ty::GenericArgs::for_item(
471-
self.infcx.tcx,
472-
impl_def_id,
473-
|param, _| {
474-
// We don't want to name the arguments, we just want to give an
475-
// idea of what the syntax is.
476-
match param.kind {
477-
ty::GenericParamDefKind::Lifetime => {
478-
self.infcx.tcx.lifetimes.re_erased.into()
479-
}
480-
ty::GenericParamDefKind::Type { .. } => {
481-
self.next_ty_var(DUMMY_SP).into()
482-
}
483-
ty::GenericParamDefKind::Const { .. } => {
484-
self.next_const_var(DUMMY_SP).into()
485-
}
486-
}
487-
},
488-
);
489-
let impl_trait_ref = self
490-
.infcx
491-
.tcx
492-
.impl_trait_ref(impl_def_id)
493-
.unwrap()
494-
.instantiate(self.infcx.tcx, impl_args);
495-
let impl_self_ty = impl_trait_ref.self_ty();
496-
if self.infcx.can_eq(param_env, impl_self_ty, ty) {
497-
// The expr's self type could conform to this impl's self type.
498-
} else {
499-
// Nope, don't bother.
500-
return;
501-
}
502-
let assocs = self.infcx.tcx.associated_items(impl_def_id);
503-
504-
if self
505-
.infcx
506-
.tcx
507-
.is_diagnostic_item(sym::blanket_into_impl, impl_def_id)
508-
&& let Some(did) = self.infcx.tcx.get_diagnostic_item(sym::From)
509-
{
510-
let mut found = false;
511-
self.infcx.tcx.for_each_impl(did, |impl_def_id| {
512-
// We had an `<A as Into<B>::into` and we've hit the blanket
513-
// impl for `From<A>`. So we try and look for the right `From`
514-
// impls that *would* apply. We *could* do this in a generalized
515-
// version by evaluating the `where` clauses, but that would be
516-
// way too involved to implement. Instead we special case the
517-
// arguably most common case of `expr.into()`.
518-
let Some(header) =
519-
self.infcx.tcx.impl_trait_header(impl_def_id)
520-
else {
521-
return;
522-
};
523-
let target = header.trait_ref.skip_binder().args.type_at(0);
524-
let _ty = header.trait_ref.skip_binder().args.type_at(1);
525-
if _ty == ty {
526-
paths.push(format!("{target}"));
527-
found = true;
528-
}
529-
});
530-
if found {
531-
return;
532-
}
533-
}
534-
535-
// We're at the `impl` level, but we want to get the same method we
536-
// called *on this `impl`*, in order to get the right DefId and args.
537-
let Some(assoc) = assocs.filter_by_name_unhygienic(name).next() else {
538-
// The method isn't in this `impl`? Not useful to us then.
539-
return;
540-
};
541-
let Some(trait_assoc_item) = assoc.trait_item_def_id else {
542-
return;
543-
};
544-
// Let's ignore the generic params and replace them with `_` in the
545-
// suggested path.
546-
let trait_assoc_substs = impl_trait_ref.args.extend_to(
547-
self.infcx.tcx,
548-
trait_assoc_item,
549-
|def, _| {
550-
// We don't want to name the arguments, we just want to give an
551-
// idea of what the syntax is.
552-
match def.kind {
553-
ty::GenericParamDefKind::Lifetime => {
554-
self.infcx.tcx.lifetimes.re_erased.into()
555-
}
556-
ty::GenericParamDefKind::Type { .. } => {
557-
self.next_ty_var(DUMMY_SP).into()
558-
}
559-
ty::GenericParamDefKind::Const { .. } => {
560-
self.next_const_var(DUMMY_SP).into()
561-
}
562-
}
563-
},
564-
);
565-
let identity_method =
566-
impl_args.rebase_onto(self.infcx.tcx, def_id, trait_assoc_substs);
567-
let fn_sig = self
568-
.infcx
569-
.tcx
570-
.fn_sig(def_id)
571-
.instantiate(self.infcx.tcx, identity_method);
572-
let ret = fn_sig.skip_binder().output();
573-
paths.push(format!("{ret}"));
574-
},
575-
);
463+
paths = self.get_suggestions(ty, def_id, true, param_env, None);
576464
}
577465

578466
if paths.is_empty() {
@@ -697,114 +585,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
697585
_ => "",
698586
};
699587

700-
let mut paths = vec![];
701-
let name = self.infcx.tcx.item_name(def_id);
702588
// Look for all the possible implementations to suggest, otherwise we'll show
703589
// just suggest the syntax for the fully qualified path with placeholders.
704-
self.infcx.tcx.for_each_relevant_impl(
705-
self.infcx.tcx.parent(def_id), // Trait `DefId`
706-
args.type_at(0), // `Self` type
707-
|impl_def_id| {
708-
let impl_args = ty::GenericArgs::for_item(
709-
self.infcx.tcx,
710-
impl_def_id,
711-
|param, _| {
712-
// We don't want to name the arguments, we just want to give an
713-
// idea of what the syntax is.
714-
match param.kind {
715-
ty::GenericParamDefKind::Lifetime => {
716-
self.infcx.tcx.lifetimes.re_erased.into()
717-
}
718-
ty::GenericParamDefKind::Type { .. } => {
719-
self.next_ty_var(DUMMY_SP).into()
720-
}
721-
ty::GenericParamDefKind::Const { .. } => {
722-
self.next_const_var(DUMMY_SP).into()
723-
}
724-
}
725-
},
726-
);
727-
let impl_trait_ref = self
728-
.infcx
729-
.tcx
730-
.impl_trait_ref(impl_def_id)
731-
.unwrap()
732-
.instantiate(self.infcx.tcx, impl_args);
733-
let impl_self_ty = impl_trait_ref.self_ty();
734-
if self.infcx.can_eq(param_env, impl_self_ty, args.type_at(0)) {
735-
// The expr's self type could conform to this impl's self type.
736-
} else {
737-
// Nope, don't bother.
738-
return;
739-
}
740-
let assocs = self.infcx.tcx.associated_items(impl_def_id);
741-
742-
if self
743-
.infcx
744-
.tcx
745-
.is_diagnostic_item(sym::blanket_into_impl, impl_def_id)
746-
&& let Some(did) = self.infcx.tcx.get_diagnostic_item(sym::From)
747-
{
748-
let mut found = false;
749-
self.infcx.tcx.for_each_impl(did, |impl_def_id| {
750-
// We had an `<A as Into<B>::into` and we've hit the blanket
751-
// impl for `From<A>`. So we try and look for the right `From`
752-
// impls that *would* apply. We *could* do this in a generalized
753-
// version by evaluating the `where` clauses, but that would be
754-
// way too involved to implement. Instead we special case the
755-
// arguably most common case of `expr.into()`.
756-
let Some(header) =
757-
self.infcx.tcx.impl_trait_header(impl_def_id)
758-
else {
759-
return;
760-
};
761-
let target = header.trait_ref.skip_binder().args.type_at(0);
762-
let ty = header.trait_ref.skip_binder().args.type_at(1);
763-
if ty == args.type_at(0) {
764-
paths.push(format!("<{ty} as Into<{target}>>::into"));
765-
found = true;
766-
}
767-
});
768-
if found {
769-
return;
770-
}
771-
}
772-
773-
// We're at the `impl` level, but we want to get the same method we
774-
// called *on this `impl`*, in order to get the right DefId and args.
775-
let Some(assoc) = assocs.filter_by_name_unhygienic(name).next() else {
776-
// The method isn't in this `impl`? Not useful to us then.
777-
return;
778-
};
779-
let Some(trait_assoc_item) = assoc.trait_item_def_id else {
780-
return;
781-
};
782-
let trait_assoc_substs = impl_trait_ref.args.extend_to(
783-
self.infcx.tcx,
784-
trait_assoc_item,
785-
|def, _| {
786-
// We don't want to name the arguments, we just want to give an
787-
// idea of what the syntax is.
788-
match def.kind {
789-
ty::GenericParamDefKind::Lifetime => {
790-
self.infcx.tcx.lifetimes.re_erased.into()
791-
}
792-
ty::GenericParamDefKind::Type { .. } => {
793-
self.next_ty_var(DUMMY_SP).into()
794-
}
795-
ty::GenericParamDefKind::Const { .. } => {
796-
self.next_const_var(DUMMY_SP).into()
797-
}
798-
}
799-
},
800-
);
801-
let identity_method =
802-
args.rebase_onto(self.infcx.tcx, def_id, trait_assoc_substs);
803-
let mut printer = fmt_printer(self, Namespace::ValueNS);
804-
printer.print_def_path(def_id, identity_method).unwrap();
805-
paths.push(printer.into_buffer());
806-
},
807-
);
590+
let paths =
591+
self.get_suggestions(args.type_at(0), def_id, false, param_env, Some(args));
808592
if paths.len() > 20 || paths.is_empty() {
809593
// This will show the fallback impl, so the expression will have type
810594
// parameter placeholders, but it's better than nothing.
@@ -875,6 +659,113 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
875659
}),
876660
}
877661
}
662+
663+
fn get_suggestions(
664+
&self,
665+
ty: Ty<'tcx>, // args.type_at(0) / ty
666+
def_id: DefId,
667+
target_type: bool, // false / true
668+
param_env: ty::ParamEnv<'tcx>,
669+
args: Option<&ty::GenericArgs<'tcx>>,
670+
) -> Vec<String> {
671+
let tcx = self.infcx.tcx;
672+
let mut paths = vec![];
673+
let name = tcx.item_name(def_id);
674+
let empty_args = |def_id| {
675+
ty::GenericArgs::for_item(tcx, def_id, |param, _| {
676+
// We don't want to name the arguments, we just want to give an
677+
// idea of what the syntax is.
678+
match param.kind {
679+
ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
680+
ty::GenericParamDefKind::Type { .. } => self.next_ty_var(DUMMY_SP).into(),
681+
ty::GenericParamDefKind::Const { .. } => self.next_const_var(DUMMY_SP).into(),
682+
}
683+
})
684+
};
685+
let args = args.unwrap_or_else(|| empty_args(def_id));
686+
tcx.for_each_relevant_impl(
687+
tcx.parent(def_id), // Trait `DefId`
688+
ty, // `Self` type
689+
|impl_def_id| {
690+
let impl_args = empty_args(impl_def_id);
691+
let impl_trait_ref =
692+
tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
693+
let impl_self_ty = impl_trait_ref.self_ty();
694+
if self.infcx.can_eq(param_env, impl_self_ty, ty) {
695+
// The expr's self type could conform to this impl's self type.
696+
} else {
697+
// Nope, don't bother.
698+
return;
699+
}
700+
let assocs = tcx.associated_items(impl_def_id);
701+
702+
if tcx.is_diagnostic_item(sym::blanket_into_impl, impl_def_id)
703+
&& let Some(did) = tcx.get_diagnostic_item(sym::From)
704+
{
705+
let mut found = false;
706+
tcx.for_each_impl(did, |impl_def_id| {
707+
// We had an `<A as Into<B>::into` and we've hit the blanket
708+
// impl for `From<A>`. So we try and look for the right `From`
709+
// impls that *would* apply. We *could* do this in a generalized
710+
// version by evaluating the `where` clauses, but that would be
711+
// way too involved to implement. Instead we special case the
712+
// arguably most common case of `expr.into()`.
713+
let Some(header) = tcx.impl_trait_header(impl_def_id) else {
714+
return;
715+
};
716+
let target = header.trait_ref.skip_binder().args.type_at(0);
717+
let _ty = header.trait_ref.skip_binder().args.type_at(1);
718+
if _ty == ty {
719+
if target_type {
720+
paths.push(format!("{target}"));
721+
} else {
722+
paths.push(format!("<{ty} as Into<{target}>>::into"));
723+
}
724+
found = true;
725+
}
726+
});
727+
if found {
728+
return;
729+
}
730+
}
731+
732+
// We're at the `impl` level, but we want to get the same method we
733+
// called *on this `impl`*, in order to get the right DefId and args.
734+
let Some(assoc) = assocs.filter_by_name_unhygienic(name).next() else {
735+
// The method isn't in this `impl`? Not useful to us then.
736+
return;
737+
};
738+
let Some(trait_assoc_item) = assoc.trait_item_def_id else {
739+
return;
740+
};
741+
let trait_assoc_substs =
742+
impl_trait_ref.args.extend_to(tcx, trait_assoc_item, |def, _| {
743+
// We don't want to name the arguments, we just want to give an
744+
// idea of what the syntax is.
745+
match def.kind {
746+
ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
747+
ty::GenericParamDefKind::Type { .. } => {
748+
self.next_ty_var(DUMMY_SP).into()
749+
}
750+
ty::GenericParamDefKind::Const { .. } => {
751+
self.next_const_var(DUMMY_SP).into()
752+
}
753+
}
754+
});
755+
let identity_method = args.rebase_onto(tcx, def_id, trait_assoc_substs);
756+
if target_type {
757+
let fn_sig = tcx.fn_sig(def_id).instantiate(tcx, identity_method);
758+
let ret = fn_sig.skip_binder().output();
759+
paths.push(format!("{ret}"));
760+
} else {
761+
let mut printer = fmt_printer(self, Namespace::ValueNS);
762+
printer.print_def_path(def_id, identity_method).unwrap();
763+
paths.push(printer.into_buffer());
764+
}
765+
},
766+
);
767+
paths
768+
}
878769
}
879770

880771
#[derive(Debug)]

0 commit comments

Comments
 (0)