@@ -6,7 +6,7 @@ use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures};
66use clippy_utils:: { ExprUseNode , expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id} ;
77use core:: ops:: ControlFlow ;
88use 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 } ;
1010use rustc_lint:: LateContext ;
1111use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment , AutoBorrow , AutoBorrowMutability } ;
1212use rustc_span:: { DUMMY_SP , Span , Symbol , sym} ;
@@ -34,6 +34,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
3434 {
3535 let mut requires_copy = false ;
3636 let mut requires_deref = false ;
37+ let mut has_mut_use = false ;
3738
3839 // The number of unprocessed return expressions.
3940 let mut ret_count = 0u32 ;
@@ -47,7 +48,8 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
4748 // Nested closures don't need to treat returns specially.
4849 let _: Option < !> = for_each_expr ( cx, cx. tcx . hir ( ) . body ( c. body ) . value , |e| {
4950 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 ( ) ;
5153 match ( kind, same_ctxt && e. span . ctxt ( ) == ctxt) {
5254 ( _, false ) | ( UseKind :: Deref | UseKind :: Return ( ..) , true ) => {
5355 requires_copy = true ;
@@ -65,7 +67,8 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
6567 } else if matches ! ( e. kind, ExprKind :: Ret ( _) ) {
6668 ret_count += 1 ;
6769 } 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 ( ) ;
6972 match ( kind, same_ctxt && e. span . ctxt ( ) == ctxt) {
7073 ( UseKind :: Return ( ..) , false ) => {
7174 return ControlFlow :: Break ( ( ) ) ;
@@ -161,6 +164,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
161164 && ( !requires_copy || cx. type_is_copy_modulo_regions ( arg_ty) )
162165 // This case could be handled, but a fair bit of care would need to be taken.
163166 && ( !requires_deref || arg_ty. is_freeze ( cx. tcx , cx. typing_env ( ) ) )
167+ && !has_mut_use
164168 {
165169 if requires_deref {
166170 edits. push ( ( param. span . shrink_to_lo ( ) , "&" . into ( ) ) ) ;
@@ -207,36 +211,77 @@ enum UseKind<'tcx> {
207211 FieldAccess ( Symbol , & ' tcx Expr < ' tcx > ) ,
208212}
209213
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 ) {
212216 let use_cx = expr_use_ctxt ( cx, e) ;
217+ let mutbl = use_mutability ( cx, e. hir_id ) ;
213218 if use_cx
214219 . adjustments
215220 . first ( )
216221 . is_some_and ( |a| matches ! ( a. kind, Adjust :: Deref ( _) ) )
217222 {
218- return ( UseKind :: AutoBorrowed , use_cx. same_ctxt ) ;
223+ return ( UseKind :: AutoBorrowed , mutbl , use_cx. same_ctxt ) ;
219224 }
220225 let res = match use_cx. use_node ( cx) {
221226 ExprUseNode :: Return ( _) => {
222227 if let ExprKind :: Ret ( Some ( e) ) = use_cx. node . expect_expr ( ) . kind {
223228 UseKind :: Return ( e. span )
224229 } else {
225- return ( UseKind :: Return ( DUMMY_SP ) , false ) ;
230+ return ( UseKind :: Return ( DUMMY_SP ) , mutbl , false ) ;
226231 }
227232 } ,
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 ( ) ) ,
229234 ExprUseNode :: Callee | ExprUseNode :: MethodArg ( _, _, 0 )
230235 if use_cx
231236 . adjustments
232237 . 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 ( _ ) ) ) ) =>
234239 {
235240 UseKind :: AutoBorrowed
236241 } ,
237242 ExprUseNode :: Callee | ExprUseNode :: MethodArg ( _, _, 0 ) => UseKind :: WillAutoDeref ,
238243 ExprUseNode :: AddrOf ( BorrowKind :: Ref , _) => UseKind :: Borrowed ( use_cx. node . expect_expr ( ) . span ) ,
239244 _ => UseKind :: Deref ,
240245 } ;
241- ( res, use_cx. same_ctxt )
246+ ( res, mutbl, use_cx. same_ctxt )
247+ }
248+
249+ fn use_mutability < ' tcx > ( cx : & LateContext < ' tcx > , 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+ match node {
263+ Node :: Expr ( expr) => 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+ } else {
278+ return Mutability :: Not ;
279+ }
280+ } ,
281+ _ => { } ,
282+ } ,
283+ _ => { } ,
284+ }
285+ }
286+ Mutability :: Not
242287}
0 commit comments