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