1+ // ignore-tidy-filelength
12use super :: {
23 ConstEvalFailure , EvaluationResult , FulfillmentError , FulfillmentErrorCode ,
34 MismatchedProjectionTypes , ObjectSafetyViolation , Obligation , ObligationCause ,
@@ -22,9 +23,12 @@ use crate::ty::TypeckTables;
2223use crate :: ty:: { self , AdtKind , DefIdTree , ToPolyTraitRef , ToPredicate , Ty , TyCtxt , TypeFoldable } ;
2324
2425use 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+ } ;
2629use rustc_hir as hir;
2730use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
31+ use rustc_hir:: intravisit:: Visitor ;
2832use rustc_hir:: Node ;
2933use rustc_span:: source_map:: SourceMap ;
3034use rustc_span:: symbol:: { kw, sym} ;
@@ -758,7 +762,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
758762 ) ) ,
759763 Some (
760764 "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"
762766 . to_owned ( ) ,
763767 ) ,
764768 )
@@ -835,6 +839,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
835839 self . suggest_remove_reference ( & obligation, & mut err, & trait_ref) ;
836840 self . suggest_semicolon_removal ( & obligation, & mut err, span, & trait_ref) ;
837841 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+ }
838846
839847 // Try to report a help message
840848 if !trait_ref. has_infer_types ( )
@@ -1696,6 +1704,195 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
16961704 }
16971705 }
16981706
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+
16991896 /// Given some node representing a fn-like thing in the HIR map,
17001897 /// returns a span and `ArgKind` information that describes the
17011898 /// arguments it expects. This can be supplied to
0 commit comments