1use core::cmp::min;
2use core::iter;
3
4use hir::def_id::LocalDefId;
5use rustc_ast::util::parser::ExprPrecedence;
6use rustc_data_structures::packed::Pu128;
7use rustc_errors::{Applicability, Diag, MultiSpan, listify};
8use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
9use rustc_hir::lang_items::LangItem;
10use rustc_hir::{
11 self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind,
12 GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind,
13 WherePredicateKind, expr_needs_parens,
14};
15use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
16use rustc_hir_analysis::suggest_impl_trait;
17use rustc_middle::middle::stability::EvalResult;
18use rustc_middle::span_bug;
19use rustc_middle::ty::print::with_no_trimmed_paths;
20use rustc_middle::ty::{
21 self, Article, Binder, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Upcast,
22 suggest_constraining_type_params,
23};
24use rustc_session::errors::ExprParenthesesNeeded;
25use rustc_span::source_map::Spanned;
26use rustc_span::{ExpnKind, Ident, MacroKind, Span, Symbol, sym};
27use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
28use rustc_trait_selection::error_reporting::traits::DefIdOrName;
29use rustc_trait_selection::infer::InferCtxtExt;
30use rustc_trait_selection::traits;
31use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
32use tracing::{debug, instrument};
33
34use super::FnCtxt;
35use crate::fn_ctxt::rustc_span::BytePos;
36use crate::method::probe;
37use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
38use crate::{errors, fluent_generated as fluent};
39
40impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
41 pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
42 self.typeck_results
43 .borrow()
44 .liberated_fn_sigs()
45 .get(self.tcx.local_def_id_to_hir_id(self.body_id))
46 .copied()
47 }
48
49 pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diag<'_>) {
50 err.span_suggestion_short(
53 span.shrink_to_hi(),
54 "consider using a semicolon here",
55 ";",
56 Applicability::MaybeIncorrect,
57 );
58 }
59
60 pub(crate) fn suggest_mismatched_types_on_tail(
66 &self,
67 err: &mut Diag<'_>,
68 expr: &'tcx hir::Expr<'tcx>,
69 expected: Ty<'tcx>,
70 found: Ty<'tcx>,
71 blk_id: HirId,
72 ) -> bool {
73 let expr = expr.peel_drop_temps();
74 let mut pointing_at_return_type = false;
75 if let hir::ExprKind::Break(..) = expr.kind {
76 return false;
78 }
79 if let Some((fn_id, fn_decl)) = self.get_fn_decl(blk_id) {
80 pointing_at_return_type =
81 self.suggest_missing_return_type(err, fn_decl, expected, found, fn_id);
82 self.suggest_missing_break_or_return_expr(
83 err, expr, fn_decl, expected, found, blk_id, fn_id,
84 );
85 }
86 pointing_at_return_type
87 }
88
89 pub(crate) fn suggest_fn_call(
97 &self,
98 err: &mut Diag<'_>,
99 expr: &hir::Expr<'_>,
100 found: Ty<'tcx>,
101 can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
102 ) -> bool {
103 let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(found) else {
104 return false;
105 };
106 if can_satisfy(output) {
107 let (sugg_call, mut applicability) = match inputs.len() {
108 0 => ("".to_string(), Applicability::MachineApplicable),
109 1..=4 => (
110 inputs
111 .iter()
112 .map(|ty| {
113 if ty.is_suggestable(self.tcx, false) {
114 format!("/* {ty} */")
115 } else {
116 "/* value */".to_string()
117 }
118 })
119 .collect::<Vec<_>>()
120 .join(", "),
121 Applicability::HasPlaceholders,
122 ),
123 _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
124 };
125
126 let msg = match def_id_or_name {
127 DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
128 DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(),
129 DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(),
130 kind => format!("call this {}", self.tcx.def_kind_descr(kind, def_id)),
131 },
132 DefIdOrName::Name(name) => format!("call this {name}"),
133 };
134
135 let sugg = match expr.kind {
136 hir::ExprKind::Call(..)
137 | hir::ExprKind::Path(..)
138 | hir::ExprKind::Index(..)
139 | hir::ExprKind::Lit(..) => {
140 vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
141 }
142 hir::ExprKind::Closure { .. } => {
143 applicability = Applicability::MaybeIncorrect;
145 vec![
146 (expr.span.shrink_to_lo(), "(".to_string()),
147 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
148 ]
149 }
150 _ => {
151 vec![
152 (expr.span.shrink_to_lo(), "(".to_string()),
153 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
154 ]
155 }
156 };
157
158 err.multipart_suggestion_verbose(
159 format!("use parentheses to {msg}"),
160 sugg,
161 applicability,
162 );
163 return true;
164 }
165 false
166 }
167
168 pub(in super::super) fn extract_callable_info(
172 &self,
173 ty: Ty<'tcx>,
174 ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
175 self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty)
176 }
177
178 pub(crate) fn suggest_two_fn_call(
179 &self,
180 err: &mut Diag<'_>,
181 lhs_expr: &'tcx hir::Expr<'tcx>,
182 lhs_ty: Ty<'tcx>,
183 rhs_expr: &'tcx hir::Expr<'tcx>,
184 rhs_ty: Ty<'tcx>,
185 can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
186 ) -> bool {
187 if lhs_expr.span.in_derive_expansion() || rhs_expr.span.in_derive_expansion() {
188 return false;
189 }
190 let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty) else {
191 return false;
192 };
193 let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_ty) else {
194 return false;
195 };
196
197 if can_satisfy(lhs_output_ty, rhs_output_ty) {
198 let mut sugg = vec![];
199 let mut applicability = Applicability::MachineApplicable;
200
201 for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
202 let (sugg_call, this_applicability) = match inputs.len() {
203 0 => ("".to_string(), Applicability::MachineApplicable),
204 1..=4 => (
205 inputs
206 .iter()
207 .map(|ty| {
208 if ty.is_suggestable(self.tcx, false) {
209 format!("/* {ty} */")
210 } else {
211 "/* value */".to_string()
212 }
213 })
214 .collect::<Vec<_>>()
215 .join(", "),
216 Applicability::HasPlaceholders,
217 ),
218 _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
219 };
220
221 applicability = applicability.max(this_applicability);
222
223 match expr.kind {
224 hir::ExprKind::Call(..)
225 | hir::ExprKind::Path(..)
226 | hir::ExprKind::Index(..)
227 | hir::ExprKind::Lit(..) => {
228 sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
229 }
230 hir::ExprKind::Closure { .. } => {
231 applicability = Applicability::MaybeIncorrect;
233 sugg.extend([
234 (expr.span.shrink_to_lo(), "(".to_string()),
235 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
236 ]);
237 }
238 _ => {
239 sugg.extend([
240 (expr.span.shrink_to_lo(), "(".to_string()),
241 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
242 ]);
243 }
244 }
245 }
246
247 err.multipart_suggestion_verbose("use parentheses to call these", sugg, applicability);
248
249 true
250 } else {
251 false
252 }
253 }
254
255 pub(crate) fn suggest_remove_last_method_call(
256 &self,
257 err: &mut Diag<'_>,
258 expr: &hir::Expr<'tcx>,
259 expected: Ty<'tcx>,
260 ) -> bool {
261 if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) =
262 expr.kind
263 && let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr)
264 && self.may_coerce(recv_ty, expected)
265 && let name = method.name.as_str()
266 && (name.starts_with("to_") || name.starts_with("as_") || name == "into")
267 {
268 let span = if let Some(recv_span) = recv_expr.span.find_ancestor_inside(expr.span) {
269 expr.span.with_lo(recv_span.hi())
270 } else {
271 expr.span.with_lo(method.span.lo() - rustc_span::BytePos(1))
272 };
273 err.span_suggestion_verbose(
274 span,
275 "try removing the method call",
276 "",
277 Applicability::MachineApplicable,
278 );
279 return true;
280 }
281 false
282 }
283
284 pub(crate) fn suggest_deref_ref_or_into(
285 &self,
286 err: &mut Diag<'_>,
287 expr: &hir::Expr<'tcx>,
288 expected: Ty<'tcx>,
289 found: Ty<'tcx>,
290 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
291 ) -> bool {
292 let expr = expr.peel_blocks();
293 let methods =
294 self.get_conversion_methods_for_diagnostic(expr.span, expected, found, expr.hir_id);
295
296 if let Some((suggestion, msg, applicability, verbose, annotation)) =
297 self.suggest_deref_or_ref(expr, found, expected)
298 {
299 if verbose {
300 err.multipart_suggestion_verbose(msg, suggestion, applicability);
301 } else {
302 err.multipart_suggestion(msg, suggestion, applicability);
303 }
304 if annotation {
305 let suggest_annotation = match expr.peel_drop_temps().kind {
306 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(),
307 _ => return true,
308 };
309 let mut tuple_indexes = Vec::new();
310 let mut expr_id = expr.hir_id;
311 for (parent_id, node) in self.tcx.hir_parent_iter(expr.hir_id) {
312 match node {
313 Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => {
314 tuple_indexes.push(
315 subs.iter()
316 .enumerate()
317 .find(|(_, sub_expr)| sub_expr.hir_id == expr_id)
318 .unwrap()
319 .0,
320 );
321 expr_id = parent_id;
322 }
323 Node::LetStmt(local) => {
324 if let Some(mut ty) = local.ty {
325 while let Some(index) = tuple_indexes.pop() {
326 match ty.kind {
327 TyKind::Tup(tys) => ty = &tys[index],
328 _ => return true,
329 }
330 }
331 let annotation_span = ty.span;
332 err.span_suggestion(
333 annotation_span.with_hi(annotation_span.lo()),
334 "alternatively, consider changing the type annotation",
335 suggest_annotation,
336 Applicability::MaybeIncorrect,
337 );
338 }
339 break;
340 }
341 _ => break,
342 }
343 }
344 }
345 return true;
346 }
347
348 if self.suggest_else_fn_with_closure(err, expr, found, expected) {
349 return true;
350 }
351
352 if self.suggest_fn_call(err, expr, found, |output| self.may_coerce(output, expected))
353 && let ty::FnDef(def_id, ..) = *found.kind()
354 && let Some(sp) = self.tcx.hir_span_if_local(def_id)
355 {
356 let name = self.tcx.item_name(def_id);
357 let kind = self.tcx.def_kind(def_id);
358 if let DefKind::Ctor(of, CtorKind::Fn) = kind {
359 err.span_label(
360 sp,
361 format!(
362 "`{name}` defines {} constructor here, which should be called",
363 match of {
364 CtorOf::Struct => "a struct",
365 CtorOf::Variant => "an enum variant",
366 }
367 ),
368 );
369 } else {
370 let descr = self.tcx.def_kind_descr(kind, def_id);
371 err.span_label(sp, format!("{descr} `{name}` defined here"));
372 }
373 return true;
374 }
375
376 if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
377 return true;
378 }
379
380 if !methods.is_empty() {
381 let mut suggestions = methods
382 .iter()
383 .filter_map(|conversion_method| {
384 let conversion_method_name = conversion_method.name();
385 let receiver_method_ident = expr.method_ident();
386 if let Some(method_ident) = receiver_method_ident
387 && method_ident.name == conversion_method_name
388 {
389 return None; }
391
392 let method_call_list = [sym::to_vec, sym::to_string];
393 let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
394 && receiver_method.ident.name == sym::clone
395 && method_call_list.contains(&conversion_method_name)
396 {
401 vec![(receiver_method.ident.span, conversion_method_name.to_string())]
402 } else if expr.precedence() < ExprPrecedence::Unambiguous {
403 vec![
404 (expr.span.shrink_to_lo(), "(".to_string()),
405 (expr.span.shrink_to_hi(), format!(").{}()", conversion_method_name)),
406 ]
407 } else {
408 vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method_name))]
409 };
410 let struct_pat_shorthand_field =
411 self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr);
412 if let Some(name) = struct_pat_shorthand_field {
413 sugg.insert(0, (expr.span.shrink_to_lo(), format!("{name}: ")));
414 }
415 Some(sugg)
416 })
417 .peekable();
418 if suggestions.peek().is_some() {
419 err.multipart_suggestions(
420 "try using a conversion method",
421 suggestions,
422 Applicability::MaybeIncorrect,
423 );
424 return true;
425 }
426 }
427
428 if let Some((found_ty_inner, expected_ty_inner, error_tys)) =
429 self.deconstruct_option_or_result(found, expected)
430 && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind()
431 {
432 let inner_expr = expr.peel_borrows();
434 if !inner_expr.span.eq_ctxt(expr.span) {
435 return false;
436 }
437 let borrow_removal_span = if inner_expr.hir_id == expr.hir_id {
438 None
439 } else {
440 Some(expr.span.shrink_to_lo().until(inner_expr.span))
441 };
442 let error_tys_equate_as_ref = error_tys.is_none_or(|(found, expected)| {
445 self.can_eq(
446 self.param_env,
447 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, found),
448 expected,
449 )
450 });
451
452 let prefix_wrap = |sugg: &str| {
453 if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
454 format!(": {}{}", name, sugg)
455 } else {
456 sugg.to_string()
457 }
458 };
459
460 if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
463 let sugg = prefix_wrap(".as_ref()");
464 err.subdiagnostic(errors::SuggestConvertViaMethod {
465 span: expr.span.shrink_to_hi(),
466 sugg,
467 expected,
468 found,
469 borrow_removal_span,
470 });
471 return true;
472 } else if let ty::Ref(_, peeled_found_ty, _) = found_ty_inner.kind()
473 && let ty::Adt(adt, _) = peeled_found_ty.peel_refs().kind()
474 && self.tcx.is_lang_item(adt.did(), LangItem::String)
475 && peeled.is_str()
476 && error_tys.is_none_or(|(found, expected)| {
478 self.can_eq(self.param_env, found, expected)
479 })
480 {
481 let sugg = prefix_wrap(".map(|x| x.as_str())");
482 err.span_suggestion_verbose(
483 expr.span.shrink_to_hi(),
484 fluent::hir_typeck_convert_to_str,
485 sugg,
486 Applicability::MachineApplicable,
487 );
488 return true;
489 } else {
490 if !error_tys_equate_as_ref {
491 return false;
492 }
493 let mut steps = self.autoderef(expr.span, found_ty_inner).silence_errors();
494 if let Some((deref_ty, _)) = steps.nth(1)
495 && self.can_eq(self.param_env, deref_ty, peeled)
496 {
497 let sugg = prefix_wrap(".as_deref()");
498 err.subdiagnostic(errors::SuggestConvertViaMethod {
499 span: expr.span.shrink_to_hi(),
500 sugg,
501 expected,
502 found,
503 borrow_removal_span,
504 });
505 return true;
506 }
507 for (deref_ty, n_step) in steps {
508 if self.can_eq(self.param_env, deref_ty, peeled) {
509 let explicit_deref = "*".repeat(n_step);
510 let sugg = prefix_wrap(&format!(".map(|v| &{explicit_deref}v)"));
511 err.subdiagnostic(errors::SuggestConvertViaMethod {
512 span: expr.span.shrink_to_hi(),
513 sugg,
514 expected,
515 found,
516 borrow_removal_span,
517 });
518 return true;
519 }
520 }
521 }
522 }
523
524 false
525 }
526
527 fn deconstruct_option_or_result(
531 &self,
532 found_ty: Ty<'tcx>,
533 expected_ty: Ty<'tcx>,
534 ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> {
535 let ty::Adt(found_adt, found_args) = found_ty.peel_refs().kind() else {
536 return None;
537 };
538 let ty::Adt(expected_adt, expected_args) = expected_ty.kind() else {
539 return None;
540 };
541 if self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
542 && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
543 {
544 Some((found_args.type_at(0), expected_args.type_at(0), None))
545 } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did())
546 && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did())
547 {
548 Some((
549 found_args.type_at(0),
550 expected_args.type_at(0),
551 Some((found_args.type_at(1), expected_args.type_at(1))),
552 ))
553 } else {
554 None
555 }
556 }
557
558 pub(in super::super) fn suggest_boxing_when_appropriate(
561 &self,
562 err: &mut Diag<'_>,
563 span: Span,
564 hir_id: HirId,
565 expected: Ty<'tcx>,
566 found: Ty<'tcx>,
567 ) -> bool {
568 if self.tcx.hir_is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
570 return false;
571 }
572 if self.may_coerce(Ty::new_box(self.tcx, found), expected) {
573 let suggest_boxing = match found.kind() {
574 ty::Tuple(tuple) if tuple.is_empty() => {
575 errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
576 }
577 ty::Coroutine(def_id, ..)
578 if matches!(
579 self.tcx.coroutine_kind(def_id),
580 Some(CoroutineKind::Desugared(
581 CoroutineDesugaring::Async,
582 CoroutineSource::Closure
583 ))
584 ) =>
585 {
586 errors::SuggestBoxing::AsyncBody
587 }
588 _ 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 if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
620 let spans_and_labels = upvars
623 .iter()
624 .take(4)
625 .map(|(var_hir_id, upvar)| {
626 let var_name = self.tcx.hir_name(*var_hir_id).to_string();
627 let msg = format!("`{var_name}` captured here");
628 (upvar.span, msg)
629 })
630 .collect::<Vec<_>>();
631
632 let mut multi_span: MultiSpan =
633 spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
634 for (sp, label) in spans_and_labels {
635 multi_span.push_span_label(sp, label);
636 }
637 err.span_note(
638 multi_span,
639 "closures can only be coerced to `fn` types if they do not capture any variables"
640 );
641 return true;
642 }
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 diag.span_suggestion_verbose(
1306 expr.span.shrink_to_hi(),
1307 "consider using clone here",
1308 suggestion,
1309 Applicability::MachineApplicable,
1310 );
1311 return true;
1312 }
1313 false
1314 }
1315
1316 pub(crate) fn suggest_copied_cloned_or_as_ref(
1317 &self,
1318 diag: &mut Diag<'_>,
1319 expr: &hir::Expr<'_>,
1320 expr_ty: Ty<'tcx>,
1321 expected_ty: Ty<'tcx>,
1322 ) -> bool {
1323 let ty::Adt(adt_def, args) = expr_ty.kind() else {
1324 return false;
1325 };
1326 let ty::Adt(expected_adt_def, expected_args) = expected_ty.kind() else {
1327 return false;
1328 };
1329 if adt_def != expected_adt_def {
1330 return false;
1331 }
1332
1333 if Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Result)
1334 && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
1335 || Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Option)
1336 {
1337 let expr_inner_ty = args.type_at(0);
1338 let expected_inner_ty = expected_args.type_at(0);
1339 if let &ty::Ref(_, ty, _mutability) = expr_inner_ty.kind()
1340 && self.can_eq(self.param_env, ty, expected_inner_ty)
1341 {
1342 let def_path = self.tcx.def_path_str(adt_def.did());
1343 let span = expr.span.shrink_to_hi();
1344 let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
1345 errors::OptionResultRefMismatch::Copied { span, def_path }
1346 } else if self.type_is_clone_modulo_regions(self.param_env, ty) {
1347 errors::OptionResultRefMismatch::Cloned { span, def_path }
1348 } else {
1349 return false;
1350 };
1351 diag.subdiagnostic(subdiag);
1352 return true;
1353 }
1354 }
1355
1356 false
1357 }
1358
1359 pub(crate) fn suggest_into(
1360 &self,
1361 diag: &mut Diag<'_>,
1362 expr: &hir::Expr<'_>,
1363 expr_ty: Ty<'tcx>,
1364 expected_ty: Ty<'tcx>,
1365 ) -> bool {
1366 let expr = expr.peel_blocks();
1367
1368 if expr_ty.is_scalar() && expected_ty.is_scalar() {
1370 return false;
1371 }
1372
1373 if matches!(expr.kind, hir::ExprKind::Block(..)) {
1375 return false;
1376 }
1377
1378 if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() {
1381 return false;
1382 }
1383
1384 if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
1385 && self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
1386 self.tcx,
1387 self.misc(expr.span),
1388 self.param_env,
1389 ty::TraitRef::new(self.tcx, into_def_id, [expr_ty, expected_ty]),
1390 ))
1391 && !expr
1392 .span
1393 .macro_backtrace()
1394 .any(|x| matches!(x.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..)))
1395 {
1396 let span = expr.span.find_oldest_ancestor_in_same_ctxt();
1397
1398 let mut sugg = if expr.precedence() >= ExprPrecedence::Unambiguous {
1399 vec![(span.shrink_to_hi(), ".into()".to_owned())]
1400 } else {
1401 vec![
1402 (span.shrink_to_lo(), "(".to_owned()),
1403 (span.shrink_to_hi(), ").into()".to_owned()),
1404 ]
1405 };
1406 if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1407 sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
1408 }
1409 diag.multipart_suggestion(
1410 format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
1411 sugg,
1412 Applicability::MaybeIncorrect
1413 );
1414 return true;
1415 }
1416
1417 false
1418 }
1419
1420 pub(crate) fn suggest_option_to_bool(
1422 &self,
1423 diag: &mut Diag<'_>,
1424 expr: &hir::Expr<'_>,
1425 expr_ty: Ty<'tcx>,
1426 expected_ty: Ty<'tcx>,
1427 ) -> bool {
1428 if !expected_ty.is_bool() {
1429 return false;
1430 }
1431
1432 let ty::Adt(def, _) = expr_ty.peel_refs().kind() else {
1433 return false;
1434 };
1435 if !self.tcx.is_diagnostic_item(sym::Option, def.did()) {
1436 return false;
1437 }
1438
1439 let cond_parent = self.tcx.hir_parent_iter(expr.hir_id).find(|(_, node)| {
1440 !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
1441 });
1442 if let Some((_, hir::Node::LetStmt(local))) = cond_parent
1448 && let hir::PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
1449 | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind
1450 && let hir::QPath::Resolved(None, path) = qpath
1451 && let Some(did) = path
1452 .res
1453 .opt_def_id()
1454 .and_then(|did| self.tcx.opt_parent(did))
1455 .and_then(|did| self.tcx.opt_parent(did))
1456 && self.tcx.is_diagnostic_item(sym::Option, did)
1457 {
1458 return false;
1459 }
1460
1461 let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1462 Some(ident) => format!(": {ident}.is_some()"),
1463 None => ".is_some()".to_string(),
1464 };
1465
1466 diag.span_suggestion_verbose(
1467 expr.span.shrink_to_hi(),
1468 "use `Option::is_some` to test if the `Option` has a value",
1469 suggestion,
1470 Applicability::MachineApplicable,
1471 );
1472 true
1473 }
1474
1475 #[instrument(level = "trace", skip(self, err, provided_expr))]
1477 pub(crate) fn suggest_deref_unwrap_or(
1478 &self,
1479 err: &mut Diag<'_>,
1480 callee_ty: Option<Ty<'tcx>>,
1481 call_ident: Option<Ident>,
1482 expected_ty: Ty<'tcx>,
1483 provided_ty: Ty<'tcx>,
1484 provided_expr: &Expr<'tcx>,
1485 is_method: bool,
1486 ) {
1487 if !is_method {
1488 return;
1489 }
1490 let Some(callee_ty) = callee_ty else {
1491 return;
1492 };
1493 let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
1494 return;
1495 };
1496 let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
1497 "Option"
1498 } else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) {
1499 "Result"
1500 } else {
1501 return;
1502 };
1503
1504 let Some(call_ident) = call_ident else {
1505 return;
1506 };
1507 if call_ident.name != sym::unwrap_or {
1508 return;
1509 }
1510
1511 let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
1512 return;
1513 };
1514
1515 let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
1519 && let ty::Infer(_) = elem_ty.kind()
1520 && self
1521 .try_structurally_resolve_const(provided_expr.span, *size)
1522 .try_to_target_usize(self.tcx)
1523 == Some(0)
1524 {
1525 let slice = Ty::new_slice(self.tcx, *elem_ty);
1526 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
1527 } else {
1528 provided_ty
1529 };
1530
1531 if !self.may_coerce(expected_ty, dummy_ty) {
1532 return;
1533 }
1534 let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
1535 err.multipart_suggestion_verbose(
1536 msg,
1537 vec![
1538 (call_ident.span, "map_or".to_owned()),
1539 (provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()),
1540 ],
1541 Applicability::MachineApplicable,
1542 );
1543 }
1544
1545 pub(crate) fn suggest_block_to_brackets(
1548 &self,
1549 diag: &mut Diag<'_>,
1550 blk: &hir::Block<'_>,
1551 blk_ty: Ty<'tcx>,
1552 expected_ty: Ty<'tcx>,
1553 ) {
1554 if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
1555 if self.may_coerce(blk_ty, *elem_ty)
1556 && blk.stmts.is_empty()
1557 && blk.rules == hir::BlockCheckMode::DefaultBlock
1558 && let source_map = self.tcx.sess.source_map()
1559 && let Ok(snippet) = source_map.span_to_snippet(blk.span)
1560 && snippet.starts_with('{')
1561 && snippet.ends_with('}')
1562 {
1563 diag.multipart_suggestion_verbose(
1564 "to create an array, use square brackets instead of curly braces",
1565 vec![
1566 (
1567 blk.span
1568 .shrink_to_lo()
1569 .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
1570 "[".to_string(),
1571 ),
1572 (
1573 blk.span
1574 .shrink_to_hi()
1575 .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
1576 "]".to_string(),
1577 ),
1578 ],
1579 Applicability::MachineApplicable,
1580 );
1581 }
1582 }
1583 }
1584
1585 #[instrument(skip(self, err))]
1586 pub(crate) fn suggest_floating_point_literal(
1587 &self,
1588 err: &mut Diag<'_>,
1589 expr: &hir::Expr<'_>,
1590 expected_ty: Ty<'tcx>,
1591 ) -> bool {
1592 if !expected_ty.is_floating_point() {
1593 return false;
1594 }
1595 match expr.kind {
1596 ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => {
1597 err.span_suggestion_verbose(
1598 start.span.shrink_to_hi().with_hi(end.span.lo()),
1599 "remove the unnecessary `.` operator for a floating point literal",
1600 '.',
1601 Applicability::MaybeIncorrect,
1602 );
1603 true
1604 }
1605 ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => {
1606 err.span_suggestion_verbose(
1607 expr.span.with_lo(start.span.hi()),
1608 "remove the unnecessary `.` operator for a floating point literal",
1609 '.',
1610 Applicability::MaybeIncorrect,
1611 );
1612 true
1613 }
1614 ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => {
1615 err.span_suggestion_verbose(
1616 expr.span.until(end.span),
1617 "remove the unnecessary `.` operator and add an integer part for a floating point literal",
1618 "0.",
1619 Applicability::MaybeIncorrect,
1620 );
1621 true
1622 }
1623 ExprKind::Lit(Spanned {
1624 node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed),
1625 span,
1626 }) => {
1627 let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(*span) else {
1628 return false;
1629 };
1630 if !(snippet.starts_with("0x") || snippet.starts_with("0X")) {
1631 return false;
1632 }
1633 if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) {
1634 return false;
1635 }
1636 let (_, suffix) = snippet.split_at(snippet.len() - 3);
1637 let value = match suffix {
1638 "f32" => (lit.get() - 0xf32) / (16 * 16 * 16),
1639 "f64" => (lit.get() - 0xf64) / (16 * 16 * 16),
1640 _ => return false,
1641 };
1642 err.span_suggestions(
1643 expr.span,
1644 "rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float",
1645 [format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")],
1646 Applicability::MaybeIncorrect,
1647 );
1648 true
1649 }
1650 _ => false,
1651 }
1652 }
1653
1654 #[instrument(skip(self, err))]
1657 pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg(
1658 &self,
1659 err: &mut Diag<'_>,
1660 expr: &hir::Expr<'_>,
1661 expected_ty: Ty<'tcx>,
1662 ) -> bool {
1663 let ty::RawPtr(_, mutbl) = expected_ty.kind() else {
1665 return false;
1666 };
1667
1668 let ExprKind::Lit(Spanned { node: rustc_ast::LitKind::Int(Pu128(0), _), span }) = expr.kind
1670 else {
1671 return false;
1672 };
1673
1674 let null_sym = match mutbl {
1676 hir::Mutability::Not => sym::ptr_null,
1677 hir::Mutability::Mut => sym::ptr_null_mut,
1678 };
1679 let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else {
1680 return false;
1681 };
1682 let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did));
1683
1684 err.span_suggestion(
1686 *span,
1687 format!("if you meant to create a null pointer, use `{null_path_str}()`"),
1688 null_path_str + "()",
1689 Applicability::MachineApplicable,
1690 );
1691
1692 true
1693 }
1694
1695 pub(crate) fn suggest_associated_const(
1696 &self,
1697 err: &mut Diag<'_>,
1698 expr: &hir::Expr<'tcx>,
1699 expected_ty: Ty<'tcx>,
1700 ) -> bool {
1701 let Some((DefKind::AssocFn, old_def_id)) =
1702 self.typeck_results.borrow().type_dependent_def(expr.hir_id)
1703 else {
1704 return false;
1705 };
1706 let old_item_name = self.tcx.item_name(old_def_id);
1707 let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
1708 if old_item_name == capitalized_name {
1709 return false;
1710 }
1711 let (item, segment) = match expr.kind {
1712 hir::ExprKind::Path(QPath::Resolved(
1713 Some(ty),
1714 hir::Path { segments: [segment], .. },
1715 ))
1716 | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => {
1717 if let Some(self_ty) = self.typeck_results.borrow().node_type_opt(ty.hir_id)
1718 && let Ok(pick) = self.probe_for_name(
1719 Mode::Path,
1720 Ident::new(capitalized_name, segment.ident.span),
1721 Some(expected_ty),
1722 IsSuggestion(true),
1723 self_ty,
1724 expr.hir_id,
1725 ProbeScope::TraitsInScope,
1726 )
1727 {
1728 (pick.item, segment)
1729 } else {
1730 return false;
1731 }
1732 }
1733 hir::ExprKind::Path(QPath::Resolved(
1734 None,
1735 hir::Path { segments: [.., segment], .. },
1736 )) => {
1737 if old_item_name != segment.ident.name {
1740 return false;
1741 }
1742 if let Some(item) = self
1743 .tcx
1744 .associated_items(self.tcx.parent(old_def_id))
1745 .filter_by_name_unhygienic(capitalized_name)
1746 .next()
1747 {
1748 (*item, segment)
1749 } else {
1750 return false;
1751 }
1752 }
1753 _ => return false,
1754 };
1755 if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst {
1756 return false;
1758 }
1759 let item_ty = self.tcx.type_of(item.def_id).instantiate_identity();
1760 if item_ty.has_param() {
1762 return false;
1763 }
1764 if self.may_coerce(item_ty, expected_ty) {
1765 err.span_suggestion_verbose(
1766 segment.ident.span,
1767 format!("try referring to the associated const `{capitalized_name}` instead",),
1768 capitalized_name,
1769 Applicability::MachineApplicable,
1770 );
1771 true
1772 } else {
1773 false
1774 }
1775 }
1776
1777 fn is_loop(&self, id: HirId) -> bool {
1778 let node = self.tcx.hir_node(id);
1779 matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
1780 }
1781
1782 fn is_local_statement(&self, id: HirId) -> bool {
1783 let node = self.tcx.hir_node(id);
1784 matches!(node, Node::Stmt(Stmt { kind: StmtKind::Let(..), .. }))
1785 }
1786
1787 pub(crate) fn note_type_is_not_clone(
1790 &self,
1791 diag: &mut Diag<'_>,
1792 expected_ty: Ty<'tcx>,
1793 found_ty: Ty<'tcx>,
1794 expr: &hir::Expr<'_>,
1795 ) {
1796 let expr = self.note_type_is_not_clone_inner_expr(expr);
1799
1800 let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else {
1802 return;
1803 };
1804
1805 let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else {
1806 return;
1807 };
1808 let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
1809 let results = self.typeck_results.borrow();
1810 if segment.ident.name == sym::clone
1812 && results.type_dependent_def_id(expr.hir_id).is_some_and(|did| {
1813 let assoc_item = self.tcx.associated_item(did);
1814 assoc_item.container == ty::AssocItemContainer::Trait
1815 && assoc_item.container_id(self.tcx) == clone_trait_did
1816 })
1817 && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
1820 && self.may_coerce(*pointee_ty, expected_ty)
1822 && let trait_ref = ty::TraitRef::new(self.tcx, clone_trait_did, [expected_ty])
1823 && !self.predicate_must_hold_considering_regions(&traits::Obligation::new(
1825 self.tcx,
1826 traits::ObligationCause::dummy(),
1827 self.param_env,
1828 trait_ref,
1829 ))
1830 {
1831 diag.span_note(
1832 callee_expr.span,
1833 format!(
1834 "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
1835 ),
1836 );
1837 let owner = self.tcx.hir_enclosing_body_owner(expr.hir_id);
1838 if let ty::Param(param) = expected_ty.kind()
1839 && let Some(generics) = self.tcx.hir_get_generics(owner)
1840 {
1841 suggest_constraining_type_params(
1842 self.tcx,
1843 generics,
1844 diag,
1845 vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
1846 None,
1847 );
1848 } else {
1849 if let Some(errors) =
1850 self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env)
1851 {
1852 match &errors[..] {
1853 [] => {}
1854 [error] => {
1855 diag.help(format!(
1856 "`Clone` is not implemented because the trait bound `{}` is \
1857 not satisfied",
1858 error.obligation.predicate,
1859 ));
1860 }
1861 _ => {
1862 diag.help(format!(
1863 "`Clone` is not implemented because the following trait bounds \
1864 could not be satisfied: {}",
1865 listify(&errors, |e| format!("`{}`", e.obligation.predicate))
1866 .unwrap(),
1867 ));
1868 }
1869 }
1870 for error in errors {
1871 if let traits::FulfillmentErrorCode::Select(
1872 traits::SelectionError::Unimplemented,
1873 ) = error.code
1874 && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1875 error.obligation.predicate.kind().skip_binder()
1876 {
1877 self.infcx.err_ctxt().suggest_derive(
1878 &error.obligation,
1879 diag,
1880 error.obligation.predicate.kind().rebind(pred),
1881 );
1882 }
1883 }
1884 }
1885 self.suggest_derive(diag, &[(trait_ref.upcast(self.tcx), None, None)]);
1886 }
1887 }
1888 }
1889
1890 fn note_type_is_not_clone_inner_expr<'b>(
1895 &'b self,
1896 expr: &'b hir::Expr<'b>,
1897 ) -> &'b hir::Expr<'b> {
1898 match expr.peel_blocks().kind {
1899 hir::ExprKind::Path(hir::QPath::Resolved(
1900 None,
1901 hir::Path { segments: [_], res: crate::Res::Local(binding), .. },
1902 )) => {
1903 let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding) else {
1904 return expr;
1905 };
1906
1907 match self.tcx.parent_hir_node(*hir_id) {
1908 hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => {
1910 self.note_type_is_not_clone_inner_expr(init)
1911 }
1912 hir::Node::Pat(hir::Pat {
1914 hir_id: pat_hir_id,
1915 kind: hir::PatKind::Tuple(pats, ..),
1916 ..
1917 }) => {
1918 let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
1919 self.tcx.parent_hir_node(*pat_hir_id)
1920 else {
1921 return expr;
1922 };
1923
1924 match init.peel_blocks().kind {
1925 ExprKind::Tup(init_tup) => {
1926 if let Some(init) = pats
1927 .iter()
1928 .enumerate()
1929 .filter(|x| x.1.hir_id == *hir_id)
1930 .find_map(|(i, _)| init_tup.get(i))
1931 {
1932 self.note_type_is_not_clone_inner_expr(init)
1933 } else {
1934 expr
1935 }
1936 }
1937 _ => expr,
1938 }
1939 }
1940 _ => expr,
1941 }
1942 }
1943 hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => {
1947 if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) =
1948 call_expr_kind
1949 && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } =
1950 call_expr_path
1951 && let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding)
1952 && let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
1953 self.tcx.parent_hir_node(*hir_id)
1954 && let Expr {
1955 kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }),
1956 ..
1957 } = init
1958 {
1959 let hir::Body { value: body_expr, .. } = self.tcx.hir_body(*body_id);
1960 self.note_type_is_not_clone_inner_expr(body_expr)
1961 } else {
1962 expr
1963 }
1964 }
1965 _ => expr,
1966 }
1967 }
1968
1969 pub(crate) fn is_field_suggestable(
1970 &self,
1971 field: &ty::FieldDef,
1972 hir_id: HirId,
1973 span: Span,
1974 ) -> bool {
1975 field.vis.is_accessible_from(self.tcx.parent_module(hir_id), self.tcx)
1977 && !matches!(
1979 self.tcx.eval_stability(field.did, None, rustc_span::DUMMY_SP, None),
1980 rustc_middle::middle::stability::EvalResult::Deny { .. }
1981 )
1982 && (field.did.is_local() || !self.tcx.is_doc_hidden(field.did))
1984 && self.tcx.def_ident_span(field.did).unwrap().normalize_to_macros_2_0().eq_ctxt(span)
1986 }
1987
1988 pub(crate) fn suggest_missing_unwrap_expect(
1989 &self,
1990 err: &mut Diag<'_>,
1991 expr: &hir::Expr<'tcx>,
1992 expected: Ty<'tcx>,
1993 found: Ty<'tcx>,
1994 ) -> bool {
1995 let ty::Adt(adt, args) = found.kind() else {
1996 return false;
1997 };
1998 let ret_ty_matches = |diagnostic_item| {
1999 let Some(sig) = self.body_fn_sig() else {
2000 return false;
2001 };
2002 let ty::Adt(kind, _) = sig.output().kind() else {
2003 return false;
2004 };
2005 self.tcx.is_diagnostic_item(diagnostic_item, kind.did())
2006 };
2007
2008 let is_ctor = matches!(
2011 expr.kind,
2012 hir::ExprKind::Call(
2013 hir::Expr {
2014 kind: hir::ExprKind::Path(hir::QPath::Resolved(
2015 None,
2016 hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
2017 )),
2018 ..
2019 },
2020 ..,
2021 ) | hir::ExprKind::Path(hir::QPath::Resolved(
2022 None,
2023 hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
2024 )),
2025 );
2026
2027 let (article, kind, variant, sugg_operator) =
2028 if self.tcx.is_diagnostic_item(sym::Result, adt.did()) {
2029 ("a", "Result", "Err", ret_ty_matches(sym::Result))
2030 } else if self.tcx.is_diagnostic_item(sym::Option, adt.did()) {
2031 ("an", "Option", "None", ret_ty_matches(sym::Option))
2032 } else {
2033 return false;
2034 };
2035 if is_ctor || !self.may_coerce(args.type_at(0), expected) {
2036 return false;
2037 }
2038
2039 let (msg, sugg) = if sugg_operator {
2040 (
2041 format!(
2042 "use the `?` operator to extract the `{found}` value, propagating \
2043 {article} `{kind}::{variant}` value to the caller"
2044 ),
2045 "?",
2046 )
2047 } else {
2048 (
2049 format!(
2050 "consider using `{kind}::expect` to unwrap the `{found}` value, \
2051 panicking if the value is {article} `{kind}::{variant}`"
2052 ),
2053 ".expect(\"REASON\")",
2054 )
2055 };
2056
2057 let sugg = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2058 Some(_) if expr.span.from_expansion() => return false,
2059 Some(ident) => format!(": {ident}{sugg}"),
2060 None => sugg.to_string(),
2061 };
2062
2063 let span = expr.span.find_oldest_ancestor_in_same_ctxt();
2064 err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders);
2065 true
2066 }
2067
2068 pub(crate) fn suggest_coercing_result_via_try_operator(
2069 &self,
2070 err: &mut Diag<'_>,
2071 expr: &hir::Expr<'tcx>,
2072 expected: Ty<'tcx>,
2073 found: Ty<'tcx>,
2074 ) -> bool {
2075 let returned = matches!(
2076 self.tcx.parent_hir_node(expr.hir_id),
2077 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })
2078 ) || self.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_some();
2079 if returned
2080 && let ty::Adt(e, args_e) = expected.kind()
2081 && let ty::Adt(f, args_f) = found.kind()
2082 && e.did() == f.did()
2083 && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
2084 && let e_ok = args_e.type_at(0)
2085 && let f_ok = args_f.type_at(0)
2086 && self.infcx.can_eq(self.param_env, f_ok, e_ok)
2087 && let e_err = args_e.type_at(1)
2088 && let f_err = args_f.type_at(1)
2089 && self
2090 .infcx
2091 .type_implements_trait(
2092 self.tcx.get_diagnostic_item(sym::Into).unwrap(),
2093 [f_err, e_err],
2094 self.param_env,
2095 )
2096 .must_apply_modulo_regions()
2097 {
2098 err.multipart_suggestion(
2099 "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
2100 in `Ok` so the expression remains of type `Result`",
2101 vec![
2102 (expr.span.shrink_to_lo(), "Ok(".to_string()),
2103 (expr.span.shrink_to_hi(), "?)".to_string()),
2104 ],
2105 Applicability::MaybeIncorrect,
2106 );
2107 return true;
2108 }
2109 false
2110 }
2111
2112 pub(crate) fn suggest_returning_value_after_loop(
2115 &self,
2116 err: &mut Diag<'_>,
2117 expr: &hir::Expr<'tcx>,
2118 expected: Ty<'tcx>,
2119 ) -> bool {
2120 let tcx = self.tcx;
2121 let enclosing_scope =
2122 tcx.hir_get_enclosing_scope(expr.hir_id).map(|hir_id| tcx.hir_node(hir_id));
2123
2124 let tail_expr = if let Some(Node::Block(hir::Block { expr, .. })) = enclosing_scope
2126 && expr.is_some()
2127 {
2128 *expr
2129 } else {
2130 let body_def_id = tcx.hir_enclosing_body_owner(expr.hir_id);
2131 let body = tcx.hir_body_owned_by(body_def_id);
2132
2133 match body.value.kind {
2135 hir::ExprKind::Block(block, _) => block.expr,
2137 hir::ExprKind::DropTemps(expr) => Some(expr),
2139 _ => None,
2140 }
2141 };
2142
2143 let Some(tail_expr) = tail_expr else {
2144 return false; };
2146
2147 let loop_expr_in_tail = match expr.kind {
2149 hir::ExprKind::Loop(_, _, hir::LoopSource::While, _) => tail_expr,
2150 hir::ExprKind::Loop(_, _, hir::LoopSource::ForLoop, _) => {
2151 match tail_expr.peel_drop_temps() {
2152 Expr { kind: ExprKind::Match(_, [Arm { body, .. }], _), .. } => body,
2153 _ => return false, }
2155 }
2156 _ => return false, };
2158
2159 if expr.hir_id == loop_expr_in_tail.hir_id {
2162 let span = expr.span;
2163
2164 let (msg, suggestion) = if expected.is_never() {
2165 (
2166 "consider adding a diverging expression here",
2167 "`loop {}` or `panic!(\"...\")`".to_string(),
2168 )
2169 } else {
2170 ("consider returning a value here", format!("`{expected}` value"))
2171 };
2172
2173 let src_map = tcx.sess.source_map();
2174 let suggestion = if src_map.is_multiline(expr.span) {
2175 let indentation = src_map.indentation_before(span).unwrap_or_else(String::new);
2176 format!("\n{indentation}/* {suggestion} */")
2177 } else {
2178 format!(" /* {suggestion} */")
2181 };
2182
2183 err.span_suggestion_verbose(
2184 span.shrink_to_hi(),
2185 msg,
2186 suggestion,
2187 Applicability::MaybeIncorrect,
2188 );
2189
2190 true
2191 } else {
2192 false
2193 }
2194 }
2195
2196 pub(crate) fn suggest_semicolon_in_repeat_expr(
2199 &self,
2200 err: &mut Diag<'_>,
2201 expr: &hir::Expr<'_>,
2202 expr_ty: Ty<'tcx>,
2203 ) -> bool {
2204 if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id)
2206 && let hir::ExprKind::Array(elements) = array_expr.kind
2207 && let [first, second] = &elements[..]
2208 && second.hir_id == expr.hir_id
2209 {
2210 let comma_span = first.span.between(second.span);
2212
2213 let expr_is_const_usize = expr_ty.is_usize()
2221 && match expr.kind {
2222 ExprKind::Path(QPath::Resolved(
2223 None,
2224 Path { res: Res::Def(DefKind::Const, _), .. },
2225 )) => true,
2226 ExprKind::Call(
2227 Expr {
2228 kind:
2229 ExprKind::Path(QPath::Resolved(
2230 None,
2231 Path { res: Res::Def(DefKind::Fn, fn_def_id), .. },
2232 )),
2233 ..
2234 },
2235 _,
2236 ) => self.tcx.is_const_fn(*fn_def_id),
2237 _ => false,
2238 };
2239
2240 let first_ty = self.typeck_results.borrow().expr_ty(first);
2245
2246 if self.tcx.sess.source_map().is_imported(array_expr.span)
2251 && self.type_is_clone_modulo_regions(self.param_env, first_ty)
2252 && (expr.is_size_lit() || expr_ty.is_usize_like())
2253 {
2254 err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2255 comma_span,
2256 descr: "a vector",
2257 });
2258 return true;
2259 }
2260
2261 if self.type_is_copy_modulo_regions(self.param_env, first_ty)
2265 && (expr.is_size_lit() || expr_is_const_usize)
2266 {
2267 err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2268 comma_span,
2269 descr: "an array",
2270 });
2271 return true;
2272 }
2273 }
2274 false
2275 }
2276
2277 pub(crate) fn suggest_compatible_variants(
2280 &self,
2281 err: &mut Diag<'_>,
2282 expr: &hir::Expr<'_>,
2283 expected: Ty<'tcx>,
2284 expr_ty: Ty<'tcx>,
2285 ) -> bool {
2286 if expr.span.in_external_macro(self.tcx.sess.source_map()) {
2287 return false;
2288 }
2289 if let ty::Adt(expected_adt, args) = expected.kind() {
2290 if let hir::ExprKind::Field(base, ident) = expr.kind {
2291 let base_ty = self.typeck_results.borrow().expr_ty(base);
2292 if self.can_eq(self.param_env, base_ty, expected)
2293 && let Some(base_span) = base.span.find_ancestor_inside(expr.span)
2294 {
2295 err.span_suggestion_verbose(
2296 expr.span.with_lo(base_span.hi()),
2297 format!("consider removing the tuple struct field `{ident}`"),
2298 "",
2299 Applicability::MaybeIncorrect,
2300 );
2301 return true;
2302 }
2303 }
2304
2305 if expr_ty.is_unit() {
2309 let mut id = expr.hir_id;
2310 let mut parent;
2311
2312 loop {
2314 parent = self.tcx.parent_hir_id(id);
2315 let parent_span = self.tcx.hir_span(parent);
2316 if parent_span.find_ancestor_inside(expr.span).is_some() {
2317 id = parent;
2320 continue;
2321 }
2322 break;
2323 }
2324
2325 if let hir::Node::Block(&hir::Block { span: block_span, expr: Some(e), .. }) =
2326 self.tcx.hir_node(parent)
2327 {
2328 if e.hir_id == id {
2329 if let Some(span) = expr.span.find_ancestor_inside(block_span) {
2330 let return_suggestions = if self
2331 .tcx
2332 .is_diagnostic_item(sym::Result, expected_adt.did())
2333 {
2334 vec!["Ok(())"]
2335 } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
2336 vec!["None", "Some(())"]
2337 } else {
2338 return false;
2339 };
2340 if let Some(indent) =
2341 self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
2342 {
2343 let semicolon =
2345 match self.tcx.sess.source_map().span_to_snippet(span) {
2346 Ok(s) if s.ends_with('}') => "",
2347 _ => ";",
2348 };
2349 err.span_suggestions(
2350 span.shrink_to_hi(),
2351 "try adding an expression at the end of the block",
2352 return_suggestions
2353 .into_iter()
2354 .map(|r| format!("{semicolon}\n{indent}{r}")),
2355 Applicability::MaybeIncorrect,
2356 );
2357 }
2358 return true;
2359 }
2360 }
2361 }
2362 }
2363
2364 let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
2365 .variants()
2366 .iter()
2367 .filter(|variant| {
2368 variant.fields.len() == 1
2369 })
2370 .filter_map(|variant| {
2371 let sole_field = &variant.single_field();
2372
2373 let field_is_local = sole_field.did.is_local();
2374 let field_is_accessible =
2375 sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
2376 && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
2378
2379 if !field_is_local && !field_is_accessible {
2380 return None;
2381 }
2382
2383 let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
2384 .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
2385
2386 let sole_field_ty = sole_field.ty(self.tcx, args);
2387 if self.may_coerce(expr_ty, sole_field_ty) {
2388 let variant_path =
2389 with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
2390 if let Some(path) = variant_path.strip_prefix("std::prelude::")
2392 && let Some((_, path)) = path.split_once("::")
2393 {
2394 return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy));
2395 }
2396 Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy))
2397 } else {
2398 None
2399 }
2400 })
2401 .collect();
2402
2403 let suggestions_for = |variant: &_, ctor_kind, field_name| {
2404 let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2405 Some(ident) => format!("{ident}: "),
2406 None => String::new(),
2407 };
2408
2409 let (open, close) = match ctor_kind {
2410 Some(CtorKind::Fn) => ("(".to_owned(), ")"),
2411 None => (format!(" {{ {field_name}: "), " }"),
2412
2413 Some(CtorKind::Const) => unreachable!("unit variants don't have fields"),
2414 };
2415
2416 let mut expr = expr;
2420 while let hir::ExprKind::Block(block, _) = &expr.kind
2421 && let Some(expr_) = &block.expr
2422 {
2423 expr = expr_
2424 }
2425
2426 vec![
2427 (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
2428 (expr.span.shrink_to_hi(), close.to_owned()),
2429 ]
2430 };
2431
2432 match &compatible_variants[..] {
2433 [] => { }
2434 [(variant, ctor_kind, field_name, note)] => {
2435 err.multipart_suggestion_verbose(
2437 format!(
2438 "try wrapping the expression in `{variant}`{note}",
2439 note = note.as_deref().unwrap_or("")
2440 ),
2441 suggestions_for(&**variant, *ctor_kind, *field_name),
2442 Applicability::MaybeIncorrect,
2443 );
2444 return true;
2445 }
2446 _ => {
2447 err.multipart_suggestions(
2449 format!(
2450 "try wrapping the expression in a variant of `{}`",
2451 self.tcx.def_path_str(expected_adt.did())
2452 ),
2453 compatible_variants.into_iter().map(
2454 |(variant, ctor_kind, field_name, _)| {
2455 suggestions_for(&variant, ctor_kind, field_name)
2456 },
2457 ),
2458 Applicability::MaybeIncorrect,
2459 );
2460 return true;
2461 }
2462 }
2463 }
2464
2465 false
2466 }
2467
2468 pub(crate) fn suggest_non_zero_new_unwrap(
2469 &self,
2470 err: &mut Diag<'_>,
2471 expr: &hir::Expr<'_>,
2472 expected: Ty<'tcx>,
2473 expr_ty: Ty<'tcx>,
2474 ) -> bool {
2475 let tcx = self.tcx;
2476 let (adt, args, unwrap) = match expected.kind() {
2477 ty::Adt(adt, args) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
2479 let nonzero_type = args.type_at(0); let ty::Adt(adt, args) = nonzero_type.kind() else {
2481 return false;
2482 };
2483 (adt, args, "")
2484 }
2485 ty::Adt(adt, args) => (adt, args, ".unwrap()"),
2487 _ => return false,
2488 };
2489
2490 if !self.tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
2491 return false;
2492 }
2493
2494 let int_type = args.type_at(0);
2495 if !self.may_coerce(expr_ty, int_type) {
2496 return false;
2497 }
2498
2499 err.multipart_suggestion(
2500 format!("consider calling `{}::new`", sym::NonZero),
2501 vec![
2502 (expr.span.shrink_to_lo(), format!("{}::new(", sym::NonZero)),
2503 (expr.span.shrink_to_hi(), format!("){unwrap}")),
2504 ],
2505 Applicability::MaybeIncorrect,
2506 );
2507
2508 true
2509 }
2510
2511 fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> {
2528 let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind else {
2529 return None;
2530 };
2531
2532 let hir::def::Res::Local(local_id) = path.res else {
2533 return None;
2534 };
2535
2536 let Node::Param(hir::Param { hir_id: param_hir_id, .. }) =
2537 self.tcx.parent_hir_node(local_id)
2538 else {
2539 return None;
2540 };
2541
2542 let Node::Expr(hir::Expr {
2543 hir_id: expr_hir_id,
2544 kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
2545 ..
2546 }) = self.tcx.parent_hir_node(*param_hir_id)
2547 else {
2548 return None;
2549 };
2550
2551 let hir = self.tcx.parent_hir_node(*expr_hir_id);
2552 let closure_params_len = closure_fn_decl.inputs.len();
2553 let (
2554 Node::Expr(hir::Expr {
2555 kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
2556 ..
2557 }),
2558 1,
2559 ) = (hir, closure_params_len)
2560 else {
2561 return None;
2562 };
2563
2564 let self_ty = self.typeck_results.borrow().expr_ty(receiver);
2565 let name = method_path.ident.name;
2566 let is_as_ref_able = match self_ty.peel_refs().kind() {
2567 ty::Adt(def, _) => {
2568 (self.tcx.is_diagnostic_item(sym::Option, def.did())
2569 || self.tcx.is_diagnostic_item(sym::Result, def.did()))
2570 && (name == sym::map || name == sym::and_then)
2571 }
2572 _ => false,
2573 };
2574 if is_as_ref_able {
2575 Some((
2576 vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())],
2577 "consider using `as_ref` instead",
2578 ))
2579 } else {
2580 None
2581 }
2582 }
2583
2584 pub(crate) fn suggest_deref_or_ref(
2601 &self,
2602 expr: &hir::Expr<'tcx>,
2603 checked_ty: Ty<'tcx>,
2604 expected: Ty<'tcx>,
2605 ) -> Option<(
2606 Vec<(Span, String)>,
2607 String,
2608 Applicability,
2609 bool, bool, )> {
2612 let sess = self.sess();
2613 let sp = expr.span;
2614 let sm = sess.source_map();
2615
2616 if sp.in_external_macro(sm) {
2618 return None;
2619 }
2620
2621 let replace_prefix = |s: &str, old: &str, new: &str| {
2622 s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
2623 };
2624
2625 let expr = expr.peel_drop_temps();
2627
2628 match (&expr.kind, expected.kind(), checked_ty.kind()) {
2629 (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
2630 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
2631 if let hir::ExprKind::Lit(_) = expr.kind
2632 && let Ok(src) = sm.span_to_snippet(sp)
2633 && replace_prefix(&src, "b\"", "\"").is_some()
2634 {
2635 let pos = sp.lo() + BytePos(1);
2636 return Some((
2637 vec![(sp.with_hi(pos), String::new())],
2638 "consider removing the leading `b`".to_string(),
2639 Applicability::MachineApplicable,
2640 true,
2641 false,
2642 ));
2643 }
2644 }
2645 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
2646 if let hir::ExprKind::Lit(_) = expr.kind
2647 && let Ok(src) = sm.span_to_snippet(sp)
2648 && replace_prefix(&src, "\"", "b\"").is_some()
2649 {
2650 return Some((
2651 vec![(sp.shrink_to_lo(), "b".to_string())],
2652 "consider adding a leading `b`".to_string(),
2653 Applicability::MachineApplicable,
2654 true,
2655 false,
2656 ));
2657 }
2658 }
2659 _ => {}
2660 },
2661 (_, &ty::Ref(_, _, mutability), _) => {
2662 let ref_ty = match mutability {
2671 hir::Mutability::Mut => {
2672 Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2673 }
2674 hir::Mutability::Not => {
2675 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2676 }
2677 };
2678 if self.may_coerce(ref_ty, expected) {
2679 let mut sugg_sp = sp;
2680 if let hir::ExprKind::MethodCall(segment, receiver, args, _) = expr.kind {
2681 let clone_trait =
2682 self.tcx.require_lang_item(LangItem::Clone, segment.ident.span);
2683 if args.is_empty()
2684 && self
2685 .typeck_results
2686 .borrow()
2687 .type_dependent_def_id(expr.hir_id)
2688 .is_some_and(|did| {
2689 let ai = self.tcx.associated_item(did);
2690 ai.trait_container(self.tcx) == Some(clone_trait)
2691 })
2692 && segment.ident.name == sym::clone
2693 {
2694 sugg_sp = receiver.span;
2697 }
2698 }
2699
2700 if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
2701 && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty)
2702 && self.typeck_results.borrow().expr_ty(inner).is_ref()
2703 {
2704 sugg_sp = sugg_sp.with_hi(inner.span.lo());
2707 return Some((
2708 vec![(sugg_sp, String::new())],
2709 "consider removing deref here".to_string(),
2710 Applicability::MachineApplicable,
2711 true,
2712 false,
2713 ));
2714 }
2715
2716 if let hir::ExprKind::If(_c, then, els) = expr.kind {
2722 let ExprKind::Block(then, _) = then.kind else { return None };
2725 let Some(then) = then.expr else { return None };
2726 let (mut suggs, help, app, verbose, mutref) =
2727 self.suggest_deref_or_ref(then, checked_ty, expected)?;
2728
2729 let els_expr = match els?.kind {
2731 ExprKind::Block(block, _) => block.expr?,
2732 _ => els?,
2733 };
2734 let (else_suggs, ..) =
2735 self.suggest_deref_or_ref(els_expr, checked_ty, expected)?;
2736 suggs.extend(else_suggs);
2737
2738 return Some((suggs, help, app, verbose, mutref));
2739 }
2740
2741 if let Some((sugg, msg)) = self.can_use_as_ref(expr) {
2742 return Some((
2743 sugg,
2744 msg.to_string(),
2745 Applicability::MachineApplicable,
2746 true,
2747 false,
2748 ));
2749 }
2750
2751 let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2752 Some(ident) => format!("{ident}: "),
2753 None => String::new(),
2754 };
2755
2756 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(..), .. }) =
2757 self.tcx.parent_hir_node(expr.hir_id)
2758 {
2759 if mutability.is_mut() {
2760 return None;
2762 }
2763 }
2764
2765 let make_sugg = |expr: &Expr<'_>, span: Span, sugg: &str| {
2766 if expr_needs_parens(expr) {
2767 (
2768 vec![
2769 (span.shrink_to_lo(), format!("{prefix}{sugg}(")),
2770 (span.shrink_to_hi(), ")".to_string()),
2771 ],
2772 false,
2773 )
2774 } else {
2775 (vec![(span.shrink_to_lo(), format!("{prefix}{sugg}"))], true)
2776 }
2777 };
2778
2779 if let hir::Node::Expr(hir::Expr {
2781 kind: hir::ExprKind::Binary(_, lhs, ..),
2782 ..
2783 }) = self.tcx.parent_hir_node(expr.hir_id)
2784 && let &ty::Ref(..) = self.check_expr(lhs).kind()
2785 {
2786 let (sugg, verbose) = make_sugg(lhs, lhs.span, "*");
2787
2788 return Some((
2789 sugg,
2790 "consider dereferencing the borrow".to_string(),
2791 Applicability::MachineApplicable,
2792 verbose,
2793 false,
2794 ));
2795 }
2796
2797 let sugg = mutability.ref_prefix_str();
2798 let (sugg, verbose) = make_sugg(expr, sp, sugg);
2799 return Some((
2800 sugg,
2801 format!("consider {}borrowing here", mutability.mutably_str()),
2802 Applicability::MachineApplicable,
2803 verbose,
2804 false,
2805 ));
2806 }
2807 }
2808 (hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr), _, &ty::Ref(_, checked, _))
2809 if self.can_eq(self.param_env, checked, expected) =>
2810 {
2811 let make_sugg = |start: Span, end: BytePos| {
2812 let sp = sm
2815 .span_extend_while(start.shrink_to_lo(), |c| c == '(' || c.is_whitespace())
2816 .map_or(start, |s| s.shrink_to_hi());
2817 Some((
2818 vec![(sp.with_hi(end), String::new())],
2819 "consider removing the borrow".to_string(),
2820 Applicability::MachineApplicable,
2821 true,
2822 true,
2823 ))
2824 };
2825
2826 if sm.is_imported(expr.span) {
2829 if let Some(call_span) =
2835 iter::successors(Some(expr.span), |s| s.parent_callsite())
2836 .find(|&s| sp.contains(s))
2837 && sm.is_span_accessible(call_span)
2838 {
2839 return make_sugg(sp, call_span.lo());
2840 }
2841 return None;
2842 }
2843 if sp.contains(expr.span) && sm.is_span_accessible(expr.span) {
2844 return make_sugg(sp, expr.span.lo());
2845 }
2846 }
2847 (_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => {
2848 if let Some(steps) = self.deref_steps_for_suggestion(ty_a, ty_b)
2849 && steps > 0
2851 && let Ok(src) = sm.span_to_snippet(sp)
2853 {
2854 let derefs = "*".repeat(steps);
2855 let old_prefix = mutbl_a.ref_prefix_str();
2856 let new_prefix = mutbl_b.ref_prefix_str().to_owned() + &derefs;
2857
2858 let suggestion = replace_prefix(&src, old_prefix, &new_prefix).map(|_| {
2859 let lo = sp.lo()
2861 + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _);
2862 let hi = sp.lo() + BytePos(old_prefix.len() as _);
2864 let sp = sp.with_lo(lo).with_hi(hi);
2865
2866 (
2867 sp,
2868 format!(
2869 "{}{derefs}",
2870 if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }
2871 ),
2872 if mutbl_b <= mutbl_a {
2873 Applicability::MachineApplicable
2874 } else {
2875 Applicability::MaybeIncorrect
2876 },
2877 )
2878 });
2879
2880 if let Some((span, src, applicability)) = suggestion {
2881 return Some((
2882 vec![(span, src)],
2883 "consider dereferencing".to_string(),
2884 applicability,
2885 true,
2886 false,
2887 ));
2888 }
2889 }
2890 }
2891 _ if sp == expr.span => {
2892 if let Some(mut steps) = self.deref_steps_for_suggestion(checked_ty, expected) {
2893 let mut expr = expr.peel_blocks();
2894 let mut prefix_span = expr.span.shrink_to_lo();
2895 let mut remove = String::new();
2896
2897 while steps > 0 {
2899 if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
2900 prefix_span = prefix_span.with_hi(inner.span.lo());
2902 expr = inner;
2903 remove.push_str(mutbl.ref_prefix_str());
2904 steps -= 1;
2905 } else {
2906 break;
2907 }
2908 }
2909 if steps == 0 && !remove.trim().is_empty() {
2911 return Some((
2912 vec![(prefix_span, String::new())],
2913 format!("consider removing the `{}`", remove.trim()),
2914 if remove.trim() == "&&" && expected == self.tcx.types.bool {
2918 Applicability::MaybeIncorrect
2919 } else {
2920 Applicability::MachineApplicable
2921 },
2922 true,
2923 false,
2924 ));
2925 }
2926
2927 if self.type_is_copy_modulo_regions(self.param_env, expected)
2930 || (checked_ty.is_box() && steps == 1)
2933 || matches!(
2935 self.tcx.parent_hir_node(expr.hir_id),
2936 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, ..), .. })
2937 if !op.node.is_by_value()
2938 )
2939 {
2940 let deref_kind = if checked_ty.is_box() {
2941 "unboxing the value"
2942 } else if checked_ty.is_ref() {
2943 "dereferencing the borrow"
2944 } else {
2945 "dereferencing the type"
2946 };
2947
2948 let message = if remove.is_empty() {
2951 format!("consider {deref_kind}")
2952 } else {
2953 format!(
2954 "consider removing the `{}` and {} instead",
2955 remove.trim(),
2956 deref_kind
2957 )
2958 };
2959
2960 let prefix =
2961 match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2962 Some(ident) => format!("{ident}: "),
2963 None => String::new(),
2964 };
2965
2966 let (span, suggestion) = if self.is_else_if_block(expr) {
2967 return None;
2969 } else if let Some(expr) = self.maybe_get_block_expr(expr) {
2970 (expr.span.shrink_to_lo(), "*".to_string())
2972 } else {
2973 (prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
2974 };
2975 if suggestion.trim().is_empty() {
2976 return None;
2977 }
2978
2979 if expr_needs_parens(expr) {
2980 return Some((
2981 vec![
2982 (span, format!("{suggestion}(")),
2983 (expr.span.shrink_to_hi(), ")".to_string()),
2984 ],
2985 message,
2986 Applicability::MachineApplicable,
2987 true,
2988 false,
2989 ));
2990 }
2991
2992 return Some((
2993 vec![(span, suggestion)],
2994 message,
2995 Applicability::MachineApplicable,
2996 true,
2997 false,
2998 ));
2999 }
3000 }
3001 }
3002 _ => {}
3003 }
3004 None
3005 }
3006
3007 fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
3009 if let hir::ExprKind::If(..) = expr.kind {
3010 if let Node::Expr(hir::Expr {
3011 kind: hir::ExprKind::If(_, _, Some(else_expr)), ..
3012 }) = self.tcx.parent_hir_node(expr.hir_id)
3013 {
3014 return else_expr.hir_id == expr.hir_id;
3015 }
3016 }
3017 false
3018 }
3019
3020 pub(crate) fn suggest_cast(
3021 &self,
3022 err: &mut Diag<'_>,
3023 expr: &hir::Expr<'_>,
3024 checked_ty: Ty<'tcx>,
3025 expected_ty: Ty<'tcx>,
3026 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
3027 ) -> bool {
3028 if self.tcx.sess.source_map().is_imported(expr.span) {
3029 return false;
3031 }
3032
3033 let span = if let hir::ExprKind::Lit(lit) = &expr.kind { lit.span } else { expr.span };
3034 let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) else {
3035 return false;
3036 };
3037
3038 let can_cast = false;
3047
3048 let mut sugg = vec![];
3049
3050 if let hir::Node::ExprField(field) = self.tcx.parent_hir_node(expr.hir_id) {
3051 if field.is_shorthand {
3053 sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
3055 } else {
3056 return false;
3058 }
3059 };
3060
3061 if let hir::ExprKind::Call(path, args) = &expr.kind
3062 && let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
3063 (&path.kind, args.len())
3064 && let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
3066 (&base_ty.kind, path_segment.ident.name)
3067 {
3068 if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
3069 match ident.name {
3070 sym::i128
3071 | sym::i64
3072 | sym::i32
3073 | sym::i16
3074 | sym::i8
3075 | sym::u128
3076 | sym::u64
3077 | sym::u32
3078 | sym::u16
3079 | sym::u8
3080 | sym::isize
3081 | sym::usize
3082 if base_ty_path.segments.len() == 1 =>
3083 {
3084 return false;
3085 }
3086 _ => {}
3087 }
3088 }
3089 }
3090
3091 let msg = format!(
3092 "you can convert {} `{}` to {} `{}`",
3093 checked_ty.kind().article(),
3094 checked_ty,
3095 expected_ty.kind().article(),
3096 expected_ty,
3097 );
3098 let cast_msg = format!(
3099 "you can cast {} `{}` to {} `{}`",
3100 checked_ty.kind().article(),
3101 checked_ty,
3102 expected_ty.kind().article(),
3103 expected_ty,
3104 );
3105 let lit_msg = format!(
3106 "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
3107 );
3108
3109 let close_paren = if expr.precedence() < ExprPrecedence::Unambiguous {
3110 sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
3111 ")"
3112 } else {
3113 ""
3114 };
3115
3116 let mut cast_suggestion = sugg.clone();
3117 cast_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren} as {expected_ty}")));
3118 let mut into_suggestion = sugg.clone();
3119 into_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren}.into()")));
3120 let mut suffix_suggestion = sugg.clone();
3121 suffix_suggestion.push((
3122 if matches!(
3123 (expected_ty.kind(), checked_ty.kind()),
3124 (ty::Int(_) | ty::Uint(_), ty::Float(_))
3125 ) {
3126 let src = src.trim_end_matches(&checked_ty.to_string());
3128 let len = src.split('.').next().unwrap().len();
3129 span.with_lo(span.lo() + BytePos(len as u32))
3130 } else {
3131 let len = src.trim_end_matches(&checked_ty.to_string()).len();
3132 span.with_lo(span.lo() + BytePos(len as u32))
3133 },
3134 if expr.precedence() < ExprPrecedence::Unambiguous {
3135 format!("{expected_ty})")
3137 } else {
3138 expected_ty.to_string()
3139 },
3140 ));
3141 let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
3142 if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
3143 };
3144 let is_negative_int =
3145 |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..));
3146 let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
3147
3148 let in_const_context = self.tcx.hir_is_inside_const_context(expr.hir_id);
3149
3150 let suggest_fallible_into_or_lhs_from =
3151 |err: &mut Diag<'_>, exp_to_found_is_fallible: bool| {
3152 let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
3159 self.tcx
3160 .sess
3161 .source_map()
3162 .span_to_snippet(expr.span)
3163 .ok()
3164 .map(|src| (expr, src))
3165 });
3166 let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
3167 (lhs_expr_and_src, exp_to_found_is_fallible)
3168 {
3169 let msg = format!(
3170 "you can convert `{lhs_src}` from `{expected_ty}` to `{checked_ty}`, matching the type of `{src}`",
3171 );
3172 let suggestion = vec![
3173 (lhs_expr.span.shrink_to_lo(), format!("{checked_ty}::from(")),
3174 (lhs_expr.span.shrink_to_hi(), ")".to_string()),
3175 ];
3176 (msg, suggestion)
3177 } else {
3178 let msg =
3179 format!("{} and panic if the converted value doesn't fit", msg.clone());
3180 let mut suggestion = sugg.clone();
3181 suggestion.push((
3182 expr.span.shrink_to_hi(),
3183 format!("{close_paren}.try_into().unwrap()"),
3184 ));
3185 (msg, suggestion)
3186 };
3187 err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
3188 };
3189
3190 let suggest_to_change_suffix_or_into =
3191 |err: &mut Diag<'_>, found_to_exp_is_fallible: bool, exp_to_found_is_fallible: bool| {
3192 let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir_is_lhs(e.hir_id));
3193
3194 if exp_is_lhs {
3195 return;
3196 }
3197
3198 let always_fallible = found_to_exp_is_fallible
3199 && (exp_to_found_is_fallible || expected_ty_expr.is_none());
3200 let msg = if literal_is_ty_suffixed(expr) {
3201 lit_msg.clone()
3202 } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
3203 let msg = format!("`{src}` cannot fit into type `{expected_ty}`");
3207 err.note(msg);
3208 return;
3209 } else if in_const_context {
3210 return;
3212 } else if found_to_exp_is_fallible {
3213 return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
3214 } else {
3215 msg.clone()
3216 };
3217 let suggestion = if literal_is_ty_suffixed(expr) {
3218 suffix_suggestion.clone()
3219 } else {
3220 into_suggestion.clone()
3221 };
3222 err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
3223 };
3224
3225 match (expected_ty.kind(), checked_ty.kind()) {
3226 (ty::Int(exp), ty::Int(found)) => {
3227 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3228 {
3229 (Some(exp), Some(found)) if exp < found => (true, false),
3230 (Some(exp), Some(found)) if exp > found => (false, true),
3231 (None, Some(8 | 16)) => (false, true),
3232 (Some(8 | 16), None) => (true, false),
3233 (None, _) | (_, None) => (true, true),
3234 _ => (false, false),
3235 };
3236 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3237 true
3238 }
3239 (ty::Uint(exp), ty::Uint(found)) => {
3240 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3241 {
3242 (Some(exp), Some(found)) if exp < found => (true, false),
3243 (Some(exp), Some(found)) if exp > found => (false, true),
3244 (None, Some(8 | 16)) => (false, true),
3245 (Some(8 | 16), None) => (true, false),
3246 (None, _) | (_, None) => (true, true),
3247 _ => (false, false),
3248 };
3249 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3250 true
3251 }
3252 (&ty::Int(exp), &ty::Uint(found)) => {
3253 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3254 {
3255 (Some(exp), Some(found)) if found < exp => (false, true),
3256 (None, Some(8)) => (false, true),
3257 _ => (true, true),
3258 };
3259 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3260 true
3261 }
3262 (&ty::Uint(exp), &ty::Int(found)) => {
3263 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3264 {
3265 (Some(exp), Some(found)) if found > exp => (true, false),
3266 (Some(8), None) => (true, false),
3267 _ => (true, true),
3268 };
3269 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3270 true
3271 }
3272 (ty::Float(exp), ty::Float(found)) => {
3273 if found.bit_width() < exp.bit_width() {
3274 suggest_to_change_suffix_or_into(err, false, true);
3275 } else if literal_is_ty_suffixed(expr) {
3276 err.multipart_suggestion_verbose(
3277 lit_msg,
3278 suffix_suggestion,
3279 Applicability::MachineApplicable,
3280 );
3281 } else if can_cast {
3282 err.multipart_suggestion_verbose(
3284 format!("{cast_msg}, producing the closest possible value"),
3285 cast_suggestion,
3286 Applicability::MaybeIncorrect, );
3288 }
3289 true
3290 }
3291 (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
3292 if literal_is_ty_suffixed(expr) {
3293 err.multipart_suggestion_verbose(
3294 lit_msg,
3295 suffix_suggestion,
3296 Applicability::MachineApplicable,
3297 );
3298 } else if can_cast {
3299 err.multipart_suggestion_verbose(
3301 format!("{msg}, rounding the float towards zero"),
3302 cast_suggestion,
3303 Applicability::MaybeIncorrect, );
3305 }
3306 true
3307 }
3308 (ty::Float(exp), ty::Uint(found)) => {
3309 if exp.bit_width() > found.bit_width().unwrap_or(256) {
3311 err.multipart_suggestion_verbose(
3312 format!(
3313 "{msg}, producing the floating point representation of the integer",
3314 ),
3315 into_suggestion,
3316 Applicability::MachineApplicable,
3317 );
3318 } else if literal_is_ty_suffixed(expr) {
3319 err.multipart_suggestion_verbose(
3320 lit_msg,
3321 suffix_suggestion,
3322 Applicability::MachineApplicable,
3323 );
3324 } else {
3325 err.multipart_suggestion_verbose(
3327 format!(
3328 "{cast_msg}, producing the floating point representation of the integer, \
3329 rounded if necessary",
3330 ),
3331 cast_suggestion,
3332 Applicability::MaybeIncorrect, );
3334 }
3335 true
3336 }
3337 (ty::Float(exp), ty::Int(found)) => {
3338 if exp.bit_width() > found.bit_width().unwrap_or(256) {
3340 err.multipart_suggestion_verbose(
3341 format!(
3342 "{}, producing the floating point representation of the integer",
3343 msg.clone(),
3344 ),
3345 into_suggestion,
3346 Applicability::MachineApplicable,
3347 );
3348 } else if literal_is_ty_suffixed(expr) {
3349 err.multipart_suggestion_verbose(
3350 lit_msg,
3351 suffix_suggestion,
3352 Applicability::MachineApplicable,
3353 );
3354 } else {
3355 err.multipart_suggestion_verbose(
3357 format!(
3358 "{}, producing the floating point representation of the integer, \
3359 rounded if necessary",
3360 &msg,
3361 ),
3362 cast_suggestion,
3363 Applicability::MaybeIncorrect, );
3365 }
3366 true
3367 }
3368 (
3369 &ty::Uint(ty::UintTy::U32 | ty::UintTy::U64 | ty::UintTy::U128)
3370 | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128),
3371 &ty::Char,
3372 ) => {
3373 err.multipart_suggestion_verbose(
3374 format!("{cast_msg}, since a `char` always occupies 4 bytes"),
3375 cast_suggestion,
3376 Applicability::MachineApplicable,
3377 );
3378 true
3379 }
3380 _ => false,
3381 }
3382 }
3383
3384 pub(crate) fn suggest_method_call_on_range_literal(
3386 &self,
3387 err: &mut Diag<'_>,
3388 expr: &hir::Expr<'tcx>,
3389 checked_ty: Ty<'tcx>,
3390 expected_ty: Ty<'tcx>,
3391 ) {
3392 if !hir::is_range_literal(expr) {
3393 return;
3394 }
3395 let hir::ExprKind::Struct(hir::QPath::LangItem(LangItem::Range, ..), [start, end], _) =
3396 expr.kind
3397 else {
3398 return;
3399 };
3400 if let hir::Node::ExprField(_) = self.tcx.parent_hir_node(expr.hir_id) {
3401 return;
3403 }
3404 let mut expr = end.expr;
3405 let mut expectation = Some(expected_ty);
3406 while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
3407 expr = rcvr;
3410 expectation = None;
3413 }
3414 let hir::ExprKind::Call(method_name, _) = expr.kind else {
3415 return;
3416 };
3417 let ty::Adt(adt, _) = checked_ty.kind() else {
3418 return;
3419 };
3420 if self.tcx.lang_items().range_struct() != Some(adt.did()) {
3421 return;
3422 }
3423 if let ty::Adt(adt, _) = expected_ty.kind()
3424 && self.tcx.is_lang_item(adt.did(), LangItem::Range)
3425 {
3426 return;
3427 }
3428 let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else {
3430 return;
3431 };
3432 let [hir::PathSegment { ident, .. }] = p.segments else {
3433 return;
3434 };
3435 let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
3436 let Ok(_pick) = self.lookup_probe_for_diagnostic(
3437 *ident,
3438 self_ty,
3439 expr,
3440 probe::ProbeScope::AllTraits,
3441 expectation,
3442 ) else {
3443 return;
3444 };
3445 let mut sugg = ".";
3446 let mut span = start.expr.span.between(end.expr.span);
3447 if span.lo() + BytePos(2) == span.hi() {
3448 span = span.with_lo(span.lo() + BytePos(1));
3451 sugg = "";
3452 }
3453 err.span_suggestion_verbose(
3454 span,
3455 "you likely meant to write a method call instead of a range",
3456 sugg,
3457 Applicability::MachineApplicable,
3458 );
3459 }
3460
3461 pub(crate) fn suggest_return_binding_for_missing_tail_expr(
3464 &self,
3465 err: &mut Diag<'_>,
3466 expr: &hir::Expr<'_>,
3467 checked_ty: Ty<'tcx>,
3468 expected_ty: Ty<'tcx>,
3469 ) {
3470 if !checked_ty.is_unit() {
3471 return;
3472 }
3473 let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {
3474 return;
3475 };
3476 let hir::def::Res::Local(hir_id) = path.res else {
3477 return;
3478 };
3479 let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else {
3480 return;
3481 };
3482 let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) =
3483 self.tcx.parent_hir_node(pat.hir_id)
3484 else {
3485 return;
3486 };
3487 let hir::ExprKind::Block(block, None) = init.kind else {
3488 return;
3489 };
3490 if block.expr.is_some() {
3491 return;
3492 }
3493 let [.., stmt] = block.stmts else {
3494 err.span_label(block.span, "this empty block is missing a tail expression");
3495 return;
3496 };
3497 let hir::StmtKind::Semi(tail_expr) = stmt.kind else {
3498 return;
3499 };
3500 let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else {
3501 return;
3502 };
3503 if self.can_eq(self.param_env, expected_ty, ty)
3504 && stmt.span.hi() != tail_expr.span.hi()
3509 {
3510 err.span_suggestion_short(
3511 stmt.span.with_lo(tail_expr.span.hi()),
3512 "remove this semicolon",
3513 "",
3514 Applicability::MachineApplicable,
3515 );
3516 } else {
3517 err.span_label(block.span, "this block is missing a tail expression");
3518 }
3519 }
3520
3521 pub(crate) fn suggest_swapping_lhs_and_rhs(
3522 &self,
3523 err: &mut Diag<'_>,
3524 rhs_ty: Ty<'tcx>,
3525 lhs_ty: Ty<'tcx>,
3526 rhs_expr: &'tcx hir::Expr<'tcx>,
3527 lhs_expr: &'tcx hir::Expr<'tcx>,
3528 ) {
3529 if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
3530 && self
3531 .infcx
3532 .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
3533 .must_apply_modulo_regions()
3534 {
3535 let sm = self.tcx.sess.source_map();
3536 if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
3537 && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
3538 {
3539 err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
3540 err.multipart_suggestion(
3541 "consider swapping the equality",
3542 vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
3543 Applicability::MaybeIncorrect,
3544 );
3545 }
3546 }
3547 }
3548}