• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use hir::def::CtorKind;
2 use hir::intravisit::{walk_expr, walk_stmt, Visitor};
3 use rustc_data_structures::fx::FxIndexSet;
4 use rustc_errors::{Applicability, Diagnostic};
5 use rustc_hir as hir;
6 use rustc_middle::traits::{
7     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
8     StatementAsExpression,
9 };
10 use rustc_middle::ty::print::with_no_trimmed_paths;
11 use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
12 use rustc_span::{sym, BytePos, Span};
13 
14 use crate::errors::{
15     ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
16     FunctionPointerSuggestion, SuggestAccessingField, SuggestBoxingForReturnImplTrait,
17     SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne,
18     TypeErrorAdditionalDiags,
19 };
20 
21 use super::TypeErrCtxt;
22 
23 #[derive(Clone, Copy)]
24 pub enum SuggestAsRefKind {
25     Option,
26     Result,
27 }
28 
29 impl<'tcx> TypeErrCtxt<'_, 'tcx> {
suggest_remove_semi_or_return_binding( &self, first_id: Option<hir::HirId>, first_ty: Ty<'tcx>, first_span: Span, second_id: Option<hir::HirId>, second_ty: Ty<'tcx>, second_span: Span, ) -> Option<SuggestRemoveSemiOrReturnBinding>30     pub(super) fn suggest_remove_semi_or_return_binding(
31         &self,
32         first_id: Option<hir::HirId>,
33         first_ty: Ty<'tcx>,
34         first_span: Span,
35         second_id: Option<hir::HirId>,
36         second_ty: Ty<'tcx>,
37         second_span: Span,
38     ) -> Option<SuggestRemoveSemiOrReturnBinding> {
39         let remove_semicolon = [
40             (first_id, self.resolve_vars_if_possible(second_ty)),
41             (second_id, self.resolve_vars_if_possible(first_ty)),
42         ]
43         .into_iter()
44         .find_map(|(id, ty)| {
45             let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None };
46             self.could_remove_semicolon(blk, ty)
47         });
48         match remove_semicolon {
49             Some((sp, StatementAsExpression::NeedsBoxing)) => {
50                 Some(SuggestRemoveSemiOrReturnBinding::RemoveAndBox {
51                     first_lo: first_span.shrink_to_lo(),
52                     first_hi: first_span.shrink_to_hi(),
53                     second_lo: second_span.shrink_to_lo(),
54                     second_hi: second_span.shrink_to_hi(),
55                     sp,
56                 })
57             }
58             Some((sp, StatementAsExpression::CorrectType)) => {
59                 Some(SuggestRemoveSemiOrReturnBinding::Remove { sp })
60             }
61             None => {
62                 let mut ret = None;
63                 for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
64                     if let Some(id) = id
65                         && let hir::Node::Block(blk) = self.tcx.hir().get(id)
66                         && let Some(diag) = self.consider_returning_binding_diag(blk, ty)
67                     {
68                         ret = Some(diag);
69                         break;
70                     }
71                 }
72                 ret
73             }
74         }
75     }
76 
suggest_boxing_for_return_impl_trait( &self, err: &mut Diagnostic, return_sp: Span, arm_spans: impl Iterator<Item = Span>, )77     pub(super) fn suggest_boxing_for_return_impl_trait(
78         &self,
79         err: &mut Diagnostic,
80         return_sp: Span,
81         arm_spans: impl Iterator<Item = Span>,
82     ) {
83         let sugg = SuggestBoxingForReturnImplTrait::ChangeReturnType {
84             start_sp: return_sp.with_hi(return_sp.lo() + BytePos(4)),
85             end_sp: return_sp.shrink_to_hi(),
86         };
87         err.subdiagnostic(sugg);
88 
89         let mut starts = Vec::new();
90         let mut ends = Vec::new();
91         for span in arm_spans {
92             starts.push(span.shrink_to_lo());
93             ends.push(span.shrink_to_hi());
94         }
95         let sugg = SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends };
96         err.subdiagnostic(sugg);
97     }
98 
suggest_tuple_pattern( &self, cause: &ObligationCause<'tcx>, exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, diag: &mut Diagnostic, )99     pub(super) fn suggest_tuple_pattern(
100         &self,
101         cause: &ObligationCause<'tcx>,
102         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
103         diag: &mut Diagnostic,
104     ) {
105         // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
106         // some modifications due to that being in typeck and this being in infer.
107         if let ObligationCauseCode::Pattern { .. } = cause.code() {
108             if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
109                 let compatible_variants: Vec<_> = expected_adt
110                     .variants()
111                     .iter()
112                     .filter(|variant| {
113                         variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
114                     })
115                     .filter_map(|variant| {
116                         let sole_field = &variant.single_field();
117                         let sole_field_ty = sole_field.ty(self.tcx, substs);
118                         if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
119                             let variant_path =
120                                 with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
121                             // FIXME #56861: DRYer prelude filtering
122                             if let Some(path) = variant_path.strip_prefix("std::prelude::") {
123                                 if let Some((_, path)) = path.split_once("::") {
124                                     return Some(path.to_string());
125                                 }
126                             }
127                             Some(variant_path)
128                         } else {
129                             None
130                         }
131                     })
132                     .collect();
133                 match &compatible_variants[..] {
134                     [] => {}
135                     [variant] => {
136                         let sugg = SuggestTuplePatternOne {
137                             variant: variant.to_owned(),
138                             span_low: cause.span.shrink_to_lo(),
139                             span_high: cause.span.shrink_to_hi(),
140                         };
141                         diag.subdiagnostic(sugg);
142                     }
143                     _ => {
144                         // More than one matching variant.
145                         let sugg = SuggestTuplePatternMany {
146                             path: self.tcx.def_path_str(expected_adt.did()),
147                             cause_span: cause.span,
148                             compatible_variants,
149                         };
150                         diag.subdiagnostic(sugg);
151                     }
152                 }
153             }
154         }
155     }
156 
157     /// A possible error is to forget to add `.await` when using futures:
158     ///
159     /// ```compile_fail,E0308
160     /// async fn make_u32() -> u32 {
161     ///     22
162     /// }
163     ///
164     /// fn take_u32(x: u32) {}
165     ///
166     /// async fn foo() {
167     ///     let x = make_u32();
168     ///     take_u32(x);
169     /// }
170     /// ```
171     ///
172     /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
173     /// expected type. If this is the case, and we are inside of an async body, it suggests adding
174     /// `.await` to the tail of the expression.
suggest_await_on_expect_found( &self, cause: &ObligationCause<'tcx>, exp_span: Span, exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, diag: &mut Diagnostic, )175     pub(super) fn suggest_await_on_expect_found(
176         &self,
177         cause: &ObligationCause<'tcx>,
178         exp_span: Span,
179         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
180         diag: &mut Diagnostic,
181     ) {
182         debug!(
183             "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
184             exp_span, exp_found.expected, exp_found.found,
185         );
186 
187         if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() {
188             return;
189         }
190 
191         let subdiag = match (
192             self.get_impl_future_output_ty(exp_found.expected),
193             self.get_impl_future_output_ty(exp_found.found),
194         ) {
195             (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
196                 .code()
197             {
198                 ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
199                     let then_span = self.find_block_span_from_hir_id(*then_id);
200                     Some(ConsiderAddingAwait::BothFuturesSugg {
201                         first: then_span.shrink_to_hi(),
202                         second: exp_span.shrink_to_hi(),
203                     })
204                 }
205                 ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
206                     prior_arms,
207                     ..
208                 }) => {
209                     if let [.., arm_span] = &prior_arms[..] {
210                         Some(ConsiderAddingAwait::BothFuturesSugg {
211                             first: arm_span.shrink_to_hi(),
212                             second: exp_span.shrink_to_hi(),
213                         })
214                     } else {
215                         Some(ConsiderAddingAwait::BothFuturesHelp)
216                     }
217                 }
218                 _ => Some(ConsiderAddingAwait::BothFuturesHelp),
219             },
220             (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
221                 // FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic
222                 diag.subdiagnostic(ConsiderAddingAwait::FutureSugg {
223                     span: exp_span.shrink_to_hi(),
224                 });
225                 Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span })
226             }
227             (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
228             {
229                 ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
230                     Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
231                 }
232                 ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
233                     let then_span = self.find_block_span_from_hir_id(*then_id);
234                     Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
235                 }
236                 ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
237                     ref prior_arms,
238                     ..
239                 }) => Some({
240                     ConsiderAddingAwait::FutureSuggMultiple {
241                         spans: prior_arms.iter().map(|arm| arm.shrink_to_hi()).collect(),
242                     }
243                 }),
244                 _ => None,
245             },
246             _ => None,
247         };
248         if let Some(subdiag) = subdiag {
249             diag.subdiagnostic(subdiag);
250         }
251     }
252 
suggest_accessing_field_where_appropriate( &self, cause: &ObligationCause<'tcx>, exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, diag: &mut Diagnostic, )253     pub(super) fn suggest_accessing_field_where_appropriate(
254         &self,
255         cause: &ObligationCause<'tcx>,
256         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
257         diag: &mut Diagnostic,
258     ) {
259         debug!(
260             "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
261             cause, exp_found
262         );
263         if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
264             if expected_def.is_enum() {
265                 return;
266             }
267 
268             if let Some((name, ty)) = expected_def
269                 .non_enum_variant()
270                 .fields
271                 .iter()
272                 .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
273                 .map(|field| (field.name, field.ty(self.tcx, expected_substs)))
274                 .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
275             {
276                 if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
277                     if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
278                         let suggestion = if expected_def.is_struct() {
279                             SuggestAccessingField::Safe { span, snippet, name, ty }
280                         } else if expected_def.is_union() {
281                             SuggestAccessingField::Unsafe { span, snippet, name, ty }
282                         } else {
283                             return;
284                         };
285                         diag.subdiagnostic(suggestion);
286                     }
287                 }
288             }
289         }
290     }
291 
suggest_function_pointers( &self, cause: &ObligationCause<'tcx>, span: Span, exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, diag: &mut Diagnostic, )292     pub(super) fn suggest_function_pointers(
293         &self,
294         cause: &ObligationCause<'tcx>,
295         span: Span,
296         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
297         diag: &mut Diagnostic,
298     ) {
299         debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
300         let ty::error::ExpectedFound { expected, found } = exp_found;
301         let expected_inner = expected.peel_refs();
302         let found_inner = found.peel_refs();
303         if !expected_inner.is_fn() || !found_inner.is_fn() {
304             return;
305         }
306         match (&expected_inner.kind(), &found_inner.kind()) {
307             (ty::FnPtr(sig), ty::FnDef(did, substs)) => {
308                 let expected_sig = &(self.normalize_fn_sig)(*sig);
309                 let found_sig =
310                     &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).subst(self.tcx, substs));
311 
312                 let fn_name = self.tcx.def_path_str_with_substs(*did, substs);
313 
314                 if !self.same_type_modulo_infer(*found_sig, *expected_sig)
315                     || !sig.is_suggestable(self.tcx, true)
316                     || self.tcx.is_intrinsic(*did)
317                 {
318                     return;
319                 }
320 
321                 let sugg = match (expected.is_ref(), found.is_ref()) {
322                     (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
323                     (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
324                     (true, true) => {
325                         diag.subdiagnostic(FnItemsAreDistinct);
326                         FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig }
327                     }
328                     (false, false) => {
329                         diag.subdiagnostic(FnItemsAreDistinct);
330                         FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig }
331                     }
332                 };
333                 diag.subdiagnostic(sugg);
334             }
335             (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
336                 let expected_sig =
337                     &(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).subst(self.tcx, substs1));
338                 let found_sig =
339                     &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).subst(self.tcx, substs2));
340 
341                 if self.same_type_modulo_infer(*expected_sig, *found_sig) {
342                     diag.subdiagnostic(FnUniqTypes);
343                 }
344 
345                 if !self.same_type_modulo_infer(*found_sig, *expected_sig)
346                     || !found_sig.is_suggestable(self.tcx, true)
347                     || !expected_sig.is_suggestable(self.tcx, true)
348                     || self.tcx.is_intrinsic(*did1)
349                     || self.tcx.is_intrinsic(*did2)
350                 {
351                     return;
352                 }
353 
354                 let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2);
355                 let sug = if found.is_ref() {
356                     FunctionPointerSuggestion::CastBothRef {
357                         span,
358                         fn_name,
359                         found_sig: *found_sig,
360                         expected_sig: *expected_sig,
361                     }
362                 } else {
363                     FunctionPointerSuggestion::CastBoth {
364                         span,
365                         fn_name,
366                         found_sig: *found_sig,
367                         expected_sig: *expected_sig,
368                     }
369                 };
370 
371                 diag.subdiagnostic(sug);
372             }
373             (ty::FnDef(did, substs), ty::FnPtr(sig)) => {
374                 let expected_sig =
375                     &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).subst(self.tcx, substs));
376                 let found_sig = &(self.normalize_fn_sig)(*sig);
377 
378                 if !self.same_type_modulo_infer(*found_sig, *expected_sig) {
379                     return;
380                 }
381 
382                 let fn_name = self.tcx.def_path_str_with_substs(*did, substs);
383 
384                 let casting = if expected.is_ref() {
385                     format!("&({fn_name} as {found_sig})")
386                 } else {
387                     format!("{fn_name} as {found_sig}")
388                 };
389 
390                 diag.subdiagnostic(FnConsiderCasting { casting });
391             }
392             _ => {
393                 return;
394             }
395         };
396     }
397 
should_suggest_as_ref_kind( &self, expected: Ty<'tcx>, found: Ty<'tcx>, ) -> Option<SuggestAsRefKind>398     pub fn should_suggest_as_ref_kind(
399         &self,
400         expected: Ty<'tcx>,
401         found: Ty<'tcx>,
402     ) -> Option<SuggestAsRefKind> {
403         if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
404             (expected.kind(), found.kind())
405         {
406             if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
407                 if exp_def == &found_def {
408                     let have_as_ref = &[
409                         (sym::Option, SuggestAsRefKind::Option),
410                         (sym::Result, SuggestAsRefKind::Result),
411                     ];
412                     if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
413                         self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
414                     }) {
415                         let mut show_suggestion = true;
416                         for (exp_ty, found_ty) in
417                             std::iter::zip(exp_substs.types(), found_substs.types())
418                         {
419                             match *exp_ty.kind() {
420                                 ty::Ref(_, exp_ty, _) => {
421                                     match (exp_ty.kind(), found_ty.kind()) {
422                                         (_, ty::Param(_))
423                                         | (_, ty::Infer(_))
424                                         | (ty::Param(_), _)
425                                         | (ty::Infer(_), _) => {}
426                                         _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
427                                         _ => show_suggestion = false,
428                                     };
429                                 }
430                                 ty::Param(_) | ty::Infer(_) => {}
431                                 _ => show_suggestion = false,
432                             }
433                         }
434                         if show_suggestion {
435                             return Some(*msg);
436                         }
437                     }
438                 }
439             }
440         }
441         None
442     }
443 
444     // FIXME: Remove once `rustc_hir_typeck` is migrated to diagnostic structs
should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str>445     pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
446         match self.should_suggest_as_ref_kind(expected, found) {
447             Some(SuggestAsRefKind::Option) => Some(
448                 "you can convert from `&Option<T>` to `Option<&T>` using \
449             `.as_ref()`",
450             ),
451             Some(SuggestAsRefKind::Result) => Some(
452                 "you can convert from `&Result<T, E>` to \
453             `Result<&T, &E>` using `.as_ref()`",
454             ),
455             None => None,
456         }
457     }
458     /// Try to find code with pattern `if Some(..) = expr`
459     /// use a `visitor` to mark the `if` which its span contains given error span,
460     /// and then try to find a assignment in the `cond` part, which span is equal with error span
suggest_let_for_letchains( &self, cause: &ObligationCause<'_>, span: Span, ) -> Option<TypeErrorAdditionalDiags>461     pub(super) fn suggest_let_for_letchains(
462         &self,
463         cause: &ObligationCause<'_>,
464         span: Span,
465     ) -> Option<TypeErrorAdditionalDiags> {
466         let hir = self.tcx.hir();
467         if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) &&
468             let hir::Node::Item(hir::Item {
469                     kind: hir::ItemKind::Fn(_sig, _, body_id), ..
470                 }) = node {
471         let body = hir.body(*body_id);
472 
473         /// Find the if expression with given span
474         struct IfVisitor {
475             pub result: bool,
476             pub found_if: bool,
477             pub err_span: Span,
478         }
479 
480         impl<'v> Visitor<'v> for IfVisitor {
481             fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
482                 if self.result { return; }
483                 match ex.kind {
484                     hir::ExprKind::If(cond, _, _) => {
485                         self.found_if = true;
486                         walk_expr(self, cond);
487                         self.found_if = false;
488                     }
489                     _ => walk_expr(self, ex),
490                 }
491             }
492 
493             fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
494                 if let hir::StmtKind::Local(hir::Local {
495                         span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
496                     }) = &ex.kind
497                     && self.found_if
498                     && span.eq(&self.err_span) {
499                         self.result = true;
500                 }
501                 walk_stmt(self, ex);
502             }
503 
504             fn visit_body(&mut self, body: &'v hir::Body<'v>) {
505                 hir::intravisit::walk_body(self, body);
506             }
507         }
508 
509         let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
510         visitor.visit_body(&body);
511         if visitor.result {
512                 return Some(TypeErrorAdditionalDiags::AddLetForLetChains{span: span.shrink_to_lo()});
513             }
514         }
515         None
516     }
517 
518     /// For "one type is more general than the other" errors on closures, suggest changing the lifetime
519     /// of the parameters to accept all lifetimes.
suggest_for_all_lifetime_closure( &self, span: Span, hir: hir::Node<'_>, exp_found: &ty::error::ExpectedFound<ty::PolyTraitRef<'tcx>>, diag: &mut Diagnostic, )520     pub(super) fn suggest_for_all_lifetime_closure(
521         &self,
522         span: Span,
523         hir: hir::Node<'_>,
524         exp_found: &ty::error::ExpectedFound<ty::PolyTraitRef<'tcx>>,
525         diag: &mut Diagnostic,
526     ) {
527         // 0. Extract fn_decl from hir
528         let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }), .. }) = hir else { return; };
529         let hir::Body { params, .. } = self.tcx.hir().body(*body);
530 
531         // 1. Get the substs of the closure.
532         // 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
533         let Some(expected) = exp_found.expected.skip_binder().substs.get(1) else { return; };
534         let Some(found) = exp_found.found.skip_binder().substs.get(1) else { return; };
535         let expected = expected.unpack();
536         let found = found.unpack();
537         // 3. Extract the tuple type from Fn trait and suggest the change.
538         if let GenericArgKind::Type(expected) = expected &&
539             let GenericArgKind::Type(found) = found &&
540             let ty::Tuple(expected) = expected.kind() &&
541             let ty::Tuple(found)= found.kind() &&
542             expected.len() == found.len() {
543             let mut suggestion = "|".to_string();
544             let mut is_first = true;
545             let mut has_suggestion = false;
546 
547             for (((expected, found), param_hir), arg_hir) in expected.iter()
548                 .zip(found.iter())
549                 .zip(params.iter())
550                 .zip(fn_decl.inputs.iter()) {
551                 if is_first {
552                     is_first = false;
553                 } else {
554                     suggestion += ", ";
555                 }
556 
557                 if let ty::Ref(expected_region, _, _) = expected.kind() &&
558                     let ty::Ref(found_region, _, _) = found.kind() &&
559                     expected_region.is_late_bound() &&
560                     !found_region.is_late_bound() &&
561                     let hir::TyKind::Infer = arg_hir.kind {
562                     // If the expected region is late bound, the found region is not, and users are asking compiler
563                     // to infer the type, we can suggest adding `: &_`.
564                     if param_hir.pat.span == param_hir.ty_span {
565                         // for `|x|`, `|_|`, `|x: impl Foo|`
566                         let Ok(pat) = self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span) else { return; };
567                         suggestion += &format!("{}: &_", pat);
568                     } else {
569                         // for `|x: ty|`, `|_: ty|`
570                         let Ok(pat) = self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span) else { return; };
571                         let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span) else { return; };
572                         suggestion += &format!("{}: &{}", pat, ty);
573                     }
574                     has_suggestion = true;
575                 } else {
576                     let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else { return; };
577                     // Otherwise, keep it as-is.
578                     suggestion += &arg;
579                 }
580             }
581             suggestion += "|";
582 
583             if has_suggestion {
584                 diag.span_suggestion_verbose(
585                     span,
586                     "consider specifying the type of the closure parameters",
587                     suggestion,
588                     Applicability::MaybeIncorrect,
589                 );
590             }
591         }
592     }
593 }
594 
595 impl<'tcx> TypeErrCtxt<'_, 'tcx> {
596     /// Be helpful when the user wrote `{... expr; }` and taking the `;` off
597     /// is enough to fix the error.
could_remove_semicolon( &self, blk: &'tcx hir::Block<'tcx>, expected_ty: Ty<'tcx>, ) -> Option<(Span, StatementAsExpression)>598     pub fn could_remove_semicolon(
599         &self,
600         blk: &'tcx hir::Block<'tcx>,
601         expected_ty: Ty<'tcx>,
602     ) -> Option<(Span, StatementAsExpression)> {
603         let blk = blk.innermost_block();
604         // Do not suggest if we have a tail expr.
605         if blk.expr.is_some() {
606             return None;
607         }
608         let last_stmt = blk.stmts.last()?;
609         let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
610             return None;
611         };
612         let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?;
613         let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
614             _ if last_expr_ty.references_error() => return None,
615             _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
616                 StatementAsExpression::CorrectType
617             }
618             (
619                 ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, .. }),
620                 ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, .. }),
621             ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
622             (
623                 ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, substs: last_bounds, .. }),
624                 ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, substs: exp_bounds, .. }),
625             ) => {
626                 debug!(
627                     "both opaque, likely future {:?} {:?} {:?} {:?}",
628                     last_def_id, last_bounds, exp_def_id, exp_bounds
629                 );
630 
631                 let last_local_id = last_def_id.as_local()?;
632                 let exp_local_id = exp_def_id.as_local()?;
633 
634                 match (
635                     &self.tcx.hir().expect_item(last_local_id).kind,
636                     &self.tcx.hir().expect_item(exp_local_id).kind,
637                 ) {
638                     (
639                         hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
640                         hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
641                     ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
642                         match (left, right) {
643                             (
644                                 hir::GenericBound::Trait(tl, ml),
645                                 hir::GenericBound::Trait(tr, mr),
646                             ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
647                                 && ml == mr =>
648                             {
649                                 true
650                             }
651                             (
652                                 hir::GenericBound::LangItemTrait(langl, _, _, argsl),
653                                 hir::GenericBound::LangItemTrait(langr, _, _, argsr),
654                             ) if langl == langr => {
655                                 // FIXME: consider the bounds!
656                                 debug!("{:?} {:?}", argsl, argsr);
657                                 true
658                             }
659                             _ => false,
660                         }
661                     }) =>
662                     {
663                         StatementAsExpression::NeedsBoxing
664                     }
665                     _ => StatementAsExpression::CorrectType,
666                 }
667             }
668             _ => return None,
669         };
670         let span = if last_stmt.span.from_expansion() {
671             let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
672             self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
673         } else {
674             last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
675         };
676         Some((span, needs_box))
677     }
678 
679     /// Suggest returning a local binding with a compatible type if the block
680     /// has no return expression.
consider_returning_binding_diag( &self, blk: &'tcx hir::Block<'tcx>, expected_ty: Ty<'tcx>, ) -> Option<SuggestRemoveSemiOrReturnBinding>681     pub fn consider_returning_binding_diag(
682         &self,
683         blk: &'tcx hir::Block<'tcx>,
684         expected_ty: Ty<'tcx>,
685     ) -> Option<SuggestRemoveSemiOrReturnBinding> {
686         let blk = blk.innermost_block();
687         // Do not suggest if we have a tail expr.
688         if blk.expr.is_some() {
689             return None;
690         }
691         let mut shadowed = FxIndexSet::default();
692         let mut candidate_idents = vec![];
693         let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
694             if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
695                 && let Some(pat_ty) = self
696                     .typeck_results
697                     .as_ref()
698                     .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
699             {
700                 let pat_ty = self.resolve_vars_if_possible(pat_ty);
701                 if self.same_type_modulo_infer(pat_ty, expected_ty)
702                     && !(pat_ty, expected_ty).references_error()
703                     && shadowed.insert(ident.name)
704                 {
705                     candidate_idents.push((*ident, pat_ty));
706                 }
707             }
708             true
709         };
710 
711         let hir = self.tcx.hir();
712         for stmt in blk.stmts.iter().rev() {
713             let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
714             local.pat.walk(&mut find_compatible_candidates);
715         }
716         match hir.find_parent(blk.hir_id) {
717             Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => match hir.find_parent(*hir_id) {
718                 Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
719                     pat.walk(&mut find_compatible_candidates);
720                 }
721                 Some(
722                     hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
723                     | hir::Node::ImplItem(hir::ImplItem {
724                         kind: hir::ImplItemKind::Fn(_, body), ..
725                     })
726                     | hir::Node::TraitItem(hir::TraitItem {
727                         kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
728                         ..
729                     })
730                     | hir::Node::Expr(hir::Expr {
731                         kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
732                         ..
733                     }),
734                 ) => {
735                     for param in hir.body(*body).params {
736                         param.pat.walk(&mut find_compatible_candidates);
737                     }
738                 }
739                 Some(hir::Node::Expr(hir::Expr {
740                     kind:
741                         hir::ExprKind::If(
742                             hir::Expr { kind: hir::ExprKind::Let(let_), .. },
743                             then_block,
744                             _,
745                         ),
746                     ..
747                 })) if then_block.hir_id == *hir_id => {
748                     let_.pat.walk(&mut find_compatible_candidates);
749                 }
750                 _ => {}
751             },
752             _ => {}
753         }
754 
755         match &candidate_idents[..] {
756             [(ident, _ty)] => {
757                 let sm = self.tcx.sess.source_map();
758                 let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
759                     let stmt_span = sm.stmt_span(stmt.span, blk.span);
760                     let sugg = if sm.is_multiline(blk.span)
761                         && let Some(spacing) = sm.indentation_before(stmt_span)
762                     {
763                         format!("\n{spacing}{ident}")
764                     } else {
765                         format!(" {ident}")
766                     };
767                     (stmt_span.shrink_to_hi(), sugg)
768                 } else {
769                     let sugg = if sm.is_multiline(blk.span)
770                         && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
771                     {
772                         format!("\n{spacing}    {ident}\n{spacing}")
773                     } else {
774                         format!(" {ident} ")
775                     };
776                     let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
777                     (
778                         sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
779                         sugg,
780                     )
781                 };
782                 Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
783             }
784             values if (1..3).contains(&values.len()) => {
785                 let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
786                 Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
787             }
788             _ => None,
789         }
790     }
791 
consider_returning_binding( &self, blk: &'tcx hir::Block<'tcx>, expected_ty: Ty<'tcx>, err: &mut Diagnostic, ) -> bool792     pub fn consider_returning_binding(
793         &self,
794         blk: &'tcx hir::Block<'tcx>,
795         expected_ty: Ty<'tcx>,
796         err: &mut Diagnostic,
797     ) -> bool {
798         let diag = self.consider_returning_binding_diag(blk, expected_ty);
799         match diag {
800             Some(diag) => {
801                 err.subdiagnostic(diag);
802                 true
803             }
804             None => false,
805         }
806     }
807 }
808