@@ -8,6 +8,10 @@ use rustc_errors::Applicability;
88use rustc_hir as hir;
99use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
1010use 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 } ;
1115
1216use super :: ASSIGN_OP_PATTERN ;
1317
@@ -29,6 +33,16 @@ pub(super) fn check<'tcx>(
2933 . map_or( true , |t| t. path. res. def_id( ) != trait_id) ;
3034 if implements_trait( cx, ty, trait_id, & [ rty. into( ) ] ) ;
3135 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+ }
3246 span_lint_and_then(
3347 cx,
3448 ASSIGN_OP_PATTERN ,
@@ -99,3 +113,69 @@ impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
99113 walk_expr ( self , expr) ;
100114 }
101115}
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