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
- use rustc_span:: { BytePos , Span } ;
8
+ use rustc_span:: { BytePos , MultiSpan , Span } ;
7
9
use rustc_target:: spec:: abi;
8
10
9
11
use std:: borrow:: Cow ;
@@ -323,6 +325,7 @@ impl<'tcx> TyCtxt<'tcx> {
323
325
self ,
324
326
db : & mut DiagnosticBuilder < ' _ > ,
325
327
err : & TypeError < ' tcx > ,
328
+ cause : & ObligationCause < ' tcx > ,
326
329
sp : Span ,
327
330
body_owner_def_id : DefId ,
328
331
) {
@@ -361,7 +364,7 @@ impl<'tcx> TyCtxt<'tcx> {
361
364
sp,
362
365
"use a float literal" ,
363
366
format ! ( "{}.0" , snippet) ,
364
- Applicability :: MachineApplicable ,
367
+ MachineApplicable ,
365
368
) ;
366
369
}
367
370
}
@@ -442,41 +445,27 @@ impl<T> Trait<T> for X {
442
445
db. span_label ( p_span, "this type parameter" ) ;
443
446
}
444
447
}
445
- ( ty:: Projection ( _) , _) => {
446
- db. note ( & format ! (
447
- "consider constraining the associated type `{}` to `{}` or calling a \
448
- method that returns `{0}`",
449
- values. expected, values. found,
450
- ) ) ;
451
- if self . sess . teach ( & db. get_code ( ) . unwrap ( ) ) {
452
- db. help (
453
- "given an associated type `T` and a method `foo`:
454
- ```
455
- trait Trait {
456
- type T;
457
- fn foo(&self) -> Self::T;
458
- }
459
- ```
460
- the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
461
- ```
462
- impl Trait for X {
463
- type T = String;
464
- fn foo(&self) -> Self::T { String::new() }
465
- }
466
- ```" ,
467
- ) ;
468
- }
469
- db. note (
470
- "for more information, visit \
471
- https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
448
+ ( ty:: Projection ( proj_ty) , _) => {
449
+ self . expected_projection (
450
+ db,
451
+ proj_ty,
452
+ values,
453
+ body_owner_def_id,
454
+ & cause. code ,
472
455
) ;
473
456
}
474
457
( _, ty:: Projection ( proj_ty) ) => {
475
458
let msg = format ! (
476
459
"consider constraining the associated type `{}` to `{}`" ,
477
460
values. found, values. expected,
478
461
) ;
479
- if !self . suggest_constraint ( db, & msg, body_owner_def_id, proj_ty, values) {
462
+ if !self . suggest_constraint (
463
+ db,
464
+ & msg,
465
+ body_owner_def_id,
466
+ proj_ty,
467
+ values. expected ,
468
+ ) {
480
469
db. help ( & msg) ;
481
470
db. note (
482
471
"for more information, visit \
@@ -512,7 +501,7 @@ impl Trait for X {
512
501
msg : & str ,
513
502
body_owner_def_id : DefId ,
514
503
proj_ty : & ty:: ProjectionTy < ' tcx > ,
515
- values : & ExpectedFound < Ty < ' tcx > > ,
504
+ ty : Ty < ' tcx > ,
516
505
) -> bool {
517
506
let assoc = self . associated_item ( proj_ty. item_def_id ) ;
518
507
let trait_ref = proj_ty. trait_ref ( * self ) ;
@@ -549,7 +538,7 @@ impl Trait for X {
549
538
& trait_ref,
550
539
pred. bounds ,
551
540
& assoc,
552
- values ,
541
+ ty ,
553
542
msg,
554
543
) {
555
544
return true ;
@@ -566,7 +555,7 @@ impl Trait for X {
566
555
& trait_ref,
567
556
param. bounds ,
568
557
& assoc,
569
- values ,
558
+ ty ,
570
559
msg,
571
560
) ;
572
561
}
@@ -576,15 +565,145 @@ impl Trait for X {
576
565
false
577
566
}
578
567
568
+ fn expected_projection (
569
+ & self ,
570
+ db : & mut DiagnosticBuilder < ' _ > ,
571
+ proj_ty : & ty:: ProjectionTy < ' tcx > ,
572
+ values : & ExpectedFound < Ty < ' tcx > > ,
573
+ body_owner_def_id : DefId ,
574
+ cause_code : & ObligationCauseCode < ' _ > ,
575
+ ) {
576
+ let msg = format ! (
577
+ "consider constraining the associated type `{}` to `{}`" ,
578
+ values. expected, values. found
579
+ ) ;
580
+ let mut suggested = false ;
581
+ let body_owner = self . hir ( ) . get_if_local ( body_owner_def_id) ;
582
+ let current_method_ident = body_owner. and_then ( |n| n. ident ( ) ) . map ( |i| i. name ) ;
583
+
584
+ let callable_scope = match body_owner {
585
+ Some (
586
+ hir:: Node :: Item ( hir:: Item {
587
+ kind :
588
+ hir:: ItemKind :: Trait ( ..)
589
+ | hir:: ItemKind :: Impl { .. }
590
+ | hir:: ItemKind :: Const ( ..)
591
+ | hir:: ItemKind :: Enum ( ..)
592
+ | hir:: ItemKind :: Struct ( ..)
593
+ | hir:: ItemKind :: Union ( ..) ,
594
+ ..
595
+ } )
596
+ | hir:: Node :: TraitItem ( hir:: TraitItem {
597
+ kind : hir:: TraitItemKind :: Const ( ..) | hir:: TraitItemKind :: Type ( ..) ,
598
+ ..
599
+ } )
600
+ | hir:: Node :: ImplItem ( hir:: ImplItem {
601
+ kind : hir:: ImplItemKind :: Const ( ..) | hir:: ImplItemKind :: TyAlias ( ..) ,
602
+ ..
603
+ } ) ,
604
+ ) => false ,
605
+ _ => true ,
606
+ } ;
607
+ let impl_comparison =
608
+ matches ! ( cause_code, ObligationCauseCode :: CompareImplMethodObligation { .. } ) ;
609
+ if !callable_scope || impl_comparison {
610
+ // We do not want to suggest calling functions when the reason of the
611
+ // type error is a comparison of an `impl` with its `trait` or when the
612
+ // scope is outside of a `Body`.
613
+ } else {
614
+ let assoc = self . associated_item ( proj_ty. item_def_id ) ;
615
+ let items = self . associated_items ( assoc. container . id ( ) ) ;
616
+ // Find all the methods in the trait that could be called to construct the
617
+ // expected associated type.
618
+ let methods: Vec < ( Span , String ) > = items
619
+ . items
620
+ . iter ( )
621
+ . filter ( |( name, item) | {
622
+ ty:: AssocKind :: Method == item. kind && Some ( * * name) != current_method_ident
623
+ } )
624
+ . filter_map ( |( _, item) | {
625
+ let method = self . fn_sig ( item. def_id ) ;
626
+ match method. output ( ) . skip_binder ( ) . kind {
627
+ ty:: Projection ( ty:: ProjectionTy { item_def_id, .. } )
628
+ if item_def_id == proj_ty. item_def_id =>
629
+ {
630
+ Some ( (
631
+ self . sess . source_map ( ) . guess_head_span ( self . def_span ( item. def_id ) ) ,
632
+ format ! ( "consider calling `{}`" , self . def_path_str( item. def_id) ) ,
633
+ ) )
634
+ }
635
+ _ => None ,
636
+ }
637
+ } )
638
+ . collect ( ) ;
639
+ if !methods. is_empty ( ) {
640
+ // Use a single `help:` to show all the methods in the trait that can
641
+ // be used to construct the expected associated type.
642
+ let mut span: MultiSpan =
643
+ methods. iter ( ) . map ( |( sp, _) | * sp) . collect :: < Vec < Span > > ( ) . into ( ) ;
644
+ let msg = format ! (
645
+ "{some} method{s} {are} available that return{r} `{ty}`" ,
646
+ some = if methods. len( ) == 1 { "a" } else { "some" } ,
647
+ s = pluralize!( methods. len( ) ) ,
648
+ are = if methods. len( ) == 1 { "is" } else { "are" } ,
649
+ r = if methods. len( ) == 1 { "s" } else { "" } ,
650
+ ty = values. expected
651
+ ) ;
652
+ for ( sp, label) in methods. into_iter ( ) {
653
+ span. push_span_label ( sp, label) ;
654
+ }
655
+ db. span_help ( span, & msg) ;
656
+ suggested = true ;
657
+ }
658
+ // Possibly suggest constraining the associated type to conform to the
659
+ // found type.
660
+ suggested |=
661
+ self . suggest_constraint ( db, & msg, body_owner_def_id, proj_ty, values. found ) ;
662
+ }
663
+ if !suggested && !impl_comparison {
664
+ // Generic suggestion when we can't be more specific.
665
+ if callable_scope {
666
+ db. help (
667
+ & format ! ( "{} or calling a method that returns `{}`" , msg, values. expected, ) ,
668
+ ) ;
669
+ } else {
670
+ db. help ( & msg) ;
671
+ }
672
+ db. note (
673
+ "for more information, visit \
674
+ https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
675
+ ) ;
676
+ }
677
+ if self . sess . teach ( & db. get_code ( ) . unwrap ( ) ) {
678
+ db. help (
679
+ "given an associated type `T` and a method `foo`:
680
+ ```
681
+ trait Trait {
682
+ type T;
683
+ fn foo(&self) -> Self::T;
684
+ }
685
+ ```
686
+ the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
687
+ ```
688
+ impl Trait for X {
689
+ type T = String;
690
+ fn foo(&self) -> Self::T { String::new() }
691
+ }
692
+ ```" ,
693
+ ) ;
694
+ }
695
+ }
696
+
579
697
fn constrain_associated_type_structured_suggestion (
580
698
& self ,
581
699
db : & mut DiagnosticBuilder < ' _ > ,
582
700
trait_ref : & ty:: TraitRef < ' tcx > ,
583
701
bounds : hir:: GenericBounds < ' _ > ,
584
702
assoc : & ty:: AssocItem ,
585
- values : & ExpectedFound < Ty < ' tcx > > ,
703
+ ty : Ty < ' tcx > ,
586
704
msg : & str ,
587
705
) -> bool {
706
+ // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
588
707
for bound in bounds {
589
708
match bound {
590
709
hir:: GenericBound :: Trait ( ptr, hir:: TraitBoundModifier :: None ) => {
@@ -599,14 +718,11 @@ impl Trait for X {
599
718
let ( span, sugg) = if has_params {
600
719
let pos = ptr. span . hi ( ) - BytePos ( 1 ) ;
601
720
let span = Span :: new ( pos, pos, ptr. span . ctxt ( ) ) ;
602
- ( span, format ! ( ", {} = {}" , assoc. ident, values . expected ) )
721
+ ( span, format ! ( ", {} = {}" , assoc. ident, ty ) )
603
722
} else {
604
- (
605
- ptr. span . shrink_to_hi ( ) ,
606
- format ! ( "<{} = {}>" , assoc. ident, values. expected) ,
607
- )
723
+ ( ptr. span . shrink_to_hi ( ) , format ! ( "<{} = {}>" , assoc. ident, ty) )
608
724
} ;
609
- db. span_suggestion ( span, msg, sugg, Applicability :: MaybeIncorrect ) ;
725
+ db. span_suggestion_verbose ( span, msg, sugg, MaybeIncorrect ) ;
610
726
return true ;
611
727
}
612
728
}
0 commit comments