1
+ // ignore-tidy-filelength
1
2
use super :: {
2
3
ConstEvalFailure , EvaluationResult , FulfillmentError , FulfillmentErrorCode ,
3
4
MismatchedProjectionTypes , ObjectSafetyViolation , Obligation , ObligationCause ,
@@ -22,9 +23,12 @@ use crate::ty::TypeckTables;
22
23
use crate :: ty:: { self , AdtKind , DefIdTree , ToPolyTraitRef , ToPredicate , Ty , TyCtxt , TypeFoldable } ;
23
24
24
25
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
25
- use rustc_errors:: { pluralize, struct_span_err, Applicability , DiagnosticBuilder , Style } ;
26
+ use rustc_errors:: {
27
+ error_code, pluralize, struct_span_err, Applicability , DiagnosticBuilder , Style ,
28
+ } ;
26
29
use rustc_hir as hir;
27
30
use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
31
+ use rustc_hir:: intravisit:: Visitor ;
28
32
use rustc_hir:: Node ;
29
33
use rustc_span:: source_map:: SourceMap ;
30
34
use rustc_span:: symbol:: { kw, sym} ;
@@ -758,7 +762,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
758
762
) ) ,
759
763
Some (
760
764
"the question mark operation (`?`) implicitly performs a \
761
- conversion on the error value using the `From` trait"
765
+ conversion on the error value using the `From` trait"
762
766
. to_owned ( ) ,
763
767
) ,
764
768
)
@@ -835,6 +839,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
835
839
self . suggest_remove_reference ( & obligation, & mut err, & trait_ref) ;
836
840
self . suggest_semicolon_removal ( & obligation, & mut err, span, & trait_ref) ;
837
841
self . note_version_mismatch ( & mut err, & trait_ref) ;
842
+ if self . suggest_impl_trait ( & mut err, span, & obligation, & trait_ref) {
843
+ err. emit ( ) ;
844
+ return ;
845
+ }
838
846
839
847
// Try to report a help message
840
848
if !trait_ref. has_infer_types ( )
@@ -1696,6 +1704,195 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
1696
1704
}
1697
1705
}
1698
1706
1707
+ fn suggest_impl_trait (
1708
+ & self ,
1709
+ err : & mut DiagnosticBuilder < ' tcx > ,
1710
+ span : Span ,
1711
+ obligation : & PredicateObligation < ' tcx > ,
1712
+ trait_ref : & ty:: Binder < ty:: TraitRef < ' tcx > > ,
1713
+ ) -> bool {
1714
+ if let ObligationCauseCode :: SizedReturnType = obligation. cause . code . peel_derives ( ) {
1715
+ } else {
1716
+ return false ;
1717
+ }
1718
+
1719
+ let hir = self . tcx . hir ( ) ;
1720
+ let parent_node = hir. get_parent_node ( obligation. cause . body_id ) ;
1721
+ let node = hir. find ( parent_node) ;
1722
+ if let Some ( hir:: Node :: Item ( hir:: Item {
1723
+ kind : hir:: ItemKind :: Fn ( sig, _, body_id) , ..
1724
+ } ) ) = node
1725
+ {
1726
+ let body = hir. body ( * body_id) ;
1727
+ let trait_ref = self . resolve_vars_if_possible ( trait_ref) ;
1728
+ let ty = trait_ref. skip_binder ( ) . self_ty ( ) ;
1729
+ if let ty:: Dynamic ( ..) = ty. kind {
1730
+ } else {
1731
+ // We only want to suggest `impl Trait` to `dyn Trait`s.
1732
+ // For example, `fn foo() -> str` needs to be filtered out.
1733
+ return false ;
1734
+ }
1735
+ // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for
1736
+ // cases like `fn foo() -> (dyn Trait, i32) {}`.
1737
+ // Recursively look for `TraitObject` types and if there's only one, use that span to
1738
+ // suggest `impl Trait`.
1739
+
1740
+ struct ReturnsVisitor < ' v > ( Vec < & ' v hir:: Expr < ' v > > ) ;
1741
+
1742
+ impl < ' v > Visitor < ' v > for ReturnsVisitor < ' v > {
1743
+ type Map = rustc:: hir:: map:: Map < ' v > ;
1744
+
1745
+ fn nested_visit_map ( & mut self ) -> hir:: intravisit:: NestedVisitorMap < ' _ , Self :: Map > {
1746
+ hir:: intravisit:: NestedVisitorMap :: None
1747
+ }
1748
+
1749
+ fn visit_expr ( & mut self , ex : & ' v hir:: Expr < ' v > ) {
1750
+ match ex. kind {
1751
+ hir:: ExprKind :: Ret ( Some ( ex) ) => self . 0 . push ( ex) ,
1752
+ _ => { }
1753
+ }
1754
+ hir:: intravisit:: walk_expr ( self , ex) ;
1755
+ }
1756
+
1757
+ fn visit_body ( & mut self , body : & ' v hir:: Body < ' v > ) {
1758
+ if body. generator_kind ( ) . is_none ( ) {
1759
+ if let hir:: ExprKind :: Block ( block, None ) = body. value . kind {
1760
+ if let Some ( expr) = block. expr {
1761
+ self . 0 . push ( expr) ;
1762
+ }
1763
+ }
1764
+ }
1765
+ hir:: intravisit:: walk_body ( self , body) ;
1766
+ }
1767
+ }
1768
+
1769
+ // Visit to make sure there's a single `return` type to suggest `impl Trait`,
1770
+ // otherwise suggest using `Box<dyn Trait>` or an enum.
1771
+ let mut visitor = ReturnsVisitor ( vec ! [ ] ) ;
1772
+ visitor. visit_body ( & body) ;
1773
+
1774
+ let tables = self . in_progress_tables . map ( |t| t. borrow ( ) ) . unwrap ( ) ;
1775
+
1776
+ if let hir:: FunctionRetTy :: Return ( ret_ty) = sig. decl . output {
1777
+ let mut all_returns_conform_to_trait = true ;
1778
+ let mut all_returns_have_same_type = true ;
1779
+ let mut last_ty = None ;
1780
+ if let Some ( ty_ret_ty) = tables. node_type_opt ( ret_ty. hir_id ) {
1781
+ let cause = ObligationCause :: misc ( ret_ty. span , ret_ty. hir_id ) ;
1782
+ if let ty:: Dynamic ( predicates, _) = & ty_ret_ty. kind {
1783
+ for predicate in predicates. iter ( ) {
1784
+ for expr in & visitor. 0 {
1785
+ if let Some ( returned_ty) = tables. node_type_opt ( expr. hir_id ) {
1786
+ if let Some ( ty) = last_ty {
1787
+ all_returns_have_same_type &= ty == returned_ty;
1788
+ }
1789
+ last_ty = Some ( returned_ty) ;
1790
+
1791
+ let param_env = ty:: ParamEnv :: empty ( ) ;
1792
+ let pred = predicate. with_self_ty ( self . tcx , returned_ty) ;
1793
+ let obligation =
1794
+ Obligation :: new ( cause. clone ( ) , param_env, pred) ;
1795
+ all_returns_conform_to_trait &=
1796
+ self . predicate_may_hold ( & obligation) ;
1797
+ }
1798
+ }
1799
+ }
1800
+ }
1801
+ } else {
1802
+ // We still want to verify whether all the return types conform to each other.
1803
+ for expr in & visitor. 0 {
1804
+ if let Some ( returned_ty) = tables. node_type_opt ( expr. hir_id ) {
1805
+ if let Some ( ty) = last_ty {
1806
+ all_returns_have_same_type &= ty == returned_ty;
1807
+ }
1808
+ last_ty = Some ( returned_ty) ;
1809
+ }
1810
+ }
1811
+ }
1812
+
1813
+ if let ( true , hir:: TyKind :: TraitObject ( ..) , Ok ( snippet) , true , Some ( last_ty) ) = (
1814
+ ret_ty. span . overlaps ( span) ,
1815
+ & ret_ty. kind ,
1816
+ self . tcx . sess . source_map ( ) . span_to_snippet ( ret_ty. span ) ,
1817
+ all_returns_conform_to_trait,
1818
+ last_ty,
1819
+ ) {
1820
+ err. code = Some ( error_code ! ( E0746 ) ) ;
1821
+ err. set_primary_message (
1822
+ "return type cannot have a bare trait because it must be `Sized`" ,
1823
+ ) ;
1824
+ err. children . clear ( ) ;
1825
+ let impl_trait_msg = "for information on `impl Trait`, see \
1826
+ <https://doc.rust-lang.org/book/ch10-02-traits.html\
1827
+ #returning-types-that-implement-traits>";
1828
+ let trait_obj_msg = "for information on trait objects, see \
1829
+ <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
1830
+ #using-trait-objects-that-allow-for-values-of-different-types>";
1831
+ let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
1832
+ let trait_obj = if has_dyn { & snippet[ 4 ..] } else { & snippet[ ..] } ;
1833
+ if all_returns_have_same_type {
1834
+ err. span_suggestion (
1835
+ ret_ty. span ,
1836
+ & format ! (
1837
+ "you can use the `impl Trait` feature \
1838
+ in the return type because all the return paths are of type \
1839
+ `{}`, which implements `dyn {}`",
1840
+ last_ty, trait_obj,
1841
+ ) ,
1842
+ format ! ( "impl {}" , trait_obj) ,
1843
+ Applicability :: MaybeIncorrect ,
1844
+ ) ;
1845
+ err. note ( impl_trait_msg) ;
1846
+ } else {
1847
+ let mut suggestions = visitor
1848
+ . 0
1849
+ . iter ( )
1850
+ . map ( |expr| {
1851
+ (
1852
+ expr. span ,
1853
+ format ! (
1854
+ "Box::new({})" ,
1855
+ self . tcx
1856
+ . sess
1857
+ . source_map( )
1858
+ . span_to_snippet( expr. span)
1859
+ . unwrap( )
1860
+ ) ,
1861
+ )
1862
+ } )
1863
+ . collect :: < Vec < _ > > ( ) ;
1864
+ suggestions. push ( (
1865
+ ret_ty. span ,
1866
+ format ! ( "Box<{}{}>" , if has_dyn { "" } else { "dyn " } , snippet) ,
1867
+ ) ) ;
1868
+ err. multipart_suggestion (
1869
+ "if the performance implications are acceptable, you can return a \
1870
+ trait object",
1871
+ suggestions,
1872
+ Applicability :: MaybeIncorrect ,
1873
+ ) ;
1874
+ err. span_help (
1875
+ visitor. 0 . iter ( ) . map ( |expr| expr. span ) . collect :: < Vec < _ > > ( ) ,
1876
+ & format ! (
1877
+ "if all the returned values were of the same type you could use \
1878
+ `impl {}` as the return type",
1879
+ trait_obj,
1880
+ ) ,
1881
+ ) ;
1882
+ err. help (
1883
+ "alternatively, you can always create a new `enum` with a variant \
1884
+ for each returned type",
1885
+ ) ;
1886
+ err. note ( impl_trait_msg) ;
1887
+ err. note ( trait_obj_msg) ;
1888
+ }
1889
+ return true ;
1890
+ }
1891
+ }
1892
+ }
1893
+ false
1894
+ }
1895
+
1699
1896
/// Given some node representing a fn-like thing in the HIR map,
1700
1897
/// returns a span and `ArgKind` information that describes the
1701
1898
/// arguments it expects. This can be supplied to
0 commit comments