@@ -148,7 +148,36 @@ declare_clippy_lint! {
148
148
"invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
149
149
}
150
150
151
- declare_lint_pass ! ( Ptr => [ PTR_ARG , CMP_NULL , MUT_FROM_REF , INVALID_NULL_PTR_USAGE ] ) ;
151
+ declare_clippy_lint ! {
152
+ /// ### What it does
153
+ /// Use `std::ptr::eq` when applicable
154
+ ///
155
+ /// ### Why is this bad?
156
+ /// `ptr::eq` can be used to compare `&T` references
157
+ /// (which coerce to `*const T` implicitly) by their address rather than
158
+ /// comparing the values they point to.
159
+ ///
160
+ /// ### Example
161
+ /// ```no_run
162
+ /// let a = &[1, 2, 3];
163
+ /// let b = &[1, 2, 3];
164
+ ///
165
+ /// assert!(a as *const _ as usize == b as *const _ as usize);
166
+ /// ```
167
+ /// Use instead:
168
+ /// ```no_run
169
+ /// let a = &[1, 2, 3];
170
+ /// let b = &[1, 2, 3];
171
+ ///
172
+ /// assert!(std::ptr::eq(a, b));
173
+ /// ```
174
+ #[ clippy:: version = "1.49.0" ]
175
+ pub PTR_EQ ,
176
+ style,
177
+ "use `std::ptr::eq` when comparing raw pointers"
178
+ }
179
+
180
+ declare_lint_pass ! ( Ptr => [ PTR_ARG , CMP_NULL , MUT_FROM_REF , INVALID_NULL_PTR_USAGE , PTR_EQ ] ) ;
152
181
153
182
impl < ' tcx > LateLintPass < ' tcx > for Ptr {
154
183
fn check_trait_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx TraitItem < ' _ > ) {
@@ -253,10 +282,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
253
282
if let ExprKind :: Binary ( op, l, r) = expr. kind
254
283
&& ( op. node == BinOpKind :: Eq || op. node == BinOpKind :: Ne )
255
284
{
256
- let non_null_path_snippet = match ( is_null_path ( cx, l) , is_null_path ( cx, r) ) {
257
- ( true , false ) if let Some ( sugg) = Sugg :: hir_opt ( cx, r) => sugg. maybe_par ( ) ,
258
- ( false , true ) if let Some ( sugg) = Sugg :: hir_opt ( cx, l) => sugg. maybe_par ( ) ,
259
- _ => return ,
285
+ let non_null_path_snippet = match (
286
+ is_lint_allowed ( cx, CMP_NULL , expr. hir_id ) ,
287
+ is_null_path ( cx, l) ,
288
+ is_null_path ( cx, r) ,
289
+ ) {
290
+ ( false , true , false ) if let Some ( sugg) = Sugg :: hir_opt ( cx, r) => sugg. maybe_par ( ) ,
291
+ ( false , false , true ) if let Some ( sugg) = Sugg :: hir_opt ( cx, l) => sugg. maybe_par ( ) ,
292
+ _ => return check_ptr_eq ( cx, expr, op. node , l, r) ,
260
293
} ;
261
294
262
295
span_lint_and_sugg (
@@ -740,3 +773,71 @@ fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
740
773
false
741
774
}
742
775
}
776
+
777
+ fn check_ptr_eq < ' tcx > (
778
+ cx : & LateContext < ' tcx > ,
779
+ expr : & ' tcx Expr < ' _ > ,
780
+ op : BinOpKind ,
781
+ left : & ' tcx Expr < ' _ > ,
782
+ right : & ' tcx Expr < ' _ > ,
783
+ ) {
784
+ if expr. span . from_expansion ( ) {
785
+ return ;
786
+ }
787
+
788
+ // Remove one level of usize conversion if any
789
+ let ( left, right) = match ( expr_as_cast_to_usize ( cx, left) , expr_as_cast_to_usize ( cx, right) ) {
790
+ ( Some ( lhs) , Some ( rhs) ) => ( lhs, rhs) ,
791
+ _ => ( left, right) ,
792
+ } ;
793
+
794
+ // This lint concerns raw pointers
795
+ let ( left_ty, right_ty) = ( cx. typeck_results ( ) . expr_ty ( left) , cx. typeck_results ( ) . expr_ty ( right) ) ;
796
+ if !left_ty. is_raw_ptr ( ) || !right_ty. is_raw_ptr ( ) {
797
+ return ;
798
+ }
799
+
800
+ let ( left_var, right_var) = ( peel_raw_casts ( cx, left, left_ty) , peel_raw_casts ( cx, right, right_ty) ) ;
801
+
802
+ if let Some ( left_snip) = left_var. span . get_source_text ( cx)
803
+ && let Some ( right_snip) = right_var. span . get_source_text ( cx)
804
+ {
805
+ let Some ( top_crate) = std_or_core ( cx) else { return } ;
806
+ let invert = if op == BinOpKind :: Eq { "" } else { "!" } ;
807
+ span_lint_and_sugg (
808
+ cx,
809
+ PTR_EQ ,
810
+ expr. span ,
811
+ format ! ( "use `{top_crate}::ptr::eq` when comparing raw pointers" ) ,
812
+ "try" ,
813
+ format ! ( "{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})" ) ,
814
+ Applicability :: MachineApplicable ,
815
+ ) ;
816
+ }
817
+ }
818
+
819
+ // If the given expression is a cast to a usize, return the lhs of the cast
820
+ // E.g., `foo as *const _ as usize` returns `foo as *const _`.
821
+ fn expr_as_cast_to_usize < ' tcx > ( cx : & LateContext < ' tcx > , cast_expr : & ' tcx Expr < ' _ > ) -> Option < & ' tcx Expr < ' tcx > > {
822
+ if cx. typeck_results ( ) . expr_ty ( cast_expr) == cx. tcx . types . usize
823
+ && let ExprKind :: Cast ( expr, _) = cast_expr. kind
824
+ {
825
+ Some ( expr)
826
+ } else {
827
+ None
828
+ }
829
+ }
830
+
831
+ // Peel raw casts if the remaining expression can be coerced to it
832
+ fn peel_raw_casts < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , expr_ty : Ty < ' tcx > ) -> & ' tcx Expr < ' tcx > {
833
+ if let ExprKind :: Cast ( inner, _) = expr. kind
834
+ && let ty:: RawPtr ( target_ty, _) = expr_ty. kind ( )
835
+ && let inner_ty = cx. typeck_results ( ) . expr_ty ( inner)
836
+ && let ty:: RawPtr ( inner_target_ty, _) | ty:: Ref ( _, inner_target_ty, _) = inner_ty. kind ( )
837
+ && target_ty == inner_target_ty
838
+ {
839
+ peel_raw_casts ( cx, inner, inner_ty)
840
+ } else {
841
+ expr
842
+ }
843
+ }
0 commit comments