11use 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 } ;
33use crate :: hir:: intravisit:: { self , Visitor , NestedVisitorMap } ;
44use crate :: infer:: InferCtxt ;
55use crate :: infer:: type_variable:: TypeVariableOriginKind ;
66use crate :: ty:: { self , Ty , Infer , TyVar } ;
77use crate :: ty:: print:: Print ;
88use syntax:: source_map:: DesugaringKind ;
99use syntax_pos:: Span ;
10- use errors:: DiagnosticBuilder ;
10+ use errors:: { Applicability , DiagnosticBuilder } ;
1111
1212struct 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
2122impl < ' 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
77104impl < ' 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
0 commit comments