@@ -4,15 +4,19 @@ use rustc_errors::MultiSpan;
44use rustc_errors:: { Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed } ;
55use rustc_hir as hir;
66use rustc_hir:: def:: CtorKind ;
7+ use rustc_hir:: intravisit:: Visitor ;
78use rustc_hir:: lang_items:: LangItem ;
89use rustc_hir:: { is_range_literal, Node } ;
910use rustc_infer:: infer:: InferOk ;
1011use rustc_middle:: lint:: in_external_macro;
1112use rustc_middle:: middle:: stability:: EvalResult ;
1213use rustc_middle:: ty:: adjustment:: AllowTwoPhase ;
1314use rustc_middle:: ty:: error:: { ExpectedFound , TypeError } ;
14- use rustc_middle:: ty:: print:: with_no_trimmed_paths;
15- use rustc_middle:: ty:: { self , Article , AssocItem , Ty , TypeAndMut } ;
15+ use rustc_middle:: ty:: fold:: TypeFolder ;
16+ use rustc_middle:: ty:: print:: { with_forced_trimmed_paths, with_no_trimmed_paths} ;
17+ use rustc_middle:: ty:: {
18+ self , Article , AssocItem , Ty , TyCtxt , TypeAndMut , TypeSuperFoldable , TypeVisitable ,
19+ } ;
1620use rustc_span:: symbol:: { sym, Symbol } ;
1721use rustc_span:: { BytePos , Span } ;
1822use rustc_trait_selection:: infer:: InferCtxtExt as _;
@@ -53,7 +57,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5357 || self . suggest_block_to_brackets_peeling_refs ( err, expr, expr_ty, expected)
5458 || self . suggest_copied_or_cloned ( err, expr, expr_ty, expected)
5559 || self . suggest_into ( err, expr, expr_ty, expected)
56- || self . suggest_floating_point_literal ( err, expr, expected) ;
60+ || self . suggest_floating_point_literal ( err, expr, expected)
61+ || self . point_inference_types ( err, expr) ;
5762 }
5863
5964 pub fn emit_coerce_suggestions (
@@ -205,6 +210,157 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
205210 ( expected, Some ( err) )
206211 }
207212
213+ fn point_inference_types ( & self , err : & mut Diagnostic , expr : & hir:: Expr < ' _ > ) -> bool {
214+ let tcx = self . tcx ;
215+ let map = self . tcx . hir ( ) ;
216+
217+ // Hack to make equality checks on types with inference variables and regions useful.
218+ struct TypeEraser < ' tcx > {
219+ tcx : TyCtxt < ' tcx > ,
220+ }
221+ impl < ' tcx > TypeFolder < ' tcx > for TypeEraser < ' tcx > {
222+ fn tcx < ' b > ( & ' b self ) -> TyCtxt < ' tcx > {
223+ self . tcx
224+ }
225+ fn fold_region ( & mut self , _r : ty:: Region < ' tcx > ) -> ty:: Region < ' tcx > {
226+ self . tcx ( ) . lifetimes . re_erased
227+ }
228+ fn fold_ty ( & mut self , t : Ty < ' tcx > ) -> Ty < ' tcx > {
229+ if !t. needs_infer ( ) && !t. has_erasable_regions ( ) {
230+ return t;
231+ }
232+ match * t. kind ( ) {
233+ ty:: Infer ( ty:: TyVar ( _) | ty:: FreshTy ( _) ) => {
234+ self . tcx . mk_ty_infer ( ty:: TyVar ( ty:: TyVid :: from_u32 ( 0 ) ) )
235+ }
236+ ty:: Infer ( ty:: IntVar ( _) | ty:: FreshIntTy ( _) ) => {
237+ self . tcx . mk_ty_infer ( ty:: IntVar ( ty:: IntVid { index : 0 } ) )
238+ }
239+ ty:: Infer ( ty:: FloatVar ( _) | ty:: FreshFloatTy ( _) ) => {
240+ self . tcx . mk_ty_infer ( ty:: FloatVar ( ty:: FloatVid { index : 0 } ) )
241+ }
242+ _ => t. super_fold_with ( self ) ,
243+ }
244+ }
245+ fn fold_const ( & mut self , ct : ty:: Const < ' tcx > ) -> ty:: Const < ' tcx > {
246+ ct. super_fold_with ( self )
247+ }
248+ }
249+
250+ let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , p) ) = expr. kind else { return false ; } ;
251+ let [ hir:: PathSegment { ident, args : None , .. } ] = p. segments else { return false ; } ;
252+ let hir:: def:: Res :: Local ( hir_id) = p. res else { return false ; } ;
253+ let Some ( node) = map. find ( hir_id) else { return false ; } ;
254+ let hir:: Node :: Pat ( pat) = node else { return false ; } ;
255+ let parent = map. get_parent_node ( pat. hir_id ) ;
256+ let Some ( hir:: Node :: Local ( hir:: Local {
257+ ty : None ,
258+ init : Some ( init) ,
259+ ..
260+ } ) ) = map. find ( parent) else { return false ; } ;
261+
262+ let ty = self . node_ty ( init. hir_id ) ;
263+ if ty. is_closure ( ) || init. span . overlaps ( expr. span ) {
264+ return false ;
265+ }
266+ let mut span_labels = vec ! [ (
267+ init. span,
268+ with_forced_trimmed_paths!( format!(
269+ "here the type of `{ident}` is inferred to be `{ty}`" ,
270+ ) ) ,
271+ ) ] ;
272+
273+ // Locate all the usages of the relevant binding.
274+ struct FindExprs < ' hir > {
275+ hir_id : hir:: HirId ,
276+ uses : Vec < & ' hir hir:: Expr < ' hir > > ,
277+ }
278+ impl < ' v > Visitor < ' v > for FindExprs < ' v > {
279+ fn visit_expr ( & mut self , ex : & ' v hir:: Expr < ' v > ) {
280+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = ex. kind
281+ && let hir:: def:: Res :: Local ( hir_id) = path. res
282+ && hir_id == self . hir_id
283+ {
284+ self . uses . push ( ex) ;
285+ }
286+ hir:: intravisit:: walk_expr ( self , ex) ;
287+ }
288+ }
289+
290+ let mut expr_finder = FindExprs { hir_id, uses : vec ! [ ] } ;
291+ let id = map. get_parent_item ( hir_id) ;
292+ let hir_id: hir:: HirId = id. into ( ) ;
293+
294+ if let Some ( node) = map. find ( hir_id) && let Some ( body_id) = node. body_id ( ) {
295+ let body = map. body ( body_id) ;
296+ expr_finder. visit_expr ( body. value ) ;
297+ let mut eraser = TypeEraser { tcx } ;
298+ let mut prev = eraser. fold_ty ( ty) ;
299+
300+ for ex in expr_finder. uses {
301+ if ex. span . overlaps ( expr. span ) { break ; }
302+ let parent = map. get_parent_node ( ex. hir_id ) ;
303+ if let Some ( hir:: Node :: Expr ( expr) )
304+ | Some ( hir:: Node :: Stmt ( hir:: Stmt {
305+ kind : hir:: StmtKind :: Expr ( expr) | hir:: StmtKind :: Semi ( expr) ,
306+ ..
307+ } ) ) = & map. find ( parent)
308+ && let hir:: ExprKind :: MethodCall ( s, rcvr, args, span) = expr. kind
309+ && rcvr. hir_id == ex. hir_id
310+ {
311+ let ty = if let Ok ( m) = self . lookup_method ( ty, s, span, expr, rcvr, args) {
312+ // We get the self type from `lookup_method` because the `rcvr` node
313+ // type will not have had any adjustments from the fn arguments.
314+ let ty = m. sig . inputs_and_output [ 0 ] ;
315+ match ty. kind ( ) {
316+ // Remove one layer of references to account for `&mut self` and
317+ // `&self`, so that we can compare it against the binding.
318+ ty:: Ref ( _, ty, _) => * ty,
319+ _ => ty,
320+ }
321+ } else {
322+ self . node_ty ( rcvr. hir_id )
323+ } ;
324+ let ty = eraser. fold_ty ( ty) ;
325+ if ty. references_error ( ) {
326+ break ;
327+ }
328+ if ty != prev {
329+ span_labels. push ( (
330+ s. ident . span ,
331+ with_forced_trimmed_paths ! ( format!(
332+ "here the type of `{ident}` is inferred to be `{ty}`" ,
333+ ) ) ,
334+ ) ) ;
335+ prev = ty;
336+ }
337+ } else {
338+ let ty = eraser. fold_ty ( self . node_ty ( ex. hir_id ) ) ;
339+ if ty. references_error ( ) {
340+ break ;
341+ }
342+ if ty != prev {
343+ span_labels. push ( (
344+ ex. span ,
345+ with_forced_trimmed_paths ! ( format!(
346+ "here the type of `{ident}` is inferred to be `{ty}`" ,
347+ ) ) ,
348+ ) ) ;
349+ }
350+ prev = ty;
351+ }
352+ if ex. hir_id == expr. hir_id {
353+ // Stop showing spans after the error type was emitted.
354+ break ;
355+ }
356+ }
357+ }
358+ for ( sp, label) in span_labels {
359+ err. span_label ( sp, & label) ;
360+ }
361+ true
362+ }
363+
208364 fn annotate_expected_due_to_let_ty (
209365 & self ,
210366 err : & mut Diagnostic ,
0 commit comments