1use core::ops::ControlFlow;
2
3use hir::def::CtorKind;
4use hir::intravisit::{Visitor, walk_expr, walk_stmt};
5use hir::{LetStmt, QPath};
6use rustc_data_structures::fx::FxIndexSet;
7use rustc_errors::{Applicability, Diag};
8use rustc_hir as hir;
9use rustc_hir::def::Res;
10use rustc_hir::{MatchSource, Node};
11use rustc_middle::traits::{MatchExpressionArmCause, ObligationCause, ObligationCauseCode};
12use rustc_middle::ty::error::TypeError;
13use rustc_middle::ty::print::with_no_trimmed_paths;
14use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
15use rustc_span::{Span, sym};
16use tracing::debug;
17
18use crate::error_reporting::TypeErrCtxt;
19use crate::error_reporting::infer::hir::Path;
20use crate::errors::{
21 ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes,
22 FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
23 SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
24};
25
26#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
27enum StatementAsExpression {
28 CorrectType,
29 NeedsBoxing,
30}
31
32#[derive(Clone, Copy)]
33enum SuggestAsRefKind {
34 Option,
35 Result,
36}
37
38impl<'tcx> TypeErrCtxt<'_, 'tcx> {
39 pub(super) fn suggest_remove_semi_or_return_binding(
40 &self,
41 first_id: Option<hir::HirId>,
42 first_ty: Ty<'tcx>,
43 first_span: Span,
44 second_id: Option<hir::HirId>,
45 second_ty: Ty<'tcx>,
46 second_span: Span,
47 ) -> Option<SuggestRemoveSemiOrReturnBinding> {
48 let remove_semicolon = [
49 (first_id, self.resolve_vars_if_possible(second_ty)),
50 (second_id, self.resolve_vars_if_possible(first_ty)),
51 ]
52 .into_iter()
53 .find_map(|(id, ty)| {
54 let hir::Node::Block(blk) = self.tcx.hir_node(id?) else { return None };
55 self.could_remove_semicolon(blk, ty)
56 });
57 match remove_semicolon {
58 Some((sp, StatementAsExpression::NeedsBoxing)) => {
59 Some(SuggestRemoveSemiOrReturnBinding::RemoveAndBox {
60 first_lo: first_span.shrink_to_lo(),
61 first_hi: first_span.shrink_to_hi(),
62 second_lo: second_span.shrink_to_lo(),
63 second_hi: second_span.shrink_to_hi(),
64 sp,
65 })
66 }
67 Some((sp, StatementAsExpression::CorrectType)) => {
68 Some(SuggestRemoveSemiOrReturnBinding::Remove { sp })
69 }
70 None => {
71 let mut ret = None;
72 for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
73 if let Some(id) = id
74 && let hir::Node::Block(blk) = self.tcx.hir_node(id)
75 && let Some(diag) = self.consider_returning_binding_diag(blk, ty)
76 {
77 ret = Some(diag);
78 break;
79 }
80 }
81 ret
82 }
83 }
84 }
85
86 pub(super) fn suggest_tuple_pattern(
87 &self,
88 cause: &ObligationCause<'tcx>,
89 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
90 diag: &mut Diag<'_>,
91 ) {
92 if let ObligationCauseCode::Pattern { .. } = cause.code()
95 && let ty::Adt(expected_adt, args) = exp_found.expected.kind()
96 {
97 let compatible_variants: Vec<_> = expected_adt
98 .variants()
99 .iter()
100 .filter(|variant| {
101 variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
102 })
103 .filter_map(|variant| {
104 let sole_field = &variant.single_field();
105 let sole_field_ty = sole_field.ty(self.tcx, args);
106 if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
107 let variant_path =
108 with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
109 if let Some(path) = variant_path.strip_prefix("std::prelude::")
111 && let Some((_, path)) = path.split_once("::")
112 {
113 return Some(path.to_string());
114 }
115 Some(variant_path)
116 } else {
117 None
118 }
119 })
120 .collect();
121 match &compatible_variants[..] {
122 [] => {}
123 [variant] => {
124 let sugg = SuggestTuplePatternOne {
125 variant: variant.to_owned(),
126 span_low: cause.span.shrink_to_lo(),
127 span_high: cause.span.shrink_to_hi(),
128 };
129 diag.subdiagnostic(sugg);
130 }
131 _ => {
132 let sugg = SuggestTuplePatternMany {
134 path: self.tcx.def_path_str(expected_adt.did()),
135 cause_span: cause.span,
136 compatible_variants,
137 };
138 diag.subdiagnostic(sugg);
139 }
140 }
141 }
142 }
143
144 pub(super) fn suggest_await_on_expect_found(
163 &self,
164 cause: &ObligationCause<'tcx>,
165 exp_span: Span,
166 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
167 diag: &mut Diag<'_>,
168 ) {
169 debug!(
170 "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
171 exp_span, exp_found.expected, exp_found.found,
172 );
173
174 match self.tcx.coroutine_kind(cause.body_id) {
175 Some(hir::CoroutineKind::Desugared(
176 hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::AsyncGen,
177 _,
178 )) => (),
179 None
180 | Some(
181 hir::CoroutineKind::Coroutine(_)
182 | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _),
183 ) => return,
184 }
185
186 if let ObligationCauseCode::CompareImplItem { .. } = cause.code() {
187 return;
188 }
189
190 let subdiag = match (
191 self.get_impl_future_output_ty(exp_found.expected),
192 self.get_impl_future_output_ty(exp_found.found),
193 ) {
194 (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
195 .code()
196 {
197 ObligationCauseCode::IfExpression { expr_id, .. } => {
198 let hir::Node::Expr(hir::Expr {
199 kind: hir::ExprKind::If(_, then_expr, _), ..
200 }) = self.tcx.hir_node(*expr_id)
201 else {
202 return;
203 };
204 let then_span = self.find_block_span_from_hir_id(then_expr.hir_id);
205 Some(ConsiderAddingAwait::BothFuturesSugg {
206 first: then_span.shrink_to_hi(),
207 second: exp_span.shrink_to_hi(),
208 })
209 }
210 ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
211 prior_non_diverging_arms,
212 ..
213 }) => {
214 if let [.., arm_span] = &prior_non_diverging_arms[..] {
215 Some(ConsiderAddingAwait::BothFuturesSugg {
216 first: arm_span.shrink_to_hi(),
217 second: exp_span.shrink_to_hi(),
218 })
219 } else {
220 Some(ConsiderAddingAwait::BothFuturesHelp)
221 }
222 }
223 _ => Some(ConsiderAddingAwait::BothFuturesHelp),
224 },
225 (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
226 diag.subdiagnostic(ConsiderAddingAwait::FutureSugg {
228 span: exp_span.shrink_to_hi(),
229 });
230 Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span })
231 }
232 (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
233 {
234 ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => {
235 origin_expr.is_some().then_some(ConsiderAddingAwait::FutureSugg {
236 span: then_span.shrink_to_hi(),
237 })
238 }
239 ObligationCauseCode::IfExpression { expr_id, .. } => {
240 let hir::Node::Expr(hir::Expr {
241 kind: hir::ExprKind::If(_, then_expr, _), ..
242 }) = self.tcx.hir_node(*expr_id)
243 else {
244 return;
245 };
246 let then_span = self.find_block_span_from_hir_id(then_expr.hir_id);
247 Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
248 }
249 ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
250 prior_non_diverging_arms,
251 ..
252 }) => Some({
253 ConsiderAddingAwait::FutureSuggMultiple {
254 spans: prior_non_diverging_arms
255 .iter()
256 .map(|arm| arm.shrink_to_hi())
257 .collect(),
258 }
259 }),
260 _ => None,
261 },
262 _ => None,
263 };
264 if let Some(subdiag) = subdiag {
265 diag.subdiagnostic(subdiag);
266 }
267 }
268
269 pub(super) fn suggest_accessing_field_where_appropriate(
270 &self,
271 cause: &ObligationCause<'tcx>,
272 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
273 diag: &mut Diag<'_>,
274 ) {
275 debug!(
276 "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
277 cause, exp_found
278 );
279 if let ty::Adt(expected_def, expected_args) = exp_found.expected.kind() {
280 if expected_def.is_enum() {
281 return;
282 }
283
284 if let Some((name, ty)) = expected_def
285 .non_enum_variant()
286 .fields
287 .iter()
288 .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
289 .map(|field| (field.name, field.ty(self.tcx, expected_args)))
290 .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
291 && let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code()
292 && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
293 {
294 let suggestion = if expected_def.is_struct() {
295 SuggestAccessingField::Safe { span, snippet, name, ty }
296 } else if expected_def.is_union() {
297 SuggestAccessingField::Unsafe { span, snippet, name, ty }
298 } else {
299 return;
300 };
301 diag.subdiagnostic(suggestion);
302 }
303 }
304 }
305
306 pub(super) fn suggest_turning_stmt_into_expr(
307 &self,
308 cause: &ObligationCause<'tcx>,
309 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
310 diag: &mut Diag<'_>,
311 ) {
312 let ty::error::ExpectedFound { expected, found } = exp_found;
313 if !found.peel_refs().is_unit() {
314 return;
315 }
316
317 let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code()
318 else {
319 return;
320 };
321
322 let node = self.tcx.hir_node(*hir_id);
323 let mut blocks = vec![];
324 if let hir::Node::Block(block) = node
325 && let Some(expr) = block.expr
326 && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind
327 && let Res::Local(local) = res
328 && let Node::LetStmt(LetStmt { init: Some(init), .. }) =
329 self.tcx.parent_hir_node(*local)
330 {
331 fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) {
332 match expr.kind {
333 hir::ExprKind::If(_, blk1, Some(blk2)) => {
335 collect_blocks(blk1, blocks);
336 collect_blocks(blk2, blocks);
337 }
338 hir::ExprKind::Match(_, arms, _) => {
339 for arm in arms.iter() {
341 collect_blocks(arm.body, blocks);
342 }
343 }
344 hir::ExprKind::Block(blk, _) => {
345 blocks.push(blk);
346 }
347 _ => {}
348 }
349 }
350 collect_blocks(init, &mut blocks);
351 }
352
353 let expected_inner: Ty<'_> = expected.peel_refs();
354 for block in blocks.iter() {
355 self.consider_removing_semicolon(block, expected_inner, diag);
356 }
357 }
358
359 pub fn consider_removing_semicolon(
371 &self,
372 blk: &'tcx hir::Block<'tcx>,
373 expected_ty: Ty<'tcx>,
374 diag: &mut Diag<'_>,
375 ) -> bool {
376 if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
377 if let StatementAsExpression::NeedsBoxing = boxed {
378 diag.span_suggestion_verbose(
379 span_semi,
380 "consider removing this semicolon and boxing the expression",
381 "",
382 Applicability::HasPlaceholders,
383 );
384 } else {
385 diag.span_suggestion_short(
386 span_semi,
387 "remove this semicolon to return this value",
388 "",
389 Applicability::MachineApplicable,
390 );
391 }
392 true
393 } else {
394 false
395 }
396 }
397
398 pub(crate) fn suggest_function_pointers_impl(
399 &self,
400 span: Option<Span>,
401 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
402 diag: &mut Diag<'_>,
403 ) {
404 let ty::error::ExpectedFound { expected, found } = exp_found;
405 let expected_inner = expected.peel_refs();
406 let found_inner = found.peel_refs();
407 if !expected_inner.is_fn() || !found_inner.is_fn() {
408 return;
409 }
410 match (expected_inner.kind(), found_inner.kind()) {
411 (ty::FnPtr(sig_tys, hdr), ty::FnDef(did, args)) => {
412 let sig = sig_tys.with(*hdr);
413 let expected_sig = &(self.normalize_fn_sig)(sig);
414 let found_sig =
415 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
416
417 let fn_name = self.tcx.def_path_str_with_args(*did, args);
418
419 if !self.same_type_modulo_infer(*found_sig, *expected_sig)
420 || !sig.is_suggestable(self.tcx, true)
421 || self.tcx.intrinsic(*did).is_some()
422 {
423 return;
424 }
425
426 let Some(span) = span else {
427 let casting = format!("{fn_name} as {sig}");
428 diag.subdiagnostic(FnItemsAreDistinct);
429 diag.subdiagnostic(FnConsiderCasting { casting });
430 return;
431 };
432
433 let sugg = match (expected.is_ref(), found.is_ref()) {
434 (true, false) => {
435 FunctionPointerSuggestion::UseRef { span: span.shrink_to_lo() }
436 }
437 (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
438 (true, true) => {
439 diag.subdiagnostic(FnItemsAreDistinct);
440 FunctionPointerSuggestion::CastRef { span, fn_name, sig }
441 }
442 (false, false) => {
443 diag.subdiagnostic(FnItemsAreDistinct);
444 FunctionPointerSuggestion::Cast { span: span.shrink_to_hi(), sig }
445 }
446 };
447 diag.subdiagnostic(sugg);
448 }
449 (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
450 let expected_sig =
451 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1));
452 let found_sig =
453 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
454
455 if self.same_type_modulo_infer(*expected_sig, *found_sig) {
456 diag.subdiagnostic(FnUniqTypes);
457 }
458
459 if !self.same_type_modulo_infer(*found_sig, *expected_sig)
460 || !found_sig.is_suggestable(self.tcx, true)
461 || !expected_sig.is_suggestable(self.tcx, true)
462 || self.tcx.intrinsic(*did1).is_some()
463 || self.tcx.intrinsic(*did2).is_some()
464 {
465 return;
466 }
467
468 let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
469
470 let Some(span) = span else {
471 diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig });
472 return;
473 };
474
475 let sug = if found.is_ref() {
476 FunctionPointerSuggestion::CastBothRef {
477 span,
478 fn_name,
479 found_sig: *found_sig,
480 expected_sig: *expected_sig,
481 }
482 } else {
483 FunctionPointerSuggestion::CastBoth {
484 span: span.shrink_to_hi(),
485 found_sig: *found_sig,
486 expected_sig: *expected_sig,
487 }
488 };
489
490 diag.subdiagnostic(sug);
491 }
492 (ty::FnDef(did, args), ty::FnPtr(sig_tys, hdr)) => {
493 let expected_sig =
494 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
495 let found_sig = &(self.normalize_fn_sig)(sig_tys.with(*hdr));
496
497 if !self.same_type_modulo_infer(*found_sig, *expected_sig) {
498 return;
499 }
500
501 let fn_name = self.tcx.def_path_str_with_args(*did, args);
502
503 let casting = if expected.is_ref() {
504 format!("&({fn_name} as {found_sig})")
505 } else {
506 format!("{fn_name} as {found_sig}")
507 };
508
509 diag.subdiagnostic(FnConsiderCasting { casting });
510 }
511 _ => {
512 return;
513 }
514 };
515 }
516
517 pub(super) fn suggest_function_pointers(
518 &self,
519 cause: &ObligationCause<'tcx>,
520 span: Span,
521 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
522 terr: TypeError<'tcx>,
523 diag: &mut Diag<'_>,
524 ) {
525 debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
526
527 if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() {
528 self.suggest_function_pointers_impl(Some(span), exp_found, diag);
529 } else if let TypeError::Sorts(exp_found) = terr {
530 self.suggest_function_pointers_impl(None, &exp_found, diag);
531 }
532 }
533
534 fn should_suggest_as_ref_kind(
535 &self,
536 expected: Ty<'tcx>,
537 found: Ty<'tcx>,
538 ) -> Option<SuggestAsRefKind> {
539 if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) =
540 (expected.kind(), found.kind())
541 && let ty::Adt(found_def, found_args) = *found_ty.kind()
542 {
543 if exp_def == &found_def {
544 let have_as_ref = &[
545 (sym::Option, SuggestAsRefKind::Option),
546 (sym::Result, SuggestAsRefKind::Result),
547 ];
548 if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
549 self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
550 }) {
551 let mut show_suggestion = true;
552 for (exp_ty, found_ty) in std::iter::zip(exp_args.types(), found_args.types()) {
553 match *exp_ty.kind() {
554 ty::Ref(_, exp_ty, _) => {
555 match (exp_ty.kind(), found_ty.kind()) {
556 (_, ty::Param(_))
557 | (_, ty::Infer(_))
558 | (ty::Param(_), _)
559 | (ty::Infer(_), _) => {}
560 _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
561 _ => show_suggestion = false,
562 };
563 }
564 ty::Param(_) | ty::Infer(_) => {}
565 _ => show_suggestion = false,
566 }
567 }
568 if show_suggestion {
569 return Some(*msg);
570 }
571 }
572 }
573 }
574 None
575 }
576
577 pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
579 match self.should_suggest_as_ref_kind(expected, found) {
580 Some(SuggestAsRefKind::Option) => Some(
581 "you can convert from `&Option<T>` to `Option<&T>` using \
582 `.as_ref()`",
583 ),
584 Some(SuggestAsRefKind::Result) => Some(
585 "you can convert from `&Result<T, E>` to \
586 `Result<&T, &E>` using `.as_ref()`",
587 ),
588 None => None,
589 }
590 }
591 pub(super) fn suggest_let_for_letchains(
595 &self,
596 cause: &ObligationCause<'_>,
597 span: Span,
598 ) -> Option<TypeErrorAdditionalDiags> {
599 struct IfVisitor {
601 found_if: bool,
602 err_span: Span,
603 }
604
605 impl<'v> Visitor<'v> for IfVisitor {
606 type Result = ControlFlow<()>;
607 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
608 match ex.kind {
609 hir::ExprKind::If(cond, _, _) => {
610 self.found_if = true;
611 walk_expr(self, cond)?;
612 self.found_if = false;
613 ControlFlow::Continue(())
614 }
615 _ => walk_expr(self, ex),
616 }
617 }
618
619 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
620 if let hir::StmtKind::Let(LetStmt {
621 span,
622 pat: hir::Pat { .. },
623 ty: None,
624 init: Some(_),
625 ..
626 }) = &ex.kind
627 && self.found_if
628 && span.eq(&self.err_span)
629 {
630 ControlFlow::Break(())
631 } else {
632 walk_stmt(self, ex)
633 }
634 }
635 }
636
637 self.tcx.hir_maybe_body_owned_by(cause.body_id).and_then(|body| {
638 IfVisitor { err_span: span, found_if: false }
639 .visit_body(&body)
640 .is_break()
641 .then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
642 })
643 }
644
645 pub(super) fn suggest_for_all_lifetime_closure(
648 &self,
649 span: Span,
650 hir: hir::Node<'_>,
651 exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>,
652 diag: &mut Diag<'_>,
653 ) {
654 let hir::Node::Expr(hir::Expr {
656 kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }),
657 ..
658 }) = hir
659 else {
660 return;
661 };
662 let hir::Body { params, .. } = self.tcx.hir_body(*body);
663
664 let Some(expected) = exp_found.expected.args.get(1) else {
667 return;
668 };
669 let Some(found) = exp_found.found.args.get(1) else {
670 return;
671 };
672 let expected = expected.kind();
673 let found = found.kind();
674 if let GenericArgKind::Type(expected) = expected
676 && let GenericArgKind::Type(found) = found
677 && let ty::Tuple(expected) = expected.kind()
678 && let ty::Tuple(found) = found.kind()
679 && expected.len() == found.len()
680 {
681 let mut suggestion = "|".to_string();
682 let mut is_first = true;
683 let mut has_suggestion = false;
684
685 for (((expected, found), param_hir), arg_hir) in
686 expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter())
687 {
688 if is_first {
689 is_first = false;
690 } else {
691 suggestion += ", ";
692 }
693
694 if let ty::Ref(expected_region, _, _) = expected.kind()
695 && let ty::Ref(found_region, _, _) = found.kind()
696 && expected_region.is_bound()
697 && !found_region.is_bound()
698 && let hir::TyKind::Infer(()) = arg_hir.kind
699 {
700 if param_hir.pat.span == param_hir.ty_span {
703 let Ok(pat) =
705 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
706 else {
707 return;
708 };
709 suggestion += &format!("{pat}: &_");
710 } else {
711 let Ok(pat) =
713 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
714 else {
715 return;
716 };
717 let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span)
718 else {
719 return;
720 };
721 suggestion += &format!("{pat}: &{ty}");
722 }
723 has_suggestion = true;
724 } else {
725 let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else {
726 return;
727 };
728 suggestion += &arg;
730 }
731 }
732 suggestion += "|";
733
734 if has_suggestion {
735 diag.span_suggestion_verbose(
736 span,
737 "consider specifying the type of the closure parameters",
738 suggestion,
739 Applicability::MaybeIncorrect,
740 );
741 }
742 }
743 }
744}
745
746impl<'tcx> TypeErrCtxt<'_, 'tcx> {
747 fn could_remove_semicolon(
750 &self,
751 blk: &'tcx hir::Block<'tcx>,
752 expected_ty: Ty<'tcx>,
753 ) -> Option<(Span, StatementAsExpression)> {
754 let blk = blk.innermost_block();
755 if blk.expr.is_some() {
757 return None;
758 }
759 let last_stmt = blk.stmts.last()?;
760 let hir::StmtKind::Semi(last_expr) = last_stmt.kind else {
761 return None;
762 };
763 let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?;
764 let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
765 _ if last_expr_ty.references_error() => return None,
766 _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
767 StatementAsExpression::CorrectType
768 }
769 (
770 ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, .. }),
771 ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, .. }),
772 ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
773 (
774 ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, args: last_bounds, .. }),
775 ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, args: exp_bounds, .. }),
776 ) => {
777 debug!(
778 "both opaque, likely future {:?} {:?} {:?} {:?}",
779 last_def_id, last_bounds, exp_def_id, exp_bounds
780 );
781
782 let last_local_id = last_def_id.as_local()?;
783 let exp_local_id = exp_def_id.as_local()?;
784
785 match (
786 &self.tcx.hir_expect_opaque_ty(last_local_id),
787 &self.tcx.hir_expect_opaque_ty(exp_local_id),
788 ) {
789 (
790 hir::OpaqueTy { bounds: last_bounds, .. },
791 hir::OpaqueTy { bounds: exp_bounds, .. },
792 ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
793 left, right,
794 ) {
795 (hir::GenericBound::Trait(tl), hir::GenericBound::Trait(tr))
797 if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
798 && tl.modifiers == tr.modifiers =>
799 {
800 true
801 }
802 _ => false,
803 }) =>
804 {
805 StatementAsExpression::NeedsBoxing
806 }
807 _ => StatementAsExpression::CorrectType,
808 }
809 }
810 _ => return None,
811 };
812 let span = if last_stmt.span.from_expansion() {
813 let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
814 self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
815 } else {
816 self.tcx
817 .sess
818 .source_map()
819 .span_extend_while_whitespace(last_expr.span)
820 .shrink_to_hi()
821 .with_hi(last_stmt.span.hi())
822 };
823
824 Some((span, needs_box))
825 }
826
827 fn consider_returning_binding_diag(
830 &self,
831 blk: &'tcx hir::Block<'tcx>,
832 expected_ty: Ty<'tcx>,
833 ) -> Option<SuggestRemoveSemiOrReturnBinding> {
834 let blk = blk.innermost_block();
835 if blk.expr.is_some() {
837 return None;
838 }
839 let mut shadowed = FxIndexSet::default();
840 let mut candidate_idents = vec![];
841 let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
842 if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
843 && let Some(pat_ty) = self
844 .typeck_results
845 .as_ref()
846 .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
847 {
848 let pat_ty = self.resolve_vars_if_possible(pat_ty);
849 if self.same_type_modulo_infer(pat_ty, expected_ty)
850 && !(pat_ty, expected_ty).references_error()
851 && shadowed.insert(ident.name)
852 {
853 candidate_idents.push((*ident, pat_ty));
854 }
855 }
856 true
857 };
858
859 for stmt in blk.stmts.iter().rev() {
860 let hir::StmtKind::Let(local) = &stmt.kind else {
861 continue;
862 };
863 local.pat.walk(&mut find_compatible_candidates);
864 }
865 match self.tcx.parent_hir_node(blk.hir_id) {
866 hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) {
867 hir::Node::Arm(hir::Arm { pat, .. }) => {
868 pat.walk(&mut find_compatible_candidates);
869 }
870
871 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { body, .. }, .. })
872 | hir::Node::ImplItem(hir::ImplItem {
873 kind: hir::ImplItemKind::Fn(_, body), ..
874 })
875 | hir::Node::TraitItem(hir::TraitItem {
876 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
877 ..
878 })
879 | hir::Node::Expr(hir::Expr {
880 kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
881 ..
882 }) => {
883 for param in self.tcx.hir_body(*body).params {
884 param.pat.walk(&mut find_compatible_candidates);
885 }
886 }
887 hir::Node::Expr(hir::Expr {
888 kind:
889 hir::ExprKind::If(
890 hir::Expr { kind: hir::ExprKind::Let(let_), .. },
891 then_block,
892 _,
893 ),
894 ..
895 }) if then_block.hir_id == *hir_id => {
896 let_.pat.walk(&mut find_compatible_candidates);
897 }
898 _ => {}
899 },
900 _ => {}
901 }
902
903 match &candidate_idents[..] {
904 [(ident, _ty)] => {
905 let sm = self.tcx.sess.source_map();
906 let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
907 let stmt_span = sm.stmt_span(stmt.span, blk.span);
908 let sugg = if sm.is_multiline(blk.span)
909 && let Some(spacing) = sm.indentation_before(stmt_span)
910 {
911 format!("\n{spacing}{ident}")
912 } else {
913 format!(" {ident}")
914 };
915 (stmt_span.shrink_to_hi(), sugg)
916 } else {
917 let sugg = if sm.is_multiline(blk.span)
918 && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
919 {
920 format!("\n{spacing} {ident}\n{spacing}")
921 } else {
922 format!(" {ident} ")
923 };
924 let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
925 (sm.span_extend_while_whitespace(left_span), sugg)
926 };
927 Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
928 }
929 values if (1..3).contains(&values.len()) => {
930 let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
931 Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
932 }
933 _ => None,
934 }
935 }
936
937 pub fn consider_returning_binding(
938 &self,
939 blk: &'tcx hir::Block<'tcx>,
940 expected_ty: Ty<'tcx>,
941 err: &mut Diag<'_>,
942 ) -> bool {
943 let diag = self.consider_returning_binding_diag(blk, expected_ty);
944 match diag {
945 Some(diag) => {
946 err.subdiagnostic(diag);
947 true
948 }
949 None => false,
950 }
951 }
952}