@@ -19,6 +19,7 @@ mod modulo_one;
1919mod needless_bitwise_bool;
2020mod numeric_arithmetic;
2121mod op_ref;
22+ mod raw_assign_to_drop;
2223mod self_assignment;
2324mod verbose_bit_mask;
2425
@@ -807,6 +808,67 @@ declare_clippy_lint! {
807808 "explicit self-assignment"
808809}
809810
811+ declare_clippy_lint ! {
812+ /// ### What it does
813+ ///
814+ /// Checks for assignments via raw pointers that involve types with destructors.
815+ ///
816+ /// ### Why restrict this?
817+ ///
818+ /// Assignments of the form `*ptr = new_value;` assume that `*ptr` contains an initialized
819+ /// value, and unconditionally execute the `std::ops::Drop`-implementation if such
820+ /// implementation is defined on the type of `*ptr`. If the old value is in fact
821+ /// uninitialized or otherwise invalid, the execution of `std::ops::Drop::drop(&mut self)`
822+ /// is always Undefined Behavior.
823+ ///
824+ /// The unsafe-block required to assign via the raw-pointer should include a SAFETY-comment
825+ /// that explains why it is always safe to execute the destructor.
826+ ///
827+ /// If the code can not guarantee that the old value can be safely dropped, use
828+ /// `std::ptr::write()` to overwrite the (possibly nonexisting) value without executing
829+ /// the destructor; this may leak resources if the old value did in fact exist.
830+ ///
831+ /// Use `std::ptr::drop_in_place()` to (selectively) execute the destructor, having
832+ /// established that it is safe to do so.
833+ ///
834+ /// ### Example
835+ /// ```no_run
836+ /// unsafe fn foo(oldvalue: *mut String) {
837+ /// // Direct assignment always executes `String`'s destructor on `oldvalue`.
838+ /// // However, we can't guarantee that executing the destructor is safe.
839+ /// unsafe { *oldvalue = "New Value".to_owned(); }
840+ /// }
841+ /// ```
842+ /// Use instead:
843+ /// ```no_run
844+ /// #[allow(clippy::raw_assign_to_drop)]
845+ /// unsafe fn foo(oldvalue: *mut String, oldvalue_is_initialized: bool) {
846+ /// let newvalue = "New Value".to_owned();
847+ ///
848+ /// // In this example, `oldvalue_is_initialized` is an oracle for what
849+ /// // the programmer might be able to establish statically.
850+ /// if oldvalue_is_initialized {
851+ /// // Having established that `oldvalue` points to a valid value, it is safe to
852+ /// // execute the destructor and assign a new value.
853+ ///
854+ /// // SAFETY: oldvalue is properly aligned, valid for writes, and initialized
855+ /// unsafe {
856+ /// *oldvalue = newvalue;
857+ /// }
858+ /// } else {
859+ /// // Overwrite the old value without running the destructor.
860+ ///
861+ /// // SAFETY: oldvalue is properly aligned and valid for writes
862+ /// unsafe { oldvalue.write(newvalue); }
863+ /// }
864+ /// }
865+ /// ```
866+ #[ clippy:: version = "1.91.0" ]
867+ pub RAW_ASSIGN_TO_DROP ,
868+ restriction,
869+ "assignment via raw pointer that involves destructors"
870+ }
871+
810872declare_clippy_lint ! {
811873 /// ### What it does
812874 /// Checks for manual implementation of `midpoint`.
@@ -904,6 +966,7 @@ impl_lint_pass!(Operators => [
904966 MODULO_ARITHMETIC ,
905967 NEEDLESS_BITWISE_BOOL ,
906968 SELF_ASSIGNMENT ,
969+ RAW_ASSIGN_TO_DROP ,
907970 MANUAL_MIDPOINT ,
908971 MANUAL_IS_MULTIPLE_OF ,
909972] ) ;
@@ -954,6 +1017,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
9541017 ExprKind :: Assign ( lhs, rhs, _) => {
9551018 assign_op_pattern:: check ( cx, e, lhs, rhs, self . msrv ) ;
9561019 self_assignment:: check ( cx, e, lhs, rhs) ;
1020+ raw_assign_to_drop:: check ( cx, lhs) ;
9571021 } ,
9581022 ExprKind :: Unary ( op, arg) => {
9591023 if op == UnOp :: Neg {
0 commit comments