@@ -6,7 +6,7 @@ use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures};
6
6
use clippy_utils:: { ExprUseNode , expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id} ;
7
7
use core:: ops:: ControlFlow ;
8
8
use rustc_errors:: Applicability ;
9
- use rustc_hir:: { BindingMode , BorrowKind , ByRef , ClosureKind , Expr , ExprKind , Mutability , Node , PatKind } ;
9
+ use rustc_hir:: { BindingMode , BorrowKind , ByRef , ClosureKind , Expr , ExprKind , HirId , Mutability , Node , PatKind } ;
10
10
use rustc_lint:: LateContext ;
11
11
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
12
12
use rustc_span:: { DUMMY_SP , Span , Symbol , sym} ;
@@ -34,6 +34,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
34
34
{
35
35
let mut requires_copy = false ;
36
36
let mut requires_deref = false ;
37
+ let mut has_mut_use = false ;
37
38
38
39
// The number of unprocessed return expressions.
39
40
let mut ret_count = 0u32 ;
@@ -47,7 +48,8 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
47
48
// Nested closures don't need to treat returns specially.
48
49
let _: Option < !> = for_each_expr ( cx, cx. tcx . hir ( ) . body ( c. body ) . value , |e| {
49
50
if path_to_local_id ( e, arg_id) {
50
- let ( kind, same_ctxt) = check_use ( cx, e) ;
51
+ let ( kind, mutbl, same_ctxt) = check_use ( cx, e) ;
52
+ has_mut_use |= mutbl. is_mut ( ) ;
51
53
match ( kind, same_ctxt && e. span . ctxt ( ) == ctxt) {
52
54
( _, false ) | ( UseKind :: Deref | UseKind :: Return ( ..) , true ) => {
53
55
requires_copy = true ;
@@ -65,7 +67,8 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
65
67
} else if matches ! ( e. kind, ExprKind :: Ret ( _) ) {
66
68
ret_count += 1 ;
67
69
} else if path_to_local_id ( e, arg_id) {
68
- let ( kind, same_ctxt) = check_use ( cx, e) ;
70
+ let ( kind, mutbl, same_ctxt) = check_use ( cx, e) ;
71
+ has_mut_use |= mutbl. is_mut ( ) ;
69
72
match ( kind, same_ctxt && e. span . ctxt ( ) == ctxt) {
70
73
( UseKind :: Return ( ..) , false ) => {
71
74
return ControlFlow :: Break ( ( ) ) ;
@@ -161,6 +164,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
161
164
&& ( !requires_copy || cx. type_is_copy_modulo_regions ( arg_ty) )
162
165
// This case could be handled, but a fair bit of care would need to be taken.
163
166
&& ( !requires_deref || arg_ty. is_freeze ( cx. tcx , cx. typing_env ( ) ) )
167
+ && !has_mut_use
164
168
{
165
169
if requires_deref {
166
170
edits. push ( ( param. span . shrink_to_lo ( ) , "&" . into ( ) ) ) ;
@@ -207,36 +211,75 @@ enum UseKind<'tcx> {
207
211
FieldAccess ( Symbol , & ' tcx Expr < ' tcx > ) ,
208
212
}
209
213
210
- /// Checks how the value is used, and whether it was used in the same `SyntaxContext`.
211
- fn check_use < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) -> ( UseKind < ' tcx > , bool ) {
214
+ /// Checks how the value is used, mutability, and whether it was used in the same `SyntaxContext`.
215
+ fn check_use < ' tcx > ( cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) -> ( UseKind < ' tcx > , Mutability , bool ) {
212
216
let use_cx = expr_use_ctxt ( cx, e) ;
217
+ let mutbl = use_mutability ( cx, e. hir_id ) ;
213
218
if use_cx
214
219
. adjustments
215
220
. first ( )
216
221
. is_some_and ( |a| matches ! ( a. kind, Adjust :: Deref ( _) ) )
217
222
{
218
- return ( UseKind :: AutoBorrowed , use_cx. same_ctxt ) ;
223
+ return ( UseKind :: AutoBorrowed , mutbl , use_cx. same_ctxt ) ;
219
224
}
220
225
let res = match use_cx. use_node ( cx) {
221
226
ExprUseNode :: Return ( _) => {
222
227
if let ExprKind :: Ret ( Some ( e) ) = use_cx. node . expect_expr ( ) . kind {
223
228
UseKind :: Return ( e. span )
224
229
} else {
225
- return ( UseKind :: Return ( DUMMY_SP ) , false ) ;
230
+ return ( UseKind :: Return ( DUMMY_SP ) , mutbl , false ) ;
226
231
}
227
232
} ,
228
- ExprUseNode :: FieldAccess ( name) => UseKind :: FieldAccess ( name. name , use_cx. node . expect_expr ( ) ) ,
233
+ ExprUseNode :: FieldAccess ( _ , name) => UseKind :: FieldAccess ( name. name , use_cx. node . expect_expr ( ) ) ,
229
234
ExprUseNode :: Callee | ExprUseNode :: MethodArg ( _, _, 0 )
230
235
if use_cx
231
236
. adjustments
232
237
. first ( )
233
- . is_some_and ( |a| matches ! ( a. kind, Adjust :: Borrow ( AutoBorrow :: Ref ( AutoBorrowMutability :: Not ) ) ) ) =>
238
+ . is_some_and ( |a| matches ! ( a. kind, Adjust :: Borrow ( AutoBorrow :: Ref ( _ ) ) ) ) =>
234
239
{
235
240
UseKind :: AutoBorrowed
236
241
} ,
237
242
ExprUseNode :: Callee | ExprUseNode :: MethodArg ( _, _, 0 ) => UseKind :: WillAutoDeref ,
238
243
ExprUseNode :: AddrOf ( BorrowKind :: Ref , _) => UseKind :: Borrowed ( use_cx. node . expect_expr ( ) . span ) ,
239
244
_ => UseKind :: Deref ,
240
245
} ;
241
- ( res, use_cx. same_ctxt )
246
+ ( res, mutbl, use_cx. same_ctxt )
247
+ }
248
+
249
+ fn use_mutability ( cx : & LateContext < ' _ > , expr_id : HirId ) -> Mutability {
250
+ let adjusted = |expr : & Expr < ' _ > | -> Mutability {
251
+ let adj = cx. typeck_results ( ) . expr_adjustments ( expr) ;
252
+ if let Some ( Adjust :: Borrow ( AutoBorrow :: Ref ( mutbl) ) ) = adj. last ( ) . map ( |adj| & adj. kind ) {
253
+ ( * mutbl) . into ( )
254
+ } else {
255
+ Mutability :: Not
256
+ }
257
+ } ;
258
+
259
+ let mut last_child = None ;
260
+
261
+ for ( _, node) in cx. tcx . hir ( ) . parent_iter ( expr_id) {
262
+ if let Node :: Expr ( expr) = node {
263
+ match expr. kind {
264
+ ExprKind :: AddrOf ( _, mutbl, _) => return mutbl,
265
+ ExprKind :: MethodCall ( _, self_arg, _, _) => return adjusted ( self_arg) ,
266
+ ExprKind :: Call ( f, args) => return adjusted ( args. iter ( ) . find ( |arg| arg. hir_id == expr_id) . unwrap_or ( f) ) ,
267
+ ExprKind :: Field ( field, _) | ExprKind :: Index ( field, _, _) => last_child = Some ( field. hir_id ) ,
268
+ ExprKind :: Assign ( lhs, _, _) | ExprKind :: AssignOp ( _, lhs, _) => {
269
+ let is_lhs = match lhs. kind {
270
+ ExprKind :: Field ( child, _) | ExprKind :: Index ( child, _, _) => {
271
+ last_child. is_some_and ( |field_id| field_id == child. hir_id )
272
+ } ,
273
+ _ => false ,
274
+ } ;
275
+ if is_lhs {
276
+ return Mutability :: Mut ;
277
+ }
278
+ return Mutability :: Not ;
279
+ } ,
280
+ _ => { } ,
281
+ }
282
+ }
283
+ }
284
+ Mutability :: Not
242
285
}
0 commit comments