]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Rollup merge of #105064 - notriddle:notriddle/main-min-width, r=GuillaumeGomez
[rust.git] / compiler / rustc_hir_typeck / src / fn_ctxt / suggestions.rs
1 use super::FnCtxt;
2
3 use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
4 use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
5 use rustc_errors::{Applicability, Diagnostic, MultiSpan};
6 use rustc_hir as hir;
7 use rustc_hir::def::{CtorOf, DefKind};
8 use rustc_hir::lang_items::LangItem;
9 use rustc_hir::{
10     Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
11 };
12 use rustc_hir_analysis::astconv::AstConv;
13 use rustc_infer::infer::{self, TyCtxtInferExt};
14 use rustc_infer::traits::{self, StatementAsExpression};
15 use rustc_middle::lint::in_external_macro;
16 use rustc_middle::ty::{self, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty};
17 use rustc_session::errors::ExprParenthesesNeeded;
18 use rustc_span::symbol::sym;
19 use rustc_span::Span;
20 use rustc_trait_selection::infer::InferCtxtExt;
21 use rustc_trait_selection::traits::error_reporting::DefIdOrName;
22 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
23 use rustc_trait_selection::traits::NormalizeExt;
24
25 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26     pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
27         self.typeck_results
28             .borrow()
29             .liberated_fn_sigs()
30             .get(self.tcx.hir().get_parent_node(self.body_id))
31             .copied()
32     }
33
34     pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) {
35         err.span_suggestion_short(
36             span.shrink_to_hi(),
37             "consider using a semicolon here",
38             ";",
39             Applicability::MachineApplicable,
40         );
41     }
42
43     /// On implicit return expressions with mismatched types, provides the following suggestions:
44     ///
45     /// - Points out the method's return type as the reason for the expected type.
46     /// - Possible missing semicolon.
47     /// - Possible missing return type if the return type is the default, and not `fn main()`.
48     pub fn suggest_mismatched_types_on_tail(
49         &self,
50         err: &mut Diagnostic,
51         expr: &'tcx hir::Expr<'tcx>,
52         expected: Ty<'tcx>,
53         found: Ty<'tcx>,
54         blk_id: hir::HirId,
55     ) -> bool {
56         let expr = expr.peel_drop_temps();
57         self.suggest_missing_semicolon(err, expr, expected, false);
58         let mut pointing_at_return_type = false;
59         if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
60             let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
61             pointing_at_return_type = self.suggest_missing_return_type(
62                 err,
63                 &fn_decl,
64                 expected,
65                 found,
66                 can_suggest,
67                 fn_id,
68             );
69             self.suggest_missing_break_or_return_expr(
70                 err, expr, &fn_decl, expected, found, blk_id, fn_id,
71             );
72         }
73         pointing_at_return_type
74     }
75
76     /// When encountering an fn-like type, try accessing the output of the type
77     /// and suggesting calling it if it satisfies a predicate (i.e. if the
78     /// output has a method or a field):
79     /// ```compile_fail,E0308
80     /// fn foo(x: usize) -> usize { x }
81     /// let x: usize = foo;  // suggest calling the `foo` function: `foo(42)`
82     /// ```
83     pub(crate) fn suggest_fn_call(
84         &self,
85         err: &mut Diagnostic,
86         expr: &hir::Expr<'_>,
87         found: Ty<'tcx>,
88         can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
89     ) -> bool {
90         let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found)
91             else { return false; };
92         if can_satisfy(output) {
93             let (sugg_call, mut applicability) = match inputs.len() {
94                 0 => ("".to_string(), Applicability::MachineApplicable),
95                 1..=4 => (
96                     inputs
97                         .iter()
98                         .map(|ty| {
99                             if ty.is_suggestable(self.tcx, false) {
100                                 format!("/* {ty} */")
101                             } else {
102                                 "/* value */".to_string()
103                             }
104                         })
105                         .collect::<Vec<_>>()
106                         .join(", "),
107                     Applicability::HasPlaceholders,
108                 ),
109                 _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
110             };
111
112             let msg = match def_id_or_name {
113                 DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
114                     DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(),
115                     DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(),
116                     kind => format!("call this {}", kind.descr(def_id)),
117                 },
118                 DefIdOrName::Name(name) => format!("call this {name}"),
119             };
120
121             let sugg = match expr.kind {
122                 hir::ExprKind::Call(..)
123                 | hir::ExprKind::Path(..)
124                 | hir::ExprKind::Index(..)
125                 | hir::ExprKind::Lit(..) => {
126                     vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
127                 }
128                 hir::ExprKind::Closure { .. } => {
129                     // Might be `{ expr } || { bool }`
130                     applicability = Applicability::MaybeIncorrect;
131                     vec![
132                         (expr.span.shrink_to_lo(), "(".to_string()),
133                         (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
134                     ]
135                 }
136                 _ => {
137                     vec![
138                         (expr.span.shrink_to_lo(), "(".to_string()),
139                         (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
140                     ]
141                 }
142             };
143
144             err.multipart_suggestion_verbose(
145                 format!("use parentheses to {msg}"),
146                 sugg,
147                 applicability,
148             );
149             return true;
150         }
151         false
152     }
153
154     /// Extracts information about a callable type for diagnostics. This is a
155     /// heuristic -- it doesn't necessarily mean that a type is always callable,
156     /// because the callable type must also be well-formed to be called.
157     pub(in super::super) fn extract_callable_info(
158         &self,
159         expr: &Expr<'_>,
160         found: Ty<'tcx>,
161     ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
162         // Autoderef is useful here because sometimes we box callables, etc.
163         let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
164             match *found.kind() {
165                 ty::FnPtr(fn_sig) =>
166                     Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
167                 ty::FnDef(def_id, _) => {
168                     let fn_sig = found.fn_sig(self.tcx);
169                     Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
170                 }
171                 ty::Closure(def_id, substs) => {
172                     let fn_sig = substs.as_closure().sig();
173                     Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
174                 }
175                 ty::Opaque(def_id, substs) => {
176                     self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
177                         if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
178                         && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
179                         // args tuple will always be substs[1]
180                         && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
181                         {
182                             Some((
183                                 DefIdOrName::DefId(def_id),
184                                 pred.kind().rebind(proj.term.ty().unwrap()),
185                                 pred.kind().rebind(args.as_slice()),
186                             ))
187                         } else {
188                             None
189                         }
190                     })
191                 }
192                 ty::Dynamic(data, _, ty::Dyn) => {
193                     data.iter().find_map(|pred| {
194                         if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
195                         && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
196                         // for existential projection, substs are shifted over by 1
197                         && let ty::Tuple(args) = proj.substs.type_at(0).kind()
198                         {
199                             Some((
200                                 DefIdOrName::Name("trait object"),
201                                 pred.rebind(proj.term.ty().unwrap()),
202                                 pred.rebind(args.as_slice()),
203                             ))
204                         } else {
205                             None
206                         }
207                     })
208                 }
209                 ty::Param(param) => {
210                     let def_id = self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx).def_id;
211                     self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
212                         if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
213                         && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
214                         && proj.projection_ty.self_ty() == found
215                         // args tuple will always be substs[1]
216                         && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
217                         {
218                             Some((
219                                 DefIdOrName::DefId(def_id),
220                                 pred.kind().rebind(proj.term.ty().unwrap()),
221                                 pred.kind().rebind(args.as_slice()),
222                             ))
223                         } else {
224                             None
225                         }
226                     })
227                 }
228                 _ => None,
229             }
230         }) else { return None; };
231
232         let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
233         let inputs = inputs
234             .skip_binder()
235             .iter()
236             .map(|ty| {
237                 self.replace_bound_vars_with_fresh_vars(
238                     expr.span,
239                     infer::FnCall,
240                     inputs.rebind(*ty),
241                 )
242             })
243             .collect();
244
245         // We don't want to register any extra obligations, which should be
246         // implied by wf, but also because that would possibly result in
247         // erroneous errors later on.
248         let infer::InferOk { value: output, obligations: _ } =
249             self.at(&self.misc(expr.span), self.param_env).normalize(output);
250
251         if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
252     }
253
254     pub fn suggest_two_fn_call(
255         &self,
256         err: &mut Diagnostic,
257         lhs_expr: &'tcx hir::Expr<'tcx>,
258         lhs_ty: Ty<'tcx>,
259         rhs_expr: &'tcx hir::Expr<'tcx>,
260         rhs_ty: Ty<'tcx>,
261         can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
262     ) -> bool {
263         let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
264             else { return false; };
265         let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
266             else { return false; };
267
268         if can_satisfy(lhs_output_ty, rhs_output_ty) {
269             let mut sugg = vec![];
270             let mut applicability = Applicability::MachineApplicable;
271
272             for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
273                 let (sugg_call, this_applicability) = match inputs.len() {
274                     0 => ("".to_string(), Applicability::MachineApplicable),
275                     1..=4 => (
276                         inputs
277                             .iter()
278                             .map(|ty| {
279                                 if ty.is_suggestable(self.tcx, false) {
280                                     format!("/* {ty} */")
281                                 } else {
282                                     "/* value */".to_string()
283                                 }
284                             })
285                             .collect::<Vec<_>>()
286                             .join(", "),
287                         Applicability::HasPlaceholders,
288                     ),
289                     _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
290                 };
291
292                 applicability = applicability.max(this_applicability);
293
294                 match expr.kind {
295                     hir::ExprKind::Call(..)
296                     | hir::ExprKind::Path(..)
297                     | hir::ExprKind::Index(..)
298                     | hir::ExprKind::Lit(..) => {
299                         sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
300                     }
301                     hir::ExprKind::Closure { .. } => {
302                         // Might be `{ expr } || { bool }`
303                         applicability = Applicability::MaybeIncorrect;
304                         sugg.extend([
305                             (expr.span.shrink_to_lo(), "(".to_string()),
306                             (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
307                         ]);
308                     }
309                     _ => {
310                         sugg.extend([
311                             (expr.span.shrink_to_lo(), "(".to_string()),
312                             (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
313                         ]);
314                     }
315                 }
316             }
317
318             err.multipart_suggestion_verbose(
319                 format!("use parentheses to call these"),
320                 sugg,
321                 applicability,
322             );
323
324             true
325         } else {
326             false
327         }
328     }
329
330     pub fn suggest_deref_ref_or_into(
331         &self,
332         err: &mut Diagnostic,
333         expr: &hir::Expr<'tcx>,
334         expected: Ty<'tcx>,
335         found: Ty<'tcx>,
336         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
337     ) -> bool {
338         let expr = expr.peel_blocks();
339         if let Some((sp, msg, suggestion, applicability, verbose, annotation)) =
340             self.check_ref(expr, found, expected)
341         {
342             if verbose {
343                 err.span_suggestion_verbose(sp, &msg, suggestion, applicability);
344             } else {
345                 err.span_suggestion(sp, &msg, suggestion, applicability);
346             }
347             if annotation {
348                 let suggest_annotation = match expr.peel_drop_temps().kind {
349                     hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(),
350                     _ => return true,
351                 };
352                 let mut tuple_indexes = Vec::new();
353                 let mut expr_id = expr.hir_id;
354                 for (parent_id, node) in self.tcx.hir().parent_iter(expr.hir_id) {
355                     match node {
356                         Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => {
357                             tuple_indexes.push(
358                                 subs.iter()
359                                     .enumerate()
360                                     .find(|(_, sub_expr)| sub_expr.hir_id == expr_id)
361                                     .unwrap()
362                                     .0,
363                             );
364                             expr_id = parent_id;
365                         }
366                         Node::Local(local) => {
367                             if let Some(mut ty) = local.ty {
368                                 while let Some(index) = tuple_indexes.pop() {
369                                     match ty.kind {
370                                         TyKind::Tup(tys) => ty = &tys[index],
371                                         _ => return true,
372                                     }
373                                 }
374                                 let annotation_span = ty.span;
375                                 err.span_suggestion(
376                                     annotation_span.with_hi(annotation_span.lo()),
377                                     "alternatively, consider changing the type annotation",
378                                     suggest_annotation,
379                                     Applicability::MaybeIncorrect,
380                                 );
381                             }
382                             break;
383                         }
384                         _ => break,
385                     }
386                 }
387             }
388             return true;
389         } else if self.suggest_else_fn_with_closure(err, expr, found, expected) {
390             return true;
391         } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
392             && let ty::FnDef(def_id, ..) = &found.kind()
393             && let Some(sp) = self.tcx.hir().span_if_local(*def_id)
394         {
395             err.span_label(sp, format!("{found} defined here"));
396             return true;
397         } else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
398             return true;
399         } else {
400             let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
401             if !methods.is_empty() {
402                 let mut suggestions = methods.iter()
403                     .filter_map(|conversion_method| {
404                         let receiver_method_ident = expr.method_ident();
405                         if let Some(method_ident) = receiver_method_ident
406                             && method_ident.name == conversion_method.name
407                         {
408                             return None // do not suggest code that is already there (#53348)
409                         }
410
411                         let method_call_list = [sym::to_vec, sym::to_string];
412                         let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
413                             && receiver_method.ident.name == sym::clone
414                             && method_call_list.contains(&conversion_method.name)
415                             // If receiver is `.clone()` and found type has one of those methods,
416                             // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
417                             // to an owned type (`Vec` or `String`).  These conversions clone internally,
418                             // so we remove the user's `clone` call.
419                         {
420                             vec![(
421                                 receiver_method.ident.span,
422                                 conversion_method.name.to_string()
423                             )]
424                         } else if expr.precedence().order()
425                             < ExprPrecedence::MethodCall.order()
426                         {
427                             vec![
428                                 (expr.span.shrink_to_lo(), "(".to_string()),
429                                 (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
430                             ]
431                         } else {
432                             vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
433                         };
434                         let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr);
435                         if let Some(name) = struct_pat_shorthand_field {
436                             sugg.insert(
437                                 0,
438                                 (expr.span.shrink_to_lo(), format!("{}: ", name)),
439                             );
440                         }
441                         Some(sugg)
442                     })
443                     .peekable();
444                 if suggestions.peek().is_some() {
445                     err.multipart_suggestions(
446                         "try using a conversion method",
447                         suggestions,
448                         Applicability::MaybeIncorrect,
449                     );
450                     return true;
451                 }
452             } else if let ty::Adt(found_adt, found_substs) = found.kind()
453                 && self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
454                 && let ty::Adt(expected_adt, expected_substs) = expected.kind()
455                 && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
456                 && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind()
457                 && inner_ty.is_str()
458             {
459                 let ty = found_substs.type_at(0);
460                 let mut peeled = ty;
461                 let mut ref_cnt = 0;
462                 while let ty::Ref(_, inner, _) = peeled.kind() {
463                     peeled = *inner;
464                     ref_cnt += 1;
465                 }
466                 if let ty::Adt(adt, _) = peeled.kind()
467                     && Some(adt.did()) == self.tcx.lang_items().string()
468                 {
469                     err.span_suggestion_verbose(
470                         expr.span.shrink_to_hi(),
471                         "try converting the passed type into a `&str`",
472                         format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)),
473                         Applicability::MaybeIncorrect,
474                     );
475                     return true;
476                 }
477             }
478         }
479
480         false
481     }
482
483     /// When encountering the expected boxed value allocated in the stack, suggest allocating it
484     /// in the heap by calling `Box::new()`.
485     pub(in super::super) fn suggest_boxing_when_appropriate(
486         &self,
487         err: &mut Diagnostic,
488         expr: &hir::Expr<'_>,
489         expected: Ty<'tcx>,
490         found: Ty<'tcx>,
491     ) -> bool {
492         if self.tcx.hir().is_inside_const_context(expr.hir_id) {
493             // Do not suggest `Box::new` in const context.
494             return false;
495         }
496         if !expected.is_box() || found.is_box() {
497             return false;
498         }
499         let boxed_found = self.tcx.mk_box(found);
500         if self.can_coerce(boxed_found, expected) {
501             err.multipart_suggestion(
502                 "store this in the heap by calling `Box::new`",
503                 vec![
504                     (expr.span.shrink_to_lo(), "Box::new(".to_string()),
505                     (expr.span.shrink_to_hi(), ")".to_string()),
506                 ],
507                 Applicability::MachineApplicable,
508             );
509             err.note(
510                 "for more on the distinction between the stack and the heap, read \
511                  https://doc.rust-lang.org/book/ch15-01-box.html, \
512                  https://doc.rust-lang.org/rust-by-example/std/box.html, and \
513                  https://doc.rust-lang.org/std/boxed/index.html",
514             );
515             true
516         } else {
517             false
518         }
519     }
520
521     /// When encountering a closure that captures variables, where a FnPtr is expected,
522     /// suggest a non-capturing closure
523     pub(in super::super) fn suggest_no_capture_closure(
524         &self,
525         err: &mut Diagnostic,
526         expected: Ty<'tcx>,
527         found: Ty<'tcx>,
528     ) -> bool {
529         if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
530             if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
531                 // Report upto four upvars being captured to reduce the amount error messages
532                 // reported back to the user.
533                 let spans_and_labels = upvars
534                     .iter()
535                     .take(4)
536                     .map(|(var_hir_id, upvar)| {
537                         let var_name = self.tcx.hir().name(*var_hir_id).to_string();
538                         let msg = format!("`{}` captured here", var_name);
539                         (upvar.span, msg)
540                     })
541                     .collect::<Vec<_>>();
542
543                 let mut multi_span: MultiSpan =
544                     spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
545                 for (sp, label) in spans_and_labels {
546                     multi_span.push_span_label(sp, label);
547                 }
548                 err.span_note(
549                     multi_span,
550                     "closures can only be coerced to `fn` types if they do not capture any variables"
551                 );
552                 return true;
553             }
554         }
555         false
556     }
557
558     /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
559     #[instrument(skip(self, err))]
560     pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
561         &self,
562         err: &mut Diagnostic,
563         expr: &hir::Expr<'_>,
564         expected: Ty<'tcx>,
565         found: Ty<'tcx>,
566     ) -> bool {
567         // Handle #68197.
568
569         if self.tcx.hir().is_inside_const_context(expr.hir_id) {
570             // Do not suggest `Box::new` in const context.
571             return false;
572         }
573         let pin_did = self.tcx.lang_items().pin_type();
574         // This guards the `unwrap` and `mk_box` below.
575         if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
576             return false;
577         }
578         let box_found = self.tcx.mk_box(found);
579         let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
580         let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap();
581         match expected.kind() {
582             ty::Adt(def, _) if Some(def.did()) == pin_did => {
583                 if self.can_coerce(pin_box_found, expected) {
584                     debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
585                     match found.kind() {
586                         ty::Adt(def, _) if def.is_box() => {
587                             err.help("use `Box::pin`");
588                         }
589                         _ => {
590                             err.multipart_suggestion(
591                                 "you need to pin and box this expression",
592                                 vec![
593                                     (expr.span.shrink_to_lo(), "Box::pin(".to_string()),
594                                     (expr.span.shrink_to_hi(), ")".to_string()),
595                                 ],
596                                 Applicability::MaybeIncorrect,
597                             );
598                         }
599                     }
600                     true
601                 } else if self.can_coerce(pin_found, expected) {
602                     match found.kind() {
603                         ty::Adt(def, _) if def.is_box() => {
604                             err.help("use `Box::pin`");
605                             true
606                         }
607                         _ => false,
608                     }
609                 } else {
610                     false
611                 }
612             }
613             ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => {
614                 // Check if the parent expression is a call to Pin::new.  If it
615                 // is and we were expecting a Box, ergo Pin<Box<expected>>, we
616                 // can suggest Box::pin.
617                 let parent = self.tcx.hir().get_parent_node(expr.hir_id);
618                 let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else {
619                     return false;
620                 };
621                 match fn_name.kind {
622                     ExprKind::Path(QPath::TypeRelative(
623                         hir::Ty {
624                             kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })),
625                             ..
626                         },
627                         method,
628                     )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => {
629                         err.span_suggestion(
630                             fn_name.span,
631                             "use `Box::pin` to pin and box this expression",
632                             "Box::pin",
633                             Applicability::MachineApplicable,
634                         );
635                         true
636                     }
637                     _ => false,
638                 }
639             }
640             _ => false,
641         }
642     }
643
644     /// A common error is to forget to add a semicolon at the end of a block, e.g.,
645     ///
646     /// ```compile_fail,E0308
647     /// # fn bar_that_returns_u32() -> u32 { 4 }
648     /// fn foo() {
649     ///     bar_that_returns_u32()
650     /// }
651     /// ```
652     ///
653     /// This routine checks if the return expression in a block would make sense on its own as a
654     /// statement and the return type has been left as default or has been specified as `()`. If so,
655     /// it suggests adding a semicolon.
656     ///
657     /// If the expression is the expression of a closure without block (`|| expr`), a
658     /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`.
659     pub fn suggest_missing_semicolon(
660         &self,
661         err: &mut Diagnostic,
662         expression: &'tcx hir::Expr<'tcx>,
663         expected: Ty<'tcx>,
664         needs_block: bool,
665     ) {
666         if expected.is_unit() {
667             // `BlockTailExpression` only relevant if the tail expr would be
668             // useful on its own.
669             match expression.kind {
670                 ExprKind::Call(..)
671                 | ExprKind::MethodCall(..)
672                 | ExprKind::Loop(..)
673                 | ExprKind::If(..)
674                 | ExprKind::Match(..)
675                 | ExprKind::Block(..)
676                     if expression.can_have_side_effects()
677                         // If the expression is from an external macro, then do not suggest
678                         // adding a semicolon, because there's nowhere to put it.
679                         // See issue #81943.
680                         && !in_external_macro(self.tcx.sess, expression.span) =>
681                 {
682                     if needs_block {
683                         err.multipart_suggestion(
684                             "consider using a semicolon here",
685                             vec![
686                                 (expression.span.shrink_to_lo(), "{ ".to_owned()),
687                                 (expression.span.shrink_to_hi(), "; }".to_owned()),
688                             ],
689                             Applicability::MachineApplicable,
690                         );
691                     } else {
692                         err.span_suggestion(
693                             expression.span.shrink_to_hi(),
694                             "consider using a semicolon here",
695                             ";",
696                             Applicability::MachineApplicable,
697                         );
698                     }
699                 }
700                 _ => (),
701             }
702         }
703     }
704
705     /// A possible error is to forget to add a return type that is needed:
706     ///
707     /// ```compile_fail,E0308
708     /// # fn bar_that_returns_u32() -> u32 { 4 }
709     /// fn foo() {
710     ///     bar_that_returns_u32()
711     /// }
712     /// ```
713     ///
714     /// This routine checks if the return type is left as default, the method is not part of an
715     /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
716     /// type.
717     pub(in super::super) fn suggest_missing_return_type(
718         &self,
719         err: &mut Diagnostic,
720         fn_decl: &hir::FnDecl<'_>,
721         expected: Ty<'tcx>,
722         found: Ty<'tcx>,
723         can_suggest: bool,
724         fn_id: hir::HirId,
725     ) -> bool {
726         let found =
727             self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
728         // Only suggest changing the return type for methods that
729         // haven't set a return type at all (and aren't `fn main()` or an impl).
730         match &fn_decl.output {
731             &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => {
732                 // `fn main()` must return `()`, do not suggest changing return type
733                 err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
734                 return true;
735             }
736             &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
737                 if found.is_suggestable(self.tcx, false) {
738                     err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
739                     return true;
740                 } else if let ty::Closure(_, substs) = found.kind()
741                     // FIXME(compiler-errors): Get better at printing binders...
742                     && let closure = substs.as_closure()
743                     && closure.sig().is_suggestable(self.tcx, false)
744                 {
745                     err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() });
746                     return true;
747                 } else {
748                     // FIXME: if `found` could be `impl Iterator` we should suggest that.
749                     err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
750                     return true
751                 }
752             }
753             &hir::FnRetTy::Return(ref ty) => {
754                 // Only point to return type if the expected type is the return type, as if they
755                 // are not, the expectation must have been caused by something else.
756                 debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
757                 let span = ty.span;
758                 let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
759                 debug!("suggest_missing_return_type: return type {:?}", ty);
760                 debug!("suggest_missing_return_type: expected type {:?}", ty);
761                 let bound_vars = self.tcx.late_bound_vars(fn_id);
762                 let ty = Binder::bind_with_vars(ty, bound_vars);
763                 let ty = self.normalize(span, ty);
764                 let ty = self.tcx.erase_late_bound_regions(ty);
765                 if self.can_coerce(expected, ty) {
766                     err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
767                     self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
768                     return true;
769                 }
770             }
771             _ => {}
772         }
773         false
774     }
775
776     /// check whether the return type is a generic type with a trait bound
777     /// only suggest this if the generic param is not present in the arguments
778     /// if this is true, hint them towards changing the return type to `impl Trait`
779     /// ```compile_fail,E0308
780     /// fn cant_name_it<T: Fn() -> u32>() -> T {
781     ///     || 3
782     /// }
783     /// ```
784     fn try_suggest_return_impl_trait(
785         &self,
786         err: &mut Diagnostic,
787         expected: Ty<'tcx>,
788         found: Ty<'tcx>,
789         fn_id: hir::HirId,
790     ) {
791         // Only apply the suggestion if:
792         //  - the return type is a generic parameter
793         //  - the generic param is not used as a fn param
794         //  - the generic param has at least one bound
795         //  - the generic param doesn't appear in any other bounds where it's not the Self type
796         // Suggest:
797         //  - Changing the return type to be `impl <all bounds>`
798
799         debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found);
800
801         let ty::Param(expected_ty_as_param) = expected.kind() else { return };
802
803         let fn_node = self.tcx.hir().find(fn_id);
804
805         let Some(hir::Node::Item(hir::Item {
806             kind:
807                 hir::ItemKind::Fn(
808                     hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. },
809                     hir::Generics { params, predicates, .. },
810                     _body_id,
811                 ),
812             ..
813         })) = fn_node else { return };
814
815         if params.get(expected_ty_as_param.index as usize).is_none() {
816             return;
817         };
818
819         // get all where BoundPredicates here, because they are used in to cases below
820         let where_predicates = predicates
821             .iter()
822             .filter_map(|p| match p {
823                 WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
824                     bounds,
825                     bounded_ty,
826                     ..
827                 }) => {
828                     // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below)
829                     let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty);
830                     Some((ty, bounds))
831                 }
832                 _ => None,
833             })
834             .map(|(ty, bounds)| match ty.kind() {
835                 ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
836                 // check whether there is any predicate that contains our `T`, like `Option<T>: Send`
837                 _ => match ty.contains(expected) {
838                     true => Err(()),
839                     false => Ok(None),
840                 },
841             })
842             .collect::<Result<Vec<_>, _>>();
843
844         let Ok(where_predicates) = where_predicates else { return };
845
846         // now get all predicates in the same types as the where bounds, so we can chain them
847         let predicates_from_where =
848             where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
849
850         // extract all bounds from the source code using their spans
851         let all_matching_bounds_strs = predicates_from_where
852             .filter_map(|bound| match bound {
853                 GenericBound::Trait(_, _) => {
854                     self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
855                 }
856                 _ => None,
857             })
858             .collect::<Vec<String>>();
859
860         if all_matching_bounds_strs.len() == 0 {
861             return;
862         }
863
864         let all_bounds_str = all_matching_bounds_strs.join(" + ");
865
866         let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
867                 let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param);
868                 matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
869             });
870
871         if ty_param_used_in_fn_params {
872             return;
873         }
874
875         err.span_suggestion(
876             fn_return.span(),
877             "consider using an impl return type",
878             format!("impl {}", all_bounds_str),
879             Applicability::MaybeIncorrect,
880         );
881     }
882
883     pub(in super::super) fn suggest_missing_break_or_return_expr(
884         &self,
885         err: &mut Diagnostic,
886         expr: &'tcx hir::Expr<'tcx>,
887         fn_decl: &hir::FnDecl<'_>,
888         expected: Ty<'tcx>,
889         found: Ty<'tcx>,
890         id: hir::HirId,
891         fn_id: hir::HirId,
892     ) {
893         if !expected.is_unit() {
894             return;
895         }
896         let found = self.resolve_vars_with_obligations(found);
897
898         let in_loop = self.is_loop(id)
899             || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id));
900
901         let in_local_statement = self.is_local_statement(id)
902             || self
903                 .tcx
904                 .hir()
905                 .parent_iter(id)
906                 .any(|(parent_id, _)| self.is_local_statement(parent_id));
907
908         if in_loop && in_local_statement {
909             err.multipart_suggestion(
910                 "you might have meant to break the loop with this value",
911                 vec![
912                     (expr.span.shrink_to_lo(), "break ".to_string()),
913                     (expr.span.shrink_to_hi(), ";".to_string()),
914                 ],
915                 Applicability::MaybeIncorrect,
916             );
917             return;
918         }
919
920         if let hir::FnRetTy::Return(ty) = fn_decl.output {
921             let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
922             let bound_vars = self.tcx.late_bound_vars(fn_id);
923             let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
924             let ty = self.normalize(expr.span, ty);
925             let ty = match self.tcx.asyncness(fn_id.owner) {
926                 hir::IsAsync::Async => {
927                     let infcx = self.tcx.infer_ctxt().build();
928                     infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| {
929                         span_bug!(
930                             fn_decl.output.span(),
931                             "failed to get output type of async function"
932                         )
933                     })
934                 }
935                 hir::IsAsync::NotAsync => ty,
936             };
937             if self.can_coerce(found, ty) {
938                 err.multipart_suggestion(
939                     "you might have meant to return this value",
940                     vec![
941                         (expr.span.shrink_to_lo(), "return ".to_string()),
942                         (expr.span.shrink_to_hi(), ";".to_string()),
943                     ],
944                     Applicability::MaybeIncorrect,
945                 );
946             }
947         }
948     }
949
950     pub(in super::super) fn suggest_missing_parentheses(
951         &self,
952         err: &mut Diagnostic,
953         expr: &hir::Expr<'_>,
954     ) -> bool {
955         let sp = self.tcx.sess.source_map().start_point(expr.span);
956         if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
957             // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
958             err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
959             true
960         } else {
961             false
962         }
963     }
964
965     /// Given an expression type mismatch, peel any `&` expressions until we get to
966     /// a block expression, and then suggest replacing the braces with square braces
967     /// if it was possibly mistaken array syntax.
968     pub(crate) fn suggest_block_to_brackets_peeling_refs(
969         &self,
970         diag: &mut Diagnostic,
971         mut expr: &hir::Expr<'_>,
972         mut expr_ty: Ty<'tcx>,
973         mut expected_ty: Ty<'tcx>,
974     ) -> bool {
975         loop {
976             match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
977                 (
978                     hir::ExprKind::AddrOf(_, _, inner_expr),
979                     ty::Ref(_, inner_expr_ty, _),
980                     ty::Ref(_, inner_expected_ty, _),
981                 ) => {
982                     expr = *inner_expr;
983                     expr_ty = *inner_expr_ty;
984                     expected_ty = *inner_expected_ty;
985                 }
986                 (hir::ExprKind::Block(blk, _), _, _) => {
987                     self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
988                     break true;
989                 }
990                 _ => break false,
991             }
992         }
993     }
994
995     pub(crate) fn suggest_copied_or_cloned(
996         &self,
997         diag: &mut Diagnostic,
998         expr: &hir::Expr<'_>,
999         expr_ty: Ty<'tcx>,
1000         expected_ty: Ty<'tcx>,
1001     ) -> bool {
1002         let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; };
1003         let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; };
1004         if adt_def != expected_adt_def {
1005             return false;
1006         }
1007
1008         let mut suggest_copied_or_cloned = || {
1009             let expr_inner_ty = substs.type_at(0);
1010             let expected_inner_ty = expected_substs.type_at(0);
1011             if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
1012                 && self.can_eq(self.param_env, *ty, expected_inner_ty).is_ok()
1013             {
1014                 let def_path = self.tcx.def_path_str(adt_def.did());
1015                 if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) {
1016                     diag.span_suggestion_verbose(
1017                         expr.span.shrink_to_hi(),
1018                         format!(
1019                             "use `{def_path}::copied` to copy the value inside the `{def_path}`"
1020                         ),
1021                         ".copied()",
1022                         Applicability::MachineApplicable,
1023                     );
1024                     return true;
1025                 } else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
1026                     && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
1027                         self,
1028                         self.param_env,
1029                         *ty,
1030                         clone_did,
1031                         expr.span
1032                     )
1033                 {
1034                     diag.span_suggestion_verbose(
1035                         expr.span.shrink_to_hi(),
1036                         format!(
1037                             "use `{def_path}::cloned` to clone the value inside the `{def_path}`"
1038                         ),
1039                         ".cloned()",
1040                         Applicability::MachineApplicable,
1041                     );
1042                     return true;
1043                 }
1044             }
1045             false
1046         };
1047
1048         if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
1049             && adt_def.did() == result_did
1050             // Check that the error types are equal
1051             && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok()
1052         {
1053             return suggest_copied_or_cloned();
1054         } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
1055             && adt_def.did() == option_did
1056         {
1057             return suggest_copied_or_cloned();
1058         }
1059
1060         false
1061     }
1062
1063     pub(crate) fn suggest_into(
1064         &self,
1065         diag: &mut Diagnostic,
1066         expr: &hir::Expr<'_>,
1067         expr_ty: Ty<'tcx>,
1068         expected_ty: Ty<'tcx>,
1069     ) -> bool {
1070         let expr = expr.peel_blocks();
1071
1072         // We have better suggestions for scalar interconversions...
1073         if expr_ty.is_scalar() && expected_ty.is_scalar() {
1074             return false;
1075         }
1076
1077         // Don't suggest turning a block into another type (e.g. `{}.into()`)
1078         if matches!(expr.kind, hir::ExprKind::Block(..)) {
1079             return false;
1080         }
1081
1082         // We'll later suggest `.as_ref` when noting the type error,
1083         // so skip if we will suggest that instead.
1084         if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() {
1085             return false;
1086         }
1087
1088         if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
1089             && self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
1090                 self.tcx,
1091                 self.misc(expr.span),
1092                 self.param_env,
1093                 ty::Binder::dummy(self.tcx.mk_trait_ref(
1094                     into_def_id,
1095                     [expr_ty, expected_ty]
1096                 )),
1097             ))
1098         {
1099             let sugg = if expr.precedence().order() >= PREC_POSTFIX {
1100                 vec![(expr.span.shrink_to_hi(), ".into()".to_owned())]
1101             } else {
1102                 vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())]
1103             };
1104             diag.multipart_suggestion(
1105                 format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
1106                 sugg,
1107                 Applicability::MaybeIncorrect
1108             );
1109             return true;
1110         }
1111
1112         false
1113     }
1114
1115     /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()`
1116     pub(crate) fn suggest_option_to_bool(
1117         &self,
1118         diag: &mut Diagnostic,
1119         expr: &hir::Expr<'_>,
1120         expr_ty: Ty<'tcx>,
1121         expected_ty: Ty<'tcx>,
1122     ) -> bool {
1123         if !expected_ty.is_bool() {
1124             return false;
1125         }
1126
1127         let ty::Adt(def, _) = expr_ty.peel_refs().kind() else { return false; };
1128         if !self.tcx.is_diagnostic_item(sym::Option, def.did()) {
1129             return false;
1130         }
1131
1132         let hir = self.tcx.hir();
1133         let cond_parent = hir.parent_iter(expr.hir_id).skip_while(|(_, node)| {
1134             matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
1135         }).next();
1136         // Don't suggest:
1137         //     `let Some(_) = a.is_some() && b`
1138         //                     ++++++++++
1139         // since the user probably just misunderstood how `let else`
1140         // and `&&` work together.
1141         if let Some((_, hir::Node::Local(local))) = cond_parent
1142             && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind
1143             && let hir::QPath::Resolved(None, path) = qpath
1144             && let Some(did) = path.res.opt_def_id()
1145                 .and_then(|did| self.tcx.opt_parent(did))
1146                 .and_then(|did| self.tcx.opt_parent(did))
1147             && self.tcx.is_diagnostic_item(sym::Option, did)
1148         {
1149             return false;
1150         }
1151
1152         diag.span_suggestion(
1153             expr.span.shrink_to_hi(),
1154             "use `Option::is_some` to test if the `Option` has a value",
1155             ".is_some()",
1156             Applicability::MachineApplicable,
1157         );
1158
1159         true
1160     }
1161
1162     /// Suggest wrapping the block in square brackets instead of curly braces
1163     /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
1164     pub(crate) fn suggest_block_to_brackets(
1165         &self,
1166         diag: &mut Diagnostic,
1167         blk: &hir::Block<'_>,
1168         blk_ty: Ty<'tcx>,
1169         expected_ty: Ty<'tcx>,
1170     ) {
1171         if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
1172             if self.can_coerce(blk_ty, *elem_ty)
1173                 && blk.stmts.is_empty()
1174                 && blk.rules == hir::BlockCheckMode::DefaultBlock
1175             {
1176                 let source_map = self.tcx.sess.source_map();
1177                 if let Ok(snippet) = source_map.span_to_snippet(blk.span) {
1178                     if snippet.starts_with('{') && snippet.ends_with('}') {
1179                         diag.multipart_suggestion_verbose(
1180                             "to create an array, use square brackets instead of curly braces",
1181                             vec![
1182                                 (
1183                                     blk.span
1184                                         .shrink_to_lo()
1185                                         .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
1186                                     "[".to_string(),
1187                                 ),
1188                                 (
1189                                     blk.span
1190                                         .shrink_to_hi()
1191                                         .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
1192                                     "]".to_string(),
1193                                 ),
1194                             ],
1195                             Applicability::MachineApplicable,
1196                         );
1197                     }
1198                 }
1199             }
1200         }
1201     }
1202
1203     #[instrument(skip(self, err))]
1204     pub(crate) fn suggest_floating_point_literal(
1205         &self,
1206         err: &mut Diagnostic,
1207         expr: &hir::Expr<'_>,
1208         expected_ty: Ty<'tcx>,
1209     ) -> bool {
1210         if !expected_ty.is_floating_point() {
1211             return false;
1212         }
1213         match expr.kind {
1214             ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => {
1215                 err.span_suggestion_verbose(
1216                     start.span.shrink_to_hi().with_hi(end.span.lo()),
1217                     "remove the unnecessary `.` operator for a floating point literal",
1218                     '.',
1219                     Applicability::MaybeIncorrect,
1220                 );
1221                 true
1222             }
1223             ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => {
1224                 err.span_suggestion_verbose(
1225                     expr.span.with_lo(start.span.hi()),
1226                     "remove the unnecessary `.` operator for a floating point literal",
1227                     '.',
1228                     Applicability::MaybeIncorrect,
1229                 );
1230                 true
1231             }
1232             ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => {
1233                 err.span_suggestion_verbose(
1234                     expr.span.until(end.span),
1235                     "remove the unnecessary `.` operator and add an integer part for a floating point literal",
1236                     "0.",
1237                     Applicability::MaybeIncorrect,
1238                 );
1239                 true
1240             }
1241             _ => false,
1242         }
1243     }
1244
1245     fn is_loop(&self, id: hir::HirId) -> bool {
1246         let node = self.tcx.hir().get(id);
1247         matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
1248     }
1249
1250     fn is_local_statement(&self, id: hir::HirId) -> bool {
1251         let node = self.tcx.hir().get(id);
1252         matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
1253     }
1254
1255     /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
1256     /// which is a side-effect of autoref.
1257     pub(crate) fn note_type_is_not_clone(
1258         &self,
1259         diag: &mut Diagnostic,
1260         expected_ty: Ty<'tcx>,
1261         found_ty: Ty<'tcx>,
1262         expr: &hir::Expr<'_>,
1263     ) {
1264         let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else { return; };
1265         let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; };
1266         let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
1267         let results = self.typeck_results.borrow();
1268         // First, look for a `Clone::clone` call
1269         if segment.ident.name == sym::clone
1270             && results.type_dependent_def_id(expr.hir_id).map_or(
1271                 false,
1272                 |did| {
1273                     let assoc_item = self.tcx.associated_item(did);
1274                     assoc_item.container == ty::AssocItemContainer::TraitContainer
1275                         && assoc_item.container_id(self.tcx) == clone_trait_did
1276                 },
1277             )
1278             // If that clone call hasn't already dereferenced the self type (i.e. don't give this
1279             // diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
1280             && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
1281             // Check that we're in fact trying to clone into the expected type
1282             && self.can_coerce(*pointee_ty, expected_ty)
1283             // And the expected type doesn't implement `Clone`
1284             && !self.predicate_must_hold_considering_regions(&traits::Obligation {
1285                 cause: traits::ObligationCause::dummy(),
1286                 param_env: self.param_env,
1287                 recursion_depth: 0,
1288                 predicate: ty::Binder::dummy(ty::TraitRef {
1289                     def_id: clone_trait_did,
1290                     substs: self.tcx.mk_substs([expected_ty.into()].iter()),
1291                 })
1292                 .without_const()
1293                 .to_predicate(self.tcx),
1294             })
1295         {
1296             diag.span_note(
1297                 callee_expr.span,
1298                 &format!(
1299                     "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
1300                 ),
1301             );
1302         }
1303     }
1304
1305     /// A common error is to add an extra semicolon:
1306     ///
1307     /// ```compile_fail,E0308
1308     /// fn foo() -> usize {
1309     ///     22;
1310     /// }
1311     /// ```
1312     ///
1313     /// This routine checks if the final statement in a block is an
1314     /// expression with an explicit semicolon whose type is compatible
1315     /// with `expected_ty`. If so, it suggests removing the semicolon.
1316     pub(crate) fn consider_removing_semicolon(
1317         &self,
1318         blk: &'tcx hir::Block<'tcx>,
1319         expected_ty: Ty<'tcx>,
1320         err: &mut Diagnostic,
1321     ) -> bool {
1322         if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) {
1323             if let StatementAsExpression::NeedsBoxing = boxed {
1324                 err.span_suggestion_verbose(
1325                     span_semi,
1326                     "consider removing this semicolon and boxing the expression",
1327                     "",
1328                     Applicability::HasPlaceholders,
1329                 );
1330             } else {
1331                 err.span_suggestion_short(
1332                     span_semi,
1333                     "remove this semicolon to return this value",
1334                     "",
1335                     Applicability::MachineApplicable,
1336                 );
1337             }
1338             true
1339         } else {
1340             false
1341         }
1342     }
1343 }