]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / compiler / rustc_typeck / src / check / fn_ctxt / suggestions.rs
1 use super::FnCtxt;
2 use crate::astconv::AstConv;
3 use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
4
5 use rustc_ast::util::parser::ExprPrecedence;
6 use rustc_errors::{Applicability, Diagnostic, MultiSpan};
7 use rustc_hir as hir;
8 use rustc_hir::def::{CtorOf, DefKind};
9 use rustc_hir::lang_items::LangItem;
10 use rustc_hir::{
11 Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
12 };
13 use rustc_infer::infer::{self, TyCtxtInferExt};
14 use rustc_infer::traits;
15 use rustc_middle::lint::in_external_macro;
16 use rustc_middle::ty::subst::GenericArgKind;
17 use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
18 use rustc_span::symbol::sym;
19 use rustc_span::Span;
20 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
21
22 use std::iter;
23
24 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
25 pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) {
26 err.span_suggestion_short(
27 span.shrink_to_hi(),
28 "consider using a semicolon here",
29 ";",
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,
41 err: &mut Diagnostic,
42 expr: &'tcx hir::Expr<'tcx>,
43 expected: Ty<'tcx>,
44 found: Ty<'tcx>,
45 blk_id: hir::HirId,
46 ) -> bool {
47 let expr = expr.peel_drop_temps();
48 self.suggest_missing_semicolon(err, expr, expected, false);
49 let mut pointing_at_return_type = false;
50 if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
51 let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap();
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 );
60 self.suggest_missing_break_or_return_expr(
61 err, expr, &fn_decl, expected, found, blk_id, fn_id,
62 );
63 }
64 pointing_at_return_type
65 }
66
67 /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
68 /// the ctor would successfully solve the type mismatch and if so, suggest it:
69 /// ```compile_fail,E0308
70 /// fn foo(x: usize) -> usize { x }
71 /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
72 /// ```
73 fn suggest_fn_call(
74 &self,
75 err: &mut Diagnostic,
76 expr: &hir::Expr<'_>,
77 expected: Ty<'tcx>,
78 found: Ty<'tcx>,
79 ) -> bool {
80 let (def_id, output, inputs) = match *found.kind() {
81 ty::FnDef(def_id, _) => {
82 let fn_sig = found.fn_sig(self.tcx);
83 (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
84 }
85 ty::Closure(def_id, substs) => {
86 let fn_sig = substs.as_closure().sig();
87 (def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)
88 }
89 ty::Opaque(def_id, substs) => {
90 let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
91 if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
92 && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
93 // args tuple will always be substs[1]
94 && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
95 {
96 Some((
97 pred.kind().rebind(proj.term.ty().unwrap()),
98 args.len(),
99 ))
100 } else {
101 None
102 }
103 });
104 if let Some((output, inputs)) = sig {
105 (def_id, output, inputs)
106 } else {
107 return false;
108 }
109 }
110 _ => return false,
111 };
112
113 let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
114 let output = self.normalize_associated_types_in(expr.span, output);
115 if !output.is_ty_var() && self.can_coerce(output, expected) {
116 let (sugg_call, mut applicability) = match inputs {
117 0 => ("".to_string(), Applicability::MachineApplicable),
118 1..=4 => (
119 (0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
120 Applicability::MachineApplicable,
121 ),
122 _ => ("...".to_string(), Applicability::HasPlaceholders),
123 };
124
125 let msg = match self.tcx.def_kind(def_id) {
126 DefKind::Fn => "call this function",
127 DefKind::Closure | DefKind::OpaqueTy => "call this closure",
128 DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
129 DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
130 _ => "call this function",
131 };
132
133 let sugg = match expr.kind {
134 hir::ExprKind::Call(..)
135 | hir::ExprKind::Path(..)
136 | hir::ExprKind::Index(..)
137 | hir::ExprKind::Lit(..) => {
138 vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
139 }
140 hir::ExprKind::Closure { .. } => {
141 // Might be `{ expr } || { bool }`
142 applicability = Applicability::MaybeIncorrect;
143 vec![
144 (expr.span.shrink_to_lo(), "(".to_string()),
145 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
146 ]
147 }
148 _ => {
149 vec![
150 (expr.span.shrink_to_lo(), "(".to_string()),
151 (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
152 ]
153 }
154 };
155
156 err.multipart_suggestion_verbose(
157 format!("use parentheses to {msg}"),
158 sugg,
159 applicability,
160 );
161
162 return true;
163 }
164 false
165 }
166
167 pub fn suggest_deref_ref_or_into(
168 &self,
169 err: &mut Diagnostic,
170 expr: &hir::Expr<'tcx>,
171 expected: Ty<'tcx>,
172 found: Ty<'tcx>,
173 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
174 ) {
175 let expr = expr.peel_blocks();
176 if let Some((sp, msg, suggestion, applicability, verbose)) =
177 self.check_ref(expr, found, expected)
178 {
179 if verbose {
180 err.span_suggestion_verbose(sp, &msg, suggestion, applicability);
181 } else {
182 err.span_suggestion(sp, &msg, suggestion, applicability);
183 }
184 } else if let (ty::FnDef(def_id, ..), true) =
185 (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
186 {
187 if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
188 let sp = self.sess().source_map().guess_head_span(sp);
189 err.span_label(sp, &format!("{} defined here", found));
190 }
191 } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
192 let is_struct_pat_shorthand_field =
193 self.maybe_get_struct_pattern_shorthand_field(expr).is_some();
194 let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
195 if !methods.is_empty() {
196 if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
197 let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods)
198 .filter_map(|(receiver, method)| {
199 let method_call = format!(".{}()", method.name);
200 if receiver.ends_with(&method_call) {
201 None // do not suggest code that is already there (#53348)
202 } else {
203 let method_call_list = [".to_vec()", ".to_string()"];
204 let mut sugg = if receiver.ends_with(".clone()")
205 && method_call_list.contains(&method_call.as_str())
206 {
207 let max_len = receiver.rfind('.').unwrap();
208 vec![(
209 expr.span,
210 format!("{}{}", &receiver[..max_len], method_call),
211 )]
212 } else {
213 if expr.precedence().order()
214 < ExprPrecedence::MethodCall.order()
215 {
216 vec![
217 (expr.span.shrink_to_lo(), "(".to_string()),
218 (expr.span.shrink_to_hi(), format!("){}", method_call)),
219 ]
220 } else {
221 vec![(expr.span.shrink_to_hi(), method_call)]
222 }
223 };
224 if is_struct_pat_shorthand_field {
225 sugg.insert(
226 0,
227 (expr.span.shrink_to_lo(), format!("{}: ", receiver)),
228 );
229 }
230 Some(sugg)
231 }
232 })
233 .peekable();
234 if suggestions.peek().is_some() {
235 err.multipart_suggestions(
236 "try using a conversion method",
237 suggestions,
238 Applicability::MaybeIncorrect,
239 );
240 }
241 }
242 } else if found.to_string().starts_with("Option<")
243 && expected.to_string() == "Option<&str>"
244 {
245 if let ty::Adt(_def, subst) = found.kind() {
246 if subst.len() != 0 {
247 if let GenericArgKind::Type(ty) = subst[0].unpack() {
248 let peeled = ty.peel_refs().to_string();
249 if peeled == "String" {
250 let ref_cnt = ty.to_string().len() - peeled.len();
251 let result = format!(".map(|x| &*{}x)", "*".repeat(ref_cnt));
252 err.span_suggestion_verbose(
253 expr.span.shrink_to_hi(),
254 "try converting the passed type into a `&str`",
255 result,
256 Applicability::MaybeIncorrect,
257 );
258 }
259 }
260 }
261 }
262 }
263 }
264 }
265
266 /// When encountering the expected boxed value allocated in the stack, suggest allocating it
267 /// in the heap by calling `Box::new()`.
268 pub(in super::super) fn suggest_boxing_when_appropriate(
269 &self,
270 err: &mut Diagnostic,
271 expr: &hir::Expr<'_>,
272 expected: Ty<'tcx>,
273 found: Ty<'tcx>,
274 ) {
275 if self.tcx.hir().is_inside_const_context(expr.hir_id) {
276 // Do not suggest `Box::new` in const context.
277 return;
278 }
279 if !expected.is_box() || found.is_box() {
280 return;
281 }
282 let boxed_found = self.tcx.mk_box(found);
283 if self.can_coerce(boxed_found, expected) {
284 err.multipart_suggestion(
285 "store this in the heap by calling `Box::new`",
286 vec![
287 (expr.span.shrink_to_lo(), "Box::new(".to_string()),
288 (expr.span.shrink_to_hi(), ")".to_string()),
289 ],
290 Applicability::MachineApplicable,
291 );
292 err.note(
293 "for more on the distinction between the stack and the heap, read \
294 https://doc.rust-lang.org/book/ch15-01-box.html, \
295 https://doc.rust-lang.org/rust-by-example/std/box.html, and \
296 https://doc.rust-lang.org/std/boxed/index.html",
297 );
298 }
299 }
300
301 /// When encountering a closure that captures variables, where a FnPtr is expected,
302 /// suggest a non-capturing closure
303 pub(in super::super) fn suggest_no_capture_closure(
304 &self,
305 err: &mut Diagnostic,
306 expected: Ty<'tcx>,
307 found: Ty<'tcx>,
308 ) {
309 if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
310 if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
311 // Report upto four upvars being captured to reduce the amount error messages
312 // reported back to the user.
313 let spans_and_labels = upvars
314 .iter()
315 .take(4)
316 .map(|(var_hir_id, upvar)| {
317 let var_name = self.tcx.hir().name(*var_hir_id).to_string();
318 let msg = format!("`{}` captured here", var_name);
319 (upvar.span, msg)
320 })
321 .collect::<Vec<_>>();
322
323 let mut multi_span: MultiSpan =
324 spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
325 for (sp, label) in spans_and_labels {
326 multi_span.push_span_label(sp, label);
327 }
328 err.span_note(
329 multi_span,
330 "closures can only be coerced to `fn` types if they do not capture any variables"
331 );
332 }
333 }
334 }
335
336 /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
337 #[instrument(skip(self, err))]
338 pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
339 &self,
340 err: &mut Diagnostic,
341 expr: &hir::Expr<'_>,
342 expected: Ty<'tcx>,
343 found: Ty<'tcx>,
344 ) -> bool {
345 // Handle #68197.
346
347 if self.tcx.hir().is_inside_const_context(expr.hir_id) {
348 // Do not suggest `Box::new` in const context.
349 return false;
350 }
351 let pin_did = self.tcx.lang_items().pin_type();
352 // This guards the `unwrap` and `mk_box` below.
353 if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
354 return false;
355 }
356 let box_found = self.tcx.mk_box(found);
357 let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
358 let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap();
359 match expected.kind() {
360 ty::Adt(def, _) if Some(def.did()) == pin_did => {
361 if self.can_coerce(pin_box_found, expected) {
362 debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
363 match found.kind() {
364 ty::Adt(def, _) if def.is_box() => {
365 err.help("use `Box::pin`");
366 }
367 _ => {
368 err.multipart_suggestion(
369 "you need to pin and box this expression",
370 vec![
371 (expr.span.shrink_to_lo(), "Box::pin(".to_string()),
372 (expr.span.shrink_to_hi(), ")".to_string()),
373 ],
374 Applicability::MaybeIncorrect,
375 );
376 }
377 }
378 true
379 } else if self.can_coerce(pin_found, expected) {
380 match found.kind() {
381 ty::Adt(def, _) if def.is_box() => {
382 err.help("use `Box::pin`");
383 true
384 }
385 _ => false,
386 }
387 } else {
388 false
389 }
390 }
391 ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => {
392 // Check if the parent expression is a call to Pin::new. If it
393 // is and we were expecting a Box, ergo Pin<Box<expected>>, we
394 // can suggest Box::pin.
395 let parent = self.tcx.hir().get_parent_node(expr.hir_id);
396 let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else {
397 return false;
398 };
399 match fn_name.kind {
400 ExprKind::Path(QPath::TypeRelative(
401 hir::Ty {
402 kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })),
403 ..
404 },
405 method,
406 )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => {
407 err.span_suggestion(
408 fn_name.span,
409 "use `Box::pin` to pin and box this expression",
410 "Box::pin",
411 Applicability::MachineApplicable,
412 );
413 true
414 }
415 _ => false,
416 }
417 }
418 _ => false,
419 }
420 }
421
422 /// A common error is to forget to add a semicolon at the end of a block, e.g.,
423 ///
424 /// ```compile_fail,E0308
425 /// # fn bar_that_returns_u32() -> u32 { 4 }
426 /// fn foo() {
427 /// bar_that_returns_u32()
428 /// }
429 /// ```
430 ///
431 /// This routine checks if the return expression in a block would make sense on its own as a
432 /// statement and the return type has been left as default or has been specified as `()`. If so,
433 /// it suggests adding a semicolon.
434 ///
435 /// If the expression is the expression of a closure without block (`|| expr`), a
436 /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`.
437 pub fn suggest_missing_semicolon(
438 &self,
439 err: &mut Diagnostic,
440 expression: &'tcx hir::Expr<'tcx>,
441 expected: Ty<'tcx>,
442 needs_block: bool,
443 ) {
444 if expected.is_unit() {
445 // `BlockTailExpression` only relevant if the tail expr would be
446 // useful on its own.
447 match expression.kind {
448 ExprKind::Call(..)
449 | ExprKind::MethodCall(..)
450 | ExprKind::Loop(..)
451 | ExprKind::If(..)
452 | ExprKind::Match(..)
453 | ExprKind::Block(..)
454 if expression.can_have_side_effects()
455 // If the expression is from an external macro, then do not suggest
456 // adding a semicolon, because there's nowhere to put it.
457 // See issue #81943.
458 && !in_external_macro(self.tcx.sess, expression.span) =>
459 {
460 if needs_block {
461 err.multipart_suggestion(
462 "consider using a semicolon here",
463 vec![
464 (expression.span.shrink_to_lo(), "{ ".to_owned()),
465 (expression.span.shrink_to_hi(), "; }".to_owned()),
466 ],
467 Applicability::MachineApplicable,
468 );
469 } else {
470 err.span_suggestion(
471 expression.span.shrink_to_hi(),
472 "consider using a semicolon here",
473 ";",
474 Applicability::MachineApplicable,
475 );
476 }
477 }
478 _ => (),
479 }
480 }
481 }
482
483 /// A possible error is to forget to add a return type that is needed:
484 ///
485 /// ```compile_fail,E0308
486 /// # fn bar_that_returns_u32() -> u32 { 4 }
487 /// fn foo() {
488 /// bar_that_returns_u32()
489 /// }
490 /// ```
491 ///
492 /// This routine checks if the return type is left as default, the method is not part of an
493 /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
494 /// type.
495 pub(in super::super) fn suggest_missing_return_type(
496 &self,
497 err: &mut Diagnostic,
498 fn_decl: &hir::FnDecl<'_>,
499 expected: Ty<'tcx>,
500 found: Ty<'tcx>,
501 can_suggest: bool,
502 fn_id: hir::HirId,
503 ) -> bool {
504 let found =
505 self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
506 // Only suggest changing the return type for methods that
507 // haven't set a return type at all (and aren't `fn main()` or an impl).
508 match (&fn_decl.output, found.is_suggestable(self.tcx), can_suggest, expected.is_unit()) {
509 (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
510 err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found });
511 true
512 }
513 (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
514 // FIXME: if `found` could be `impl Iterator` or `impl Fn*`, we should suggest
515 // that.
516 err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
517 true
518 }
519 (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
520 // `fn main()` must return `()`, do not suggest changing return type
521 err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
522 true
523 }
524 // expectation was caused by something else, not the default return
525 (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false,
526 (&hir::FnRetTy::Return(ref ty), _, _, _) => {
527 // Only point to return type if the expected type is the return type, as if they
528 // are not, the expectation must have been caused by something else.
529 debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
530 let span = ty.span;
531 let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
532 debug!("suggest_missing_return_type: return type {:?}", ty);
533 debug!("suggest_missing_return_type: expected type {:?}", ty);
534 let bound_vars = self.tcx.late_bound_vars(fn_id);
535 let ty = Binder::bind_with_vars(ty, bound_vars);
536 let ty = self.normalize_associated_types_in(span, ty);
537 let ty = self.tcx.erase_late_bound_regions(ty);
538 if self.can_coerce(expected, ty) {
539 err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
540 self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
541 return true;
542 }
543 false
544 }
545 }
546 }
547
548 /// check whether the return type is a generic type with a trait bound
549 /// only suggest this if the generic param is not present in the arguments
550 /// if this is true, hint them towards changing the return type to `impl Trait`
551 /// ```compile_fail,E0308
552 /// fn cant_name_it<T: Fn() -> u32>() -> T {
553 /// || 3
554 /// }
555 /// ```
556 fn try_suggest_return_impl_trait(
557 &self,
558 err: &mut Diagnostic,
559 expected: Ty<'tcx>,
560 found: Ty<'tcx>,
561 fn_id: hir::HirId,
562 ) {
563 // Only apply the suggestion if:
564 // - the return type is a generic parameter
565 // - the generic param is not used as a fn param
566 // - the generic param has at least one bound
567 // - the generic param doesn't appear in any other bounds where it's not the Self type
568 // Suggest:
569 // - Changing the return type to be `impl <all bounds>`
570
571 debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found);
572
573 let ty::Param(expected_ty_as_param) = expected.kind() else { return };
574
575 let fn_node = self.tcx.hir().find(fn_id);
576
577 let Some(hir::Node::Item(hir::Item {
578 kind:
579 hir::ItemKind::Fn(
580 hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. },
581 hir::Generics { params, predicates, .. },
582 _body_id,
583 ),
584 ..
585 })) = fn_node else { return };
586
587 if params.get(expected_ty_as_param.index as usize).is_none() {
588 return;
589 };
590
591 // get all where BoundPredicates here, because they are used in to cases below
592 let where_predicates = predicates
593 .iter()
594 .filter_map(|p| match p {
595 WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
596 bounds,
597 bounded_ty,
598 ..
599 }) => {
600 // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below)
601 let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty);
602 Some((ty, bounds))
603 }
604 _ => None,
605 })
606 .map(|(ty, bounds)| match ty.kind() {
607 ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
608 // check whether there is any predicate that contains our `T`, like `Option<T>: Send`
609 _ => match ty.contains(expected) {
610 true => Err(()),
611 false => Ok(None),
612 },
613 })
614 .collect::<Result<Vec<_>, _>>();
615
616 let Ok(where_predicates) = where_predicates else { return };
617
618 // now get all predicates in the same types as the where bounds, so we can chain them
619 let predicates_from_where =
620 where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
621
622 // extract all bounds from the source code using their spans
623 let all_matching_bounds_strs = predicates_from_where
624 .filter_map(|bound| match bound {
625 GenericBound::Trait(_, _) => {
626 self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
627 }
628 _ => None,
629 })
630 .collect::<Vec<String>>();
631
632 if all_matching_bounds_strs.len() == 0 {
633 return;
634 }
635
636 let all_bounds_str = all_matching_bounds_strs.join(" + ");
637
638 let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
639 let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param);
640 matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
641 });
642
643 if ty_param_used_in_fn_params {
644 return;
645 }
646
647 err.span_suggestion(
648 fn_return.span(),
649 "consider using an impl return type",
650 format!("impl {}", all_bounds_str),
651 Applicability::MaybeIncorrect,
652 );
653 }
654
655 pub(in super::super) fn suggest_missing_break_or_return_expr(
656 &self,
657 err: &mut Diagnostic,
658 expr: &'tcx hir::Expr<'tcx>,
659 fn_decl: &hir::FnDecl<'_>,
660 expected: Ty<'tcx>,
661 found: Ty<'tcx>,
662 id: hir::HirId,
663 fn_id: hir::HirId,
664 ) {
665 if !expected.is_unit() {
666 return;
667 }
668 let found = self.resolve_vars_with_obligations(found);
669
670 let in_loop = self.is_loop(id)
671 || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id));
672
673 let in_local_statement = self.is_local_statement(id)
674 || self
675 .tcx
676 .hir()
677 .parent_iter(id)
678 .any(|(parent_id, _)| self.is_local_statement(parent_id));
679
680 if in_loop && in_local_statement {
681 err.multipart_suggestion(
682 "you might have meant to break the loop with this value",
683 vec![
684 (expr.span.shrink_to_lo(), "break ".to_string()),
685 (expr.span.shrink_to_hi(), ";".to_string()),
686 ],
687 Applicability::MaybeIncorrect,
688 );
689 return;
690 }
691
692 if let hir::FnRetTy::Return(ty) = fn_decl.output {
693 let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
694 let bound_vars = self.tcx.late_bound_vars(fn_id);
695 let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
696 let ty = self.normalize_associated_types_in(expr.span, ty);
697 let ty = match self.tcx.asyncness(fn_id.owner) {
698 hir::IsAsync::Async => self
699 .tcx
700 .infer_ctxt()
701 .enter(|infcx| {
702 infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| {
703 span_bug!(
704 fn_decl.output.span(),
705 "failed to get output type of async function"
706 )
707 })
708 })
709 .skip_binder(),
710 hir::IsAsync::NotAsync => ty,
711 };
712 if self.can_coerce(found, ty) {
713 err.multipart_suggestion(
714 "you might have meant to return this value",
715 vec![
716 (expr.span.shrink_to_lo(), "return ".to_string()),
717 (expr.span.shrink_to_hi(), ";".to_string()),
718 ],
719 Applicability::MaybeIncorrect,
720 );
721 }
722 }
723 }
724
725 pub(in super::super) fn suggest_missing_parentheses(
726 &self,
727 err: &mut Diagnostic,
728 expr: &hir::Expr<'_>,
729 ) {
730 let sp = self.tcx.sess.source_map().start_point(expr.span);
731 if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
732 // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
733 self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp);
734 }
735 }
736
737 /// Given an expression type mismatch, peel any `&` expressions until we get to
738 /// a block expression, and then suggest replacing the braces with square braces
739 /// if it was possibly mistaken array syntax.
740 pub(crate) fn suggest_block_to_brackets_peeling_refs(
741 &self,
742 diag: &mut Diagnostic,
743 mut expr: &hir::Expr<'_>,
744 mut expr_ty: Ty<'tcx>,
745 mut expected_ty: Ty<'tcx>,
746 ) {
747 loop {
748 match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
749 (
750 hir::ExprKind::AddrOf(_, _, inner_expr),
751 ty::Ref(_, inner_expr_ty, _),
752 ty::Ref(_, inner_expected_ty, _),
753 ) => {
754 expr = *inner_expr;
755 expr_ty = *inner_expr_ty;
756 expected_ty = *inner_expected_ty;
757 }
758 (hir::ExprKind::Block(blk, _), _, _) => {
759 self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
760 break;
761 }
762 _ => break,
763 }
764 }
765 }
766
767 /// Suggest wrapping the block in square brackets instead of curly braces
768 /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
769 pub(crate) fn suggest_block_to_brackets(
770 &self,
771 diag: &mut Diagnostic,
772 blk: &hir::Block<'_>,
773 blk_ty: Ty<'tcx>,
774 expected_ty: Ty<'tcx>,
775 ) {
776 if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
777 if self.can_coerce(blk_ty, *elem_ty)
778 && blk.stmts.is_empty()
779 && blk.rules == hir::BlockCheckMode::DefaultBlock
780 {
781 let source_map = self.tcx.sess.source_map();
782 if let Ok(snippet) = source_map.span_to_snippet(blk.span) {
783 if snippet.starts_with('{') && snippet.ends_with('}') {
784 diag.multipart_suggestion_verbose(
785 "to create an array, use square brackets instead of curly braces",
786 vec![
787 (
788 blk.span
789 .shrink_to_lo()
790 .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
791 "[".to_string(),
792 ),
793 (
794 blk.span
795 .shrink_to_hi()
796 .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
797 "]".to_string(),
798 ),
799 ],
800 Applicability::MachineApplicable,
801 );
802 }
803 }
804 }
805 }
806 }
807
808 fn is_loop(&self, id: hir::HirId) -> bool {
809 let node = self.tcx.hir().get(id);
810 matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
811 }
812
813 fn is_local_statement(&self, id: hir::HirId) -> bool {
814 let node = self.tcx.hir().get(id);
815 matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. }))
816 }
817
818 /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
819 /// which is a side-effect of autoref.
820 pub(crate) fn note_type_is_not_clone(
821 &self,
822 diag: &mut Diagnostic,
823 expected_ty: Ty<'tcx>,
824 found_ty: Ty<'tcx>,
825 expr: &hir::Expr<'_>,
826 ) {
827 let hir::ExprKind::MethodCall(segment, &[ref callee_expr], _) = expr.kind else { return; };
828 let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; };
829 let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
830 let results = self.typeck_results.borrow();
831 // First, look for a `Clone::clone` call
832 if segment.ident.name == sym::clone
833 && results.type_dependent_def_id(expr.hir_id).map_or(
834 false,
835 |did| {
836 self.tcx.associated_item(did).container
837 == ty::AssocItemContainer::TraitContainer(clone_trait_did)
838 },
839 )
840 // If that clone call hasn't already dereferenced the self type (i.e. don't give this
841 // diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
842 && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
843 // Check that we're in fact trying to clone into the expected type
844 && self.can_coerce(*pointee_ty, expected_ty)
845 // And the expected type doesn't implement `Clone`
846 && !self.predicate_must_hold_considering_regions(&traits::Obligation {
847 cause: traits::ObligationCause::dummy(),
848 param_env: self.param_env,
849 recursion_depth: 0,
850 predicate: ty::Binder::dummy(ty::TraitRef {
851 def_id: clone_trait_did,
852 substs: self.tcx.mk_substs([expected_ty.into()].iter()),
853 })
854 .without_const()
855 .to_predicate(self.tcx),
856 })
857 {
858 diag.span_note(
859 callee_expr.span,
860 &format!(
861 "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
862 ),
863 );
864 }
865 }
866 }