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