@@ -563,157 +563,159 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
563563 let hir = self . tcx . hir ( ) ;
564564 let parent_node = hir. get_parent_node ( obligation. cause . body_id ) ;
565565 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+ ..
568569 } ) ) = node
569570 {
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 ( ) )
584584 }
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+ } ;
585589
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+ } ;
603595
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) ;
621624 }
622625 }
623626 }
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- }
631627 }
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+ }
632636
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 ( (
662695 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 ,
670702 ) ;
671- err. note ( impl_trait_msg) ;
672703 } 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- }
705704 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" ,
708706 trait_obj,
709707 ) ) ;
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" ) ;
713708 }
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" ) ;
715717 }
716- false
718+ true
717719 }
718720
719721 crate fn point_at_returns_when_relevant (
@@ -1686,6 +1688,8 @@ pub fn suggest_constraining_type_param(
16861688 false
16871689}
16881690
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.
16891693struct ReturnsVisitor < ' v > ( Vec < & ' v hir:: Expr < ' v > > ) ;
16901694
16911695impl < ' v > Visitor < ' v > for ReturnsVisitor < ' v > {
0 commit comments