1
1
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 } ;
3
3
use crate :: hir:: intravisit:: { self , Visitor , NestedVisitorMap } ;
4
4
use crate :: infer:: InferCtxt ;
5
5
use crate :: infer:: type_variable:: TypeVariableOriginKind ;
6
6
use crate :: ty:: { self , Ty , Infer , TyVar } ;
7
7
use crate :: ty:: print:: Print ;
8
8
use syntax:: source_map:: DesugaringKind ;
9
9
use syntax_pos:: Span ;
10
- use errors:: DiagnosticBuilder ;
10
+ use errors:: { Applicability , DiagnosticBuilder } ;
11
11
12
12
struct FindLocalByTypeVisitor < ' a , ' tcx > {
13
13
infcx : & ' a InferCtxt < ' a , ' tcx > ,
@@ -16,9 +16,26 @@ struct FindLocalByTypeVisitor<'a, 'tcx> {
16
16
found_local_pattern : Option < & ' tcx Pat > ,
17
17
found_arg_pattern : Option < & ' tcx Pat > ,
18
18
found_ty : Option < Ty < ' tcx > > ,
19
+ found_closure : Option < & ' tcx ExprKind > ,
19
20
}
20
21
21
22
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
+
22
39
fn node_matches_type ( & mut self , hir_id : HirId ) -> Option < Ty < ' tcx > > {
23
40
let ty_opt = self . infcx . in_progress_tables . and_then ( |tables| {
24
41
tables. borrow ( ) . node_type_opt ( hir_id)
@@ -72,6 +89,16 @@ impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> {
72
89
}
73
90
intravisit:: walk_body ( self , body) ;
74
91
}
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
+ }
75
102
}
76
103
77
104
impl < ' a , ' tcx > InferCtxt < ' a , ' tcx > {
@@ -106,16 +133,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
106
133
let ty = self . resolve_vars_if_possible ( & ty) ;
107
134
let name = self . extract_type_name ( & ty, None ) ;
108
135
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 ( ) ) ;
119
137
let ty_to_string = |ty : Ty < ' tcx > | -> String {
120
138
let mut s = String :: new ( ) ;
121
139
let mut printer = ty:: print:: FmtPrinter :: new ( self . tcx , & mut s, Namespace :: TypeNS ) ;
@@ -136,6 +154,35 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
136
154
let expr = self . tcx . hir ( ) . expect_expr ( body_id. hir_id ) ;
137
155
local_visitor. visit_expr ( expr) ;
138
156
}
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
+ } ;
139
186
140
187
// When `name` corresponds to a type argument, show the path of the full type we're
141
188
// trying to infer. In the following example, `ty_msg` contains
@@ -150,34 +197,47 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
150
197
// | consider giving `b` the explicit type `std::result::Result<i32, E>`, where
151
198
// | the type parameter `E` is specified
152
199
// ```
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 {
178
209
Some ( ty:: TyS { sty : ty:: Closure ( def_id, substs) , .. } ) => {
179
- let msg = " for the closure" . to_string ( ) ;
180
210
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.
181
241
let args = fn_sig. inputs ( )
182
242
. skip_binder ( )
183
243
. iter ( )
@@ -189,18 +249,32 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
189
249
// This suggestion is incomplete, as the user will get further type inference
190
250
// errors due to the `_` placeholders and the introduction of `Box`, but it does
191
251
// 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)
197
261
}
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 ( ) ,
199
275
} ;
200
- let mut labels = vec ! [ ( span, InferCtxt :: missing_type_msg( & name) ) ] ;
201
276
202
277
if let Some ( pattern) = local_visitor. found_arg_pattern {
203
- err_span = pattern. span ;
204
278
// We don't want to show the default label for closures.
205
279
//
206
280
// So, before clearing, the output would look something like this:
@@ -217,39 +291,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
217
291
// ^ consider giving this closure parameter the type `[_; 0]`
218
292
// with the type parameter `_` specified
219
293
// ```
220
- labels. clear ( ) ;
221
- labels. push ( (
294
+ err. span_label (
222
295
pattern. span ,
223
296
format ! ( "consider giving this closure parameter {}" , suffix) ,
224
- ) ) ;
297
+ ) ;
225
298
} else if let Some ( pattern) = local_visitor. found_local_pattern {
226
299
if let Some ( simple_ident) = pattern. simple_ident ( ) {
227
300
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
+ }
236
313
_ => { }
237
314
}
238
315
} else {
239
- labels . push ( ( pattern. span , format ! ( "consider giving this pattern {}" , suffix) ) ) ;
316
+ err . span_label ( pattern. span , format ! ( "consider giving this pattern {}" , suffix) ) ;
240
317
}
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) ) ;
253
324
}
254
325
255
326
err
0 commit comments