Skip to content

Commit 806476c

Browse files
committed
Suggest giving return type to closures on E0282
1 parent 939c1cb commit 806476c

13 files changed

+199
-90
lines changed

src/librustc/hir/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1541,7 +1541,7 @@ pub enum ExprKind {
15411541
Match(P<Expr>, HirVec<Arm>, MatchSource),
15421542
/// A closure (e.g., `move |a, b, c| {a + b + c}`).
15431543
///
1544-
/// The final span is the span of the argument block `|...|`.
1544+
/// The `Span` is the argument block `|...|`.
15451545
///
15461546
/// This may also be a generator literal or an `async block` as indicated by the
15471547
/// `Option<GeneratorMovability>`.

src/librustc/infer/error_reporting/need_type_info.rs

+141-70
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use crate::hir::def::Namespace;
2-
use crate::hir::{self, Local, Pat, Body, HirId};
2+
use crate::hir::{self, Body, FunctionRetTy, Expr, ExprKind, HirId, Local, Pat};
33
use crate::hir::intravisit::{self, Visitor, NestedVisitorMap};
44
use crate::infer::InferCtxt;
55
use crate::infer::type_variable::TypeVariableOriginKind;
66
use crate::ty::{self, Ty, Infer, TyVar};
77
use crate::ty::print::Print;
88
use syntax::source_map::DesugaringKind;
99
use syntax_pos::Span;
10-
use errors::DiagnosticBuilder;
10+
use errors::{Applicability, DiagnosticBuilder};
1111

1212
struct FindLocalByTypeVisitor<'a, 'tcx> {
1313
infcx: &'a InferCtxt<'a, 'tcx>,
@@ -16,9 +16,26 @@ struct FindLocalByTypeVisitor<'a, 'tcx> {
1616
found_local_pattern: Option<&'tcx Pat>,
1717
found_arg_pattern: Option<&'tcx Pat>,
1818
found_ty: Option<Ty<'tcx>>,
19+
found_closure: Option<&'tcx ExprKind>,
1920
}
2021

2122
impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> {
23+
fn new(
24+
infcx: &'a InferCtxt<'a, 'tcx>,
25+
target_ty: Ty<'tcx>,
26+
hir_map: &'a hir::map::Map<'tcx>,
27+
) -> FindLocalByTypeVisitor<'a, 'tcx> {
28+
FindLocalByTypeVisitor {
29+
infcx,
30+
target_ty,
31+
hir_map,
32+
found_local_pattern: None,
33+
found_arg_pattern: None,
34+
found_ty: None,
35+
found_closure: None,
36+
}
37+
}
38+
2239
fn node_matches_type(&mut self, hir_id: HirId) -> Option<Ty<'tcx>> {
2340
let ty_opt = self.infcx.in_progress_tables.and_then(|tables| {
2441
tables.borrow().node_type_opt(hir_id)
@@ -72,6 +89,16 @@ impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> {
7289
}
7390
intravisit::walk_body(self, body);
7491
}
92+
93+
fn visit_expr(&mut self, expr: &'tcx Expr) {
94+
if let (ExprKind::Closure(_, _fn_decl, _id, _sp, _), Some(_)) = (
95+
&expr.node,
96+
self.node_matches_type(expr.hir_id),
97+
) {
98+
self.found_closure = Some(&expr.node);
99+
}
100+
intravisit::walk_expr(self, expr);
101+
}
75102
}
76103

77104
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
@@ -106,16 +133,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
106133
let ty = self.resolve_vars_if_possible(&ty);
107134
let name = self.extract_type_name(&ty, None);
108135

109-
let mut err_span = span;
110-
111-
let mut local_visitor = FindLocalByTypeVisitor {
112-
infcx: &self,
113-
target_ty: ty,
114-
hir_map: &self.tcx.hir(),
115-
found_local_pattern: None,
116-
found_arg_pattern: None,
117-
found_ty: None,
118-
};
136+
let mut local_visitor = FindLocalByTypeVisitor::new(&self, ty, &self.tcx.hir());
119137
let ty_to_string = |ty: Ty<'tcx>| -> String {
120138
let mut s = String::new();
121139
let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS);
@@ -136,6 +154,35 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
136154
let expr = self.tcx.hir().expect_expr(body_id.hir_id);
137155
local_visitor.visit_expr(expr);
138156
}
157+
let err_span = if let Some(pattern) = local_visitor.found_arg_pattern {
158+
pattern.span
159+
} else {
160+
span
161+
};
162+
163+
let ty_msg = match local_visitor.found_ty {
164+
Some(ty::TyS { sty: ty::Closure(def_id, substs), .. }) => {
165+
let fn_sig = substs.closure_sig(*def_id, self.tcx);
166+
let args = fn_sig.inputs()
167+
.skip_binder()
168+
.iter()
169+
.next()
170+
.map(|args| args.tuple_fields()
171+
.map(|arg| arg.to_string())
172+
.collect::<Vec<_>>().join(", "))
173+
.unwrap_or_default();
174+
let ret = fn_sig.output().skip_binder().to_string();
175+
format!(" for the closure `fn({}) -> {}`", args, ret)
176+
}
177+
Some(ty) if &ty.to_string() != "_" &&
178+
// FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527
179+
(!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) =>
180+
{
181+
let ty = ty_to_string(ty);
182+
format!(" for `{}`", ty)
183+
}
184+
_ => String::new(),
185+
};
139186

140187
// When `name` corresponds to a type argument, show the path of the full type we're
141188
// trying to infer. In the following example, `ty_msg` contains
@@ -150,34 +197,47 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
150197
// | consider giving `b` the explicit type `std::result::Result<i32, E>`, where
151198
// | the type parameter `E` is specified
152199
// ```
153-
let (ty_msg, suffix) = match &local_visitor.found_ty {
154-
Some(ty) if &ty.to_string() != "_" &&
155-
name == "_" &&
156-
// FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527
157-
(!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) &&
158-
!ty.is_closure() => // The suggestion doesn't make sense for closures.
159-
{
160-
let ty = ty_to_string(ty);
161-
(format!(" for `{}`", ty),
162-
format!("the explicit type `{}`, with the type parameters specified", ty))
163-
}
164-
Some(ty) if &ty.to_string() != "_" &&
165-
ty.to_string() != name &&
166-
// FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527
167-
(!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) &&
168-
!ty.is_closure() => // The suggestion doesn't make sense for closures.
169-
{
170-
let ty = ty_to_string(ty);
171-
(format!(" for `{}`", ty),
172-
format!(
173-
"the explicit type `{}`, where the type parameter `{}` is specified",
174-
ty,
175-
name,
176-
))
177-
}
200+
let mut err = struct_span_err!(
201+
self.tcx.sess,
202+
err_span,
203+
E0282,
204+
"type annotations needed{}",
205+
ty_msg,
206+
);
207+
208+
let suffix = match local_visitor.found_ty {
178209
Some(ty::TyS { sty: ty::Closure(def_id, substs), .. }) => {
179-
let msg = " for the closure".to_string();
180210
let fn_sig = substs.closure_sig(*def_id, self.tcx);
211+
let ret = fn_sig.output().skip_binder().to_string();
212+
213+
if let Some(ExprKind::Closure(_, decl, body_id, ..)) = local_visitor.found_closure {
214+
let (arrow, post) = match decl.output {
215+
FunctionRetTy::DefaultReturn(_) => ("-> ", " "),
216+
_ => ("", ""),
217+
};
218+
if let Some(body) = self.tcx.hir().krate().bodies.get(body_id) {
219+
let suggestion = match body.value.node {
220+
ExprKind::Block(..) => {
221+
vec![(decl.output.span(), format!("{}{}{}", arrow, ret, post))]
222+
}
223+
_ => {
224+
vec![
225+
(decl.output.span(), format!("{}{}{}{{ ", arrow, ret, post)),
226+
(body.value.span.shrink_to_hi(), " }".to_string()),
227+
]
228+
}
229+
};
230+
err.multipart_suggestion(
231+
"give this closure an explicit return type without `_` placeholders",
232+
suggestion,
233+
Applicability::HasPlaceholders,
234+
);
235+
err.span_label(span, InferCtxt::missing_type_msg(&name));
236+
return err;
237+
}
238+
}
239+
240+
// This shouldn't be reachable, but just in case we leave a reasonable fallback.
181241
let args = fn_sig.inputs()
182242
.skip_binder()
183243
.iter()
@@ -189,18 +249,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
189249
// This suggestion is incomplete, as the user will get further type inference
190250
// errors due to the `_` placeholders and the introduction of `Box`, but it does
191251
// nudge them in the right direction.
192-
(msg, format!(
193-
"a boxed closure type like `Box<dyn Fn({}) -> {}>`",
194-
args,
195-
fn_sig.output().skip_binder().to_string(),
196-
))
252+
format!("a boxed closure type like `Box<dyn Fn({}) -> {}>`", args, ret)
253+
}
254+
Some(ty) if &ty.to_string() != "_" &&
255+
name == "_" &&
256+
// FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527
257+
(!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) =>
258+
{
259+
let ty = ty_to_string(ty);
260+
format!("the explicit type `{}`, with the type parameters specified", ty)
197261
}
198-
_ => (String::new(), "a type".to_owned()),
262+
Some(ty) if &ty.to_string() != "_" &&
263+
ty.to_string() != name &&
264+
// FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527
265+
(!ty.is_impl_trait() || self.tcx.features().impl_trait_in_bindings) =>
266+
{
267+
let ty = ty_to_string(ty);
268+
format!(
269+
"the explicit type `{}`, where the type parameter `{}` is specified",
270+
ty,
271+
name,
272+
)
273+
}
274+
_ => "a type".to_string(),
199275
};
200-
let mut labels = vec![(span, InferCtxt::missing_type_msg(&name))];
201276

202277
if let Some(pattern) = local_visitor.found_arg_pattern {
203-
err_span = pattern.span;
204278
// We don't want to show the default label for closures.
205279
//
206280
// So, before clearing, the output would look something like this:
@@ -217,39 +291,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
217291
// ^ consider giving this closure parameter the type `[_; 0]`
218292
// with the type parameter `_` specified
219293
// ```
220-
labels.clear();
221-
labels.push((
294+
err.span_label(
222295
pattern.span,
223296
format!("consider giving this closure parameter {}", suffix),
224-
));
297+
);
225298
} else if let Some(pattern) = local_visitor.found_local_pattern {
226299
if let Some(simple_ident) = pattern.simple_ident() {
227300
match pattern.span.desugaring_kind() {
228-
None => labels.push((
229-
pattern.span,
230-
format!("consider giving `{}` {}", simple_ident, suffix),
231-
)),
232-
Some(DesugaringKind::ForLoop) => labels.push((
233-
pattern.span,
234-
"the element type for this iterator is not specified".to_owned(),
235-
)),
301+
None => {
302+
err.span_label(
303+
pattern.span,
304+
format!("consider giving `{}` {}", simple_ident, suffix),
305+
);
306+
}
307+
Some(DesugaringKind::ForLoop) => {
308+
err.span_label(
309+
pattern.span,
310+
"the element type for this iterator is not specified".to_string(),
311+
);
312+
}
236313
_ => {}
237314
}
238315
} else {
239-
labels.push((pattern.span, format!("consider giving this pattern {}", suffix)));
316+
err.span_label(pattern.span, format!("consider giving this pattern {}", suffix));
240317
}
241-
};
242-
243-
let mut err = struct_span_err!(
244-
self.tcx.sess,
245-
err_span,
246-
E0282,
247-
"type annotations needed{}",
248-
ty_msg,
249-
);
250-
251-
for (target_span, label_message) in labels {
252-
err.span_label(target_span, label_message);
318+
}
319+
if !err.span.span_labels().iter().any(|span_label| {
320+
span_label.label.is_some() && span_label.span == span
321+
}) && local_visitor.found_arg_pattern.is_none()
322+
{ // Avoid multiple labels pointing at `span`.
323+
err.span_label(span, InferCtxt::missing_type_msg(&name));
253324
}
254325

255326
err

src/test/ui/error-codes/E0282.stderr

+1-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ error[E0282]: type annotations needed
22
--> $DIR/E0282.rs:2:9
33
|
44
LL | let x = "hello".chars().rev().collect();
5-
| ^
6-
| |
7-
| cannot infer type
8-
| consider giving `x` a type
5+
| ^ consider giving `x` a type
96

107
error: aborting due to previous error
118

src/test/ui/for/for-loop-unconstrained-element-type.stderr

+1-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ error[E0282]: type annotations needed
22
--> $DIR/for-loop-unconstrained-element-type.rs:8:14
33
|
44
LL | for i in Vec::new() { }
5-
| ^^^^^^^^^^
6-
| |
7-
| cannot infer type
8-
| the element type for this iterator is not specified
5+
| ^^^^^^^^^^ the element type for this iterator is not specified
96

107
error: aborting due to previous error
118

src/test/ui/inference/cannot-infer-closure.stderr

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
error[E0282]: type annotations needed for the closure
1+
error[E0282]: type annotations needed for the closure `fn((), ()) -> std::result::Result<(), _>`
22
--> $DIR/cannot-infer-closure.rs:3:9
33
|
4-
LL | let x = |a: (), b: ()| {
5-
| - consider giving `x` a boxed closure type like `Box<dyn Fn((), ()) -> std::result::Result<(), _>>`
64
LL | Err(a)?;
75
| ^^^^^^^ cannot infer type
6+
help: give this closure an explicit return type without `_` placeholders
7+
|
8+
LL | let x = |a: (), b: ()| -> std::result::Result<(), _> {
9+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
810

911
error: aborting due to previous error
1012

src/test/ui/issues/issue-18159.stderr

+1-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ error[E0282]: type annotations needed
22
--> $DIR/issue-18159.rs:2:9
33
|
44
LL | let x;
5-
| ^
6-
| |
7-
| cannot infer type
8-
| consider giving `x` a type
5+
| ^ consider giving `x` a type
96

107
error: aborting due to previous error
118

src/test/ui/match/match-unresolved-one-arm.stderr

+1-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ error[E0282]: type annotations needed
22
--> $DIR/match-unresolved-one-arm.rs:4:9
33
|
44
LL | let x = match () {
5-
| ^
6-
| |
7-
| cannot infer type
8-
| consider giving `x` a type
5+
| ^ consider giving `x` a type
96

107
error: aborting due to previous error
118

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
let _v = || -> _ { [] }; //~ ERROR type annotations needed for the closure
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0282]: type annotations needed for the closure `fn() -> [_; 0]`
2+
--> $DIR/suggest-closure-return-type-1.rs:2:24
3+
|
4+
LL | let _v = || -> _ { [] };
5+
| ^^ cannot infer type
6+
help: give this closure an explicit return type without `_` placeholders
7+
|
8+
LL | let _v = || -> [_; 0] { [] };
9+
| ^^^^^^
10+
11+
error: aborting due to previous error
12+
13+
For more information about this error, try `rustc --explain E0282`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
let _v = || { [] }; //~ ERROR type annotations needed for the closure
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0282]: type annotations needed for the closure `fn() -> [_; 0]`
2+
--> $DIR/suggest-closure-return-type-2.rs:2:19
3+
|
4+
LL | let _v = || { [] };
5+
| ^^ cannot infer type
6+
help: give this closure an explicit return type without `_` placeholders
7+
|
8+
LL | let _v = || -> [_; 0] { [] };
9+
| ^^^^^^^^^
10+
11+
error: aborting due to previous error
12+
13+
For more information about this error, try `rustc --explain E0282`.

0 commit comments

Comments
 (0)