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 if let ty::Adt(expected_adt, args) = exp_found.expected.kind() {
96 let compatible_variants: Vec<_> = expected_adt
97 .variants()
98 .iter()
99 .filter(|variant| {
100 variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
101 })
102 .filter_map(|variant| {
103 let sole_field = &variant.single_field();
104 let sole_field_ty = sole_field.ty(self.tcx, args);
105 if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
106 let variant_path =
107 with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
108 if let Some(path) = variant_path.strip_prefix("std::prelude::") {
110 if let Some((_, path)) = path.split_once("::") {
111 return Some(path.to_string());
112 }
113 }
114 Some(variant_path)
115 } else {
116 None
117 }
118 })
119 .collect();
120 match &compatible_variants[..] {
121 [] => {}
122 [variant] => {
123 let sugg = SuggestTuplePatternOne {
124 variant: variant.to_owned(),
125 span_low: cause.span.shrink_to_lo(),
126 span_high: cause.span.shrink_to_hi(),
127 };
128 diag.subdiagnostic(sugg);
129 }
130 _ => {
131 let sugg = SuggestTuplePatternMany {
133 path: self.tcx.def_path_str(expected_adt.did()),
134 cause_span: cause.span,
135 compatible_variants,
136 };
137 diag.subdiagnostic(sugg);
138 }
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 {
292 if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
293 if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
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 }
307
308 pub(super) fn suggest_turning_stmt_into_expr(
309 &self,
310 cause: &ObligationCause<'tcx>,
311 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
312 diag: &mut Diag<'_>,
313 ) {
314 let ty::error::ExpectedFound { expected, found } = exp_found;
315 if !found.peel_refs().is_unit() {
316 return;
317 }
318
319 let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code()
320 else {
321 return;
322 };
323
324 let node = self.tcx.hir_node(*hir_id);
325 let mut blocks = vec![];
326 if let hir::Node::Block(block) = node
327 && let Some(expr) = block.expr
328 && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind
329 && let Res::Local(local) = res
330 && let Node::LetStmt(LetStmt { init: Some(init), .. }) =
331 self.tcx.parent_hir_node(*local)
332 {
333 fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) {
334 match expr.kind {
335 hir::ExprKind::If(_, blk1, Some(blk2)) => {
337 collect_blocks(blk1, blocks);
338 collect_blocks(blk2, blocks);
339 }
340 hir::ExprKind::Match(_, arms, _) => {
341 for arm in arms.iter() {
343 collect_blocks(arm.body, blocks);
344 }
345 }
346 hir::ExprKind::Block(blk, _) => {
347 blocks.push(blk);
348 }
349 _ => {}
350 }
351 }
352 collect_blocks(init, &mut blocks);
353 }
354
355 let expected_inner: Ty<'_> = expected.peel_refs();
356 for block in blocks.iter() {
357 self.consider_removing_semicolon(block, expected_inner, diag);
358 }
359 }
360
361 pub fn consider_removing_semicolon(
373 &self,
374 blk: &'tcx hir::Block<'tcx>,
375 expected_ty: Ty<'tcx>,
376 diag: &mut Diag<'_>,
377 ) -> bool {
378 if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
379 if let StatementAsExpression::NeedsBoxing = boxed {
380 diag.span_suggestion_verbose(
381 span_semi,
382 "consider removing this semicolon and boxing the expression",
383 "",
384 Applicability::HasPlaceholders,
385 );
386 } else {
387 diag.span_suggestion_short(
388 span_semi,
389 "remove this semicolon to return this value",
390 "",
391 Applicability::MachineApplicable,
392 );
393 }
394 true
395 } else {
396 false
397 }
398 }
399
400 pub(crate) fn suggest_function_pointers_impl(
401 &self,
402 span: Option<Span>,
403 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
404 diag: &mut Diag<'_>,
405 ) {
406 let ty::error::ExpectedFound { expected, found } = exp_found;
407 let expected_inner = expected.peel_refs();
408 let found_inner = found.peel_refs();
409 if !expected_inner.is_fn() || !found_inner.is_fn() {
410 return;
411 }
412 match (expected_inner.kind(), found_inner.kind()) {
413 (ty::FnPtr(sig_tys, hdr), ty::FnDef(did, args)) => {
414 let sig = sig_tys.with(*hdr);
415 let expected_sig = &(self.normalize_fn_sig)(sig);
416 let found_sig =
417 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
418
419 let fn_name = self.tcx.def_path_str_with_args(*did, args);
420
421 if !self.same_type_modulo_infer(*found_sig, *expected_sig)
422 || !sig.is_suggestable(self.tcx, true)
423 || self.tcx.intrinsic(*did).is_some()
424 {
425 return;
426 }
427
428 let Some(span) = span else {
429 let casting = format!("{fn_name} as {sig}");
430 diag.subdiagnostic(FnItemsAreDistinct);
431 diag.subdiagnostic(FnConsiderCasting { casting });
432 return;
433 };
434
435 let sugg = match (expected.is_ref(), found.is_ref()) {
436 (true, false) => {
437 FunctionPointerSuggestion::UseRef { span: span.shrink_to_lo() }
438 }
439 (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
440 (true, true) => {
441 diag.subdiagnostic(FnItemsAreDistinct);
442 FunctionPointerSuggestion::CastRef { span, fn_name, sig }
443 }
444 (false, false) => {
445 diag.subdiagnostic(FnItemsAreDistinct);
446 FunctionPointerSuggestion::Cast { span: span.shrink_to_hi(), sig }
447 }
448 };
449 diag.subdiagnostic(sugg);
450 }
451 (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
452 let expected_sig =
453 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1));
454 let found_sig =
455 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
456
457 if self.same_type_modulo_infer(*expected_sig, *found_sig) {
458 diag.subdiagnostic(FnUniqTypes);
459 }
460
461 if !self.same_type_modulo_infer(*found_sig, *expected_sig)
462 || !found_sig.is_suggestable(self.tcx, true)
463 || !expected_sig.is_suggestable(self.tcx, true)
464 || self.tcx.intrinsic(*did1).is_some()
465 || self.tcx.intrinsic(*did2).is_some()
466 {
467 return;
468 }
469
470 let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
471
472 let Some(span) = span else {
473 diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig });
474 return;
475 };
476
477 let sug = if found.is_ref() {
478 FunctionPointerSuggestion::CastBothRef {
479 span,
480 fn_name,
481 found_sig: *found_sig,
482 expected_sig: *expected_sig,
483 }
484 } else {
485 FunctionPointerSuggestion::CastBoth {
486 span: span.shrink_to_hi(),
487 found_sig: *found_sig,
488 expected_sig: *expected_sig,
489 }
490 };
491
492 diag.subdiagnostic(sug);
493 }
494 (ty::FnDef(did, args), ty::FnPtr(sig_tys, hdr)) => {
495 let expected_sig =
496 &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
497 let found_sig = &(self.normalize_fn_sig)(sig_tys.with(*hdr));
498
499 if !self.same_type_modulo_infer(*found_sig, *expected_sig) {
500 return;
501 }
502
503 let fn_name = self.tcx.def_path_str_with_args(*did, args);
504
505 let casting = if expected.is_ref() {
506 format!("&({fn_name} as {found_sig})")
507 } else {
508 format!("{fn_name} as {found_sig}")
509 };
510
511 diag.subdiagnostic(FnConsiderCasting { casting });
512 }
513 _ => {
514 return;
515 }
516 };
517 }
518
519 pub(super) fn suggest_function_pointers(
520 &self,
521 cause: &ObligationCause<'tcx>,
522 span: Span,
523 exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
524 terr: TypeError<'tcx>,
525 diag: &mut Diag<'_>,
526 ) {
527 debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
528
529 if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() {
530 self.suggest_function_pointers_impl(Some(span), exp_found, diag);
531 } else if let TypeError::Sorts(exp_found) = terr {
532 self.suggest_function_pointers_impl(None, &exp_found, diag);
533 }
534 }
535
536 fn should_suggest_as_ref_kind(
537 &self,
538 expected: Ty<'tcx>,
539 found: Ty<'tcx>,
540 ) -> Option<SuggestAsRefKind> {
541 if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) =
542 (expected.kind(), found.kind())
543 {
544 if let ty::Adt(found_def, found_args) = *found_ty.kind() {
545 if exp_def == &found_def {
546 let have_as_ref = &[
547 (sym::Option, SuggestAsRefKind::Option),
548 (sym::Result, SuggestAsRefKind::Result),
549 ];
550 if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
551 self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
552 }) {
553 let mut show_suggestion = true;
554 for (exp_ty, found_ty) in
555 std::iter::zip(exp_args.types(), found_args.types())
556 {
557 match *exp_ty.kind() {
558 ty::Ref(_, exp_ty, _) => {
559 match (exp_ty.kind(), found_ty.kind()) {
560 (_, ty::Param(_))
561 | (_, ty::Infer(_))
562 | (ty::Param(_), _)
563 | (ty::Infer(_), _) => {}
564 _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
565 _ => show_suggestion = false,
566 };
567 }
568 ty::Param(_) | ty::Infer(_) => {}
569 _ => show_suggestion = false,
570 }
571 }
572 if show_suggestion {
573 return Some(*msg);
574 }
575 }
576 }
577 }
578 }
579 None
580 }
581
582 pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
584 match self.should_suggest_as_ref_kind(expected, found) {
585 Some(SuggestAsRefKind::Option) => Some(
586 "you can convert from `&Option<T>` to `Option<&T>` using \
587 `.as_ref()`",
588 ),
589 Some(SuggestAsRefKind::Result) => Some(
590 "you can convert from `&Result<T, E>` to \
591 `Result<&T, &E>` using `.as_ref()`",
592 ),
593 None => None,
594 }
595 }
596 pub(super) fn suggest_let_for_letchains(
600 &self,
601 cause: &ObligationCause<'_>,
602 span: Span,
603 ) -> Option<TypeErrorAdditionalDiags> {
604 struct IfVisitor {
606 found_if: bool,
607 err_span: Span,
608 }
609
610 impl<'v> Visitor<'v> for IfVisitor {
611 type Result = ControlFlow<()>;
612 fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
613 match ex.kind {
614 hir::ExprKind::If(cond, _, _) => {
615 self.found_if = true;
616 walk_expr(self, cond)?;
617 self.found_if = false;
618 ControlFlow::Continue(())
619 }
620 _ => walk_expr(self, ex),
621 }
622 }
623
624 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
625 if let hir::StmtKind::Let(LetStmt {
626 span,
627 pat: hir::Pat { .. },
628 ty: None,
629 init: Some(_),
630 ..
631 }) = &ex.kind
632 && self.found_if
633 && span.eq(&self.err_span)
634 {
635 ControlFlow::Break(())
636 } else {
637 walk_stmt(self, ex)
638 }
639 }
640 }
641
642 self.tcx.hir_maybe_body_owned_by(cause.body_id).and_then(|body| {
643 IfVisitor { err_span: span, found_if: false }
644 .visit_body(&body)
645 .is_break()
646 .then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
647 })
648 }
649
650 pub(super) fn suggest_for_all_lifetime_closure(
653 &self,
654 span: Span,
655 hir: hir::Node<'_>,
656 exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>,
657 diag: &mut Diag<'_>,
658 ) {
659 let hir::Node::Expr(hir::Expr {
661 kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }),
662 ..
663 }) = hir
664 else {
665 return;
666 };
667 let hir::Body { params, .. } = self.tcx.hir_body(*body);
668
669 let Some(expected) = exp_found.expected.args.get(1) else {
672 return;
673 };
674 let Some(found) = exp_found.found.args.get(1) else {
675 return;
676 };
677 let expected = expected.kind();
678 let found = found.kind();
679 if let GenericArgKind::Type(expected) = expected
681 && let GenericArgKind::Type(found) = found
682 && let ty::Tuple(expected) = expected.kind()
683 && let ty::Tuple(found) = found.kind()
684 && expected.len() == found.len()
685 {
686 let mut suggestion = "|".to_string();
687 let mut is_first = true;
688 let mut has_suggestion = false;
689
690 for (((expected, found), param_hir), arg_hir) in
691 expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter())
692 {
693 if is_first {
694 is_first = false;
695 } else {
696 suggestion += ", ";
697 }
698
699 if let ty::Ref(expected_region, _, _) = expected.kind()
700 && let ty::Ref(found_region, _, _) = found.kind()
701 && expected_region.is_bound()
702 && !found_region.is_bound()
703 && let hir::TyKind::Infer(()) = arg_hir.kind
704 {
705 if param_hir.pat.span == param_hir.ty_span {
708 let Ok(pat) =
710 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
711 else {
712 return;
713 };
714 suggestion += &format!("{pat}: &_");
715 } else {
716 let Ok(pat) =
718 self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
719 else {
720 return;
721 };
722 let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span)
723 else {
724 return;
725 };
726 suggestion += &format!("{pat}: &{ty}");
727 }
728 has_suggestion = true;
729 } else {
730 let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else {
731 return;
732 };
733 suggestion += &arg;
735 }
736 }
737 suggestion += "|";
738
739 if has_suggestion {
740 diag.span_suggestion_verbose(
741 span,
742 "consider specifying the type of the closure parameters",
743 suggestion,
744 Applicability::MaybeIncorrect,
745 );
746 }
747 }
748 }
749}
750
751impl<'tcx> TypeErrCtxt<'_, 'tcx> {
752 fn could_remove_semicolon(
755 &self,
756 blk: &'tcx hir::Block<'tcx>,
757 expected_ty: Ty<'tcx>,
758 ) -> Option<(Span, StatementAsExpression)> {
759 let blk = blk.innermost_block();
760 if blk.expr.is_some() {
762 return None;
763 }
764 let last_stmt = blk.stmts.last()?;
765 let hir::StmtKind::Semi(last_expr) = last_stmt.kind else {
766 return None;
767 };
768 let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?;
769 let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
770 _ if last_expr_ty.references_error() => return None,
771 _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
772 StatementAsExpression::CorrectType
773 }
774 (
775 ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, .. }),
776 ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, .. }),
777 ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
778 (
779 ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, args: last_bounds, .. }),
780 ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, args: exp_bounds, .. }),
781 ) => {
782 debug!(
783 "both opaque, likely future {:?} {:?} {:?} {:?}",
784 last_def_id, last_bounds, exp_def_id, exp_bounds
785 );
786
787 let last_local_id = last_def_id.as_local()?;
788 let exp_local_id = exp_def_id.as_local()?;
789
790 match (
791 &self.tcx.hir_expect_opaque_ty(last_local_id),
792 &self.tcx.hir_expect_opaque_ty(exp_local_id),
793 ) {
794 (
795 hir::OpaqueTy { bounds: last_bounds, .. },
796 hir::OpaqueTy { bounds: exp_bounds, .. },
797 ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
798 left, right,
799 ) {
800 (hir::GenericBound::Trait(tl), hir::GenericBound::Trait(tr))
802 if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
803 && tl.modifiers == tr.modifiers =>
804 {
805 true
806 }
807 _ => false,
808 }) =>
809 {
810 StatementAsExpression::NeedsBoxing
811 }
812 _ => StatementAsExpression::CorrectType,
813 }
814 }
815 _ => return None,
816 };
817 let span = if last_stmt.span.from_expansion() {
818 let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
819 self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
820 } else {
821 self.tcx
822 .sess
823 .source_map()
824 .span_extend_while_whitespace(last_expr.span)
825 .shrink_to_hi()
826 .with_hi(last_stmt.span.hi())
827 };
828
829 Some((span, needs_box))
830 }
831
832 fn consider_returning_binding_diag(
835 &self,
836 blk: &'tcx hir::Block<'tcx>,
837 expected_ty: Ty<'tcx>,
838 ) -> Option<SuggestRemoveSemiOrReturnBinding> {
839 let blk = blk.innermost_block();
840 if blk.expr.is_some() {
842 return None;
843 }
844 let mut shadowed = FxIndexSet::default();
845 let mut candidate_idents = vec![];
846 let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
847 if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
848 && let Some(pat_ty) = self
849 .typeck_results
850 .as_ref()
851 .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
852 {
853 let pat_ty = self.resolve_vars_if_possible(pat_ty);
854 if self.same_type_modulo_infer(pat_ty, expected_ty)
855 && !(pat_ty, expected_ty).references_error()
856 && shadowed.insert(ident.name)
857 {
858 candidate_idents.push((*ident, pat_ty));
859 }
860 }
861 true
862 };
863
864 for stmt in blk.stmts.iter().rev() {
865 let hir::StmtKind::Let(local) = &stmt.kind else {
866 continue;
867 };
868 local.pat.walk(&mut find_compatible_candidates);
869 }
870 match self.tcx.parent_hir_node(blk.hir_id) {
871 hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) {
872 hir::Node::Arm(hir::Arm { pat, .. }) => {
873 pat.walk(&mut find_compatible_candidates);
874 }
875
876 hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn { body, .. }, .. })
877 | hir::Node::ImplItem(hir::ImplItem {
878 kind: hir::ImplItemKind::Fn(_, body), ..
879 })
880 | hir::Node::TraitItem(hir::TraitItem {
881 kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
882 ..
883 })
884 | hir::Node::Expr(hir::Expr {
885 kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
886 ..
887 }) => {
888 for param in self.tcx.hir_body(*body).params {
889 param.pat.walk(&mut find_compatible_candidates);
890 }
891 }
892 hir::Node::Expr(hir::Expr {
893 kind:
894 hir::ExprKind::If(
895 hir::Expr { kind: hir::ExprKind::Let(let_), .. },
896 then_block,
897 _,
898 ),
899 ..
900 }) if then_block.hir_id == *hir_id => {
901 let_.pat.walk(&mut find_compatible_candidates);
902 }
903 _ => {}
904 },
905 _ => {}
906 }
907
908 match &candidate_idents[..] {
909 [(ident, _ty)] => {
910 let sm = self.tcx.sess.source_map();
911 let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
912 let stmt_span = sm.stmt_span(stmt.span, blk.span);
913 let sugg = if sm.is_multiline(blk.span)
914 && let Some(spacing) = sm.indentation_before(stmt_span)
915 {
916 format!("\n{spacing}{ident}")
917 } else {
918 format!(" {ident}")
919 };
920 (stmt_span.shrink_to_hi(), sugg)
921 } else {
922 let sugg = if sm.is_multiline(blk.span)
923 && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
924 {
925 format!("\n{spacing} {ident}\n{spacing}")
926 } else {
927 format!(" {ident} ")
928 };
929 let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
930 (sm.span_extend_while_whitespace(left_span), sugg)
931 };
932 Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
933 }
934 values if (1..3).contains(&values.len()) => {
935 let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
936 Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
937 }
938 _ => None,
939 }
940 }
941
942 pub fn consider_returning_binding(
943 &self,
944 blk: &'tcx hir::Block<'tcx>,
945 expected_ty: Ty<'tcx>,
946 err: &mut Diag<'_>,
947 ) -> bool {
948 let diag = self.consider_returning_binding_diag(blk, expected_ty);
949 match diag {
950 Some(diag) => {
951 err.subdiagnostic(diag);
952 true
953 }
954 None => false,
955 }
956 }
957}