@@ -37,59 +37,73 @@ declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
37
37
38
38
impl < ' tcx > LateLintPass < ' tcx > for InvalidReferenceCasting {
39
39
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
+ }
61
67
}
62
68
}
63
69
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 > (
65
77
cx : & LateContext < ' tcx > ,
66
78
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 } )
72
86
// <expr> = ...
73
87
} else if let ExprKind :: Assign ( expr, _, _) = expr. kind {
74
- expr
88
+ ( expr, PatternKind :: Assign )
75
89
// <expr> += ...
76
90
} else if let ExprKind :: AssignOp ( _, expr, _) = expr. kind {
77
- expr
91
+ ( expr, PatternKind :: Assign )
78
92
} else {
79
93
return None ;
80
94
} ;
81
95
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 ) )
87
101
}
88
102
89
103
fn ptr_write < ' tcx > (
90
104
cx : & LateContext < ' tcx > ,
91
105
e : & ' tcx Expr < ' tcx > ,
92
- ) -> Option < ( bool , & ' tcx Expr < ' tcx > ) > {
106
+ ) -> Option < ( & ' tcx Expr < ' tcx > , PatternKind ) > {
93
107
if let ExprKind :: Call ( path, [ arg_ptr, _arg_val] ) = e. kind
94
108
&& let ExprKind :: Path ( ref qpath) = path. kind
95
109
&& 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>(
98
112
Some ( sym:: ptr_write | sym:: ptr_write_volatile | sym:: ptr_write_unaligned)
99
113
)
100
114
{
101
- Some ( ( true , arg_ptr ) )
115
+ Some ( ( arg_ptr , PatternKind :: Assign ) )
102
116
} else {
103
117
None
104
118
}
@@ -107,20 +121,39 @@ fn is_operation_we_care_about<'tcx>(
107
121
deref_assign_or_addr_of ( e) . or_else ( || ptr_write ( cx, e) )
108
122
}
109
123
110
- fn is_cast_from_const_to_mut < ' tcx > (
124
+ fn is_cast_from_ref_to_mut_ptr < ' tcx > (
111
125
cx : & LateContext < ' tcx > ,
112
126
orig_expr : & ' tcx Expr < ' tcx > ,
113
127
) -> Option < bool > {
114
- let mut need_check_freeze = false ;
115
- let mut e = orig_expr;
116
-
117
128
let end_ty = cx. typeck_results ( ) . node_type ( orig_expr. hir_id ) ;
118
129
119
130
// Bail out early if the end type is **not** a mutable pointer.
120
131
if !matches ! ( end_ty. kind( ) , ty:: RawPtr ( TypeAndMut { ty: _, mutbl: Mutability :: Mut } ) ) {
121
132
return None ;
122
133
}
123
134
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
+
124
157
loop {
125
158
e = e. peel_blocks ( ) ;
126
159
// <expr> as ...
@@ -145,27 +178,18 @@ fn is_cast_from_const_to_mut<'tcx>(
145
178
)
146
179
{
147
180
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 ;
149
182
}
150
183
arg
151
184
} 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
+ }
153
191
} ;
154
192
}
155
193
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)
171
195
}
0 commit comments