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+ ) -> 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+
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,60 @@ 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+ }
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 ( )
75146}
76147
77148impl < ' a , ' tcx > InferCtxt < ' a , ' tcx > {
@@ -106,16 +177,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
106177 let ty = self . resolve_vars_if_possible ( & ty) ;
107178 let name = self . extract_type_name ( & ty, None ) ;
108179
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 ( ) ) ;
119181 let ty_to_string = |ty : Ty < ' tcx > | -> String {
120182 let mut s = String :: new ( ) ;
121183 let mut printer = ty:: print:: FmtPrinter :: new ( self . tcx , & mut s, Namespace :: TypeNS ) ;
@@ -136,6 +198,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
136198 let expr = self . tcx . hir ( ) . expect_expr ( body_id. hir_id ) ;
137199 local_visitor. visit_expr ( expr) ;
138200 }
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+ } ;
139226
140227 // When `name` corresponds to a type argument, show the path of the full type we're
141228 // trying to infer. In the following example, `ty_msg` contains
@@ -150,27 +237,58 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
150237 // | consider giving `b` the explicit type `std::result::Result<i32, E>`, where
151238 // | the type parameter `E` is specified
152239 // ```
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 == "_" => {
155277 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)
158279 }
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 => {
160281 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" ,
164284 ty,
165285 name,
166- ) )
286+ )
167287 }
168- _ => ( String :: new ( ) , "a type" . to_owned ( ) ) ,
288+ _ => "a type" . to_string ( ) ,
169289 } ;
170- let mut labels = vec ! [ ( span, InferCtxt :: missing_type_msg( & name) ) ] ;
171290
172291 if let Some ( pattern) = local_visitor. found_arg_pattern {
173- err_span = pattern. span ;
174292 // We don't want to show the default label for closures.
175293 //
176294 // So, before clearing, the output would look something like this:
@@ -187,39 +305,31 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
187305 // ^ consider giving this closure parameter the type `[_; 0]`
188306 // with the type parameter `_` specified
189307 // ```
190- labels. clear ( ) ;
191- labels. push ( (
308+ err. span_label (
192309 pattern. span ,
193310 format ! ( "consider giving this closure parameter {}" , suffix) ,
194- ) ) ;
311+ ) ;
195312 } 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 ( ) {
197314 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) ,
207322 }
208323 } 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) ) ;
223333 }
224334
225335 err
0 commit comments