1use std::borrow::Cow;
2use std::iter;
3use std::path::PathBuf;
4
5use rustc_errors::codes::*;
6use rustc_errors::{Diag, IntoDiagArg};
7use rustc_hir as hir;
8use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
9use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
10use rustc_hir::intravisit::{self, Visitor};
11use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource};
12use rustc_middle::bug;
13use rustc_middle::hir::nested_filter;
14use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
15use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
16use rustc_middle::ty::{
17 self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Ty, TyCtxt,
18 TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults,
19};
20use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym};
21use rustc_type_ir::TypeVisitableExt;
22use tracing::{debug, instrument, warn};
23
24use super::nice_region_error::placeholder_error::Highlighted;
25use crate::error_reporting::TypeErrCtxt;
26use crate::errors::{
27 AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError,
28 SourceKindMultiSuggestion, SourceKindSubdiag,
29};
30use crate::infer::InferCtxt;
31
32pub enum TypeAnnotationNeeded {
33 E0282,
37 E0283,
42 E0284,
47}
48
49impl From<TypeAnnotationNeeded> for ErrCode {
50 fn from(val: TypeAnnotationNeeded) -> Self {
51 match val {
52 TypeAnnotationNeeded::E0282 => E0282,
53 TypeAnnotationNeeded::E0283 => E0283,
54 TypeAnnotationNeeded::E0284 => E0284,
55 }
56 }
57}
58
59pub struct InferenceDiagnosticsData {
61 pub name: String,
62 pub span: Option<Span>,
63 pub kind: UnderspecifiedArgKind,
64 pub parent: Option<InferenceDiagnosticsParentData>,
65}
66
67pub struct InferenceDiagnosticsParentData {
69 prefix: &'static str,
70 name: String,
71}
72
73#[derive(Clone)]
74pub enum UnderspecifiedArgKind {
75 Type { prefix: Cow<'static, str> },
76 Const { is_parameter: bool },
77}
78
79impl InferenceDiagnosticsData {
80 fn can_add_more_info(&self) -> bool {
81 !(self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }))
82 }
83
84 fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str {
85 if in_type.is_ty_or_numeric_infer() {
86 ""
87 } else if self.name == "_" {
88 "underscore"
91 } else {
92 "has_name"
93 }
94 }
95
96 fn make_bad_error(&self, span: Span) -> InferenceBadError<'_> {
100 let has_parent = self.parent.is_some();
101 let bad_kind = if self.can_add_more_info() { "more_info" } else { "other" };
102 let (parent_prefix, parent_name) = self
103 .parent
104 .as_ref()
105 .map(|parent| (parent.prefix, parent.name.clone()))
106 .unwrap_or_default();
107 InferenceBadError {
108 span,
109 bad_kind,
110 prefix_kind: self.kind.clone(),
111 prefix: self.kind.try_get_prefix().unwrap_or_default(),
112 name: self.name.clone(),
113 has_parent,
114 parent_prefix,
115 parent_name,
116 }
117 }
118}
119
120impl InferenceDiagnosticsParentData {
121 fn for_parent_def_id(
122 tcx: TyCtxt<'_>,
123 parent_def_id: DefId,
124 ) -> Option<InferenceDiagnosticsParentData> {
125 let parent_name =
126 tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string();
127
128 Some(InferenceDiagnosticsParentData {
129 prefix: tcx.def_descr(parent_def_id),
130 name: parent_name,
131 })
132 }
133
134 fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> {
135 Self::for_parent_def_id(tcx, tcx.parent(def_id))
136 }
137}
138
139impl IntoDiagArg for UnderspecifiedArgKind {
140 fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
141 let kind = match self {
142 Self::Type { .. } => "type",
143 Self::Const { is_parameter: true } => "const_with_param",
144 Self::Const { is_parameter: false } => "const",
145 };
146 rustc_errors::DiagArgValue::Str(kind.into())
147 }
148}
149
150impl UnderspecifiedArgKind {
151 fn try_get_prefix(&self) -> Option<&str> {
152 match self {
153 Self::Type { prefix } => Some(prefix.as_ref()),
154 Self::Const { .. } => None,
155 }
156 }
157}
158
159struct ClosureEraser<'a, 'tcx> {
160 infcx: &'a InferCtxt<'tcx>,
161}
162
163impl<'a, 'tcx> ClosureEraser<'a, 'tcx> {
164 fn new_infer(&mut self) -> Ty<'tcx> {
165 self.infcx.next_ty_var(DUMMY_SP)
166 }
167}
168
169impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'a, 'tcx> {
170 fn cx(&self) -> TyCtxt<'tcx> {
171 self.infcx.tcx
172 }
173
174 fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
175 match ty.kind() {
176 ty::Closure(_, args) => {
177 let closure_sig = args.as_closure().sig();
180 Ty::new_fn_ptr(
181 self.cx(),
182 self.cx().signature_unclosure(closure_sig, hir::Safety::Safe),
183 )
184 }
185 ty::Adt(_, args) if !args.iter().any(|a| a.has_infer()) => {
186 self.new_infer()
191 }
192 ty::Adt(def, args) => {
193 let generics = self.cx().generics_of(def.did());
194 let generics: Vec<bool> = generics
195 .own_params
196 .iter()
197 .map(|param| param.default_value(self.cx()).is_some())
198 .collect();
199 let ty = Ty::new_adt(
200 self.cx(),
201 *def,
202 self.cx().mk_args_from_iter(generics.into_iter().zip(args.iter()).map(
203 |(has_default, arg)| {
204 if arg.has_infer() {
205 arg.fold_with(self)
211 } else if has_default {
212 arg
219 } else if let GenericArgKind::Type(_) = arg.unpack() {
220 self.new_infer().into()
222 } else {
223 arg.fold_with(self)
224 }
225 },
226 )),
227 );
228 ty
229 }
230 _ if ty.has_infer() => {
231 ty.super_fold_with(self)
235 }
236 _ => self.new_infer(),
238 }
239 }
240
241 fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
242 c
244 }
245}
246
247fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> {
248 let mut printer = FmtPrinter::new(infcx.tcx, ns);
249 let ty_getter = move |ty_vid| {
250 if infcx.probe_ty_var(ty_vid).is_ok() {
251 warn!("resolved ty var in error message");
252 }
253
254 let var_origin = infcx.type_var_origin(ty_vid);
255 if let Some(def_id) = var_origin.param_def_id
256 && infcx.tcx.def_kind(def_id) == DefKind::TyParam
259 && let name = infcx.tcx.item_name(def_id)
260 && !var_origin.span.from_expansion()
261 {
262 let generics = infcx.tcx.generics_of(infcx.tcx.parent(def_id));
263 let idx = generics.param_def_id_to_index(infcx.tcx, def_id).unwrap();
264 let generic_param_def = generics.param_at(idx as usize, infcx.tcx);
265 if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param_def.kind {
266 None
267 } else {
268 Some(name)
269 }
270 } else {
271 None
272 }
273 };
274 printer.ty_infer_name_resolver = Some(Box::new(ty_getter));
275 let const_getter =
276 move |ct_vid| Some(infcx.tcx.item_name(infcx.const_var_origin(ct_vid)?.param_def_id?));
277 printer.const_infer_name_resolver = Some(Box::new(const_getter));
278 printer
279}
280
281fn ty_to_string<'tcx>(
282 infcx: &InferCtxt<'tcx>,
283 ty: Ty<'tcx>,
284 called_method_def_id: Option<DefId>,
285) -> String {
286 let mut printer = fmt_printer(infcx, Namespace::TypeNS);
287 let ty = infcx.resolve_vars_if_possible(ty);
288 let ty = ty.fold_with(&mut ClosureEraser { infcx });
291
292 match (ty.kind(), called_method_def_id) {
293 (ty::FnDef(..), _) => {
296 ty.fn_sig(infcx.tcx).print(&mut printer).unwrap();
297 printer.into_buffer()
298 }
299 (_, Some(def_id))
300 if ty.is_ty_or_numeric_infer()
301 && infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn) == Some(def_id) =>
302 {
303 "Vec<_>".to_string()
304 }
305 _ if ty.is_ty_or_numeric_infer() => "/* Type */".to_string(),
306 _ => {
307 ty.print(&mut printer).unwrap();
308 printer.into_buffer()
309 }
310 }
311}
312
313fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>) -> String {
317 let ty::Closure(_, args) = ty.kind() else {
318 bug!("cannot convert non-closure to fn str in `closure_as_fn_str`")
319 };
320 let fn_sig = args.as_closure().sig();
321 let args = fn_sig
322 .inputs()
323 .skip_binder()
324 .iter()
325 .next()
326 .map(|args| {
327 args.tuple_fields()
328 .iter()
329 .map(|arg| ty_to_string(infcx, arg, None))
330 .collect::<Vec<_>>()
331 .join(", ")
332 })
333 .unwrap_or_default();
334 let ret = if fn_sig.output().skip_binder().is_unit() {
335 String::new()
336 } else {
337 format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder(), None))
338 };
339 format!("fn({args}){ret}")
340}
341
342impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
343 pub fn extract_inference_diagnostics_data(
346 &self,
347 arg: GenericArg<'tcx>,
348 highlight: ty::print::RegionHighlightMode<'tcx>,
349 ) -> InferenceDiagnosticsData {
350 let tcx = self.tcx;
351 match arg.unpack() {
352 GenericArgKind::Type(ty) => {
353 if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() {
354 let var_origin = self.infcx.type_var_origin(ty_vid);
355 if let Some(def_id) = var_origin.param_def_id
356 && self.tcx.def_kind(def_id) == DefKind::TyParam
359 && !var_origin.span.from_expansion()
360 {
361 return InferenceDiagnosticsData {
362 name: self.tcx.item_name(def_id).to_string(),
363 span: Some(var_origin.span),
364 kind: UnderspecifiedArgKind::Type { prefix: "type parameter".into() },
365 parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id),
366 };
367 }
368 }
369
370 InferenceDiagnosticsData {
371 name: Highlighted { highlight, ns: Namespace::TypeNS, tcx, value: ty }
372 .to_string(),
373 span: None,
374 kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) },
375 parent: None,
376 }
377 }
378 GenericArgKind::Const(ct) => {
379 if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() {
380 let origin = self.const_var_origin(vid).expect("expected unresolved const var");
381 if let Some(def_id) = origin.param_def_id {
382 return InferenceDiagnosticsData {
383 name: self.tcx.item_name(def_id).to_string(),
384 span: Some(origin.span),
385 kind: UnderspecifiedArgKind::Const { is_parameter: true },
386 parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id),
387 };
388 }
389
390 debug_assert!(!origin.span.is_dummy());
391 InferenceDiagnosticsData {
392 name: Highlighted { highlight, ns: Namespace::ValueNS, tcx, value: ct }
393 .to_string(),
394 span: Some(origin.span),
395 kind: UnderspecifiedArgKind::Const { is_parameter: false },
396 parent: None,
397 }
398 } else {
399 InferenceDiagnosticsData {
406 name: Highlighted { highlight, ns: Namespace::ValueNS, tcx, value: ct }
407 .to_string(),
408 span: None,
409 kind: UnderspecifiedArgKind::Const { is_parameter: false },
410 parent: None,
411 }
412 }
413 }
414 GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
415 }
416 }
417
418 fn bad_inference_failure_err(
421 &self,
422 span: Span,
423 arg_data: InferenceDiagnosticsData,
424 error_code: TypeAnnotationNeeded,
425 ) -> Diag<'a> {
426 let source_kind = "other";
427 let source_name = "";
428 let failure_span = None;
429 let infer_subdiags = Vec::new();
430 let multi_suggestions = Vec::new();
431 let bad_label = Some(arg_data.make_bad_error(span));
432 match error_code {
433 TypeAnnotationNeeded::E0282 => self.dcx().create_err(AnnotationRequired {
434 span,
435 source_kind,
436 source_name,
437 failure_span,
438 infer_subdiags,
439 multi_suggestions,
440 bad_label,
441 was_written: false,
442 path: Default::default(),
443 time_version: false,
444 }),
445 TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl {
446 span,
447 source_kind,
448 source_name,
449 failure_span,
450 infer_subdiags,
451 multi_suggestions,
452 bad_label,
453 was_written: false,
454 path: Default::default(),
455 }),
456 TypeAnnotationNeeded::E0284 => self.dcx().create_err(AmbiguousReturn {
457 span,
458 source_kind,
459 source_name,
460 failure_span,
461 infer_subdiags,
462 multi_suggestions,
463 bad_label,
464 was_written: false,
465 path: Default::default(),
466 }),
467 }
468 }
469
470 #[instrument(level = "debug", skip(self, error_code))]
471 pub fn emit_inference_failure_err(
472 &self,
473 body_def_id: LocalDefId,
474 failure_span: Span,
475 arg: GenericArg<'tcx>,
476 error_code: TypeAnnotationNeeded,
477 should_label_span: bool,
478 ) -> Diag<'a> {
479 let arg = self.resolve_vars_if_possible(arg);
480 let arg_data =
481 self.extract_inference_diagnostics_data(arg, ty::print::RegionHighlightMode::default());
482
483 let Some(typeck_results) = &self.typeck_results else {
484 return self.bad_inference_failure_err(failure_span, arg_data, error_code);
488 };
489
490 let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, arg);
491 if let Some(body) = self.tcx.hir_maybe_body_owned_by(
492 self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(),
493 ) {
494 let expr = body.value;
495 local_visitor.visit_expr(expr);
496 }
497
498 let Some(InferSource { span, kind }) = local_visitor.infer_source else {
499 return self.bad_inference_failure_err(failure_span, arg_data, error_code);
500 };
501
502 let (source_kind, name, path) = kind.ty_localized_msg(self);
503 let failure_span = if should_label_span && !failure_span.overlaps(span) {
504 Some(failure_span)
505 } else {
506 None
507 };
508
509 let mut infer_subdiags = Vec::new();
510 let mut multi_suggestions = Vec::new();
511 match kind {
512 InferSourceKind::LetBinding { insert_span, pattern_name, ty, def_id } => {
513 infer_subdiags.push(SourceKindSubdiag::LetLike {
514 span: insert_span,
515 name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new),
516 x_kind: arg_data.where_x_is_kind(ty),
517 prefix_kind: arg_data.kind.clone(),
518 prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
519 arg_name: arg_data.name,
520 kind: if pattern_name.is_some() { "with_pattern" } else { "other" },
521 type_name: ty_to_string(self, ty, def_id),
522 });
523 }
524 InferSourceKind::ClosureArg { insert_span, ty } => {
525 infer_subdiags.push(SourceKindSubdiag::LetLike {
526 span: insert_span,
527 name: String::new(),
528 x_kind: arg_data.where_x_is_kind(ty),
529 prefix_kind: arg_data.kind.clone(),
530 prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
531 arg_name: arg_data.name,
532 kind: "closure",
533 type_name: ty_to_string(self, ty, None),
534 });
535 }
536 InferSourceKind::GenericArg {
537 insert_span,
538 argument_index,
539 generics_def_id,
540 def_id: _,
541 generic_args,
542 have_turbofish,
543 } => {
544 let generics = self.tcx.generics_of(generics_def_id);
545 let is_type = matches!(arg.unpack(), GenericArgKind::Type(_));
546
547 let (parent_exists, parent_prefix, parent_name) =
548 InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id)
549 .map_or((false, String::new(), String::new()), |parent| {
550 (true, parent.prefix.to_string(), parent.name)
551 });
552
553 infer_subdiags.push(SourceKindSubdiag::GenericLabel {
554 span,
555 is_type,
556 param_name: generics.own_params[argument_index].name.to_string(),
557 parent_exists,
558 parent_prefix,
559 parent_name,
560 });
561
562 let args = if self.tcx.get_diagnostic_item(sym::iterator_collect_fn)
563 == Some(generics_def_id)
564 {
565 "Vec<_>".to_string()
566 } else {
567 let mut printer = fmt_printer(self, Namespace::TypeNS);
568 printer
569 .comma_sep(generic_args.iter().copied().map(|arg| {
570 if arg.is_suggestable(self.tcx, true) {
571 return arg;
572 }
573
574 match arg.unpack() {
575 GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
576 GenericArgKind::Type(_) => self.next_ty_var(DUMMY_SP).into(),
577 GenericArgKind::Const(_) => self.next_const_var(DUMMY_SP).into(),
578 }
579 }))
580 .unwrap();
581 printer.into_buffer()
582 };
583
584 if !have_turbofish {
585 infer_subdiags.push(SourceKindSubdiag::GenericSuggestion {
586 span: insert_span,
587 arg_count: generic_args.len(),
588 args,
589 });
590 }
591 }
592 InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => {
593 let placeholder = Some(self.next_ty_var(DUMMY_SP));
594 if let Some(args) = args.make_suggestable(self.infcx.tcx, true, placeholder) {
595 let mut printer = fmt_printer(self, Namespace::ValueNS);
596 printer.print_def_path(def_id, args).unwrap();
597 let def_path = printer.into_buffer();
598
599 let adjustment = match typeck_results.expr_adjustments(receiver) {
603 [
604 Adjustment { kind: Adjust::Deref(None), target: _ },
605 ..,
606 Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ },
607 ] => "",
608 [
609 ..,
610 Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mut_)), target: _ },
611 ] => hir::Mutability::from(*mut_).ref_prefix_str(),
612 _ => "",
613 };
614
615 multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified(
616 receiver.span,
617 def_path,
618 adjustment,
619 successor,
620 ));
621 }
622 }
623 InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
624 let placeholder = Some(self.next_ty_var(DUMMY_SP));
625 if let Some(ty) = ty.make_suggestable(self.infcx.tcx, true, placeholder) {
626 let ty_info = ty_to_string(self, ty, None);
627 multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return(
628 ty_info,
629 data,
630 should_wrap_expr,
631 ));
632 }
633 }
634 }
635
636 let time_version =
637 self.detect_old_time_crate_version(failure_span, &kind, &mut infer_subdiags);
638
639 match error_code {
640 TypeAnnotationNeeded::E0282 => self.dcx().create_err(AnnotationRequired {
641 span,
642 source_kind,
643 source_name: &name,
644 failure_span,
645 infer_subdiags,
646 multi_suggestions,
647 bad_label: None,
648 was_written: path.is_some(),
649 path: path.unwrap_or_default(),
650 time_version,
651 }),
652 TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl {
653 span,
654 source_kind,
655 source_name: &name,
656 failure_span,
657 infer_subdiags,
658 multi_suggestions,
659 bad_label: None,
660 was_written: path.is_some(),
661 path: path.unwrap_or_default(),
662 }),
663 TypeAnnotationNeeded::E0284 => self.dcx().create_err(AmbiguousReturn {
664 span,
665 source_kind,
666 source_name: &name,
667 failure_span,
668 infer_subdiags,
669 multi_suggestions,
670 bad_label: None,
671 was_written: path.is_some(),
672 path: path.unwrap_or_default(),
673 }),
674 }
675 }
676
677 fn detect_old_time_crate_version(
681 &self,
682 span: Option<Span>,
683 kind: &InferSourceKind<'_>,
684 infer_subdiags: &mut Vec<SourceKindSubdiag<'_>>,
686 ) -> bool {
687 #[cfg(not(version("1.89")))]
690 const fn version_check() {}
691 #[cfg(version("1.89"))]
692 const fn version_check() {
693 panic!("remove this check as presumably the ecosystem has moved from needing it");
694 }
695 const { version_check() };
696 if self.infcx.tcx.crate_name(LOCAL_CRATE) == sym::time
698 && let Some(span) = span
699 && let InferSourceKind::LetBinding { pattern_name, .. } = kind
700 && let Some(name) = pattern_name
701 && name.as_str() == "items"
702 && let FileName::Real(file) = self.infcx.tcx.sess.source_map().span_to_filename(span)
703 {
704 let path = file.local_path_if_available().to_string_lossy();
705 if path.contains("format_description") && path.contains("parse") {
706 infer_subdiags.clear();
707 return true;
708 }
709 }
710 false
711 }
712}
713
714#[derive(Debug)]
715struct InferSource<'tcx> {
716 span: Span,
717 kind: InferSourceKind<'tcx>,
718}
719
720#[derive(Debug)]
721enum InferSourceKind<'tcx> {
722 LetBinding {
723 insert_span: Span,
724 pattern_name: Option<Ident>,
725 ty: Ty<'tcx>,
726 def_id: Option<DefId>,
727 },
728 ClosureArg {
729 insert_span: Span,
730 ty: Ty<'tcx>,
731 },
732 GenericArg {
733 insert_span: Span,
734 argument_index: usize,
735 generics_def_id: DefId,
736 def_id: DefId,
737 generic_args: &'tcx [GenericArg<'tcx>],
738 have_turbofish: bool,
739 },
740 FullyQualifiedMethodCall {
741 receiver: &'tcx Expr<'tcx>,
742 successor: (&'static str, BytePos),
745 args: GenericArgsRef<'tcx>,
746 def_id: DefId,
747 },
748 ClosureReturn {
749 ty: Ty<'tcx>,
750 data: &'tcx FnRetTy<'tcx>,
751 should_wrap_expr: Option<Span>,
752 },
753}
754
755impl<'tcx> InferSource<'tcx> {
756 fn from_expansion(&self) -> bool {
757 let source_from_expansion = match self.kind {
758 InferSourceKind::LetBinding { insert_span, .. }
759 | InferSourceKind::ClosureArg { insert_span, .. }
760 | InferSourceKind::GenericArg { insert_span, .. } => insert_span.from_expansion(),
761 InferSourceKind::FullyQualifiedMethodCall { receiver, .. } => {
762 receiver.span.from_expansion()
763 }
764 InferSourceKind::ClosureReturn { data, should_wrap_expr, .. } => {
765 data.span().from_expansion() || should_wrap_expr.is_some_and(Span::from_expansion)
766 }
767 };
768 source_from_expansion || self.span.from_expansion()
769 }
770}
771
772impl<'tcx> InferSourceKind<'tcx> {
773 fn ty_localized_msg(&self, infcx: &InferCtxt<'tcx>) -> (&'static str, String, Option<PathBuf>) {
774 let mut path = None;
775 match *self {
776 InferSourceKind::LetBinding { ty, .. }
777 | InferSourceKind::ClosureArg { ty, .. }
778 | InferSourceKind::ClosureReturn { ty, .. } => {
779 if ty.is_closure() {
780 ("closure", closure_as_fn_str(infcx, ty), path)
781 } else if !ty.is_ty_or_numeric_infer() {
782 ("normal", infcx.tcx.short_string(ty, &mut path), path)
783 } else {
784 ("other", String::new(), path)
785 }
786 }
787 InferSourceKind::GenericArg { .. }
789 | InferSourceKind::FullyQualifiedMethodCall { .. } => ("other", String::new(), path),
790 }
791 }
792}
793
794#[derive(Debug)]
795struct InsertableGenericArgs<'tcx> {
796 insert_span: Span,
797 args: GenericArgsRef<'tcx>,
798 generics_def_id: DefId,
799 def_id: DefId,
800 have_turbofish: bool,
801}
802
803struct FindInferSourceVisitor<'a, 'tcx> {
811 tecx: &'a TypeErrCtxt<'a, 'tcx>,
812 typeck_results: &'a TypeckResults<'tcx>,
813
814 target: GenericArg<'tcx>,
815
816 attempt: usize,
817 infer_source_cost: usize,
818 infer_source: Option<InferSource<'tcx>>,
819}
820
821impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
822 fn new(
823 tecx: &'a TypeErrCtxt<'a, 'tcx>,
824 typeck_results: &'a TypeckResults<'tcx>,
825 target: GenericArg<'tcx>,
826 ) -> Self {
827 FindInferSourceVisitor {
828 tecx,
829 typeck_results,
830
831 target,
832
833 attempt: 0,
834 infer_source_cost: usize::MAX,
835 infer_source: None,
836 }
837 }
838
839 fn source_cost(&self, source: &InferSource<'tcx>) -> usize {
844 #[derive(Clone, Copy)]
845 struct CostCtxt<'tcx> {
846 tcx: TyCtxt<'tcx>,
847 }
848 impl<'tcx> CostCtxt<'tcx> {
849 fn arg_cost(self, arg: GenericArg<'tcx>) -> usize {
850 match arg.unpack() {
851 GenericArgKind::Lifetime(_) => 0, GenericArgKind::Type(ty) => self.ty_cost(ty),
853 GenericArgKind::Const(_) => 3, }
855 }
856 fn ty_cost(self, ty: Ty<'tcx>) -> usize {
857 match *ty.kind() {
858 ty::Closure(..) => 1000,
859 ty::FnDef(..) => 150,
860 ty::FnPtr(..) => 30,
861 ty::Adt(def, args) => {
862 5 + self
863 .tcx
864 .generics_of(def.did())
865 .own_args_no_defaults(self.tcx, args)
866 .iter()
867 .map(|&arg| self.arg_cost(arg))
868 .sum::<usize>()
869 }
870 ty::Tuple(args) => 5 + args.iter().map(|arg| self.ty_cost(arg)).sum::<usize>(),
871 ty::Ref(_, ty, _) => 2 + self.ty_cost(ty),
872 ty::Infer(..) => 0,
873 _ => 1,
874 }
875 }
876 }
877
878 let tcx = self.tecx.tcx;
880 let ctx = CostCtxt { tcx };
881 match source.kind {
882 InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty),
883 InferSourceKind::ClosureArg { ty, .. } => ctx.ty_cost(ty),
884 InferSourceKind::GenericArg { def_id, generic_args, .. } => {
885 let variant_cost = match tcx.def_kind(def_id) {
886 DefKind::Variant | DefKind::Ctor(CtorOf::Variant, _) => 15,
888 _ => 10,
889 };
890 variant_cost + generic_args.iter().map(|&arg| ctx.arg_cost(arg)).sum::<usize>()
891 }
892 InferSourceKind::FullyQualifiedMethodCall { args, .. } => {
893 20 + args.iter().map(|arg| ctx.arg_cost(arg)).sum::<usize>()
894 }
895 InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => {
896 30 + ctx.ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 }
897 }
898 }
899 }
900
901 #[instrument(level = "debug", skip(self))]
904 fn update_infer_source(&mut self, mut new_source: InferSource<'tcx>) {
905 if new_source.from_expansion() {
906 return;
907 }
908
909 let cost = self.source_cost(&new_source) + self.attempt;
910 debug!(?cost);
911 self.attempt += 1;
912 if let Some(InferSource { kind: InferSourceKind::GenericArg { def_id: did, .. }, .. }) =
913 self.infer_source
914 && let InferSourceKind::LetBinding { ref ty, ref mut def_id, .. } = new_source.kind
915 && ty.is_ty_or_numeric_infer()
916 {
917 *def_id = Some(did);
920 }
921
922 if cost < self.infer_source_cost {
923 self.infer_source_cost = cost;
924 self.infer_source = Some(new_source);
925 }
926 }
927
928 fn node_args_opt(&self, hir_id: HirId) -> Option<GenericArgsRef<'tcx>> {
929 let args = self.typeck_results.node_args_opt(hir_id);
930 self.tecx.resolve_vars_if_possible(args)
931 }
932
933 fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
934 let ty = self.typeck_results.node_type_opt(hir_id);
935 self.tecx.resolve_vars_if_possible(ty)
936 }
937
938 fn generic_arg_is_target(&self, arg: GenericArg<'tcx>) -> bool {
941 if arg == self.target {
942 return true;
943 }
944
945 match (arg.unpack(), self.target.unpack()) {
946 (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => {
947 use ty::{Infer, TyVar};
948 match (inner_ty.kind(), target_ty.kind()) {
949 (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
950 self.tecx.sub_relations.borrow_mut().unified(self.tecx, a_vid, b_vid)
951 }
952 _ => false,
953 }
954 }
955 (GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => {
956 use ty::InferConst::*;
957 match (inner_ct.kind(), target_ct.kind()) {
958 (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => {
959 self.tecx.root_const_var(a_vid) == self.tecx.root_const_var(b_vid)
960 }
961 _ => false,
962 }
963 }
964 _ => false,
965 }
966 }
967
968 fn generic_arg_contains_target(&self, arg: GenericArg<'tcx>) -> bool {
971 let mut walker = arg.walk();
972 while let Some(inner) = walker.next() {
973 if self.generic_arg_is_target(inner) {
974 return true;
975 }
976 match inner.unpack() {
977 GenericArgKind::Lifetime(_) => {}
978 GenericArgKind::Type(ty) => {
979 if matches!(
980 ty.kind(),
981 ty::Alias(ty::Opaque, ..)
982 | ty::Closure(..)
983 | ty::CoroutineClosure(..)
984 | ty::Coroutine(..)
985 ) {
986 walker.skip_current_subtree();
997 }
998 }
999 GenericArgKind::Const(ct) => {
1000 if matches!(ct.kind(), ty::ConstKind::Unevaluated(..)) {
1001 walker.skip_current_subtree();
1004 }
1005 }
1006 }
1007 }
1008 false
1009 }
1010
1011 fn expr_inferred_arg_iter(
1012 &self,
1013 expr: &'tcx hir::Expr<'tcx>,
1014 ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
1015 let tcx = self.tecx.tcx;
1016 match expr.kind {
1017 hir::ExprKind::Path(ref path) => {
1018 if let Some(args) = self.node_args_opt(expr.hir_id) {
1019 return self.path_inferred_arg_iter(expr.hir_id, args, path);
1020 }
1021 }
1022 hir::ExprKind::Struct(&hir::QPath::Resolved(_self_ty, path), _, _)
1033 if matches!(path.res, Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) => {
1040 if let Some(ty) = self.opt_node_type(expr.hir_id)
1041 && let ty::Adt(_, args) = ty.kind()
1042 {
1043 return Box::new(self.resolved_path_inferred_arg_iter(path, args));
1044 }
1045 }
1046 hir::ExprKind::MethodCall(segment, ..) => {
1047 if let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) {
1048 let generics = tcx.generics_of(def_id);
1049 let insertable: Option<_> = try {
1050 if generics.has_impl_trait() {
1051 None?
1052 }
1053 let args = self.node_args_opt(expr.hir_id)?;
1054 let span = tcx.hir().span(segment.hir_id);
1055 let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
1056 InsertableGenericArgs {
1057 insert_span,
1058 args,
1059 generics_def_id: def_id,
1060 def_id,
1061 have_turbofish: false,
1062 }
1063 };
1064 return Box::new(insertable.into_iter());
1065 }
1066 }
1067 _ => {}
1068 }
1069
1070 Box::new(iter::empty())
1071 }
1072
1073 fn resolved_path_inferred_arg_iter(
1074 &self,
1075 path: &'tcx hir::Path<'tcx>,
1076 args: GenericArgsRef<'tcx>,
1077 ) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'tcx {
1078 let tcx = self.tecx.tcx;
1079 let have_turbofish = path.segments.iter().any(|segment| {
1080 segment.args.is_some_and(|args| args.args.iter().any(|arg| arg.is_ty_or_const()))
1081 });
1082 let last_segment_using_path_data: Option<_> = try {
1088 let generics_def_id = tcx.res_generics_def_id(path.res)?;
1089 let generics = tcx.generics_of(generics_def_id);
1090 if generics.has_impl_trait() {
1091 do yeet ();
1092 }
1093 let insert_span =
1094 path.segments.last().unwrap().ident.span.shrink_to_hi().with_hi(path.span.hi());
1095 InsertableGenericArgs {
1096 insert_span,
1097 args,
1098 generics_def_id,
1099 def_id: path.res.def_id(),
1100 have_turbofish,
1101 }
1102 };
1103
1104 path.segments
1105 .iter()
1106 .filter_map(move |segment| {
1107 let res = segment.res;
1108 let generics_def_id = tcx.res_generics_def_id(res)?;
1109 let generics = tcx.generics_of(generics_def_id);
1110 if generics.has_impl_trait() {
1111 return None;
1112 }
1113 let span = tcx.hir().span(segment.hir_id);
1114 let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
1115 Some(InsertableGenericArgs {
1116 insert_span,
1117 args,
1118 generics_def_id,
1119 def_id: res.def_id(),
1120 have_turbofish,
1121 })
1122 })
1123 .chain(last_segment_using_path_data)
1124 }
1125
1126 fn path_inferred_arg_iter(
1127 &self,
1128 hir_id: HirId,
1129 args: GenericArgsRef<'tcx>,
1130 qpath: &'tcx hir::QPath<'tcx>,
1131 ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
1132 let tcx = self.tecx.tcx;
1133 match qpath {
1134 hir::QPath::Resolved(_self_ty, path) => {
1135 Box::new(self.resolved_path_inferred_arg_iter(path, args))
1136 }
1137 hir::QPath::TypeRelative(ty, segment) => {
1138 let Some(def_id) = self.typeck_results.type_dependent_def_id(hir_id) else {
1139 return Box::new(iter::empty());
1140 };
1141
1142 let generics = tcx.generics_of(def_id);
1143 let segment: Option<_> = try {
1144 if !segment.infer_args || generics.has_impl_trait() {
1145 do yeet ();
1146 }
1147 let span = tcx.hir().span(segment.hir_id);
1148 let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
1149 InsertableGenericArgs {
1150 insert_span,
1151 args,
1152 generics_def_id: def_id,
1153 def_id,
1154 have_turbofish: false,
1155 }
1156 };
1157
1158 let parent_def_id = generics.parent.unwrap();
1159 if let DefKind::Impl { .. } = tcx.def_kind(parent_def_id) {
1160 let parent_ty = tcx.type_of(parent_def_id).instantiate(tcx, args);
1161 match (parent_ty.kind(), &ty.kind) {
1162 (
1163 ty::Adt(def, args),
1164 hir::TyKind::Path(hir::QPath::Resolved(_self_ty, path)),
1165 ) => {
1166 if tcx.res_generics_def_id(path.res) != Some(def.did()) {
1167 match path.res {
1168 Res::Def(DefKind::TyAlias, _) => {
1169 }
1176 Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => {}
1179 _ => warn!(
1180 "unexpected path: def={:?} args={:?} path={:?}",
1181 def, args, path,
1182 ),
1183 }
1184 } else {
1185 return Box::new(
1186 self.resolved_path_inferred_arg_iter(path, args).chain(segment),
1187 );
1188 }
1189 }
1190 _ => (),
1191 }
1192 }
1193
1194 Box::new(segment.into_iter())
1195 }
1196 hir::QPath::LangItem(_, _) => Box::new(iter::empty()),
1197 }
1198 }
1199}
1200
1201impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
1202 type NestedFilter = nested_filter::OnlyBodies;
1203
1204 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1205 self.tecx.tcx
1206 }
1207
1208 fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) {
1209 intravisit::walk_local(self, local);
1210
1211 if let Some(ty) = self.opt_node_type(local.hir_id) {
1212 if self.generic_arg_contains_target(ty.into()) {
1213 match local.source {
1214 LocalSource::Normal if local.ty.is_none() => {
1215 self.update_infer_source(InferSource {
1216 span: local.pat.span,
1217 kind: InferSourceKind::LetBinding {
1218 insert_span: local.pat.span.shrink_to_hi(),
1219 pattern_name: local.pat.simple_ident(),
1220 ty,
1221 def_id: None,
1222 },
1223 })
1224 }
1225 _ => {}
1226 }
1227 }
1228 }
1229 }
1230
1231 fn visit_body(&mut self, body: &Body<'tcx>) {
1234 for param in body.params {
1235 debug!(
1236 "param: span {:?}, ty_span {:?}, pat.span {:?}",
1237 param.span, param.ty_span, param.pat.span
1238 );
1239 if param.ty_span != param.pat.span {
1240 debug!("skipping param: has explicit type");
1241 continue;
1242 }
1243
1244 let Some(param_ty) = self.opt_node_type(param.hir_id) else { continue };
1245
1246 if self.generic_arg_contains_target(param_ty.into()) {
1247 self.update_infer_source(InferSource {
1248 span: param.pat.span,
1249 kind: InferSourceKind::ClosureArg {
1250 insert_span: param.pat.span.shrink_to_hi(),
1251 ty: param_ty,
1252 },
1253 })
1254 }
1255 }
1256 intravisit::walk_body(self, body);
1257 }
1258
1259 #[instrument(level = "debug", skip(self))]
1260 fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
1261 let tcx = self.tecx.tcx;
1262 match expr.kind {
1263 ExprKind::Call(func, args) => {
1266 for arg in args {
1267 self.visit_expr(arg);
1268 }
1269 self.visit_expr(func);
1270 }
1271 _ => intravisit::walk_expr(self, expr),
1272 }
1273
1274 for args in self.expr_inferred_arg_iter(expr) {
1275 debug!(?args);
1276 let InsertableGenericArgs {
1277 insert_span,
1278 args,
1279 generics_def_id,
1280 def_id,
1281 have_turbofish,
1282 } = args;
1283 let generics = tcx.generics_of(generics_def_id);
1284 if let Some(mut argument_index) = generics
1285 .own_args(args)
1286 .iter()
1287 .position(|&arg| self.generic_arg_contains_target(arg))
1288 {
1289 if generics.parent.is_none() && generics.has_self {
1290 argument_index += 1;
1291 }
1292 let args = self.tecx.resolve_vars_if_possible(args);
1293 let generic_args =
1294 &generics.own_args_no_defaults(tcx, args)[generics.own_counts().lifetimes..];
1295 let span = match expr.kind {
1296 ExprKind::MethodCall(path, ..) => path.ident.span,
1297 _ => expr.span,
1298 };
1299
1300 self.update_infer_source(InferSource {
1301 span,
1302 kind: InferSourceKind::GenericArg {
1303 insert_span,
1304 argument_index,
1305 generics_def_id,
1306 def_id,
1307 generic_args,
1308 have_turbofish,
1309 },
1310 });
1311 }
1312 }
1313
1314 if let Some(node_ty) = self.opt_node_type(expr.hir_id) {
1315 if let (
1316 &ExprKind::Closure(&Closure { fn_decl, body, fn_decl_span, .. }),
1317 ty::Closure(_, args),
1318 ) = (&expr.kind, node_ty.kind())
1319 {
1320 let output = args.as_closure().sig().output().skip_binder();
1321 if self.generic_arg_contains_target(output.into()) {
1322 let body = self.tecx.tcx.hir_body(body);
1323 let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) {
1324 None
1325 } else {
1326 Some(body.value.span.shrink_to_hi())
1327 };
1328 self.update_infer_source(InferSource {
1329 span: fn_decl_span,
1330 kind: InferSourceKind::ClosureReturn {
1331 ty: output,
1332 data: &fn_decl.output,
1333 should_wrap_expr,
1334 },
1335 })
1336 }
1337 }
1338 }
1339
1340 let has_impl_trait = |def_id| {
1341 iter::successors(Some(tcx.generics_of(def_id)), |generics| {
1342 generics.parent.map(|def_id| tcx.generics_of(def_id))
1343 })
1344 .any(|generics| generics.has_impl_trait())
1345 };
1346 if let ExprKind::MethodCall(path, receiver, method_args, span) = expr.kind
1347 && let Some(args) = self.node_args_opt(expr.hir_id)
1348 && args.iter().any(|arg| self.generic_arg_contains_target(arg))
1349 && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id)
1350 && self.tecx.tcx.trait_of_item(def_id).is_some()
1351 && !has_impl_trait(def_id)
1352 && tcx.hir_opt_delegation_sig_id(expr.hir_id.owner.def_id).is_none()
1355 {
1356 let successor =
1357 method_args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo()));
1358 let args = self.tecx.resolve_vars_if_possible(args);
1359 self.update_infer_source(InferSource {
1360 span: path.ident.span,
1361 kind: InferSourceKind::FullyQualifiedMethodCall {
1362 receiver,
1363 successor,
1364 args,
1365 def_id,
1366 },
1367 })
1368 }
1369 }
1370}