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;
6
7
use rustc_hir:: def:: CtorKind ;
8
+ use rustc_hir:: intravisit:: Visitor ;
7
9
use rustc_hir:: lang_items:: LangItem ;
8
10
use rustc_hir:: { is_range_literal, Node } ;
9
11
use rustc_infer:: infer:: InferOk ;
10
12
use rustc_middle:: lint:: in_external_macro;
11
13
use rustc_middle:: middle:: stability:: EvalResult ;
12
14
use rustc_middle:: ty:: adjustment:: AllowTwoPhase ;
13
15
use 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 } ;
16
+ use rustc_middle:: ty:: fold:: { BottomUpFolder , TypeFolder } ;
17
+ use rustc_middle:: ty:: print:: { with_forced_trimmed_paths, with_no_trimmed_paths} ;
18
+ use rustc_middle:: ty:: relate:: TypeRelation ;
19
+ use rustc_middle:: ty:: { self , Article , AssocItem , Ty , TypeAndMut , TypeVisitable } ;
16
20
use rustc_span:: symbol:: { sym, Symbol } ;
17
21
use rustc_span:: { BytePos , Span } ;
18
22
use rustc_trait_selection:: infer:: InferCtxtExt as _;
23
+ use rustc_trait_selection:: traits:: error_reporting:: method_chain:: CollectAllMismatches ;
19
24
use rustc_trait_selection:: traits:: ObligationCause ;
20
25
21
26
use super :: method:: probe;
@@ -40,7 +45,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
40
45
self . annotate_alternative_method_deref ( err, expr, error) ;
41
46
42
47
// Use `||` to give these suggestions a precedence
43
- let _ = self . suggest_missing_parentheses ( err, expr)
48
+ let suggested = self . suggest_missing_parentheses ( err, expr)
44
49
|| self . suggest_remove_last_method_call ( err, expr, expected)
45
50
|| self . suggest_associated_const ( err, expr, expected)
46
51
|| self . suggest_deref_ref_or_into ( err, expr, expected, expr_ty, expected_ty_expr)
@@ -54,6 +59,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
54
59
|| self . suggest_copied_or_cloned ( err, expr, expr_ty, expected)
55
60
|| self . suggest_into ( err, expr, expr_ty, expected)
56
61
|| self . suggest_floating_point_literal ( err, expr, expected) ;
62
+ if !suggested {
63
+ self . point_at_expr_source_of_inferred_type ( err, expr, expr_ty, expected) ;
64
+ }
57
65
}
58
66
59
67
pub fn emit_coerce_suggestions (
@@ -205,6 +213,217 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
205
213
( expected, Some ( err) )
206
214
}
207
215
216
+ pub fn point_at_expr_source_of_inferred_type (
217
+ & self ,
218
+ err : & mut Diagnostic ,
219
+ expr : & hir:: Expr < ' _ > ,
220
+ found : Ty < ' tcx > ,
221
+ expected : Ty < ' tcx > ,
222
+ ) -> bool {
223
+ let map = self . tcx . hir ( ) ;
224
+
225
+ let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , p) ) = expr. kind else { return false ; } ;
226
+ let [ hir:: PathSegment { ident, args : None , .. } ] = p. segments else { return false ; } ;
227
+ let hir:: def:: Res :: Local ( hir_id) = p. res else { return false ; } ;
228
+ let Some ( hir:: Node :: Pat ( pat) ) = map. find ( hir_id) else { return false ; } ;
229
+ let parent = map. get_parent_node ( pat. hir_id ) ;
230
+ let Some ( hir:: Node :: Local ( hir:: Local {
231
+ ty : None ,
232
+ init : Some ( init) ,
233
+ ..
234
+ } ) ) = map. find ( parent) else { return false ; } ;
235
+ let Some ( ty) = self . node_ty_opt ( init. hir_id ) else { return false ; } ;
236
+ if ty. is_closure ( ) || init. span . overlaps ( expr. span ) || pat. span . from_expansion ( ) {
237
+ return false ;
238
+ }
239
+
240
+ // Locate all the usages of the relevant binding.
241
+ struct FindExprs < ' hir > {
242
+ hir_id : hir:: HirId ,
243
+ uses : Vec < & ' hir hir:: Expr < ' hir > > ,
244
+ }
245
+ impl < ' v > Visitor < ' v > for FindExprs < ' v > {
246
+ fn visit_expr ( & mut self , ex : & ' v hir:: Expr < ' v > ) {
247
+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = ex. kind
248
+ && let hir:: def:: Res :: Local ( hir_id) = path. res
249
+ && hir_id == self . hir_id
250
+ {
251
+ self . uses . push ( ex) ;
252
+ }
253
+ hir:: intravisit:: walk_expr ( self , ex) ;
254
+ }
255
+ }
256
+
257
+ let mut expr_finder = FindExprs { hir_id, uses : vec ! [ ] } ;
258
+ let id = map. get_parent_item ( hir_id) ;
259
+ let hir_id: hir:: HirId = id. into ( ) ;
260
+
261
+ let Some ( node) = map. find ( hir_id) else { return false ; } ;
262
+ let Some ( body_id) = node. body_id ( ) else { return false ; } ;
263
+ let body = map. body ( body_id) ;
264
+ expr_finder. visit_expr ( body. value ) ;
265
+ // Hack to make equality checks on types with inference variables and regions useful.
266
+ let mut eraser = BottomUpFolder {
267
+ tcx : self . tcx ,
268
+ lt_op : |_| self . tcx . lifetimes . re_erased ,
269
+ ct_op : |c| c,
270
+ ty_op : |t| match * t. kind ( ) {
271
+ ty:: Infer ( ty:: TyVar ( vid) ) => self . tcx . mk_ty_infer ( ty:: TyVar ( self . root_var ( vid) ) ) ,
272
+ ty:: Infer ( ty:: IntVar ( _) ) => {
273
+ self . tcx . mk_ty_infer ( ty:: IntVar ( ty:: IntVid { index : 0 } ) )
274
+ }
275
+ ty:: Infer ( ty:: FloatVar ( _) ) => {
276
+ self . tcx . mk_ty_infer ( ty:: FloatVar ( ty:: FloatVid { index : 0 } ) )
277
+ }
278
+ _ => t,
279
+ } ,
280
+ } ;
281
+ let mut prev = eraser. fold_ty ( ty) ;
282
+ let mut prev_span = None ;
283
+
284
+ for binding in expr_finder. uses {
285
+ // In every expression where the binding is referenced, we will look at that
286
+ // expression's type and see if it is where the incorrect found type was fully
287
+ // "materialized" and point at it. We will also try to provide a suggestion there.
288
+ let parent = map. get_parent_node ( binding. hir_id ) ;
289
+ if let Some ( hir:: Node :: Expr ( expr) )
290
+ | Some ( hir:: Node :: Stmt ( hir:: Stmt {
291
+ kind : hir:: StmtKind :: Expr ( expr) | hir:: StmtKind :: Semi ( expr) ,
292
+ ..
293
+ } ) ) = & map. find ( parent)
294
+ && let hir:: ExprKind :: MethodCall ( segment, rcvr, args, _span) = expr. kind
295
+ && rcvr. hir_id == binding. hir_id
296
+ && let Some ( def_id) = self . typeck_results . borrow ( ) . type_dependent_def_id ( expr. hir_id )
297
+ {
298
+ // We special case methods, because they can influence inference through the
299
+ // call's arguments and we can provide a more explicit span.
300
+ let sig = self . tcx . fn_sig ( def_id) ;
301
+ let def_self_ty = sig. input ( 0 ) . skip_binder ( ) ;
302
+ let rcvr_ty = self . node_ty ( rcvr. hir_id ) ;
303
+ // Get the evaluated type *after* calling the method call, so that the influence
304
+ // of the arguments can be reflected in the receiver type. The receiver
305
+ // expression has the type *before* theis analysis is done.
306
+ let ty = match self . lookup_probe (
307
+ segment. ident ,
308
+ rcvr_ty,
309
+ expr,
310
+ probe:: ProbeScope :: TraitsInScope ,
311
+ ) {
312
+ Ok ( pick) => pick. self_ty ,
313
+ Err ( _) => rcvr_ty,
314
+ } ;
315
+ // Remove one layer of references to account for `&mut self` and
316
+ // `&self`, so that we can compare it against the binding.
317
+ let ( ty, def_self_ty) = match ( ty. kind ( ) , def_self_ty. kind ( ) ) {
318
+ ( ty:: Ref ( _, ty, a) , ty:: Ref ( _, self_ty, b) ) if a == b => ( * ty, * self_ty) ,
319
+ _ => ( ty, def_self_ty) ,
320
+ } ;
321
+ let mut param_args = FxHashMap :: default ( ) ;
322
+ let mut param_expected = FxHashMap :: default ( ) ;
323
+ let mut param_found = FxHashMap :: default ( ) ;
324
+ if self . can_eq ( self . param_env , ty, found) . is_ok ( ) {
325
+ // We only point at the first place where the found type was inferred.
326
+ for ( i, param_ty) in sig. inputs ( ) . skip_binder ( ) . iter ( ) . skip ( 1 ) . enumerate ( ) {
327
+ if def_self_ty. contains ( * param_ty) && let ty:: Param ( _) = param_ty. kind ( ) {
328
+ // We found an argument that references a type parameter in `Self`,
329
+ // so we assume that this is the argument that caused the found
330
+ // type, which we know already because of `can_eq` above was first
331
+ // inferred in this method call.
332
+ let arg = & args[ i] ;
333
+ let arg_ty = self . node_ty ( arg. hir_id ) ;
334
+ err. span_label (
335
+ arg. span ,
336
+ & format ! (
337
+ "this is of type `{arg_ty}`, which causes `{ident}` to be \
338
+ inferred as `{ty}`",
339
+ ) ,
340
+ ) ;
341
+ param_args. insert ( param_ty, ( arg, arg_ty) ) ;
342
+ }
343
+ }
344
+ }
345
+
346
+ // Here we find, for a type param `T`, the type that `T` is in the current
347
+ // method call *and* in the original expected type. That way, we can see if we
348
+ // can give any structured suggestion for the function argument.
349
+ let mut c = CollectAllMismatches {
350
+ infcx : & self . infcx ,
351
+ param_env : self . param_env ,
352
+ errors : vec ! [ ] ,
353
+ } ;
354
+ let _ = c. relate ( def_self_ty, ty) ;
355
+ for error in c. errors {
356
+ if let TypeError :: Sorts ( error) = error {
357
+ param_found. insert ( error. expected , error. found ) ;
358
+ }
359
+ }
360
+ c. errors = vec ! [ ] ;
361
+ let _ = c. relate ( def_self_ty, expected) ;
362
+ for error in c. errors {
363
+ if let TypeError :: Sorts ( error) = error {
364
+ param_expected. insert ( error. expected , error. found ) ;
365
+ }
366
+ }
367
+ for ( param, ( arg, arg_ty) ) in param_args. iter ( ) {
368
+ let Some ( expected) = param_expected. get ( param) else { continue ; } ;
369
+ let Some ( found) = param_found. get ( param) else { continue ; } ;
370
+ if self . can_eq ( self . param_env , * arg_ty, * found) . is_err ( ) { continue ; }
371
+ self . emit_coerce_suggestions ( err, arg, * found, * expected, None , None ) ;
372
+ }
373
+
374
+ let ty = eraser. fold_ty ( ty) ;
375
+ if ty. references_error ( ) {
376
+ break ;
377
+ }
378
+ if ty != prev
379
+ && param_args. is_empty ( )
380
+ && self . can_eq ( self . param_env , ty, found) . is_ok ( )
381
+ {
382
+ // We only point at the first place where the found type was inferred.
383
+ err. span_label (
384
+ segment. ident . span ,
385
+ with_forced_trimmed_paths ! ( format!(
386
+ "here the type of `{ident}` is inferred to be `{ty}`" ,
387
+ ) ) ,
388
+ ) ;
389
+ break ;
390
+ } else if !param_args. is_empty ( ) {
391
+ break ;
392
+ }
393
+ prev = ty;
394
+ } else {
395
+ let ty = eraser. fold_ty ( self . node_ty ( binding. hir_id ) ) ;
396
+ if ty. references_error ( ) {
397
+ break ;
398
+ }
399
+ if ty != prev
400
+ && let Some ( span) = prev_span
401
+ && self . can_eq ( self . param_env , ty, found) . is_ok ( )
402
+ {
403
+ // We only point at the first place where the found type was inferred.
404
+ // We use the *previous* span because if the type is known *here* it means
405
+ // it was *evaluated earlier*. We don't do this for method calls because we
406
+ // evaluate the method's self type eagerly, but not in any other case.
407
+ err. span_label (
408
+ span,
409
+ with_forced_trimmed_paths ! ( format!(
410
+ "here the type of `{ident}` is inferred to be `{ty}`" ,
411
+ ) ) ,
412
+ ) ;
413
+ break ;
414
+ }
415
+ prev = ty;
416
+ }
417
+ if binding. hir_id == expr. hir_id {
418
+ // Do not look at expressions that come after the expression we were originally
419
+ // evaluating and had a type error.
420
+ break ;
421
+ }
422
+ prev_span = Some ( binding. span ) ;
423
+ }
424
+ true
425
+ }
426
+
208
427
fn annotate_expected_due_to_let_ty (
209
428
& self ,
210
429
err : & mut Diagnostic ,
0 commit comments