@@ -19,7 +19,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
19
19
use rustc_middle:: hir:: map:: Map ;
20
20
use rustc_middle:: lint:: in_external_macro;
21
21
use rustc_middle:: ty:: TypeFoldable ;
22
- use rustc_middle:: ty:: { self , InferTy , Ty , TyCtxt , TyS , TypeckResults } ;
22
+ use rustc_middle:: ty:: { self , InferTy , Ty , TyCtxt , TyS , TypeAndMut , TypeckResults } ;
23
+ use rustc_semver:: RustcVersion ;
23
24
use rustc_session:: { declare_lint_pass, declare_tool_lint, impl_lint_pass} ;
24
25
use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
25
26
use rustc_span:: source_map:: Span ;
@@ -30,11 +31,13 @@ use rustc_typeck::hir_ty_to_ty;
30
31
31
32
use crate :: consts:: { constant, Constant } ;
32
33
use crate :: utils:: paths;
34
+ use crate :: utils:: sugg:: Sugg ;
33
35
use crate :: utils:: {
34
36
clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item,
35
- last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal:: NumericLiteral ,
36
- qpath_res, reindent_multiline, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite,
37
- span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
37
+ last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args, multispan_sugg,
38
+ numeric_literal:: NumericLiteral , qpath_res, reindent_multiline, sext, snippet, snippet_opt,
39
+ snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg,
40
+ span_lint_and_then, unsext,
38
41
} ;
39
42
40
43
declare_clippy_lint ! {
@@ -2878,3 +2881,93 @@ impl<'tcx> LateLintPass<'tcx> for RefToMut {
2878
2881
}
2879
2882
}
2880
2883
}
2884
+
2885
+ const PTR_AS_PTR_MSRV : RustcVersion = RustcVersion :: new ( 1 , 38 , 0 ) ;
2886
+
2887
+ declare_clippy_lint ! {
2888
+ /// **What it does:**
2889
+ /// Checks for `as` casts between raw pointers without changing its mutability,
2890
+ /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
2891
+ ///
2892
+ /// **Why is this bad?**
2893
+ /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
2894
+ /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
2895
+ ///
2896
+ /// **Known problems:** None.
2897
+ ///
2898
+ /// **Example:**
2899
+ ///
2900
+ /// ```rust
2901
+ /// let ptr: *const u32 = &42_u32;
2902
+ /// let mut_ptr: *mut u32 = &mut 42_u32;
2903
+ /// let _ = ptr as *const i32;
2904
+ /// let _ = mut_ptr as *mut i32;
2905
+ /// ```
2906
+ /// Use instead:
2907
+ /// ```rust
2908
+ /// let ptr: *const u32 = &42_u32;
2909
+ /// let mut_ptr: *mut u32 = &mut 42_u32;
2910
+ /// let _ = ptr.cast::<i32>();
2911
+ /// let _ = mut_ptr.cast::<i32>();
2912
+ /// ```
2913
+ pub PTR_AS_PTR ,
2914
+ pedantic,
2915
+ "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
2916
+ }
2917
+
2918
+ pub struct PtrAsPtr {
2919
+ msrv : Option < RustcVersion > ,
2920
+ }
2921
+
2922
+ impl PtrAsPtr {
2923
+ #[ must_use]
2924
+ pub fn new ( msrv : Option < RustcVersion > ) -> Self {
2925
+ Self { msrv }
2926
+ }
2927
+ }
2928
+
2929
+ impl_lint_pass ! ( PtrAsPtr => [ PTR_AS_PTR ] ) ;
2930
+
2931
+ impl < ' tcx > LateLintPass < ' tcx > for PtrAsPtr {
2932
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
2933
+ if !meets_msrv ( self . msrv . as_ref ( ) , & PTR_AS_PTR_MSRV ) {
2934
+ return ;
2935
+ }
2936
+
2937
+ if expr. span . from_expansion ( ) {
2938
+ return ;
2939
+ }
2940
+
2941
+ if_chain ! {
2942
+ if let ExprKind :: Cast ( cast_expr, cast_to_hir_ty) = expr. kind;
2943
+ let ( cast_from, cast_to) = ( cx. typeck_results( ) . expr_ty( cast_expr) , cx. typeck_results( ) . expr_ty( expr) ) ;
2944
+ if let ty:: RawPtr ( TypeAndMut { mutbl: from_mutbl, .. } ) = cast_from. kind( ) ;
2945
+ if let ty:: RawPtr ( TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl } ) = cast_to. kind( ) ;
2946
+ if matches!( ( from_mutbl, to_mutbl) ,
2947
+ ( Mutability :: Not , Mutability :: Not ) | ( Mutability :: Mut , Mutability :: Mut ) ) ;
2948
+ // The `U` in `pointer::cast` have to be `Sized`
2949
+ // as explained here: https://github.com/rust-lang/rust/issues/60602.
2950
+ if to_pointee_ty. is_sized( cx. tcx. at( expr. span) , cx. param_env) ;
2951
+ then {
2952
+ let mut applicability = Applicability :: MachineApplicable ;
2953
+ let cast_expr_sugg = Sugg :: hir_with_applicability( cx, cast_expr, "_" , & mut applicability) ;
2954
+ let turbofish = match & cast_to_hir_ty. kind {
2955
+ TyKind :: Infer => Cow :: Borrowed ( "" ) ,
2956
+ TyKind :: Ptr ( mut_ty) if matches!( mut_ty. ty. kind, TyKind :: Infer ) => Cow :: Borrowed ( "" ) ,
2957
+ _ => Cow :: Owned ( format!( "::<{}>" , to_pointee_ty) ) ,
2958
+ } ;
2959
+ span_lint_and_sugg(
2960
+ cx,
2961
+ PTR_AS_PTR ,
2962
+ expr. span,
2963
+ "`as` casting between raw pointers without changing its mutability" ,
2964
+ "try `pointer::cast`, a safer alternative" ,
2965
+ format!( "{}.cast{}()" , cast_expr_sugg. maybe_par( ) , turbofish) ,
2966
+ applicability,
2967
+ ) ;
2968
+ }
2969
+ }
2970
+ }
2971
+
2972
+ extract_msrv_attr ! ( LateContext ) ;
2973
+ }
0 commit comments