]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
Merge tag 'debian/1.52.1+dfsg1-1_exp2' into proxmox/buster
[rustc.git] / compiler / rustc_typeck / src / check / fn_ctxt / suggestions.rs
CommitLineData
29967ef6
XL
1use super::FnCtxt;
2use crate::astconv::AstConv;
3
4use rustc_ast::util::parser::ExprPrecedence;
5869c6ff 5use rustc_span::{self, MultiSpan, Span};
29967ef6
XL
6
7use rustc_errors::{Applicability, DiagnosticBuilder};
8use rustc_hir as hir;
9use rustc_hir::def::{CtorOf, DefKind};
10use rustc_hir::lang_items::LangItem;
11use rustc_hir::{ExprKind, ItemKind, Node};
12use rustc_infer::infer;
6a06907d
XL
13use rustc_middle::lint::in_external_macro;
14use rustc_middle::ty::{self, Binder, Ty};
29967ef6
XL
15use rustc_span::symbol::kw;
16
17use std::iter;
18
19impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20 pub(in super::super) fn suggest_semicolon_at_end(
21 &self,
22 span: Span,
23 err: &mut DiagnosticBuilder<'_>,
24 ) {
25 err.span_suggestion_short(
26 span.shrink_to_hi(),
27 "consider using a semicolon here",
28 ";".to_string(),
29 Applicability::MachineApplicable,
30 );
31 }
32
33 /// On implicit return expressions with mismatched types, provides the following suggestions:
34 ///
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(
39 &self,
40 err: &mut DiagnosticBuilder<'_>,
41 expr: &'tcx hir::Expr<'tcx>,
42 expected: Ty<'tcx>,
43 found: Ty<'tcx>,
44 cause_span: Span,
45 blk_id: hir::HirId,
46 ) -> bool {
47 let expr = expr.peel_drop_temps();
6a06907d
XL
48 // If the expression is from an external macro, then do not suggest
49 // adding a semicolon, because there's nowhere to put it.
50 // See issue #81943.
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);
53 }
29967ef6
XL
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);
6a06907d 58 self.suggest_missing_return_expr(err, expr, &fn_decl, expected, found);
29967ef6
XL
59 }
60 pointing_at_return_type
61 }
62
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:
65 /// ```
66 /// fn foo(x: usize) -> usize { x }
67 /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
68 /// ```
69 fn suggest_fn_call(
70 &self,
71 err: &mut DiagnosticBuilder<'_>,
72 expr: &hir::Expr<'_>,
73 expected: Ty<'tcx>,
74 found: Ty<'tcx>,
75 ) -> bool {
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()),
80 _ => return false,
81 };
82
fc512014
XL
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);
29967ef6
XL
85 if self.can_coerce(sig.output(), expected) {
86 let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
87 (String::new(), Applicability::MachineApplicable)
88 } else {
89 ("...".to_string(), Applicability::HasPlaceholders)
90 };
91 let mut msg = "call this function";
92 match hir.get_if_local(def_id) {
93 Some(
94 Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })
95 | Node::ImplItem(hir::ImplItem {
96 kind: hir::ImplItemKind::Fn(_, body_id), ..
97 })
98 | Node::TraitItem(hir::TraitItem {
99 kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)),
100 ..
101 }),
102 ) => {
103 let body = hir.body(*body_id);
104 sugg_call = body
105 .params
106 .iter()
107 .map(|param| match &param.pat.kind {
108 hir::PatKind::Binding(_, _, ident, None)
109 if ident.name != kw::SelfLower =>
110 {
111 ident.to_string()
112 }
113 _ => "_".to_string(),
114 })
115 .collect::<Vec<_>>()
116 .join(", ");
117 }
118 Some(Node::Expr(hir::Expr {
119 kind: ExprKind::Closure(_, _, body_id, _, _),
120 span: full_closure_span,
121 ..
122 })) => {
123 if *full_closure_span == expr.span {
124 return false;
125 }
126 msg = "call this closure";
127 let body = hir.body(*body_id);
128 sugg_call = body
129 .params
130 .iter()
131 .map(|param| match &param.pat.kind {
132 hir::PatKind::Binding(_, _, ident, None)
133 if ident.name != kw::SelfLower =>
134 {
135 ident.to_string()
136 }
137 _ => "_".to_string(),
138 })
139 .collect::<Vec<_>>()
140 .join(", ");
141 }
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";
147 }
148 Some(DefKind::Ctor(CtorOf::Struct, _)) => {
149 msg = "instantiate this tuple struct";
150 }
151 _ => {}
152 }
153 }
154 Some(Node::ForeignItem(hir::ForeignItem {
155 kind: hir::ForeignItemKind::Fn(_, idents, _),
156 ..
157 })) => {
158 sugg_call = idents
159 .iter()
160 .map(|ident| {
161 if ident.name != kw::SelfLower {
162 ident.to_string()
163 } else {
164 "_".to_string()
165 }
166 })
167 .collect::<Vec<_>>()
168 .join(", ")
169 }
170 Some(Node::TraitItem(hir::TraitItem {
171 kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)),
172 ..
173 })) => {
174 sugg_call = idents
175 .iter()
176 .map(|ident| {
177 if ident.name != kw::SelfLower {
178 ident.to_string()
179 } else {
180 "_".to_string()
181 }
182 })
183 .collect::<Vec<_>>()
184 .join(", ")
185 }
186 _ => {}
187 }
188 err.span_suggestion_verbose(
189 expr.span.shrink_to_hi(),
190 &format!("use parentheses to {}", msg),
191 format!("({})", sugg_call),
192 applicability,
193 );
194 return true;
195 }
196 false
197 }
198
199 pub fn suggest_deref_ref_or_into(
200 &self,
201 err: &mut DiagnosticBuilder<'_>,
202 expr: &hir::Expr<'_>,
203 expected: Ty<'tcx>,
204 found: Ty<'tcx>,
205 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
206 ) {
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))
211 {
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));
215 }
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)
222 .zip(methods.iter())
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)
227 } else {
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())
231 {
232 let max_len = receiver.rfind('.').unwrap();
233 format!("{}{}", &receiver[..max_len], method_call)
234 } else {
235 if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
236 format!("({}){}", receiver, method_call)
237 } else {
238 format!("{}{}", receiver, method_call)
239 }
240 };
241 Some(if is_struct_pat_shorthand_field {
242 format!("{}: {}", receiver, sugg)
243 } else {
244 sugg
245 })
246 }
247 })
248 .peekable();
249 if suggestions.peek().is_some() {
250 err.span_suggestions(
251 expr.span,
252 "try using a conversion method",
253 suggestions,
254 Applicability::MaybeIncorrect,
255 );
256 }
257 }
258 }
259 }
260
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(
264 &self,
265 err: &mut DiagnosticBuilder<'_>,
266 expr: &hir::Expr<'_>,
267 expected: Ty<'tcx>,
268 found: Ty<'tcx>,
269 ) {
270 if self.tcx.hir().is_inside_const_context(expr.hir_id) {
271 // Do not suggest `Box::new` in const context.
272 return;
273 }
274 if !expected.is_box() || found.is_box() {
275 return;
276 }
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),
281 ) {
282 err.span_suggestion(
283 expr.span,
284 "store this in the heap by calling `Box::new`",
285 format!("Box::new({})", snippet),
286 Applicability::MachineApplicable,
287 );
288 err.note(
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",
293 );
294 }
295 }
296
5869c6ff
XL
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(
300 &self,
301 err: &mut DiagnosticBuilder<'_>,
302 expected: Ty<'tcx>,
303 found: Ty<'tcx>,
304 ) {
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
310 .iter()
311 .take(4)
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);
315 (upvar.span, msg)
316 })
317 .collect::<Vec<_>>();
318
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);
323 }
324 err.span_note(multi_span, "closures can only be coerced to `fn` types if they do not capture any variables");
325 }
326 }
327 }
328
29967ef6
XL
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(
331 &self,
332 err: &mut DiagnosticBuilder<'_>,
333 expr: &hir::Expr<'_>,
334 expected: Ty<'tcx>,
335 found: Ty<'tcx>,
336 ) -> bool {
337 // Handle #68197.
338
339 if self.tcx.hir().is_inside_const_context(expr.hir_id) {
340 // Do not suggest `Box::new` in const context.
341 return false;
342 }
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,
348 _ => {}
349 }
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),
355 ) {
356 match found.kind() {
357 ty::Adt(def, _) if def.is_box() => {
358 err.help("use `Box::pin`");
359 }
360 _ => {
361 err.span_suggestion(
362 expr.span,
363 "you need to pin and box this expression",
364 format!("Box::pin({})", snippet),
365 Applicability::MachineApplicable,
366 );
367 }
368 }
369 true
370 } else {
371 false
372 }
373 }
374
375 /// A common error is to forget to add a semicolon at the end of a block, e.g.,
376 ///
377 /// ```
378 /// fn foo() {
379 /// bar_that_returns_u32()
380 /// }
381 /// ```
382 ///
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(
387 &self,
388 err: &mut DiagnosticBuilder<'_>,
389 expression: &'tcx hir::Expr<'tcx>,
390 expected: Ty<'tcx>,
391 cause_span: Span,
392 ) {
393 if expected.is_unit() {
394 // `BlockTailExpression` only relevant if the tail expr would be
395 // useful on its own.
396 match expression.kind {
397 ExprKind::Call(..)
398 | ExprKind::MethodCall(..)
399 | ExprKind::Loop(..)
5869c6ff 400 | ExprKind::If(..)
29967ef6 401 | ExprKind::Match(..)
6a06907d
XL
402 | ExprKind::Block(..)
403 if expression.can_have_side_effects() =>
404 {
29967ef6
XL
405 err.span_suggestion(
406 cause_span.shrink_to_hi(),
6a06907d 407 "consider using a semicolon here",
29967ef6
XL
408 ";".to_string(),
409 Applicability::MachineApplicable,
410 );
411 }
412 _ => (),
413 }
414 }
415 }
416
417 /// A possible error is to forget to add a return type that is needed:
418 ///
419 /// ```
420 /// fn foo() {
421 /// bar_that_returns_u32()
422 /// }
423 /// ```
424 ///
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
427 /// type.
428 pub(in super::super) fn suggest_missing_return_type(
429 &self,
430 err: &mut DiagnosticBuilder<'_>,
431 fn_decl: &hir::FnDecl<'_>,
432 expected: Ty<'tcx>,
433 found: Ty<'tcx>,
434 can_suggest: bool,
435 ) -> bool {
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) => {
440 err.span_suggestion(
441 span,
442 "try adding a return type",
443 format!("-> {} ", self.resolve_vars_with_obligations(found)),
444 Applicability::MachineApplicable,
445 );
446 true
447 }
448 (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
449 err.span_label(span, "possibly return type missing here?");
450 true
451 }
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");
455 true
456 }
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);
463 let sp = ty.span;
6a06907d 464 let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
29967ef6
XL
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));
469 return true;
470 }
471 false
472 }
473 }
474 }
475
6a06907d
XL
476 pub(in super::super) fn suggest_missing_return_expr(
477 &self,
478 err: &mut DiagnosticBuilder<'_>,
479 expr: &'tcx hir::Expr<'tcx>,
480 fn_decl: &hir::FnDecl<'_>,
481 expected: Ty<'tcx>,
482 found: Ty<'tcx>,
483 ) {
484 if !expected.is_unit() {
485 return;
486 }
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",
495 vec![
496 (expr.span.shrink_to_lo(), "return ".to_string()),
497 (expr.span.shrink_to_hi(), ";".to_string()),
498 ],
499 Applicability::MaybeIncorrect,
500 );
501 }
502 }
503 }
504
29967ef6
XL
505 pub(in super::super) fn suggest_missing_parentheses(
506 &self,
507 err: &mut DiagnosticBuilder<'_>,
508 expr: &hir::Expr<'_>,
509 ) {
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);
514 }
515 }
516}