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 conversion_method_name = conversion_method.name();
385 let receiver_method_ident = expr.method_ident();
386 if let Some(method_ident) = receiver_method_ident
387 && method_ident.name == conversion_method_name
388 {
389 return None; }
391
392 let method_call_list = [sym::to_vec, sym::to_string];
393 let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
394 && receiver_method.ident.name == sym::clone
395 && method_call_list.contains(&conversion_method_name)
396 {
401 vec![(receiver_method.ident.span, conversion_method_name.to_string())]
402 } else if expr.precedence() < ExprPrecedence::Unambiguous {
403 vec![
404 (expr.span.shrink_to_lo(), "(".to_string()),
405 (expr.span.shrink_to_hi(), format!(").{}()", conversion_method_name)),
406 ]
407 } else {
408 vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method_name))]
409 };
410 let struct_pat_shorthand_field =
411 self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr);
412 if let Some(name) = struct_pat_shorthand_field {
413 sugg.insert(0, (expr.span.shrink_to_lo(), format!("{name}: ")));
414 }
415 Some(sugg)
416 })
417 .peekable();
418 if suggestions.peek().is_some() {
419 err.multipart_suggestions(
420 "try using a conversion method",
421 suggestions,
422 Applicability::MaybeIncorrect,
423 );
424 return true;
425 }
426 }
427
428 if let Some((found_ty_inner, expected_ty_inner, error_tys)) =
429 self.deconstruct_option_or_result(found, expected)
430 && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind()
431 {
432 let inner_expr = expr.peel_borrows();
434 if !inner_expr.span.eq_ctxt(expr.span) {
435 return false;
436 }
437 let borrow_removal_span = if inner_expr.hir_id == expr.hir_id {
438 None
439 } else {
440 Some(expr.span.shrink_to_lo().until(inner_expr.span))
441 };
442 let error_tys_equate_as_ref = error_tys.is_none_or(|(found, expected)| {
445 self.can_eq(
446 self.param_env,
447 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, found),
448 expected,
449 )
450 });
451
452 let prefix_wrap = |sugg: &str| {
453 if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
454 format!(": {}{}", name, sugg)
455 } else {
456 sugg.to_string()
457 }
458 };
459
460 if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
463 let sugg = prefix_wrap(".as_ref()");
464 err.subdiagnostic(errors::SuggestConvertViaMethod {
465 span: expr.span.shrink_to_hi(),
466 sugg,
467 expected,
468 found,
469 borrow_removal_span,
470 });
471 return true;
472 } else if let ty::Ref(_, peeled_found_ty, _) = found_ty_inner.kind()
473 && let ty::Adt(adt, _) = peeled_found_ty.peel_refs().kind()
474 && self.tcx.is_lang_item(adt.did(), LangItem::String)
475 && peeled.is_str()
476 && error_tys.is_none_or(|(found, expected)| {
478 self.can_eq(self.param_env, found, expected)
479 })
480 {
481 let sugg = prefix_wrap(".map(|x| x.as_str())");
482 err.span_suggestion_verbose(
483 expr.span.shrink_to_hi(),
484 fluent::hir_typeck_convert_to_str,
485 sugg,
486 Applicability::MachineApplicable,
487 );
488 return true;
489 } else {
490 if !error_tys_equate_as_ref {
491 return false;
492 }
493 let mut steps = self.autoderef(expr.span, found_ty_inner).silence_errors();
494 if let Some((deref_ty, _)) = steps.nth(1)
495 && self.can_eq(self.param_env, deref_ty, peeled)
496 {
497 let sugg = prefix_wrap(".as_deref()");
498 err.subdiagnostic(errors::SuggestConvertViaMethod {
499 span: expr.span.shrink_to_hi(),
500 sugg,
501 expected,
502 found,
503 borrow_removal_span,
504 });
505 return true;
506 }
507 for (deref_ty, n_step) in steps {
508 if self.can_eq(self.param_env, deref_ty, peeled) {
509 let explicit_deref = "*".repeat(n_step);
510 let sugg = prefix_wrap(&format!(".map(|v| &{explicit_deref}v)"));
511 err.subdiagnostic(errors::SuggestConvertViaMethod {
512 span: expr.span.shrink_to_hi(),
513 sugg,
514 expected,
515 found,
516 borrow_removal_span,
517 });
518 return true;
519 }
520 }
521 }
522 }
523
524 false
525 }
526
527 fn deconstruct_option_or_result(
531 &self,
532 found_ty: Ty<'tcx>,
533 expected_ty: Ty<'tcx>,
534 ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> {
535 let ty::Adt(found_adt, found_args) = found_ty.peel_refs().kind() else {
536 return None;
537 };
538 let ty::Adt(expected_adt, expected_args) = expected_ty.kind() else {
539 return None;
540 };
541 if self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
542 && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
543 {
544 Some((found_args.type_at(0), expected_args.type_at(0), None))
545 } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did())
546 && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did())
547 {
548 Some((
549 found_args.type_at(0),
550 expected_args.type_at(0),
551 Some((found_args.type_at(1), expected_args.type_at(1))),
552 ))
553 } else {
554 None
555 }
556 }
557
558 pub(in super::super) fn suggest_boxing_when_appropriate(
561 &self,
562 err: &mut Diag<'_>,
563 span: Span,
564 hir_id: HirId,
565 expected: Ty<'tcx>,
566 found: Ty<'tcx>,
567 ) -> bool {
568 if self.tcx.hir_is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
570 return false;
571 }
572 if self.may_coerce(Ty::new_box(self.tcx, found), expected) {
573 let suggest_boxing = match found.kind() {
574 ty::Tuple(tuple) if tuple.is_empty() => {
575 errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
576 }
577 ty::Coroutine(def_id, ..)
578 if matches!(
579 self.tcx.coroutine_kind(def_id),
580 Some(CoroutineKind::Desugared(
581 CoroutineDesugaring::Async,
582 CoroutineSource::Closure
583 ))
584 ) =>
585 {
586 errors::SuggestBoxing::AsyncBody
587 }
588 _ => errors::SuggestBoxing::Other {
589 start: span.shrink_to_lo(),
590 end: span.shrink_to_hi(),
591 },
592 };
593 err.subdiagnostic(suggest_boxing);
594
595 true
596 } else {
597 false
598 }
599 }
600
601 pub(in super::super) fn suggest_no_capture_closure(
604 &self,
605 err: &mut Diag<'_>,
606 expected: Ty<'tcx>,
607 found: Ty<'tcx>,
608 ) -> bool {
609 if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
610 if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
611 let spans_and_labels = upvars
614 .iter()
615 .take(4)
616 .map(|(var_hir_id, upvar)| {
617 let var_name = self.tcx.hir_name(*var_hir_id).to_string();
618 let msg = format!("`{var_name}` captured here");
619 (upvar.span, msg)
620 })
621 .collect::<Vec<_>>();
622
623 let mut multi_span: MultiSpan =
624 spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
625 for (sp, label) in spans_and_labels {
626 multi_span.push_span_label(sp, label);
627 }
628 err.span_note(
629 multi_span,
630 "closures can only be coerced to `fn` types if they do not capture any variables"
631 );
632 return true;
633 }
634 }
635 false
636 }
637
638 #[instrument(skip(self, err))]
640 pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
641 &self,
642 err: &mut Diag<'_>,
643 expr: &hir::Expr<'_>,
644 expected: Ty<'tcx>,
645 found: Ty<'tcx>,
646 ) -> bool {
647 if self.tcx.hir_is_inside_const_context(expr.hir_id) {
650 return false;
652 }
653 let pin_did = self.tcx.lang_items().pin_type();
654 if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
656 return false;
657 }
658 let box_found = Ty::new_box(self.tcx, found);
659 let Some(pin_box_found) = Ty::new_lang_item(self.tcx, box_found, LangItem::Pin) else {
660 return false;
661 };
662 let Some(pin_found) = Ty::new_lang_item(self.tcx, found, LangItem::Pin) else {
663 return false;
664 };
665 match expected.kind() {
666 ty::Adt(def, _) if Some(def.did()) == pin_did => {
667 if self.may_coerce(pin_box_found, expected) {
668 debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
669 match found.kind() {
670 ty::Adt(def, _) if def.is_box() => {
671 err.help("use `Box::pin`");
672 }
673 _ => {
674 let prefix = if let Some(name) =
675 self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr)
676 {
677 format!("{}: ", name)
678 } else {
679 String::new()
680 };
681 let suggestion = vec![
682 (expr.span.shrink_to_lo(), format!("{prefix}Box::pin(")),
683 (expr.span.shrink_to_hi(), ")".to_string()),
684 ];
685 err.multipart_suggestion(
686 "you need to pin and box this expression",
687 suggestion,
688 Applicability::MaybeIncorrect,
689 );
690 }
691 }
692 true
693 } else if self.may_coerce(pin_found, expected) {
694 match found.kind() {
695 ty::Adt(def, _) if def.is_box() => {
696 err.help("use `Box::pin`");
697 true
698 }
699 _ => false,
700 }
701 } else {
702 false
703 }
704 }
705 ty::Adt(def, _) if def.is_box() && self.may_coerce(box_found, expected) => {
706 let Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. }) =
710 self.tcx.parent_hir_node(expr.hir_id)
711 else {
712 return false;
713 };
714 match fn_name.kind {
715 ExprKind::Path(QPath::TypeRelative(
716 hir::Ty {
717 kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })),
718 ..
719 },
720 method,
721 )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => {
722 err.span_suggestion(
723 fn_name.span,
724 "use `Box::pin` to pin and box this expression",
725 "Box::pin",
726 Applicability::MachineApplicable,
727 );
728 true
729 }
730 _ => false,
731 }
732 }
733 _ => false,
734 }
735 }
736
737 pub(crate) fn suggest_missing_semicolon(
753 &self,
754 err: &mut Diag<'_>,
755 expression: &'tcx hir::Expr<'tcx>,
756 expected: Ty<'tcx>,
757 needs_block: bool,
758 ) {
759 if expected.is_unit() {
760 match expression.kind {
763 ExprKind::Call(..)
764 | ExprKind::MethodCall(..)
765 | ExprKind::Loop(..)
766 | ExprKind::If(..)
767 | ExprKind::Match(..)
768 | ExprKind::Block(..)
769 if expression.can_have_side_effects()
770 && !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
774 {
775 if needs_block {
776 err.multipart_suggestion(
777 "consider using a semicolon here",
778 vec![
779 (expression.span.shrink_to_lo(), "{ ".to_owned()),
780 (expression.span.shrink_to_hi(), "; }".to_owned()),
781 ],
782 Applicability::MachineApplicable,
783 );
784 } else {
785 err.span_suggestion(
786 expression.span.shrink_to_hi(),
787 "consider using a semicolon here",
788 ";",
789 Applicability::MachineApplicable,
790 );
791 }
792 }
793 _ => (),
794 }
795 }
796 }
797
798 #[instrument(level = "trace", skip(self, err))]
811 pub(in super::super) fn suggest_missing_return_type(
812 &self,
813 err: &mut Diag<'_>,
814 fn_decl: &hir::FnDecl<'tcx>,
815 expected: Ty<'tcx>,
816 found: Ty<'tcx>,
817 fn_id: LocalDefId,
818 ) -> bool {
819 if let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Block)) =
821 self.tcx.coroutine_kind(fn_id)
822 {
823 return false;
824 }
825
826 let found =
827 self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
828 match &fn_decl.output {
831 &hir::FnRetTy::DefaultReturn(_) if self.tcx.is_closure_like(fn_id.to_def_id()) => {}
833 &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
834 if !self.can_add_return_type(fn_id) {
835 err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span });
836 } else if let Some(found) = found.make_suggestable(self.tcx, false, None) {
837 err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
838 span,
839 found: found.to_string(),
840 });
841 } else if let Some(sugg) = suggest_impl_trait(self, self.param_env, found) {
842 err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: sugg });
843 } else {
844 err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span });
846 }
847
848 return true;
849 }
850 hir::FnRetTy::Return(hir_ty) => {
851 if let hir::TyKind::OpaqueDef(op_ty, ..) = hir_ty.kind
852 && let [hir::GenericBound::Trait(trait_ref)] = op_ty.bounds
854 && let Some(hir::PathSegment { args: Some(generic_args), .. }) =
855 trait_ref.trait_ref.path.segments.last()
856 && let [constraint] = generic_args.constraints
857 && let Some(ty) = constraint.ty()
858 {
859 debug!(?found);
862 if found.is_suggestable(self.tcx, false) {
863 if ty.span.is_empty() {
864 err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
865 span: ty.span,
866 found: found.to_string(),
867 });
868 return true;
869 } else {
870 err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
871 span: ty.span,
872 expected,
873 });
874 }
875 }
876 } else {
877 debug!(?hir_ty, "return type");
880 let ty = self.lowerer().lower_ty(hir_ty);
881 debug!(?ty, "return type (lowered)");
882 debug!(?expected, "expected type");
883 let bound_vars =
884 self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
885 let ty = Binder::bind_with_vars(ty, bound_vars);
886 let ty = self.normalize(hir_ty.span, ty);
887 let ty = self.tcx.instantiate_bound_regions_with_erased(ty);
888 if self.may_coerce(expected, ty) {
889 err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
890 span: hir_ty.span,
891 expected,
892 });
893 self.try_suggest_return_impl_trait(err, expected, found, fn_id);
894 self.try_note_caller_chooses_ty_for_ty_param(err, expected, found);
895 return true;
896 }
897 }
898 }
899 _ => {}
900 }
901 false
902 }
903
904 fn can_add_return_type(&self, fn_id: LocalDefId) -> bool {
907 match self.tcx.hir_node_by_def_id(fn_id) {
908 Node::Item(item) => {
909 let (ident, _, _, _) = item.expect_fn();
910 ident.name != sym::main
914 }
915 Node::ImplItem(item) => {
916 let Node::Item(&hir::Item {
918 kind: hir::ItemKind::Impl(&hir::Impl { of_trait, .. }),
919 ..
920 }) = self.tcx.parent_hir_node(item.hir_id())
921 else {
922 unreachable!();
923 };
924
925 of_trait.is_none()
926 }
927 _ => true,
928 }
929 }
930
931 fn try_note_caller_chooses_ty_for_ty_param(
932 &self,
933 diag: &mut Diag<'_>,
934 expected: Ty<'tcx>,
935 found: Ty<'tcx>,
936 ) {
937 let ty::Param(expected_ty_as_param) = expected.kind() else {
944 return;
945 };
946
947 if found.contains(expected) {
948 return;
949 }
950
951 diag.subdiagnostic(errors::NoteCallerChoosesTyForTyParam {
952 ty_param_name: expected_ty_as_param.name,
953 found_ty: found,
954 });
955 }
956
957 fn try_suggest_return_impl_trait(
966 &self,
967 err: &mut Diag<'_>,
968 expected: Ty<'tcx>,
969 found: Ty<'tcx>,
970 fn_id: LocalDefId,
971 ) {
972 debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found);
981
982 let ty::Param(expected_ty_as_param) = expected.kind() else { return };
983
984 let fn_node = self.tcx.hir_node_by_def_id(fn_id);
985
986 let hir::Node::Item(hir::Item {
987 kind:
988 hir::ItemKind::Fn {
989 sig:
990 hir::FnSig {
991 decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. },
992 ..
993 },
994 generics: hir::Generics { params, predicates, .. },
995 ..
996 },
997 ..
998 }) = fn_node
999 else {
1000 return;
1001 };
1002
1003 if params.get(expected_ty_as_param.index as usize).is_none() {
1004 return;
1005 };
1006
1007 let where_predicates = predicates
1009 .iter()
1010 .filter_map(|p| match p.kind {
1011 WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
1012 bounds,
1013 bounded_ty,
1014 ..
1015 }) => {
1016 let ty = self.lowerer().lower_ty(bounded_ty);
1018 Some((ty, bounds))
1019 }
1020 _ => None,
1021 })
1022 .map(|(ty, bounds)| match ty.kind() {
1023 ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
1024 _ => match ty.contains(expected) {
1026 true => Err(()),
1027 false => Ok(None),
1028 },
1029 })
1030 .collect::<Result<Vec<_>, _>>();
1031
1032 let Ok(where_predicates) = where_predicates else { return };
1033
1034 let predicates_from_where =
1036 where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
1037
1038 let all_matching_bounds_strs = predicates_from_where
1040 .filter_map(|bound| match bound {
1041 GenericBound::Trait(_) => {
1042 self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
1043 }
1044 _ => None,
1045 })
1046 .collect::<Vec<String>>();
1047
1048 if all_matching_bounds_strs.len() == 0 {
1049 return;
1050 }
1051
1052 let all_bounds_str = all_matching_bounds_strs.join(" + ");
1053
1054 let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
1055 let ty = self.lowerer().lower_ty( param);
1056 matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
1057 });
1058
1059 if ty_param_used_in_fn_params {
1060 return;
1061 }
1062
1063 err.span_suggestion(
1064 fn_return.span(),
1065 "consider using an impl return type",
1066 format!("impl {all_bounds_str}"),
1067 Applicability::MaybeIncorrect,
1068 );
1069 }
1070
1071 pub(in super::super) fn suggest_missing_break_or_return_expr(
1072 &self,
1073 err: &mut Diag<'_>,
1074 expr: &'tcx hir::Expr<'tcx>,
1075 fn_decl: &hir::FnDecl<'tcx>,
1076 expected: Ty<'tcx>,
1077 found: Ty<'tcx>,
1078 id: HirId,
1079 fn_id: LocalDefId,
1080 ) {
1081 if !expected.is_unit() {
1082 return;
1083 }
1084 let found = self.resolve_vars_if_possible(found);
1085
1086 let in_loop = self.is_loop(id)
1087 || self
1088 .tcx
1089 .hir_parent_iter(id)
1090 .take_while(|(_, node)| {
1091 node.body_id().is_none()
1093 })
1094 .any(|(parent_id, _)| self.is_loop(parent_id));
1095
1096 let in_local_statement = self.is_local_statement(id)
1097 || self
1098 .tcx
1099 .hir_parent_iter(id)
1100 .any(|(parent_id, _)| self.is_local_statement(parent_id));
1101
1102 if in_loop && in_local_statement {
1103 err.multipart_suggestion(
1104 "you might have meant to break the loop with this value",
1105 vec![
1106 (expr.span.shrink_to_lo(), "break ".to_string()),
1107 (expr.span.shrink_to_hi(), ";".to_string()),
1108 ],
1109 Applicability::MaybeIncorrect,
1110 );
1111 return;
1112 }
1113
1114 let scope = self.tcx.hir_parent_iter(id).find(|(_, node)| {
1115 matches!(
1116 node,
1117 Node::Expr(Expr { kind: ExprKind::Closure(..), .. })
1118 | Node::Item(_)
1119 | Node::TraitItem(_)
1120 | Node::ImplItem(_)
1121 )
1122 });
1123 let in_closure =
1124 matches!(scope, Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. }))));
1125
1126 let can_return = match fn_decl.output {
1127 hir::FnRetTy::Return(ty) => {
1128 let ty = self.lowerer().lower_ty(ty);
1129 let bound_vars = self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
1130 let ty = self
1131 .tcx
1132 .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
1133 let ty = match self.tcx.asyncness(fn_id) {
1134 ty::Asyncness::Yes => {
1135 self.err_ctxt().get_impl_future_output_ty(ty).unwrap_or_else(|| {
1136 span_bug!(
1137 fn_decl.output.span(),
1138 "failed to get output type of async function"
1139 )
1140 })
1141 }
1142 ty::Asyncness::No => ty,
1143 };
1144 let ty = self.normalize(expr.span, ty);
1145 self.may_coerce(found, ty)
1146 }
1147 hir::FnRetTy::DefaultReturn(_) if in_closure => {
1148 self.ret_coercion.as_ref().is_some_and(|ret| {
1149 let ret_ty = ret.borrow().expected_ty();
1150 self.may_coerce(found, ret_ty)
1151 })
1152 }
1153 _ => false,
1154 };
1155 if can_return
1156 && let Some(span) = expr.span.find_ancestor_inside(
1157 self.tcx.hir_span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)),
1158 )
1159 {
1160 fn is_in_arm<'tcx>(expr: &'tcx hir::Expr<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
1171 for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
1172 match node {
1173 hir::Node::Block(block) => {
1174 if let Some(ret) = block.expr
1175 && ret.hir_id == expr.hir_id
1176 {
1177 continue;
1178 }
1179 }
1180 hir::Node::Arm(arm) => {
1181 if let hir::ExprKind::Block(block, _) = arm.body.kind
1182 && let Some(ret) = block.expr
1183 && ret.hir_id == expr.hir_id
1184 {
1185 return true;
1186 }
1187 }
1188 hir::Node::Expr(e) if let hir::ExprKind::Block(block, _) = e.kind => {
1189 if let Some(ret) = block.expr
1190 && ret.hir_id == expr.hir_id
1191 {
1192 continue;
1193 }
1194 }
1195 _ => {
1196 return false;
1197 }
1198 }
1199 }
1200
1201 false
1202 }
1203 let mut suggs = vec![(span.shrink_to_lo(), "return ".to_string())];
1204 if !is_in_arm(expr, self.tcx) {
1205 suggs.push((span.shrink_to_hi(), ";".to_string()));
1206 }
1207 err.multipart_suggestion_verbose(
1208 "you might have meant to return this value",
1209 suggs,
1210 Applicability::MaybeIncorrect,
1211 );
1212 }
1213 }
1214
1215 pub(in super::super) fn suggest_missing_parentheses(
1216 &self,
1217 err: &mut Diag<'_>,
1218 expr: &hir::Expr<'_>,
1219 ) -> bool {
1220 let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None);
1221 if let Some(sp) = self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
1222 err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
1224 true
1225 } else {
1226 false
1227 }
1228 }
1229
1230 pub(crate) fn suggest_block_to_brackets_peeling_refs(
1234 &self,
1235 diag: &mut Diag<'_>,
1236 mut expr: &hir::Expr<'_>,
1237 mut expr_ty: Ty<'tcx>,
1238 mut expected_ty: Ty<'tcx>,
1239 ) -> bool {
1240 loop {
1241 match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
1242 (
1243 hir::ExprKind::AddrOf(_, _, inner_expr),
1244 ty::Ref(_, inner_expr_ty, _),
1245 ty::Ref(_, inner_expected_ty, _),
1246 ) => {
1247 expr = *inner_expr;
1248 expr_ty = *inner_expr_ty;
1249 expected_ty = *inner_expected_ty;
1250 }
1251 (hir::ExprKind::Block(blk, _), _, _) => {
1252 self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
1253 break true;
1254 }
1255 _ => break false,
1256 }
1257 }
1258 }
1259
1260 pub(crate) fn suggest_clone_for_ref(
1261 &self,
1262 diag: &mut Diag<'_>,
1263 expr: &hir::Expr<'_>,
1264 expr_ty: Ty<'tcx>,
1265 expected_ty: Ty<'tcx>,
1266 ) -> bool {
1267 if let ty::Ref(_, inner_ty, hir::Mutability::Not) = expr_ty.kind()
1268 && let Some(clone_trait_def) = self.tcx.lang_items().clone_trait()
1269 && expected_ty == *inner_ty
1270 && self
1271 .infcx
1272 .type_implements_trait(
1273 clone_trait_def,
1274 [self.tcx.erase_regions(expected_ty)],
1275 self.param_env,
1276 )
1277 .must_apply_modulo_regions()
1278 {
1279 let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1280 Some(ident) => format!(": {ident}.clone()"),
1281 None => ".clone()".to_string(),
1282 };
1283
1284 diag.span_suggestion_verbose(
1285 expr.span.shrink_to_hi(),
1286 "consider using clone here",
1287 suggestion,
1288 Applicability::MachineApplicable,
1289 );
1290 return true;
1291 }
1292 false
1293 }
1294
1295 pub(crate) fn suggest_copied_cloned_or_as_ref(
1296 &self,
1297 diag: &mut Diag<'_>,
1298 expr: &hir::Expr<'_>,
1299 expr_ty: Ty<'tcx>,
1300 expected_ty: Ty<'tcx>,
1301 ) -> bool {
1302 let ty::Adt(adt_def, args) = expr_ty.kind() else {
1303 return false;
1304 };
1305 let ty::Adt(expected_adt_def, expected_args) = expected_ty.kind() else {
1306 return false;
1307 };
1308 if adt_def != expected_adt_def {
1309 return false;
1310 }
1311
1312 if Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Result)
1313 && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
1314 || Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Option)
1315 {
1316 let expr_inner_ty = args.type_at(0);
1317 let expected_inner_ty = expected_args.type_at(0);
1318 if let &ty::Ref(_, ty, _mutability) = expr_inner_ty.kind()
1319 && self.can_eq(self.param_env, ty, expected_inner_ty)
1320 {
1321 let def_path = self.tcx.def_path_str(adt_def.did());
1322 let span = expr.span.shrink_to_hi();
1323 let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
1324 errors::OptionResultRefMismatch::Copied { span, def_path }
1325 } else if self.type_is_clone_modulo_regions(self.param_env, ty) {
1326 errors::OptionResultRefMismatch::Cloned { span, def_path }
1327 } else {
1328 return false;
1329 };
1330 diag.subdiagnostic(subdiag);
1331 return true;
1332 }
1333 }
1334
1335 false
1336 }
1337
1338 pub(crate) fn suggest_into(
1339 &self,
1340 diag: &mut Diag<'_>,
1341 expr: &hir::Expr<'_>,
1342 expr_ty: Ty<'tcx>,
1343 expected_ty: Ty<'tcx>,
1344 ) -> bool {
1345 let expr = expr.peel_blocks();
1346
1347 if expr_ty.is_scalar() && expected_ty.is_scalar() {
1349 return false;
1350 }
1351
1352 if matches!(expr.kind, hir::ExprKind::Block(..)) {
1354 return false;
1355 }
1356
1357 if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() {
1360 return false;
1361 }
1362
1363 if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
1364 && self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
1365 self.tcx,
1366 self.misc(expr.span),
1367 self.param_env,
1368 ty::TraitRef::new(self.tcx, into_def_id, [expr_ty, expected_ty]),
1369 ))
1370 && !expr
1371 .span
1372 .macro_backtrace()
1373 .any(|x| matches!(x.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..)))
1374 {
1375 let span = expr.span.find_oldest_ancestor_in_same_ctxt();
1376
1377 let mut sugg = if expr.precedence() >= ExprPrecedence::Unambiguous {
1378 vec![(span.shrink_to_hi(), ".into()".to_owned())]
1379 } else {
1380 vec![
1381 (span.shrink_to_lo(), "(".to_owned()),
1382 (span.shrink_to_hi(), ").into()".to_owned()),
1383 ]
1384 };
1385 if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1386 sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
1387 }
1388 diag.multipart_suggestion(
1389 format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
1390 sugg,
1391 Applicability::MaybeIncorrect
1392 );
1393 return true;
1394 }
1395
1396 false
1397 }
1398
1399 pub(crate) fn suggest_option_to_bool(
1401 &self,
1402 diag: &mut Diag<'_>,
1403 expr: &hir::Expr<'_>,
1404 expr_ty: Ty<'tcx>,
1405 expected_ty: Ty<'tcx>,
1406 ) -> bool {
1407 if !expected_ty.is_bool() {
1408 return false;
1409 }
1410
1411 let ty::Adt(def, _) = expr_ty.peel_refs().kind() else {
1412 return false;
1413 };
1414 if !self.tcx.is_diagnostic_item(sym::Option, def.did()) {
1415 return false;
1416 }
1417
1418 let cond_parent = self.tcx.hir_parent_iter(expr.hir_id).find(|(_, node)| {
1419 !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
1420 });
1421 if let Some((_, hir::Node::LetStmt(local))) = cond_parent
1427 && let hir::PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
1428 | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind
1429 && let hir::QPath::Resolved(None, path) = qpath
1430 && let Some(did) = path
1431 .res
1432 .opt_def_id()
1433 .and_then(|did| self.tcx.opt_parent(did))
1434 .and_then(|did| self.tcx.opt_parent(did))
1435 && self.tcx.is_diagnostic_item(sym::Option, did)
1436 {
1437 return false;
1438 }
1439
1440 let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1441 Some(ident) => format!(": {ident}.is_some()"),
1442 None => ".is_some()".to_string(),
1443 };
1444
1445 diag.span_suggestion_verbose(
1446 expr.span.shrink_to_hi(),
1447 "use `Option::is_some` to test if the `Option` has a value",
1448 suggestion,
1449 Applicability::MachineApplicable,
1450 );
1451 true
1452 }
1453
1454 #[instrument(level = "trace", skip(self, err, provided_expr))]
1456 pub(crate) fn suggest_deref_unwrap_or(
1457 &self,
1458 err: &mut Diag<'_>,
1459 callee_ty: Option<Ty<'tcx>>,
1460 call_ident: Option<Ident>,
1461 expected_ty: Ty<'tcx>,
1462 provided_ty: Ty<'tcx>,
1463 provided_expr: &Expr<'tcx>,
1464 is_method: bool,
1465 ) {
1466 if !is_method {
1467 return;
1468 }
1469 let Some(callee_ty) = callee_ty else {
1470 return;
1471 };
1472 let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
1473 return;
1474 };
1475 let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
1476 "Option"
1477 } else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) {
1478 "Result"
1479 } else {
1480 return;
1481 };
1482
1483 let Some(call_ident) = call_ident else {
1484 return;
1485 };
1486 if call_ident.name != sym::unwrap_or {
1487 return;
1488 }
1489
1490 let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
1491 return;
1492 };
1493
1494 let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
1498 && let ty::Infer(_) = elem_ty.kind()
1499 && self
1500 .try_structurally_resolve_const(provided_expr.span, *size)
1501 .try_to_target_usize(self.tcx)
1502 == Some(0)
1503 {
1504 let slice = Ty::new_slice(self.tcx, *elem_ty);
1505 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
1506 } else {
1507 provided_ty
1508 };
1509
1510 if !self.may_coerce(expected_ty, dummy_ty) {
1511 return;
1512 }
1513 let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
1514 err.multipart_suggestion_verbose(
1515 msg,
1516 vec![
1517 (call_ident.span, "map_or".to_owned()),
1518 (provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()),
1519 ],
1520 Applicability::MachineApplicable,
1521 );
1522 }
1523
1524 pub(crate) fn suggest_block_to_brackets(
1527 &self,
1528 diag: &mut Diag<'_>,
1529 blk: &hir::Block<'_>,
1530 blk_ty: Ty<'tcx>,
1531 expected_ty: Ty<'tcx>,
1532 ) {
1533 if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
1534 if self.may_coerce(blk_ty, *elem_ty)
1535 && blk.stmts.is_empty()
1536 && blk.rules == hir::BlockCheckMode::DefaultBlock
1537 && let source_map = self.tcx.sess.source_map()
1538 && let Ok(snippet) = source_map.span_to_snippet(blk.span)
1539 && snippet.starts_with('{')
1540 && snippet.ends_with('}')
1541 {
1542 diag.multipart_suggestion_verbose(
1543 "to create an array, use square brackets instead of curly braces",
1544 vec![
1545 (
1546 blk.span
1547 .shrink_to_lo()
1548 .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
1549 "[".to_string(),
1550 ),
1551 (
1552 blk.span
1553 .shrink_to_hi()
1554 .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
1555 "]".to_string(),
1556 ),
1557 ],
1558 Applicability::MachineApplicable,
1559 );
1560 }
1561 }
1562 }
1563
1564 #[instrument(skip(self, err))]
1565 pub(crate) fn suggest_floating_point_literal(
1566 &self,
1567 err: &mut Diag<'_>,
1568 expr: &hir::Expr<'_>,
1569 expected_ty: Ty<'tcx>,
1570 ) -> bool {
1571 if !expected_ty.is_floating_point() {
1572 return false;
1573 }
1574 match expr.kind {
1575 ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => {
1576 err.span_suggestion_verbose(
1577 start.span.shrink_to_hi().with_hi(end.span.lo()),
1578 "remove the unnecessary `.` operator for a floating point literal",
1579 '.',
1580 Applicability::MaybeIncorrect,
1581 );
1582 true
1583 }
1584 ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => {
1585 err.span_suggestion_verbose(
1586 expr.span.with_lo(start.span.hi()),
1587 "remove the unnecessary `.` operator for a floating point literal",
1588 '.',
1589 Applicability::MaybeIncorrect,
1590 );
1591 true
1592 }
1593 ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => {
1594 err.span_suggestion_verbose(
1595 expr.span.until(end.span),
1596 "remove the unnecessary `.` operator and add an integer part for a floating point literal",
1597 "0.",
1598 Applicability::MaybeIncorrect,
1599 );
1600 true
1601 }
1602 ExprKind::Lit(Spanned {
1603 node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed),
1604 span,
1605 }) => {
1606 let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(*span) else {
1607 return false;
1608 };
1609 if !(snippet.starts_with("0x") || snippet.starts_with("0X")) {
1610 return false;
1611 }
1612 if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) {
1613 return false;
1614 }
1615 let (_, suffix) = snippet.split_at(snippet.len() - 3);
1616 let value = match suffix {
1617 "f32" => (lit.get() - 0xf32) / (16 * 16 * 16),
1618 "f64" => (lit.get() - 0xf64) / (16 * 16 * 16),
1619 _ => return false,
1620 };
1621 err.span_suggestions(
1622 expr.span,
1623 "rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float",
1624 [format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")],
1625 Applicability::MaybeIncorrect,
1626 );
1627 true
1628 }
1629 _ => false,
1630 }
1631 }
1632
1633 #[instrument(skip(self, err))]
1636 pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg(
1637 &self,
1638 err: &mut Diag<'_>,
1639 expr: &hir::Expr<'_>,
1640 expected_ty: Ty<'tcx>,
1641 ) -> bool {
1642 let ty::RawPtr(_, mutbl) = expected_ty.kind() else {
1644 return false;
1645 };
1646
1647 let ExprKind::Lit(Spanned { node: rustc_ast::LitKind::Int(Pu128(0), _), span }) = expr.kind
1649 else {
1650 return false;
1651 };
1652
1653 let null_sym = match mutbl {
1655 hir::Mutability::Not => sym::ptr_null,
1656 hir::Mutability::Mut => sym::ptr_null_mut,
1657 };
1658 let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else {
1659 return false;
1660 };
1661 let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did));
1662
1663 err.span_suggestion(
1665 *span,
1666 format!("if you meant to create a null pointer, use `{null_path_str}()`"),
1667 null_path_str + "()",
1668 Applicability::MachineApplicable,
1669 );
1670
1671 true
1672 }
1673
1674 pub(crate) fn suggest_associated_const(
1675 &self,
1676 err: &mut Diag<'_>,
1677 expr: &hir::Expr<'tcx>,
1678 expected_ty: Ty<'tcx>,
1679 ) -> bool {
1680 let Some((DefKind::AssocFn, old_def_id)) =
1681 self.typeck_results.borrow().type_dependent_def(expr.hir_id)
1682 else {
1683 return false;
1684 };
1685 let old_item_name = self.tcx.item_name(old_def_id);
1686 let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
1687 if old_item_name == capitalized_name {
1688 return false;
1689 }
1690 let (item, segment) = match expr.kind {
1691 hir::ExprKind::Path(QPath::Resolved(
1692 Some(ty),
1693 hir::Path { segments: [segment], .. },
1694 ))
1695 | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => {
1696 if let Some(self_ty) = self.typeck_results.borrow().node_type_opt(ty.hir_id)
1697 && let Ok(pick) = self.probe_for_name(
1698 Mode::Path,
1699 Ident::new(capitalized_name, segment.ident.span),
1700 Some(expected_ty),
1701 IsSuggestion(true),
1702 self_ty,
1703 expr.hir_id,
1704 ProbeScope::TraitsInScope,
1705 )
1706 {
1707 (pick.item, segment)
1708 } else {
1709 return false;
1710 }
1711 }
1712 hir::ExprKind::Path(QPath::Resolved(
1713 None,
1714 hir::Path { segments: [.., segment], .. },
1715 )) => {
1716 if old_item_name != segment.ident.name {
1719 return false;
1720 }
1721 if let Some(item) = self
1722 .tcx
1723 .associated_items(self.tcx.parent(old_def_id))
1724 .filter_by_name_unhygienic(capitalized_name)
1725 .next()
1726 {
1727 (*item, segment)
1728 } else {
1729 return false;
1730 }
1731 }
1732 _ => return false,
1733 };
1734 if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst {
1735 return false;
1737 }
1738 let item_ty = self.tcx.type_of(item.def_id).instantiate_identity();
1739 if item_ty.has_param() {
1741 return false;
1742 }
1743 if self.may_coerce(item_ty, expected_ty) {
1744 err.span_suggestion_verbose(
1745 segment.ident.span,
1746 format!("try referring to the associated const `{capitalized_name}` instead",),
1747 capitalized_name,
1748 Applicability::MachineApplicable,
1749 );
1750 true
1751 } else {
1752 false
1753 }
1754 }
1755
1756 fn is_loop(&self, id: HirId) -> bool {
1757 let node = self.tcx.hir_node(id);
1758 matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
1759 }
1760
1761 fn is_local_statement(&self, id: HirId) -> bool {
1762 let node = self.tcx.hir_node(id);
1763 matches!(node, Node::Stmt(Stmt { kind: StmtKind::Let(..), .. }))
1764 }
1765
1766 pub(crate) fn note_type_is_not_clone(
1769 &self,
1770 diag: &mut Diag<'_>,
1771 expected_ty: Ty<'tcx>,
1772 found_ty: Ty<'tcx>,
1773 expr: &hir::Expr<'_>,
1774 ) {
1775 let expr = self.note_type_is_not_clone_inner_expr(expr);
1778
1779 let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else {
1781 return;
1782 };
1783
1784 let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else {
1785 return;
1786 };
1787 let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
1788 let results = self.typeck_results.borrow();
1789 if segment.ident.name == sym::clone
1791 && results.type_dependent_def_id(expr.hir_id).is_some_and(|did| {
1792 let assoc_item = self.tcx.associated_item(did);
1793 assoc_item.container == ty::AssocItemContainer::Trait
1794 && assoc_item.container_id(self.tcx) == clone_trait_did
1795 })
1796 && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
1799 && self.may_coerce(*pointee_ty, expected_ty)
1801 && let trait_ref = ty::TraitRef::new(self.tcx, clone_trait_did, [expected_ty])
1802 && !self.predicate_must_hold_considering_regions(&traits::Obligation::new(
1804 self.tcx,
1805 traits::ObligationCause::dummy(),
1806 self.param_env,
1807 trait_ref,
1808 ))
1809 {
1810 diag.span_note(
1811 callee_expr.span,
1812 format!(
1813 "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
1814 ),
1815 );
1816 let owner = self.tcx.hir_enclosing_body_owner(expr.hir_id);
1817 if let ty::Param(param) = expected_ty.kind()
1818 && let Some(generics) = self.tcx.hir_get_generics(owner)
1819 {
1820 suggest_constraining_type_params(
1821 self.tcx,
1822 generics,
1823 diag,
1824 vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
1825 None,
1826 );
1827 } else {
1828 if let Some(errors) =
1829 self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env)
1830 {
1831 match &errors[..] {
1832 [] => {}
1833 [error] => {
1834 diag.help(format!(
1835 "`Clone` is not implemented because the trait bound `{}` is \
1836 not satisfied",
1837 error.obligation.predicate,
1838 ));
1839 }
1840 _ => {
1841 diag.help(format!(
1842 "`Clone` is not implemented because the following trait bounds \
1843 could not be satisfied: {}",
1844 listify(&errors, |e| format!("`{}`", e.obligation.predicate))
1845 .unwrap(),
1846 ));
1847 }
1848 }
1849 for error in errors {
1850 if let traits::FulfillmentErrorCode::Select(
1851 traits::SelectionError::Unimplemented,
1852 ) = error.code
1853 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1854 error.obligation.predicate.kind().skip_binder()
1855 {
1856 self.infcx.err_ctxt().suggest_derive(
1857 &error.obligation,
1858 diag,
1859 error.obligation.predicate.kind().rebind(pred),
1860 );
1861 }
1862 }
1863 }
1864 self.suggest_derive(diag, &[(trait_ref.upcast(self.tcx), None, None)]);
1865 }
1866 }
1867 }
1868
1869 fn note_type_is_not_clone_inner_expr<'b>(
1874 &'b self,
1875 expr: &'b hir::Expr<'b>,
1876 ) -> &'b hir::Expr<'b> {
1877 match expr.peel_blocks().kind {
1878 hir::ExprKind::Path(hir::QPath::Resolved(
1879 None,
1880 hir::Path { segments: [_], res: crate::Res::Local(binding), .. },
1881 )) => {
1882 let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding) else {
1883 return expr;
1884 };
1885
1886 match self.tcx.parent_hir_node(*hir_id) {
1887 hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => {
1889 self.note_type_is_not_clone_inner_expr(init)
1890 }
1891 hir::Node::Pat(hir::Pat {
1893 hir_id: pat_hir_id,
1894 kind: hir::PatKind::Tuple(pats, ..),
1895 ..
1896 }) => {
1897 let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
1898 self.tcx.parent_hir_node(*pat_hir_id)
1899 else {
1900 return expr;
1901 };
1902
1903 match init.peel_blocks().kind {
1904 ExprKind::Tup(init_tup) => {
1905 if let Some(init) = pats
1906 .iter()
1907 .enumerate()
1908 .filter(|x| x.1.hir_id == *hir_id)
1909 .find_map(|(i, _)| init_tup.get(i))
1910 {
1911 self.note_type_is_not_clone_inner_expr(init)
1912 } else {
1913 expr
1914 }
1915 }
1916 _ => expr,
1917 }
1918 }
1919 _ => expr,
1920 }
1921 }
1922 hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => {
1926 if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) =
1927 call_expr_kind
1928 && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } =
1929 call_expr_path
1930 && let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding)
1931 && let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
1932 self.tcx.parent_hir_node(*hir_id)
1933 && let Expr {
1934 kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }),
1935 ..
1936 } = init
1937 {
1938 let hir::Body { value: body_expr, .. } = self.tcx.hir_body(*body_id);
1939 self.note_type_is_not_clone_inner_expr(body_expr)
1940 } else {
1941 expr
1942 }
1943 }
1944 _ => expr,
1945 }
1946 }
1947
1948 pub(crate) fn is_field_suggestable(
1949 &self,
1950 field: &ty::FieldDef,
1951 hir_id: HirId,
1952 span: Span,
1953 ) -> bool {
1954 field.vis.is_accessible_from(self.tcx.parent_module(hir_id), self.tcx)
1956 && !matches!(
1958 self.tcx.eval_stability(field.did, None, rustc_span::DUMMY_SP, None),
1959 rustc_middle::middle::stability::EvalResult::Deny { .. }
1960 )
1961 && (field.did.is_local() || !self.tcx.is_doc_hidden(field.did))
1963 && self.tcx.def_ident_span(field.did).unwrap().normalize_to_macros_2_0().eq_ctxt(span)
1965 }
1966
1967 pub(crate) fn suggest_missing_unwrap_expect(
1968 &self,
1969 err: &mut Diag<'_>,
1970 expr: &hir::Expr<'tcx>,
1971 expected: Ty<'tcx>,
1972 found: Ty<'tcx>,
1973 ) -> bool {
1974 let ty::Adt(adt, args) = found.kind() else {
1975 return false;
1976 };
1977 let ret_ty_matches = |diagnostic_item| {
1978 let Some(sig) = self.body_fn_sig() else {
1979 return false;
1980 };
1981 let ty::Adt(kind, _) = sig.output().kind() else {
1982 return false;
1983 };
1984 self.tcx.is_diagnostic_item(diagnostic_item, kind.did())
1985 };
1986
1987 let is_ctor = matches!(
1990 expr.kind,
1991 hir::ExprKind::Call(
1992 hir::Expr {
1993 kind: hir::ExprKind::Path(hir::QPath::Resolved(
1994 None,
1995 hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
1996 )),
1997 ..
1998 },
1999 ..,
2000 ) | hir::ExprKind::Path(hir::QPath::Resolved(
2001 None,
2002 hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
2003 )),
2004 );
2005
2006 let (article, kind, variant, sugg_operator) =
2007 if self.tcx.is_diagnostic_item(sym::Result, adt.did()) {
2008 ("a", "Result", "Err", ret_ty_matches(sym::Result))
2009 } else if self.tcx.is_diagnostic_item(sym::Option, adt.did()) {
2010 ("an", "Option", "None", ret_ty_matches(sym::Option))
2011 } else {
2012 return false;
2013 };
2014 if is_ctor || !self.may_coerce(args.type_at(0), expected) {
2015 return false;
2016 }
2017
2018 let (msg, sugg) = if sugg_operator {
2019 (
2020 format!(
2021 "use the `?` operator to extract the `{found}` value, propagating \
2022 {article} `{kind}::{variant}` value to the caller"
2023 ),
2024 "?",
2025 )
2026 } else {
2027 (
2028 format!(
2029 "consider using `{kind}::expect` to unwrap the `{found}` value, \
2030 panicking if the value is {article} `{kind}::{variant}`"
2031 ),
2032 ".expect(\"REASON\")",
2033 )
2034 };
2035
2036 let sugg = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2037 Some(ident) => format!(": {ident}{sugg}"),
2038 None => sugg.to_string(),
2039 };
2040
2041 let span = expr.span.find_oldest_ancestor_in_same_ctxt();
2042 err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders);
2043 true
2044 }
2045
2046 pub(crate) fn suggest_coercing_result_via_try_operator(
2047 &self,
2048 err: &mut Diag<'_>,
2049 expr: &hir::Expr<'tcx>,
2050 expected: Ty<'tcx>,
2051 found: Ty<'tcx>,
2052 ) -> bool {
2053 let returned = matches!(
2054 self.tcx.parent_hir_node(expr.hir_id),
2055 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })
2056 ) || self.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_some();
2057 if returned
2058 && let ty::Adt(e, args_e) = expected.kind()
2059 && let ty::Adt(f, args_f) = found.kind()
2060 && e.did() == f.did()
2061 && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
2062 && let e_ok = args_e.type_at(0)
2063 && let f_ok = args_f.type_at(0)
2064 && self.infcx.can_eq(self.param_env, f_ok, e_ok)
2065 && let e_err = args_e.type_at(1)
2066 && let f_err = args_f.type_at(1)
2067 && self
2068 .infcx
2069 .type_implements_trait(
2070 self.tcx.get_diagnostic_item(sym::Into).unwrap(),
2071 [f_err, e_err],
2072 self.param_env,
2073 )
2074 .must_apply_modulo_regions()
2075 {
2076 err.multipart_suggestion(
2077 "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
2078 in `Ok` so the expression remains of type `Result`",
2079 vec![
2080 (expr.span.shrink_to_lo(), "Ok(".to_string()),
2081 (expr.span.shrink_to_hi(), "?)".to_string()),
2082 ],
2083 Applicability::MaybeIncorrect,
2084 );
2085 return true;
2086 }
2087 false
2088 }
2089
2090 pub(crate) fn suggest_returning_value_after_loop(
2093 &self,
2094 err: &mut Diag<'_>,
2095 expr: &hir::Expr<'tcx>,
2096 expected: Ty<'tcx>,
2097 ) -> bool {
2098 let tcx = self.tcx;
2099 let enclosing_scope =
2100 tcx.hir_get_enclosing_scope(expr.hir_id).map(|hir_id| tcx.hir_node(hir_id));
2101
2102 let tail_expr = if let Some(Node::Block(hir::Block { expr, .. })) = enclosing_scope
2104 && expr.is_some()
2105 {
2106 *expr
2107 } else {
2108 let body_def_id = tcx.hir_enclosing_body_owner(expr.hir_id);
2109 let body = tcx.hir_body_owned_by(body_def_id);
2110
2111 match body.value.kind {
2113 hir::ExprKind::Block(block, _) => block.expr,
2115 hir::ExprKind::DropTemps(expr) => Some(expr),
2117 _ => None,
2118 }
2119 };
2120
2121 let Some(tail_expr) = tail_expr else {
2122 return false; };
2124
2125 let loop_expr_in_tail = match expr.kind {
2127 hir::ExprKind::Loop(_, _, hir::LoopSource::While, _) => tail_expr,
2128 hir::ExprKind::Loop(_, _, hir::LoopSource::ForLoop, _) => {
2129 match tail_expr.peel_drop_temps() {
2130 Expr { kind: ExprKind::Match(_, [Arm { body, .. }], _), .. } => body,
2131 _ => return false, }
2133 }
2134 _ => return false, };
2136
2137 if expr.hir_id == loop_expr_in_tail.hir_id {
2140 let span = expr.span;
2141
2142 let (msg, suggestion) = if expected.is_never() {
2143 (
2144 "consider adding a diverging expression here",
2145 "`loop {}` or `panic!(\"...\")`".to_string(),
2146 )
2147 } else {
2148 ("consider returning a value here", format!("`{expected}` value"))
2149 };
2150
2151 let src_map = tcx.sess.source_map();
2152 let suggestion = if src_map.is_multiline(expr.span) {
2153 let indentation = src_map.indentation_before(span).unwrap_or_else(String::new);
2154 format!("\n{indentation}/* {suggestion} */")
2155 } else {
2156 format!(" /* {suggestion} */")
2159 };
2160
2161 err.span_suggestion_verbose(
2162 span.shrink_to_hi(),
2163 msg,
2164 suggestion,
2165 Applicability::MaybeIncorrect,
2166 );
2167
2168 true
2169 } else {
2170 false
2171 }
2172 }
2173
2174 pub(crate) fn suggest_semicolon_in_repeat_expr(
2177 &self,
2178 err: &mut Diag<'_>,
2179 expr: &hir::Expr<'_>,
2180 expr_ty: Ty<'tcx>,
2181 ) -> bool {
2182 if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id)
2184 && let hir::ExprKind::Array(elements) = array_expr.kind
2185 && let [first, second] = &elements[..]
2186 && second.hir_id == expr.hir_id
2187 {
2188 let comma_span = first.span.between(second.span);
2190
2191 let expr_is_const_usize = expr_ty.is_usize()
2199 && match expr.kind {
2200 ExprKind::Path(QPath::Resolved(
2201 None,
2202 Path { res: Res::Def(DefKind::Const, _), .. },
2203 )) => true,
2204 ExprKind::Call(
2205 Expr {
2206 kind:
2207 ExprKind::Path(QPath::Resolved(
2208 None,
2209 Path { res: Res::Def(DefKind::Fn, fn_def_id), .. },
2210 )),
2211 ..
2212 },
2213 _,
2214 ) => self.tcx.is_const_fn(*fn_def_id),
2215 _ => false,
2216 };
2217
2218 let first_ty = self.typeck_results.borrow().expr_ty(first);
2223
2224 if self.tcx.sess.source_map().is_imported(array_expr.span)
2229 && self.type_is_clone_modulo_regions(self.param_env, first_ty)
2230 && (expr.is_size_lit() || expr_ty.is_usize_like())
2231 {
2232 err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2233 comma_span,
2234 descr: "a vector",
2235 });
2236 return true;
2237 }
2238
2239 if self.type_is_copy_modulo_regions(self.param_env, first_ty)
2243 && (expr.is_size_lit() || expr_is_const_usize)
2244 {
2245 err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2246 comma_span,
2247 descr: "an array",
2248 });
2249 return true;
2250 }
2251 }
2252 false
2253 }
2254
2255 pub(crate) fn suggest_compatible_variants(
2258 &self,
2259 err: &mut Diag<'_>,
2260 expr: &hir::Expr<'_>,
2261 expected: Ty<'tcx>,
2262 expr_ty: Ty<'tcx>,
2263 ) -> bool {
2264 if expr.span.in_external_macro(self.tcx.sess.source_map()) {
2265 return false;
2266 }
2267 if let ty::Adt(expected_adt, args) = expected.kind() {
2268 if let hir::ExprKind::Field(base, ident) = expr.kind {
2269 let base_ty = self.typeck_results.borrow().expr_ty(base);
2270 if self.can_eq(self.param_env, base_ty, expected)
2271 && let Some(base_span) = base.span.find_ancestor_inside(expr.span)
2272 {
2273 err.span_suggestion_verbose(
2274 expr.span.with_lo(base_span.hi()),
2275 format!("consider removing the tuple struct field `{ident}`"),
2276 "",
2277 Applicability::MaybeIncorrect,
2278 );
2279 return true;
2280 }
2281 }
2282
2283 if expr_ty.is_unit() {
2287 let mut id = expr.hir_id;
2288 let mut parent;
2289
2290 loop {
2292 parent = self.tcx.parent_hir_id(id);
2293 let parent_span = self.tcx.hir_span(parent);
2294 if parent_span.find_ancestor_inside(expr.span).is_some() {
2295 id = parent;
2298 continue;
2299 }
2300 break;
2301 }
2302
2303 if let hir::Node::Block(&hir::Block { span: block_span, expr: Some(e), .. }) =
2304 self.tcx.hir_node(parent)
2305 {
2306 if e.hir_id == id {
2307 if let Some(span) = expr.span.find_ancestor_inside(block_span) {
2308 let return_suggestions = if self
2309 .tcx
2310 .is_diagnostic_item(sym::Result, expected_adt.did())
2311 {
2312 vec!["Ok(())"]
2313 } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
2314 vec!["None", "Some(())"]
2315 } else {
2316 return false;
2317 };
2318 if let Some(indent) =
2319 self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
2320 {
2321 let semicolon =
2323 match self.tcx.sess.source_map().span_to_snippet(span) {
2324 Ok(s) if s.ends_with('}') => "",
2325 _ => ";",
2326 };
2327 err.span_suggestions(
2328 span.shrink_to_hi(),
2329 "try adding an expression at the end of the block",
2330 return_suggestions
2331 .into_iter()
2332 .map(|r| format!("{semicolon}\n{indent}{r}")),
2333 Applicability::MaybeIncorrect,
2334 );
2335 }
2336 return true;
2337 }
2338 }
2339 }
2340 }
2341
2342 let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
2343 .variants()
2344 .iter()
2345 .filter(|variant| {
2346 variant.fields.len() == 1
2347 })
2348 .filter_map(|variant| {
2349 let sole_field = &variant.single_field();
2350
2351 let field_is_local = sole_field.did.is_local();
2352 let field_is_accessible =
2353 sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
2354 && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
2356
2357 if !field_is_local && !field_is_accessible {
2358 return None;
2359 }
2360
2361 let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
2362 .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
2363
2364 let sole_field_ty = sole_field.ty(self.tcx, args);
2365 if self.may_coerce(expr_ty, sole_field_ty) {
2366 let variant_path =
2367 with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
2368 if let Some(path) = variant_path.strip_prefix("std::prelude::")
2370 && let Some((_, path)) = path.split_once("::")
2371 {
2372 return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy));
2373 }
2374 Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy))
2375 } else {
2376 None
2377 }
2378 })
2379 .collect();
2380
2381 let suggestions_for = |variant: &_, ctor_kind, field_name| {
2382 let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2383 Some(ident) => format!("{ident}: "),
2384 None => String::new(),
2385 };
2386
2387 let (open, close) = match ctor_kind {
2388 Some(CtorKind::Fn) => ("(".to_owned(), ")"),
2389 None => (format!(" {{ {field_name}: "), " }"),
2390
2391 Some(CtorKind::Const) => unreachable!("unit variants don't have fields"),
2392 };
2393
2394 let mut expr = expr;
2398 while let hir::ExprKind::Block(block, _) = &expr.kind
2399 && let Some(expr_) = &block.expr
2400 {
2401 expr = expr_
2402 }
2403
2404 vec![
2405 (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
2406 (expr.span.shrink_to_hi(), close.to_owned()),
2407 ]
2408 };
2409
2410 match &compatible_variants[..] {
2411 [] => { }
2412 [(variant, ctor_kind, field_name, note)] => {
2413 err.multipart_suggestion_verbose(
2415 format!(
2416 "try wrapping the expression in `{variant}`{note}",
2417 note = note.as_deref().unwrap_or("")
2418 ),
2419 suggestions_for(&**variant, *ctor_kind, *field_name),
2420 Applicability::MaybeIncorrect,
2421 );
2422 return true;
2423 }
2424 _ => {
2425 err.multipart_suggestions(
2427 format!(
2428 "try wrapping the expression in a variant of `{}`",
2429 self.tcx.def_path_str(expected_adt.did())
2430 ),
2431 compatible_variants.into_iter().map(
2432 |(variant, ctor_kind, field_name, _)| {
2433 suggestions_for(&variant, ctor_kind, field_name)
2434 },
2435 ),
2436 Applicability::MaybeIncorrect,
2437 );
2438 return true;
2439 }
2440 }
2441 }
2442
2443 false
2444 }
2445
2446 pub(crate) fn suggest_non_zero_new_unwrap(
2447 &self,
2448 err: &mut Diag<'_>,
2449 expr: &hir::Expr<'_>,
2450 expected: Ty<'tcx>,
2451 expr_ty: Ty<'tcx>,
2452 ) -> bool {
2453 let tcx = self.tcx;
2454 let (adt, args, unwrap) = match expected.kind() {
2455 ty::Adt(adt, args) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
2457 let nonzero_type = args.type_at(0); let ty::Adt(adt, args) = nonzero_type.kind() else {
2459 return false;
2460 };
2461 (adt, args, "")
2462 }
2463 ty::Adt(adt, args) => (adt, args, ".unwrap()"),
2465 _ => return false,
2466 };
2467
2468 if !self.tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
2469 return false;
2470 }
2471
2472 let int_type = args.type_at(0);
2473 if !self.may_coerce(expr_ty, int_type) {
2474 return false;
2475 }
2476
2477 err.multipart_suggestion(
2478 format!("consider calling `{}::new`", sym::NonZero),
2479 vec![
2480 (expr.span.shrink_to_lo(), format!("{}::new(", sym::NonZero)),
2481 (expr.span.shrink_to_hi(), format!("){unwrap}")),
2482 ],
2483 Applicability::MaybeIncorrect,
2484 );
2485
2486 true
2487 }
2488
2489 fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> {
2506 let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind else {
2507 return None;
2508 };
2509
2510 let hir::def::Res::Local(local_id) = path.res else {
2511 return None;
2512 };
2513
2514 let Node::Param(hir::Param { hir_id: param_hir_id, .. }) =
2515 self.tcx.parent_hir_node(local_id)
2516 else {
2517 return None;
2518 };
2519
2520 let Node::Expr(hir::Expr {
2521 hir_id: expr_hir_id,
2522 kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
2523 ..
2524 }) = self.tcx.parent_hir_node(*param_hir_id)
2525 else {
2526 return None;
2527 };
2528
2529 let hir = self.tcx.parent_hir_node(*expr_hir_id);
2530 let closure_params_len = closure_fn_decl.inputs.len();
2531 let (
2532 Node::Expr(hir::Expr {
2533 kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
2534 ..
2535 }),
2536 1,
2537 ) = (hir, closure_params_len)
2538 else {
2539 return None;
2540 };
2541
2542 let self_ty = self.typeck_results.borrow().expr_ty(receiver);
2543 let name = method_path.ident.name;
2544 let is_as_ref_able = match self_ty.peel_refs().kind() {
2545 ty::Adt(def, _) => {
2546 (self.tcx.is_diagnostic_item(sym::Option, def.did())
2547 || self.tcx.is_diagnostic_item(sym::Result, def.did()))
2548 && (name == sym::map || name == sym::and_then)
2549 }
2550 _ => false,
2551 };
2552 if is_as_ref_able {
2553 Some((
2554 vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())],
2555 "consider using `as_ref` instead",
2556 ))
2557 } else {
2558 None
2559 }
2560 }
2561
2562 pub(crate) fn suggest_deref_or_ref(
2579 &self,
2580 expr: &hir::Expr<'tcx>,
2581 checked_ty: Ty<'tcx>,
2582 expected: Ty<'tcx>,
2583 ) -> Option<(
2584 Vec<(Span, String)>,
2585 String,
2586 Applicability,
2587 bool, bool, )> {
2590 let sess = self.sess();
2591 let sp = expr.span;
2592 let sm = sess.source_map();
2593
2594 if sp.in_external_macro(sm) {
2596 return None;
2597 }
2598
2599 let replace_prefix = |s: &str, old: &str, new: &str| {
2600 s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
2601 };
2602
2603 let expr = expr.peel_drop_temps();
2605
2606 match (&expr.kind, expected.kind(), checked_ty.kind()) {
2607 (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
2608 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
2609 if let hir::ExprKind::Lit(_) = expr.kind
2610 && let Ok(src) = sm.span_to_snippet(sp)
2611 && replace_prefix(&src, "b\"", "\"").is_some()
2612 {
2613 let pos = sp.lo() + BytePos(1);
2614 return Some((
2615 vec![(sp.with_hi(pos), String::new())],
2616 "consider removing the leading `b`".to_string(),
2617 Applicability::MachineApplicable,
2618 true,
2619 false,
2620 ));
2621 }
2622 }
2623 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
2624 if let hir::ExprKind::Lit(_) = expr.kind
2625 && let Ok(src) = sm.span_to_snippet(sp)
2626 && replace_prefix(&src, "\"", "b\"").is_some()
2627 {
2628 return Some((
2629 vec![(sp.shrink_to_lo(), "b".to_string())],
2630 "consider adding a leading `b`".to_string(),
2631 Applicability::MachineApplicable,
2632 true,
2633 false,
2634 ));
2635 }
2636 }
2637 _ => {}
2638 },
2639 (_, &ty::Ref(_, _, mutability), _) => {
2640 let ref_ty = match mutability {
2649 hir::Mutability::Mut => {
2650 Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2651 }
2652 hir::Mutability::Not => {
2653 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2654 }
2655 };
2656 if self.may_coerce(ref_ty, expected) {
2657 let mut sugg_sp = sp;
2658 if let hir::ExprKind::MethodCall(segment, receiver, args, _) = expr.kind {
2659 let clone_trait =
2660 self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span));
2661 if args.is_empty()
2662 && self
2663 .typeck_results
2664 .borrow()
2665 .type_dependent_def_id(expr.hir_id)
2666 .is_some_and(|did| {
2667 let ai = self.tcx.associated_item(did);
2668 ai.trait_container(self.tcx) == Some(clone_trait)
2669 })
2670 && segment.ident.name == sym::clone
2671 {
2672 sugg_sp = receiver.span;
2675 }
2676 }
2677
2678 if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
2679 && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty)
2680 && self.typeck_results.borrow().expr_ty(inner).is_ref()
2681 {
2682 sugg_sp = sugg_sp.with_hi(inner.span.lo());
2685 return Some((
2686 vec![(sugg_sp, String::new())],
2687 "consider removing deref here".to_string(),
2688 Applicability::MachineApplicable,
2689 true,
2690 false,
2691 ));
2692 }
2693
2694 if let Some((sugg, msg)) = self.can_use_as_ref(expr) {
2695 return Some((
2696 sugg,
2697 msg.to_string(),
2698 Applicability::MachineApplicable,
2699 true,
2700 false,
2701 ));
2702 }
2703
2704 let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
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 ) {
3482 if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
3483 && self
3484 .infcx
3485 .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
3486 .must_apply_modulo_regions()
3487 {
3488 let sm = self.tcx.sess.source_map();
3489 if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
3490 && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
3491 {
3492 err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
3493 err.multipart_suggestion(
3494 "consider swapping the equality",
3495 vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
3496 Applicability::MaybeIncorrect,
3497 );
3498 }
3499 }
3500 }
3501}