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