21
21
//! If you define a new `LateLintPass`, you will also need to add it to the
22
22
//! `late_lint_methods!` invocation in `lib.rs`.
23
23
24
+ use std:: fmt:: Write ;
25
+
24
26
use rustc:: hir:: def:: { Res , DefKind } ;
25
27
use rustc:: hir:: def_id:: { DefId , LOCAL_CRATE } ;
26
28
use rustc:: ty:: { self , Ty , TyCtxt , layout:: VariantIdx } ;
@@ -1877,41 +1879,57 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
1877
1879
const ZEROED_PATH : & [ Symbol ] = & [ sym:: core, sym:: mem, sym:: zeroed] ;
1878
1880
const UININIT_PATH : & [ Symbol ] = & [ sym:: core, sym:: mem, sym:: uninitialized] ;
1879
1881
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*
1881
1887
/// 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 > {
1883
1889
use rustc:: ty:: TyKind :: * ;
1884
1890
match ty. sty {
1885
1891
// 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 ) ) ,
1888
1896
// Recurse for some compound types.
1889
1897
Adt ( adt_def, substs) if !adt_def. is_union ( ) => {
1890
1898
match adt_def. variants . len ( ) {
1891
- 0 => false , // Uninhabited enum!
1899
+ 0 => Some ( ( format ! ( "0-variant enums have no valid value" ) , None ) ) ,
1892
1900
1 => {
1893
1901
// Struct, or enum with exactly one variant.
1894
1902
// Proceed recursively, check all fields.
1895
1903
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 (
1898
1906
tcx,
1899
1907
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
+ } )
1901
1919
} )
1902
1920
}
1903
- _ => true , // Conservative fallback for multi-variant enum.
1921
+ _ => None , // Conservative fallback for multi-variant enum.
1904
1922
}
1905
1923
}
1906
1924
Tuple ( ..) => {
1907
1925
// 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) )
1909
1927
}
1910
1928
// FIXME: Would be nice to also warn for `NonNull`/`NonZero*`.
1911
1929
// FIXME: *Only for `mem::uninitialized`*, we could also warn for `bool`,
1912
1930
// `char`, and any multivariant enum.
1913
1931
// Conservative fallback.
1914
- _ => true ,
1932
+ _ => None ,
1915
1933
}
1916
1934
}
1917
1935
@@ -1925,9 +1943,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
1925
1943
// using zeroed or uninitialized memory.
1926
1944
// We are extremely conservative with what we warn about.
1927
1945
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 (
1931
1948
INVALID_VALUE ,
1932
1949
expr. span ,
1933
1950
& format ! (
@@ -1939,11 +1956,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
1939
1956
"being left uninitialized"
1940
1957
}
1941
1958
) ,
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 ( ) ;
1947
1969
}
1948
1970
}
1949
1971
}
0 commit comments