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 let field_is_local = sole_field.did.is_local();
2382 let field_is_accessible =
2383 sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
2384 && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
2386
2387 if !field_is_local && !field_is_accessible {
2388 return None;
2389 }
2390
2391 let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
2392 .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
2393
2394 let sole_field_ty = sole_field.ty(self.tcx, args);
2395 if self.may_coerce(expr_ty, sole_field_ty) {
2396 let variant_path =
2397 with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
2398 if let Some(path) = variant_path.strip_prefix("std::prelude::")
2400 && let Some((_, path)) = path.split_once("::")
2401 {
2402 return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy));
2403 }
2404 Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy))
2405 } else {
2406 None
2407 }
2408 })
2409 .collect();
2410
2411 let suggestions_for = |variant: &_, ctor_kind, field_name| {
2412 let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2413 Some(ident) => format!("{ident}: "),
2414 None => String::new(),
2415 };
2416
2417 let (open, close) = match ctor_kind {
2418 Some(CtorKind::Fn) => ("(".to_owned(), ")"),
2419 None => (format!(" {{ {field_name}: "), " }"),
2420
2421 Some(CtorKind::Const) => unreachable!("unit variants don't have fields"),
2422 };
2423
2424 let mut expr = expr;
2428 while let hir::ExprKind::Block(block, _) = &expr.kind
2429 && let Some(expr_) = &block.expr
2430 {
2431 expr = expr_
2432 }
2433
2434 vec![
2435 (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
2436 (expr.span.shrink_to_hi(), close.to_owned()),
2437 ]
2438 };
2439
2440 match &compatible_variants[..] {
2441 [] => { }
2442 [(variant, ctor_kind, field_name, note)] => {
2443 err.multipart_suggestion_verbose(
2445 format!(
2446 "try wrapping the expression in `{variant}`{note}",
2447 note = note.as_deref().unwrap_or("")
2448 ),
2449 suggestions_for(&**variant, *ctor_kind, *field_name),
2450 Applicability::MaybeIncorrect,
2451 );
2452 return true;
2453 }
2454 _ => {
2455 err.multipart_suggestions(
2457 format!(
2458 "try wrapping the expression in a variant of `{}`",
2459 self.tcx.def_path_str(expected_adt.did())
2460 ),
2461 compatible_variants.into_iter().map(
2462 |(variant, ctor_kind, field_name, _)| {
2463 suggestions_for(&variant, ctor_kind, field_name)
2464 },
2465 ),
2466 Applicability::MaybeIncorrect,
2467 );
2468 return true;
2469 }
2470 }
2471 }
2472
2473 false
2474 }
2475
2476 pub(crate) fn suggest_non_zero_new_unwrap(
2477 &self,
2478 err: &mut Diag<'_>,
2479 expr: &hir::Expr<'_>,
2480 expected: Ty<'tcx>,
2481 expr_ty: Ty<'tcx>,
2482 ) -> bool {
2483 let tcx = self.tcx;
2484 let (adt, args, unwrap) = match expected.kind() {
2485 ty::Adt(adt, args) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
2487 let nonzero_type = args.type_at(0); let ty::Adt(adt, args) = nonzero_type.kind() else {
2489 return false;
2490 };
2491 (adt, args, "")
2492 }
2493 ty::Adt(adt, args) => (adt, args, ".unwrap()"),
2495 _ => return false,
2496 };
2497
2498 if !self.tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
2499 return false;
2500 }
2501
2502 let int_type = args.type_at(0);
2503 if !self.may_coerce(expr_ty, int_type) {
2504 return false;
2505 }
2506
2507 err.multipart_suggestion(
2508 format!("consider calling `{}::new`", sym::NonZero),
2509 vec![
2510 (expr.span.shrink_to_lo(), format!("{}::new(", sym::NonZero)),
2511 (expr.span.shrink_to_hi(), format!("){unwrap}")),
2512 ],
2513 Applicability::MaybeIncorrect,
2514 );
2515
2516 true
2517 }
2518
2519 fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> {
2536 let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind else {
2537 return None;
2538 };
2539
2540 let hir::def::Res::Local(local_id) = path.res else {
2541 return None;
2542 };
2543
2544 let Node::Param(hir::Param { hir_id: param_hir_id, .. }) =
2545 self.tcx.parent_hir_node(local_id)
2546 else {
2547 return None;
2548 };
2549
2550 let Node::Expr(hir::Expr {
2551 hir_id: expr_hir_id,
2552 kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
2553 ..
2554 }) = self.tcx.parent_hir_node(*param_hir_id)
2555 else {
2556 return None;
2557 };
2558
2559 let hir = self.tcx.parent_hir_node(*expr_hir_id);
2560 let closure_params_len = closure_fn_decl.inputs.len();
2561 let (
2562 Node::Expr(hir::Expr {
2563 kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
2564 ..
2565 }),
2566 1,
2567 ) = (hir, closure_params_len)
2568 else {
2569 return None;
2570 };
2571
2572 let self_ty = self.typeck_results.borrow().expr_ty(receiver);
2573 let name = method_path.ident.name;
2574 let is_as_ref_able = match self_ty.peel_refs().kind() {
2575 ty::Adt(def, _) => {
2576 (self.tcx.is_diagnostic_item(sym::Option, def.did())
2577 || self.tcx.is_diagnostic_item(sym::Result, def.did()))
2578 && (name == sym::map || name == sym::and_then)
2579 }
2580 _ => false,
2581 };
2582 if is_as_ref_able {
2583 Some((
2584 vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())],
2585 "consider using `as_ref` instead",
2586 ))
2587 } else {
2588 None
2589 }
2590 }
2591
2592 pub(crate) fn suggest_deref_or_ref(
2609 &self,
2610 expr: &hir::Expr<'tcx>,
2611 checked_ty: Ty<'tcx>,
2612 expected: Ty<'tcx>,
2613 ) -> Option<(
2614 Vec<(Span, String)>,
2615 String,
2616 Applicability,
2617 bool, bool, )> {
2620 let sess = self.sess();
2621 let sp = expr.span;
2622 let sm = sess.source_map();
2623
2624 if sp.in_external_macro(sm) {
2626 return None;
2627 }
2628
2629 let replace_prefix = |s: &str, old: &str, new: &str| {
2630 s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
2631 };
2632
2633 let expr = expr.peel_drop_temps();
2635
2636 match (&expr.kind, expected.kind(), checked_ty.kind()) {
2637 (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
2638 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
2639 if let hir::ExprKind::Lit(_) = expr.kind
2640 && let Ok(src) = sm.span_to_snippet(sp)
2641 && replace_prefix(&src, "b\"", "\"").is_some()
2642 {
2643 let pos = sp.lo() + BytePos(1);
2644 return Some((
2645 vec![(sp.with_hi(pos), String::new())],
2646 "consider removing the leading `b`".to_string(),
2647 Applicability::MachineApplicable,
2648 true,
2649 false,
2650 ));
2651 }
2652 }
2653 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
2654 if let hir::ExprKind::Lit(_) = expr.kind
2655 && let Ok(src) = sm.span_to_snippet(sp)
2656 && replace_prefix(&src, "\"", "b\"").is_some()
2657 {
2658 return Some((
2659 vec![(sp.shrink_to_lo(), "b".to_string())],
2660 "consider adding a leading `b`".to_string(),
2661 Applicability::MachineApplicable,
2662 true,
2663 false,
2664 ));
2665 }
2666 }
2667 _ => {}
2668 },
2669 (_, &ty::Ref(_, _, mutability), _) => {
2670 let ref_ty = match mutability {
2679 hir::Mutability::Mut => {
2680 Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2681 }
2682 hir::Mutability::Not => {
2683 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2684 }
2685 };
2686 if self.may_coerce(ref_ty, expected) {
2687 let mut sugg_sp = sp;
2688 if let hir::ExprKind::MethodCall(segment, receiver, args, _) = expr.kind {
2689 let clone_trait =
2690 self.tcx.require_lang_item(LangItem::Clone, segment.ident.span);
2691 if args.is_empty()
2692 && self
2693 .typeck_results
2694 .borrow()
2695 .type_dependent_def_id(expr.hir_id)
2696 .is_some_and(|did| {
2697 let ai = self.tcx.associated_item(did);
2698 ai.trait_container(self.tcx) == Some(clone_trait)
2699 })
2700 && segment.ident.name == sym::clone
2701 {
2702 sugg_sp = receiver.span;
2705 }
2706 }
2707
2708 if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
2709 && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty)
2710 && self.typeck_results.borrow().expr_ty(inner).is_ref()
2711 {
2712 sugg_sp = sugg_sp.with_hi(inner.span.lo());
2715 return Some((
2716 vec![(sugg_sp, String::new())],
2717 "consider removing deref here".to_string(),
2718 Applicability::MachineApplicable,
2719 true,
2720 false,
2721 ));
2722 }
2723
2724 if let hir::ExprKind::If(_c, then, els) = expr.kind {
2730 let ExprKind::Block(then, _) = then.kind else { return None };
2733 let Some(then) = then.expr else { return None };
2734 let (mut suggs, help, app, verbose, mutref) =
2735 self.suggest_deref_or_ref(then, checked_ty, expected)?;
2736
2737 let els_expr = match els?.kind {
2739 ExprKind::Block(block, _) => block.expr?,
2740 _ => els?,
2741 };
2742 let (else_suggs, ..) =
2743 self.suggest_deref_or_ref(els_expr, checked_ty, expected)?;
2744 suggs.extend(else_suggs);
2745
2746 return Some((suggs, help, app, verbose, mutref));
2747 }
2748
2749 if let Some((sugg, msg)) = self.can_use_as_ref(expr) {
2750 return Some((
2751 sugg,
2752 msg.to_string(),
2753 Applicability::MachineApplicable,
2754 true,
2755 false,
2756 ));
2757 }
2758
2759 let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2760 Some(ident) => format!("{ident}: "),
2761 None => String::new(),
2762 };
2763
2764 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(..), .. }) =
2765 self.tcx.parent_hir_node(expr.hir_id)
2766 {
2767 if mutability.is_mut() {
2768 return None;
2770 }
2771 }
2772
2773 let make_sugg = |expr: &Expr<'_>, span: Span, sugg: &str| {
2774 if expr_needs_parens(expr) {
2775 (
2776 vec![
2777 (span.shrink_to_lo(), format!("{prefix}{sugg}(")),
2778 (span.shrink_to_hi(), ")".to_string()),
2779 ],
2780 false,
2781 )
2782 } else {
2783 (vec![(span.shrink_to_lo(), format!("{prefix}{sugg}"))], true)
2784 }
2785 };
2786
2787 if let hir::Node::Expr(hir::Expr {
2789 kind: hir::ExprKind::Binary(_, lhs, ..),
2790 ..
2791 }) = self.tcx.parent_hir_node(expr.hir_id)
2792 && let &ty::Ref(..) = self.check_expr(lhs).kind()
2793 {
2794 let (sugg, verbose) = make_sugg(lhs, lhs.span, "*");
2795
2796 return Some((
2797 sugg,
2798 "consider dereferencing the borrow".to_string(),
2799 Applicability::MachineApplicable,
2800 verbose,
2801 false,
2802 ));
2803 }
2804
2805 let sugg = mutability.ref_prefix_str();
2806 let (sugg, verbose) = make_sugg(expr, sp, sugg);
2807 return Some((
2808 sugg,
2809 format!("consider {}borrowing here", mutability.mutably_str()),
2810 Applicability::MachineApplicable,
2811 verbose,
2812 false,
2813 ));
2814 }
2815 }
2816 (hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr), _, &ty::Ref(_, checked, _))
2817 if self.can_eq(self.param_env, checked, expected) =>
2818 {
2819 let make_sugg = |start: Span, end: BytePos| {
2820 let sp = sm
2823 .span_extend_while(start.shrink_to_lo(), |c| c == '(' || c.is_whitespace())
2824 .map_or(start, |s| s.shrink_to_hi());
2825 Some((
2826 vec![(sp.with_hi(end), String::new())],
2827 "consider removing the borrow".to_string(),
2828 Applicability::MachineApplicable,
2829 true,
2830 true,
2831 ))
2832 };
2833
2834 if sm.is_imported(expr.span) {
2837 if let Some(call_span) =
2843 iter::successors(Some(expr.span), |s| s.parent_callsite())
2844 .find(|&s| sp.contains(s))
2845 && sm.is_span_accessible(call_span)
2846 {
2847 return make_sugg(sp, call_span.lo());
2848 }
2849 return None;
2850 }
2851 if sp.contains(expr.span) && sm.is_span_accessible(expr.span) {
2852 return make_sugg(sp, expr.span.lo());
2853 }
2854 }
2855 (_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => {
2856 if let Some(steps) = self.deref_steps_for_suggestion(ty_a, ty_b)
2857 && steps > 0
2859 && let Ok(src) = sm.span_to_snippet(sp)
2861 {
2862 let derefs = "*".repeat(steps);
2863 let old_prefix = mutbl_a.ref_prefix_str();
2864 let new_prefix = mutbl_b.ref_prefix_str().to_owned() + &derefs;
2865
2866 let suggestion = replace_prefix(&src, old_prefix, &new_prefix).map(|_| {
2867 let lo = sp.lo()
2869 + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _);
2870 let hi = sp.lo() + BytePos(old_prefix.len() as _);
2872 let sp = sp.with_lo(lo).with_hi(hi);
2873
2874 (
2875 sp,
2876 format!(
2877 "{}{derefs}",
2878 if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }
2879 ),
2880 if mutbl_b <= mutbl_a {
2881 Applicability::MachineApplicable
2882 } else {
2883 Applicability::MaybeIncorrect
2884 },
2885 )
2886 });
2887
2888 if let Some((span, src, applicability)) = suggestion {
2889 return Some((
2890 vec![(span, src)],
2891 "consider dereferencing".to_string(),
2892 applicability,
2893 true,
2894 false,
2895 ));
2896 }
2897 }
2898 }
2899 _ if sp == expr.span => {
2900 if let Some(mut steps) = self.deref_steps_for_suggestion(checked_ty, expected) {
2901 let mut expr = expr.peel_blocks();
2902 let mut prefix_span = expr.span.shrink_to_lo();
2903 let mut remove = String::new();
2904
2905 while steps > 0 {
2907 if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
2908 prefix_span = prefix_span.with_hi(inner.span.lo());
2910 expr = inner;
2911 remove.push_str(mutbl.ref_prefix_str());
2912 steps -= 1;
2913 } else {
2914 break;
2915 }
2916 }
2917 if steps == 0 && !remove.trim().is_empty() {
2919 return Some((
2920 vec![(prefix_span, String::new())],
2921 format!("consider removing the `{}`", remove.trim()),
2922 if remove.trim() == "&&" && expected == self.tcx.types.bool {
2926 Applicability::MaybeIncorrect
2927 } else {
2928 Applicability::MachineApplicable
2929 },
2930 true,
2931 false,
2932 ));
2933 }
2934
2935 if self.type_is_copy_modulo_regions(self.param_env, expected)
2938 || (checked_ty.is_box() && steps == 1)
2941 || matches!(
2943 self.tcx.parent_hir_node(expr.hir_id),
2944 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, ..), .. })
2945 if !op.node.is_by_value()
2946 )
2947 {
2948 let deref_kind = if checked_ty.is_box() {
2949 "unboxing the value"
2950 } else if checked_ty.is_ref() {
2951 "dereferencing the borrow"
2952 } else {
2953 "dereferencing the type"
2954 };
2955
2956 let message = if remove.is_empty() {
2959 format!("consider {deref_kind}")
2960 } else {
2961 format!(
2962 "consider removing the `{}` and {} instead",
2963 remove.trim(),
2964 deref_kind
2965 )
2966 };
2967
2968 let prefix =
2969 match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2970 Some(ident) => format!("{ident}: "),
2971 None => String::new(),
2972 };
2973
2974 let (span, suggestion) = if self.is_else_if_block(expr) {
2975 return None;
2977 } else if let Some(expr) = self.maybe_get_block_expr(expr) {
2978 (expr.span.shrink_to_lo(), "*".to_string())
2980 } else {
2981 (prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
2982 };
2983 if suggestion.trim().is_empty() {
2984 return None;
2985 }
2986
2987 if expr_needs_parens(expr) {
2988 return Some((
2989 vec![
2990 (span, format!("{suggestion}(")),
2991 (expr.span.shrink_to_hi(), ")".to_string()),
2992 ],
2993 message,
2994 Applicability::MachineApplicable,
2995 true,
2996 false,
2997 ));
2998 }
2999
3000 return Some((
3001 vec![(span, suggestion)],
3002 message,
3003 Applicability::MachineApplicable,
3004 true,
3005 false,
3006 ));
3007 }
3008 }
3009 }
3010 _ => {}
3011 }
3012 None
3013 }
3014
3015 fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
3017 if let hir::ExprKind::If(..) = expr.kind
3018 && let Node::Expr(hir::Expr { kind: hir::ExprKind::If(_, _, Some(else_expr)), .. }) =
3019 self.tcx.parent_hir_node(expr.hir_id)
3020 {
3021 return else_expr.hir_id == expr.hir_id;
3022 }
3023 false
3024 }
3025
3026 pub(crate) fn suggest_cast(
3027 &self,
3028 err: &mut Diag<'_>,
3029 expr: &hir::Expr<'_>,
3030 checked_ty: Ty<'tcx>,
3031 expected_ty: Ty<'tcx>,
3032 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
3033 ) -> bool {
3034 if self.tcx.sess.source_map().is_imported(expr.span) {
3035 return false;
3037 }
3038
3039 let span = if let hir::ExprKind::Lit(lit) = &expr.kind { lit.span } else { expr.span };
3040 let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) else {
3041 return false;
3042 };
3043
3044 let can_cast = false;
3053
3054 let mut sugg = vec![];
3055
3056 if let hir::Node::ExprField(field) = self.tcx.parent_hir_node(expr.hir_id) {
3057 if field.is_shorthand {
3059 sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
3061 } else {
3062 return false;
3064 }
3065 };
3066
3067 if let hir::ExprKind::Call(path, args) = &expr.kind
3068 && let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
3069 (&path.kind, args.len())
3070 && let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
3072 (&base_ty.kind, path_segment.ident.name)
3073 {
3074 if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
3075 match ident.name {
3076 sym::i128
3077 | sym::i64
3078 | sym::i32
3079 | sym::i16
3080 | sym::i8
3081 | sym::u128
3082 | sym::u64
3083 | sym::u32
3084 | sym::u16
3085 | sym::u8
3086 | sym::isize
3087 | sym::usize
3088 if base_ty_path.segments.len() == 1 =>
3089 {
3090 return false;
3091 }
3092 _ => {}
3093 }
3094 }
3095 }
3096
3097 let msg = format!(
3098 "you can convert {} `{}` to {} `{}`",
3099 checked_ty.kind().article(),
3100 checked_ty,
3101 expected_ty.kind().article(),
3102 expected_ty,
3103 );
3104 let cast_msg = format!(
3105 "you can cast {} `{}` to {} `{}`",
3106 checked_ty.kind().article(),
3107 checked_ty,
3108 expected_ty.kind().article(),
3109 expected_ty,
3110 );
3111 let lit_msg = format!(
3112 "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
3113 );
3114
3115 let close_paren = if self.precedence(expr) < ExprPrecedence::Unambiguous {
3116 sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
3117 ")"
3118 } else {
3119 ""
3120 };
3121
3122 let mut cast_suggestion = sugg.clone();
3123 cast_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren} as {expected_ty}")));
3124 let mut into_suggestion = sugg.clone();
3125 into_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren}.into()")));
3126 let mut suffix_suggestion = sugg.clone();
3127 suffix_suggestion.push((
3128 if matches!(
3129 (expected_ty.kind(), checked_ty.kind()),
3130 (ty::Int(_) | ty::Uint(_), ty::Float(_))
3131 ) {
3132 let src = src.trim_end_matches(&checked_ty.to_string());
3134 let len = src.split('.').next().unwrap().len();
3135 span.with_lo(span.lo() + BytePos(len as u32))
3136 } else {
3137 let len = src.trim_end_matches(&checked_ty.to_string()).len();
3138 span.with_lo(span.lo() + BytePos(len as u32))
3139 },
3140 if self.precedence(expr) < ExprPrecedence::Unambiguous {
3141 format!("{expected_ty})")
3143 } else {
3144 expected_ty.to_string()
3145 },
3146 ));
3147 let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
3148 if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
3149 };
3150 let is_negative_int =
3151 |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..));
3152 let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
3153
3154 let in_const_context = self.tcx.hir_is_inside_const_context(expr.hir_id);
3155
3156 let suggest_fallible_into_or_lhs_from =
3157 |err: &mut Diag<'_>, exp_to_found_is_fallible: bool| {
3158 let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
3165 self.tcx
3166 .sess
3167 .source_map()
3168 .span_to_snippet(expr.span)
3169 .ok()
3170 .map(|src| (expr, src))
3171 });
3172 let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
3173 (lhs_expr_and_src, exp_to_found_is_fallible)
3174 {
3175 let msg = format!(
3176 "you can convert `{lhs_src}` from `{expected_ty}` to `{checked_ty}`, matching the type of `{src}`",
3177 );
3178 let suggestion = vec![
3179 (lhs_expr.span.shrink_to_lo(), format!("{checked_ty}::from(")),
3180 (lhs_expr.span.shrink_to_hi(), ")".to_string()),
3181 ];
3182 (msg, suggestion)
3183 } else {
3184 let msg =
3185 format!("{} and panic if the converted value doesn't fit", msg.clone());
3186 let mut suggestion = sugg.clone();
3187 suggestion.push((
3188 expr.span.shrink_to_hi(),
3189 format!("{close_paren}.try_into().unwrap()"),
3190 ));
3191 (msg, suggestion)
3192 };
3193 err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
3194 };
3195
3196 let suggest_to_change_suffix_or_into =
3197 |err: &mut Diag<'_>, found_to_exp_is_fallible: bool, exp_to_found_is_fallible: bool| {
3198 let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir_is_lhs(e.hir_id));
3199
3200 if exp_is_lhs {
3201 return;
3202 }
3203
3204 let always_fallible = found_to_exp_is_fallible
3205 && (exp_to_found_is_fallible || expected_ty_expr.is_none());
3206 let msg = if literal_is_ty_suffixed(expr) {
3207 lit_msg.clone()
3208 } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
3209 let msg = format!("`{src}` cannot fit into type `{expected_ty}`");
3213 err.note(msg);
3214 return;
3215 } else if in_const_context {
3216 return;
3218 } else if found_to_exp_is_fallible {
3219 return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
3220 } else {
3221 msg.clone()
3222 };
3223 let suggestion = if literal_is_ty_suffixed(expr) {
3224 suffix_suggestion.clone()
3225 } else {
3226 into_suggestion.clone()
3227 };
3228 err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
3229 };
3230
3231 match (expected_ty.kind(), checked_ty.kind()) {
3232 (ty::Int(exp), ty::Int(found)) => {
3233 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3234 {
3235 (Some(exp), Some(found)) if exp < found => (true, false),
3236 (Some(exp), Some(found)) if exp > found => (false, true),
3237 (None, Some(8 | 16)) => (false, true),
3238 (Some(8 | 16), None) => (true, false),
3239 (None, _) | (_, None) => (true, true),
3240 _ => (false, false),
3241 };
3242 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3243 true
3244 }
3245 (ty::Uint(exp), ty::Uint(found)) => {
3246 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3247 {
3248 (Some(exp), Some(found)) if exp < found => (true, false),
3249 (Some(exp), Some(found)) if exp > found => (false, true),
3250 (None, Some(8 | 16)) => (false, true),
3251 (Some(8 | 16), None) => (true, false),
3252 (None, _) | (_, None) => (true, true),
3253 _ => (false, false),
3254 };
3255 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3256 true
3257 }
3258 (&ty::Int(exp), &ty::Uint(found)) => {
3259 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3260 {
3261 (Some(exp), Some(found)) if found < exp => (false, true),
3262 (None, Some(8)) => (false, true),
3263 _ => (true, true),
3264 };
3265 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3266 true
3267 }
3268 (&ty::Uint(exp), &ty::Int(found)) => {
3269 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3270 {
3271 (Some(exp), Some(found)) if found > exp => (true, false),
3272 (Some(8), None) => (true, false),
3273 _ => (true, true),
3274 };
3275 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3276 true
3277 }
3278 (ty::Float(exp), ty::Float(found)) => {
3279 if found.bit_width() < exp.bit_width() {
3280 suggest_to_change_suffix_or_into(err, false, true);
3281 } else if literal_is_ty_suffixed(expr) {
3282 err.multipart_suggestion_verbose(
3283 lit_msg,
3284 suffix_suggestion,
3285 Applicability::MachineApplicable,
3286 );
3287 } else if can_cast {
3288 err.multipart_suggestion_verbose(
3290 format!("{cast_msg}, producing the closest possible value"),
3291 cast_suggestion,
3292 Applicability::MaybeIncorrect, );
3294 }
3295 true
3296 }
3297 (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
3298 if literal_is_ty_suffixed(expr) {
3299 err.multipart_suggestion_verbose(
3300 lit_msg,
3301 suffix_suggestion,
3302 Applicability::MachineApplicable,
3303 );
3304 } else if can_cast {
3305 err.multipart_suggestion_verbose(
3307 format!("{msg}, rounding the float towards zero"),
3308 cast_suggestion,
3309 Applicability::MaybeIncorrect, );
3311 }
3312 true
3313 }
3314 (ty::Float(exp), ty::Uint(found)) => {
3315 if exp.bit_width() > found.bit_width().unwrap_or(256) {
3317 err.multipart_suggestion_verbose(
3318 format!(
3319 "{msg}, producing the floating point representation of the integer",
3320 ),
3321 into_suggestion,
3322 Applicability::MachineApplicable,
3323 );
3324 } else if literal_is_ty_suffixed(expr) {
3325 err.multipart_suggestion_verbose(
3326 lit_msg,
3327 suffix_suggestion,
3328 Applicability::MachineApplicable,
3329 );
3330 } else {
3331 err.multipart_suggestion_verbose(
3333 format!(
3334 "{cast_msg}, producing the floating point representation of the integer, \
3335 rounded if necessary",
3336 ),
3337 cast_suggestion,
3338 Applicability::MaybeIncorrect, );
3340 }
3341 true
3342 }
3343 (ty::Float(exp), ty::Int(found)) => {
3344 if exp.bit_width() > found.bit_width().unwrap_or(256) {
3346 err.multipart_suggestion_verbose(
3347 format!(
3348 "{}, producing the floating point representation of the integer",
3349 msg.clone(),
3350 ),
3351 into_suggestion,
3352 Applicability::MachineApplicable,
3353 );
3354 } else if literal_is_ty_suffixed(expr) {
3355 err.multipart_suggestion_verbose(
3356 lit_msg,
3357 suffix_suggestion,
3358 Applicability::MachineApplicable,
3359 );
3360 } else {
3361 err.multipart_suggestion_verbose(
3363 format!(
3364 "{}, producing the floating point representation of the integer, \
3365 rounded if necessary",
3366 &msg,
3367 ),
3368 cast_suggestion,
3369 Applicability::MaybeIncorrect, );
3371 }
3372 true
3373 }
3374 (
3375 &ty::Uint(ty::UintTy::U32 | ty::UintTy::U64 | ty::UintTy::U128)
3376 | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128),
3377 &ty::Char,
3378 ) => {
3379 err.multipart_suggestion_verbose(
3380 format!("{cast_msg}, since a `char` always occupies 4 bytes"),
3381 cast_suggestion,
3382 Applicability::MachineApplicable,
3383 );
3384 true
3385 }
3386 _ => false,
3387 }
3388 }
3389
3390 pub(crate) fn suggest_method_call_on_range_literal(
3392 &self,
3393 err: &mut Diag<'_>,
3394 expr: &hir::Expr<'tcx>,
3395 checked_ty: Ty<'tcx>,
3396 expected_ty: Ty<'tcx>,
3397 ) {
3398 if !hir::is_range_literal(expr) {
3399 return;
3400 }
3401 let hir::ExprKind::Struct(hir::QPath::LangItem(LangItem::Range, ..), [start, end], _) =
3402 expr.kind
3403 else {
3404 return;
3405 };
3406 if let hir::Node::ExprField(_) = self.tcx.parent_hir_node(expr.hir_id) {
3407 return;
3409 }
3410 let mut expr = end.expr;
3411 let mut expectation = Some(expected_ty);
3412 while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
3413 expr = rcvr;
3416 expectation = None;
3419 }
3420 let hir::ExprKind::Call(method_name, _) = expr.kind else {
3421 return;
3422 };
3423 let ty::Adt(adt, _) = checked_ty.kind() else {
3424 return;
3425 };
3426 if self.tcx.lang_items().range_struct() != Some(adt.did()) {
3427 return;
3428 }
3429 if let ty::Adt(adt, _) = expected_ty.kind()
3430 && self.tcx.is_lang_item(adt.did(), LangItem::Range)
3431 {
3432 return;
3433 }
3434 let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else {
3436 return;
3437 };
3438 let [hir::PathSegment { ident, .. }] = p.segments else {
3439 return;
3440 };
3441 let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
3442 let Ok(_pick) = self.lookup_probe_for_diagnostic(
3443 *ident,
3444 self_ty,
3445 expr,
3446 probe::ProbeScope::AllTraits,
3447 expectation,
3448 ) else {
3449 return;
3450 };
3451 let mut sugg = ".";
3452 let mut span = start.expr.span.between(end.expr.span);
3453 if span.lo() + BytePos(2) == span.hi() {
3454 span = span.with_lo(span.lo() + BytePos(1));
3457 sugg = "";
3458 }
3459 err.span_suggestion_verbose(
3460 span,
3461 "you likely meant to write a method call instead of a range",
3462 sugg,
3463 Applicability::MachineApplicable,
3464 );
3465 }
3466
3467 pub(crate) fn suggest_return_binding_for_missing_tail_expr(
3470 &self,
3471 err: &mut Diag<'_>,
3472 expr: &hir::Expr<'_>,
3473 checked_ty: Ty<'tcx>,
3474 expected_ty: Ty<'tcx>,
3475 ) {
3476 if !checked_ty.is_unit() {
3477 return;
3478 }
3479 let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {
3480 return;
3481 };
3482 let hir::def::Res::Local(hir_id) = path.res else {
3483 return;
3484 };
3485 let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else {
3486 return;
3487 };
3488 let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) =
3489 self.tcx.parent_hir_node(pat.hir_id)
3490 else {
3491 return;
3492 };
3493 let hir::ExprKind::Block(block, None) = init.kind else {
3494 return;
3495 };
3496 if block.expr.is_some() {
3497 return;
3498 }
3499 let [.., stmt] = block.stmts else {
3500 err.span_label(block.span, "this empty block is missing a tail expression");
3501 return;
3502 };
3503 let hir::StmtKind::Semi(tail_expr) = stmt.kind else {
3504 return;
3505 };
3506 let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else {
3507 return;
3508 };
3509 if self.can_eq(self.param_env, expected_ty, ty)
3510 && stmt.span.hi() != tail_expr.span.hi()
3515 {
3516 err.span_suggestion_short(
3517 stmt.span.with_lo(tail_expr.span.hi()),
3518 "remove this semicolon",
3519 "",
3520 Applicability::MachineApplicable,
3521 );
3522 } else {
3523 err.span_label(block.span, "this block is missing a tail expression");
3524 }
3525 }
3526
3527 pub(crate) fn suggest_swapping_lhs_and_rhs(
3528 &self,
3529 err: &mut Diag<'_>,
3530 rhs_ty: Ty<'tcx>,
3531 lhs_ty: Ty<'tcx>,
3532 rhs_expr: &'tcx hir::Expr<'tcx>,
3533 lhs_expr: &'tcx hir::Expr<'tcx>,
3534 ) {
3535 if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
3536 && self
3537 .infcx
3538 .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
3539 .must_apply_modulo_regions()
3540 {
3541 let sm = self.tcx.sess.source_map();
3542 if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
3543 && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
3544 {
3545 err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
3546 err.multipart_suggestion(
3547 "consider swapping the equality",
3548 vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
3549 Applicability::MaybeIncorrect,
3550 );
3551 }
3552 }
3553 }
3554}