2121//! If you define a new `LateLintPass`, you will also need to add it to the
2222//! `late_lint_methods!` invocation in `lib.rs`.
2323
24+ use std:: fmt:: Write ;
25+
2426use rustc:: hir:: def:: { Res , DefKind } ;
2527use rustc:: hir:: def_id:: { DefId , LOCAL_CRATE } ;
2628use rustc:: ty:: { self , Ty , TyCtxt , layout:: VariantIdx } ;
@@ -1877,41 +1879,57 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
18771879 const ZEROED_PATH : & [ Symbol ] = & [ sym:: core, sym:: mem, sym:: zeroed] ;
18781880 const UININIT_PATH : & [ Symbol ] = & [ sym:: core, sym:: mem, sym:: uninitialized] ;
18791881
1880- /// Return `false` only if we are sure this type does *not*
1882+ /// Information about why a type cannot be initialized this way.
1883+ /// Contains an error message and optionally a span to point at.
1884+ type InitError = ( String , Option < Span > ) ;
1885+
1886+ /// Return `Some` only if we are sure this type does *not*
18811887 /// allow zero initialization.
1882- fn ty_maybe_allows_zero_init < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> bool {
1888+ fn ty_find_init_error < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Option < InitError > {
18831889 use rustc:: ty:: TyKind :: * ;
18841890 match ty. sty {
18851891 // Primitive types that don't like 0 as a value.
1886- Ref ( ..) | FnPtr ( ..) | Never => false ,
1887- Adt ( ..) if ty. is_box ( ) => false ,
1892+ Ref ( ..) => Some ( ( format ! ( "References must be non-null" ) , None ) ) ,
1893+ Adt ( ..) if ty. is_box ( ) => Some ( ( format ! ( "`Box` must be non-null" ) , None ) ) ,
1894+ FnPtr ( ..) => Some ( ( format ! ( "Function pointers must be non-null" ) , None ) ) ,
1895+ Never => Some ( ( format ! ( "The never type (`!`) has no valid value" ) , None ) ) ,
18881896 // Recurse for some compound types.
18891897 Adt ( adt_def, substs) if !adt_def. is_union ( ) => {
18901898 match adt_def. variants . len ( ) {
1891- 0 => false , // Uninhabited enum!
1899+ 0 => Some ( ( format ! ( "0-variant enums have no valid value" ) , None ) ) ,
18921900 1 => {
18931901 // Struct, or enum with exactly one variant.
18941902 // Proceed recursively, check all fields.
18951903 let variant = & adt_def. variants [ VariantIdx :: from_u32 ( 0 ) ] ;
1896- variant. fields . iter ( ) . all ( |field| {
1897- ty_maybe_allows_zero_init (
1904+ variant. fields . iter ( ) . find_map ( |field| {
1905+ ty_find_init_error (
18981906 tcx,
18991907 field. ty ( tcx, substs) ,
1900- )
1908+ ) . map ( |( mut msg, span) | if span. is_none ( ) {
1909+ // Point to this field, should be helpful for figuring
1910+ // out where the source of the error is.
1911+ let span = tcx. def_span ( field. did ) ;
1912+ write ! ( & mut msg, " (in this {} field)" , adt_def. descr( ) )
1913+ . unwrap ( ) ;
1914+ ( msg, Some ( span) )
1915+ } else {
1916+ // Just forward.
1917+ ( msg, span)
1918+ } )
19011919 } )
19021920 }
1903- _ => true , // Conservative fallback for multi-variant enum.
1921+ _ => None , // Conservative fallback for multi-variant enum.
19041922 }
19051923 }
19061924 Tuple ( ..) => {
19071925 // Proceed recursively, check all fields.
1908- ty. tuple_fields ( ) . all ( |field| ty_maybe_allows_zero_init ( tcx, field) )
1926+ ty. tuple_fields ( ) . find_map ( |field| ty_find_init_error ( tcx, field) )
19091927 }
19101928 // FIXME: Would be nice to also warn for `NonNull`/`NonZero*`.
19111929 // FIXME: *Only for `mem::uninitialized`*, we could also warn for `bool`,
19121930 // `char`, and any multivariant enum.
19131931 // Conservative fallback.
1914- _ => true ,
1932+ _ => None ,
19151933 }
19161934 }
19171935
@@ -1925,9 +1943,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
19251943 // using zeroed or uninitialized memory.
19261944 // We are extremely conservative with what we warn about.
19271945 let conjured_ty = cx. tables . expr_ty ( expr) ;
1928-
1929- if !ty_maybe_allows_zero_init ( cx. tcx , conjured_ty) {
1930- cx. struct_span_lint (
1946+ if let Some ( ( msg, span) ) = ty_find_init_error ( cx. tcx , conjured_ty) {
1947+ let mut err = cx. struct_span_lint (
19311948 INVALID_VALUE ,
19321949 expr. span ,
19331950 & format ! (
@@ -1939,11 +1956,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
19391956 "being left uninitialized"
19401957 }
19411958 ) ,
1942- )
1943- . note ( "this means that this code causes undefined behavior \
1944- when executed")
1945- . help ( "use `MaybeUninit` instead" )
1946- . emit ( ) ;
1959+ ) ;
1960+ err. span_label ( expr. span ,
1961+ "this code causes undefined behavior when executed" ) ;
1962+ err. span_label ( expr. span , "help: use `MaybeUninit<T>` instead" ) ;
1963+ if let Some ( span) = span {
1964+ err. span_note ( span, & msg) ;
1965+ } else {
1966+ err. note ( & msg) ;
1967+ }
1968+ err. emit ( ) ;
19471969 }
19481970 }
19491971 }
0 commit comments