1 use super::FnCtxt; 2 3 use crate::errors::{ 4 AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod, 5 }; 6 use crate::fluent_generated as fluent; 7 use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; 8 use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; 9 use rustc_errors::{Applicability, Diagnostic, MultiSpan}; 10 use rustc_hir as hir; 11 use rustc_hir::def::{CtorKind, CtorOf, DefKind}; 12 use rustc_hir::lang_items::LangItem; 13 use rustc_hir::{ 14 AsyncGeneratorKind, Expr, ExprKind, GeneratorKind, GenericBound, HirId, Node, Path, QPath, 15 Stmt, StmtKind, TyKind, WherePredicate, 16 }; 17 use rustc_hir_analysis::astconv::AstConv; 18 use rustc_infer::traits::{self, StatementAsExpression}; 19 use rustc_middle::lint::in_external_macro; 20 use rustc_middle::ty::print::with_no_trimmed_paths; 21 use rustc_middle::ty::{ 22 self, suggest_constraining_type_params, Binder, IsSuggestable, ToPredicate, Ty, 23 TypeVisitableExt, 24 }; 25 use rustc_session::errors::ExprParenthesesNeeded; 26 use rustc_span::source_map::Spanned; 27 use rustc_span::symbol::{sym, Ident}; 28 use rustc_span::{Span, Symbol}; 29 use rustc_trait_selection::infer::InferCtxtExt; 30 use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; 31 use rustc_trait_selection::traits::error_reporting::DefIdOrName; 32 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; 33 34 impl<'a, 'tcx> FnCtxt<'a, 'tcx> { body_fn_sig(&self) -> Option<ty::FnSig<'tcx>>35 pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> { 36 self.typeck_results 37 .borrow() 38 .liberated_fn_sigs() 39 .get(self.tcx.hir().local_def_id_to_hir_id(self.body_id)) 40 .copied() 41 } 42 suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic)43 pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { 44 // This suggestion is incorrect for 45 // fn foo() -> bool { match () { () => true } || match () { () => true } } 46 err.span_suggestion_short( 47 span.shrink_to_hi(), 48 "consider using a semicolon here", 49 ";", 50 Applicability::MaybeIncorrect, 51 ); 52 } 53 54 /// On implicit return expressions with mismatched types, provides the following suggestions: 55 /// 56 /// - Points out the method's return type as the reason for the expected type. 57 /// - Possible missing semicolon. 58 /// - Possible missing return type if the return type is the default, and not `fn main()`. suggest_mismatched_types_on_tail( &self, err: &mut Diagnostic, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, blk_id: hir::HirId, ) -> bool59 pub fn suggest_mismatched_types_on_tail( 60 &self, 61 err: &mut Diagnostic, 62 expr: &'tcx hir::Expr<'tcx>, 63 expected: Ty<'tcx>, 64 found: Ty<'tcx>, 65 blk_id: hir::HirId, 66 ) -> bool { 67 let expr = expr.peel_drop_temps(); 68 self.suggest_missing_semicolon(err, expr, expected, false); 69 let mut pointing_at_return_type = false; 70 if let Some((fn_id, fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { 71 pointing_at_return_type = self.suggest_missing_return_type( 72 err, 73 &fn_decl, 74 expected, 75 found, 76 can_suggest, 77 fn_id, 78 ); 79 self.suggest_missing_break_or_return_expr( 80 err, expr, &fn_decl, expected, found, blk_id, fn_id, 81 ); 82 } 83 pointing_at_return_type 84 } 85 86 /// When encountering an fn-like type, try accessing the output of the type 87 /// and suggesting calling it if it satisfies a predicate (i.e. if the 88 /// output has a method or a field): 89 /// ```compile_fail,E0308 90 /// fn foo(x: usize) -> usize { x } 91 /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` 92 /// ``` suggest_fn_call( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, found: Ty<'tcx>, can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, ) -> bool93 pub(crate) fn suggest_fn_call( 94 &self, 95 err: &mut Diagnostic, 96 expr: &hir::Expr<'_>, 97 found: Ty<'tcx>, 98 can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, 99 ) -> bool { 100 let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(found) 101 else { return false; }; 102 if can_satisfy(output) { 103 let (sugg_call, mut applicability) = match inputs.len() { 104 0 => ("".to_string(), Applicability::MachineApplicable), 105 1..=4 => ( 106 inputs 107 .iter() 108 .map(|ty| { 109 if ty.is_suggestable(self.tcx, false) { 110 format!("/* {ty} */") 111 } else { 112 "/* value */".to_string() 113 } 114 }) 115 .collect::<Vec<_>>() 116 .join(", "), 117 Applicability::HasPlaceholders, 118 ), 119 _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), 120 }; 121 122 let msg = match def_id_or_name { 123 DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { 124 DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(), 125 DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(), 126 kind => format!("call this {}", self.tcx.def_kind_descr(kind, def_id)), 127 }, 128 DefIdOrName::Name(name) => format!("call this {name}"), 129 }; 130 131 let sugg = match expr.kind { 132 hir::ExprKind::Call(..) 133 | hir::ExprKind::Path(..) 134 | hir::ExprKind::Index(..) 135 | hir::ExprKind::Lit(..) => { 136 vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))] 137 } 138 hir::ExprKind::Closure { .. } => { 139 // Might be `{ expr } || { bool }` 140 applicability = Applicability::MaybeIncorrect; 141 vec![ 142 (expr.span.shrink_to_lo(), "(".to_string()), 143 (expr.span.shrink_to_hi(), format!(")({sugg_call})")), 144 ] 145 } 146 _ => { 147 vec![ 148 (expr.span.shrink_to_lo(), "(".to_string()), 149 (expr.span.shrink_to_hi(), format!(")({sugg_call})")), 150 ] 151 } 152 }; 153 154 err.multipart_suggestion_verbose( 155 format!("use parentheses to {msg}"), 156 sugg, 157 applicability, 158 ); 159 return true; 160 } 161 false 162 } 163 164 /// Extracts information about a callable type for diagnostics. This is a 165 /// heuristic -- it doesn't necessarily mean that a type is always callable, 166 /// because the callable type must also be well-formed to be called. extract_callable_info( &self, ty: Ty<'tcx>, ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)>167 pub(in super::super) fn extract_callable_info( 168 &self, 169 ty: Ty<'tcx>, 170 ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> { 171 self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty) 172 } 173 suggest_two_fn_call( &self, err: &mut Diagnostic, lhs_expr: &'tcx hir::Expr<'tcx>, lhs_ty: Ty<'tcx>, rhs_expr: &'tcx hir::Expr<'tcx>, rhs_ty: Ty<'tcx>, can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool, ) -> bool174 pub fn suggest_two_fn_call( 175 &self, 176 err: &mut Diagnostic, 177 lhs_expr: &'tcx hir::Expr<'tcx>, 178 lhs_ty: Ty<'tcx>, 179 rhs_expr: &'tcx hir::Expr<'tcx>, 180 rhs_ty: Ty<'tcx>, 181 can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool, 182 ) -> bool { 183 let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty) 184 else { return false; }; 185 let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_ty) 186 else { return false; }; 187 188 if can_satisfy(lhs_output_ty, rhs_output_ty) { 189 let mut sugg = vec![]; 190 let mut applicability = Applicability::MachineApplicable; 191 192 for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] { 193 let (sugg_call, this_applicability) = match inputs.len() { 194 0 => ("".to_string(), Applicability::MachineApplicable), 195 1..=4 => ( 196 inputs 197 .iter() 198 .map(|ty| { 199 if ty.is_suggestable(self.tcx, false) { 200 format!("/* {ty} */") 201 } else { 202 "/* value */".to_string() 203 } 204 }) 205 .collect::<Vec<_>>() 206 .join(", "), 207 Applicability::HasPlaceholders, 208 ), 209 _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), 210 }; 211 212 applicability = applicability.max(this_applicability); 213 214 match expr.kind { 215 hir::ExprKind::Call(..) 216 | hir::ExprKind::Path(..) 217 | hir::ExprKind::Index(..) 218 | hir::ExprKind::Lit(..) => { 219 sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]); 220 } 221 hir::ExprKind::Closure { .. } => { 222 // Might be `{ expr } || { bool }` 223 applicability = Applicability::MaybeIncorrect; 224 sugg.extend([ 225 (expr.span.shrink_to_lo(), "(".to_string()), 226 (expr.span.shrink_to_hi(), format!(")({sugg_call})")), 227 ]); 228 } 229 _ => { 230 sugg.extend([ 231 (expr.span.shrink_to_lo(), "(".to_string()), 232 (expr.span.shrink_to_hi(), format!(")({sugg_call})")), 233 ]); 234 } 235 } 236 } 237 238 err.multipart_suggestion_verbose("use parentheses to call these", sugg, applicability); 239 240 true 241 } else { 242 false 243 } 244 } 245 suggest_remove_last_method_call( &self, err: &mut Diagnostic, expr: &hir::Expr<'tcx>, expected: Ty<'tcx>, ) -> bool246 pub fn suggest_remove_last_method_call( 247 &self, 248 err: &mut Diagnostic, 249 expr: &hir::Expr<'tcx>, 250 expected: Ty<'tcx>, 251 ) -> bool { 252 if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) = expr.kind && 253 let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr) && 254 self.can_coerce(recv_ty, expected) { 255 let span = if let Some(recv_span) = recv_expr.span.find_ancestor_inside(expr.span) { 256 expr.span.with_lo(recv_span.hi()) 257 } else { 258 expr.span.with_lo(method.span.lo() - rustc_span::BytePos(1)) 259 }; 260 err.span_suggestion_verbose( 261 span, 262 "try removing the method call", 263 "", 264 Applicability::MachineApplicable, 265 ); 266 return true; 267 } 268 false 269 } 270 suggest_deref_ref_or_into( &self, err: &mut Diagnostic, expr: &hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, ) -> bool271 pub fn suggest_deref_ref_or_into( 272 &self, 273 err: &mut Diagnostic, 274 expr: &hir::Expr<'tcx>, 275 expected: Ty<'tcx>, 276 found: Ty<'tcx>, 277 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, 278 ) -> bool { 279 let expr = expr.peel_blocks(); 280 let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); 281 282 if let Some((suggestion, msg, applicability, verbose, annotation)) = 283 self.suggest_deref_or_ref(expr, found, expected) 284 { 285 if verbose { 286 err.multipart_suggestion_verbose(msg, suggestion, applicability); 287 } else { 288 err.multipart_suggestion(msg, suggestion, applicability); 289 } 290 if annotation { 291 let suggest_annotation = match expr.peel_drop_temps().kind { 292 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(), 293 _ => return true, 294 }; 295 let mut tuple_indexes = Vec::new(); 296 let mut expr_id = expr.hir_id; 297 for (parent_id, node) in self.tcx.hir().parent_iter(expr.hir_id) { 298 match node { 299 Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => { 300 tuple_indexes.push( 301 subs.iter() 302 .enumerate() 303 .find(|(_, sub_expr)| sub_expr.hir_id == expr_id) 304 .unwrap() 305 .0, 306 ); 307 expr_id = parent_id; 308 } 309 Node::Local(local) => { 310 if let Some(mut ty) = local.ty { 311 while let Some(index) = tuple_indexes.pop() { 312 match ty.kind { 313 TyKind::Tup(tys) => ty = &tys[index], 314 _ => return true, 315 } 316 } 317 let annotation_span = ty.span; 318 err.span_suggestion( 319 annotation_span.with_hi(annotation_span.lo()), 320 "alternatively, consider changing the type annotation", 321 suggest_annotation, 322 Applicability::MaybeIncorrect, 323 ); 324 } 325 break; 326 } 327 _ => break, 328 } 329 } 330 } 331 return true; 332 } 333 334 if self.suggest_else_fn_with_closure(err, expr, found, expected) { 335 return true; 336 } 337 338 if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) 339 && let ty::FnDef(def_id, ..) = *found.kind() 340 && let Some(sp) = self.tcx.hir().span_if_local(def_id) 341 { 342 let name = self.tcx.item_name(def_id); 343 let kind = self.tcx.def_kind(def_id); 344 if let DefKind::Ctor(of, CtorKind::Fn) = kind { 345 err.span_label(sp, format!("`{name}` defines {} constructor here, which should be called", match of { 346 CtorOf::Struct => "a struct", 347 CtorOf::Variant => "an enum variant", 348 })); 349 } else { 350 let descr = self.tcx.def_kind_descr(kind, def_id); 351 err.span_label(sp, format!("{descr} `{name}` defined here")); 352 } 353 return true; 354 } 355 356 if self.suggest_cast(err, expr, found, expected, expected_ty_expr) { 357 return true; 358 } 359 360 if !methods.is_empty() { 361 let mut suggestions = methods 362 .iter() 363 .filter_map(|conversion_method| { 364 let receiver_method_ident = expr.method_ident(); 365 if let Some(method_ident) = receiver_method_ident 366 && method_ident.name == conversion_method.name 367 { 368 return None // do not suggest code that is already there (#53348) 369 } 370 371 let method_call_list = [sym::to_vec, sym::to_string]; 372 let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind 373 && receiver_method.ident.name == sym::clone 374 && method_call_list.contains(&conversion_method.name) 375 // If receiver is `.clone()` and found type has one of those methods, 376 // we guess that the user wants to convert from a slice type (`&[]` or `&str`) 377 // to an owned type (`Vec` or `String`). These conversions clone internally, 378 // so we remove the user's `clone` call. 379 { 380 vec![( 381 receiver_method.ident.span, 382 conversion_method.name.to_string() 383 )] 384 } else if expr.precedence().order() 385 < ExprPrecedence::MethodCall.order() 386 { 387 vec![ 388 (expr.span.shrink_to_lo(), "(".to_string()), 389 (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), 390 ] 391 } else { 392 vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] 393 }; 394 let struct_pat_shorthand_field = 395 self.maybe_get_struct_pattern_shorthand_field(expr); 396 if let Some(name) = struct_pat_shorthand_field { 397 sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name))); 398 } 399 Some(sugg) 400 }) 401 .peekable(); 402 if suggestions.peek().is_some() { 403 err.multipart_suggestions( 404 "try using a conversion method", 405 suggestions, 406 Applicability::MaybeIncorrect, 407 ); 408 return true; 409 } 410 } 411 412 if let Some((found_ty_inner, expected_ty_inner, error_tys)) = 413 self.deconstruct_option_or_result(found, expected) 414 && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind() 415 { 416 // Suggest removing any stray borrows (unless there's macro shenanigans involved). 417 let inner_expr = expr.peel_borrows(); 418 if !inner_expr.span.eq_ctxt(expr.span) { 419 return false; 420 } 421 let borrow_removal_span = if inner_expr.hir_id == expr.hir_id { 422 None 423 } else { 424 Some(expr.span.shrink_to_lo().until(inner_expr.span)) 425 }; 426 // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for 427 // `as_ref` and `as_deref` compatibility. 428 let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| { 429 self.can_eq(self.param_env, Ty::new_imm_ref(self.tcx,self.tcx.lifetimes.re_erased, found), expected) 430 }); 431 // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`, 432 // but those checks need to be a bit more delicate and the benefit is diminishing. 433 if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref { 434 err.subdiagnostic(SuggestConvertViaMethod { 435 span: expr.span.shrink_to_hi(), 436 sugg: ".as_ref()", 437 expected, 438 found, 439 borrow_removal_span, 440 }); 441 return true; 442 } else if let Some((deref_ty, _)) = 443 self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1) 444 && self.can_eq(self.param_env, deref_ty, peeled) 445 && error_tys_equate_as_ref 446 { 447 err.subdiagnostic(SuggestConvertViaMethod { 448 span: expr.span.shrink_to_hi(), 449 sugg: ".as_deref()", 450 expected, 451 found, 452 borrow_removal_span, 453 }); 454 return true; 455 } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind() 456 && Some(adt.did()) == self.tcx.lang_items().string() 457 && peeled.is_str() 458 // `Result::map`, conversely, does not take ref of the error type. 459 && error_tys.map_or(true, |(found, expected)| { 460 self.can_eq(self.param_env, found, expected) 461 }) 462 { 463 err.span_suggestion_verbose( 464 expr.span.shrink_to_hi(), 465 fluent::hir_typeck_convert_to_str, 466 ".map(|x| x.as_str())", 467 Applicability::MachineApplicable, 468 ); 469 return true; 470 } 471 } 472 473 false 474 } 475 deconstruct_option_or_result( &self, found_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)>476 fn deconstruct_option_or_result( 477 &self, 478 found_ty: Ty<'tcx>, 479 expected_ty: Ty<'tcx>, 480 ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> { 481 let ty::Adt(found_adt, found_substs) = found_ty.peel_refs().kind() else { 482 return None; 483 }; 484 let ty::Adt(expected_adt, expected_substs) = expected_ty.kind() else { 485 return None; 486 }; 487 if self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) 488 && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) 489 { 490 Some((found_substs.type_at(0), expected_substs.type_at(0), None)) 491 } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did()) 492 && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did()) 493 { 494 Some(( 495 found_substs.type_at(0), 496 expected_substs.type_at(0), 497 Some((found_substs.type_at(1), expected_substs.type_at(1))), 498 )) 499 } else { 500 None 501 } 502 } 503 504 /// When encountering the expected boxed value allocated in the stack, suggest allocating it 505 /// in the heap by calling `Box::new()`. suggest_boxing_when_appropriate( &self, err: &mut Diagnostic, span: Span, hir_id: HirId, expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool506 pub(in super::super) fn suggest_boxing_when_appropriate( 507 &self, 508 err: &mut Diagnostic, 509 span: Span, 510 hir_id: HirId, 511 expected: Ty<'tcx>, 512 found: Ty<'tcx>, 513 ) -> bool { 514 // Do not suggest `Box::new` in const context. 515 if self.tcx.hir().is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() { 516 return false; 517 } 518 if self.can_coerce(Ty::new_box(self.tcx, found), expected) { 519 let suggest_boxing = match found.kind() { 520 ty::Tuple(tuple) if tuple.is_empty() => { 521 SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span } 522 } 523 ty::Generator(def_id, ..) 524 if matches!( 525 self.tcx.generator_kind(def_id), 526 Some(GeneratorKind::Async(AsyncGeneratorKind::Closure)) 527 ) => 528 { 529 SuggestBoxing::AsyncBody 530 } 531 _ => SuggestBoxing::Other { start: span.shrink_to_lo(), end: span.shrink_to_hi() }, 532 }; 533 err.subdiagnostic(suggest_boxing); 534 535 true 536 } else { 537 false 538 } 539 } 540 541 /// When encountering a closure that captures variables, where a FnPtr is expected, 542 /// suggest a non-capturing closure suggest_no_capture_closure( &self, err: &mut Diagnostic, expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool543 pub(in super::super) fn suggest_no_capture_closure( 544 &self, 545 err: &mut Diagnostic, 546 expected: Ty<'tcx>, 547 found: Ty<'tcx>, 548 ) -> bool { 549 if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { 550 if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { 551 // Report upto four upvars being captured to reduce the amount error messages 552 // reported back to the user. 553 let spans_and_labels = upvars 554 .iter() 555 .take(4) 556 .map(|(var_hir_id, upvar)| { 557 let var_name = self.tcx.hir().name(*var_hir_id).to_string(); 558 let msg = format!("`{}` captured here", var_name); 559 (upvar.span, msg) 560 }) 561 .collect::<Vec<_>>(); 562 563 let mut multi_span: MultiSpan = 564 spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into(); 565 for (sp, label) in spans_and_labels { 566 multi_span.push_span_label(sp, label); 567 } 568 err.span_note( 569 multi_span, 570 "closures can only be coerced to `fn` types if they do not capture any variables" 571 ); 572 return true; 573 } 574 } 575 false 576 } 577 578 /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. 579 #[instrument(skip(self, err))] suggest_calling_boxed_future_when_appropriate( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool580 pub(in super::super) fn suggest_calling_boxed_future_when_appropriate( 581 &self, 582 err: &mut Diagnostic, 583 expr: &hir::Expr<'_>, 584 expected: Ty<'tcx>, 585 found: Ty<'tcx>, 586 ) -> bool { 587 // Handle #68197. 588 589 if self.tcx.hir().is_inside_const_context(expr.hir_id) { 590 // Do not suggest `Box::new` in const context. 591 return false; 592 } 593 let pin_did = self.tcx.lang_items().pin_type(); 594 // This guards the `unwrap` and `mk_box` below. 595 if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() { 596 return false; 597 } 598 let box_found = Ty::new_box(self.tcx, found); 599 let pin_box_found = Ty::new_lang_item(self.tcx, box_found, LangItem::Pin).unwrap(); 600 let pin_found = Ty::new_lang_item(self.tcx, found, LangItem::Pin).unwrap(); 601 match expected.kind() { 602 ty::Adt(def, _) if Some(def.did()) == pin_did => { 603 if self.can_coerce(pin_box_found, expected) { 604 debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected); 605 match found.kind() { 606 ty::Adt(def, _) if def.is_box() => { 607 err.help("use `Box::pin`"); 608 } 609 _ => { 610 err.multipart_suggestion( 611 "you need to pin and box this expression", 612 vec![ 613 (expr.span.shrink_to_lo(), "Box::pin(".to_string()), 614 (expr.span.shrink_to_hi(), ")".to_string()), 615 ], 616 Applicability::MaybeIncorrect, 617 ); 618 } 619 } 620 true 621 } else if self.can_coerce(pin_found, expected) { 622 match found.kind() { 623 ty::Adt(def, _) if def.is_box() => { 624 err.help("use `Box::pin`"); 625 true 626 } 627 _ => false, 628 } 629 } else { 630 false 631 } 632 } 633 ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => { 634 // Check if the parent expression is a call to Pin::new. If it 635 // is and we were expecting a Box, ergo Pin<Box<expected>>, we 636 // can suggest Box::pin. 637 let parent = self.tcx.hir().parent_id(expr.hir_id); 638 let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else { 639 return false; 640 }; 641 match fn_name.kind { 642 ExprKind::Path(QPath::TypeRelative( 643 hir::Ty { 644 kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })), 645 .. 646 }, 647 method, 648 )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => { 649 err.span_suggestion( 650 fn_name.span, 651 "use `Box::pin` to pin and box this expression", 652 "Box::pin", 653 Applicability::MachineApplicable, 654 ); 655 true 656 } 657 _ => false, 658 } 659 } 660 _ => false, 661 } 662 } 663 664 /// A common error is to forget to add a semicolon at the end of a block, e.g., 665 /// 666 /// ```compile_fail,E0308 667 /// # fn bar_that_returns_u32() -> u32 { 4 } 668 /// fn foo() { 669 /// bar_that_returns_u32() 670 /// } 671 /// ``` 672 /// 673 /// This routine checks if the return expression in a block would make sense on its own as a 674 /// statement and the return type has been left as default or has been specified as `()`. If so, 675 /// it suggests adding a semicolon. 676 /// 677 /// If the expression is the expression of a closure without block (`|| expr`), a 678 /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`. suggest_missing_semicolon( &self, err: &mut Diagnostic, expression: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>, needs_block: bool, )679 pub fn suggest_missing_semicolon( 680 &self, 681 err: &mut Diagnostic, 682 expression: &'tcx hir::Expr<'tcx>, 683 expected: Ty<'tcx>, 684 needs_block: bool, 685 ) { 686 if expected.is_unit() { 687 // `BlockTailExpression` only relevant if the tail expr would be 688 // useful on its own. 689 match expression.kind { 690 ExprKind::Call(..) 691 | ExprKind::MethodCall(..) 692 | ExprKind::Loop(..) 693 | ExprKind::If(..) 694 | ExprKind::Match(..) 695 | ExprKind::Block(..) 696 if expression.can_have_side_effects() 697 // If the expression is from an external macro, then do not suggest 698 // adding a semicolon, because there's nowhere to put it. 699 // See issue #81943. 700 && !in_external_macro(self.tcx.sess, expression.span) => 701 { 702 if needs_block { 703 err.multipart_suggestion( 704 "consider using a semicolon here", 705 vec![ 706 (expression.span.shrink_to_lo(), "{ ".to_owned()), 707 (expression.span.shrink_to_hi(), "; }".to_owned()), 708 ], 709 Applicability::MachineApplicable, 710 ); 711 } else { 712 err.span_suggestion( 713 expression.span.shrink_to_hi(), 714 "consider using a semicolon here", 715 ";", 716 Applicability::MachineApplicable, 717 ); 718 } 719 } 720 _ => (), 721 } 722 } 723 } 724 725 /// A possible error is to forget to add a return type that is needed: 726 /// 727 /// ```compile_fail,E0308 728 /// # fn bar_that_returns_u32() -> u32 { 4 } 729 /// fn foo() { 730 /// bar_that_returns_u32() 731 /// } 732 /// ``` 733 /// 734 /// This routine checks if the return type is left as default, the method is not part of an 735 /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return 736 /// type. 737 #[instrument(level = "trace", skip(self, err))] suggest_missing_return_type( &self, err: &mut Diagnostic, fn_decl: &hir::FnDecl<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, can_suggest: bool, fn_id: hir::HirId, ) -> bool738 pub(in super::super) fn suggest_missing_return_type( 739 &self, 740 err: &mut Diagnostic, 741 fn_decl: &hir::FnDecl<'_>, 742 expected: Ty<'tcx>, 743 found: Ty<'tcx>, 744 can_suggest: bool, 745 fn_id: hir::HirId, 746 ) -> bool { 747 let found = 748 self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); 749 // Only suggest changing the return type for methods that 750 // haven't set a return type at all (and aren't `fn main()` or an impl). 751 match &fn_decl.output { 752 &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => { 753 // `fn main()` must return `()`, do not suggest changing return type 754 err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span }); 755 return true; 756 } 757 &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { 758 if let Some(found) = found.make_suggestable(self.tcx, false) { 759 err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); 760 return true; 761 } else if let ty::Closure(_, substs) = found.kind() 762 // FIXME(compiler-errors): Get better at printing binders... 763 && let closure = substs.as_closure() 764 && closure.sig().is_suggestable(self.tcx, false) 765 { 766 err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() }); 767 return true; 768 } else { 769 // FIXME: if `found` could be `impl Iterator` we should suggest that. 770 err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span }); 771 return true 772 } 773 } 774 hir::FnRetTy::Return(hir_ty) => { 775 let span = hir_ty.span; 776 777 if let hir::TyKind::OpaqueDef(item_id, ..) = hir_ty.kind 778 && let hir::Node::Item(hir::Item { 779 kind: hir::ItemKind::OpaqueTy(op_ty), 780 .. 781 }) = self.tcx.hir().get(item_id.hir_id()) 782 && let [hir::GenericBound::LangItemTrait( 783 hir::LangItem::Future, _, _, generic_args)] = op_ty.bounds 784 && let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args 785 && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(term) } = ty_binding.kind 786 { 787 // Check if async function's return type was omitted. 788 // Don't emit suggestions if the found type is `impl Future<...>`. 789 debug!(?found); 790 if found.is_suggestable(self.tcx, false) { 791 if term.span.is_empty() { 792 err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); 793 return true; 794 } else { 795 err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); 796 } 797 } 798 } 799 800 // Only point to return type if the expected type is the return type, as if they 801 // are not, the expectation must have been caused by something else. 802 debug!("return type {:?}", hir_ty); 803 let ty = self.astconv().ast_ty_to_ty(hir_ty); 804 debug!("return type {:?}", ty); 805 debug!("expected type {:?}", expected); 806 let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into()); 807 let ty = Binder::bind_with_vars(ty, bound_vars); 808 let ty = self.normalize(span, ty); 809 let ty = self.tcx.erase_late_bound_regions(ty); 810 if self.can_coerce(expected, ty) { 811 err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); 812 self.try_suggest_return_impl_trait(err, expected, ty, fn_id); 813 return true; 814 } 815 } 816 _ => {} 817 } 818 false 819 } 820 821 /// check whether the return type is a generic type with a trait bound 822 /// only suggest this if the generic param is not present in the arguments 823 /// if this is true, hint them towards changing the return type to `impl Trait` 824 /// ```compile_fail,E0308 825 /// fn cant_name_it<T: Fn() -> u32>() -> T { 826 /// || 3 827 /// } 828 /// ``` try_suggest_return_impl_trait( &self, err: &mut Diagnostic, expected: Ty<'tcx>, found: Ty<'tcx>, fn_id: hir::HirId, )829 fn try_suggest_return_impl_trait( 830 &self, 831 err: &mut Diagnostic, 832 expected: Ty<'tcx>, 833 found: Ty<'tcx>, 834 fn_id: hir::HirId, 835 ) { 836 // Only apply the suggestion if: 837 // - the return type is a generic parameter 838 // - the generic param is not used as a fn param 839 // - the generic param has at least one bound 840 // - the generic param doesn't appear in any other bounds where it's not the Self type 841 // Suggest: 842 // - Changing the return type to be `impl <all bounds>` 843 844 debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found); 845 846 let ty::Param(expected_ty_as_param) = expected.kind() else { return }; 847 848 let fn_node = self.tcx.hir().find(fn_id); 849 850 let Some(hir::Node::Item(hir::Item { 851 kind: 852 hir::ItemKind::Fn( 853 hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. }, 854 hir::Generics { params, predicates, .. }, 855 _body_id, 856 ), 857 .. 858 })) = fn_node else { return }; 859 860 if params.get(expected_ty_as_param.index as usize).is_none() { 861 return; 862 }; 863 864 // get all where BoundPredicates here, because they are used in two cases below 865 let where_predicates = predicates 866 .iter() 867 .filter_map(|p| match p { 868 WherePredicate::BoundPredicate(hir::WhereBoundPredicate { 869 bounds, 870 bounded_ty, 871 .. 872 }) => { 873 // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below) 874 let ty = self.astconv().ast_ty_to_ty(bounded_ty); 875 Some((ty, bounds)) 876 } 877 _ => None, 878 }) 879 .map(|(ty, bounds)| match ty.kind() { 880 ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)), 881 // check whether there is any predicate that contains our `T`, like `Option<T>: Send` 882 _ => match ty.contains(expected) { 883 true => Err(()), 884 false => Ok(None), 885 }, 886 }) 887 .collect::<Result<Vec<_>, _>>(); 888 889 let Ok(where_predicates) = where_predicates else { return }; 890 891 // now get all predicates in the same types as the where bounds, so we can chain them 892 let predicates_from_where = 893 where_predicates.iter().flatten().flat_map(|bounds| bounds.iter()); 894 895 // extract all bounds from the source code using their spans 896 let all_matching_bounds_strs = predicates_from_where 897 .filter_map(|bound| match bound { 898 GenericBound::Trait(_, _) => { 899 self.tcx.sess.source_map().span_to_snippet(bound.span()).ok() 900 } 901 _ => None, 902 }) 903 .collect::<Vec<String>>(); 904 905 if all_matching_bounds_strs.len() == 0 { 906 return; 907 } 908 909 let all_bounds_str = all_matching_bounds_strs.join(" + "); 910 911 let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| { 912 let ty = self.astconv().ast_ty_to_ty( param); 913 matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param) 914 }); 915 916 if ty_param_used_in_fn_params { 917 return; 918 } 919 920 err.span_suggestion( 921 fn_return.span(), 922 "consider using an impl return type", 923 format!("impl {}", all_bounds_str), 924 Applicability::MaybeIncorrect, 925 ); 926 } 927 suggest_missing_break_or_return_expr( &self, err: &mut Diagnostic, expr: &'tcx hir::Expr<'tcx>, fn_decl: &hir::FnDecl<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, id: hir::HirId, fn_id: hir::HirId, )928 pub(in super::super) fn suggest_missing_break_or_return_expr( 929 &self, 930 err: &mut Diagnostic, 931 expr: &'tcx hir::Expr<'tcx>, 932 fn_decl: &hir::FnDecl<'_>, 933 expected: Ty<'tcx>, 934 found: Ty<'tcx>, 935 id: hir::HirId, 936 fn_id: hir::HirId, 937 ) { 938 if !expected.is_unit() { 939 return; 940 } 941 let found = self.resolve_vars_with_obligations(found); 942 943 let in_loop = self.is_loop(id) 944 || self 945 .tcx 946 .hir() 947 .parent_iter(id) 948 .take_while(|(_, node)| { 949 // look at parents until we find the first body owner 950 node.body_id().is_none() 951 }) 952 .any(|(parent_id, _)| self.is_loop(parent_id)); 953 954 let in_local_statement = self.is_local_statement(id) 955 || self 956 .tcx 957 .hir() 958 .parent_iter(id) 959 .any(|(parent_id, _)| self.is_local_statement(parent_id)); 960 961 if in_loop && in_local_statement { 962 err.multipart_suggestion( 963 "you might have meant to break the loop with this value", 964 vec![ 965 (expr.span.shrink_to_lo(), "break ".to_string()), 966 (expr.span.shrink_to_hi(), ";".to_string()), 967 ], 968 Applicability::MaybeIncorrect, 969 ); 970 return; 971 } 972 973 if let hir::FnRetTy::Return(ty) = fn_decl.output { 974 let ty = self.astconv().ast_ty_to_ty(ty); 975 let bound_vars = self.tcx.late_bound_vars(fn_id); 976 let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); 977 let ty = match self.tcx.asyncness(fn_id.owner) { 978 hir::IsAsync::Async => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { 979 span_bug!(fn_decl.output.span(), "failed to get output type of async function") 980 }), 981 hir::IsAsync::NotAsync => ty, 982 }; 983 let ty = self.normalize(expr.span, ty); 984 if self.can_coerce(found, ty) { 985 err.multipart_suggestion( 986 "you might have meant to return this value", 987 vec![ 988 (expr.span.shrink_to_lo(), "return ".to_string()), 989 (expr.span.shrink_to_hi(), ";".to_string()), 990 ], 991 Applicability::MaybeIncorrect, 992 ); 993 } 994 } 995 } 996 suggest_missing_parentheses( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, ) -> bool997 pub(in super::super) fn suggest_missing_parentheses( 998 &self, 999 err: &mut Diagnostic, 1000 expr: &hir::Expr<'_>, 1001 ) -> bool { 1002 let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None); 1003 if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { 1004 // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` 1005 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); 1006 true 1007 } else { 1008 false 1009 } 1010 } 1011 1012 /// Given an expression type mismatch, peel any `&` expressions until we get to 1013 /// a block expression, and then suggest replacing the braces with square braces 1014 /// if it was possibly mistaken array syntax. suggest_block_to_brackets_peeling_refs( &self, diag: &mut Diagnostic, mut expr: &hir::Expr<'_>, mut expr_ty: Ty<'tcx>, mut expected_ty: Ty<'tcx>, ) -> bool1015 pub(crate) fn suggest_block_to_brackets_peeling_refs( 1016 &self, 1017 diag: &mut Diagnostic, 1018 mut expr: &hir::Expr<'_>, 1019 mut expr_ty: Ty<'tcx>, 1020 mut expected_ty: Ty<'tcx>, 1021 ) -> bool { 1022 loop { 1023 match (&expr.kind, expr_ty.kind(), expected_ty.kind()) { 1024 ( 1025 hir::ExprKind::AddrOf(_, _, inner_expr), 1026 ty::Ref(_, inner_expr_ty, _), 1027 ty::Ref(_, inner_expected_ty, _), 1028 ) => { 1029 expr = *inner_expr; 1030 expr_ty = *inner_expr_ty; 1031 expected_ty = *inner_expected_ty; 1032 } 1033 (hir::ExprKind::Block(blk, _), _, _) => { 1034 self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty); 1035 break true; 1036 } 1037 _ => break false, 1038 } 1039 } 1040 } 1041 suggest_clone_for_ref( &self, diag: &mut Diagnostic, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, ) -> bool1042 pub(crate) fn suggest_clone_for_ref( 1043 &self, 1044 diag: &mut Diagnostic, 1045 expr: &hir::Expr<'_>, 1046 expr_ty: Ty<'tcx>, 1047 expected_ty: Ty<'tcx>, 1048 ) -> bool { 1049 if let ty::Ref(_, inner_ty, hir::Mutability::Not) = expr_ty.kind() 1050 && let Some(clone_trait_def) = self.tcx.lang_items().clone_trait() 1051 && expected_ty == *inner_ty 1052 && self 1053 .infcx 1054 .type_implements_trait( 1055 clone_trait_def, 1056 [self.tcx.erase_regions(expected_ty)], 1057 self.param_env 1058 ) 1059 .must_apply_modulo_regions() 1060 { 1061 let suggestion = match self.maybe_get_struct_pattern_shorthand_field(expr) { 1062 Some(ident) => format!(": {}.clone()", ident), 1063 None => ".clone()".to_string() 1064 }; 1065 1066 diag.span_suggestion_verbose( 1067 expr.span.shrink_to_hi(), 1068 "consider using clone here", 1069 suggestion, 1070 Applicability::MachineApplicable, 1071 ); 1072 return true; 1073 } 1074 false 1075 } 1076 suggest_copied_or_cloned( &self, diag: &mut Diagnostic, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, ) -> bool1077 pub(crate) fn suggest_copied_or_cloned( 1078 &self, 1079 diag: &mut Diagnostic, 1080 expr: &hir::Expr<'_>, 1081 expr_ty: Ty<'tcx>, 1082 expected_ty: Ty<'tcx>, 1083 ) -> bool { 1084 let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; }; 1085 let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; }; 1086 if adt_def != expected_adt_def { 1087 return false; 1088 } 1089 1090 let mut suggest_copied_or_cloned = || { 1091 let expr_inner_ty = substs.type_at(0); 1092 let expected_inner_ty = expected_substs.type_at(0); 1093 if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() 1094 && self.can_eq(self.param_env, ty, expected_inner_ty) 1095 { 1096 let def_path = self.tcx.def_path_str(adt_def.did()); 1097 if self.type_is_copy_modulo_regions(self.param_env, ty) { 1098 diag.span_suggestion_verbose( 1099 expr.span.shrink_to_hi(), 1100 format!( 1101 "use `{def_path}::copied` to copy the value inside the `{def_path}`" 1102 ), 1103 ".copied()", 1104 Applicability::MachineApplicable, 1105 ); 1106 return true; 1107 } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() 1108 && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( 1109 self, 1110 self.param_env, 1111 ty, 1112 clone_did, 1113 ) 1114 { 1115 diag.span_suggestion_verbose( 1116 expr.span.shrink_to_hi(), 1117 format!( 1118 "use `{def_path}::cloned` to clone the value inside the `{def_path}`" 1119 ), 1120 ".cloned()", 1121 Applicability::MachineApplicable, 1122 ); 1123 return true; 1124 } 1125 } 1126 false 1127 }; 1128 1129 if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result) 1130 && adt_def.did() == result_did 1131 // Check that the error types are equal 1132 && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)) 1133 { 1134 return suggest_copied_or_cloned(); 1135 } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) 1136 && adt_def.did() == option_did 1137 { 1138 return suggest_copied_or_cloned(); 1139 } 1140 1141 false 1142 } 1143 suggest_into( &self, diag: &mut Diagnostic, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, ) -> bool1144 pub(crate) fn suggest_into( 1145 &self, 1146 diag: &mut Diagnostic, 1147 expr: &hir::Expr<'_>, 1148 expr_ty: Ty<'tcx>, 1149 expected_ty: Ty<'tcx>, 1150 ) -> bool { 1151 let expr = expr.peel_blocks(); 1152 1153 // We have better suggestions for scalar interconversions... 1154 if expr_ty.is_scalar() && expected_ty.is_scalar() { 1155 return false; 1156 } 1157 1158 // Don't suggest turning a block into another type (e.g. `{}.into()`) 1159 if matches!(expr.kind, hir::ExprKind::Block(..)) { 1160 return false; 1161 } 1162 1163 // We'll later suggest `.as_ref` when noting the type error, 1164 // so skip if we will suggest that instead. 1165 if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() { 1166 return false; 1167 } 1168 1169 if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into) 1170 && self.predicate_must_hold_modulo_regions(&traits::Obligation::new( 1171 self.tcx, 1172 self.misc(expr.span), 1173 self.param_env, 1174 ty::TraitRef::new(self.tcx, 1175 into_def_id, 1176 [expr_ty, expected_ty] 1177 ), 1178 )) 1179 { 1180 let sugg = if expr.precedence().order() >= PREC_POSTFIX { 1181 vec![(expr.span.shrink_to_hi(), ".into()".to_owned())] 1182 } else { 1183 vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())] 1184 }; 1185 diag.multipart_suggestion( 1186 format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"), 1187 sugg, 1188 Applicability::MaybeIncorrect 1189 ); 1190 return true; 1191 } 1192 1193 false 1194 } 1195 1196 /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()` suggest_option_to_bool( &self, diag: &mut Diagnostic, expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, ) -> bool1197 pub(crate) fn suggest_option_to_bool( 1198 &self, 1199 diag: &mut Diagnostic, 1200 expr: &hir::Expr<'_>, 1201 expr_ty: Ty<'tcx>, 1202 expected_ty: Ty<'tcx>, 1203 ) -> bool { 1204 if !expected_ty.is_bool() { 1205 return false; 1206 } 1207 1208 let ty::Adt(def, _) = expr_ty.peel_refs().kind() else { return false; }; 1209 if !self.tcx.is_diagnostic_item(sym::Option, def.did()) { 1210 return false; 1211 } 1212 1213 let hir = self.tcx.hir(); 1214 let cond_parent = hir.parent_iter(expr.hir_id).find(|(_, node)| { 1215 !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And) 1216 }); 1217 // Don't suggest: 1218 // `let Some(_) = a.is_some() && b` 1219 // ++++++++++ 1220 // since the user probably just misunderstood how `let else` 1221 // and `&&` work together. 1222 if let Some((_, hir::Node::Local(local))) = cond_parent 1223 && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind 1224 && let hir::QPath::Resolved(None, path) = qpath 1225 && let Some(did) = path.res.opt_def_id() 1226 .and_then(|did| self.tcx.opt_parent(did)) 1227 .and_then(|did| self.tcx.opt_parent(did)) 1228 && self.tcx.is_diagnostic_item(sym::Option, did) 1229 { 1230 return false; 1231 } 1232 1233 let suggestion = match self.maybe_get_struct_pattern_shorthand_field(expr) { 1234 Some(ident) => format!(": {}.is_some()", ident), 1235 None => ".is_some()".to_string(), 1236 }; 1237 1238 diag.span_suggestion_verbose( 1239 expr.span.shrink_to_hi(), 1240 "use `Option::is_some` to test if the `Option` has a value", 1241 suggestion, 1242 Applicability::MachineApplicable, 1243 ); 1244 true 1245 } 1246 1247 /// Suggest wrapping the block in square brackets instead of curly braces 1248 /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. suggest_block_to_brackets( &self, diag: &mut Diagnostic, blk: &hir::Block<'_>, blk_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, )1249 pub(crate) fn suggest_block_to_brackets( 1250 &self, 1251 diag: &mut Diagnostic, 1252 blk: &hir::Block<'_>, 1253 blk_ty: Ty<'tcx>, 1254 expected_ty: Ty<'tcx>, 1255 ) { 1256 if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() { 1257 if self.can_coerce(blk_ty, *elem_ty) 1258 && blk.stmts.is_empty() 1259 && blk.rules == hir::BlockCheckMode::DefaultBlock 1260 { 1261 let source_map = self.tcx.sess.source_map(); 1262 if let Ok(snippet) = source_map.span_to_snippet(blk.span) { 1263 if snippet.starts_with('{') && snippet.ends_with('}') { 1264 diag.multipart_suggestion_verbose( 1265 "to create an array, use square brackets instead of curly braces", 1266 vec![ 1267 ( 1268 blk.span 1269 .shrink_to_lo() 1270 .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)), 1271 "[".to_string(), 1272 ), 1273 ( 1274 blk.span 1275 .shrink_to_hi() 1276 .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)), 1277 "]".to_string(), 1278 ), 1279 ], 1280 Applicability::MachineApplicable, 1281 ); 1282 } 1283 } 1284 } 1285 } 1286 } 1287 1288 #[instrument(skip(self, err))] suggest_floating_point_literal( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, expected_ty: Ty<'tcx>, ) -> bool1289 pub(crate) fn suggest_floating_point_literal( 1290 &self, 1291 err: &mut Diagnostic, 1292 expr: &hir::Expr<'_>, 1293 expected_ty: Ty<'tcx>, 1294 ) -> bool { 1295 if !expected_ty.is_floating_point() { 1296 return false; 1297 } 1298 match expr.kind { 1299 ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => { 1300 err.span_suggestion_verbose( 1301 start.span.shrink_to_hi().with_hi(end.span.lo()), 1302 "remove the unnecessary `.` operator for a floating point literal", 1303 '.', 1304 Applicability::MaybeIncorrect, 1305 ); 1306 true 1307 } 1308 ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => { 1309 err.span_suggestion_verbose( 1310 expr.span.with_lo(start.span.hi()), 1311 "remove the unnecessary `.` operator for a floating point literal", 1312 '.', 1313 Applicability::MaybeIncorrect, 1314 ); 1315 true 1316 } 1317 ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => { 1318 err.span_suggestion_verbose( 1319 expr.span.until(end.span), 1320 "remove the unnecessary `.` operator and add an integer part for a floating point literal", 1321 "0.", 1322 Applicability::MaybeIncorrect, 1323 ); 1324 true 1325 } 1326 ExprKind::Lit(Spanned { 1327 node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed), 1328 span, 1329 }) => { 1330 let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(*span) else { return false; }; 1331 if !(snippet.starts_with("0x") || snippet.starts_with("0X")) { 1332 return false; 1333 } 1334 if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) { 1335 return false; 1336 } 1337 let (_, suffix) = snippet.split_at(snippet.len() - 3); 1338 let value = match suffix { 1339 "f32" => (lit - 0xf32) / (16 * 16 * 16), 1340 "f64" => (lit - 0xf64) / (16 * 16 * 16), 1341 _ => return false, 1342 }; 1343 err.span_suggestions( 1344 expr.span, 1345 "rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float", 1346 [format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")], 1347 Applicability::MaybeIncorrect, 1348 ); 1349 true 1350 } 1351 _ => false, 1352 } 1353 } 1354 1355 /// Suggest providing `std::ptr::null()` or `std::ptr::null_mut()` if they 1356 /// pass in a literal 0 to an raw pointer. 1357 #[instrument(skip(self, err))] suggest_null_ptr_for_literal_zero_given_to_ptr_arg( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, expected_ty: Ty<'tcx>, ) -> bool1358 pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg( 1359 &self, 1360 err: &mut Diagnostic, 1361 expr: &hir::Expr<'_>, 1362 expected_ty: Ty<'tcx>, 1363 ) -> bool { 1364 // Expected type needs to be a raw pointer. 1365 let ty::RawPtr(ty::TypeAndMut { mutbl, .. }) = expected_ty.kind() else { 1366 return false; 1367 }; 1368 1369 // Provided expression needs to be a literal `0`. 1370 let ExprKind::Lit(Spanned { 1371 node: rustc_ast::LitKind::Int(0, _), 1372 span, 1373 }) = expr.kind else { 1374 return false; 1375 }; 1376 1377 // We need to find a null pointer symbol to suggest 1378 let null_sym = match mutbl { 1379 hir::Mutability::Not => sym::ptr_null, 1380 hir::Mutability::Mut => sym::ptr_null_mut, 1381 }; 1382 let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else { 1383 return false; 1384 }; 1385 let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did)); 1386 1387 // We have satisfied all requirements to provide a suggestion. Emit it. 1388 err.span_suggestion( 1389 *span, 1390 format!("if you meant to create a null pointer, use `{null_path_str}()`"), 1391 null_path_str + "()", 1392 Applicability::MachineApplicable, 1393 ); 1394 1395 true 1396 } 1397 suggest_associated_const( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, expected_ty: Ty<'tcx>, ) -> bool1398 pub(crate) fn suggest_associated_const( 1399 &self, 1400 err: &mut Diagnostic, 1401 expr: &hir::Expr<'_>, 1402 expected_ty: Ty<'tcx>, 1403 ) -> bool { 1404 let Some((DefKind::AssocFn, old_def_id)) = self.typeck_results.borrow().type_dependent_def(expr.hir_id) else { 1405 return false; 1406 }; 1407 let old_item_name = self.tcx.item_name(old_def_id); 1408 let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase()); 1409 if old_item_name == capitalized_name { 1410 return false; 1411 } 1412 let (item, segment) = match expr.kind { 1413 hir::ExprKind::Path(QPath::Resolved( 1414 Some(ty), 1415 hir::Path { segments: [segment], .. }, 1416 )) 1417 | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => { 1418 if let Some(self_ty) = self.typeck_results.borrow().node_type_opt(ty.hir_id) 1419 && let Ok(pick) = self.probe_for_name( 1420 Mode::Path, 1421 Ident::new(capitalized_name, segment.ident.span), 1422 Some(expected_ty), 1423 IsSuggestion(true), 1424 self_ty, 1425 expr.hir_id, 1426 ProbeScope::TraitsInScope, 1427 ) 1428 { 1429 (pick.item, segment) 1430 } else { 1431 return false; 1432 } 1433 } 1434 hir::ExprKind::Path(QPath::Resolved( 1435 None, 1436 hir::Path { segments: [.., segment], .. }, 1437 )) => { 1438 // we resolved through some path that doesn't end in the item name, 1439 // better not do a bad suggestion by accident. 1440 if old_item_name != segment.ident.name { 1441 return false; 1442 } 1443 if let Some(item) = self 1444 .tcx 1445 .associated_items(self.tcx.parent(old_def_id)) 1446 .filter_by_name_unhygienic(capitalized_name) 1447 .next() 1448 { 1449 (*item, segment) 1450 } else { 1451 return false; 1452 } 1453 } 1454 _ => return false, 1455 }; 1456 if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst { 1457 // Same item 1458 return false; 1459 } 1460 let item_ty = self.tcx.type_of(item.def_id).subst_identity(); 1461 // FIXME(compiler-errors): This check is *so* rudimentary 1462 if item_ty.has_param() { 1463 return false; 1464 } 1465 if self.can_coerce(item_ty, expected_ty) { 1466 err.span_suggestion_verbose( 1467 segment.ident.span, 1468 format!("try referring to the associated const `{capitalized_name}` instead",), 1469 capitalized_name, 1470 Applicability::MachineApplicable, 1471 ); 1472 true 1473 } else { 1474 false 1475 } 1476 } 1477 is_loop(&self, id: hir::HirId) -> bool1478 fn is_loop(&self, id: hir::HirId) -> bool { 1479 let node = self.tcx.hir().get(id); 1480 matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) 1481 } 1482 is_local_statement(&self, id: hir::HirId) -> bool1483 fn is_local_statement(&self, id: hir::HirId) -> bool { 1484 let node = self.tcx.hir().get(id); 1485 matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. })) 1486 } 1487 1488 /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`, 1489 /// which is a side-effect of autoref. note_type_is_not_clone( &self, diag: &mut Diagnostic, expected_ty: Ty<'tcx>, found_ty: Ty<'tcx>, expr: &hir::Expr<'_>, )1490 pub(crate) fn note_type_is_not_clone( 1491 &self, 1492 diag: &mut Diagnostic, 1493 expected_ty: Ty<'tcx>, 1494 found_ty: Ty<'tcx>, 1495 expr: &hir::Expr<'_>, 1496 ) { 1497 let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else { return; }; 1498 let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; }; 1499 let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return }; 1500 let results = self.typeck_results.borrow(); 1501 // First, look for a `Clone::clone` call 1502 if segment.ident.name == sym::clone 1503 && results.type_dependent_def_id(expr.hir_id).map_or( 1504 false, 1505 |did| { 1506 let assoc_item = self.tcx.associated_item(did); 1507 assoc_item.container == ty::AssocItemContainer::TraitContainer 1508 && assoc_item.container_id(self.tcx) == clone_trait_did 1509 }, 1510 ) 1511 // If that clone call hasn't already dereferenced the self type (i.e. don't give this 1512 // diagnostic in cases where we have `(&&T).clone()` and we expect `T`). 1513 && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..))) 1514 // Check that we're in fact trying to clone into the expected type 1515 && self.can_coerce(*pointee_ty, expected_ty) 1516 && let trait_ref = ty::TraitRef::new(self.tcx, clone_trait_did, [expected_ty]) 1517 // And the expected type doesn't implement `Clone` 1518 && !self.predicate_must_hold_considering_regions(&traits::Obligation::new( 1519 self.tcx, 1520 traits::ObligationCause::dummy(), 1521 self.param_env, 1522 trait_ref, 1523 )) 1524 { 1525 diag.span_note( 1526 callee_expr.span, 1527 format!( 1528 "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead" 1529 ), 1530 ); 1531 let owner = self.tcx.hir().enclosing_body_owner(expr.hir_id); 1532 if let ty::Param(param) = expected_ty.kind() 1533 && let Some(generics) = self.tcx.hir().get_generics(owner) 1534 { 1535 suggest_constraining_type_params( 1536 self.tcx, 1537 generics, 1538 diag, 1539 vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(), 1540 None, 1541 ); 1542 } else { 1543 self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]); 1544 } 1545 } 1546 } 1547 1548 /// A common error is to add an extra semicolon: 1549 /// 1550 /// ```compile_fail,E0308 1551 /// fn foo() -> usize { 1552 /// 22; 1553 /// } 1554 /// ``` 1555 /// 1556 /// This routine checks if the final statement in a block is an 1557 /// expression with an explicit semicolon whose type is compatible 1558 /// with `expected_ty`. If so, it suggests removing the semicolon. consider_removing_semicolon( &self, blk: &'tcx hir::Block<'tcx>, expected_ty: Ty<'tcx>, err: &mut Diagnostic, ) -> bool1559 pub(crate) fn consider_removing_semicolon( 1560 &self, 1561 blk: &'tcx hir::Block<'tcx>, 1562 expected_ty: Ty<'tcx>, 1563 err: &mut Diagnostic, 1564 ) -> bool { 1565 if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) { 1566 if let StatementAsExpression::NeedsBoxing = boxed { 1567 err.span_suggestion_verbose( 1568 span_semi, 1569 "consider removing this semicolon and boxing the expression", 1570 "", 1571 Applicability::HasPlaceholders, 1572 ); 1573 } else { 1574 err.span_suggestion_short( 1575 span_semi, 1576 "remove this semicolon to return this value", 1577 "", 1578 Applicability::MachineApplicable, 1579 ); 1580 } 1581 true 1582 } else { 1583 false 1584 } 1585 } 1586 } 1587