1
1
use crate :: FnCtxt ;
2
2
use rustc_ast:: util:: parser:: PREC_POSTFIX ;
3
+ use rustc_data_structures:: fx:: FxHashMap ;
3
4
use rustc_errors:: MultiSpan ;
4
5
use rustc_errors:: { Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed } ;
5
6
use rustc_hir as hir;
@@ -14,12 +15,14 @@ use rustc_middle::ty::adjustment::AllowTwoPhase;
14
15
use rustc_middle:: ty:: error:: { ExpectedFound , TypeError } ;
15
16
use rustc_middle:: ty:: fold:: TypeFolder ;
16
17
use rustc_middle:: ty:: print:: { with_forced_trimmed_paths, with_no_trimmed_paths} ;
18
+ use rustc_middle:: ty:: relate:: TypeRelation ;
17
19
use rustc_middle:: ty:: {
18
20
self , Article , AssocItem , Ty , TyCtxt , TypeAndMut , TypeSuperFoldable , TypeVisitable ,
19
21
} ;
20
22
use rustc_span:: symbol:: { sym, Symbol } ;
21
23
use rustc_span:: { BytePos , Span } ;
22
24
use rustc_trait_selection:: infer:: InferCtxtExt as _;
25
+ use rustc_trait_selection:: traits:: error_reporting:: method_chain:: CollectAllMismatches ;
23
26
use rustc_trait_selection:: traits:: ObligationCause ;
24
27
25
28
use super :: method:: probe;
@@ -44,7 +47,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
44
47
self . annotate_alternative_method_deref ( err, expr, error) ;
45
48
46
49
// Use `||` to give these suggestions a precedence
47
- let _ = self . suggest_missing_parentheses ( err, expr)
50
+ let suggested = self . suggest_missing_parentheses ( err, expr)
48
51
|| self . suggest_remove_last_method_call ( err, expr, expected)
49
52
|| self . suggest_associated_const ( err, expr, expected)
50
53
|| self . suggest_deref_ref_or_into ( err, expr, expected, expr_ty, expected_ty_expr)
@@ -57,8 +60,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
57
60
|| self . suggest_block_to_brackets_peeling_refs ( err, expr, expr_ty, expected)
58
61
|| self . suggest_copied_or_cloned ( err, expr, expr_ty, expected)
59
62
|| self . suggest_into ( err, expr, expr_ty, expected)
60
- || self . suggest_floating_point_literal ( err, expr, expected)
61
- || self . point_inference_types ( err, expr) ;
63
+ || self . suggest_floating_point_literal ( err, expr, expected) ;
64
+ if !suggested {
65
+ self . point_at_expr_source_of_inferred_type ( err, expr, expr_ty, expected) ;
66
+ }
62
67
}
63
68
64
69
pub fn emit_coerce_suggestions (
@@ -210,7 +215,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
210
215
( expected, Some ( err) )
211
216
}
212
217
213
- fn point_inference_types ( & self , err : & mut Diagnostic , expr : & hir:: Expr < ' _ > ) -> bool {
218
+ fn point_at_expr_source_of_inferred_type (
219
+ & self ,
220
+ err : & mut Diagnostic ,
221
+ expr : & hir:: Expr < ' _ > ,
222
+ found : Ty < ' tcx > ,
223
+ expected : Ty < ' tcx > ,
224
+ ) -> bool {
214
225
let tcx = self . tcx ;
215
226
let map = self . tcx . hir ( ) ;
216
227
@@ -250,25 +261,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
250
261
let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , p) ) = expr. kind else { return false ; } ;
251
262
let [ hir:: PathSegment { ident, args : None , .. } ] = p. segments else { return false ; } ;
252
263
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 ; } ;
264
+ let Some ( hir:: Node :: Pat ( pat) ) = map. find ( hir_id) else { return false ; } ;
255
265
let parent = map. get_parent_node ( pat. hir_id ) ;
256
266
let Some ( hir:: Node :: Local ( hir:: Local {
257
267
ty : None ,
258
268
init : Some ( init) ,
259
269
..
260
270
} ) ) = map. find ( parent) else { return false ; } ;
261
-
262
- let ty = self . node_ty ( init. hir_id ) ;
271
+ let Some ( ty) = self . node_ty_opt ( init. hir_id ) else { return false ; } ;
263
272
if ty. is_closure ( ) || init. span . overlaps ( expr. span ) || pat. span . from_expansion ( ) {
264
273
return false ;
265
274
}
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
275
273
276
// Locate all the usages of the relevant binding.
274
277
struct FindExprs < ' hir > {
@@ -296,71 +299,139 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
296
299
expr_finder. visit_expr ( body. value ) ;
297
300
let mut eraser = TypeEraser { tcx } ;
298
301
let mut prev = eraser. fold_ty ( ty) ;
302
+ let mut prev_span = None ;
299
303
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 ) ;
304
+ for binding in expr_finder. uses {
305
+ // In every expression where the binding is referenced, we will look at that
306
+ // expression's type and see if it is where the incorrect found type was fully
307
+ // "materialized" and point at it. We will also try to provide a suggestion there.
308
+ let parent = map. get_parent_node ( binding. hir_id ) ;
303
309
if let Some ( hir:: Node :: Expr ( expr) )
304
310
| Some ( hir:: Node :: Stmt ( hir:: Stmt {
305
311
kind : hir:: StmtKind :: Expr ( expr) | hir:: StmtKind :: Semi ( expr) ,
306
312
..
307
313
} ) ) = & map. find ( parent)
308
- && let hir:: ExprKind :: MethodCall ( s, rcvr, args, span) = expr. kind
309
- && rcvr. hir_id == ex. hir_id
314
+ && let hir:: ExprKind :: MethodCall ( s, rcvr, args, _span) = expr. kind
315
+ && rcvr. hir_id == binding. hir_id
316
+ && let Some ( def_id) = self . typeck_results . borrow ( ) . type_dependent_def_id ( expr. hir_id )
310
317
{
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,
318
+ // We special case methods, because they can influence inference through the
319
+ // call's arguments and we can provide a more explicit span.
320
+ let sig = self . tcx . fn_sig ( def_id) ;
321
+ let def_self_ty = sig. input ( 0 ) . skip_binder ( ) ;
322
+ let rcvr_ty = self . node_ty ( rcvr. hir_id ) ;
323
+ // Get the evaluated type *after* calling the method call, so that the influence
324
+ // of the arguments can be reflected in the receiver type. The receiver
325
+ // expression has the type *before* theis analysis is done.
326
+ let ty = match self . lookup_probe ( s. ident , rcvr_ty, expr, probe:: ProbeScope :: TraitsInScope ) {
327
+ Ok ( pick) => pick. self_ty ,
328
+ Err ( _) => rcvr_ty,
329
+ } ;
330
+ // Remove one layer of references to account for `&mut self` and
331
+ // `&self`, so that we can compare it against the binding.
332
+ let ( ty, def_self_ty) = match ( ty. kind ( ) , def_self_ty. kind ( ) ) {
333
+ ( ty:: Ref ( _, ty, a) , ty:: Ref ( _, self_ty, b) ) if a == b => ( * ty, * self_ty) ,
334
+ _ => ( ty, def_self_ty) ,
335
+ } ;
336
+ let mut param_args = FxHashMap :: default ( ) ;
337
+ let mut param_expected = FxHashMap :: default ( ) ;
338
+ let mut param_found = FxHashMap :: default ( ) ;
339
+ if self . can_eq ( self . param_env , ty, found) . is_ok ( ) {
340
+ // We only point at the first place where the found type was inferred.
341
+ for ( i, param_ty) in sig. inputs ( ) . skip_binder ( ) . iter ( ) . skip ( 1 ) . enumerate ( ) {
342
+ if def_self_ty. contains ( * param_ty) && let ty:: Param ( _) = param_ty. kind ( ) {
343
+ // We found an argument that references a type parameter in `Self`,
344
+ // so we assume that this is the argument that caused the found
345
+ // type, which we know already because of `can_eq` above was first
346
+ // inferred in this method call.
347
+ let arg = & args[ i] ;
348
+ let arg_ty = self . node_ty ( arg. hir_id ) ;
349
+ err. span_label (
350
+ arg. span ,
351
+ & format ! (
352
+ "this is of type `{arg_ty}`, which makes `{ident}` to be \
353
+ inferred as `{ty}`",
354
+ ) ,
355
+ ) ;
356
+ param_args. insert ( param_ty, ( arg, arg_ty) ) ;
357
+ }
320
358
}
321
- } else {
322
- self . node_ty ( rcvr. hir_id )
359
+ }
360
+
361
+ // Here we find, for a type param `T`, the type that `T` is in the current
362
+ // method call *and* in the original expected type. That way, we can see if we
363
+ // can give any structured suggestion for the function argument.
364
+ let mut c = CollectAllMismatches {
365
+ infcx : & self . infcx ,
366
+ param_env : self . param_env ,
367
+ errors : vec ! [ ] ,
323
368
} ;
369
+ let _ = c. relate ( def_self_ty, ty) ;
370
+ for error in c. errors {
371
+ if let TypeError :: Sorts ( error) = error {
372
+ param_found. insert ( error. expected , error. found ) ;
373
+ }
374
+ }
375
+ c. errors = vec ! [ ] ;
376
+ let _ = c. relate ( def_self_ty, expected) ;
377
+ for error in c. errors {
378
+ if let TypeError :: Sorts ( error) = error {
379
+ param_expected. insert ( error. expected , error. found ) ;
380
+ }
381
+ }
382
+ for ( param, ( arg, arg_ty) ) in param_args. iter ( ) {
383
+ let Some ( expected) = param_expected. get ( param) else { continue ; } ;
384
+ let Some ( found) = param_found. get ( param) else { continue ; } ;
385
+ if self . can_eq ( self . param_env , * arg_ty, * found) . is_err ( ) { continue ; }
386
+ self . suggest_deref_ref_or_into ( err, arg, * expected, * found, None ) ;
387
+ }
388
+
324
389
let ty = eraser. fold_ty ( ty) ;
325
390
if ty. references_error ( ) {
326
391
break ;
327
392
}
328
- if ty != prev {
329
- span_labels. push ( (
393
+ if ty != prev
394
+ && param_args. is_empty ( )
395
+ && self . can_eq ( self . param_env , ty, found) . is_ok ( )
396
+ {
397
+ // We only point at the first place where the found type was inferred.
398
+ err. span_label (
330
399
s. ident . span ,
331
400
with_forced_trimmed_paths ! ( format!(
332
401
"here the type of `{ident}` is inferred to be `{ty}`" ,
333
402
) ) ,
334
- ) ) ;
335
- prev = ty ;
403
+ ) ;
404
+ break ;
336
405
}
406
+ prev = ty;
337
407
} else {
338
- let ty = eraser. fold_ty ( self . node_ty ( ex . hir_id ) ) ;
408
+ let ty = eraser. fold_ty ( self . node_ty ( binding . hir_id ) ) ;
339
409
if ty. references_error ( ) {
340
410
break ;
341
411
}
342
- if ty != prev {
343
- span_labels. push ( (
344
- ex. span ,
412
+ if ty != prev && let Some ( span) = prev_span && self . can_eq ( self . param_env , ty, found) . is_ok ( ) {
413
+ // We only point at the first place where the found type was inferred.
414
+ // We use the *previous* span because if the type is known *here* it means
415
+ // it was *evaluated earlier*. We don't do this for method calls because we
416
+ // evaluate the method's self type eagerly, but not in any other case.
417
+ err. span_label (
418
+ span,
345
419
with_forced_trimmed_paths ! ( format!(
346
420
"here the type of `{ident}` is inferred to be `{ty}`" ,
347
421
) ) ,
348
- ) ) ;
422
+ ) ;
423
+ break ;
349
424
}
350
425
prev = ty;
351
426
}
352
- if ex. hir_id == expr. hir_id {
353
- // Stop showing spans after the error type was emitted.
427
+ if binding. hir_id == expr. hir_id {
428
+ // Do not look at expressions that come after the expression we were originally
429
+ // evaluating and had a type error.
354
430
break ;
355
431
}
432
+ prev_span = Some ( binding. span ) ;
356
433
}
357
434
}
358
- if span_labels. len ( ) < 2 {
359
- return false ;
360
- }
361
- for ( sp, label) in span_labels {
362
- err. span_label ( sp, & label) ;
363
- }
364
435
true
365
436
}
366
437
0 commit comments