@@ -37,59 +37,73 @@ declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
3737
3838impl < ' tcx > LateLintPass < ' tcx > for InvalidReferenceCasting {
3939 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
40- let Some ( ( is_assignment, e) ) = is_operation_we_care_about ( cx, expr) else {
41- return ;
42- } ;
43-
44- let init = cx. expr_or_init ( e) ;
45-
46- let Some ( ty_has_interior_mutability) = is_cast_from_const_to_mut ( cx, init) else {
47- return ;
48- } ;
49- let orig_cast = if init. span != e. span { Some ( init. span ) } else { None } ;
50- let ty_has_interior_mutability = ty_has_interior_mutability. then_some ( ( ) ) ;
51-
52- cx. emit_spanned_lint (
53- INVALID_REFERENCE_CASTING ,
54- expr. span ,
55- if is_assignment {
56- InvalidReferenceCastingDiag :: AssignToRef { orig_cast, ty_has_interior_mutability }
57- } else {
58- InvalidReferenceCastingDiag :: BorrowAsMut { orig_cast, ty_has_interior_mutability }
59- } ,
60- ) ;
40+ if let Some ( ( e, pat) ) = borrow_or_assign ( cx, expr) {
41+ if matches ! ( pat, PatternKind :: Borrow { mutbl: Mutability :: Mut } | PatternKind :: Assign ) {
42+ let init = cx. expr_or_init ( e) ;
43+
44+ let Some ( ty_has_interior_mutability) = is_cast_from_ref_to_mut_ptr ( cx, init) else {
45+ return ;
46+ } ;
47+ let orig_cast = if init. span != e. span { Some ( init. span ) } else { None } ;
48+ let ty_has_interior_mutability = ty_has_interior_mutability. then_some ( ( ) ) ;
49+
50+ cx. emit_spanned_lint (
51+ INVALID_REFERENCE_CASTING ,
52+ expr. span ,
53+ if pat == PatternKind :: Assign {
54+ InvalidReferenceCastingDiag :: AssignToRef {
55+ orig_cast,
56+ ty_has_interior_mutability,
57+ }
58+ } else {
59+ InvalidReferenceCastingDiag :: BorrowAsMut {
60+ orig_cast,
61+ ty_has_interior_mutability,
62+ }
63+ } ,
64+ ) ;
65+ }
66+ }
6167 }
6268}
6369
64- fn is_operation_we_care_about < ' tcx > (
70+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
71+ enum PatternKind {
72+ Borrow { mutbl : Mutability } ,
73+ Assign ,
74+ }
75+
76+ fn borrow_or_assign < ' tcx > (
6577 cx : & LateContext < ' tcx > ,
6678 e : & ' tcx Expr < ' tcx > ,
67- ) -> Option < ( bool , & ' tcx Expr < ' tcx > ) > {
68- fn deref_assign_or_addr_of < ' tcx > ( expr : & ' tcx Expr < ' tcx > ) -> Option < ( bool , & ' tcx Expr < ' tcx > ) > {
69- // &mut <expr>
70- let inner = if let ExprKind :: AddrOf ( _, Mutability :: Mut , expr) = expr. kind {
71- expr
79+ ) -> Option < ( & ' tcx Expr < ' tcx > , PatternKind ) > {
80+ fn deref_assign_or_addr_of < ' tcx > (
81+ expr : & ' tcx Expr < ' tcx > ,
82+ ) -> Option < ( & ' tcx Expr < ' tcx > , PatternKind ) > {
83+ // &(mut) <expr>
84+ let ( inner, pat) = if let ExprKind :: AddrOf ( _, mutbl, expr) = expr. kind {
85+ ( expr, PatternKind :: Borrow { mutbl } )
7286 // <expr> = ...
7387 } else if let ExprKind :: Assign ( expr, _, _) = expr. kind {
74- expr
88+ ( expr, PatternKind :: Assign )
7589 // <expr> += ...
7690 } else if let ExprKind :: AssignOp ( _, expr, _) = expr. kind {
77- expr
91+ ( expr, PatternKind :: Assign )
7892 } else {
7993 return None ;
8094 } ;
8195
82- if let ExprKind :: Unary ( UnOp :: Deref , e ) = & inner. kind {
83- Some ( ( ! matches ! ( expr . kind , ExprKind :: AddrOf ( .. ) ) , e) )
84- } else {
85- None
86- }
96+ // *< inner>
97+ let ExprKind :: Unary ( UnOp :: Deref , e) = & inner . kind else {
98+ return None ;
99+ } ;
100+ Some ( ( e , pat ) )
87101 }
88102
89103 fn ptr_write < ' tcx > (
90104 cx : & LateContext < ' tcx > ,
91105 e : & ' tcx Expr < ' tcx > ,
92- ) -> Option < ( bool , & ' tcx Expr < ' tcx > ) > {
106+ ) -> Option < ( & ' tcx Expr < ' tcx > , PatternKind ) > {
93107 if let ExprKind :: Call ( path, [ arg_ptr, _arg_val] ) = e. kind
94108 && let ExprKind :: Path ( ref qpath) = path. kind
95109 && let Some ( def_id) = cx. qpath_res ( qpath, path. hir_id ) . opt_def_id ( )
@@ -98,7 +112,7 @@ fn is_operation_we_care_about<'tcx>(
98112 Some ( sym:: ptr_write | sym:: ptr_write_volatile | sym:: ptr_write_unaligned)
99113 )
100114 {
101- Some ( ( true , arg_ptr ) )
115+ Some ( ( arg_ptr , PatternKind :: Assign ) )
102116 } else {
103117 None
104118 }
@@ -107,20 +121,39 @@ fn is_operation_we_care_about<'tcx>(
107121 deref_assign_or_addr_of ( e) . or_else ( || ptr_write ( cx, e) )
108122}
109123
110- fn is_cast_from_const_to_mut < ' tcx > (
124+ fn is_cast_from_ref_to_mut_ptr < ' tcx > (
111125 cx : & LateContext < ' tcx > ,
112126 orig_expr : & ' tcx Expr < ' tcx > ,
113127) -> Option < bool > {
114- let mut need_check_freeze = false ;
115- let mut e = orig_expr;
116-
117128 let end_ty = cx. typeck_results ( ) . node_type ( orig_expr. hir_id ) ;
118129
119130 // Bail out early if the end type is **not** a mutable pointer.
120131 if !matches ! ( end_ty. kind( ) , ty:: RawPtr ( TypeAndMut { ty: _, mutbl: Mutability :: Mut } ) ) {
121132 return None ;
122133 }
123134
135+ let ( e, need_check_freeze) = peel_casts ( cx, orig_expr) ;
136+
137+ let start_ty = cx. typeck_results ( ) . node_type ( e. hir_id ) ;
138+ if let ty:: Ref ( _, inner_ty, Mutability :: Not ) = start_ty. kind ( ) {
139+ // If an UnsafeCell method is involved, we need to additionally check the
140+ // inner type for the presence of the Freeze trait (ie does NOT contain
141+ // an UnsafeCell), since in that case we would incorrectly lint on valid casts.
142+ //
143+ // Except on the presence of non concrete skeleton types (ie generics)
144+ // since there is no way to make it safe for arbitrary types.
145+ let inner_ty_has_interior_mutability =
146+ !inner_ty. is_freeze ( cx. tcx , cx. param_env ) && inner_ty. has_concrete_skeleton ( ) ;
147+ ( !need_check_freeze || !inner_ty_has_interior_mutability)
148+ . then_some ( inner_ty_has_interior_mutability)
149+ } else {
150+ None
151+ }
152+ }
153+
154+ fn peel_casts < ' tcx > ( cx : & LateContext < ' tcx > , mut e : & ' tcx Expr < ' tcx > ) -> ( & ' tcx Expr < ' tcx > , bool ) {
155+ let mut gone_trough_unsafe_cell_raw_get = false ;
156+
124157 loop {
125158 e = e. peel_blocks ( ) ;
126159 // <expr> as ...
@@ -145,27 +178,18 @@ fn is_cast_from_const_to_mut<'tcx>(
145178 )
146179 {
147180 if cx. tcx . is_diagnostic_item ( sym:: unsafe_cell_raw_get, def_id) {
148- need_check_freeze = true ;
181+ gone_trough_unsafe_cell_raw_get = true ;
149182 }
150183 arg
151184 } else {
152- break ;
185+ let init = cx. expr_or_init ( e) ;
186+ if init. hir_id != e. hir_id {
187+ init
188+ } else {
189+ break ;
190+ }
153191 } ;
154192 }
155193
156- let start_ty = cx. typeck_results ( ) . node_type ( e. hir_id ) ;
157- if let ty:: Ref ( _, inner_ty, Mutability :: Not ) = start_ty. kind ( ) {
158- // If an UnsafeCell method is involved we need to additionally check the
159- // inner type for the presence of the Freeze trait (ie does NOT contain
160- // an UnsafeCell), since in that case we would incorrectly lint on valid casts.
161- //
162- // We also consider non concrete skeleton types (ie generics)
163- // to be an issue since there is no way to make it safe for abitrary types.
164- let inner_ty_has_interior_mutability =
165- !inner_ty. is_freeze ( cx. tcx , cx. param_env ) && inner_ty. has_concrete_skeleton ( ) ;
166- ( !need_check_freeze || !inner_ty_has_interior_mutability)
167- . then_some ( inner_ty_has_interior_mutability)
168- } else {
169- None
170- }
194+ ( e, gone_trough_unsafe_cell_raw_get)
171195}
0 commit comments