]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
New upstream version 1.64.0+dfsg1
[rustc.git] / compiler / rustc_infer / src / infer / error_reporting / need_type_info.rs
1 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
2 use crate::infer::InferCtxt;
3 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
4 use rustc_hir as hir;
5 use rustc_hir::def::Res;
6 use rustc_hir::def::{CtorOf, DefKind, Namespace};
7 use rustc_hir::def_id::DefId;
8 use rustc_hir::intravisit::{self, Visitor};
9 use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource};
10 use rustc_middle::hir::nested_filter;
11 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
12 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
13 use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
14 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef};
15 use rustc_middle::ty::{self, DefIdTree, InferConst};
16 use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults};
17 use rustc_span::symbol::{kw, Ident};
18 use rustc_span::{BytePos, Span};
19 use std::borrow::Cow;
20 use std::iter;
21
22 pub enum TypeAnnotationNeeded {
23 /// ```compile_fail,E0282
24 /// let x = "hello".chars().rev().collect();
25 /// ```
26 E0282,
27 /// An implementation cannot be chosen unambiguously because of lack of information.
28 /// ```compile_fail,E0283
29 /// let _ = Default::default();
30 /// ```
31 E0283,
32 /// ```compile_fail,E0284
33 /// let mut d: u64 = 2;
34 /// d = d % 1u32.into();
35 /// ```
36 E0284,
37 }
38
39 impl Into<rustc_errors::DiagnosticId> for TypeAnnotationNeeded {
40 fn into(self) -> rustc_errors::DiagnosticId {
41 match self {
42 Self::E0282 => rustc_errors::error_code!(E0282),
43 Self::E0283 => rustc_errors::error_code!(E0283),
44 Self::E0284 => rustc_errors::error_code!(E0284),
45 }
46 }
47 }
48
49 /// Information about a constant or a type containing inference variables.
50 pub struct InferenceDiagnosticsData {
51 pub name: String,
52 pub span: Option<Span>,
53 pub kind: UnderspecifiedArgKind,
54 pub parent: Option<InferenceDiagnosticsParentData>,
55 }
56
57 /// Data on the parent definition where a generic argument was declared.
58 pub struct InferenceDiagnosticsParentData {
59 prefix: &'static str,
60 name: String,
61 }
62
63 pub enum UnderspecifiedArgKind {
64 Type { prefix: Cow<'static, str> },
65 Const { is_parameter: bool },
66 }
67
68 impl InferenceDiagnosticsData {
69 /// Generate a label for a generic argument which can't be inferred. When not
70 /// much is known about the argument, `use_diag` may be used to describe the
71 /// labeled value.
72 fn cannot_infer_msg(&self) -> String {
73 if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) {
74 return "cannot infer type".to_string();
75 }
76
77 let suffix = match &self.parent {
78 Some(parent) => parent.suffix_string(),
79 None => String::new(),
80 };
81
82 // For example: "cannot infer type for type parameter `T`"
83 format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix)
84 }
85
86 fn where_x_is_specified(&self, in_type: Ty<'_>) -> String {
87 if in_type.is_ty_infer() {
88 String::new()
89 } else if self.name == "_" {
90 // FIXME: Consider specializing this message if there is a single `_`
91 // in the type.
92 ", where the placeholders `_` are specified".to_string()
93 } else {
94 format!(", where the {} `{}` is specified", self.kind.prefix_string(), self.name)
95 }
96 }
97 }
98
99 impl InferenceDiagnosticsParentData {
100 fn for_parent_def_id(
101 tcx: TyCtxt<'_>,
102 parent_def_id: DefId,
103 ) -> Option<InferenceDiagnosticsParentData> {
104 let parent_name =
105 tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string();
106
107 Some(InferenceDiagnosticsParentData {
108 prefix: tcx.def_kind(parent_def_id).descr(parent_def_id),
109 name: parent_name,
110 })
111 }
112
113 fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> {
114 Self::for_parent_def_id(tcx, tcx.parent(def_id))
115 }
116
117 fn suffix_string(&self) -> String {
118 format!(" declared on the {} `{}`", self.prefix, self.name)
119 }
120 }
121
122 impl UnderspecifiedArgKind {
123 fn prefix_string(&self) -> Cow<'static, str> {
124 match self {
125 Self::Type { prefix } => format!("type for {}", prefix).into(),
126 Self::Const { is_parameter: true } => "the value of const parameter".into(),
127 Self::Const { is_parameter: false } => "the value of the constant".into(),
128 }
129 }
130 }
131
132 fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> {
133 let mut printer = FmtPrinter::new(infcx.tcx, ns);
134 let ty_getter = move |ty_vid| {
135 if infcx.probe_ty_var(ty_vid).is_ok() {
136 warn!("resolved ty var in error message");
137 }
138 if let TypeVariableOriginKind::TypeParameterDefinition(name, _) =
139 infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind
140 {
141 Some(name)
142 } else {
143 None
144 }
145 };
146 printer.ty_infer_name_resolver = Some(Box::new(ty_getter));
147 let const_getter = move |ct_vid| {
148 if infcx.probe_const_var(ct_vid).is_ok() {
149 warn!("resolved const var in error message");
150 }
151 if let ConstVariableOriginKind::ConstParameterDefinition(name, _) =
152 infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind
153 {
154 return Some(name);
155 } else {
156 None
157 }
158 };
159 printer.const_infer_name_resolver = Some(Box::new(const_getter));
160 printer
161 }
162
163 fn ty_to_string<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String {
164 let printer = fmt_printer(infcx, Namespace::TypeNS);
165 let ty = infcx.resolve_vars_if_possible(ty);
166 match ty.kind() {
167 // We don't want the regular output for `fn`s because it includes its path in
168 // invalid pseudo-syntax, we want the `fn`-pointer output instead.
169 ty::FnDef(..) => ty.fn_sig(infcx.tcx).print(printer).unwrap().into_buffer(),
170 // FIXME: The same thing for closures, but this only works when the closure
171 // does not capture anything.
172 //
173 // We do have to hide the `extern "rust-call"` ABI in that case though,
174 // which is too much of a bother for now.
175 _ => ty.print(printer).unwrap().into_buffer(),
176 }
177 }
178
179 /// We don't want to directly use `ty_to_string` for closures as their type isn't really
180 /// something users are familar with. Directly printing the `fn_sig` of closures also
181 /// doesn't work as they actually use the "rust-call" API.
182 fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String {
183 let ty::Closure(_, substs) = ty.kind() else { unreachable!() };
184 let fn_sig = substs.as_closure().sig();
185 let args = fn_sig
186 .inputs()
187 .skip_binder()
188 .iter()
189 .next()
190 .map(|args| {
191 args.tuple_fields()
192 .iter()
193 .map(|arg| ty_to_string(infcx, arg))
194 .collect::<Vec<_>>()
195 .join(", ")
196 })
197 .unwrap_or_default();
198 let ret = if fn_sig.output().skip_binder().is_unit() {
199 String::new()
200 } else {
201 format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder()))
202 };
203 format!("fn({}){}", args, ret)
204 }
205
206 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
207 /// Extracts data used by diagnostic for either types or constants
208 /// which were stuck during inference.
209 pub fn extract_inference_diagnostics_data(
210 &self,
211 arg: GenericArg<'tcx>,
212 highlight: Option<ty::print::RegionHighlightMode<'tcx>>,
213 ) -> InferenceDiagnosticsData {
214 match arg.unpack() {
215 GenericArgKind::Type(ty) => {
216 if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() {
217 let mut inner = self.inner.borrow_mut();
218 let ty_vars = &inner.type_variables();
219 let var_origin = ty_vars.var_origin(ty_vid);
220 if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) =
221 var_origin.kind
222 {
223 if name != kw::SelfUpper {
224 return InferenceDiagnosticsData {
225 name: name.to_string(),
226 span: Some(var_origin.span),
227 kind: UnderspecifiedArgKind::Type {
228 prefix: "type parameter".into(),
229 },
230 parent: def_id.and_then(|def_id| {
231 InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id)
232 }),
233 };
234 }
235 }
236 }
237
238 let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
239 if let Some(highlight) = highlight {
240 printer.region_highlight_mode = highlight;
241 }
242 InferenceDiagnosticsData {
243 name: ty.print(printer).unwrap().into_buffer(),
244 span: None,
245 kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) },
246 parent: None,
247 }
248 }
249 GenericArgKind::Const(ct) => {
250 if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() {
251 let origin =
252 self.inner.borrow_mut().const_unification_table().probe_value(vid).origin;
253 if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) =
254 origin.kind
255 {
256 return InferenceDiagnosticsData {
257 name: name.to_string(),
258 span: Some(origin.span),
259 kind: UnderspecifiedArgKind::Const { is_parameter: true },
260 parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id),
261 };
262 }
263
264 debug_assert!(!origin.span.is_dummy());
265 let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS);
266 if let Some(highlight) = highlight {
267 printer.region_highlight_mode = highlight;
268 }
269 InferenceDiagnosticsData {
270 name: ct.print(printer).unwrap().into_buffer(),
271 span: Some(origin.span),
272 kind: UnderspecifiedArgKind::Const { is_parameter: false },
273 parent: None,
274 }
275 } else {
276 // If we end up here the `FindInferSourceVisitor`
277 // won't work, as its expected argument isn't an inference variable.
278 //
279 // FIXME: Ideally we should look into the generic constant
280 // to figure out which inference var is actually unresolved so that
281 // this path is unreachable.
282 let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS);
283 if let Some(highlight) = highlight {
284 printer.region_highlight_mode = highlight;
285 }
286 InferenceDiagnosticsData {
287 name: ct.print(printer).unwrap().into_buffer(),
288 span: None,
289 kind: UnderspecifiedArgKind::Const { is_parameter: false },
290 parent: None,
291 }
292 }
293 }
294 GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
295 }
296 }
297
298 /// Used as a fallback in [InferCtxt::emit_inference_failure_err]
299 /// in case we weren't able to get a better error.
300 fn bad_inference_failure_err(
301 &self,
302 span: Span,
303 arg_data: InferenceDiagnosticsData,
304 error_code: TypeAnnotationNeeded,
305 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
306 let error_code = error_code.into();
307 let mut err =
308 self.tcx.sess.struct_span_err_with_code(span, "type annotations needed", error_code);
309 err.span_label(span, arg_data.cannot_infer_msg());
310 err
311 }
312
313 pub fn emit_inference_failure_err(
314 &self,
315 body_id: Option<hir::BodyId>,
316 failure_span: Span,
317 arg: GenericArg<'tcx>,
318 error_code: TypeAnnotationNeeded,
319 should_label_span: bool,
320 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
321 let arg = self.resolve_vars_if_possible(arg);
322 let arg_data = self.extract_inference_diagnostics_data(arg, None);
323
324 let Some(typeck_results) = self.in_progress_typeck_results else {
325 // If we don't have any typeck results we're outside
326 // of a body, so we won't be able to get better info
327 // here.
328 return self.bad_inference_failure_err(failure_span, arg_data, error_code);
329 };
330 let typeck_results = typeck_results.borrow();
331 let typeck_results = &typeck_results;
332
333 let mut local_visitor = FindInferSourceVisitor::new(&self, typeck_results, arg);
334 if let Some(body_id) = body_id {
335 let expr = self.tcx.hir().expect_expr(body_id.hir_id);
336 local_visitor.visit_expr(expr);
337 }
338
339 let Some(InferSource { span, kind }) = local_visitor.infer_source else {
340 return self.bad_inference_failure_err(failure_span, arg_data, error_code)
341 };
342
343 let error_code = error_code.into();
344 let mut err = self.tcx.sess.struct_span_err_with_code(
345 span,
346 &format!("type annotations needed{}", kind.ty_msg(self)),
347 error_code,
348 );
349
350 if should_label_span && !failure_span.overlaps(span) {
351 err.span_label(failure_span, "type must be known at this point");
352 }
353
354 match kind {
355 InferSourceKind::LetBinding { insert_span, pattern_name, ty } => {
356 let suggestion_msg = if let Some(name) = pattern_name {
357 format!(
358 "consider giving `{}` an explicit type{}",
359 name,
360 arg_data.where_x_is_specified(ty)
361 )
362 } else {
363 format!(
364 "consider giving this pattern a type{}",
365 arg_data.where_x_is_specified(ty)
366 )
367 };
368 err.span_suggestion_verbose(
369 insert_span,
370 &suggestion_msg,
371 format!(": {}", ty_to_string(self, ty)),
372 Applicability::HasPlaceholders,
373 );
374 }
375 InferSourceKind::ClosureArg { insert_span, ty } => {
376 err.span_suggestion_verbose(
377 insert_span,
378 &format!(
379 "consider giving this closure parameter an explicit type{}",
380 arg_data.where_x_is_specified(ty)
381 ),
382 format!(": {}", ty_to_string(self, ty)),
383 Applicability::HasPlaceholders,
384 );
385 }
386 InferSourceKind::GenericArg {
387 insert_span,
388 argument_index,
389 generics_def_id,
390 def_id: _,
391 generic_args,
392 } => {
393 let generics = self.tcx.generics_of(generics_def_id);
394 let is_type = matches!(arg.unpack(), GenericArgKind::Type(_));
395
396 let cannot_infer_msg = format!(
397 "cannot infer {} of the {} parameter `{}`{}",
398 if is_type { "type" } else { "the value" },
399 if is_type { "type" } else { "const" },
400 generics.params[argument_index].name,
401 // We use the `generics_def_id` here, as even when suggesting `None::<T>`,
402 // the type parameter `T` was still declared on the enum, not on the
403 // variant.
404 InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id)
405 .map_or(String::new(), |parent| parent.suffix_string()),
406 );
407
408 err.span_label(span, cannot_infer_msg);
409
410 let args = fmt_printer(self, Namespace::TypeNS)
411 .comma_sep(generic_args.iter().copied().map(|arg| {
412 if arg.is_suggestable(self.tcx, true) {
413 return arg;
414 }
415
416 match arg.unpack() {
417 GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
418 GenericArgKind::Type(_) => self
419 .next_ty_var(TypeVariableOrigin {
420 span: rustc_span::DUMMY_SP,
421 kind: TypeVariableOriginKind::MiscVariable,
422 })
423 .into(),
424 GenericArgKind::Const(arg) => self
425 .next_const_var(
426 arg.ty(),
427 ConstVariableOrigin {
428 span: rustc_span::DUMMY_SP,
429 kind: ConstVariableOriginKind::MiscVariable,
430 },
431 )
432 .into(),
433 }
434 }))
435 .unwrap()
436 .into_buffer();
437
438 err.span_suggestion_verbose(
439 insert_span,
440 &format!(
441 "consider specifying the generic argument{}",
442 pluralize!(generic_args.len()),
443 ),
444 format!("::<{}>", args),
445 Applicability::HasPlaceholders,
446 );
447 }
448 InferSourceKind::FullyQualifiedMethodCall { receiver, successor, substs, def_id } => {
449 let printer = fmt_printer(self, Namespace::ValueNS);
450 let def_path = printer.print_def_path(def_id, substs).unwrap().into_buffer();
451
452 // We only care about whether we have to add `&` or `&mut ` for now.
453 // This is the case if the last adjustment is a borrow and the
454 // first adjustment was not a builtin deref.
455 let adjustment = match typeck_results.expr_adjustments(receiver) {
456 [
457 Adjustment { kind: Adjust::Deref(None), target: _ },
458 ..,
459 Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ },
460 ] => "",
461 [
462 ..,
463 Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), target: _ },
464 ] => match mut_ {
465 AutoBorrowMutability::Mut { .. } => "&mut ",
466 AutoBorrowMutability::Not => "&",
467 },
468 _ => "",
469 };
470
471 let suggestion = vec![
472 (receiver.span.shrink_to_lo(), format!("{def_path}({adjustment}")),
473 (receiver.span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()),
474 ];
475 err.multipart_suggestion_verbose(
476 "try using a fully qualified path to specify the expected types",
477 suggestion,
478 Applicability::HasPlaceholders,
479 );
480 }
481 InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
482 let ret = ty_to_string(self, ty);
483 let (arrow, post) = match data {
484 FnRetTy::DefaultReturn(_) => ("-> ", " "),
485 _ => ("", ""),
486 };
487 let suggestion = match should_wrap_expr {
488 Some(end_span) => vec![
489 (data.span(), format!("{}{}{}{{ ", arrow, ret, post)),
490 (end_span, " }".to_string()),
491 ],
492 None => vec![(data.span(), format!("{}{}{}", arrow, ret, post))],
493 };
494 err.multipart_suggestion_verbose(
495 "try giving this closure an explicit return type",
496 suggestion,
497 Applicability::HasPlaceholders,
498 );
499 }
500 }
501 err
502 }
503
504 pub fn need_type_info_err_in_generator(
505 &self,
506 kind: hir::GeneratorKind,
507 span: Span,
508 ty: Ty<'tcx>,
509 ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
510 let ty = self.resolve_vars_if_possible(ty);
511 let data = self.extract_inference_diagnostics_data(ty.into(), None);
512
513 let mut err = struct_span_err!(
514 self.tcx.sess,
515 span,
516 E0698,
517 "type inside {} must be known in this context",
518 kind,
519 );
520 err.span_label(span, data.cannot_infer_msg());
521 err
522 }
523 }
524
525 #[derive(Debug)]
526 struct InferSource<'tcx> {
527 span: Span,
528 kind: InferSourceKind<'tcx>,
529 }
530
531 #[derive(Debug)]
532 enum InferSourceKind<'tcx> {
533 LetBinding {
534 insert_span: Span,
535 pattern_name: Option<Ident>,
536 ty: Ty<'tcx>,
537 },
538 ClosureArg {
539 insert_span: Span,
540 ty: Ty<'tcx>,
541 },
542 GenericArg {
543 insert_span: Span,
544 argument_index: usize,
545 generics_def_id: DefId,
546 def_id: DefId,
547 generic_args: &'tcx [GenericArg<'tcx>],
548 },
549 FullyQualifiedMethodCall {
550 receiver: &'tcx Expr<'tcx>,
551 /// If the method has other arguments, this is ", " and the start of the first argument,
552 /// while for methods without arguments this is ")" and the end of the method call.
553 successor: (&'static str, BytePos),
554 substs: SubstsRef<'tcx>,
555 def_id: DefId,
556 },
557 ClosureReturn {
558 ty: Ty<'tcx>,
559 data: &'tcx FnRetTy<'tcx>,
560 should_wrap_expr: Option<Span>,
561 },
562 }
563
564 impl<'tcx> InferSource<'tcx> {
565 fn from_expansion(&self) -> bool {
566 let source_from_expansion = match self.kind {
567 InferSourceKind::LetBinding { insert_span, .. }
568 | InferSourceKind::ClosureArg { insert_span, .. }
569 | InferSourceKind::GenericArg { insert_span, .. } => insert_span.from_expansion(),
570 InferSourceKind::FullyQualifiedMethodCall { receiver, .. } => {
571 receiver.span.from_expansion()
572 }
573 InferSourceKind::ClosureReturn { data, should_wrap_expr, .. } => {
574 data.span().from_expansion() || should_wrap_expr.map_or(false, Span::from_expansion)
575 }
576 };
577 source_from_expansion || self.span.from_expansion()
578 }
579 }
580
581 impl<'tcx> InferSourceKind<'tcx> {
582 fn ty_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> String {
583 match *self {
584 InferSourceKind::LetBinding { ty, .. }
585 | InferSourceKind::ClosureArg { ty, .. }
586 | InferSourceKind::ClosureReturn { ty, .. } => {
587 if ty.is_closure() {
588 format!(" for the closure `{}`", closure_as_fn_str(infcx, ty))
589 } else if !ty.is_ty_infer() {
590 format!(" for `{}`", ty_to_string(infcx, ty))
591 } else {
592 String::new()
593 }
594 }
595 // FIXME: We should be able to add some additional info here.
596 InferSourceKind::GenericArg { .. }
597 | InferSourceKind::FullyQualifiedMethodCall { .. } => String::new(),
598 }
599 }
600 }
601
602 #[derive(Debug)]
603 struct InsertableGenericArgs<'tcx> {
604 insert_span: Span,
605 substs: SubstsRef<'tcx>,
606 generics_def_id: DefId,
607 def_id: DefId,
608 }
609
610 /// A visitor which searches for the "best" spot to use in the inference error.
611 ///
612 /// For this it walks over the hir body and tries to check all places where
613 /// inference variables could be bound.
614 ///
615 /// While doing so, the currently best spot is stored in `infer_source`.
616 /// For details on how we rank spots, see [Self::source_cost]
617 struct FindInferSourceVisitor<'a, 'tcx> {
618 infcx: &'a InferCtxt<'a, 'tcx>,
619 typeck_results: &'a TypeckResults<'tcx>,
620
621 target: GenericArg<'tcx>,
622
623 attempt: usize,
624 infer_source_cost: usize,
625 infer_source: Option<InferSource<'tcx>>,
626 }
627
628 impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
629 fn new(
630 infcx: &'a InferCtxt<'a, 'tcx>,
631 typeck_results: &'a TypeckResults<'tcx>,
632 target: GenericArg<'tcx>,
633 ) -> Self {
634 FindInferSourceVisitor {
635 infcx,
636 typeck_results,
637
638 target,
639
640 attempt: 0,
641 infer_source_cost: usize::MAX,
642 infer_source: None,
643 }
644 }
645
646 /// Computes cost for the given source.
647 ///
648 /// Sources with a small cost are prefer and should result
649 /// in a clearer and idiomatic suggestion.
650 fn source_cost(&self, source: &InferSource<'tcx>) -> usize {
651 #[derive(Clone, Copy)]
652 struct CostCtxt<'tcx> {
653 tcx: TyCtxt<'tcx>,
654 }
655 impl<'tcx> CostCtxt<'tcx> {
656 fn arg_cost(self, arg: GenericArg<'tcx>) -> usize {
657 match arg.unpack() {
658 GenericArgKind::Lifetime(_) => 0, // erased
659 GenericArgKind::Type(ty) => self.ty_cost(ty),
660 GenericArgKind::Const(_) => 3, // some non-zero value
661 }
662 }
663 fn ty_cost(self, ty: Ty<'tcx>) -> usize {
664 match *ty.kind() {
665 ty::Closure(..) => 1000,
666 ty::FnDef(..) => 150,
667 ty::FnPtr(..) => 30,
668 ty::Adt(def, substs) => {
669 5 + self
670 .tcx
671 .generics_of(def.did())
672 .own_substs_no_defaults(self.tcx, substs)
673 .iter()
674 .map(|&arg| self.arg_cost(arg))
675 .sum::<usize>()
676 }
677 ty::Tuple(args) => 5 + args.iter().map(|arg| self.ty_cost(arg)).sum::<usize>(),
678 ty::Ref(_, ty, _) => 2 + self.ty_cost(ty),
679 ty::Infer(..) => 0,
680 _ => 1,
681 }
682 }
683 }
684
685 // The sources are listed in order of preference here.
686 let tcx = self.infcx.tcx;
687 let ctx = CostCtxt { tcx };
688 let base_cost = match source.kind {
689 InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty),
690 InferSourceKind::ClosureArg { ty, .. } => ctx.ty_cost(ty),
691 InferSourceKind::GenericArg { def_id, generic_args, .. } => {
692 let variant_cost = match tcx.def_kind(def_id) {
693 // `None::<u32>` and friends are ugly.
694 DefKind::Variant | DefKind::Ctor(CtorOf::Variant, _) => 15,
695 _ => 10,
696 };
697 variant_cost + generic_args.iter().map(|&arg| ctx.arg_cost(arg)).sum::<usize>()
698 }
699 InferSourceKind::FullyQualifiedMethodCall { substs, .. } => {
700 20 + substs.iter().map(|arg| ctx.arg_cost(arg)).sum::<usize>()
701 }
702 InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => {
703 30 + ctx.ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 }
704 }
705 };
706
707 let suggestion_may_apply = if source.from_expansion() { 10000 } else { 0 };
708
709 base_cost + suggestion_may_apply
710 }
711
712 /// Uses `fn source_cost` to determine whether this inference source is preferable to
713 /// previous sources. We generally prefer earlier sources.
714 #[instrument(level = "debug", skip(self))]
715 fn update_infer_source(&mut self, new_source: InferSource<'tcx>) {
716 let cost = self.source_cost(&new_source) + self.attempt;
717 debug!(?cost);
718 self.attempt += 1;
719 if cost < self.infer_source_cost {
720 self.infer_source_cost = cost;
721 self.infer_source = Some(new_source);
722 }
723 }
724
725 fn node_substs_opt(&self, hir_id: HirId) -> Option<SubstsRef<'tcx>> {
726 let substs = self.typeck_results.node_substs_opt(hir_id);
727 self.infcx.resolve_vars_if_possible(substs)
728 }
729
730 fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
731 let ty = self.typeck_results.node_type_opt(hir_id);
732 self.infcx.resolve_vars_if_possible(ty)
733 }
734
735 // Check whether this generic argument is the inference variable we
736 // are looking for.
737 fn generic_arg_is_target(&self, arg: GenericArg<'tcx>) -> bool {
738 if arg == self.target {
739 return true;
740 }
741
742 match (arg.unpack(), self.target.unpack()) {
743 (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => {
744 use ty::{Infer, TyVar};
745 match (inner_ty.kind(), target_ty.kind()) {
746 (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
747 self.infcx.inner.borrow_mut().type_variables().sub_unified(a_vid, b_vid)
748 }
749 _ => false,
750 }
751 }
752 (GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => {
753 use ty::InferConst::*;
754 match (inner_ct.kind(), target_ct.kind()) {
755 (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => self
756 .infcx
757 .inner
758 .borrow_mut()
759 .const_unification_table()
760 .unioned(a_vid, b_vid),
761 _ => false,
762 }
763 }
764 _ => false,
765 }
766 }
767
768 /// Does this generic argument contain our target inference variable
769 /// in a way which can be written by the user.
770 fn generic_arg_contains_target(&self, arg: GenericArg<'tcx>) -> bool {
771 let mut walker = arg.walk();
772 while let Some(inner) = walker.next() {
773 if self.generic_arg_is_target(inner) {
774 return true;
775 }
776 match inner.unpack() {
777 GenericArgKind::Lifetime(_) => {}
778 GenericArgKind::Type(ty) => {
779 if matches!(ty.kind(), ty::Opaque(..) | ty::Closure(..) | ty::Generator(..)) {
780 // Opaque types can't be named by the user right now.
781 //
782 // Both the generic arguments of closures and generators can
783 // also not be named. We may want to only look into the closure
784 // signature in case it has no captures, as that can be represented
785 // using `fn(T) -> R`.
786
787 // FIXME(type_alias_impl_trait): These opaque types
788 // can actually be named, so it would make sense to
789 // adjust this case and add a test for it.
790 walker.skip_current_subtree();
791 }
792 }
793 GenericArgKind::Const(ct) => {
794 if matches!(ct.kind(), ty::ConstKind::Unevaluated(..)) {
795 // You can't write the generic arguments for
796 // unevaluated constants.
797 walker.skip_current_subtree();
798 }
799 }
800 }
801 }
802 false
803 }
804
805 fn expr_inferred_subst_iter(
806 &self,
807 expr: &'tcx hir::Expr<'tcx>,
808 ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
809 let tcx = self.infcx.tcx;
810 match expr.kind {
811 hir::ExprKind::Path(ref path) => {
812 if let Some(substs) = self.node_substs_opt(expr.hir_id) {
813 return self.path_inferred_subst_iter(expr.hir_id, substs, path);
814 }
815 }
816 // FIXME(#98711): Ideally we would also deal with type relative
817 // paths here, even if that is quite rare.
818 //
819 // See the `need_type_info/expr-struct-type-relative-gat.rs` test
820 // for an example where that would be needed.
821 //
822 // However, the `type_dependent_def_id` for `Self::Output` in an
823 // impl is currently the `DefId` of `Output` in the trait definition
824 // which makes this somewhat difficult and prevents us from just
825 // using `self.path_inferred_subst_iter` here.
826 hir::ExprKind::Struct(&hir::QPath::Resolved(_self_ty, path), _, _) => {
827 if let Some(ty) = self.opt_node_type(expr.hir_id) {
828 if let ty::Adt(_, substs) = ty.kind() {
829 return Box::new(self.resolved_path_inferred_subst_iter(path, substs));
830 }
831 }
832 }
833 hir::ExprKind::MethodCall(segment, _, _) => {
834 if let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) {
835 let generics = tcx.generics_of(def_id);
836 let insertable: Option<_> = try {
837 if generics.has_impl_trait() {
838 None?
839 }
840 let substs = self.node_substs_opt(expr.hir_id)?;
841 let span = tcx.hir().span(segment.hir_id?);
842 let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
843 InsertableGenericArgs {
844 insert_span,
845 substs,
846 generics_def_id: def_id,
847 def_id,
848 }
849 };
850 return Box::new(insertable.into_iter());
851 }
852 }
853 _ => {}
854 }
855
856 Box::new(iter::empty())
857 }
858
859 fn resolved_path_inferred_subst_iter(
860 &self,
861 path: &'tcx hir::Path<'tcx>,
862 substs: SubstsRef<'tcx>,
863 ) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a {
864 let tcx = self.infcx.tcx;
865 // The last segment of a path often has `Res::Err` and the
866 // correct `Res` is the one of the whole path.
867 //
868 // FIXME: We deal with that one separately for now,
869 // would be good to remove this special case.
870 let last_segment_using_path_data: Option<_> = try {
871 let generics_def_id = tcx.res_generics_def_id(path.res)?;
872 let generics = tcx.generics_of(generics_def_id);
873 if generics.has_impl_trait() {
874 None?
875 }
876 let insert_span =
877 path.segments.last().unwrap().ident.span.shrink_to_hi().with_hi(path.span.hi());
878 InsertableGenericArgs {
879 insert_span,
880 substs,
881 generics_def_id,
882 def_id: path.res.def_id(),
883 }
884 };
885
886 path.segments
887 .iter()
888 .filter_map(move |segment| {
889 let res = segment.res?;
890 let generics_def_id = tcx.res_generics_def_id(res)?;
891 let generics = tcx.generics_of(generics_def_id);
892 if generics.has_impl_trait() {
893 return None;
894 }
895 let span = tcx.hir().span(segment.hir_id?);
896 let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
897 Some(InsertableGenericArgs {
898 insert_span,
899 substs,
900 generics_def_id,
901 def_id: res.def_id(),
902 })
903 })
904 .chain(last_segment_using_path_data)
905 }
906
907 fn path_inferred_subst_iter(
908 &self,
909 hir_id: HirId,
910 substs: SubstsRef<'tcx>,
911 qpath: &'tcx hir::QPath<'tcx>,
912 ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
913 let tcx = self.infcx.tcx;
914 match qpath {
915 hir::QPath::Resolved(_self_ty, path) => {
916 Box::new(self.resolved_path_inferred_subst_iter(path, substs))
917 }
918 hir::QPath::TypeRelative(ty, segment) => {
919 let Some(def_id) = self.typeck_results.type_dependent_def_id(hir_id) else {
920 return Box::new(iter::empty());
921 };
922
923 let generics = tcx.generics_of(def_id);
924 let segment: Option<_> = try {
925 if !segment.infer_args || generics.has_impl_trait() {
926 None?;
927 }
928 let span = tcx.hir().span(segment.hir_id?);
929 let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
930 InsertableGenericArgs { insert_span, substs, generics_def_id: def_id, def_id }
931 };
932
933 let parent_def_id = generics.parent.unwrap();
934 if tcx.def_kind(parent_def_id) == DefKind::Impl {
935 let parent_ty = tcx.bound_type_of(parent_def_id).subst(tcx, substs);
936 match (parent_ty.kind(), &ty.kind) {
937 (
938 ty::Adt(def, substs),
939 hir::TyKind::Path(hir::QPath::Resolved(_self_ty, path)),
940 ) => {
941 if tcx.res_generics_def_id(path.res) != Some(def.did()) {
942 match path.res {
943 Res::Def(DefKind::TyAlias, _) => {
944 // FIXME: Ideally we should support this. For that
945 // we have to map back from the self type to the
946 // type alias though. That's difficult.
947 //
948 // See the `need_type_info/type-alias.rs` test for
949 // some examples.
950 }
951 // There cannot be inference variables in the self type,
952 // so there's nothing for us to do here.
953 Res::SelfTy { .. } => {}
954 _ => warn!(
955 "unexpected path: def={:?} substs={:?} path={:?}",
956 def, substs, path,
957 ),
958 }
959 } else {
960 return Box::new(
961 self.resolved_path_inferred_subst_iter(path, substs)
962 .chain(segment),
963 );
964 }
965 }
966 _ => (),
967 }
968 }
969
970 Box::new(segment.into_iter())
971 }
972 hir::QPath::LangItem(_, _, _) => Box::new(iter::empty()),
973 }
974 }
975 }
976
977 impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
978 type NestedFilter = nested_filter::OnlyBodies;
979
980 fn nested_visit_map(&mut self) -> Self::Map {
981 self.infcx.tcx.hir()
982 }
983
984 fn visit_local(&mut self, local: &'tcx Local<'tcx>) {
985 intravisit::walk_local(self, local);
986
987 if let Some(ty) = self.opt_node_type(local.hir_id) {
988 if self.generic_arg_contains_target(ty.into()) {
989 match local.source {
990 LocalSource::Normal if local.ty.is_none() => {
991 self.update_infer_source(InferSource {
992 span: local.pat.span,
993 kind: InferSourceKind::LetBinding {
994 insert_span: local.pat.span.shrink_to_hi(),
995 pattern_name: local.pat.simple_ident(),
996 ty,
997 },
998 })
999 }
1000 _ => {}
1001 }
1002 }
1003 }
1004 }
1005
1006 /// For closures, we first visit the parameters and then the content,
1007 /// as we prefer those.
1008 fn visit_body(&mut self, body: &'tcx Body<'tcx>) {
1009 for param in body.params {
1010 debug!(
1011 "param: span {:?}, ty_span {:?}, pat.span {:?}",
1012 param.span, param.ty_span, param.pat.span
1013 );
1014 if param.ty_span != param.pat.span {
1015 debug!("skipping param: has explicit type");
1016 continue;
1017 }
1018
1019 let Some(param_ty) = self.opt_node_type(param.hir_id) else {
1020 continue
1021 };
1022
1023 if self.generic_arg_contains_target(param_ty.into()) {
1024 self.update_infer_source(InferSource {
1025 span: param.pat.span,
1026 kind: InferSourceKind::ClosureArg {
1027 insert_span: param.pat.span.shrink_to_hi(),
1028 ty: param_ty,
1029 },
1030 })
1031 }
1032 }
1033 intravisit::walk_body(self, body);
1034 }
1035
1036 #[instrument(level = "debug", skip(self))]
1037 fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
1038 let tcx = self.infcx.tcx;
1039 match expr.kind {
1040 // When encountering `func(arg)` first look into `arg` and then `func`,
1041 // as `arg` is "more specific".
1042 ExprKind::Call(func, args) => {
1043 for arg in args {
1044 self.visit_expr(arg);
1045 }
1046 self.visit_expr(func);
1047 }
1048 _ => intravisit::walk_expr(self, expr),
1049 }
1050
1051 for args in self.expr_inferred_subst_iter(expr) {
1052 debug!(?args);
1053 let InsertableGenericArgs { insert_span, substs, generics_def_id, def_id } = args;
1054 let generics = tcx.generics_of(generics_def_id);
1055 if let Some(argument_index) = generics
1056 .own_substs(substs)
1057 .iter()
1058 .position(|&arg| self.generic_arg_contains_target(arg))
1059 {
1060 let substs = self.infcx.resolve_vars_if_possible(substs);
1061 let generic_args = &generics.own_substs_no_defaults(tcx, substs)
1062 [generics.own_counts().lifetimes..];
1063 let span = match expr.kind {
1064 ExprKind::MethodCall(path, _, _) => path.ident.span,
1065 _ => expr.span,
1066 };
1067
1068 self.update_infer_source(InferSource {
1069 span,
1070 kind: InferSourceKind::GenericArg {
1071 insert_span,
1072 argument_index,
1073 generics_def_id,
1074 def_id,
1075 generic_args,
1076 },
1077 });
1078 }
1079 }
1080
1081 if let Some(node_ty) = self.opt_node_type(expr.hir_id) {
1082 if let (
1083 &ExprKind::Closure(&Closure { fn_decl, body, fn_decl_span, .. }),
1084 ty::Closure(_, substs),
1085 ) = (&expr.kind, node_ty.kind())
1086 {
1087 let output = substs.as_closure().sig().output().skip_binder();
1088 if self.generic_arg_contains_target(output.into()) {
1089 let body = self.infcx.tcx.hir().body(body);
1090 let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) {
1091 None
1092 } else {
1093 Some(body.value.span.shrink_to_hi())
1094 };
1095 self.update_infer_source(InferSource {
1096 span: fn_decl_span,
1097 kind: InferSourceKind::ClosureReturn {
1098 ty: output,
1099 data: &fn_decl.output,
1100 should_wrap_expr,
1101 },
1102 })
1103 }
1104 }
1105 }
1106
1107 let has_impl_trait = |def_id| {
1108 iter::successors(Some(tcx.generics_of(def_id)), |generics| {
1109 generics.parent.map(|def_id| tcx.generics_of(def_id))
1110 })
1111 .any(|generics| generics.has_impl_trait())
1112 };
1113 if let ExprKind::MethodCall(path, args, span) = expr.kind
1114 && let Some(substs) = self.node_substs_opt(expr.hir_id)
1115 && substs.iter().any(|arg| self.generic_arg_contains_target(arg))
1116 && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id)
1117 && self.infcx.tcx.trait_of_item(def_id).is_some()
1118 && !has_impl_trait(def_id)
1119 {
1120 let successor =
1121 args.get(1).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo()));
1122 let substs = self.infcx.resolve_vars_if_possible(substs);
1123 self.update_infer_source(InferSource {
1124 span: path.ident.span,
1125 kind: InferSourceKind::FullyQualifiedMethodCall {
1126 receiver: args.first().unwrap(),
1127 successor,
1128 substs,
1129 def_id,
1130 }
1131 })
1132 }
1133 }
1134 }