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
+ ) -> Self {
28
+ Self {
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,60 @@ 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
+ }
102
+ }
103
+
104
+ /// Suggest giving an appropriate return type to a closure expression.
105
+ fn closure_return_type_suggestion (
106
+ span : Span ,
107
+ err : & mut DiagnosticBuilder < ' _ > ,
108
+ output : & FunctionRetTy ,
109
+ body : & Body ,
110
+ name : & str ,
111
+ ret : & str ,
112
+ ) {
113
+ let ( arrow, post) = match output {
114
+ FunctionRetTy :: DefaultReturn ( _) => ( "-> " , " " ) ,
115
+ _ => ( "" , "" ) ,
116
+ } ;
117
+ let suggestion = match body. value . node {
118
+ ExprKind :: Block ( ..) => {
119
+ vec ! [ ( output. span( ) , format!( "{}{}{}" , arrow, ret, post) ) ]
120
+ }
121
+ _ => {
122
+ vec ! [
123
+ ( output. span( ) , format!( "{}{}{}{{ " , arrow, ret, post) ) ,
124
+ ( body. value. span. shrink_to_hi( ) , " }" . to_string( ) ) ,
125
+ ]
126
+ }
127
+ } ;
128
+ err. multipart_suggestion (
129
+ "give this closure an explicit return type without `_` placeholders" ,
130
+ suggestion,
131
+ Applicability :: HasPlaceholders ,
132
+ ) ;
133
+ err. span_label ( span, InferCtxt :: missing_type_msg ( & name) ) ;
134
+ }
135
+
136
+ /// Given a closure signature, return a `String` containing a list of all its argument types.
137
+ fn closure_args ( fn_sig : & ty:: PolyFnSig < ' _ > ) -> String {
138
+ fn_sig. inputs ( )
139
+ . skip_binder ( )
140
+ . iter ( )
141
+ . next ( )
142
+ . map ( |args| args. tuple_fields ( )
143
+ . map ( |arg| arg. to_string ( ) )
144
+ . collect :: < Vec < _ > > ( ) . join ( ", " ) )
145
+ . unwrap_or_default ( )
75
146
}
76
147
77
148
impl < ' a , ' tcx > InferCtxt < ' a , ' tcx > {
@@ -106,16 +177,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
106
177
let ty = self . resolve_vars_if_possible ( & ty) ;
107
178
let name = self . extract_type_name ( & ty, None ) ;
108
179
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
- } ;
180
+ let mut local_visitor = FindLocalByTypeVisitor :: new ( & self , ty, & self . tcx . hir ( ) ) ;
119
181
let ty_to_string = |ty : Ty < ' tcx > | -> String {
120
182
let mut s = String :: new ( ) ;
121
183
let mut printer = ty:: print:: FmtPrinter :: new ( self . tcx , & mut s, Namespace :: TypeNS ) ;
@@ -136,6 +198,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
136
198
let expr = self . tcx . hir ( ) . expect_expr ( body_id. hir_id ) ;
137
199
local_visitor. visit_expr ( expr) ;
138
200
}
201
+ let err_span = if let Some ( pattern) = local_visitor. found_arg_pattern {
202
+ pattern. span
203
+ } else {
204
+ span
205
+ } ;
206
+
207
+ let is_named_and_not_impl_trait = |ty : Ty < ' _ > | {
208
+ & ty. to_string ( ) != "_" &&
209
+ // FIXME: Remove this check after `impl_trait_in_bindings` is stabilized. #63527
210
+ ( !ty. is_impl_trait ( ) || self . tcx . features ( ) . impl_trait_in_bindings )
211
+ } ;
212
+
213
+ let ty_msg = match local_visitor. found_ty {
214
+ Some ( ty:: TyS { sty : ty:: Closure ( def_id, substs) , .. } ) => {
215
+ let fn_sig = substs. closure_sig ( * def_id, self . tcx ) ;
216
+ let args = closure_args ( & fn_sig) ;
217
+ let ret = fn_sig. output ( ) . skip_binder ( ) . to_string ( ) ;
218
+ format ! ( " for the closure `fn({}) -> {}`" , args, ret)
219
+ }
220
+ Some ( ty) if is_named_and_not_impl_trait ( ty) => {
221
+ let ty = ty_to_string ( ty) ;
222
+ format ! ( " for `{}`" , ty)
223
+ }
224
+ _ => String :: new ( ) ,
225
+ } ;
139
226
140
227
// When `name` corresponds to a type argument, show the path of the full type we're
141
228
// trying to infer. In the following example, `ty_msg` contains
@@ -150,27 +237,58 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
150
237
// | consider giving `b` the explicit type `std::result::Result<i32, E>`, where
151
238
// | the type parameter `E` is specified
152
239
// ```
153
- let ( ty_msg, suffix) = match & local_visitor. found_ty {
154
- Some ( ty) if & ty. to_string ( ) != "_" && name == "_" => {
240
+ let mut err = struct_span_err ! (
241
+ self . tcx. sess,
242
+ err_span,
243
+ E0282 ,
244
+ "type annotations needed{}" ,
245
+ ty_msg,
246
+ ) ;
247
+
248
+ let suffix = match local_visitor. found_ty {
249
+ Some ( ty:: TyS { sty : ty:: Closure ( def_id, substs) , .. } ) => {
250
+ let fn_sig = substs. closure_sig ( * def_id, self . tcx ) ;
251
+ let ret = fn_sig. output ( ) . skip_binder ( ) . to_string ( ) ;
252
+
253
+ if let Some ( ExprKind :: Closure ( _, decl, body_id, ..) ) = local_visitor. found_closure {
254
+ if let Some ( body) = self . tcx . hir ( ) . krate ( ) . bodies . get ( body_id) {
255
+ closure_return_type_suggestion (
256
+ span,
257
+ & mut err,
258
+ & decl. output ,
259
+ & body,
260
+ & name,
261
+ & ret,
262
+ ) ;
263
+ // We don't want to give the other suggestions when the problem is the
264
+ // closure return type.
265
+ return err;
266
+ }
267
+ }
268
+
269
+ // This shouldn't be reachable, but just in case we leave a reasonable fallback.
270
+ let args = closure_args ( & fn_sig) ;
271
+ // This suggestion is incomplete, as the user will get further type inference
272
+ // errors due to the `_` placeholders and the introduction of `Box`, but it does
273
+ // nudge them in the right direction.
274
+ format ! ( "a boxed closure type like `Box<dyn Fn({}) -> {}>`" , args, ret)
275
+ }
276
+ Some ( ty) if is_named_and_not_impl_trait ( ty) && name == "_" => {
155
277
let ty = ty_to_string ( ty) ;
156
- ( format ! ( " for `{}`" , ty) ,
157
- format ! ( "the explicit type `{}`, with the type parameters specified" , ty) )
278
+ format ! ( "the explicit type `{}`, with the type parameters specified" , ty)
158
279
}
159
- Some ( ty) if & ty . to_string ( ) != "_" && ty. to_string ( ) != name => {
280
+ Some ( ty) if is_named_and_not_impl_trait ( ty ) && ty. to_string ( ) != name => {
160
281
let ty = ty_to_string ( ty) ;
161
- ( format ! ( " for `{}`" , ty) ,
162
- format ! (
163
- "the explicit type `{}`, where the type parameter `{}` is specified" ,
282
+ format ! (
283
+ "the explicit type `{}`, where the type parameter `{}` is specified" ,
164
284
ty,
165
285
name,
166
- ) )
286
+ )
167
287
}
168
- _ => ( String :: new ( ) , "a type" . to_owned ( ) ) ,
288
+ _ => "a type" . to_string ( ) ,
169
289
} ;
170
- let mut labels = vec ! [ ( span, InferCtxt :: missing_type_msg( & name) ) ] ;
171
290
172
291
if let Some ( pattern) = local_visitor. found_arg_pattern {
173
- err_span = pattern. span ;
174
292
// We don't want to show the default label for closures.
175
293
//
176
294
// So, before clearing, the output would look something like this:
@@ -187,39 +305,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
187
305
// ^ consider giving this closure parameter the type `[_; 0]`
188
306
// with the type parameter `_` specified
189
307
// ```
190
- labels. clear ( ) ;
191
- labels. push ( (
308
+ err. span_label (
192
309
pattern. span ,
193
310
format ! ( "consider giving this closure parameter {}" , suffix) ,
194
- ) ) ;
311
+ ) ;
195
312
} else if let Some ( pattern) = local_visitor. found_local_pattern {
196
- if let Some ( simple_ident) = pattern. simple_ident ( ) {
313
+ let msg = if let Some ( simple_ident) = pattern. simple_ident ( ) {
197
314
match pattern. span . desugaring_kind ( ) {
198
- None => labels. push ( (
199
- pattern. span ,
200
- format ! ( "consider giving `{}` {}" , simple_ident, suffix) ,
201
- ) ) ,
202
- Some ( DesugaringKind :: ForLoop ) => labels. push ( (
203
- pattern. span ,
204
- "the element type for this iterator is not specified" . to_owned ( ) ,
205
- ) ) ,
206
- _ => { }
315
+ None => {
316
+ format ! ( "consider giving `{}` {}" , simple_ident, suffix)
317
+ }
318
+ Some ( DesugaringKind :: ForLoop ) => {
319
+ "the element type for this iterator is not specified" . to_string ( )
320
+ }
321
+ _ => format ! ( "this needs {}" , suffix) ,
207
322
}
208
323
} else {
209
- labels. push ( ( pattern. span , format ! ( "consider giving this pattern {}" , suffix) ) ) ;
210
- }
211
- } ;
212
-
213
- let mut err = struct_span_err ! (
214
- self . tcx. sess,
215
- err_span,
216
- E0282 ,
217
- "type annotations needed{}" ,
218
- ty_msg,
219
- ) ;
220
-
221
- for ( target_span, label_message) in labels {
222
- err. span_label ( target_span, label_message) ;
324
+ format ! ( "consider giving this pattern {}" , suffix)
325
+ } ;
326
+ err. span_label ( pattern. span , msg) ;
327
+ }
328
+ if !err. span . span_labels ( ) . iter ( ) . any ( |span_label| {
329
+ span_label. label . is_some ( ) && span_label. span == span
330
+ } ) && local_visitor. found_arg_pattern . is_none ( )
331
+ { // Avoid multiple labels pointing at `span`.
332
+ err. span_label ( span, InferCtxt :: missing_type_msg ( & name) ) ;
223
333
}
224
334
225
335
err
0 commit comments