@@ -563,157 +563,159 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
563
563
let hir = self . tcx . hir ( ) ;
564
564
let parent_node = hir. get_parent_node ( obligation. cause . body_id ) ;
565
565
let node = hir. find ( parent_node) ;
566
- if let Some ( hir:: Node :: Item ( hir:: Item {
567
- kind : hir:: ItemKind :: Fn ( sig, _, body_id) , ..
566
+ let ( sig, body_id) = if let Some ( hir:: Node :: Item ( hir:: Item {
567
+ kind : hir:: ItemKind :: Fn ( sig, _, body_id) ,
568
+ ..
568
569
} ) ) = node
569
570
{
570
- let body = hir. body ( * body_id) ;
571
- let trait_ref = self . resolve_vars_if_possible ( trait_ref) ;
572
- let ty = trait_ref. skip_binder ( ) . self_ty ( ) ;
573
- let is_object_safe;
574
- match ty. kind {
575
- ty:: Dynamic ( predicates, _) => {
576
- // The `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
577
- is_object_safe = predicates. principal_def_id ( ) . map_or ( true , |def_id| {
578
- !object_safety_violations ( self . tcx , def_id) . is_empty ( )
579
- } )
580
- }
581
- // We only want to suggest `impl Trait` to `dyn Trait`s.
582
- // For example, `fn foo() -> str` needs to be filtered out.
583
- _ => return false ,
571
+ ( sig, body_id)
572
+ } else {
573
+ return false ;
574
+ } ;
575
+ let body = hir. body ( * body_id) ;
576
+ let trait_ref = self . resolve_vars_if_possible ( trait_ref) ;
577
+ let ty = trait_ref. skip_binder ( ) . self_ty ( ) ;
578
+ let is_object_safe = match ty. kind {
579
+ ty:: Dynamic ( predicates, _) => {
580
+ // If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
581
+ predicates
582
+ . principal_def_id ( )
583
+ . map_or ( true , |def_id| object_safety_violations ( self . tcx , def_id) . is_empty ( ) )
584
584
}
585
+ // We only want to suggest `impl Trait` to `dyn Trait`s.
586
+ // For example, `fn foo() -> str` needs to be filtered out.
587
+ _ => return false ,
588
+ } ;
585
589
586
- let ret_ty = if let hir:: FunctionRetTy :: Return ( ret_ty) = sig. decl . output {
587
- ret_ty
588
- } else {
589
- return false ;
590
- } ;
591
-
592
- // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for
593
- // cases like `fn foo() -> (dyn Trait, i32) {}`.
594
- // Recursively look for `TraitObject` types and if there's only one, use that span to
595
- // suggest `impl Trait`.
596
-
597
- // Visit to make sure there's a single `return` type to suggest `impl Trait`,
598
- // otherwise suggest using `Box<dyn Trait>` or an enum.
599
- let mut visitor = ReturnsVisitor ( vec ! [ ] ) ;
600
- visitor. visit_body ( & body) ;
601
-
602
- let tables = self . in_progress_tables . map ( |t| t. borrow ( ) ) . unwrap ( ) ;
590
+ let ret_ty = if let hir:: FunctionRetTy :: Return ( ret_ty) = sig. decl . output {
591
+ ret_ty
592
+ } else {
593
+ return false ;
594
+ } ;
603
595
604
- let mut all_returns_conform_to_trait = true ;
605
- let mut all_returns_have_same_type = true ;
606
- let mut last_ty = None ;
607
- if let Some ( ty_ret_ty) = tables. node_type_opt ( ret_ty. hir_id ) {
608
- let cause = ObligationCause :: misc ( ret_ty. span , ret_ty. hir_id ) ;
609
- let param_env = ty:: ParamEnv :: empty ( ) ;
610
- if let ty:: Dynamic ( predicates, _) = & ty_ret_ty. kind {
611
- for expr in & visitor. 0 {
612
- if let Some ( returned_ty) = tables. node_type_opt ( expr. hir_id ) {
613
- all_returns_have_same_type &=
614
- Some ( returned_ty) == last_ty || last_ty. is_none ( ) ;
615
- last_ty = Some ( returned_ty) ;
616
- for predicate in predicates. iter ( ) {
617
- let pred = predicate. with_self_ty ( self . tcx , returned_ty) ;
618
- let obl = Obligation :: new ( cause. clone ( ) , param_env, pred) ;
619
- all_returns_conform_to_trait &= self . predicate_may_hold ( & obl) ;
620
- }
596
+ // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for
597
+ // cases like `fn foo() -> (dyn Trait, i32) {}`.
598
+ // Recursively look for `TraitObject` types and if there's only one, use that span to
599
+ // suggest `impl Trait`.
600
+
601
+ // Visit to make sure there's a single `return` type to suggest `impl Trait`,
602
+ // otherwise suggest using `Box<dyn Trait>` or an enum.
603
+ let mut visitor = ReturnsVisitor ( vec ! [ ] ) ;
604
+ visitor. visit_body ( & body) ;
605
+
606
+ let tables = self . in_progress_tables . map ( |t| t. borrow ( ) ) . unwrap ( ) ;
607
+
608
+ let mut all_returns_conform_to_trait = true ;
609
+ let mut all_returns_have_same_type = true ;
610
+ let mut last_ty = None ;
611
+ if let Some ( ty_ret_ty) = tables. node_type_opt ( ret_ty. hir_id ) {
612
+ let cause = ObligationCause :: misc ( ret_ty. span , ret_ty. hir_id ) ;
613
+ let param_env = ty:: ParamEnv :: empty ( ) ;
614
+ if let ty:: Dynamic ( predicates, _) = & ty_ret_ty. kind {
615
+ for expr in & visitor. 0 {
616
+ if let Some ( returned_ty) = tables. node_type_opt ( expr. hir_id ) {
617
+ all_returns_have_same_type &=
618
+ Some ( returned_ty) == last_ty || last_ty. is_none ( ) ;
619
+ last_ty = Some ( returned_ty) ;
620
+ for predicate in predicates. iter ( ) {
621
+ let pred = predicate. with_self_ty ( self . tcx , returned_ty) ;
622
+ let obl = Obligation :: new ( cause. clone ( ) , param_env, pred) ;
623
+ all_returns_conform_to_trait &= self . predicate_may_hold ( & obl) ;
621
624
}
622
625
}
623
626
}
624
- } else {
625
- // We still want to verify whether all the return types conform to each other.
626
- for expr in & visitor. 0 {
627
- let returned_ty = tables. node_type_opt ( expr. hir_id ) ;
628
- all_returns_have_same_type &= last_ty == returned_ty || last_ty. is_none ( ) ;
629
- last_ty = returned_ty;
630
- }
631
627
}
628
+ } else {
629
+ // We still want to verify whether all the return types conform to each other.
630
+ for expr in & visitor. 0 {
631
+ let returned_ty = tables. node_type_opt ( expr. hir_id ) ;
632
+ all_returns_have_same_type &= last_ty == returned_ty || last_ty. is_none ( ) ;
633
+ last_ty = returned_ty;
634
+ }
635
+ }
632
636
633
- let ( snippet, last_ty) =
634
- if let ( true , hir:: TyKind :: TraitObject ( ..) , Ok ( snippet) , true , Some ( last_ty) ) = (
635
- // Verify that we're dealing with a return `dyn Trait`
636
- ret_ty. span . overlaps ( span) ,
637
- & ret_ty. kind ,
638
- self . tcx . sess . source_map ( ) . span_to_snippet ( ret_ty. span ) ,
639
- // If any of the return types does not conform to the trait, then we can't
640
- // suggest `impl Trait` nor trait objects, it is a type mismatch error.
641
- all_returns_conform_to_trait,
642
- last_ty,
643
- ) {
644
- ( snippet, last_ty)
645
- } else {
646
- return false ;
647
- } ;
648
- err. code ( error_code ! ( E0746 ) ) ;
649
- err. set_primary_message ( "return type cannot have an unboxed trait object" ) ;
650
- err. children . clear ( ) ;
651
- let impl_trait_msg = "for information on `impl Trait`, see \
652
- <https://doc.rust-lang.org/book/ch10-02-traits.html\
653
- #returning-types-that-implement-traits>";
654
- let trait_obj_msg = "for information on trait objects, see \
655
- <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
656
- #using-trait-objects-that-allow-for-values-of-different-types>";
657
- let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
658
- let trait_obj = if has_dyn { & snippet[ 4 ..] } else { & snippet[ ..] } ;
659
- if all_returns_have_same_type {
660
- // Suggest `-> impl Trait`.
661
- err. span_suggestion (
637
+ let ( snippet, last_ty) =
638
+ if let ( true , hir:: TyKind :: TraitObject ( ..) , Ok ( snippet) , true , Some ( last_ty) ) = (
639
+ // Verify that we're dealing with a return `dyn Trait`
640
+ ret_ty. span . overlaps ( span) ,
641
+ & ret_ty. kind ,
642
+ self . tcx . sess . source_map ( ) . span_to_snippet ( ret_ty. span ) ,
643
+ // If any of the return types does not conform to the trait, then we can't
644
+ // suggest `impl Trait` nor trait objects, it is a type mismatch error.
645
+ all_returns_conform_to_trait,
646
+ last_ty,
647
+ ) {
648
+ ( snippet, last_ty)
649
+ } else {
650
+ return false ;
651
+ } ;
652
+ err. code ( error_code ! ( E0746 ) ) ;
653
+ err. set_primary_message ( "return type cannot have an unboxed trait object" ) ;
654
+ err. children . clear ( ) ;
655
+ let impl_trait_msg = "for information on `impl Trait`, see \
656
+ <https://doc.rust-lang.org/book/ch10-02-traits.html\
657
+ #returning-types-that-implement-traits>";
658
+ let trait_obj_msg = "for information on trait objects, see \
659
+ <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
660
+ #using-trait-objects-that-allow-for-values-of-different-types>";
661
+ let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
662
+ let trait_obj = if has_dyn { & snippet[ 4 ..] } else { & snippet[ ..] } ;
663
+ if all_returns_have_same_type {
664
+ // Suggest `-> impl Trait`.
665
+ err. span_suggestion (
666
+ ret_ty. span ,
667
+ & format ! (
668
+ "return `impl {1}` instead, as all return paths are of type `{}`, \
669
+ which implements `{1}`",
670
+ last_ty, trait_obj,
671
+ ) ,
672
+ format ! ( "impl {}" , trait_obj) ,
673
+ Applicability :: MachineApplicable ,
674
+ ) ;
675
+ err. note ( impl_trait_msg) ;
676
+ } else {
677
+ if is_object_safe {
678
+ // Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
679
+ // Get all the return values and collect their span and suggestion.
680
+ let mut suggestions = visitor
681
+ . 0
682
+ . iter ( )
683
+ . map ( |expr| {
684
+ (
685
+ expr. span ,
686
+ format ! (
687
+ "Box::new({})" ,
688
+ self . tcx. sess. source_map( ) . span_to_snippet( expr. span) . unwrap( )
689
+ ) ,
690
+ )
691
+ } )
692
+ . collect :: < Vec < _ > > ( ) ;
693
+ // Add the suggestion for the return type.
694
+ suggestions. push ( (
662
695
ret_ty. span ,
663
- & format ! (
664
- "return `impl {1}` instead, as all return paths are of type `{}`, \
665
- which implements `{1}`",
666
- last_ty, trait_obj,
667
- ) ,
668
- format ! ( "impl {}" , trait_obj) ,
669
- Applicability :: MachineApplicable ,
696
+ format ! ( "Box<{}{}>" , if has_dyn { "" } else { "dyn " } , snippet) ,
697
+ ) ) ;
698
+ err. multipart_suggestion (
699
+ "return a trait object instead" ,
700
+ suggestions,
701
+ Applicability :: MaybeIncorrect ,
670
702
) ;
671
- err. note ( impl_trait_msg) ;
672
703
} else {
673
- if is_object_safe {
674
- // Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
675
- // Get all the return values and collect their span and suggestion.
676
- let mut suggestions = visitor
677
- . 0
678
- . iter ( )
679
- . map ( |expr| {
680
- (
681
- expr. span ,
682
- format ! (
683
- "Box::new({})" ,
684
- self . tcx. sess. source_map( ) . span_to_snippet( expr. span) . unwrap( )
685
- ) ,
686
- )
687
- } )
688
- . collect :: < Vec < _ > > ( ) ;
689
- // Add the suggestion for the return type.
690
- suggestions. push ( (
691
- ret_ty. span ,
692
- format ! ( "Box<{}{}>" , if has_dyn { "" } else { "dyn " } , snippet) ,
693
- ) ) ;
694
- err. multipart_suggestion (
695
- "return a trait object instead" ,
696
- suggestions,
697
- Applicability :: MaybeIncorrect ,
698
- ) ;
699
- } else {
700
- err. note ( & format ! (
701
- "if trait `{}` was object safe, you could return a trait object" ,
702
- trait_obj,
703
- ) ) ;
704
- }
705
704
err. note ( & format ! (
706
- "if all the returned values were of the same type you could use \
707
- `impl {}` as the return type",
705
+ "if trait `{}` was object safe, you could return a trait object" ,
708
706
trait_obj,
709
707
) ) ;
710
- err. note ( impl_trait_msg) ;
711
- err. note ( trait_obj_msg) ;
712
- err. note ( "you can create a new `enum` with a variant for each returned type" ) ;
713
708
}
714
- return true ;
709
+ err. note ( trait_obj_msg) ;
710
+ err. note ( & format ! (
711
+ "if all the returned values were of the same type you could use \
712
+ `impl {}` as the return type",
713
+ trait_obj,
714
+ ) ) ;
715
+ err. note ( impl_trait_msg) ;
716
+ err. note ( "you can create a new `enum` with a variant for each returned type" ) ;
715
717
}
716
- false
718
+ true
717
719
}
718
720
719
721
crate fn point_at_returns_when_relevant (
@@ -1686,6 +1688,8 @@ pub fn suggest_constraining_type_param(
1686
1688
false
1687
1689
}
1688
1690
1691
+ /// Collect all the returned expressions within the input expression.
1692
+ /// Used to point at the return spans when we want to suggest some change to them.
1689
1693
struct ReturnsVisitor < ' v > ( Vec < & ' v hir:: Expr < ' v > > ) ;
1690
1694
1691
1695
impl < ' v > Visitor < ' v > for ReturnsVisitor < ' v > {
0 commit comments