@@ -19,7 +19,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
1919use rustc_middle:: hir:: map:: Map ;
2020use rustc_middle:: lint:: in_external_macro;
2121use 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 ;
2324use rustc_session:: { declare_lint_pass, declare_tool_lint, impl_lint_pass} ;
2425use rustc_span:: hygiene:: { ExpnKind , MacroKind } ;
2526use rustc_span:: source_map:: Span ;
@@ -30,11 +31,13 @@ use rustc_typeck::hir_ty_to_ty;
3031
3132use crate :: consts:: { constant, Constant } ;
3233use crate :: utils:: paths;
34+ use crate :: utils:: sugg:: Sugg ;
3335use crate :: utils:: {
3436 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,
3841} ;
3942
4043declare_clippy_lint ! {
@@ -2878,3 +2881,93 @@ impl<'tcx> LateLintPass<'tcx> for RefToMut {
28782881 }
28792882 }
28802883}
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