2 use crate::astconv
::AstConv
;
4 use rustc_ast
::util
::parser
::ExprPrecedence
;
5 use rustc_span
::{self, MultiSpan, Span}
;
7 use rustc_errors
::{Applicability, DiagnosticBuilder}
;
9 use rustc_hir
::def
::{CtorOf, DefKind}
;
10 use rustc_hir
::lang_items
::LangItem
;
11 use rustc_hir
::{ExprKind, ItemKind, Node}
;
12 use rustc_infer
::infer
;
13 use rustc_middle
::lint
::in_external_macro
;
14 use rustc_middle
::ty
::{self, Binder, Ty}
;
15 use rustc_span
::symbol
::kw
;
19 impl<'a
, 'tcx
> FnCtxt
<'a
, 'tcx
> {
20 pub(in super::super) fn suggest_semicolon_at_end(
23 err
: &mut DiagnosticBuilder
<'_
>,
25 err
.span_suggestion_short(
27 "consider using a semicolon here",
29 Applicability
::MachineApplicable
,
33 /// On implicit return expressions with mismatched types, provides the following suggestions:
35 /// - Points out the method's return type as the reason for the expected type.
36 /// - Possible missing semicolon.
37 /// - Possible missing return type if the return type is the default, and not `fn main()`.
38 pub fn suggest_mismatched_types_on_tail(
40 err
: &mut DiagnosticBuilder
<'_
>,
41 expr
: &'tcx hir
::Expr
<'tcx
>,
47 let expr
= expr
.peel_drop_temps();
48 // If the expression is from an external macro, then do not suggest
49 // adding a semicolon, because there's nowhere to put it.
51 if expr
.can_have_side_effects() && !in_external_macro(self.tcx
.sess
, cause_span
) {
52 self.suggest_missing_semicolon(err
, expr
, expected
, cause_span
);
54 let mut pointing_at_return_type
= false;
55 if let Some((fn_decl
, can_suggest
)) = self.get_fn_decl(blk_id
) {
56 pointing_at_return_type
=
57 self.suggest_missing_return_type(err
, &fn_decl
, expected
, found
, can_suggest
);
58 self.suggest_missing_return_expr(err
, expr
, &fn_decl
, expected
, found
);
60 pointing_at_return_type
63 /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
64 /// the ctor would successfully solve the type mismatch and if so, suggest it:
66 /// fn foo(x: usize) -> usize { x }
67 /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
71 err
: &mut DiagnosticBuilder
<'_
>,
76 let hir
= self.tcx
.hir();
77 let (def_id
, sig
) = match *found
.kind() {
78 ty
::FnDef(def_id
, _
) => (def_id
, found
.fn_sig(self.tcx
)),
79 ty
::Closure(def_id
, substs
) => (def_id
, substs
.as_closure().sig()),
83 let sig
= self.replace_bound_vars_with_fresh_vars(expr
.span
, infer
::FnCall
, sig
).0;
84 let sig
= self.normalize_associated_types_in(expr
.span
, sig
);
85 if self.can_coerce(sig
.output(), expected
) {
86 let (mut sugg_call
, applicability
) = if sig
.inputs().is_empty() {
87 (String
::new(), Applicability
::MachineApplicable
)
89 ("...".to_string(), Applicability
::HasPlaceholders
)
91 let mut msg
= "call this function";
92 match hir
.get_if_local(def_id
) {
94 Node
::Item(hir
::Item { kind: ItemKind::Fn(.., body_id), .. }
)
95 | Node
::ImplItem(hir
::ImplItem
{
96 kind
: hir
::ImplItemKind
::Fn(_
, body_id
), ..
98 | Node
::TraitItem(hir
::TraitItem
{
99 kind
: hir
::TraitItemKind
::Fn(.., hir
::TraitFn
::Provided(body_id
)),
103 let body
= hir
.body(*body_id
);
107 .map(|param
| match ¶m
.pat
.kind
{
108 hir
::PatKind
::Binding(_
, _
, ident
, None
)
109 if ident
.name
!= kw
::SelfLower
=>
113 _
=> "_".to_string(),
118 Some(Node
::Expr(hir
::Expr
{
119 kind
: ExprKind
::Closure(_
, _
, body_id
, _
, _
),
120 span
: full_closure_span
,
123 if *full_closure_span
== expr
.span
{
126 msg
= "call this closure";
127 let body
= hir
.body(*body_id
);
131 .map(|param
| match ¶m
.pat
.kind
{
132 hir
::PatKind
::Binding(_
, _
, ident
, None
)
133 if ident
.name
!= kw
::SelfLower
=>
137 _
=> "_".to_string(),
142 Some(Node
::Ctor(hir
::VariantData
::Tuple(fields
, _
))) => {
143 sugg_call
= fields
.iter().map(|_
| "_").collect
::<Vec
<_
>>().join(", ");
144 match def_id
.as_local().map(|def_id
| hir
.def_kind(def_id
)) {
145 Some(DefKind
::Ctor(hir
::def
::CtorOf
::Variant
, _
)) => {
146 msg
= "instantiate this tuple variant";
148 Some(DefKind
::Ctor(CtorOf
::Struct
, _
)) => {
149 msg
= "instantiate this tuple struct";
154 Some(Node
::ForeignItem(hir
::ForeignItem
{
155 kind
: hir
::ForeignItemKind
::Fn(_
, idents
, _
),
161 if ident
.name
!= kw
::SelfLower
{
170 Some(Node
::TraitItem(hir
::TraitItem
{
171 kind
: hir
::TraitItemKind
::Fn(.., hir
::TraitFn
::Required(idents
)),
177 if ident
.name
!= kw
::SelfLower
{
188 err
.span_suggestion_verbose(
189 expr
.span
.shrink_to_hi(),
190 &format
!("use parentheses to {}", msg
),
191 format
!("({})", sugg_call
),
199 pub fn suggest_deref_ref_or_into(
201 err
: &mut DiagnosticBuilder
<'_
>,
202 expr
: &hir
::Expr
<'_
>,
205 expected_ty_expr
: Option
<&'tcx hir
::Expr
<'tcx
>>,
207 if let Some((sp
, msg
, suggestion
, applicability
)) = self.check_ref(expr
, found
, expected
) {
208 err
.span_suggestion(sp
, msg
, suggestion
, applicability
);
209 } else if let (ty
::FnDef(def_id
, ..), true) =
210 (&found
.kind(), self.suggest_fn_call(err
, expr
, expected
, found
))
212 if let Some(sp
) = self.tcx
.hir().span_if_local(*def_id
) {
213 let sp
= self.sess().source_map().guess_head_span(sp
);
214 err
.span_label(sp
, &format
!("{} defined here", found
));
216 } else if !self.check_for_cast(err
, expr
, found
, expected
, expected_ty_expr
) {
217 let is_struct_pat_shorthand_field
=
218 self.is_hir_id_from_struct_pattern_shorthand_field(expr
.hir_id
, expr
.span
);
219 let methods
= self.get_conversion_methods(expr
.span
, expected
, found
, expr
.hir_id
);
220 if let Ok(expr_text
) = self.sess().source_map().span_to_snippet(expr
.span
) {
221 let mut suggestions
= iter
::repeat(&expr_text
)
223 .filter_map(|(receiver
, method
)| {
224 let method_call
= format
!(".{}()", method
.ident
);
225 if receiver
.ends_with(&method_call
) {
226 None
// do not suggest code that is already there (#53348)
228 let method_call_list
= [".to_vec()", ".to_string()"];
229 let sugg
= if receiver
.ends_with(".clone()")
230 && method_call_list
.contains(&method_call
.as_str())
232 let max_len
= receiver
.rfind('
.'
).unwrap();
233 format
!("{}{}", &receiver
[..max_len
], method_call
)
235 if expr
.precedence().order() < ExprPrecedence
::MethodCall
.order() {
236 format
!("({}){}", receiver
, method_call
)
238 format
!("{}{}", receiver
, method_call
)
241 Some(if is_struct_pat_shorthand_field
{
242 format
!("{}: {}", receiver
, sugg
)
249 if suggestions
.peek().is_some() {
250 err
.span_suggestions(
252 "try using a conversion method",
254 Applicability
::MaybeIncorrect
,
261 /// When encountering the expected boxed value allocated in the stack, suggest allocating it
262 /// in the heap by calling `Box::new()`.
263 pub(in super::super) fn suggest_boxing_when_appropriate(
265 err
: &mut DiagnosticBuilder
<'_
>,
266 expr
: &hir
::Expr
<'_
>,
270 if self.tcx
.hir().is_inside_const_context(expr
.hir_id
) {
271 // Do not suggest `Box::new` in const context.
274 if !expected
.is_box() || found
.is_box() {
277 let boxed_found
= self.tcx
.mk_box(found
);
278 if let (true, Ok(snippet
)) = (
279 self.can_coerce(boxed_found
, expected
),
280 self.sess().source_map().span_to_snippet(expr
.span
),
284 "store this in the heap by calling `Box::new`",
285 format
!("Box::new({})", snippet
),
286 Applicability
::MachineApplicable
,
289 "for more on the distinction between the stack and the heap, read \
290 https://doc.rust-lang.org/book/ch15-01-box.html, \
291 https://doc.rust-lang.org/rust-by-example/std/box.html, and \
292 https://doc.rust-lang.org/std/boxed/index.html",
297 /// When encountering a closure that captures variables, where a FnPtr is expected,
298 /// suggest a non-capturing closure
299 pub(in super::super) fn suggest_no_capture_closure(
301 err
: &mut DiagnosticBuilder
<'_
>,
305 if let (ty
::FnPtr(_
), ty
::Closure(def_id
, _
)) = (expected
.kind(), found
.kind()) {
306 if let Some(upvars
) = self.tcx
.upvars_mentioned(*def_id
) {
307 // Report upto four upvars being captured to reduce the amount error messages
308 // reported back to the user.
309 let spans_and_labels
= upvars
312 .map(|(var_hir_id
, upvar
)| {
313 let var_name
= self.tcx
.hir().name(*var_hir_id
).to_string();
314 let msg
= format
!("`{}` captured here", var_name
);
317 .collect
::<Vec
<_
>>();
319 let mut multi_span
: MultiSpan
=
320 spans_and_labels
.iter().map(|(sp
, _
)| *sp
).collect
::<Vec
<_
>>().into();
321 for (sp
, label
) in spans_and_labels
{
322 multi_span
.push_span_label(sp
, label
);
324 err
.span_note(multi_span
, "closures can only be coerced to `fn` types if they do not capture any variables");
329 /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
330 pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
332 err
: &mut DiagnosticBuilder
<'_
>,
333 expr
: &hir
::Expr
<'_
>,
339 if self.tcx
.hir().is_inside_const_context(expr
.hir_id
) {
340 // Do not suggest `Box::new` in const context.
343 let pin_did
= self.tcx
.lang_items().pin_type();
344 match expected
.kind() {
345 ty
::Adt(def
, _
) if Some(def
.did
) != pin_did
=> return false,
346 // This guards the `unwrap` and `mk_box` below.
347 _
if pin_did
.is_none() || self.tcx
.lang_items().owned_box().is_none() => return false,
350 let boxed_found
= self.tcx
.mk_box(found
);
351 let new_found
= self.tcx
.mk_lang_item(boxed_found
, LangItem
::Pin
).unwrap();
352 if let (true, Ok(snippet
)) = (
353 self.can_coerce(new_found
, expected
),
354 self.sess().source_map().span_to_snippet(expr
.span
),
357 ty
::Adt(def
, _
) if def
.is_box() => {
358 err
.help("use `Box::pin`");
363 "you need to pin and box this expression",
364 format
!("Box::pin({})", snippet
),
365 Applicability
::MachineApplicable
,
375 /// A common error is to forget to add a semicolon at the end of a block, e.g.,
379 /// bar_that_returns_u32()
383 /// This routine checks if the return expression in a block would make sense on its own as a
384 /// statement and the return type has been left as default or has been specified as `()`. If so,
385 /// it suggests adding a semicolon.
386 fn suggest_missing_semicolon(
388 err
: &mut DiagnosticBuilder
<'_
>,
389 expression
: &'tcx hir
::Expr
<'tcx
>,
393 if expected
.is_unit() {
394 // `BlockTailExpression` only relevant if the tail expr would be
395 // useful on its own.
396 match expression
.kind
{
398 | ExprKind
::MethodCall(..)
401 | ExprKind
::Match(..)
402 | ExprKind
::Block(..)
403 if expression
.can_have_side_effects() =>
406 cause_span
.shrink_to_hi(),
407 "consider using a semicolon here",
409 Applicability
::MachineApplicable
,
417 /// A possible error is to forget to add a return type that is needed:
421 /// bar_that_returns_u32()
425 /// This routine checks if the return type is left as default, the method is not part of an
426 /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
428 pub(in super::super) fn suggest_missing_return_type(
430 err
: &mut DiagnosticBuilder
<'_
>,
431 fn_decl
: &hir
::FnDecl
<'_
>,
436 // Only suggest changing the return type for methods that
437 // haven't set a return type at all (and aren't `fn main()` or an impl).
438 match (&fn_decl
.output
, found
.is_suggestable(), can_suggest
, expected
.is_unit()) {
439 (&hir
::FnRetTy
::DefaultReturn(span
), true, true, true) => {
442 "try adding a return type",
443 format
!("-> {} ", self.resolve_vars_with_obligations(found
)),
444 Applicability
::MachineApplicable
,
448 (&hir
::FnRetTy
::DefaultReturn(span
), false, true, true) => {
449 err
.span_label(span
, "possibly return type missing here?");
452 (&hir
::FnRetTy
::DefaultReturn(span
), _
, false, true) => {
453 // `fn main()` must return `()`, do not suggest changing return type
454 err
.span_label(span
, "expected `()` because of default return type");
457 // expectation was caused by something else, not the default return
458 (&hir
::FnRetTy
::DefaultReturn(_
), _
, _
, false) => false,
459 (&hir
::FnRetTy
::Return(ref ty
), _
, _
, _
) => {
460 // Only point to return type if the expected type is the return type, as if they
461 // are not, the expectation must have been caused by something else.
462 debug
!("suggest_missing_return_type: return type {:?} node {:?}", ty
, ty
.kind
);
464 let ty
= <dyn AstConv
<'_
>>::ast_ty_to_ty(self, ty
);
465 debug
!("suggest_missing_return_type: return type {:?}", ty
);
466 debug
!("suggest_missing_return_type: expected type {:?}", ty
);
467 if ty
.kind() == expected
.kind() {
468 err
.span_label(sp
, format
!("expected `{}` because of return type", expected
));
476 pub(in super::super) fn suggest_missing_return_expr(
478 err
: &mut DiagnosticBuilder
<'_
>,
479 expr
: &'tcx hir
::Expr
<'tcx
>,
480 fn_decl
: &hir
::FnDecl
<'_
>,
484 if !expected
.is_unit() {
487 let found
= self.resolve_vars_with_obligations(found
);
488 if let hir
::FnRetTy
::Return(ty
) = fn_decl
.output
{
489 let ty
= <dyn AstConv
<'_
>>::ast_ty_to_ty(self, ty
);
490 let ty
= self.tcx
.erase_late_bound_regions(Binder
::bind(ty
));
491 let ty
= self.normalize_associated_types_in(expr
.span
, ty
);
492 if self.can_coerce(found
, ty
) {
493 err
.multipart_suggestion(
494 "you might have meant to return this value",
496 (expr
.span
.shrink_to_lo(), "return ".to_string()),
497 (expr
.span
.shrink_to_hi(), ";".to_string()),
499 Applicability
::MaybeIncorrect
,
505 pub(in super::super) fn suggest_missing_parentheses(
507 err
: &mut DiagnosticBuilder
<'_
>,
508 expr
: &hir
::Expr
<'_
>,
510 let sp
= self.tcx
.sess
.source_map().start_point(expr
.span
);
511 if let Some(sp
) = self.tcx
.sess
.parse_sess
.ambiguous_block_expr_parse
.borrow().get(&sp
) {
512 // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
513 self.tcx
.sess
.parse_sess
.expr_parentheses_needed(err
, *sp
, None
);