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