@@ -2591,6 +2591,204 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
2591
2591
}
2592
2592
}
2593
2593
2594
+ declare_lint ! {
2595
+ /// The `mem_uninitialized` lint detects all uses of `std::mem::uninitialized` that are not
2596
+ /// known to be safe.
2597
+ ///
2598
+ /// This function is extremely dangerous, and nearly all uses of it cause immediate Undefined
2599
+ /// Behavior.
2600
+ ///
2601
+ /// ### Example
2602
+ ///
2603
+ /// ```rust,compile_fail
2604
+ /// #![deny(mem_uninitialized)]
2605
+ /// fn main() {
2606
+ /// let x: [char; 16] = unsafe { std::mem::uninitialized() };
2607
+ /// }
2608
+ /// ```
2609
+ ///
2610
+ /// {{produces}}
2611
+ ///
2612
+ /// ### Explanation
2613
+ ///
2614
+ /// Creating an invalid value is undefined behavior, and nearly all types are invalid when left
2615
+ /// uninitialized.
2616
+ ///
2617
+ /// To avoid churn, however, this will not lint for types made up entirely of integers, floats,
2618
+ /// or raw pointers. This is not saying that leaving these types uninitialized is okay,
2619
+ /// however.
2620
+ pub MEM_UNINITIALIZED ,
2621
+ Warn ,
2622
+ "use of mem::uninitialized" ,
2623
+ @future_incompatible = FutureIncompatibleInfo {
2624
+ reference: "FIXME: fill this in" ,
2625
+ reason: FutureIncompatibilityReason :: FutureReleaseErrorReportNow ,
2626
+ explain_reason: false ,
2627
+ } ;
2628
+ }
2629
+
2630
+ declare_lint_pass ! ( MemUninitialized => [ MEM_UNINITIALIZED ] ) ;
2631
+
2632
+ impl < ' tcx > LateLintPass < ' tcx > for MemUninitialized {
2633
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & hir:: Expr < ' _ > ) {
2634
+ /// Information about why a type cannot be initialized this way.
2635
+ /// Contains an error message and optionally a span to point at.
2636
+ type InitError = ( String , Option < Span > ) ;
2637
+
2638
+ /// Determine if this expression is a "dangerous initialization".
2639
+ fn is_dangerous_init ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> bool {
2640
+ if let hir:: ExprKind :: Call ( ref path_expr, _) = expr. kind {
2641
+ // Find calls to `mem::{uninitialized,zeroed}` methods.
2642
+ if let hir:: ExprKind :: Path ( ref qpath) = path_expr. kind {
2643
+ if let Some ( def_id) = cx. qpath_res ( qpath, path_expr. hir_id ) . opt_def_id ( ) {
2644
+ if cx. tcx . is_diagnostic_item ( sym:: mem_uninitialized, def_id) {
2645
+ return true ;
2646
+ }
2647
+ }
2648
+ }
2649
+ }
2650
+
2651
+ false
2652
+ }
2653
+
2654
+ /// Return `None` only if we are sure this type does
2655
+ /// allow being left uninitialized.
2656
+ fn ty_find_init_error < ' tcx > ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> Option < InitError > {
2657
+ use rustc_type_ir:: sty:: TyKind :: * ;
2658
+ match ty. kind ( ) {
2659
+ // Primitive types that don't like 0 as a value.
2660
+ Ref ( ..) => Some ( ( "references must be non-null" . to_string ( ) , None ) ) ,
2661
+ Adt ( ..) if ty. is_box ( ) => Some ( ( "`Box` must be non-null" . to_string ( ) , None ) ) ,
2662
+ FnPtr ( ..) => Some ( ( "function pointers must be non-null" . to_string ( ) , None ) ) ,
2663
+ Never => Some ( ( "the `!` type has no valid value" . to_string ( ) , None ) ) ,
2664
+ RawPtr ( tm) if matches ! ( tm. ty. kind( ) , Dynamic ( ..) ) =>
2665
+ // raw ptr to dyn Trait
2666
+ {
2667
+ Some ( ( "the vtable of a wide raw pointer must be non-null" . to_string ( ) , None ) )
2668
+ }
2669
+ // Primitive types with other constraints.
2670
+ Bool => Some ( ( "booleans must be either `true` or `false`" . to_string ( ) , None ) ) ,
2671
+ Char => Some ( ( "characters must be a valid Unicode codepoint" . to_string ( ) , None ) ) ,
2672
+ Adt ( adt_def, _) if adt_def. is_union ( ) => None ,
2673
+ // Recurse and checks for some compound types.
2674
+ Adt ( adt_def, substs) => {
2675
+ // First check if this ADT has a layout attribute (like `NonNull` and friends).
2676
+ use std:: ops:: Bound ;
2677
+ match cx. tcx . layout_scalar_valid_range ( adt_def. did ( ) ) {
2678
+ // We exploit here that `layout_scalar_valid_range` will never
2679
+ // return `Bound::Excluded`. (And we have tests checking that we
2680
+ // handle the attribute correctly.)
2681
+ ( Bound :: Included ( lo) , _) if lo > 0 => {
2682
+ return Some ( ( format ! ( "`{}` must be non-null" , ty) , None ) ) ;
2683
+ }
2684
+ ( Bound :: Included ( _) , _) | ( _, Bound :: Included ( _) ) => {
2685
+ return Some ( (
2686
+ format ! (
2687
+ "`{}` must be initialized inside its custom valid range" ,
2688
+ ty,
2689
+ ) ,
2690
+ None ,
2691
+ ) ) ;
2692
+ }
2693
+ _ => { }
2694
+ }
2695
+ // Now, recurse.
2696
+ match adt_def. variants ( ) . len ( ) {
2697
+ 0 => Some ( ( "enums with no variants have no valid value" . to_string ( ) , None ) ) ,
2698
+ 1 => {
2699
+ // Struct, or enum with exactly one variant.
2700
+ // Proceed recursively, check all fields.
2701
+ let variant = & adt_def. variant ( VariantIdx :: from_u32 ( 0 ) ) ;
2702
+ variant. fields . iter ( ) . find_map ( |field| {
2703
+ ty_find_init_error ( cx, field. ty ( cx. tcx , substs) ) . map (
2704
+ |( mut msg, span) | {
2705
+ if span. is_none ( ) {
2706
+ // Point to this field, should be helpful for figuring
2707
+ // out where the source of the error is.
2708
+ let span = cx. tcx . def_span ( field. did ) ;
2709
+ write ! (
2710
+ & mut msg,
2711
+ " (in this {} field)" ,
2712
+ adt_def. descr( )
2713
+ )
2714
+ . unwrap ( ) ;
2715
+ ( msg, Some ( span) )
2716
+ } else {
2717
+ // Just forward.
2718
+ ( msg, span)
2719
+ }
2720
+ } ,
2721
+ )
2722
+ } )
2723
+ }
2724
+ // Multi-variant enum.
2725
+ _ => {
2726
+ // This will warn on something like Result<MaybeUninit<u32>, !> which
2727
+ // is not UB under the current enum layout, even ignoring the 0x01
2728
+ // filling.
2729
+ //
2730
+ // That's probably fine though.
2731
+ let span = cx. tcx . def_span ( adt_def. did ( ) ) ;
2732
+ Some ( (
2733
+ "enums have to be initialized to a variant" . to_string ( ) ,
2734
+ Some ( span) ,
2735
+ ) )
2736
+ }
2737
+ }
2738
+ }
2739
+ Tuple ( ..) => {
2740
+ // Proceed recursively, check all fields.
2741
+ ty. tuple_fields ( ) . iter ( ) . find_map ( |field| ty_find_init_error ( cx, field) )
2742
+ }
2743
+ Array ( ty, len) => {
2744
+ match len. try_eval_usize ( cx. tcx , cx. param_env ) {
2745
+ // Array known to be zero sized, we can't warn.
2746
+ Some ( 0 ) => None ,
2747
+
2748
+ Some ( _) | None => ty_find_init_error ( cx, * ty) ,
2749
+ }
2750
+ }
2751
+ Int ( _) | Uint ( _) | Float ( _) | RawPtr ( _) => {
2752
+ // These are Plain Old Data types that people expect to work if they leave them
2753
+ // uninitialized.
2754
+ None
2755
+ }
2756
+ // Pessimistic fallback.
2757
+ _ => Some ( ( "type might not be allowed to be left uninitialized" . to_string ( ) , None ) ) ,
2758
+ }
2759
+ }
2760
+
2761
+ if is_dangerous_init ( cx, expr) {
2762
+ // This conjures an instance of a type out of nothing,
2763
+ // using zeroed or uninitialized memory.
2764
+ // We are extremely conservative with what we warn about.
2765
+ let conjured_ty = cx. typeck_results ( ) . expr_ty ( expr) ;
2766
+ if let Some ( ( msg, span) ) = with_no_trimmed_paths ! ( ty_find_init_error( cx, conjured_ty) ) {
2767
+ // FIXME(davidtwco): make translatable
2768
+ cx. struct_span_lint ( MEM_UNINITIALIZED , expr. span , |lint| {
2769
+ let mut err = with_no_trimmed_paths ! ( lint. build( & format!(
2770
+ "the type `{}` does not definitely permit being left uninitialized" ,
2771
+ conjured_ty,
2772
+ ) ) ) ;
2773
+
2774
+ err. span_label ( expr. span , "this code causes undefined behavior when executed" ) ;
2775
+ err. span_label (
2776
+ expr. span ,
2777
+ "help: use `MaybeUninit<T>` instead, \
2778
+ and only call `assume_init` after initialization is done",
2779
+ ) ;
2780
+ if let Some ( span) = span {
2781
+ err. span_note ( span, & msg) ;
2782
+ } else {
2783
+ err. note ( & msg) ;
2784
+ }
2785
+ err. emit ( ) ;
2786
+ } ) ;
2787
+ }
2788
+ }
2789
+ }
2790
+ }
2791
+
2594
2792
declare_lint ! {
2595
2793
/// The `clashing_extern_declarations` lint detects when an `extern fn`
2596
2794
/// has been declared with the same name but different types.
0 commit comments