@@ -6,6 +6,7 @@ use crate::infer::type_variable::TypeVariableOriginKind;
66use crate :: ty:: { self , Ty , Infer , TyVar } ;
77use crate :: ty:: print:: Print ;
88use syntax:: source_map:: DesugaringKind ;
9+ use syntax:: symbol:: kw;
910use syntax_pos:: Span ;
1011use errors:: { Applicability , DiagnosticBuilder } ;
1112
@@ -19,6 +20,7 @@ struct FindLocalByTypeVisitor<'a, 'tcx> {
1920 found_arg_pattern : Option < & ' tcx Pat > ,
2021 found_ty : Option < Ty < ' tcx > > ,
2122 found_closure : Option < & ' tcx ExprKind > ,
23+ found_method_call : Option < & ' tcx Expr > ,
2224}
2325
2426impl < ' a , ' tcx > FindLocalByTypeVisitor < ' a , ' tcx > {
@@ -35,6 +37,7 @@ impl<'a, 'tcx> FindLocalByTypeVisitor<'a, 'tcx> {
3537 found_arg_pattern : None ,
3638 found_ty : None ,
3739 found_closure : None ,
40+ found_method_call : None ,
3841 }
3942 }
4043
@@ -93,11 +96,12 @@ impl<'a, 'tcx> Visitor<'tcx> for FindLocalByTypeVisitor<'a, 'tcx> {
9396 }
9497
9598 fn visit_expr ( & mut self , expr : & ' tcx Expr ) {
96- if let ( ExprKind :: Closure ( _, _fn_decl, _id, _sp, _) , Some ( _) ) = (
97- & expr. kind ,
98- self . node_matches_type ( expr. hir_id ) ,
99- ) {
100- self . found_closure = Some ( & expr. kind ) ;
99+ if self . node_matches_type ( expr. hir_id ) . is_some ( ) {
100+ match expr. kind {
101+ ExprKind :: Closure ( ..) => self . found_closure = Some ( & expr. kind ) ,
102+ ExprKind :: MethodCall ( ..) => self . found_method_call = Some ( & expr) ,
103+ _ => { }
104+ }
101105 }
102106 intravisit:: walk_expr ( self , expr) ;
103107 }
@@ -147,6 +151,25 @@ fn closure_args(fn_sig: &ty::PolyFnSig<'_>) -> String {
147151 . unwrap_or_default ( )
148152}
149153
154+ pub enum TypeAnnotationNeeded {
155+ E0282 ,
156+ E0283 ,
157+ E0284 ,
158+ }
159+
160+ impl Into < errors:: DiagnosticId > for TypeAnnotationNeeded {
161+ fn into ( self ) -> errors:: DiagnosticId {
162+ syntax:: diagnostic_used!( E0282 ) ;
163+ syntax:: diagnostic_used!( E0283 ) ;
164+ syntax:: diagnostic_used!( E0284 ) ;
165+ errors:: DiagnosticId :: Error ( match self {
166+ Self :: E0282 => "E0282" . to_string ( ) ,
167+ Self :: E0283 => "E0283" . to_string ( ) ,
168+ Self :: E0284 => "E0284" . to_string ( ) ,
169+ } )
170+ }
171+ }
172+
150173impl < ' a , ' tcx > InferCtxt < ' a , ' tcx > {
151174 pub fn extract_type_name (
152175 & self ,
@@ -157,7 +180,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
157180 let ty_vars = self . type_variables . borrow ( ) ;
158181 let var_origin = ty_vars. var_origin ( ty_vid) ;
159182 if let TypeVariableOriginKind :: TypeParameterDefinition ( name) = var_origin. kind {
160- return ( name. to_string ( ) , Some ( var_origin. span ) ) ;
183+ if name != kw:: SelfUpper {
184+ return ( name. to_string ( ) , Some ( var_origin. span ) ) ;
185+ }
161186 }
162187 }
163188
@@ -175,6 +200,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
175200 body_id : Option < hir:: BodyId > ,
176201 span : Span ,
177202 ty : Ty < ' tcx > ,
203+ error_code : TypeAnnotationNeeded ,
178204 ) -> DiagnosticBuilder < ' tcx > {
179205 let ty = self . resolve_vars_if_possible ( & ty) ;
180206 let ( name, name_sp) = self . extract_type_name ( & ty, None ) ;
@@ -185,8 +211,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
185211 let mut printer = ty:: print:: FmtPrinter :: new ( self . tcx , & mut s, Namespace :: TypeNS ) ;
186212 let ty_vars = self . type_variables . borrow ( ) ;
187213 let getter = move |ty_vid| {
188- if let TypeVariableOriginKind :: TypeParameterDefinition ( name ) =
189- ty_vars . var_origin ( ty_vid ) . kind {
214+ let var_origin = ty_vars . var_origin ( ty_vid ) ;
215+ if let TypeVariableOriginKind :: TypeParameterDefinition ( name ) = var_origin. kind {
190216 return Some ( name. to_string ( ) ) ;
191217 }
192218 None
@@ -210,6 +236,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
210236 // 3 | let _ = x.sum() as f64;
211237 // | ^^^ cannot infer type for `S`
212238 span
239+ } else if let Some (
240+ ExprKind :: MethodCall ( _, call_span, _) ,
241+ ) = local_visitor. found_method_call . map ( |e| & e. kind ) {
242+ // Point at the call instead of the whole expression:
243+ // error[E0284]: type annotations needed
244+ // --> file.rs:2:5
245+ // |
246+ // 2 | vec![Ok(2)].into_iter().collect()?;
247+ // | ^^^^^^^ cannot infer type
248+ // |
249+ // = note: cannot resolve `<_ as std::ops::Try>::Ok == _`
250+ if span. contains ( * call_span) {
251+ * call_span
252+ } else {
253+ span
254+ }
213255 } else {
214256 span
215257 } ;
@@ -247,12 +289,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
247289 // | consider giving `b` the explicit type `std::result::Result<i32, E>`, where
248290 // | the type parameter `E` is specified
249291 // ```
250- let mut err = struct_span_err ! (
251- self . tcx. sess,
292+ let error_code = error_code . into ( ) ;
293+ let mut err = self . tcx . sess . struct_span_err_with_code (
252294 err_span,
253- E0282 ,
254- "type annotations needed{}" ,
255- ty_msg,
295+ & format ! ( "type annotations needed{}" , ty_msg) ,
296+ error_code,
256297 ) ;
257298
258299 let suffix = match local_visitor. found_ty {
@@ -334,6 +375,36 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
334375 format ! ( "consider giving this pattern {}" , suffix)
335376 } ;
336377 err. span_label ( pattern. span , msg) ;
378+ } else if let Some ( e) = local_visitor. found_method_call {
379+ if let ExprKind :: MethodCall ( segment, ..) = & e. kind {
380+ // Suggest specifiying type params or point out the return type of the call:
381+ //
382+ // error[E0282]: type annotations needed
383+ // --> $DIR/type-annotations-needed-expr.rs:2:39
384+ // |
385+ // LL | let _ = x.into_iter().sum() as f64;
386+ // | ^^^
387+ // | |
388+ // | cannot infer type for `S`
389+ // | help: consider specifying the type argument in
390+ // | the method call: `sum::<S>`
391+ // |
392+ // = note: type must be known at this point
393+ //
394+ // or
395+ //
396+ // error[E0282]: type annotations needed
397+ // --> $DIR/issue-65611.rs:59:20
398+ // |
399+ // LL | let x = buffer.last().unwrap().0.clone();
400+ // | -------^^^^--
401+ // | | |
402+ // | | cannot infer type for `T`
403+ // | this method call resolves to `std::option::Option<&T>`
404+ // |
405+ // = note: type must be known at this point
406+ self . annotate_method_call ( segment, e, & mut err) ;
407+ }
337408 }
338409 // Instead of the following:
339410 // error[E0282]: type annotations needed
@@ -351,7 +422,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
351422 // | ^^^ cannot infer type for `S`
352423 // |
353424 // = note: type must be known at this point
354- let span = name_sp. unwrap_or ( span ) ;
425+ let span = name_sp. unwrap_or ( err_span ) ;
355426 if !err. span . span_labels ( ) . iter ( ) . any ( |span_label| {
356427 span_label. label . is_some ( ) && span_label. span == span
357428 } ) && local_visitor. found_arg_pattern . is_none ( )
@@ -362,6 +433,51 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
362433 err
363434 }
364435
436+ /// If the `FnSig` for the method call can be found and type arguments are identified as
437+ /// needed, suggest annotating the call, otherwise point out the resulting type of the call.
438+ fn annotate_method_call (
439+ & self ,
440+ segment : & hir:: ptr:: P < hir:: PathSegment > ,
441+ e : & Expr ,
442+ err : & mut DiagnosticBuilder < ' _ > ,
443+ ) {
444+ if let ( Ok ( snippet) , Some ( tables) , None ) = (
445+ self . tcx . sess . source_map ( ) . span_to_snippet ( segment. ident . span ) ,
446+ self . in_progress_tables ,
447+ & segment. args ,
448+ ) {
449+ let borrow = tables. borrow ( ) ;
450+ let method_defs = borrow. node_method_def_id ( ) ;
451+ if let Some ( did) = method_defs. get ( e. hir_id ) {
452+ let generics = self . tcx . generics_of ( * did) ;
453+ if !generics. params . is_empty ( ) {
454+ err. span_suggestion (
455+ segment. ident . span ,
456+ & format ! (
457+ "consider specifying the type argument{} in the method call" ,
458+ if generics. params. len( ) > 1 {
459+ "s"
460+ } else {
461+ ""
462+ } ,
463+ ) ,
464+ format ! ( "{}::<{}>" , snippet, generics. params. iter( )
465+ . map( |p| p. name. to_string( ) )
466+ . collect:: <Vec <String >>( )
467+ . join( ", " ) ) ,
468+ Applicability :: HasPlaceholders ,
469+ ) ;
470+ } else {
471+ let sig = self . tcx . fn_sig ( * did) ;
472+ err. span_label ( e. span , & format ! (
473+ "this method call resolves to `{:?}`" ,
474+ sig. output( ) . skip_binder( ) ,
475+ ) ) ;
476+ }
477+ }
478+ }
479+ }
480+
365481 pub fn need_type_info_err_in_generator (
366482 & self ,
367483 kind : hir:: GeneratorKind ,
0 commit comments