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