@@ -2,7 +2,9 @@ use super::potentially_plural_count;
2
2
use crate :: errors:: LifetimesOrBoundsMismatchOnTrait ;
3
3
use hir:: def_id:: { DefId , LocalDefId } ;
4
4
use rustc_data_structures:: fx:: { FxHashMap , FxIndexSet } ;
5
- use rustc_errors:: { pluralize, struct_span_err, Applicability , DiagnosticId , ErrorGuaranteed } ;
5
+ use rustc_errors:: {
6
+ pluralize, struct_span_err, Applicability , DiagnosticId , ErrorGuaranteed , MultiSpan ,
7
+ } ;
6
8
use rustc_hir as hir;
7
9
use rustc_hir:: def:: { DefKind , Res } ;
8
10
use rustc_hir:: intravisit;
@@ -320,15 +322,6 @@ fn compare_method_predicate_entailment<'tcx>(
320
322
ty:: Binder :: dummy ( ty:: PredicateKind :: WellFormed ( unnormalized_impl_fty. into ( ) ) ) ,
321
323
) ) ;
322
324
}
323
- let emit_implied_wf_lint = || {
324
- infcx. tcx . struct_span_lint_hir (
325
- rustc_session:: lint:: builtin:: IMPLIED_BOUNDS_ENTAILMENT ,
326
- impl_m_hir_id,
327
- infcx. tcx . def_span ( impl_m. def_id ) ,
328
- "impl method assumes more implied bounds than the corresponding trait method" ,
329
- |lint| lint,
330
- ) ;
331
- } ;
332
325
333
326
// Check that all obligations are satisfied by the implementation's
334
327
// version.
@@ -346,7 +339,7 @@ fn compare_method_predicate_entailment<'tcx>(
346
339
)
347
340
. map ( |( ) | {
348
341
// If the skip-mode was successful, emit a lint.
349
- emit_implied_wf_lint ( ) ;
342
+ emit_implied_wf_lint ( infcx . tcx , impl_m , impl_m_hir_id , vec ! [ ] ) ;
350
343
} ) ;
351
344
}
352
345
CheckImpliedWfMode :: Skip => {
@@ -382,8 +375,16 @@ fn compare_method_predicate_entailment<'tcx>(
382
375
CheckImpliedWfMode :: Skip ,
383
376
)
384
377
. map ( |( ) | {
378
+ let bad_args = extract_bad_args_for_implies_lint (
379
+ tcx,
380
+ & errors,
381
+ ( trait_m, trait_sig) ,
382
+ // Unnormalized impl sig corresponds to the HIR types written
383
+ ( impl_m, unnormalized_impl_sig) ,
384
+ impl_m_hir_id,
385
+ ) ;
385
386
// If the skip-mode was successful, emit a lint.
386
- emit_implied_wf_lint ( ) ;
387
+ emit_implied_wf_lint ( tcx , impl_m , impl_m_hir_id , bad_args ) ;
387
388
} ) ;
388
389
}
389
390
CheckImpliedWfMode :: Skip => {
@@ -400,6 +401,141 @@ fn compare_method_predicate_entailment<'tcx>(
400
401
Ok ( ( ) )
401
402
}
402
403
404
+ fn extract_bad_args_for_implies_lint < ' tcx > (
405
+ tcx : TyCtxt < ' tcx > ,
406
+ errors : & [ infer:: RegionResolutionError < ' tcx > ] ,
407
+ ( trait_m, trait_sig) : ( & ty:: AssocItem , ty:: FnSig < ' tcx > ) ,
408
+ ( impl_m, impl_sig) : ( & ty:: AssocItem , ty:: FnSig < ' tcx > ) ,
409
+ hir_id : hir:: HirId ,
410
+ ) -> Vec < ( Span , Option < String > ) > {
411
+ let mut blame_generics = vec ! [ ] ;
412
+ for error in errors {
413
+ // Look for the subregion origin that contains an input/output type
414
+ let origin = match error {
415
+ infer:: RegionResolutionError :: ConcreteFailure ( o, ..) => o,
416
+ infer:: RegionResolutionError :: GenericBoundFailure ( o, ..) => o,
417
+ infer:: RegionResolutionError :: SubSupConflict ( _, _, o, ..) => o,
418
+ infer:: RegionResolutionError :: UpperBoundUniverseConflict ( .., o, _) => o,
419
+ } ;
420
+ // Extract (possible) input/output types from origin
421
+ match origin {
422
+ infer:: SubregionOrigin :: Subtype ( trace) => {
423
+ if let Some ( ( a, b) ) = trace. values . ty ( ) {
424
+ blame_generics. extend ( [ a, b] ) ;
425
+ }
426
+ }
427
+ infer:: SubregionOrigin :: RelateParamBound ( _, ty, _) => blame_generics. push ( * ty) ,
428
+ infer:: SubregionOrigin :: ReferenceOutlivesReferent ( ty, _) => blame_generics. push ( * ty) ,
429
+ _ => { }
430
+ }
431
+ }
432
+
433
+ let fn_decl = tcx. hir ( ) . fn_decl_by_hir_id ( hir_id) . unwrap ( ) ;
434
+ let opt_ret_ty = match fn_decl. output {
435
+ hir:: FnRetTy :: DefaultReturn ( _) => None ,
436
+ hir:: FnRetTy :: Return ( ty) => Some ( ty) ,
437
+ } ;
438
+
439
+ // Map late-bound regions from trait to impl, so the names are right.
440
+ let mapping = std:: iter:: zip (
441
+ tcx. fn_sig ( trait_m. def_id ) . bound_vars ( ) ,
442
+ tcx. fn_sig ( impl_m. def_id ) . bound_vars ( ) ,
443
+ )
444
+ . filter_map ( |( impl_bv, trait_bv) | {
445
+ if let ty:: BoundVariableKind :: Region ( impl_bv) = impl_bv
446
+ && let ty:: BoundVariableKind :: Region ( trait_bv) = trait_bv
447
+ {
448
+ Some ( ( impl_bv, trait_bv) )
449
+ } else {
450
+ None
451
+ }
452
+ } )
453
+ . collect ( ) ;
454
+
455
+ // For each arg, see if it was in the "blame" of any of the region errors.
456
+ // If so, then try to produce a suggestion to replace the argument type with
457
+ // one from the trait.
458
+ let mut bad_args = vec ! [ ] ;
459
+ for ( idx, ( ty, hir_ty) ) in
460
+ std:: iter:: zip ( impl_sig. inputs_and_output , fn_decl. inputs . iter ( ) . chain ( opt_ret_ty) )
461
+ . enumerate ( )
462
+ {
463
+ let expected_ty = trait_sig. inputs_and_output [ idx]
464
+ . fold_with ( & mut RemapLateBound { tcx, mapping : & mapping } ) ;
465
+ if blame_generics. iter ( ) . any ( |blame| ty. contains ( * blame) ) {
466
+ let expected_ty_sugg = expected_ty. to_string ( ) ;
467
+ bad_args. push ( (
468
+ hir_ty. span ,
469
+ // Only suggest something if it actually changed.
470
+ ( expected_ty_sugg != ty. to_string ( ) ) . then_some ( expected_ty_sugg) ,
471
+ ) ) ;
472
+ }
473
+ }
474
+
475
+ bad_args
476
+ }
477
+
478
+ struct RemapLateBound < ' a , ' tcx > {
479
+ tcx : TyCtxt < ' tcx > ,
480
+ mapping : & ' a FxHashMap < ty:: BoundRegionKind , ty:: BoundRegionKind > ,
481
+ }
482
+
483
+ impl < ' tcx > TypeFolder < ' tcx > for RemapLateBound < ' _ , ' tcx > {
484
+ fn tcx ( & self ) -> TyCtxt < ' tcx > {
485
+ self . tcx
486
+ }
487
+
488
+ fn fold_region ( & mut self , r : ty:: Region < ' tcx > ) -> ty:: Region < ' tcx > {
489
+ if let ty:: ReFree ( fr) = * r {
490
+ self . tcx . mk_region ( ty:: ReFree ( ty:: FreeRegion {
491
+ bound_region : self
492
+ . mapping
493
+ . get ( & fr. bound_region )
494
+ . copied ( )
495
+ . unwrap_or ( fr. bound_region ) ,
496
+ ..fr
497
+ } ) )
498
+ } else {
499
+ r
500
+ }
501
+ }
502
+ }
503
+
504
+ fn emit_implied_wf_lint < ' tcx > (
505
+ tcx : TyCtxt < ' tcx > ,
506
+ impl_m : & ty:: AssocItem ,
507
+ hir_id : hir:: HirId ,
508
+ bad_args : Vec < ( Span , Option < String > ) > ,
509
+ ) {
510
+ let span: MultiSpan = if bad_args. is_empty ( ) {
511
+ tcx. def_span ( impl_m. def_id ) . into ( )
512
+ } else {
513
+ bad_args. iter ( ) . map ( |( span, _) | * span) . collect :: < Vec < _ > > ( ) . into ( )
514
+ } ;
515
+ tcx. struct_span_lint_hir (
516
+ rustc_session:: lint:: builtin:: IMPLIED_BOUNDS_ENTAILMENT ,
517
+ hir_id,
518
+ span,
519
+ "impl method assumes more implied bounds than the corresponding trait method" ,
520
+ |lint| {
521
+ let bad_args: Vec < _ > =
522
+ bad_args. into_iter ( ) . filter_map ( |( span, sugg) | Some ( ( span, sugg?) ) ) . collect ( ) ;
523
+ if !bad_args. is_empty ( ) {
524
+ lint. multipart_suggestion (
525
+ format ! (
526
+ "replace {} type{} to make the impl signature compatible" ,
527
+ pluralize!( "this" , bad_args. len( ) ) ,
528
+ pluralize!( bad_args. len( ) )
529
+ ) ,
530
+ bad_args,
531
+ Applicability :: MaybeIncorrect ,
532
+ ) ;
533
+ }
534
+ lint
535
+ } ,
536
+ ) ;
537
+ }
538
+
403
539
#[ derive( Debug , PartialEq , Eq ) ]
404
540
enum CheckImpliedWfMode {
405
541
/// Checks implied well-formedness of the impl method. If it fails, we will
0 commit comments