@@ -30,9 +30,9 @@ use rustc_middle::hir::map;
30
30
use rustc_middle:: ty:: error:: TypeError :: { self , Sorts } ;
31
31
use rustc_middle:: ty:: {
32
32
self , suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind ,
33
- GeneratorDiagnosticData , GeneratorInteriorTypeCause , Infer , InferTy , InternalSubsts ,
34
- IsSuggestable , ToPredicate , Ty , TyCtxt , TypeAndMut , TypeFoldable , TypeFolder ,
35
- TypeSuperFoldable , TypeVisitableExt , TypeckResults ,
33
+ GeneratorDiagnosticData , GeneratorInteriorTypeCause , InferTy , InternalSubsts , IsSuggestable ,
34
+ ToPredicate , Ty , TyCtxt , TypeAndMut , TypeFoldable , TypeFolder , TypeSuperFoldable ,
35
+ TypeVisitableExt , TypeckResults ,
36
36
} ;
37
37
use rustc_span:: def_id:: LocalDefId ;
38
38
use rustc_span:: symbol:: { sym, Ident , Symbol } ;
@@ -261,7 +261,6 @@ pub trait TypeErrCtxtExt<'tcx> {
261
261
fn suggest_impl_trait (
262
262
& self ,
263
263
err : & mut Diagnostic ,
264
- span : Span ,
265
264
obligation : & PredicateObligation < ' tcx > ,
266
265
trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
267
266
) -> bool ;
@@ -1792,215 +1791,66 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
1792
1791
fn suggest_impl_trait (
1793
1792
& self ,
1794
1793
err : & mut Diagnostic ,
1795
- span : Span ,
1796
1794
obligation : & PredicateObligation < ' tcx > ,
1797
1795
trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
1798
1796
) -> bool {
1799
- match obligation. cause . code ( ) . peel_derives ( ) {
1800
- // Only suggest `impl Trait` if the return type is unsized because it is `dyn Trait`.
1801
- ObligationCauseCode :: SizedReturnType => { }
1802
- _ => return false ,
1803
- }
1804
-
1805
- let hir = self . tcx . hir ( ) ;
1806
- let fn_hir_id = hir. local_def_id_to_hir_id ( obligation. cause . body_id ) ;
1807
- let node = hir. find_by_def_id ( obligation. cause . body_id ) ;
1808
- let Some ( hir:: Node :: Item ( hir:: Item {
1809
- kind : hir:: ItemKind :: Fn ( sig, _, body_id) ,
1810
- ..
1811
- } ) ) = node
1812
- else {
1797
+ let ObligationCauseCode :: SizedReturnType = obligation. cause . code ( ) else {
1813
1798
return false ;
1814
1799
} ;
1815
- let body = hir. body ( * body_id) ;
1816
- let trait_pred = self . resolve_vars_if_possible ( trait_pred) ;
1817
- let ty = trait_pred. skip_binder ( ) . self_ty ( ) ;
1818
- let is_object_safe = match ty. kind ( ) {
1819
- ty:: Dynamic ( predicates, _, ty:: Dyn ) => {
1820
- // If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
1821
- predicates
1822
- . principal_def_id ( )
1823
- . map_or ( true , |def_id| self . tcx . check_is_object_safe ( def_id) )
1824
- }
1825
- // We only want to suggest `impl Trait` to `dyn Trait`s.
1826
- // For example, `fn foo() -> str` needs to be filtered out.
1827
- _ => return false ,
1828
- } ;
1829
-
1830
- let hir:: FnRetTy :: Return ( ret_ty) = sig. decl . output else {
1800
+ let ty:: Dynamic ( _, _, ty:: Dyn ) = trait_pred. self_ty ( ) . skip_binder ( ) . kind ( ) else {
1831
1801
return false ;
1832
1802
} ;
1833
1803
1834
- // Use `TypeVisitor` instead of the output type directly to find the span of `ty` for
1835
- // cases like `fn foo() -> (dyn Trait, i32) {}`.
1836
- // Recursively look for `TraitObject` types and if there's only one, use that span to
1837
- // suggest `impl Trait`.
1838
-
1839
- // Visit to make sure there's a single `return` type to suggest `impl Trait`,
1840
- // otherwise suggest using `Box<dyn Trait>` or an enum.
1841
- let mut visitor = ReturnsVisitor :: default ( ) ;
1842
- visitor. visit_body ( & body) ;
1843
-
1844
- let typeck_results = self . typeck_results . as_ref ( ) . unwrap ( ) ;
1845
- let Some ( liberated_sig) = typeck_results. liberated_fn_sigs ( ) . get ( fn_hir_id) . copied ( ) else { return false ; } ;
1846
-
1847
- let ret_types = visitor
1848
- . returns
1849
- . iter ( )
1850
- . filter_map ( |expr| Some ( ( expr. span , typeck_results. node_type_opt ( expr. hir_id ) ?) ) )
1851
- . map ( |( expr_span, ty) | ( expr_span, self . resolve_vars_if_possible ( ty) ) ) ;
1852
- let ( last_ty, all_returns_have_same_type, only_never_return) = ret_types. clone ( ) . fold (
1853
- ( None , true , true ) ,
1854
- |( last_ty, mut same, only_never_return) : ( std:: option:: Option < Ty < ' _ > > , bool , bool ) ,
1855
- ( _, ty) | {
1856
- let ty = self . resolve_vars_if_possible ( ty) ;
1857
- same &=
1858
- !matches ! ( ty. kind( ) , ty:: Error ( _) )
1859
- && last_ty. map_or ( true , |last_ty| {
1860
- // FIXME: ideally we would use `can_coerce` here instead, but `typeck` comes
1861
- // *after* in the dependency graph.
1862
- match ( ty. kind ( ) , last_ty. kind ( ) ) {
1863
- ( Infer ( InferTy :: IntVar ( _) ) , Infer ( InferTy :: IntVar ( _) ) )
1864
- | ( Infer ( InferTy :: FloatVar ( _) ) , Infer ( InferTy :: FloatVar ( _) ) )
1865
- | ( Infer ( InferTy :: FreshIntTy ( _) ) , Infer ( InferTy :: FreshIntTy ( _) ) )
1866
- | (
1867
- Infer ( InferTy :: FreshFloatTy ( _) ) ,
1868
- Infer ( InferTy :: FreshFloatTy ( _) ) ,
1869
- ) => true ,
1870
- _ => ty == last_ty,
1871
- }
1872
- } ) ;
1873
- ( Some ( ty) , same, only_never_return && matches ! ( ty. kind( ) , ty:: Never ) )
1874
- } ,
1875
- ) ;
1876
- let mut spans_and_needs_box = vec ! [ ] ;
1877
-
1878
- match liberated_sig. output ( ) . kind ( ) {
1879
- ty:: Dynamic ( predicates, _, ty:: Dyn ) => {
1880
- let cause = ObligationCause :: misc ( ret_ty. span , obligation. cause . body_id ) ;
1881
- let param_env = ty:: ParamEnv :: empty ( ) ;
1882
-
1883
- if !only_never_return {
1884
- for ( expr_span, return_ty) in ret_types {
1885
- let self_ty_satisfies_dyn_predicates = |self_ty| {
1886
- predicates. iter ( ) . all ( |predicate| {
1887
- let pred = predicate. with_self_ty ( self . tcx , self_ty) ;
1888
- let obl = Obligation :: new ( self . tcx , cause. clone ( ) , param_env, pred) ;
1889
- self . predicate_may_hold ( & obl)
1890
- } )
1891
- } ;
1892
-
1893
- if let ty:: Adt ( def, substs) = return_ty. kind ( )
1894
- && def. is_box ( )
1895
- && self_ty_satisfies_dyn_predicates ( substs. type_at ( 0 ) )
1896
- {
1897
- spans_and_needs_box. push ( ( expr_span, false ) ) ;
1898
- } else if self_ty_satisfies_dyn_predicates ( return_ty) {
1899
- spans_and_needs_box. push ( ( expr_span, true ) ) ;
1900
- } else {
1901
- return false ;
1902
- }
1903
- }
1904
- }
1905
- }
1906
- _ => return false ,
1907
- } ;
1908
-
1909
- let sm = self . tcx . sess . source_map ( ) ;
1910
- if !ret_ty. span . overlaps ( span) {
1911
- return false ;
1912
- }
1913
- let snippet = if let hir:: TyKind :: TraitObject ( ..) = ret_ty. kind {
1914
- if let Ok ( snippet) = sm. span_to_snippet ( ret_ty. span ) {
1915
- snippet
1916
- } else {
1917
- return false ;
1918
- }
1919
- } else {
1920
- // Substitute the type, so we can print a fixup given `type Alias = dyn Trait`
1921
- let name = liberated_sig. output ( ) . to_string ( ) ;
1922
- let name =
1923
- name. strip_prefix ( '(' ) . and_then ( |name| name. strip_suffix ( ')' ) ) . unwrap_or ( & name) ;
1924
- if !name. starts_with ( "dyn " ) {
1925
- return false ;
1926
- }
1927
- name. to_owned ( )
1928
- } ;
1929
-
1930
1804
err. code ( error_code ! ( E0746 ) ) ;
1931
1805
err. set_primary_message ( "return type cannot have an unboxed trait object" ) ;
1932
1806
err. children . clear ( ) ;
1933
- let impl_trait_msg = "for information on `impl Trait`, see \
1934
- <https://doc.rust-lang.org/book/ch10-02-traits.html\
1935
- #returning-types-that-implement-traits>";
1936
- let trait_obj_msg = "for information on trait objects, see \
1937
- <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
1938
- #using-trait-objects-that-allow-for-values-of-different-types>";
1939
-
1940
- let has_dyn = snippet. split_whitespace ( ) . next ( ) . map_or ( false , |s| s == "dyn" ) ;
1941
- let trait_obj = if has_dyn { & snippet[ 4 ..] } else { & snippet } ;
1942
- if only_never_return {
1943
- // No return paths, probably using `panic!()` or similar.
1944
- // Suggest `-> impl Trait`, and if `Trait` is object safe, `-> Box<dyn Trait>`.
1945
- suggest_trait_object_return_type_alternatives (
1946
- err,
1947
- ret_ty. span ,
1948
- trait_obj,
1949
- is_object_safe,
1950
- ) ;
1951
- } else if let ( Some ( last_ty) , true ) = ( last_ty, all_returns_have_same_type) {
1952
- // Suggest `-> impl Trait`.
1807
+
1808
+ let span = obligation. cause . span ;
1809
+ if let Ok ( snip) = self . tcx . sess . source_map ( ) . span_to_snippet ( span)
1810
+ && snip. starts_with ( "dyn " )
1811
+ {
1953
1812
err. span_suggestion (
1954
- ret_ty. span ,
1955
- format ! (
1956
- "use `impl {1}` as the return type, as all return paths are of type `{}`, \
1957
- which implements `{1}`",
1958
- last_ty, trait_obj,
1959
- ) ,
1960
- format ! ( "impl {}" , trait_obj) ,
1961
- Applicability :: MachineApplicable ,
1813
+ span. with_hi ( span. lo ( ) + BytePos ( 4 ) ) ,
1814
+ "return an `impl Trait` instead of a `dyn Trait`, \
1815
+ if all returned values are the same type",
1816
+ "impl " ,
1817
+ Applicability :: MaybeIncorrect ,
1962
1818
) ;
1963
- err. note ( impl_trait_msg) ;
1964
- } else {
1965
- if is_object_safe {
1966
- // Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
1967
- err. multipart_suggestion (
1968
- "return a boxed trait object instead" ,
1969
- vec ! [
1970
- ( ret_ty. span. shrink_to_lo( ) , "Box<" . to_string( ) ) ,
1971
- ( span. shrink_to_hi( ) , ">" . to_string( ) ) ,
1972
- ] ,
1973
- Applicability :: MaybeIncorrect ,
1974
- ) ;
1975
- for ( span, needs_box) in spans_and_needs_box {
1976
- if needs_box {
1977
- err. multipart_suggestion (
1978
- "... and box this value" ,
1979
- vec ! [
1980
- ( span. shrink_to_lo( ) , "Box::new(" . to_string( ) ) ,
1981
- ( span. shrink_to_hi( ) , ")" . to_string( ) ) ,
1982
- ] ,
1983
- Applicability :: MaybeIncorrect ,
1984
- ) ;
1985
- }
1986
- }
1819
+ }
1820
+
1821
+ let body = self . tcx . hir ( ) . body ( self . tcx . hir ( ) . body_owned_by ( obligation. cause . body_id ) ) ;
1822
+
1823
+ let mut visitor = ReturnsVisitor :: default ( ) ;
1824
+ visitor. visit_body ( & body) ;
1825
+
1826
+ let mut sugg =
1827
+ vec ! [ ( span. shrink_to_lo( ) , "Box<" . to_string( ) ) , ( span. shrink_to_hi( ) , ">" . to_string( ) ) ] ;
1828
+ sugg. extend ( visitor. returns . into_iter ( ) . flat_map ( |expr| {
1829
+ let span = expr. span . find_ancestor_in_same_ctxt ( obligation. cause . span ) . unwrap_or ( expr. span ) ;
1830
+ if !span. can_be_used_for_suggestions ( ) {
1831
+ vec ! [ ]
1832
+ } else if let hir:: ExprKind :: Call ( path, ..) = expr. kind
1833
+ && let hir:: ExprKind :: Path ( hir:: QPath :: TypeRelative ( ty, method) ) = path. kind
1834
+ && method. ident . name == sym:: new
1835
+ && let hir:: TyKind :: Path ( hir:: QPath :: Resolved ( .., box_path) ) = ty. kind
1836
+ && box_path. res . opt_def_id ( ) . is_some_and ( |def_id| Some ( def_id) == self . tcx . lang_items ( ) . owned_box ( ) )
1837
+ {
1838
+ // Don't box `Box::new`
1839
+ vec ! [ ]
1987
1840
} else {
1988
- // This is currently not possible to trigger because E0038 takes precedence, but
1989
- // leave it in for completeness in case anything changes in an earlier stage.
1990
- err. note ( format ! (
1991
- "if trait `{}` were object-safe, you could return a trait object" ,
1992
- trait_obj,
1993
- ) ) ;
1841
+ vec ! [
1842
+ ( span. shrink_to_lo( ) , "Box::new(" . to_string( ) ) ,
1843
+ ( span. shrink_to_hi( ) , ")" . to_string( ) ) ,
1844
+ ]
1994
1845
}
1995
- err. note ( trait_obj_msg) ;
1996
- err. note ( format ! (
1997
- "if all the returned values were of the same type you could use `impl {}` as the \
1998
- return type",
1999
- trait_obj,
2000
- ) ) ;
2001
- err. note ( impl_trait_msg) ;
2002
- err. note ( "you can create a new `enum` with a variant for each returned type" ) ;
2003
- }
1846
+ } ) ) ;
1847
+
1848
+ err. multipart_suggestion (
1849
+ "box the return type, and wrap all of the returned values in `Box::new`" ,
1850
+ sugg,
1851
+ Applicability :: MaybeIncorrect ,
1852
+ ) ;
1853
+
2004
1854
true
2005
1855
}
2006
1856
@@ -4139,37 +3989,6 @@ impl NextTypeParamName for &[hir::GenericParam<'_>] {
4139
3989
}
4140
3990
}
4141
3991
4142
- fn suggest_trait_object_return_type_alternatives (
4143
- err : & mut Diagnostic ,
4144
- ret_ty : Span ,
4145
- trait_obj : & str ,
4146
- is_object_safe : bool ,
4147
- ) {
4148
- err. span_suggestion (
4149
- ret_ty,
4150
- format ! (
4151
- "use `impl {}` as the return type if all return paths have the same type but you \
4152
- want to expose only the trait in the signature",
4153
- trait_obj,
4154
- ) ,
4155
- format ! ( "impl {}" , trait_obj) ,
4156
- Applicability :: MaybeIncorrect ,
4157
- ) ;
4158
- if is_object_safe {
4159
- err. multipart_suggestion (
4160
- format ! (
4161
- "use a boxed trait object if all return paths implement trait `{}`" ,
4162
- trait_obj,
4163
- ) ,
4164
- vec ! [
4165
- ( ret_ty. shrink_to_lo( ) , "Box<" . to_string( ) ) ,
4166
- ( ret_ty. shrink_to_hi( ) , ">" . to_string( ) ) ,
4167
- ] ,
4168
- Applicability :: MaybeIncorrect ,
4169
- ) ;
4170
- }
4171
- }
4172
-
4173
3992
/// Collect the spans that we see the generic param `param_did`
4174
3993
struct ReplaceImplTraitVisitor < ' a > {
4175
3994
ty_spans : & ' a mut Vec < Span > ,
0 commit comments