@@ -4,19 +4,20 @@ use rustc_ast::BorrowKind;
44use rustc_errors:: { Applicability , Diag } ;
55use rustc_hir:: { Expr , ExprKind , Node , QPath } ;
66use rustc_lint:: LateContext ;
7+ use rustc_middle:: ty:: adjustment:: Adjust ;
78use rustc_span:: sym;
89
910use super :: SWAP_WITH_TEMPORARY ;
1011
1112const MSG_TEMPORARY : & str = "this expression returns a temporary value" ;
1213const MSG_TEMPORARY_REFMUT : & str = "this is a mutable reference to a temporary value" ;
1314
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 < ' _ > ] ) {
1516 if let ExprKind :: Path ( QPath :: Resolved ( _, func_path) ) = func. kind
1617 && let Some ( func_def_id) = func_path. res . opt_def_id ( )
1718 && cx. tcx . is_diagnostic_item ( sym:: mem_swap, func_def_id)
1819 {
19- match ( ArgKind :: new ( & args[ 0 ] ) , ArgKind :: new ( & args[ 1 ] ) ) {
20+ match ( ArgKind :: new ( cx , & args[ 0 ] ) , ArgKind :: new ( cx , & args[ 1 ] ) ) {
2021 ( ArgKind :: RefMutToTemp ( left_temp) , ArgKind :: RefMutToTemp ( right_temp) ) => {
2122 emit_lint_useless ( cx, expr, & args[ 0 ] , & args[ 1 ] , left_temp, right_temp) ;
2223 } ,
@@ -28,24 +29,40 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args
2829}
2930
3031enum 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 ) ,
3536 // Temporary value behind a mutable reference
3637 RefMutToTemp ( & ' tcx Expr < ' tcx > ) ,
3738 // Any other case
3839 Expr ( & ' tcx Expr < ' tcx > ) ,
3940}
4041
4142impl < ' 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
48+ . typeck_results ( )
49+ . expr_adjustments ( arg)
50+ . iter ( )
51+ . map ( |adj| & adj. kind )
52+ . collect :: < Vec < _ > > ( )
53+ && let [ Adjust :: Deref ( None ) , extra_adjusts @ .., Adjust :: Borrow ( _) ] = adjustments. as_slice ( )
54+ {
55+ let extra_derefs = extra_adjusts
56+ . iter ( )
57+ . filter ( |adjust| matches ! ( adjust, 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 {
4562 if arg. span . from_expansion ( ) {
46- ArgKind :: RefMutToPlaceAsMacro ( arg)
63+ ArgKind :: RefMutToPlaceAsMacro ( arg, extra_derefs )
4764 } else {
48- ArgKind :: RefMutToPlace ( target)
65+ ArgKind :: RefMutToPlace ( target, extra_derefs )
4966 }
5067 } else {
5168 ArgKind :: RefMutToTemp ( target)
@@ -106,10 +123,15 @@ fn emit_lint_assign(cx: &LateContext<'_>, expr: &Expr<'_>, target: &ArgKind<'_>,
106123 let mut applicability = Applicability :: MachineApplicable ;
107124 let ctxt = expr. span . ctxt ( ) ;
108125 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+ ) ,
113135 ArgKind :: RefMutToTemp ( _) => unreachable ! ( ) ,
114136 } ;
115137 let assign_source = Sugg :: hir_with_context ( cx, temp, ctxt, "_" , & mut applicability) ;
0 commit comments