1
+ use crate :: traits:: { ObligationCause , ObligationCauseCode } ;
1
2
use crate :: ty:: { self , BoundRegion , Region , Ty , TyCtxt } ;
2
3
use rustc_ast:: ast;
3
- use rustc_errors:: { pluralize, Applicability , DiagnosticBuilder } ;
4
+ use rustc_errors:: Applicability :: { MachineApplicable , MaybeIncorrect } ;
5
+ use rustc_errors:: { pluralize, DiagnosticBuilder } ;
4
6
use rustc_hir as hir;
5
7
use rustc_hir:: def_id:: DefId ;
6
8
use rustc_span:: symbol:: sym;
7
- use rustc_span:: { BytePos , Span } ;
9
+ use rustc_span:: { BytePos , MultiSpan , Span } ;
8
10
use rustc_target:: spec:: abi;
9
11
10
12
use std:: borrow:: Cow ;
@@ -332,6 +334,7 @@ impl<'tcx> TyCtxt<'tcx> {
332
334
self ,
333
335
db : & mut DiagnosticBuilder < ' _ > ,
334
336
err : & TypeError < ' tcx > ,
337
+ cause : & ObligationCause < ' tcx > ,
335
338
sp : Span ,
336
339
body_owner_def_id : DefId ,
337
340
) {
@@ -370,7 +373,7 @@ impl<'tcx> TyCtxt<'tcx> {
370
373
sp,
371
374
"use a float literal" ,
372
375
format ! ( "{}.0" , snippet) ,
373
- Applicability :: MachineApplicable ,
376
+ MachineApplicable ,
374
377
) ;
375
378
}
376
379
}
@@ -451,41 +454,27 @@ impl<T> Trait<T> for X {
451
454
db. span_label ( p_span, "this type parameter" ) ;
452
455
}
453
456
}
454
- ( ty:: Projection ( _) , _) => {
455
- db. note ( & format ! (
456
- "consider constraining the associated type `{}` to `{}` or calling a \
457
- method that returns `{0}`",
458
- values. expected, values. found,
459
- ) ) ;
460
- if self . sess . teach ( & db. get_code ( ) . unwrap ( ) ) {
461
- db. help (
462
- "given an associated type `T` and a method `foo`:
463
- ```
464
- trait Trait {
465
- type T;
466
- fn foo(&self) -> Self::T;
467
- }
468
- ```
469
- the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
470
- ```
471
- impl Trait for X {
472
- type T = String;
473
- fn foo(&self) -> Self::T { String::new() }
474
- }
475
- ```" ,
476
- ) ;
477
- }
478
- db. note (
479
- "for more information, visit \
480
- https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
457
+ ( ty:: Projection ( proj_ty) , _) => {
458
+ self . expected_projection (
459
+ db,
460
+ proj_ty,
461
+ values,
462
+ body_owner_def_id,
463
+ & cause. code ,
481
464
) ;
482
465
}
483
466
( _, ty:: Projection ( proj_ty) ) => {
484
467
let msg = format ! (
485
468
"consider constraining the associated type `{}` to `{}`" ,
486
469
values. found, values. expected,
487
470
) ;
488
- if !self . suggest_constraint ( db, & msg, body_owner_def_id, proj_ty, values) {
471
+ if !self . suggest_constraint (
472
+ db,
473
+ & msg,
474
+ body_owner_def_id,
475
+ proj_ty,
476
+ values. expected ,
477
+ ) {
489
478
db. help ( & msg) ;
490
479
db. note (
491
480
"for more information, visit \
@@ -533,7 +522,7 @@ impl Trait for X {
533
522
msg : & str ,
534
523
body_owner_def_id : DefId ,
535
524
proj_ty : & ty:: ProjectionTy < ' tcx > ,
536
- values : & ExpectedFound < Ty < ' tcx > > ,
525
+ ty : Ty < ' tcx > ,
537
526
) -> bool {
538
527
let assoc = self . associated_item ( proj_ty. item_def_id ) ;
539
528
let trait_ref = proj_ty. trait_ref ( * self ) ;
@@ -570,7 +559,7 @@ impl Trait for X {
570
559
& trait_ref,
571
560
pred. bounds ,
572
561
& assoc,
573
- values ,
562
+ ty ,
574
563
msg,
575
564
) {
576
565
return true ;
@@ -587,7 +576,7 @@ impl Trait for X {
587
576
& trait_ref,
588
577
param. bounds ,
589
578
& assoc,
590
- values ,
579
+ ty ,
591
580
msg,
592
581
) ;
593
582
}
@@ -597,15 +586,145 @@ impl Trait for X {
597
586
false
598
587
}
599
588
589
+ fn expected_projection (
590
+ & self ,
591
+ db : & mut DiagnosticBuilder < ' _ > ,
592
+ proj_ty : & ty:: ProjectionTy < ' tcx > ,
593
+ values : & ExpectedFound < Ty < ' tcx > > ,
594
+ body_owner_def_id : DefId ,
595
+ cause_code : & ObligationCauseCode < ' _ > ,
596
+ ) {
597
+ let msg = format ! (
598
+ "consider constraining the associated type `{}` to `{}`" ,
599
+ values. expected, values. found
600
+ ) ;
601
+ let mut suggested = false ;
602
+ let body_owner = self . hir ( ) . get_if_local ( body_owner_def_id) ;
603
+ let current_method_ident = body_owner. and_then ( |n| n. ident ( ) ) . map ( |i| i. name ) ;
604
+
605
+ let callable_scope = match body_owner {
606
+ Some (
607
+ hir:: Node :: Item ( hir:: Item {
608
+ kind :
609
+ hir:: ItemKind :: Trait ( ..)
610
+ | hir:: ItemKind :: Impl { .. }
611
+ | hir:: ItemKind :: Const ( ..)
612
+ | hir:: ItemKind :: Enum ( ..)
613
+ | hir:: ItemKind :: Struct ( ..)
614
+ | hir:: ItemKind :: Union ( ..) ,
615
+ ..
616
+ } )
617
+ | hir:: Node :: TraitItem ( hir:: TraitItem {
618
+ kind : hir:: TraitItemKind :: Const ( ..) | hir:: TraitItemKind :: Type ( ..) ,
619
+ ..
620
+ } )
621
+ | hir:: Node :: ImplItem ( hir:: ImplItem {
622
+ kind : hir:: ImplItemKind :: Const ( ..) | hir:: ImplItemKind :: TyAlias ( ..) ,
623
+ ..
624
+ } ) ,
625
+ ) => false ,
626
+ _ => true ,
627
+ } ;
628
+ let impl_comparison =
629
+ matches ! ( cause_code, ObligationCauseCode :: CompareImplMethodObligation { .. } ) ;
630
+ if !callable_scope || impl_comparison {
631
+ // We do not want to suggest calling functions when the reason of the
632
+ // type error is a comparison of an `impl` with its `trait` or when the
633
+ // scope is outside of a `Body`.
634
+ } else {
635
+ let assoc = self . associated_item ( proj_ty. item_def_id ) ;
636
+ let items = self . associated_items ( assoc. container . id ( ) ) ;
637
+ // Find all the methods in the trait that could be called to construct the
638
+ // expected associated type.
639
+ let methods: Vec < ( Span , String ) > = items
640
+ . items
641
+ . iter ( )
642
+ . filter ( |( name, item) | {
643
+ ty:: AssocKind :: Method == item. kind && Some ( * * name) != current_method_ident
644
+ } )
645
+ . filter_map ( |( _, item) | {
646
+ let method = self . fn_sig ( item. def_id ) ;
647
+ match method. output ( ) . skip_binder ( ) . kind {
648
+ ty:: Projection ( ty:: ProjectionTy { item_def_id, .. } )
649
+ if item_def_id == proj_ty. item_def_id =>
650
+ {
651
+ Some ( (
652
+ self . sess . source_map ( ) . guess_head_span ( self . def_span ( item. def_id ) ) ,
653
+ format ! ( "consider calling `{}`" , self . def_path_str( item. def_id) ) ,
654
+ ) )
655
+ }
656
+ _ => None ,
657
+ }
658
+ } )
659
+ . collect ( ) ;
660
+ if !methods. is_empty ( ) {
661
+ // Use a single `help:` to show all the methods in the trait that can
662
+ // be used to construct the expected associated type.
663
+ let mut span: MultiSpan =
664
+ methods. iter ( ) . map ( |( sp, _) | * sp) . collect :: < Vec < Span > > ( ) . into ( ) ;
665
+ let msg = format ! (
666
+ "{some} method{s} {are} available that return{r} `{ty}`" ,
667
+ some = if methods. len( ) == 1 { "a" } else { "some" } ,
668
+ s = pluralize!( methods. len( ) ) ,
669
+ are = if methods. len( ) == 1 { "is" } else { "are" } ,
670
+ r = if methods. len( ) == 1 { "s" } else { "" } ,
671
+ ty = values. expected
672
+ ) ;
673
+ for ( sp, label) in methods. into_iter ( ) {
674
+ span. push_span_label ( sp, label) ;
675
+ }
676
+ db. span_help ( span, & msg) ;
677
+ suggested = true ;
678
+ }
679
+ // Possibly suggest constraining the associated type to conform to the
680
+ // found type.
681
+ suggested |=
682
+ self . suggest_constraint ( db, & msg, body_owner_def_id, proj_ty, values. found ) ;
683
+ }
684
+ if !suggested && !impl_comparison {
685
+ // Generic suggestion when we can't be more specific.
686
+ if callable_scope {
687
+ db. help (
688
+ & format ! ( "{} or calling a method that returns `{}`" , msg, values. expected, ) ,
689
+ ) ;
690
+ } else {
691
+ db. help ( & msg) ;
692
+ }
693
+ db. note (
694
+ "for more information, visit \
695
+ https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
696
+ ) ;
697
+ }
698
+ if self . sess . teach ( & db. get_code ( ) . unwrap ( ) ) {
699
+ db. help (
700
+ "given an associated type `T` and a method `foo`:
701
+ ```
702
+ trait Trait {
703
+ type T;
704
+ fn foo(&self) -> Self::T;
705
+ }
706
+ ```
707
+ the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
708
+ ```
709
+ impl Trait for X {
710
+ type T = String;
711
+ fn foo(&self) -> Self::T { String::new() }
712
+ }
713
+ ```" ,
714
+ ) ;
715
+ }
716
+ }
717
+
600
718
fn constrain_associated_type_structured_suggestion (
601
719
& self ,
602
720
db : & mut DiagnosticBuilder < ' _ > ,
603
721
trait_ref : & ty:: TraitRef < ' tcx > ,
604
722
bounds : hir:: GenericBounds < ' _ > ,
605
723
assoc : & ty:: AssocItem ,
606
- values : & ExpectedFound < Ty < ' tcx > > ,
724
+ ty : Ty < ' tcx > ,
607
725
msg : & str ,
608
726
) -> bool {
727
+ // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
609
728
for bound in bounds {
610
729
match bound {
611
730
hir:: GenericBound :: Trait ( ptr, hir:: TraitBoundModifier :: None ) => {
@@ -620,14 +739,11 @@ impl Trait for X {
620
739
let ( span, sugg) = if has_params {
621
740
let pos = ptr. span . hi ( ) - BytePos ( 1 ) ;
622
741
let span = Span :: new ( pos, pos, ptr. span . ctxt ( ) ) ;
623
- ( span, format ! ( ", {} = {}" , assoc. ident, values . expected ) )
742
+ ( span, format ! ( ", {} = {}" , assoc. ident, ty ) )
624
743
} else {
625
- (
626
- ptr. span . shrink_to_hi ( ) ,
627
- format ! ( "<{} = {}>" , assoc. ident, values. expected) ,
628
- )
744
+ ( ptr. span . shrink_to_hi ( ) , format ! ( "<{} = {}>" , assoc. ident, ty) )
629
745
} ;
630
- db. span_suggestion ( span, msg, sugg, Applicability :: MaybeIncorrect ) ;
746
+ db. span_suggestion_verbose ( span, msg, sugg, MaybeIncorrect ) ;
631
747
return true ;
632
748
}
633
749
}
0 commit comments