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