1use core::cmp::min;
2use core::iter;
3
4use hir::def_id::LocalDefId;
5use rustc_ast::util::parser::ExprPrecedence;
6use rustc_data_structures::packed::Pu128;
7use rustc_errors::{Applicability, Diag, MultiSpan, listify};
8use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
9use rustc_hir::lang_items::LangItem;
10use rustc_hir::{
11 self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind,
12 GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind,
13 WherePredicateKind, expr_needs_parens,
14};
15use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
16use rustc_hir_analysis::suggest_impl_trait;
17use rustc_middle::middle::stability::EvalResult;
18use rustc_middle::span_bug;
19use rustc_middle::ty::print::with_no_trimmed_paths;
20use rustc_middle::ty::{
21 self, Article, Binder, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Upcast,
22 suggest_constraining_type_params,
23};
24use rustc_session::errors::ExprParenthesesNeeded;
25use rustc_span::source_map::Spanned;
26use rustc_span::{ExpnKind, Ident, MacroKind, Span, Symbol, sym};
27use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
28use rustc_trait_selection::error_reporting::traits::DefIdOrName;
29use rustc_trait_selection::infer::InferCtxtExt;
30use rustc_trait_selection::traits;
31use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
32use tracing::{debug, instrument};
33
34use super::FnCtxt;
35use crate::fn_ctxt::rustc_span::BytePos;
36use crate::method::probe;
37use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
38use crate::{errors, fluent_generated as fluent};
39
40impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
41 pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
42 self.typeck_results
43 .borrow()
44 .liberated_fn_sigs()
45 .get(self.tcx.local_def_id_to_hir_id(self.body_id))
46 .copied()
47 }
48
49 pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diag<'_>) {
50 err.span_suggestion_short(
53 span.shrink_to_hi(),
54 "consider using a semicolon here",
55 ";",
56 Applicability::MaybeIncorrect,
57 );
58 }
59
60 pub(crate) fn suggest_mismatched_types_on_tail(
66 &self,
67 err: &mut Diag<'_>,
68 expr: &'tcx hir::Expr<'tcx>,
69 expected: Ty<'tcx>,
70 found: Ty<'tcx>,
71 blk_id: HirId,
72 ) -> bool {
73 let expr = expr.peel_drop_temps();
74 let mut pointing_at_return_type = false;
75 if let hir::ExprKind::Break(..) = expr.kind {
76 return false;
78 }
79 if let Some((fn_id, fn_decl)) = self.get_fn_decl(blk_id) {
80 pointing_at_return_type =
81 self.suggest_missing_return_type(err, fn_decl, expected, found, fn_id);
82 self.suggest_missing_break_or_return_expr(
83 err, expr, fn_decl, expected, found, blk_id, fn_id,
84 );
85 }
86 pointing_at_return_type
87 }
88
89 pub(crate) fn suggest_fn_call(
97 &self,
98 err: &mut Diag<'_>,
99 expr: &hir::Expr<'_>,
100 found: Ty<'tcx>,
101 can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
102 ) -> bool {
103 let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(found) else {
104 return false;
105 };
106 if can_satisfy(output) {
107 let (sugg_call, mut applicability) = match inputs.len() {
108 0 => ("".to_string(), Applicability::MachineApplicable),
109 1..=4 => (
110 inputs
111 .iter()
112 .map(|ty| {
113 if ty.is_suggestable(self.tcx, false) {
114 format!("/* {ty} */")
115 } else {
116 "/* value */".to_string()
117 }
118 })
119 .collect::<Vec<_>>()
120 .join(", "),
121 Applicability::HasPlaceholders,
122 ),
123 _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
124 };
125
126 let msg = match def_id_or_name {
127 DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
128 DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(),
129 DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(),
130 kind => format!("call this {}", self.tcx.def_kind_descr(kind, def_id)),
131 },
132 DefIdOrName::Name(name) => format!("call this {name}"),
133 };
134
135 let sugg = match expr.kind {
136 hir::ExprKind::Call(..)
137 | hir::ExprKind::Path(..)
138 | hir::ExprKind::Index(..)
139 | hir::ExprKind::Lit(..) => {
140 vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
141 }
142 hir::ExprKind::Closure { .. } => {
143 applicability = Applicability::MaybeIncorrect;
145 vec![
146 (expr.span.shrink_to_lo(), "(".to_string()),
147 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
148 ]
149 }
150 _ => {
151 vec![
152 (expr.span.shrink_to_lo(), "(".to_string()),
153 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
154 ]
155 }
156 };
157
158 err.multipart_suggestion_verbose(
159 format!("use parentheses to {msg}"),
160 sugg,
161 applicability,
162 );
163 return true;
164 }
165 false
166 }
167
168 pub(in super::super) fn extract_callable_info(
172 &self,
173 ty: Ty<'tcx>,
174 ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
175 self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty)
176 }
177
178 pub(crate) fn suggest_two_fn_call(
179 &self,
180 err: &mut Diag<'_>,
181 lhs_expr: &'tcx hir::Expr<'tcx>,
182 lhs_ty: Ty<'tcx>,
183 rhs_expr: &'tcx hir::Expr<'tcx>,
184 rhs_ty: Ty<'tcx>,
185 can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
186 ) -> bool {
187 if lhs_expr.span.in_derive_expansion() || rhs_expr.span.in_derive_expansion() {
188 return false;
189 }
190 let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty) else {
191 return false;
192 };
193 let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_ty) else {
194 return false;
195 };
196
197 if can_satisfy(lhs_output_ty, rhs_output_ty) {
198 let mut sugg = vec![];
199 let mut applicability = Applicability::MachineApplicable;
200
201 for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
202 let (sugg_call, this_applicability) = match inputs.len() {
203 0 => ("".to_string(), Applicability::MachineApplicable),
204 1..=4 => (
205 inputs
206 .iter()
207 .map(|ty| {
208 if ty.is_suggestable(self.tcx, false) {
209 format!("/* {ty} */")
210 } else {
211 "/* value */".to_string()
212 }
213 })
214 .collect::<Vec<_>>()
215 .join(", "),
216 Applicability::HasPlaceholders,
217 ),
218 _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
219 };
220
221 applicability = applicability.max(this_applicability);
222
223 match expr.kind {
224 hir::ExprKind::Call(..)
225 | hir::ExprKind::Path(..)
226 | hir::ExprKind::Index(..)
227 | hir::ExprKind::Lit(..) => {
228 sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
229 }
230 hir::ExprKind::Closure { .. } => {
231 applicability = Applicability::MaybeIncorrect;
233 sugg.extend([
234 (expr.span.shrink_to_lo(), "(".to_string()),
235 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
236 ]);
237 }
238 _ => {
239 sugg.extend([
240 (expr.span.shrink_to_lo(), "(".to_string()),
241 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
242 ]);
243 }
244 }
245 }
246
247 err.multipart_suggestion_verbose("use parentheses to call these", sugg, applicability);
248
249 true
250 } else {
251 false
252 }
253 }
254
255 pub(crate) fn suggest_remove_last_method_call(
256 &self,
257 err: &mut Diag<'_>,
258 expr: &hir::Expr<'tcx>,
259 expected: Ty<'tcx>,
260 ) -> bool {
261 if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) =
262 expr.kind
263 && let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr)
264 && self.may_coerce(recv_ty, expected)
265 && let name = method.name.as_str()
266 && (name.starts_with("to_") || name.starts_with("as_") || name == "into")
267 {
268 let span = if let Some(recv_span) = recv_expr.span.find_ancestor_inside(expr.span) {
269 expr.span.with_lo(recv_span.hi())
270 } else {
271 expr.span.with_lo(method.span.lo() - rustc_span::BytePos(1))
272 };
273 err.span_suggestion_verbose(
274 span,
275 "try removing the method call",
276 "",
277 Applicability::MachineApplicable,
278 );
279 return true;
280 }
281 false
282 }
283
284 pub(crate) fn suggest_deref_ref_or_into(
285 &self,
286 err: &mut Diag<'_>,
287 expr: &hir::Expr<'tcx>,
288 expected: Ty<'tcx>,
289 found: Ty<'tcx>,
290 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
291 ) -> bool {
292 let expr = expr.peel_blocks();
293 let methods =
294 self.get_conversion_methods_for_diagnostic(expr.span, expected, found, expr.hir_id);
295
296 if let Some((suggestion, msg, applicability, verbose, annotation)) =
297 self.suggest_deref_or_ref(expr, found, expected)
298 {
299 if verbose {
300 err.multipart_suggestion_verbose(msg, suggestion, applicability);
301 } else {
302 err.multipart_suggestion(msg, suggestion, applicability);
303 }
304 if annotation {
305 let suggest_annotation = match expr.peel_drop_temps().kind {
306 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(),
307 _ => return true,
308 };
309 let mut tuple_indexes = Vec::new();
310 let mut expr_id = expr.hir_id;
311 for (parent_id, node) in self.tcx.hir_parent_iter(expr.hir_id) {
312 match node {
313 Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => {
314 tuple_indexes.push(
315 subs.iter()
316 .enumerate()
317 .find(|(_, sub_expr)| sub_expr.hir_id == expr_id)
318 .unwrap()
319 .0,
320 );
321 expr_id = parent_id;
322 }
323 Node::LetStmt(local) => {
324 if let Some(mut ty) = local.ty {
325 while let Some(index) = tuple_indexes.pop() {
326 match ty.kind {
327 TyKind::Tup(tys) => ty = &tys[index],
328 _ => return true,
329 }
330 }
331 let annotation_span = ty.span;
332 err.span_suggestion(
333 annotation_span.with_hi(annotation_span.lo()),
334 "alternatively, consider changing the type annotation",
335 suggest_annotation,
336 Applicability::MaybeIncorrect,
337 );
338 }
339 break;
340 }
341 _ => break,
342 }
343 }
344 }
345 return true;
346 }
347
348 if self.suggest_else_fn_with_closure(err, expr, found, expected) {
349 return true;
350 }
351
352 if self.suggest_fn_call(err, expr, found, |output| self.may_coerce(output, expected))
353 && let ty::FnDef(def_id, ..) = *found.kind()
354 && let Some(sp) = self.tcx.hir().span_if_local(def_id)
355 {
356 let name = self.tcx.item_name(def_id);
357 let kind = self.tcx.def_kind(def_id);
358 if let DefKind::Ctor(of, CtorKind::Fn) = kind {
359 err.span_label(
360 sp,
361 format!(
362 "`{name}` defines {} constructor here, which should be called",
363 match of {
364 CtorOf::Struct => "a struct",
365 CtorOf::Variant => "an enum variant",
366 }
367 ),
368 );
369 } else {
370 let descr = self.tcx.def_kind_descr(kind, def_id);
371 err.span_label(sp, format!("{descr} `{name}` defined here"));
372 }
373 return true;
374 }
375
376 if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
377 return true;
378 }
379
380 if !methods.is_empty() {
381 let mut suggestions = methods
382 .iter()
383 .filter_map(|conversion_method| {
384 let receiver_method_ident = expr.method_ident();
385 if let Some(method_ident) = receiver_method_ident
386 && method_ident.name == conversion_method.name
387 {
388 return None; }
390
391 let method_call_list = [sym::to_vec, sym::to_string];
392 let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
393 && receiver_method.ident.name == sym::clone
394 && method_call_list.contains(&conversion_method.name)
395 {
400 vec![(receiver_method.ident.span, conversion_method.name.to_string())]
401 } else if expr.precedence() < ExprPrecedence::Unambiguous {
402 vec![
403 (expr.span.shrink_to_lo(), "(".to_string()),
404 (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
405 ]
406 } else {
407 vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
408 };
409 let struct_pat_shorthand_field =
410 self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr);
411 if let Some(name) = struct_pat_shorthand_field {
412 sugg.insert(0, (expr.span.shrink_to_lo(), format!("{name}: ")));
413 }
414 Some(sugg)
415 })
416 .peekable();
417 if suggestions.peek().is_some() {
418 err.multipart_suggestions(
419 "try using a conversion method",
420 suggestions,
421 Applicability::MaybeIncorrect,
422 );
423 return true;
424 }
425 }
426
427 if let Some((found_ty_inner, expected_ty_inner, error_tys)) =
428 self.deconstruct_option_or_result(found, expected)
429 && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind()
430 {
431 let inner_expr = expr.peel_borrows();
433 if !inner_expr.span.eq_ctxt(expr.span) {
434 return false;
435 }
436 let borrow_removal_span = if inner_expr.hir_id == expr.hir_id {
437 None
438 } else {
439 Some(expr.span.shrink_to_lo().until(inner_expr.span))
440 };
441 let error_tys_equate_as_ref = error_tys.is_none_or(|(found, expected)| {
444 self.can_eq(
445 self.param_env,
446 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, found),
447 expected,
448 )
449 });
450
451 let prefix_wrap = |sugg: &str| {
452 if let Some(name) = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
453 format!(": {}{}", name, sugg)
454 } else {
455 sugg.to_string()
456 }
457 };
458
459 if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
462 let sugg = prefix_wrap(".as_ref()");
463 err.subdiagnostic(errors::SuggestConvertViaMethod {
464 span: expr.span.shrink_to_hi(),
465 sugg,
466 expected,
467 found,
468 borrow_removal_span,
469 });
470 return true;
471 } else if let ty::Ref(_, peeled_found_ty, _) = found_ty_inner.kind()
472 && let ty::Adt(adt, _) = peeled_found_ty.peel_refs().kind()
473 && self.tcx.is_lang_item(adt.did(), LangItem::String)
474 && peeled.is_str()
475 && error_tys.is_none_or(|(found, expected)| {
477 self.can_eq(self.param_env, found, expected)
478 })
479 {
480 let sugg = prefix_wrap(".map(|x| x.as_str())");
481 err.span_suggestion_verbose(
482 expr.span.shrink_to_hi(),
483 fluent::hir_typeck_convert_to_str,
484 sugg,
485 Applicability::MachineApplicable,
486 );
487 return true;
488 } else {
489 if !error_tys_equate_as_ref {
490 return false;
491 }
492 let mut steps = self.autoderef(expr.span, found_ty_inner).silence_errors();
493 if let Some((deref_ty, _)) = steps.nth(1)
494 && self.can_eq(self.param_env, deref_ty, peeled)
495 {
496 let sugg = prefix_wrap(".as_deref()");
497 err.subdiagnostic(errors::SuggestConvertViaMethod {
498 span: expr.span.shrink_to_hi(),
499 sugg,
500 expected,
501 found,
502 borrow_removal_span,
503 });
504 return true;
505 }
506 for (deref_ty, n_step) in steps {
507 if self.can_eq(self.param_env, deref_ty, peeled) {
508 let explicit_deref = "*".repeat(n_step);
509 let sugg = prefix_wrap(&format!(".map(|v| &{explicit_deref}v)"));
510 err.subdiagnostic(errors::SuggestConvertViaMethod {
511 span: expr.span.shrink_to_hi(),
512 sugg,
513 expected,
514 found,
515 borrow_removal_span,
516 });
517 return true;
518 }
519 }
520 }
521 }
522
523 false
524 }
525
526 fn deconstruct_option_or_result(
530 &self,
531 found_ty: Ty<'tcx>,
532 expected_ty: Ty<'tcx>,
533 ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> {
534 let ty::Adt(found_adt, found_args) = found_ty.peel_refs().kind() else {
535 return None;
536 };
537 let ty::Adt(expected_adt, expected_args) = expected_ty.kind() else {
538 return None;
539 };
540 if self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
541 && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
542 {
543 Some((found_args.type_at(0), expected_args.type_at(0), None))
544 } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did())
545 && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did())
546 {
547 Some((
548 found_args.type_at(0),
549 expected_args.type_at(0),
550 Some((found_args.type_at(1), expected_args.type_at(1))),
551 ))
552 } else {
553 None
554 }
555 }
556
557 pub(in super::super) fn suggest_boxing_when_appropriate(
560 &self,
561 err: &mut Diag<'_>,
562 span: Span,
563 hir_id: HirId,
564 expected: Ty<'tcx>,
565 found: Ty<'tcx>,
566 ) -> bool {
567 if self.tcx.hir_is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
569 return false;
570 }
571 if self.may_coerce(Ty::new_box(self.tcx, found), expected) {
572 let suggest_boxing = match found.kind() {
573 ty::Tuple(tuple) if tuple.is_empty() => {
574 errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
575 }
576 ty::Coroutine(def_id, ..)
577 if matches!(
578 self.tcx.coroutine_kind(def_id),
579 Some(CoroutineKind::Desugared(
580 CoroutineDesugaring::Async,
581 CoroutineSource::Closure
582 ))
583 ) =>
584 {
585 errors::SuggestBoxing::AsyncBody
586 }
587 _ => errors::SuggestBoxing::Other {
588 start: span.shrink_to_lo(),
589 end: span.shrink_to_hi(),
590 },
591 };
592 err.subdiagnostic(suggest_boxing);
593
594 true
595 } else {
596 false
597 }
598 }
599
600 pub(in super::super) fn suggest_no_capture_closure(
603 &self,
604 err: &mut Diag<'_>,
605 expected: Ty<'tcx>,
606 found: Ty<'tcx>,
607 ) -> bool {
608 if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
609 if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
610 let spans_and_labels = upvars
613 .iter()
614 .take(4)
615 .map(|(var_hir_id, upvar)| {
616 let var_name = self.tcx.hir_name(*var_hir_id).to_string();
617 let msg = format!("`{var_name}` captured here");
618 (upvar.span, msg)
619 })
620 .collect::<Vec<_>>();
621
622 let mut multi_span: MultiSpan =
623 spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
624 for (sp, label) in spans_and_labels {
625 multi_span.push_span_label(sp, label);
626 }
627 err.span_note(
628 multi_span,
629 "closures can only be coerced to `fn` types if they do not capture any variables"
630 );
631 return true;
632 }
633 }
634 false
635 }
636
637 #[instrument(skip(self, err))]
639 pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
640 &self,
641 err: &mut Diag<'_>,
642 expr: &hir::Expr<'_>,
643 expected: Ty<'tcx>,
644 found: Ty<'tcx>,
645 ) -> bool {
646 if self.tcx.hir_is_inside_const_context(expr.hir_id) {
649 return false;
651 }
652 let pin_did = self.tcx.lang_items().pin_type();
653 if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
655 return false;
656 }
657 let box_found = Ty::new_box(self.tcx, found);
658 let Some(pin_box_found) = Ty::new_lang_item(self.tcx, box_found, LangItem::Pin) else {
659 return false;
660 };
661 let Some(pin_found) = Ty::new_lang_item(self.tcx, found, LangItem::Pin) else {
662 return false;
663 };
664 match expected.kind() {
665 ty::Adt(def, _) if Some(def.did()) == pin_did => {
666 if self.may_coerce(pin_box_found, expected) {
667 debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
668 match found.kind() {
669 ty::Adt(def, _) if def.is_box() => {
670 err.help("use `Box::pin`");
671 }
672 _ => {
673 let prefix = if let Some(name) =
674 self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr)
675 {
676 format!("{}: ", name)
677 } else {
678 String::new()
679 };
680 let suggestion = vec![
681 (expr.span.shrink_to_lo(), format!("{prefix}Box::pin(")),
682 (expr.span.shrink_to_hi(), ")".to_string()),
683 ];
684 err.multipart_suggestion(
685 "you need to pin and box this expression",
686 suggestion,
687 Applicability::MaybeIncorrect,
688 );
689 }
690 }
691 true
692 } else if self.may_coerce(pin_found, expected) {
693 match found.kind() {
694 ty::Adt(def, _) if def.is_box() => {
695 err.help("use `Box::pin`");
696 true
697 }
698 _ => false,
699 }
700 } else {
701 false
702 }
703 }
704 ty::Adt(def, _) if def.is_box() && self.may_coerce(box_found, expected) => {
705 let Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. }) =
709 self.tcx.parent_hir_node(expr.hir_id)
710 else {
711 return false;
712 };
713 match fn_name.kind {
714 ExprKind::Path(QPath::TypeRelative(
715 hir::Ty {
716 kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })),
717 ..
718 },
719 method,
720 )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => {
721 err.span_suggestion(
722 fn_name.span,
723 "use `Box::pin` to pin and box this expression",
724 "Box::pin",
725 Applicability::MachineApplicable,
726 );
727 true
728 }
729 _ => false,
730 }
731 }
732 _ => false,
733 }
734 }
735
736 pub(crate) fn suggest_missing_semicolon(
752 &self,
753 err: &mut Diag<'_>,
754 expression: &'tcx hir::Expr<'tcx>,
755 expected: Ty<'tcx>,
756 needs_block: bool,
757 ) {
758 if expected.is_unit() {
759 match expression.kind {
762 ExprKind::Call(..)
763 | ExprKind::MethodCall(..)
764 | ExprKind::Loop(..)
765 | ExprKind::If(..)
766 | ExprKind::Match(..)
767 | ExprKind::Block(..)
768 if expression.can_have_side_effects()
769 && !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
773 {
774 if needs_block {
775 err.multipart_suggestion(
776 "consider using a semicolon here",
777 vec![
778 (expression.span.shrink_to_lo(), "{ ".to_owned()),
779 (expression.span.shrink_to_hi(), "; }".to_owned()),
780 ],
781 Applicability::MachineApplicable,
782 );
783 } else {
784 err.span_suggestion(
785 expression.span.shrink_to_hi(),
786 "consider using a semicolon here",
787 ";",
788 Applicability::MachineApplicable,
789 );
790 }
791 }
792 _ => (),
793 }
794 }
795 }
796
797 #[instrument(level = "trace", skip(self, err))]
810 pub(in super::super) fn suggest_missing_return_type(
811 &self,
812 err: &mut Diag<'_>,
813 fn_decl: &hir::FnDecl<'tcx>,
814 expected: Ty<'tcx>,
815 found: Ty<'tcx>,
816 fn_id: LocalDefId,
817 ) -> bool {
818 if let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Block)) =
820 self.tcx.coroutine_kind(fn_id)
821 {
822 return false;
823 }
824
825 let found =
826 self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
827 match &fn_decl.output {
830 &hir::FnRetTy::DefaultReturn(_) if self.tcx.is_closure_like(fn_id.to_def_id()) => {}
832 &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
833 if !self.can_add_return_type(fn_id) {
834 err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span });
835 } else if let Some(found) = found.make_suggestable(self.tcx, false, None) {
836 err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
837 span,
838 found: found.to_string(),
839 });
840 } else if let Some(sugg) = suggest_impl_trait(self, self.param_env, found) {
841 err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: sugg });
842 } else {
843 err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span });
845 }
846
847 return true;
848 }
849 hir::FnRetTy::Return(hir_ty) => {
850 if let hir::TyKind::OpaqueDef(op_ty, ..) = hir_ty.kind
851 && let [hir::GenericBound::Trait(trait_ref)] = op_ty.bounds
853 && let Some(hir::PathSegment { args: Some(generic_args), .. }) =
854 trait_ref.trait_ref.path.segments.last()
855 && let [constraint] = generic_args.constraints
856 && let Some(ty) = constraint.ty()
857 {
858 debug!(?found);
861 if found.is_suggestable(self.tcx, false) {
862 if ty.span.is_empty() {
863 err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
864 span: ty.span,
865 found: found.to_string(),
866 });
867 return true;
868 } else {
869 err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
870 span: ty.span,
871 expected,
872 });
873 }
874 }
875 } else {
876 debug!(?hir_ty, "return type");
879 let ty = self.lowerer().lower_ty(hir_ty);
880 debug!(?ty, "return type (lowered)");
881 debug!(?expected, "expected type");
882 let bound_vars =
883 self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
884 let ty = Binder::bind_with_vars(ty, bound_vars);
885 let ty = self.normalize(hir_ty.span, ty);
886 let ty = self.tcx.instantiate_bound_regions_with_erased(ty);
887 if self.may_coerce(expected, ty) {
888 err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
889 span: hir_ty.span,
890 expected,
891 });
892 self.try_suggest_return_impl_trait(err, expected, found, fn_id);
893 self.try_note_caller_chooses_ty_for_ty_param(err, expected, found);
894 return true;
895 }
896 }
897 }
898 _ => {}
899 }
900 false
901 }
902
903 fn can_add_return_type(&self, fn_id: LocalDefId) -> bool {
906 match self.tcx.hir_node_by_def_id(fn_id) {
907 Node::Item(item) => {
908 let (ident, _, _, _) = item.expect_fn();
909 ident.name != sym::main
913 }
914 Node::ImplItem(item) => {
915 let Node::Item(&hir::Item {
917 kind: hir::ItemKind::Impl(&hir::Impl { of_trait, .. }),
918 ..
919 }) = self.tcx.parent_hir_node(item.hir_id())
920 else {
921 unreachable!();
922 };
923
924 of_trait.is_none()
925 }
926 _ => true,
927 }
928 }
929
930 fn try_note_caller_chooses_ty_for_ty_param(
931 &self,
932 diag: &mut Diag<'_>,
933 expected: Ty<'tcx>,
934 found: Ty<'tcx>,
935 ) {
936 let ty::Param(expected_ty_as_param) = expected.kind() else {
943 return;
944 };
945
946 if found.contains(expected) {
947 return;
948 }
949
950 diag.subdiagnostic(errors::NoteCallerChoosesTyForTyParam {
951 ty_param_name: expected_ty_as_param.name,
952 found_ty: found,
953 });
954 }
955
956 fn try_suggest_return_impl_trait(
965 &self,
966 err: &mut Diag<'_>,
967 expected: Ty<'tcx>,
968 found: Ty<'tcx>,
969 fn_id: LocalDefId,
970 ) {
971 debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found);
980
981 let ty::Param(expected_ty_as_param) = expected.kind() else { return };
982
983 let fn_node = self.tcx.hir_node_by_def_id(fn_id);
984
985 let hir::Node::Item(hir::Item {
986 kind:
987 hir::ItemKind::Fn {
988 sig:
989 hir::FnSig {
990 decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. },
991 ..
992 },
993 generics: hir::Generics { params, predicates, .. },
994 ..
995 },
996 ..
997 }) = fn_node
998 else {
999 return;
1000 };
1001
1002 if params.get(expected_ty_as_param.index as usize).is_none() {
1003 return;
1004 };
1005
1006 let where_predicates = predicates
1008 .iter()
1009 .filter_map(|p| match p.kind {
1010 WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
1011 bounds,
1012 bounded_ty,
1013 ..
1014 }) => {
1015 let ty = self.lowerer().lower_ty(bounded_ty);
1017 Some((ty, bounds))
1018 }
1019 _ => None,
1020 })
1021 .map(|(ty, bounds)| match ty.kind() {
1022 ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
1023 _ => match ty.contains(expected) {
1025 true => Err(()),
1026 false => Ok(None),
1027 },
1028 })
1029 .collect::<Result<Vec<_>, _>>();
1030
1031 let Ok(where_predicates) = where_predicates else { return };
1032
1033 let predicates_from_where =
1035 where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
1036
1037 let all_matching_bounds_strs = predicates_from_where
1039 .filter_map(|bound| match bound {
1040 GenericBound::Trait(_) => {
1041 self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
1042 }
1043 _ => None,
1044 })
1045 .collect::<Vec<String>>();
1046
1047 if all_matching_bounds_strs.len() == 0 {
1048 return;
1049 }
1050
1051 let all_bounds_str = all_matching_bounds_strs.join(" + ");
1052
1053 let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
1054 let ty = self.lowerer().lower_ty( param);
1055 matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
1056 });
1057
1058 if ty_param_used_in_fn_params {
1059 return;
1060 }
1061
1062 err.span_suggestion(
1063 fn_return.span(),
1064 "consider using an impl return type",
1065 format!("impl {all_bounds_str}"),
1066 Applicability::MaybeIncorrect,
1067 );
1068 }
1069
1070 pub(in super::super) fn suggest_missing_break_or_return_expr(
1071 &self,
1072 err: &mut Diag<'_>,
1073 expr: &'tcx hir::Expr<'tcx>,
1074 fn_decl: &hir::FnDecl<'tcx>,
1075 expected: Ty<'tcx>,
1076 found: Ty<'tcx>,
1077 id: HirId,
1078 fn_id: LocalDefId,
1079 ) {
1080 if !expected.is_unit() {
1081 return;
1082 }
1083 let found = self.resolve_vars_if_possible(found);
1084
1085 let in_loop = self.is_loop(id)
1086 || self
1087 .tcx
1088 .hir_parent_iter(id)
1089 .take_while(|(_, node)| {
1090 node.body_id().is_none()
1092 })
1093 .any(|(parent_id, _)| self.is_loop(parent_id));
1094
1095 let in_local_statement = self.is_local_statement(id)
1096 || self
1097 .tcx
1098 .hir_parent_iter(id)
1099 .any(|(parent_id, _)| self.is_local_statement(parent_id));
1100
1101 if in_loop && in_local_statement {
1102 err.multipart_suggestion(
1103 "you might have meant to break the loop with this value",
1104 vec![
1105 (expr.span.shrink_to_lo(), "break ".to_string()),
1106 (expr.span.shrink_to_hi(), ";".to_string()),
1107 ],
1108 Applicability::MaybeIncorrect,
1109 );
1110 return;
1111 }
1112
1113 let scope = self.tcx.hir_parent_iter(id).find(|(_, node)| {
1114 matches!(
1115 node,
1116 Node::Expr(Expr { kind: ExprKind::Closure(..), .. })
1117 | Node::Item(_)
1118 | Node::TraitItem(_)
1119 | Node::ImplItem(_)
1120 )
1121 });
1122 let in_closure =
1123 matches!(scope, Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. }))));
1124
1125 let can_return = match fn_decl.output {
1126 hir::FnRetTy::Return(ty) => {
1127 let ty = self.lowerer().lower_ty(ty);
1128 let bound_vars = self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
1129 let ty = self
1130 .tcx
1131 .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
1132 let ty = match self.tcx.asyncness(fn_id) {
1133 ty::Asyncness::Yes => {
1134 self.err_ctxt().get_impl_future_output_ty(ty).unwrap_or_else(|| {
1135 span_bug!(
1136 fn_decl.output.span(),
1137 "failed to get output type of async function"
1138 )
1139 })
1140 }
1141 ty::Asyncness::No => ty,
1142 };
1143 let ty = self.normalize(expr.span, ty);
1144 self.may_coerce(found, ty)
1145 }
1146 hir::FnRetTy::DefaultReturn(_) if in_closure => {
1147 self.ret_coercion.as_ref().is_some_and(|ret| {
1148 let ret_ty = ret.borrow().expected_ty();
1149 self.may_coerce(found, ret_ty)
1150 })
1151 }
1152 _ => false,
1153 };
1154 if can_return
1155 && let Some(span) = expr.span.find_ancestor_inside(
1156 self.tcx.hir().span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)),
1157 )
1158 {
1159 fn is_in_arm<'tcx>(expr: &'tcx hir::Expr<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
1170 for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
1171 match node {
1172 hir::Node::Block(block) => {
1173 if let Some(ret) = block.expr
1174 && ret.hir_id == expr.hir_id
1175 {
1176 continue;
1177 }
1178 }
1179 hir::Node::Arm(arm) => {
1180 if let hir::ExprKind::Block(block, _) = arm.body.kind
1181 && let Some(ret) = block.expr
1182 && ret.hir_id == expr.hir_id
1183 {
1184 return true;
1185 }
1186 }
1187 hir::Node::Expr(e) if let hir::ExprKind::Block(block, _) = e.kind => {
1188 if let Some(ret) = block.expr
1189 && ret.hir_id == expr.hir_id
1190 {
1191 continue;
1192 }
1193 }
1194 _ => {
1195 return false;
1196 }
1197 }
1198 }
1199
1200 false
1201 }
1202 let mut suggs = vec![(span.shrink_to_lo(), "return ".to_string())];
1203 if !is_in_arm(expr, self.tcx) {
1204 suggs.push((span.shrink_to_hi(), ";".to_string()));
1205 }
1206 err.multipart_suggestion_verbose(
1207 "you might have meant to return this value",
1208 suggs,
1209 Applicability::MaybeIncorrect,
1210 );
1211 }
1212 }
1213
1214 pub(in super::super) fn suggest_missing_parentheses(
1215 &self,
1216 err: &mut Diag<'_>,
1217 expr: &hir::Expr<'_>,
1218 ) -> bool {
1219 let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None);
1220 if let Some(sp) = self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
1221 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
1223 true
1224 } else {
1225 false
1226 }
1227 }
1228
1229 pub(crate) fn suggest_block_to_brackets_peeling_refs(
1233 &self,
1234 diag: &mut Diag<'_>,
1235 mut expr: &hir::Expr<'_>,
1236 mut expr_ty: Ty<'tcx>,
1237 mut expected_ty: Ty<'tcx>,
1238 ) -> bool {
1239 loop {
1240 match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
1241 (
1242 hir::ExprKind::AddrOf(_, _, inner_expr),
1243 ty::Ref(_, inner_expr_ty, _),
1244 ty::Ref(_, inner_expected_ty, _),
1245 ) => {
1246 expr = *inner_expr;
1247 expr_ty = *inner_expr_ty;
1248 expected_ty = *inner_expected_ty;
1249 }
1250 (hir::ExprKind::Block(blk, _), _, _) => {
1251 self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
1252 break true;
1253 }
1254 _ => break false,
1255 }
1256 }
1257 }
1258
1259 pub(crate) fn suggest_clone_for_ref(
1260 &self,
1261 diag: &mut Diag<'_>,
1262 expr: &hir::Expr<'_>,
1263 expr_ty: Ty<'tcx>,
1264 expected_ty: Ty<'tcx>,
1265 ) -> bool {
1266 if let ty::Ref(_, inner_ty, hir::Mutability::Not) = expr_ty.kind()
1267 && let Some(clone_trait_def) = self.tcx.lang_items().clone_trait()
1268 && expected_ty == *inner_ty
1269 && self
1270 .infcx
1271 .type_implements_trait(
1272 clone_trait_def,
1273 [self.tcx.erase_regions(expected_ty)],
1274 self.param_env,
1275 )
1276 .must_apply_modulo_regions()
1277 {
1278 let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
1279 Some(ident) => format!(": {ident}.clone()"),
1280 None => ".clone()".to_string(),
1281 };
1282
1283 diag.span_suggestion_verbose(
1284 expr.span.shrink_to_hi(),
1285 "consider using clone here",
1286 suggestion,
1287 Applicability::MachineApplicable,
1288 );
1289 return true;
1290 }
1291 false
1292 }
1293
1294 pub(crate) fn suggest_copied_cloned_or_as_ref(
1295 &self,
1296 diag: &mut Diag<'_>,
1297 expr: &hir::Expr<'_>,
1298 expr_ty: Ty<'tcx>,
1299 expected_ty: Ty<'tcx>,
1300 ) -> bool {
1301 let ty::Adt(adt_def, args) = expr_ty.kind() else {
1302 return false;
1303 };
1304 let ty::Adt(expected_adt_def, expected_args) = expected_ty.kind() else {
1305 return false;
1306 };
1307 if adt_def != expected_adt_def {
1308 return false;
1309 }
1310
1311 if Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Result)
1312 && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
1313 || Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Option)
1314 {
1315 let expr_inner_ty = args.type_at(0);
1316 let expected_inner_ty = expected_args.type_at(0);
1317 if let &ty::Ref(_, ty, _mutability) = expr_inner_ty.kind()
1318 && self.can_eq(self.param_env, ty, expected_inner_ty)
1319 {
1320 let def_path = self.tcx.def_path_str(adt_def.did());
1321 let span = expr.span.shrink_to_hi();
1322 let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
1323 errors::OptionResultRefMismatch::Copied { span, def_path }
1324 } else if self.type_is_clone_modulo_regions(self.param_env, ty) {
1325 errors::OptionResultRefMismatch::Cloned { span, def_path }
1326 } else {
1327 return false;
1328 };
1329 diag.subdiagnostic(subdiag);
1330 return true;
1331 }
1332 }
1333
1334 false
1335 }
1336
1337 pub(crate) fn suggest_into(
1338 &self,
1339 diag: &mut Diag<'_>,
1340 expr: &hir::Expr<'_>,
1341 expr_ty: Ty<'tcx>,
1342 expected_ty: Ty<'tcx>,
1343 ) -> bool {
1344 let expr = expr.peel_blocks();
1345
1346 if expr_ty.is_scalar() && expected_ty.is_scalar() {
1348 return false;
1349 }
1350
1351 if matches!(expr.kind, hir::ExprKind::Block(..)) {
1353 return false;
1354 }
1355
1356 if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() {
1359 return false;
1360 }
1361
1362 if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
1363 && self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
1364 self.tcx,
1365 self.misc(expr.span),
1366 self.param_env,
1367 ty::TraitRef::new(self.tcx, into_def_id, [expr_ty, expected_ty]),
1368 ))
1369 && !expr
1370 .span
1371 .macro_backtrace()
1372 .any(|x| matches!(x.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..)))
1373 {
1374 let span = expr.span.find_oldest_ancestor_in_same_ctxt();
1375
1376 let mut sugg = if expr.precedence() >= ExprPrecedence::Unambiguous {
1377 vec![(span.shrink_to_hi(), ".into()".to_owned())]
1378 } else {
1379 vec![
1380 (span.shrink_to_lo(), "(".to_owned()),
1381 (span.shrink_to_hi(), ").into()".to_owned()),
1382 ]
1383 };
1384 if let Some(name) = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
1385 sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
1386 }
1387 diag.multipart_suggestion(
1388 format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
1389 sugg,
1390 Applicability::MaybeIncorrect
1391 );
1392 return true;
1393 }
1394
1395 false
1396 }
1397
1398 pub(crate) fn suggest_option_to_bool(
1400 &self,
1401 diag: &mut Diag<'_>,
1402 expr: &hir::Expr<'_>,
1403 expr_ty: Ty<'tcx>,
1404 expected_ty: Ty<'tcx>,
1405 ) -> bool {
1406 if !expected_ty.is_bool() {
1407 return false;
1408 }
1409
1410 let ty::Adt(def, _) = expr_ty.peel_refs().kind() else {
1411 return false;
1412 };
1413 if !self.tcx.is_diagnostic_item(sym::Option, def.did()) {
1414 return false;
1415 }
1416
1417 let cond_parent = self.tcx.hir_parent_iter(expr.hir_id).find(|(_, node)| {
1418 !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
1419 });
1420 if let Some((_, hir::Node::LetStmt(local))) = cond_parent
1426 && let hir::PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
1427 | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind
1428 && let hir::QPath::Resolved(None, path) = qpath
1429 && let Some(did) = path
1430 .res
1431 .opt_def_id()
1432 .and_then(|did| self.tcx.opt_parent(did))
1433 .and_then(|did| self.tcx.opt_parent(did))
1434 && self.tcx.is_diagnostic_item(sym::Option, did)
1435 {
1436 return false;
1437 }
1438
1439 let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
1440 Some(ident) => format!(": {ident}.is_some()"),
1441 None => ".is_some()".to_string(),
1442 };
1443
1444 diag.span_suggestion_verbose(
1445 expr.span.shrink_to_hi(),
1446 "use `Option::is_some` to test if the `Option` has a value",
1447 suggestion,
1448 Applicability::MachineApplicable,
1449 );
1450 true
1451 }
1452
1453 #[instrument(level = "trace", skip(self, err, provided_expr))]
1455 pub(crate) fn suggest_deref_unwrap_or(
1456 &self,
1457 err: &mut Diag<'_>,
1458 callee_ty: Option<Ty<'tcx>>,
1459 call_ident: Option<Ident>,
1460 expected_ty: Ty<'tcx>,
1461 provided_ty: Ty<'tcx>,
1462 provided_expr: &Expr<'tcx>,
1463 is_method: bool,
1464 ) {
1465 if !is_method {
1466 return;
1467 }
1468 let Some(callee_ty) = callee_ty else {
1469 return;
1470 };
1471 let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
1472 return;
1473 };
1474 let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
1475 "Option"
1476 } else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) {
1477 "Result"
1478 } else {
1479 return;
1480 };
1481
1482 let Some(call_ident) = call_ident else {
1483 return;
1484 };
1485 if call_ident.name != sym::unwrap_or {
1486 return;
1487 }
1488
1489 let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
1490 return;
1491 };
1492
1493 let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
1497 && let ty::Infer(_) = elem_ty.kind()
1498 && self
1499 .try_structurally_resolve_const(provided_expr.span, *size)
1500 .try_to_target_usize(self.tcx)
1501 == Some(0)
1502 {
1503 let slice = Ty::new_slice(self.tcx, *elem_ty);
1504 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
1505 } else {
1506 provided_ty
1507 };
1508
1509 if !self.may_coerce(expected_ty, dummy_ty) {
1510 return;
1511 }
1512 let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
1513 err.multipart_suggestion_verbose(
1514 msg,
1515 vec![
1516 (call_ident.span, "map_or".to_owned()),
1517 (provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()),
1518 ],
1519 Applicability::MachineApplicable,
1520 );
1521 }
1522
1523 pub(crate) fn suggest_block_to_brackets(
1526 &self,
1527 diag: &mut Diag<'_>,
1528 blk: &hir::Block<'_>,
1529 blk_ty: Ty<'tcx>,
1530 expected_ty: Ty<'tcx>,
1531 ) {
1532 if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
1533 if self.may_coerce(blk_ty, *elem_ty)
1534 && blk.stmts.is_empty()
1535 && blk.rules == hir::BlockCheckMode::DefaultBlock
1536 && let source_map = self.tcx.sess.source_map()
1537 && let Ok(snippet) = source_map.span_to_snippet(blk.span)
1538 && snippet.starts_with('{')
1539 && snippet.ends_with('}')
1540 {
1541 diag.multipart_suggestion_verbose(
1542 "to create an array, use square brackets instead of curly braces",
1543 vec![
1544 (
1545 blk.span
1546 .shrink_to_lo()
1547 .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
1548 "[".to_string(),
1549 ),
1550 (
1551 blk.span
1552 .shrink_to_hi()
1553 .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
1554 "]".to_string(),
1555 ),
1556 ],
1557 Applicability::MachineApplicable,
1558 );
1559 }
1560 }
1561 }
1562
1563 #[instrument(skip(self, err))]
1564 pub(crate) fn suggest_floating_point_literal(
1565 &self,
1566 err: &mut Diag<'_>,
1567 expr: &hir::Expr<'_>,
1568 expected_ty: Ty<'tcx>,
1569 ) -> bool {
1570 if !expected_ty.is_floating_point() {
1571 return false;
1572 }
1573 match expr.kind {
1574 ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => {
1575 err.span_suggestion_verbose(
1576 start.span.shrink_to_hi().with_hi(end.span.lo()),
1577 "remove the unnecessary `.` operator for a floating point literal",
1578 '.',
1579 Applicability::MaybeIncorrect,
1580 );
1581 true
1582 }
1583 ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => {
1584 err.span_suggestion_verbose(
1585 expr.span.with_lo(start.span.hi()),
1586 "remove the unnecessary `.` operator for a floating point literal",
1587 '.',
1588 Applicability::MaybeIncorrect,
1589 );
1590 true
1591 }
1592 ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => {
1593 err.span_suggestion_verbose(
1594 expr.span.until(end.span),
1595 "remove the unnecessary `.` operator and add an integer part for a floating point literal",
1596 "0.",
1597 Applicability::MaybeIncorrect,
1598 );
1599 true
1600 }
1601 ExprKind::Lit(Spanned {
1602 node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed),
1603 span,
1604 }) => {
1605 let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(*span) else {
1606 return false;
1607 };
1608 if !(snippet.starts_with("0x") || snippet.starts_with("0X")) {
1609 return false;
1610 }
1611 if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) {
1612 return false;
1613 }
1614 let (_, suffix) = snippet.split_at(snippet.len() - 3);
1615 let value = match suffix {
1616 "f32" => (lit.get() - 0xf32) / (16 * 16 * 16),
1617 "f64" => (lit.get() - 0xf64) / (16 * 16 * 16),
1618 _ => return false,
1619 };
1620 err.span_suggestions(
1621 expr.span,
1622 "rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float",
1623 [format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")],
1624 Applicability::MaybeIncorrect,
1625 );
1626 true
1627 }
1628 _ => false,
1629 }
1630 }
1631
1632 #[instrument(skip(self, err))]
1635 pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg(
1636 &self,
1637 err: &mut Diag<'_>,
1638 expr: &hir::Expr<'_>,
1639 expected_ty: Ty<'tcx>,
1640 ) -> bool {
1641 let ty::RawPtr(_, mutbl) = expected_ty.kind() else {
1643 return false;
1644 };
1645
1646 let ExprKind::Lit(Spanned { node: rustc_ast::LitKind::Int(Pu128(0), _), span }) = expr.kind
1648 else {
1649 return false;
1650 };
1651
1652 let null_sym = match mutbl {
1654 hir::Mutability::Not => sym::ptr_null,
1655 hir::Mutability::Mut => sym::ptr_null_mut,
1656 };
1657 let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else {
1658 return false;
1659 };
1660 let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did));
1661
1662 err.span_suggestion(
1664 *span,
1665 format!("if you meant to create a null pointer, use `{null_path_str}()`"),
1666 null_path_str + "()",
1667 Applicability::MachineApplicable,
1668 );
1669
1670 true
1671 }
1672
1673 pub(crate) fn suggest_associated_const(
1674 &self,
1675 err: &mut Diag<'_>,
1676 expr: &hir::Expr<'tcx>,
1677 expected_ty: Ty<'tcx>,
1678 ) -> bool {
1679 let Some((DefKind::AssocFn, old_def_id)) =
1680 self.typeck_results.borrow().type_dependent_def(expr.hir_id)
1681 else {
1682 return false;
1683 };
1684 let old_item_name = self.tcx.item_name(old_def_id);
1685 let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
1686 if old_item_name == capitalized_name {
1687 return false;
1688 }
1689 let (item, segment) = match expr.kind {
1690 hir::ExprKind::Path(QPath::Resolved(
1691 Some(ty),
1692 hir::Path { segments: [segment], .. },
1693 ))
1694 | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => {
1695 if let Some(self_ty) = self.typeck_results.borrow().node_type_opt(ty.hir_id)
1696 && let Ok(pick) = self.probe_for_name(
1697 Mode::Path,
1698 Ident::new(capitalized_name, segment.ident.span),
1699 Some(expected_ty),
1700 IsSuggestion(true),
1701 self_ty,
1702 expr.hir_id,
1703 ProbeScope::TraitsInScope,
1704 )
1705 {
1706 (pick.item, segment)
1707 } else {
1708 return false;
1709 }
1710 }
1711 hir::ExprKind::Path(QPath::Resolved(
1712 None,
1713 hir::Path { segments: [.., segment], .. },
1714 )) => {
1715 if old_item_name != segment.ident.name {
1718 return false;
1719 }
1720 if let Some(item) = self
1721 .tcx
1722 .associated_items(self.tcx.parent(old_def_id))
1723 .filter_by_name_unhygienic(capitalized_name)
1724 .next()
1725 {
1726 (*item, segment)
1727 } else {
1728 return false;
1729 }
1730 }
1731 _ => return false,
1732 };
1733 if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst {
1734 return false;
1736 }
1737 let item_ty = self.tcx.type_of(item.def_id).instantiate_identity();
1738 if item_ty.has_param() {
1740 return false;
1741 }
1742 if self.may_coerce(item_ty, expected_ty) {
1743 err.span_suggestion_verbose(
1744 segment.ident.span,
1745 format!("try referring to the associated const `{capitalized_name}` instead",),
1746 capitalized_name,
1747 Applicability::MachineApplicable,
1748 );
1749 true
1750 } else {
1751 false
1752 }
1753 }
1754
1755 fn is_loop(&self, id: HirId) -> bool {
1756 let node = self.tcx.hir_node(id);
1757 matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
1758 }
1759
1760 fn is_local_statement(&self, id: HirId) -> bool {
1761 let node = self.tcx.hir_node(id);
1762 matches!(node, Node::Stmt(Stmt { kind: StmtKind::Let(..), .. }))
1763 }
1764
1765 pub(crate) fn note_type_is_not_clone(
1768 &self,
1769 diag: &mut Diag<'_>,
1770 expected_ty: Ty<'tcx>,
1771 found_ty: Ty<'tcx>,
1772 expr: &hir::Expr<'_>,
1773 ) {
1774 let expr = self.note_type_is_not_clone_inner_expr(expr);
1777
1778 let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else {
1780 return;
1781 };
1782
1783 let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else {
1784 return;
1785 };
1786 let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
1787 let results = self.typeck_results.borrow();
1788 if segment.ident.name == sym::clone
1790 && results.type_dependent_def_id(expr.hir_id).is_some_and(|did| {
1791 let assoc_item = self.tcx.associated_item(did);
1792 assoc_item.container == ty::AssocItemContainer::Trait
1793 && assoc_item.container_id(self.tcx) == clone_trait_did
1794 })
1795 && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
1798 && self.may_coerce(*pointee_ty, expected_ty)
1800 && let trait_ref = ty::TraitRef::new(self.tcx, clone_trait_did, [expected_ty])
1801 && !self.predicate_must_hold_considering_regions(&traits::Obligation::new(
1803 self.tcx,
1804 traits::ObligationCause::dummy(),
1805 self.param_env,
1806 trait_ref,
1807 ))
1808 {
1809 diag.span_note(
1810 callee_expr.span,
1811 format!(
1812 "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
1813 ),
1814 );
1815 let owner = self.tcx.hir_enclosing_body_owner(expr.hir_id);
1816 if let ty::Param(param) = expected_ty.kind()
1817 && let Some(generics) = self.tcx.hir_get_generics(owner)
1818 {
1819 suggest_constraining_type_params(
1820 self.tcx,
1821 generics,
1822 diag,
1823 vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
1824 None,
1825 );
1826 } else {
1827 if let Some(errors) =
1828 self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env)
1829 {
1830 match &errors[..] {
1831 [] => {}
1832 [error] => {
1833 diag.help(format!(
1834 "`Clone` is not implemented because the trait bound `{}` is \
1835 not satisfied",
1836 error.obligation.predicate,
1837 ));
1838 }
1839 _ => {
1840 diag.help(format!(
1841 "`Clone` is not implemented because the following trait bounds \
1842 could not be satisfied: {}",
1843 listify(&errors, |e| format!("`{}`", e.obligation.predicate))
1844 .unwrap(),
1845 ));
1846 }
1847 }
1848 for error in errors {
1849 if let traits::FulfillmentErrorCode::Select(
1850 traits::SelectionError::Unimplemented,
1851 ) = error.code
1852 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1853 error.obligation.predicate.kind().skip_binder()
1854 {
1855 self.infcx.err_ctxt().suggest_derive(
1856 &error.obligation,
1857 diag,
1858 error.obligation.predicate.kind().rebind(pred),
1859 );
1860 }
1861 }
1862 }
1863 self.suggest_derive(diag, &[(trait_ref.upcast(self.tcx), None, None)]);
1864 }
1865 }
1866 }
1867
1868 fn note_type_is_not_clone_inner_expr<'b>(
1873 &'b self,
1874 expr: &'b hir::Expr<'b>,
1875 ) -> &'b hir::Expr<'b> {
1876 match expr.peel_blocks().kind {
1877 hir::ExprKind::Path(hir::QPath::Resolved(
1878 None,
1879 hir::Path { segments: [_], res: crate::Res::Local(binding), .. },
1880 )) => {
1881 let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding) else {
1882 return expr;
1883 };
1884
1885 match self.tcx.parent_hir_node(*hir_id) {
1886 hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => {
1888 self.note_type_is_not_clone_inner_expr(init)
1889 }
1890 hir::Node::Pat(hir::Pat {
1892 hir_id: pat_hir_id,
1893 kind: hir::PatKind::Tuple(pats, ..),
1894 ..
1895 }) => {
1896 let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
1897 self.tcx.parent_hir_node(*pat_hir_id)
1898 else {
1899 return expr;
1900 };
1901
1902 match init.peel_blocks().kind {
1903 ExprKind::Tup(init_tup) => {
1904 if let Some(init) = pats
1905 .iter()
1906 .enumerate()
1907 .filter(|x| x.1.hir_id == *hir_id)
1908 .find_map(|(i, _)| init_tup.get(i))
1909 {
1910 self.note_type_is_not_clone_inner_expr(init)
1911 } else {
1912 expr
1913 }
1914 }
1915 _ => expr,
1916 }
1917 }
1918 _ => expr,
1919 }
1920 }
1921 hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => {
1925 if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) =
1926 call_expr_kind
1927 && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } =
1928 call_expr_path
1929 && let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding)
1930 && let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
1931 self.tcx.parent_hir_node(*hir_id)
1932 && let Expr {
1933 kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }),
1934 ..
1935 } = init
1936 {
1937 let hir::Body { value: body_expr, .. } = self.tcx.hir_body(*body_id);
1938 self.note_type_is_not_clone_inner_expr(body_expr)
1939 } else {
1940 expr
1941 }
1942 }
1943 _ => expr,
1944 }
1945 }
1946
1947 pub(crate) fn is_field_suggestable(
1948 &self,
1949 field: &ty::FieldDef,
1950 hir_id: HirId,
1951 span: Span,
1952 ) -> bool {
1953 field.vis.is_accessible_from(self.tcx.parent_module(hir_id), self.tcx)
1955 && !matches!(
1957 self.tcx.eval_stability(field.did, None, rustc_span::DUMMY_SP, None),
1958 rustc_middle::middle::stability::EvalResult::Deny { .. }
1959 )
1960 && (field.did.is_local() || !self.tcx.is_doc_hidden(field.did))
1962 && self.tcx.def_ident_span(field.did).unwrap().normalize_to_macros_2_0().eq_ctxt(span)
1964 }
1965
1966 pub(crate) fn suggest_missing_unwrap_expect(
1967 &self,
1968 err: &mut Diag<'_>,
1969 expr: &hir::Expr<'tcx>,
1970 expected: Ty<'tcx>,
1971 found: Ty<'tcx>,
1972 ) -> bool {
1973 let ty::Adt(adt, args) = found.kind() else {
1974 return false;
1975 };
1976 let ret_ty_matches = |diagnostic_item| {
1977 let Some(sig) = self.body_fn_sig() else {
1978 return false;
1979 };
1980 let ty::Adt(kind, _) = sig.output().kind() else {
1981 return false;
1982 };
1983 self.tcx.is_diagnostic_item(diagnostic_item, kind.did())
1984 };
1985
1986 let is_ctor = matches!(
1989 expr.kind,
1990 hir::ExprKind::Call(
1991 hir::Expr {
1992 kind: hir::ExprKind::Path(hir::QPath::Resolved(
1993 None,
1994 hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
1995 )),
1996 ..
1997 },
1998 ..,
1999 ) | hir::ExprKind::Path(hir::QPath::Resolved(
2000 None,
2001 hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
2002 )),
2003 );
2004
2005 let (article, kind, variant, sugg_operator) =
2006 if self.tcx.is_diagnostic_item(sym::Result, adt.did()) {
2007 ("a", "Result", "Err", ret_ty_matches(sym::Result))
2008 } else if self.tcx.is_diagnostic_item(sym::Option, adt.did()) {
2009 ("an", "Option", "None", ret_ty_matches(sym::Option))
2010 } else {
2011 return false;
2012 };
2013 if is_ctor || !self.may_coerce(args.type_at(0), expected) {
2014 return false;
2015 }
2016
2017 let (msg, sugg) = if sugg_operator {
2018 (
2019 format!(
2020 "use the `?` operator to extract the `{found}` value, propagating \
2021 {article} `{kind}::{variant}` value to the caller"
2022 ),
2023 "?",
2024 )
2025 } else {
2026 (
2027 format!(
2028 "consider using `{kind}::expect` to unwrap the `{found}` value, \
2029 panicking if the value is {article} `{kind}::{variant}`"
2030 ),
2031 ".expect(\"REASON\")",
2032 )
2033 };
2034
2035 let sugg = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
2036 Some(ident) => format!(": {ident}{sugg}"),
2037 None => sugg.to_string(),
2038 };
2039
2040 let span = expr.span.find_oldest_ancestor_in_same_ctxt();
2041 err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders);
2042 true
2043 }
2044
2045 pub(crate) fn suggest_coercing_result_via_try_operator(
2046 &self,
2047 err: &mut Diag<'_>,
2048 expr: &hir::Expr<'tcx>,
2049 expected: Ty<'tcx>,
2050 found: Ty<'tcx>,
2051 ) -> bool {
2052 let returned = matches!(
2053 self.tcx.parent_hir_node(expr.hir_id),
2054 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })
2055 ) || self.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_some();
2056 if returned
2057 && let ty::Adt(e, args_e) = expected.kind()
2058 && let ty::Adt(f, args_f) = found.kind()
2059 && e.did() == f.did()
2060 && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
2061 && let e_ok = args_e.type_at(0)
2062 && let f_ok = args_f.type_at(0)
2063 && self.infcx.can_eq(self.param_env, f_ok, e_ok)
2064 && let e_err = args_e.type_at(1)
2065 && let f_err = args_f.type_at(1)
2066 && self
2067 .infcx
2068 .type_implements_trait(
2069 self.tcx.get_diagnostic_item(sym::Into).unwrap(),
2070 [f_err, e_err],
2071 self.param_env,
2072 )
2073 .must_apply_modulo_regions()
2074 {
2075 err.multipart_suggestion(
2076 "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
2077 in `Ok` so the expression remains of type `Result`",
2078 vec![
2079 (expr.span.shrink_to_lo(), "Ok(".to_string()),
2080 (expr.span.shrink_to_hi(), "?)".to_string()),
2081 ],
2082 Applicability::MaybeIncorrect,
2083 );
2084 return true;
2085 }
2086 false
2087 }
2088
2089 pub(crate) fn suggest_returning_value_after_loop(
2092 &self,
2093 err: &mut Diag<'_>,
2094 expr: &hir::Expr<'tcx>,
2095 expected: Ty<'tcx>,
2096 ) -> bool {
2097 let tcx = self.tcx;
2098 let enclosing_scope =
2099 tcx.hir_get_enclosing_scope(expr.hir_id).map(|hir_id| tcx.hir_node(hir_id));
2100
2101 let tail_expr = if let Some(Node::Block(hir::Block { expr, .. })) = enclosing_scope
2103 && expr.is_some()
2104 {
2105 *expr
2106 } else {
2107 let body_def_id = tcx.hir_enclosing_body_owner(expr.hir_id);
2108 let body = tcx.hir_body_owned_by(body_def_id);
2109
2110 match body.value.kind {
2112 hir::ExprKind::Block(block, _) => block.expr,
2114 hir::ExprKind::DropTemps(expr) => Some(expr),
2116 _ => None,
2117 }
2118 };
2119
2120 let Some(tail_expr) = tail_expr else {
2121 return false; };
2123
2124 let loop_expr_in_tail = match expr.kind {
2126 hir::ExprKind::Loop(_, _, hir::LoopSource::While, _) => tail_expr,
2127 hir::ExprKind::Loop(_, _, hir::LoopSource::ForLoop, _) => {
2128 match tail_expr.peel_drop_temps() {
2129 Expr { kind: ExprKind::Match(_, [Arm { body, .. }], _), .. } => body,
2130 _ => return false, }
2132 }
2133 _ => return false, };
2135
2136 if expr.hir_id == loop_expr_in_tail.hir_id {
2139 let span = expr.span;
2140
2141 let (msg, suggestion) = if expected.is_never() {
2142 (
2143 "consider adding a diverging expression here",
2144 "`loop {}` or `panic!(\"...\")`".to_string(),
2145 )
2146 } else {
2147 ("consider returning a value here", format!("`{expected}` value"))
2148 };
2149
2150 let src_map = tcx.sess.source_map();
2151 let suggestion = if src_map.is_multiline(expr.span) {
2152 let indentation = src_map.indentation_before(span).unwrap_or_else(String::new);
2153 format!("\n{indentation}/* {suggestion} */")
2154 } else {
2155 format!(" /* {suggestion} */")
2158 };
2159
2160 err.span_suggestion_verbose(
2161 span.shrink_to_hi(),
2162 msg,
2163 suggestion,
2164 Applicability::MaybeIncorrect,
2165 );
2166
2167 true
2168 } else {
2169 false
2170 }
2171 }
2172
2173 pub(crate) fn suggest_semicolon_in_repeat_expr(
2176 &self,
2177 err: &mut Diag<'_>,
2178 expr: &hir::Expr<'_>,
2179 expr_ty: Ty<'tcx>,
2180 ) -> bool {
2181 if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id)
2183 && let hir::ExprKind::Array(elements) = array_expr.kind
2184 && let [first, second] = &elements[..]
2185 && second.hir_id == expr.hir_id
2186 {
2187 let comma_span = first.span.between(second.span);
2189
2190 let expr_is_const_usize = expr_ty.is_usize()
2198 && match expr.kind {
2199 ExprKind::Path(QPath::Resolved(
2200 None,
2201 Path { res: Res::Def(DefKind::Const, _), .. },
2202 )) => true,
2203 ExprKind::Call(
2204 Expr {
2205 kind:
2206 ExprKind::Path(QPath::Resolved(
2207 None,
2208 Path { res: Res::Def(DefKind::Fn, fn_def_id), .. },
2209 )),
2210 ..
2211 },
2212 _,
2213 ) => self.tcx.is_const_fn(*fn_def_id),
2214 _ => false,
2215 };
2216
2217 let first_ty = self.typeck_results.borrow().expr_ty(first);
2222
2223 if self.tcx.sess.source_map().is_imported(array_expr.span)
2228 && self.type_is_clone_modulo_regions(self.param_env, first_ty)
2229 && (expr.is_size_lit() || expr_ty.is_usize_like())
2230 {
2231 err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2232 comma_span,
2233 descr: "a vector",
2234 });
2235 return true;
2236 }
2237
2238 if self.type_is_copy_modulo_regions(self.param_env, first_ty)
2242 && (expr.is_size_lit() || expr_is_const_usize)
2243 {
2244 err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2245 comma_span,
2246 descr: "an array",
2247 });
2248 return true;
2249 }
2250 }
2251 false
2252 }
2253
2254 pub(crate) fn suggest_compatible_variants(
2257 &self,
2258 err: &mut Diag<'_>,
2259 expr: &hir::Expr<'_>,
2260 expected: Ty<'tcx>,
2261 expr_ty: Ty<'tcx>,
2262 ) -> bool {
2263 if expr.span.in_external_macro(self.tcx.sess.source_map()) {
2264 return false;
2265 }
2266 if let ty::Adt(expected_adt, args) = expected.kind() {
2267 if let hir::ExprKind::Field(base, ident) = expr.kind {
2268 let base_ty = self.typeck_results.borrow().expr_ty(base);
2269 if self.can_eq(self.param_env, base_ty, expected)
2270 && let Some(base_span) = base.span.find_ancestor_inside(expr.span)
2271 {
2272 err.span_suggestion_verbose(
2273 expr.span.with_lo(base_span.hi()),
2274 format!("consider removing the tuple struct field `{ident}`"),
2275 "",
2276 Applicability::MaybeIncorrect,
2277 );
2278 return true;
2279 }
2280 }
2281
2282 if expr_ty.is_unit() {
2286 let mut id = expr.hir_id;
2287 let mut parent;
2288
2289 loop {
2291 parent = self.tcx.parent_hir_id(id);
2292 let parent_span = self.tcx.hir().span(parent);
2293 if parent_span.find_ancestor_inside(expr.span).is_some() {
2294 id = parent;
2297 continue;
2298 }
2299 break;
2300 }
2301
2302 if let hir::Node::Block(&hir::Block { span: block_span, expr: Some(e), .. }) =
2303 self.tcx.hir_node(parent)
2304 {
2305 if e.hir_id == id {
2306 if let Some(span) = expr.span.find_ancestor_inside(block_span) {
2307 let return_suggestions = if self
2308 .tcx
2309 .is_diagnostic_item(sym::Result, expected_adt.did())
2310 {
2311 vec!["Ok(())"]
2312 } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
2313 vec!["None", "Some(())"]
2314 } else {
2315 return false;
2316 };
2317 if let Some(indent) =
2318 self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
2319 {
2320 let semicolon =
2322 match self.tcx.sess.source_map().span_to_snippet(span) {
2323 Ok(s) if s.ends_with('}') => "",
2324 _ => ";",
2325 };
2326 err.span_suggestions(
2327 span.shrink_to_hi(),
2328 "try adding an expression at the end of the block",
2329 return_suggestions
2330 .into_iter()
2331 .map(|r| format!("{semicolon}\n{indent}{r}")),
2332 Applicability::MaybeIncorrect,
2333 );
2334 }
2335 return true;
2336 }
2337 }
2338 }
2339 }
2340
2341 let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
2342 .variants()
2343 .iter()
2344 .filter(|variant| {
2345 variant.fields.len() == 1
2346 })
2347 .filter_map(|variant| {
2348 let sole_field = &variant.single_field();
2349
2350 let field_is_local = sole_field.did.is_local();
2351 let field_is_accessible =
2352 sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
2353 && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
2355
2356 if !field_is_local && !field_is_accessible {
2357 return None;
2358 }
2359
2360 let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
2361 .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
2362
2363 let sole_field_ty = sole_field.ty(self.tcx, args);
2364 if self.may_coerce(expr_ty, sole_field_ty) {
2365 let variant_path =
2366 with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
2367 if let Some(path) = variant_path.strip_prefix("std::prelude::")
2369 && let Some((_, path)) = path.split_once("::")
2370 {
2371 return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy));
2372 }
2373 Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy))
2374 } else {
2375 None
2376 }
2377 })
2378 .collect();
2379
2380 let suggestions_for = |variant: &_, ctor_kind, field_name| {
2381 let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
2382 Some(ident) => format!("{ident}: "),
2383 None => String::new(),
2384 };
2385
2386 let (open, close) = match ctor_kind {
2387 Some(CtorKind::Fn) => ("(".to_owned(), ")"),
2388 None => (format!(" {{ {field_name}: "), " }"),
2389
2390 Some(CtorKind::Const) => unreachable!("unit variants don't have fields"),
2391 };
2392
2393 let mut expr = expr;
2397 while let hir::ExprKind::Block(block, _) = &expr.kind
2398 && let Some(expr_) = &block.expr
2399 {
2400 expr = expr_
2401 }
2402
2403 vec![
2404 (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
2405 (expr.span.shrink_to_hi(), close.to_owned()),
2406 ]
2407 };
2408
2409 match &compatible_variants[..] {
2410 [] => { }
2411 [(variant, ctor_kind, field_name, note)] => {
2412 err.multipart_suggestion_verbose(
2414 format!(
2415 "try wrapping the expression in `{variant}`{note}",
2416 note = note.as_deref().unwrap_or("")
2417 ),
2418 suggestions_for(&**variant, *ctor_kind, *field_name),
2419 Applicability::MaybeIncorrect,
2420 );
2421 return true;
2422 }
2423 _ => {
2424 err.multipart_suggestions(
2426 format!(
2427 "try wrapping the expression in a variant of `{}`",
2428 self.tcx.def_path_str(expected_adt.did())
2429 ),
2430 compatible_variants.into_iter().map(
2431 |(variant, ctor_kind, field_name, _)| {
2432 suggestions_for(&variant, ctor_kind, field_name)
2433 },
2434 ),
2435 Applicability::MaybeIncorrect,
2436 );
2437 return true;
2438 }
2439 }
2440 }
2441
2442 false
2443 }
2444
2445 pub(crate) fn suggest_non_zero_new_unwrap(
2446 &self,
2447 err: &mut Diag<'_>,
2448 expr: &hir::Expr<'_>,
2449 expected: Ty<'tcx>,
2450 expr_ty: Ty<'tcx>,
2451 ) -> bool {
2452 let tcx = self.tcx;
2453 let (adt, args, unwrap) = match expected.kind() {
2454 ty::Adt(adt, args) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
2456 let nonzero_type = args.type_at(0); let ty::Adt(adt, args) = nonzero_type.kind() else {
2458 return false;
2459 };
2460 (adt, args, "")
2461 }
2462 ty::Adt(adt, args) => (adt, args, ".unwrap()"),
2464 _ => return false,
2465 };
2466
2467 if !self.tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
2468 return false;
2469 }
2470
2471 let int_type = args.type_at(0);
2472 if !self.may_coerce(expr_ty, int_type) {
2473 return false;
2474 }
2475
2476 err.multipart_suggestion(
2477 format!("consider calling `{}::new`", sym::NonZero),
2478 vec![
2479 (expr.span.shrink_to_lo(), format!("{}::new(", sym::NonZero)),
2480 (expr.span.shrink_to_hi(), format!("){unwrap}")),
2481 ],
2482 Applicability::MaybeIncorrect,
2483 );
2484
2485 true
2486 }
2487
2488 fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> {
2505 let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind else {
2506 return None;
2507 };
2508
2509 let hir::def::Res::Local(local_id) = path.res else {
2510 return None;
2511 };
2512
2513 let Node::Param(hir::Param { hir_id: param_hir_id, .. }) =
2514 self.tcx.parent_hir_node(local_id)
2515 else {
2516 return None;
2517 };
2518
2519 let Node::Expr(hir::Expr {
2520 hir_id: expr_hir_id,
2521 kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
2522 ..
2523 }) = self.tcx.parent_hir_node(*param_hir_id)
2524 else {
2525 return None;
2526 };
2527
2528 let hir = self.tcx.parent_hir_node(*expr_hir_id);
2529 let closure_params_len = closure_fn_decl.inputs.len();
2530 let (
2531 Node::Expr(hir::Expr {
2532 kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
2533 ..
2534 }),
2535 1,
2536 ) = (hir, closure_params_len)
2537 else {
2538 return None;
2539 };
2540
2541 let self_ty = self.typeck_results.borrow().expr_ty(receiver);
2542 let name = method_path.ident.name;
2543 let is_as_ref_able = match self_ty.peel_refs().kind() {
2544 ty::Adt(def, _) => {
2545 (self.tcx.is_diagnostic_item(sym::Option, def.did())
2546 || self.tcx.is_diagnostic_item(sym::Result, def.did()))
2547 && (name == sym::map || name == sym::and_then)
2548 }
2549 _ => false,
2550 };
2551 if is_as_ref_able {
2552 Some((
2553 vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())],
2554 "consider using `as_ref` instead",
2555 ))
2556 } else {
2557 None
2558 }
2559 }
2560
2561 pub(crate) fn suggest_deref_or_ref(
2578 &self,
2579 expr: &hir::Expr<'tcx>,
2580 checked_ty: Ty<'tcx>,
2581 expected: Ty<'tcx>,
2582 ) -> Option<(
2583 Vec<(Span, String)>,
2584 String,
2585 Applicability,
2586 bool, bool, )> {
2589 let sess = self.sess();
2590 let sp = expr.span;
2591 let sm = sess.source_map();
2592
2593 if sp.in_external_macro(sm) {
2595 return None;
2596 }
2597
2598 let replace_prefix = |s: &str, old: &str, new: &str| {
2599 s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
2600 };
2601
2602 let expr = expr.peel_drop_temps();
2604
2605 match (&expr.kind, expected.kind(), checked_ty.kind()) {
2606 (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
2607 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
2608 if let hir::ExprKind::Lit(_) = expr.kind
2609 && let Ok(src) = sm.span_to_snippet(sp)
2610 && replace_prefix(&src, "b\"", "\"").is_some()
2611 {
2612 let pos = sp.lo() + BytePos(1);
2613 return Some((
2614 vec![(sp.with_hi(pos), String::new())],
2615 "consider removing the leading `b`".to_string(),
2616 Applicability::MachineApplicable,
2617 true,
2618 false,
2619 ));
2620 }
2621 }
2622 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
2623 if let hir::ExprKind::Lit(_) = expr.kind
2624 && let Ok(src) = sm.span_to_snippet(sp)
2625 && replace_prefix(&src, "\"", "b\"").is_some()
2626 {
2627 return Some((
2628 vec![(sp.shrink_to_lo(), "b".to_string())],
2629 "consider adding a leading `b`".to_string(),
2630 Applicability::MachineApplicable,
2631 true,
2632 false,
2633 ));
2634 }
2635 }
2636 _ => {}
2637 },
2638 (_, &ty::Ref(_, _, mutability), _) => {
2639 let ref_ty = match mutability {
2648 hir::Mutability::Mut => {
2649 Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2650 }
2651 hir::Mutability::Not => {
2652 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2653 }
2654 };
2655 if self.may_coerce(ref_ty, expected) {
2656 let mut sugg_sp = sp;
2657 if let hir::ExprKind::MethodCall(segment, receiver, args, _) = expr.kind {
2658 let clone_trait =
2659 self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span));
2660 if args.is_empty()
2661 && self
2662 .typeck_results
2663 .borrow()
2664 .type_dependent_def_id(expr.hir_id)
2665 .is_some_and(|did| {
2666 let ai = self.tcx.associated_item(did);
2667 ai.trait_container(self.tcx) == Some(clone_trait)
2668 })
2669 && segment.ident.name == sym::clone
2670 {
2671 sugg_sp = receiver.span;
2674 }
2675 }
2676
2677 if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
2678 && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty)
2679 && self.typeck_results.borrow().expr_ty(inner).is_ref()
2680 {
2681 sugg_sp = sugg_sp.with_hi(inner.span.lo());
2684 return Some((
2685 vec![(sugg_sp, String::new())],
2686 "consider removing deref here".to_string(),
2687 Applicability::MachineApplicable,
2688 true,
2689 false,
2690 ));
2691 }
2692
2693 if let Some((sugg, msg)) = self.can_use_as_ref(expr) {
2694 return Some((
2695 sugg,
2696 msg.to_string(),
2697 Applicability::MachineApplicable,
2698 true,
2699 false,
2700 ));
2701 }
2702
2703 let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr)
2704 {
2705 Some(ident) => format!("{ident}: "),
2706 None => String::new(),
2707 };
2708
2709 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(..), .. }) =
2710 self.tcx.parent_hir_node(expr.hir_id)
2711 {
2712 if mutability.is_mut() {
2713 return None;
2715 }
2716 }
2717
2718 let make_sugg = |expr: &Expr<'_>, span: Span, sugg: &str| {
2719 if expr_needs_parens(expr) {
2720 (
2721 vec![
2722 (span.shrink_to_lo(), format!("{prefix}{sugg}(")),
2723 (span.shrink_to_hi(), ")".to_string()),
2724 ],
2725 false,
2726 )
2727 } else {
2728 (vec![(span.shrink_to_lo(), format!("{prefix}{sugg}"))], true)
2729 }
2730 };
2731
2732 if let hir::Node::Expr(hir::Expr {
2734 kind: hir::ExprKind::Binary(_, lhs, ..),
2735 ..
2736 }) = self.tcx.parent_hir_node(expr.hir_id)
2737 && let &ty::Ref(..) = self.check_expr(lhs).kind()
2738 {
2739 let (sugg, verbose) = make_sugg(lhs, lhs.span, "*");
2740
2741 return Some((
2742 sugg,
2743 "consider dereferencing the borrow".to_string(),
2744 Applicability::MachineApplicable,
2745 verbose,
2746 false,
2747 ));
2748 }
2749
2750 let sugg = mutability.ref_prefix_str();
2751 let (sugg, verbose) = make_sugg(expr, sp, sugg);
2752 return Some((
2753 sugg,
2754 format!("consider {}borrowing here", mutability.mutably_str()),
2755 Applicability::MachineApplicable,
2756 verbose,
2757 false,
2758 ));
2759 }
2760 }
2761 (hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr), _, &ty::Ref(_, checked, _))
2762 if self.can_eq(self.param_env, checked, expected) =>
2763 {
2764 let make_sugg = |start: Span, end: BytePos| {
2765 let sp = sm
2768 .span_extend_while(start.shrink_to_lo(), |c| c == '(' || c.is_whitespace())
2769 .map_or(start, |s| s.shrink_to_hi());
2770 Some((
2771 vec![(sp.with_hi(end), String::new())],
2772 "consider removing the borrow".to_string(),
2773 Applicability::MachineApplicable,
2774 true,
2775 true,
2776 ))
2777 };
2778
2779 if sm.is_imported(expr.span) {
2782 if let Some(call_span) =
2788 iter::successors(Some(expr.span), |s| s.parent_callsite())
2789 .find(|&s| sp.contains(s))
2790 && sm.is_span_accessible(call_span)
2791 {
2792 return make_sugg(sp, call_span.lo());
2793 }
2794 return None;
2795 }
2796 if sp.contains(expr.span) && sm.is_span_accessible(expr.span) {
2797 return make_sugg(sp, expr.span.lo());
2798 }
2799 }
2800 (_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => {
2801 if let Some(steps) = self.deref_steps_for_suggestion(ty_a, ty_b)
2802 && steps > 0
2804 && let Ok(src) = sm.span_to_snippet(sp)
2806 {
2807 let derefs = "*".repeat(steps);
2808 let old_prefix = mutbl_a.ref_prefix_str();
2809 let new_prefix = mutbl_b.ref_prefix_str().to_owned() + &derefs;
2810
2811 let suggestion = replace_prefix(&src, old_prefix, &new_prefix).map(|_| {
2812 let lo = sp.lo()
2814 + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _);
2815 let hi = sp.lo() + BytePos(old_prefix.len() as _);
2817 let sp = sp.with_lo(lo).with_hi(hi);
2818
2819 (
2820 sp,
2821 format!(
2822 "{}{derefs}",
2823 if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }
2824 ),
2825 if mutbl_b <= mutbl_a {
2826 Applicability::MachineApplicable
2827 } else {
2828 Applicability::MaybeIncorrect
2829 },
2830 )
2831 });
2832
2833 if let Some((span, src, applicability)) = suggestion {
2834 return Some((
2835 vec![(span, src)],
2836 "consider dereferencing".to_string(),
2837 applicability,
2838 true,
2839 false,
2840 ));
2841 }
2842 }
2843 }
2844 _ if sp == expr.span => {
2845 if let Some(mut steps) = self.deref_steps_for_suggestion(checked_ty, expected) {
2846 let mut expr = expr.peel_blocks();
2847 let mut prefix_span = expr.span.shrink_to_lo();
2848 let mut remove = String::new();
2849
2850 while steps > 0 {
2852 if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
2853 prefix_span = prefix_span.with_hi(inner.span.lo());
2855 expr = inner;
2856 remove.push_str(mutbl.ref_prefix_str());
2857 steps -= 1;
2858 } else {
2859 break;
2860 }
2861 }
2862 if steps == 0 && !remove.trim().is_empty() {
2864 return Some((
2865 vec![(prefix_span, String::new())],
2866 format!("consider removing the `{}`", remove.trim()),
2867 if remove.trim() == "&&" && expected == self.tcx.types.bool {
2871 Applicability::MaybeIncorrect
2872 } else {
2873 Applicability::MachineApplicable
2874 },
2875 true,
2876 false,
2877 ));
2878 }
2879
2880 if self.type_is_copy_modulo_regions(self.param_env, expected)
2883 || (checked_ty.is_box() && steps == 1)
2886 || matches!(
2888 self.tcx.parent_hir_node(expr.hir_id),
2889 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, ..), .. })
2890 if !op.node.is_by_value()
2891 )
2892 {
2893 let deref_kind = if checked_ty.is_box() {
2894 "unboxing the value"
2895 } else if checked_ty.is_ref() {
2896 "dereferencing the borrow"
2897 } else {
2898 "dereferencing the type"
2899 };
2900
2901 let message = if remove.is_empty() {
2904 format!("consider {deref_kind}")
2905 } else {
2906 format!(
2907 "consider removing the `{}` and {} instead",
2908 remove.trim(),
2909 deref_kind
2910 )
2911 };
2912
2913 let prefix =
2914 match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
2915 Some(ident) => format!("{ident}: "),
2916 None => String::new(),
2917 };
2918
2919 let (span, suggestion) = if self.is_else_if_block(expr) {
2920 return None;
2922 } else if let Some(expr) = self.maybe_get_block_expr(expr) {
2923 (expr.span.shrink_to_lo(), "*".to_string())
2925 } else {
2926 (prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
2927 };
2928 if suggestion.trim().is_empty() {
2929 return None;
2930 }
2931
2932 if expr_needs_parens(expr) {
2933 return Some((
2934 vec![
2935 (span, format!("{suggestion}(")),
2936 (expr.span.shrink_to_hi(), ")".to_string()),
2937 ],
2938 message,
2939 Applicability::MachineApplicable,
2940 true,
2941 false,
2942 ));
2943 }
2944
2945 return Some((
2946 vec![(span, suggestion)],
2947 message,
2948 Applicability::MachineApplicable,
2949 true,
2950 false,
2951 ));
2952 }
2953 }
2954 }
2955 _ => {}
2956 }
2957 None
2958 }
2959
2960 fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
2962 if let hir::ExprKind::If(..) = expr.kind {
2963 if let Node::Expr(hir::Expr {
2964 kind: hir::ExprKind::If(_, _, Some(else_expr)), ..
2965 }) = self.tcx.parent_hir_node(expr.hir_id)
2966 {
2967 return else_expr.hir_id == expr.hir_id;
2968 }
2969 }
2970 false
2971 }
2972
2973 pub(crate) fn suggest_cast(
2974 &self,
2975 err: &mut Diag<'_>,
2976 expr: &hir::Expr<'_>,
2977 checked_ty: Ty<'tcx>,
2978 expected_ty: Ty<'tcx>,
2979 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
2980 ) -> bool {
2981 if self.tcx.sess.source_map().is_imported(expr.span) {
2982 return false;
2984 }
2985
2986 let span = if let hir::ExprKind::Lit(lit) = &expr.kind { lit.span } else { expr.span };
2987 let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) else {
2988 return false;
2989 };
2990
2991 let can_cast = false;
3000
3001 let mut sugg = vec![];
3002
3003 if let hir::Node::ExprField(field) = self.tcx.parent_hir_node(expr.hir_id) {
3004 if field.is_shorthand {
3006 sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
3008 } else {
3009 return false;
3011 }
3012 };
3013
3014 if let hir::ExprKind::Call(path, args) = &expr.kind
3015 && let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
3016 (&path.kind, args.len())
3017 && let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
3019 (&base_ty.kind, path_segment.ident.name)
3020 {
3021 if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
3022 match ident.name {
3023 sym::i128
3024 | sym::i64
3025 | sym::i32
3026 | sym::i16
3027 | sym::i8
3028 | sym::u128
3029 | sym::u64
3030 | sym::u32
3031 | sym::u16
3032 | sym::u8
3033 | sym::isize
3034 | sym::usize
3035 if base_ty_path.segments.len() == 1 =>
3036 {
3037 return false;
3038 }
3039 _ => {}
3040 }
3041 }
3042 }
3043
3044 let msg = format!(
3045 "you can convert {} `{}` to {} `{}`",
3046 checked_ty.kind().article(),
3047 checked_ty,
3048 expected_ty.kind().article(),
3049 expected_ty,
3050 );
3051 let cast_msg = format!(
3052 "you can cast {} `{}` to {} `{}`",
3053 checked_ty.kind().article(),
3054 checked_ty,
3055 expected_ty.kind().article(),
3056 expected_ty,
3057 );
3058 let lit_msg = format!(
3059 "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
3060 );
3061
3062 let close_paren = if expr.precedence() < ExprPrecedence::Unambiguous {
3063 sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
3064 ")"
3065 } else {
3066 ""
3067 };
3068
3069 let mut cast_suggestion = sugg.clone();
3070 cast_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren} as {expected_ty}")));
3071 let mut into_suggestion = sugg.clone();
3072 into_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren}.into()")));
3073 let mut suffix_suggestion = sugg.clone();
3074 suffix_suggestion.push((
3075 if matches!(
3076 (expected_ty.kind(), checked_ty.kind()),
3077 (ty::Int(_) | ty::Uint(_), ty::Float(_))
3078 ) {
3079 let src = src.trim_end_matches(&checked_ty.to_string());
3081 let len = src.split('.').next().unwrap().len();
3082 span.with_lo(span.lo() + BytePos(len as u32))
3083 } else {
3084 let len = src.trim_end_matches(&checked_ty.to_string()).len();
3085 span.with_lo(span.lo() + BytePos(len as u32))
3086 },
3087 if expr.precedence() < ExprPrecedence::Unambiguous {
3088 format!("{expected_ty})")
3090 } else {
3091 expected_ty.to_string()
3092 },
3093 ));
3094 let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
3095 if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
3096 };
3097 let is_negative_int =
3098 |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..));
3099 let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
3100
3101 let in_const_context = self.tcx.hir_is_inside_const_context(expr.hir_id);
3102
3103 let suggest_fallible_into_or_lhs_from =
3104 |err: &mut Diag<'_>, exp_to_found_is_fallible: bool| {
3105 let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
3112 self.tcx
3113 .sess
3114 .source_map()
3115 .span_to_snippet(expr.span)
3116 .ok()
3117 .map(|src| (expr, src))
3118 });
3119 let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
3120 (lhs_expr_and_src, exp_to_found_is_fallible)
3121 {
3122 let msg = format!(
3123 "you can convert `{lhs_src}` from `{expected_ty}` to `{checked_ty}`, matching the type of `{src}`",
3124 );
3125 let suggestion = vec![
3126 (lhs_expr.span.shrink_to_lo(), format!("{checked_ty}::from(")),
3127 (lhs_expr.span.shrink_to_hi(), ")".to_string()),
3128 ];
3129 (msg, suggestion)
3130 } else {
3131 let msg =
3132 format!("{} and panic if the converted value doesn't fit", msg.clone());
3133 let mut suggestion = sugg.clone();
3134 suggestion.push((
3135 expr.span.shrink_to_hi(),
3136 format!("{close_paren}.try_into().unwrap()"),
3137 ));
3138 (msg, suggestion)
3139 };
3140 err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
3141 };
3142
3143 let suggest_to_change_suffix_or_into =
3144 |err: &mut Diag<'_>, found_to_exp_is_fallible: bool, exp_to_found_is_fallible: bool| {
3145 let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir_is_lhs(e.hir_id));
3146
3147 if exp_is_lhs {
3148 return;
3149 }
3150
3151 let always_fallible = found_to_exp_is_fallible
3152 && (exp_to_found_is_fallible || expected_ty_expr.is_none());
3153 let msg = if literal_is_ty_suffixed(expr) {
3154 lit_msg.clone()
3155 } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
3156 let msg = format!("`{src}` cannot fit into type `{expected_ty}`");
3160 err.note(msg);
3161 return;
3162 } else if in_const_context {
3163 return;
3165 } else if found_to_exp_is_fallible {
3166 return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
3167 } else {
3168 msg.clone()
3169 };
3170 let suggestion = if literal_is_ty_suffixed(expr) {
3171 suffix_suggestion.clone()
3172 } else {
3173 into_suggestion.clone()
3174 };
3175 err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
3176 };
3177
3178 match (expected_ty.kind(), checked_ty.kind()) {
3179 (ty::Int(exp), ty::Int(found)) => {
3180 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3181 {
3182 (Some(exp), Some(found)) if exp < found => (true, false),
3183 (Some(exp), Some(found)) if exp > found => (false, true),
3184 (None, Some(8 | 16)) => (false, true),
3185 (Some(8 | 16), None) => (true, false),
3186 (None, _) | (_, None) => (true, true),
3187 _ => (false, false),
3188 };
3189 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3190 true
3191 }
3192 (ty::Uint(exp), ty::Uint(found)) => {
3193 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3194 {
3195 (Some(exp), Some(found)) if exp < found => (true, false),
3196 (Some(exp), Some(found)) if exp > found => (false, true),
3197 (None, Some(8 | 16)) => (false, true),
3198 (Some(8 | 16), None) => (true, false),
3199 (None, _) | (_, None) => (true, true),
3200 _ => (false, false),
3201 };
3202 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3203 true
3204 }
3205 (&ty::Int(exp), &ty::Uint(found)) => {
3206 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3207 {
3208 (Some(exp), Some(found)) if found < exp => (false, true),
3209 (None, Some(8)) => (false, true),
3210 _ => (true, true),
3211 };
3212 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3213 true
3214 }
3215 (&ty::Uint(exp), &ty::Int(found)) => {
3216 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3217 {
3218 (Some(exp), Some(found)) if found > exp => (true, false),
3219 (Some(8), None) => (true, false),
3220 _ => (true, true),
3221 };
3222 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3223 true
3224 }
3225 (ty::Float(exp), ty::Float(found)) => {
3226 if found.bit_width() < exp.bit_width() {
3227 suggest_to_change_suffix_or_into(err, false, true);
3228 } else if literal_is_ty_suffixed(expr) {
3229 err.multipart_suggestion_verbose(
3230 lit_msg,
3231 suffix_suggestion,
3232 Applicability::MachineApplicable,
3233 );
3234 } else if can_cast {
3235 err.multipart_suggestion_verbose(
3237 format!("{cast_msg}, producing the closest possible value"),
3238 cast_suggestion,
3239 Applicability::MaybeIncorrect, );
3241 }
3242 true
3243 }
3244 (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
3245 if literal_is_ty_suffixed(expr) {
3246 err.multipart_suggestion_verbose(
3247 lit_msg,
3248 suffix_suggestion,
3249 Applicability::MachineApplicable,
3250 );
3251 } else if can_cast {
3252 err.multipart_suggestion_verbose(
3254 format!("{msg}, rounding the float towards zero"),
3255 cast_suggestion,
3256 Applicability::MaybeIncorrect, );
3258 }
3259 true
3260 }
3261 (ty::Float(exp), ty::Uint(found)) => {
3262 if exp.bit_width() > found.bit_width().unwrap_or(256) {
3264 err.multipart_suggestion_verbose(
3265 format!(
3266 "{msg}, producing the floating point representation of the integer",
3267 ),
3268 into_suggestion,
3269 Applicability::MachineApplicable,
3270 );
3271 } else if literal_is_ty_suffixed(expr) {
3272 err.multipart_suggestion_verbose(
3273 lit_msg,
3274 suffix_suggestion,
3275 Applicability::MachineApplicable,
3276 );
3277 } else {
3278 err.multipart_suggestion_verbose(
3280 format!(
3281 "{cast_msg}, producing the floating point representation of the integer, \
3282 rounded if necessary",
3283 ),
3284 cast_suggestion,
3285 Applicability::MaybeIncorrect, );
3287 }
3288 true
3289 }
3290 (ty::Float(exp), ty::Int(found)) => {
3291 if exp.bit_width() > found.bit_width().unwrap_or(256) {
3293 err.multipart_suggestion_verbose(
3294 format!(
3295 "{}, producing the floating point representation of the integer",
3296 msg.clone(),
3297 ),
3298 into_suggestion,
3299 Applicability::MachineApplicable,
3300 );
3301 } else if literal_is_ty_suffixed(expr) {
3302 err.multipart_suggestion_verbose(
3303 lit_msg,
3304 suffix_suggestion,
3305 Applicability::MachineApplicable,
3306 );
3307 } else {
3308 err.multipart_suggestion_verbose(
3310 format!(
3311 "{}, producing the floating point representation of the integer, \
3312 rounded if necessary",
3313 &msg,
3314 ),
3315 cast_suggestion,
3316 Applicability::MaybeIncorrect, );
3318 }
3319 true
3320 }
3321 (
3322 &ty::Uint(ty::UintTy::U32 | ty::UintTy::U64 | ty::UintTy::U128)
3323 | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128),
3324 &ty::Char,
3325 ) => {
3326 err.multipart_suggestion_verbose(
3327 format!("{cast_msg}, since a `char` always occupies 4 bytes"),
3328 cast_suggestion,
3329 Applicability::MachineApplicable,
3330 );
3331 true
3332 }
3333 _ => false,
3334 }
3335 }
3336
3337 pub(crate) fn suggest_method_call_on_range_literal(
3339 &self,
3340 err: &mut Diag<'_>,
3341 expr: &hir::Expr<'tcx>,
3342 checked_ty: Ty<'tcx>,
3343 expected_ty: Ty<'tcx>,
3344 ) {
3345 if !hir::is_range_literal(expr) {
3346 return;
3347 }
3348 let hir::ExprKind::Struct(hir::QPath::LangItem(LangItem::Range, ..), [start, end], _) =
3349 expr.kind
3350 else {
3351 return;
3352 };
3353 if let hir::Node::ExprField(_) = self.tcx.parent_hir_node(expr.hir_id) {
3354 return;
3356 }
3357 let mut expr = end.expr;
3358 let mut expectation = Some(expected_ty);
3359 while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
3360 expr = rcvr;
3363 expectation = None;
3366 }
3367 let hir::ExprKind::Call(method_name, _) = expr.kind else {
3368 return;
3369 };
3370 let ty::Adt(adt, _) = checked_ty.kind() else {
3371 return;
3372 };
3373 if self.tcx.lang_items().range_struct() != Some(adt.did()) {
3374 return;
3375 }
3376 if let ty::Adt(adt, _) = expected_ty.kind()
3377 && self.tcx.is_lang_item(adt.did(), LangItem::Range)
3378 {
3379 return;
3380 }
3381 let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else {
3383 return;
3384 };
3385 let [hir::PathSegment { ident, .. }] = p.segments else {
3386 return;
3387 };
3388 let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
3389 let Ok(_pick) = self.lookup_probe_for_diagnostic(
3390 *ident,
3391 self_ty,
3392 expr,
3393 probe::ProbeScope::AllTraits,
3394 expectation,
3395 ) else {
3396 return;
3397 };
3398 let mut sugg = ".";
3399 let mut span = start.expr.span.between(end.expr.span);
3400 if span.lo() + BytePos(2) == span.hi() {
3401 span = span.with_lo(span.lo() + BytePos(1));
3404 sugg = "";
3405 }
3406 err.span_suggestion_verbose(
3407 span,
3408 "you likely meant to write a method call instead of a range",
3409 sugg,
3410 Applicability::MachineApplicable,
3411 );
3412 }
3413
3414 pub(crate) fn suggest_return_binding_for_missing_tail_expr(
3417 &self,
3418 err: &mut Diag<'_>,
3419 expr: &hir::Expr<'_>,
3420 checked_ty: Ty<'tcx>,
3421 expected_ty: Ty<'tcx>,
3422 ) {
3423 if !checked_ty.is_unit() {
3424 return;
3425 }
3426 let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {
3427 return;
3428 };
3429 let hir::def::Res::Local(hir_id) = path.res else {
3430 return;
3431 };
3432 let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else {
3433 return;
3434 };
3435 let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) =
3436 self.tcx.parent_hir_node(pat.hir_id)
3437 else {
3438 return;
3439 };
3440 let hir::ExprKind::Block(block, None) = init.kind else {
3441 return;
3442 };
3443 if block.expr.is_some() {
3444 return;
3445 }
3446 let [.., stmt] = block.stmts else {
3447 err.span_label(block.span, "this empty block is missing a tail expression");
3448 return;
3449 };
3450 let hir::StmtKind::Semi(tail_expr) = stmt.kind else {
3451 return;
3452 };
3453 let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else {
3454 return;
3455 };
3456 if self.can_eq(self.param_env, expected_ty, ty)
3457 && stmt.span.hi() != tail_expr.span.hi()
3462 {
3463 err.span_suggestion_short(
3464 stmt.span.with_lo(tail_expr.span.hi()),
3465 "remove this semicolon",
3466 "",
3467 Applicability::MachineApplicable,
3468 );
3469 } else {
3470 err.span_label(block.span, "this block is missing a tail expression");
3471 }
3472 }
3473
3474 pub(crate) fn suggest_swapping_lhs_and_rhs(
3475 &self,
3476 err: &mut Diag<'_>,
3477 rhs_ty: Ty<'tcx>,
3478 lhs_ty: Ty<'tcx>,
3479 rhs_expr: &'tcx hir::Expr<'tcx>,
3480 lhs_expr: &'tcx hir::Expr<'tcx>,
3481 op: hir::BinOp,
3482 ) {
3483 match op.node {
3484 hir::BinOpKind::Eq => {
3485 if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
3486 && self
3487 .infcx
3488 .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
3489 .must_apply_modulo_regions()
3490 {
3491 let sm = self.tcx.sess.source_map();
3492 if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
3493 && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
3494 {
3495 err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
3496 err.multipart_suggestion(
3497 "consider swapping the equality",
3498 vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
3499 Applicability::MaybeIncorrect,
3500 );
3501 }
3502 }
3503 }
3504 _ => {}
3505 }
3506 }
3507}