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