@@ -16,6 +16,8 @@ use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder,
1616use rustc_hir as hir;
1717use rustc_hir:: def_id:: DefId ;
1818use rustc_hir:: intravisit:: Visitor ;
19+ use rustc_hir:: GenericParam ;
20+ use rustc_hir:: Item ;
1921use rustc_hir:: Node ;
2022use rustc_middle:: mir:: abstract_const:: NotConstEvaluatable ;
2123use rustc_middle:: ty:: error:: ExpectedFound ;
@@ -1138,6 +1140,20 @@ trait InferCtxtPrivExt<'tcx> {
11381140 obligation : & PredicateObligation < ' tcx > ,
11391141 ) ;
11401142
1143+ fn maybe_suggest_unsized_generics (
1144+ & self ,
1145+ err : & mut DiagnosticBuilder < ' tcx > ,
1146+ span : Span ,
1147+ node : Node < ' hir > ,
1148+ ) ;
1149+
1150+ fn maybe_indirection_for_unsized (
1151+ & self ,
1152+ err : & mut DiagnosticBuilder < ' tcx > ,
1153+ item : & ' hir Item < ' hir > ,
1154+ param : & ' hir GenericParam < ' hir > ,
1155+ ) -> bool ;
1156+
11411157 fn is_recursive_obligation (
11421158 & self ,
11431159 obligated_types : & mut Vec < & ty:: TyS < ' tcx > > ,
@@ -1816,88 +1832,116 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
18161832 ) => ( pred, item_def_id, span) ,
18171833 _ => return ,
18181834 } ;
1819-
1835+ debug ! (
1836+ "suggest_unsized_bound_if_applicable: pred={:?} item_def_id={:?} span={:?}" ,
1837+ pred, item_def_id, span
1838+ ) ;
18201839 let node = match (
18211840 self . tcx . hir ( ) . get_if_local ( item_def_id) ,
18221841 Some ( pred. def_id ( ) ) == self . tcx . lang_items ( ) . sized_trait ( ) ,
18231842 ) {
18241843 ( Some ( node) , true ) => node,
18251844 _ => return ,
18261845 } ;
1846+ self . maybe_suggest_unsized_generics ( err, span, node) ;
1847+ }
1848+
1849+ fn maybe_suggest_unsized_generics (
1850+ & self ,
1851+ err : & mut DiagnosticBuilder < ' tcx > ,
1852+ span : Span ,
1853+ node : Node < ' hir > ,
1854+ ) {
18271855 let generics = match node. generics ( ) {
18281856 Some ( generics) => generics,
18291857 None => return ,
18301858 } ;
1831- for param in generics. params {
1832- if param. span != span
1833- || param. bounds . iter ( ) . any ( |bound| {
1834- bound. trait_ref ( ) . and_then ( |trait_ref| trait_ref. trait_def_id ( ) )
1835- == self . tcx . lang_items ( ) . sized_trait ( )
1836- } )
1837- {
1838- continue ;
1839- }
1840- match node {
1841- hir:: Node :: Item (
1842- item
1843- @
1844- hir:: Item {
1845- kind :
1846- hir:: ItemKind :: Enum ( ..)
1847- | hir:: ItemKind :: Struct ( ..)
1848- | hir:: ItemKind :: Union ( ..) ,
1849- ..
1850- } ,
1851- ) => {
1852- // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
1853- // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
1854- // is not.
1855- let mut visitor = FindTypeParam {
1856- param : param. name . ident ( ) . name ,
1857- invalid_spans : vec ! [ ] ,
1858- nested : false ,
1859- } ;
1860- visitor. visit_item ( item) ;
1861- if !visitor. invalid_spans . is_empty ( ) {
1862- let mut multispan: MultiSpan = param. span . into ( ) ;
1863- multispan. push_span_label (
1864- param. span ,
1865- format ! ( "this could be changed to `{}: ?Sized`..." , param. name. ident( ) ) ,
1866- ) ;
1867- for sp in visitor. invalid_spans {
1868- multispan. push_span_label (
1869- sp,
1870- format ! (
1871- "...if indirection were used here: `Box<{}>`" ,
1872- param. name. ident( ) ,
1873- ) ,
1874- ) ;
1875- }
1876- err. span_help (
1877- multispan,
1878- & format ! (
1879- "you could relax the implicit `Sized` bound on `{T}` if it were \
1880- used through indirection like `&{T}` or `Box<{T}>`",
1881- T = param. name. ident( ) ,
1882- ) ,
1883- ) ;
1884- return ;
1885- }
1859+ let sized_trait = self . tcx . lang_items ( ) . sized_trait ( ) ;
1860+ debug ! ( "maybe_suggest_unsized_generics: generics.params={:?}" , generics. params) ;
1861+ debug ! ( "maybe_suggest_unsized_generics: generics.where_clause={:?}" , generics. where_clause) ;
1862+ let param = generics
1863+ . params
1864+ . iter ( )
1865+ . filter ( |param| param. span == span)
1866+ . filter ( |param| {
1867+ // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
1868+ // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
1869+ param
1870+ . bounds
1871+ . iter ( )
1872+ . all ( |bound| bound. trait_ref ( ) . and_then ( |tr| tr. trait_def_id ( ) ) != sized_trait)
1873+ } )
1874+ . next ( ) ;
1875+ let param = match param {
1876+ Some ( param) => param,
1877+ _ => return ,
1878+ } ;
1879+ debug ! ( "maybe_suggest_unsized_generics: param={:?}" , param) ;
1880+ match node {
1881+ hir:: Node :: Item (
1882+ item
1883+ @
1884+ hir:: Item {
1885+ // Only suggest indirection for uses of type parameters in ADTs.
1886+ kind :
1887+ hir:: ItemKind :: Enum ( ..) | hir:: ItemKind :: Struct ( ..) | hir:: ItemKind :: Union ( ..) ,
1888+ ..
1889+ } ,
1890+ ) => {
1891+ if self . maybe_indirection_for_unsized ( err, item, param) {
1892+ return ;
18861893 }
1887- _ => { }
18881894 }
1889- let ( span, separator) = match param. bounds {
1890- [ ] => ( span. shrink_to_hi ( ) , ":" ) ,
1891- [ .., bound] => ( bound. span ( ) . shrink_to_hi ( ) , " +" ) ,
1892- } ;
1893- err. span_suggestion_verbose (
1894- span,
1895- "consider relaxing the implicit `Sized` restriction" ,
1896- format ! ( "{} ?Sized" , separator) ,
1897- Applicability :: MachineApplicable ,
1895+ _ => { }
1896+ } ;
1897+ // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
1898+ let ( span, separator) = match param. bounds {
1899+ [ ] => ( span. shrink_to_hi ( ) , ":" ) ,
1900+ [ .., bound] => ( bound. span ( ) . shrink_to_hi ( ) , " +" ) ,
1901+ } ;
1902+ err. span_suggestion_verbose (
1903+ span,
1904+ "consider relaxing the implicit `Sized` restriction" ,
1905+ format ! ( "{} ?Sized" , separator) ,
1906+ Applicability :: MachineApplicable ,
1907+ ) ;
1908+ }
1909+
1910+ fn maybe_indirection_for_unsized (
1911+ & self ,
1912+ err : & mut DiagnosticBuilder < ' tcx > ,
1913+ item : & ' hir Item < ' hir > ,
1914+ param : & ' hir GenericParam < ' hir > ,
1915+ ) -> bool {
1916+ // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
1917+ // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
1918+ // is not. Look for invalid "bare" parameter uses, and suggest using indirection.
1919+ let mut visitor =
1920+ FindTypeParam { param : param. name . ident ( ) . name , invalid_spans : vec ! [ ] , nested : false } ;
1921+ visitor. visit_item ( item) ;
1922+ if visitor. invalid_spans . is_empty ( ) {
1923+ return false ;
1924+ }
1925+ let mut multispan: MultiSpan = param. span . into ( ) ;
1926+ multispan. push_span_label (
1927+ param. span ,
1928+ format ! ( "this could be changed to `{}: ?Sized`..." , param. name. ident( ) ) ,
1929+ ) ;
1930+ for sp in visitor. invalid_spans {
1931+ multispan. push_span_label (
1932+ sp,
1933+ format ! ( "...if indirection were used here: `Box<{}>`" , param. name. ident( ) ) ,
18981934 ) ;
1899- return ;
19001935 }
1936+ err. span_help (
1937+ multispan,
1938+ & format ! (
1939+ "you could relax the implicit `Sized` bound on `{T}` if it were \
1940+ used through indirection like `&{T}` or `Box<{T}>`",
1941+ T = param. name. ident( ) ,
1942+ ) ,
1943+ ) ;
1944+ true
19011945 }
19021946
19031947 fn is_recursive_obligation (
@@ -1948,6 +1992,7 @@ impl<'v> Visitor<'v> for FindTypeParam {
19481992 if path. segments . len ( ) == 1 && path. segments [ 0 ] . ident . name == self . param =>
19491993 {
19501994 if !self . nested {
1995+ debug ! ( "FindTypeParam::visit_ty: ty={:?}" , ty) ;
19511996 self . invalid_spans . push ( ty. span ) ;
19521997 }
19531998 }
0 commit comments