@@ -30,9 +30,9 @@ use rustc_middle::hir::map;
3030use rustc_middle:: ty:: error:: TypeError :: { self , Sorts } ;
3131use rustc_middle:: ty:: {
3232 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 ,
3636} ;
3737use rustc_span:: def_id:: LocalDefId ;
3838use rustc_span:: symbol:: { sym, Ident , Symbol } ;
@@ -261,7 +261,6 @@ pub trait TypeErrCtxtExt<'tcx> {
261261 fn suggest_impl_trait (
262262 & self ,
263263 err : & mut Diagnostic ,
264- span : Span ,
265264 obligation : & PredicateObligation < ' tcx > ,
266265 trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
267266 ) -> bool ;
@@ -1792,215 +1791,66 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
17921791 fn suggest_impl_trait (
17931792 & self ,
17941793 err : & mut Diagnostic ,
1795- span : Span ,
17961794 obligation : & PredicateObligation < ' tcx > ,
17971795 trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
17981796 ) -> 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 {
18131798 return false ;
18141799 } ;
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 {
18311801 return false ;
18321802 } ;
18331803
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-
19301804 err. code ( error_code ! ( E0746 ) ) ;
19311805 err. set_primary_message ( "return type cannot have an unboxed trait object" ) ;
19321806 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+ {
19531812 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 ,
19621818 ) ;
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 ! [ ]
19871840 } 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+ ]
19941845 }
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+
20041854 true
20051855 }
20061856
@@ -4139,37 +3989,6 @@ impl NextTypeParamName for &[hir::GenericParam<'_>] {
41393989 }
41403990}
41413991
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-
41733992/// Collect the spans that we see the generic param `param_did`
41743993struct ReplaceImplTraitVisitor < ' a > {
41753994 ty_spans : & ' a mut Vec < Span > ,
0 commit comments