@@ -8,6 +8,10 @@ use rustc_errors::Applicability;
8
8
use rustc_hir as hir;
9
9
use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
10
10
use rustc_lint:: LateContext ;
11
+ use rustc_middle:: mir:: FakeReadCause ;
12
+ use rustc_middle:: ty:: BorrowKind ;
13
+ use rustc_trait_selection:: infer:: TyCtxtInferExt ;
14
+ use rustc_typeck:: expr_use_visitor:: { Delegate , ExprUseVisitor , PlaceBase , PlaceWithHirId } ;
11
15
12
16
use super :: ASSIGN_OP_PATTERN ;
13
17
@@ -29,6 +33,16 @@ pub(super) fn check<'tcx>(
29
33
. map_or( true , |t| t. path. res. def_id( ) != trait_id) ;
30
34
if implements_trait( cx, ty, trait_id, & [ rty. into( ) ] ) ;
31
35
then {
36
+ // Primitive types execute assign-ops right-to-left. Every other type is left-to-right.
37
+ if !( ty. is_primitive( ) && rty. is_primitive( ) ) {
38
+ // TODO: This will have false negatives as it doesn't check if the borrows are
39
+ // actually live at the end of their respective expressions.
40
+ let mut_borrows = mut_borrows_in_expr( cx, assignee) ;
41
+ let imm_borrows = imm_borrows_in_expr( cx, rhs) ;
42
+ if mut_borrows. iter( ) . any( |id| imm_borrows. contains( id) ) {
43
+ return ;
44
+ }
45
+ }
32
46
span_lint_and_then(
33
47
cx,
34
48
ASSIGN_OP_PATTERN ,
@@ -99,3 +113,69 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
99
113
walk_expr ( self , expr) ;
100
114
}
101
115
}
116
+
117
+ fn imm_borrows_in_expr ( cx : & LateContext < ' _ > , e : & hir:: Expr < ' _ > ) -> hir:: HirIdSet {
118
+ struct S ( hir:: HirIdSet ) ;
119
+ impl Delegate < ' _ > for S {
120
+ fn borrow ( & mut self , place : & PlaceWithHirId < ' _ > , _: hir:: HirId , kind : BorrowKind ) {
121
+ if matches ! ( kind, BorrowKind :: ImmBorrow | BorrowKind :: UniqueImmBorrow ) {
122
+ self . 0 . insert ( match place. place . base {
123
+ PlaceBase :: Local ( id) => id,
124
+ PlaceBase :: Upvar ( id) => id. var_path . hir_id ,
125
+ _ => return ,
126
+ } ) ;
127
+ }
128
+ }
129
+
130
+ fn consume ( & mut self , _: & PlaceWithHirId < ' _ > , _: hir:: HirId ) { }
131
+ fn mutate ( & mut self , _: & PlaceWithHirId < ' _ > , _: hir:: HirId ) { }
132
+ fn fake_read ( & mut self , _: & PlaceWithHirId < ' _ > , _: FakeReadCause , _: hir:: HirId ) { }
133
+ fn copy ( & mut self , _: & PlaceWithHirId < ' _ > , _: hir:: HirId ) { }
134
+ }
135
+
136
+ let mut s = S ( hir:: HirIdSet :: default ( ) ) ;
137
+ cx. tcx . infer_ctxt ( ) . enter ( |infcx| {
138
+ let mut v = ExprUseVisitor :: new (
139
+ & mut s,
140
+ & infcx,
141
+ cx. tcx . hir ( ) . body_owner_def_id ( cx. enclosing_body . unwrap ( ) ) ,
142
+ cx. param_env ,
143
+ cx. typeck_results ( ) ,
144
+ ) ;
145
+ v. consume_expr ( e) ;
146
+ } ) ;
147
+ s. 0
148
+ }
149
+
150
+ fn mut_borrows_in_expr ( cx : & LateContext < ' _ > , e : & hir:: Expr < ' _ > ) -> hir:: HirIdSet {
151
+ struct S ( hir:: HirIdSet ) ;
152
+ impl Delegate < ' _ > for S {
153
+ fn borrow ( & mut self , place : & PlaceWithHirId < ' _ > , _: hir:: HirId , kind : BorrowKind ) {
154
+ if matches ! ( kind, BorrowKind :: MutBorrow ) {
155
+ self . 0 . insert ( match place. place . base {
156
+ PlaceBase :: Local ( id) => id,
157
+ PlaceBase :: Upvar ( id) => id. var_path . hir_id ,
158
+ _ => return ,
159
+ } ) ;
160
+ }
161
+ }
162
+
163
+ fn consume ( & mut self , _: & PlaceWithHirId < ' _ > , _: hir:: HirId ) { }
164
+ fn mutate ( & mut self , _: & PlaceWithHirId < ' _ > , _: hir:: HirId ) { }
165
+ fn fake_read ( & mut self , _: & PlaceWithHirId < ' _ > , _: FakeReadCause , _: hir:: HirId ) { }
166
+ fn copy ( & mut self , _: & PlaceWithHirId < ' _ > , _: hir:: HirId ) { }
167
+ }
168
+
169
+ let mut s = S ( hir:: HirIdSet :: default ( ) ) ;
170
+ cx. tcx . infer_ctxt ( ) . enter ( |infcx| {
171
+ let mut v = ExprUseVisitor :: new (
172
+ & mut s,
173
+ & infcx,
174
+ cx. tcx . hir ( ) . body_owner_def_id ( cx. enclosing_body . unwrap ( ) ) ,
175
+ cx. param_env ,
176
+ cx. typeck_results ( ) ,
177
+ ) ;
178
+ v. consume_expr ( e) ;
179
+ } ) ;
180
+ s. 0
181
+ }
0 commit comments