@@ -4,19 +4,20 @@ use rustc_ast::BorrowKind;
4
4
use rustc_errors:: { Applicability , Diag } ;
5
5
use rustc_hir:: { Expr , ExprKind , Node , QPath } ;
6
6
use rustc_lint:: LateContext ;
7
+ use rustc_middle:: ty:: adjustment:: Adjust ;
7
8
use rustc_span:: sym;
8
9
9
10
use super :: SWAP_WITH_TEMPORARY ;
10
11
11
12
const MSG_TEMPORARY : & str = "this expression returns a temporary value" ;
12
13
const MSG_TEMPORARY_REFMUT : & str = "this is a mutable reference to a temporary value" ;
13
14
14
- pub ( super ) fn check ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , func : & Expr < ' _ > , args : & [ Expr < ' _ > ] ) {
15
+ pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' _ > , func : & Expr < ' _ > , args : & ' tcx [ Expr < ' _ > ] ) {
15
16
if let ExprKind :: Path ( QPath :: Resolved ( _, func_path) ) = func. kind
16
17
&& let Some ( func_def_id) = func_path. res . opt_def_id ( )
17
18
&& cx. tcx . is_diagnostic_item ( sym:: mem_swap, func_def_id)
18
19
{
19
- match ( ArgKind :: new ( & args[ 0 ] ) , ArgKind :: new ( & args[ 1 ] ) ) {
20
+ match ( ArgKind :: new ( cx , & args[ 0 ] ) , ArgKind :: new ( cx , & args[ 1 ] ) ) {
20
21
( ArgKind :: RefMutToTemp ( left_temp) , ArgKind :: RefMutToTemp ( right_temp) ) => {
21
22
emit_lint_useless ( cx, expr, & args[ 0 ] , & args[ 1 ] , left_temp, right_temp) ;
22
23
} ,
@@ -28,24 +29,40 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args
28
29
}
29
30
30
31
enum ArgKind < ' tcx > {
31
- // Mutable reference to a place, coming from a macro
32
- RefMutToPlaceAsMacro ( & ' tcx Expr < ' tcx > ) ,
33
- // Place behind a mutable reference
34
- RefMutToPlace ( & ' tcx Expr < ' tcx > ) ,
32
+ // Mutable reference to a place, coming from a macro, and number of dereferences to use
33
+ RefMutToPlaceAsMacro ( & ' tcx Expr < ' tcx > , usize ) ,
34
+ // Place behind a mutable reference, and number of dereferences to use
35
+ RefMutToPlace ( & ' tcx Expr < ' tcx > , usize ) ,
35
36
// Temporary value behind a mutable reference
36
37
RefMutToTemp ( & ' tcx Expr < ' tcx > ) ,
37
38
// Any other case
38
39
Expr ( & ' tcx Expr < ' tcx > ) ,
39
40
}
40
41
41
42
impl < ' tcx > ArgKind < ' tcx > {
42
- fn new ( arg : & ' tcx Expr < ' tcx > ) -> Self {
43
- if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, target) = arg. kind {
44
- if target. is_syntactic_place_expr ( ) {
43
+ /// Build a new `ArgKind` from `arg`. There must be no false positive when returning a
44
+ /// `ArgKind::RefMutToTemp` variant, as this may cause a spurious lint to be emitted.
45
+ fn new ( cx : & LateContext < ' tcx > , arg : & ' tcx Expr < ' tcx > ) -> Self {
46
+ if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, target) = arg. kind
47
+ && let adjustments = cx. typeck_results ( ) . expr_adjustments ( arg)
48
+ && adjustments
49
+ . first ( )
50
+ . is_some_and ( |adj| matches ! ( adj. kind, Adjust :: Deref ( None ) ) )
51
+ && adjustments
52
+ . last ( )
53
+ . is_some_and ( |adj| matches ! ( adj. kind, Adjust :: Borrow ( _) ) )
54
+ {
55
+ let extra_derefs = adjustments[ 1 ..adjustments. len ( ) - 1 ]
56
+ . iter ( )
57
+ . filter ( |adj| matches ! ( adj. kind, Adjust :: Deref ( _) ) )
58
+ . count ( ) ;
59
+ // If a deref is used, `arg` might be a place expression. For example, a mutex guard
60
+ // would dereference into the mutex content which is probably not temporary.
61
+ if target. is_syntactic_place_expr ( ) || extra_derefs > 0 {
45
62
if arg. span . from_expansion ( ) {
46
- ArgKind :: RefMutToPlaceAsMacro ( arg)
63
+ ArgKind :: RefMutToPlaceAsMacro ( arg, extra_derefs )
47
64
} else {
48
- ArgKind :: RefMutToPlace ( target)
65
+ ArgKind :: RefMutToPlace ( target, extra_derefs )
49
66
}
50
67
} else {
51
68
ArgKind :: RefMutToTemp ( target)
@@ -106,10 +123,15 @@ fn emit_lint_assign(cx: &LateContext<'_>, expr: &Expr<'_>, target: &ArgKind<'_>,
106
123
let mut applicability = Applicability :: MachineApplicable ;
107
124
let ctxt = expr. span . ctxt ( ) ;
108
125
let assign_target = match target {
109
- ArgKind :: Expr ( target) | ArgKind :: RefMutToPlaceAsMacro ( target) => {
110
- Sugg :: hir_with_context ( cx, target, ctxt, "_" , & mut applicability) . deref ( )
111
- } ,
112
- ArgKind :: RefMutToPlace ( target) => Sugg :: hir_with_context ( cx, target, ctxt, "_" , & mut applicability) ,
126
+ ArgKind :: Expr ( target) => Sugg :: hir_with_context ( cx, target, ctxt, "_" , & mut applicability) . deref ( ) ,
127
+ ArgKind :: RefMutToPlaceAsMacro ( arg, derefs) => ( 0 ..* derefs) . fold (
128
+ Sugg :: hir_with_context ( cx, arg, ctxt, "_" , & mut applicability) . deref ( ) ,
129
+ |sugg, _| sugg. deref ( ) ,
130
+ ) ,
131
+ ArgKind :: RefMutToPlace ( target, derefs) => ( 0 ..* derefs) . fold (
132
+ Sugg :: hir_with_context ( cx, target, ctxt, "_" , & mut applicability) ,
133
+ |sugg, _| sugg. deref ( ) ,
134
+ ) ,
113
135
ArgKind :: RefMutToTemp ( _) => unreachable ! ( ) ,
114
136
} ;
115
137
let assign_source = Sugg :: hir_with_context ( cx, temp, ctxt, "_" , & mut applicability) ;
0 commit comments