@@ -418,7 +418,12 @@ declare_clippy_lint! {
418418 /// **Why is this bad?** It's more concise and clear to just use the proper
419419 /// utility function
420420 ///
421- /// **Known problems:** None.
421+ /// **Known problems:** This will change the drop order for the matched type. Both `if let` and
422+ /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
423+ /// value before entering the block. For most types this change will not matter, but for a few
424+ /// types this will not be an acceptable change (e.g. locks). See the
425+ /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
426+ /// drop order.
422427 ///
423428 /// **Example:**
424429 ///
@@ -1553,50 +1558,214 @@ where
15531558
15541559mod redundant_pattern_match {
15551560 use super :: REDUNDANT_PATTERN_MATCHING ;
1556- use crate :: utils:: { match_qpath, match_trait_method, paths, snippet, span_lint_and_then} ;
1561+ use crate :: utils:: {
1562+ implements_trait, is_type_diagnostic_item, is_type_lang_item, match_qpath, match_trait_method, paths, snippet,
1563+ snippet_with_applicability, span_lint_and_then,
1564+ } ;
15571565 use if_chain:: if_chain;
15581566 use rustc_ast:: ast:: LitKind ;
15591567 use rustc_errors:: Applicability ;
1560- use rustc_hir:: { Arm , Expr , ExprKind , MatchSource , PatKind , QPath } ;
1568+ use rustc_hir:: { Arm , Expr , ExprKind , LangItem , MatchSource , Node , PatKind , QPath , UnOp } ;
15611569 use rustc_lint:: LateContext ;
1570+ use rustc_middle:: ty:: { self , subst:: GenericArgKind , Ty , TyCtxt , TypeckResults } ;
15621571 use rustc_span:: sym;
15631572
15641573 pub fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
15651574 if let ExprKind :: Match ( op, arms, ref match_source) = & expr. kind {
15661575 match match_source {
15671576 MatchSource :: Normal => find_sugg_for_match ( cx, expr, op, arms) ,
1568- MatchSource :: IfLetDesugar { .. } => find_sugg_for_if_let ( cx, expr, op, & arms[ 0 ] , "if" ) ,
1569- MatchSource :: WhileLetDesugar => find_sugg_for_if_let ( cx, expr, op, & arms[ 0 ] , "while" ) ,
1577+ MatchSource :: IfLetDesugar { contains_else_clause } => {
1578+ find_sugg_for_if_let ( cx, expr, op, & arms[ 0 ] , "if" , * contains_else_clause)
1579+ } ,
1580+ MatchSource :: WhileLetDesugar => find_sugg_for_if_let ( cx, expr, op, & arms[ 0 ] , "while" , false ) ,
15701581 _ => { } ,
15711582 }
15721583 }
15731584 }
15741585
1586+ // Check if the drop order for a type matters
1587+ fn type_needs_ordered_drop ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
1588+ if !ty. needs_drop ( cx. tcx , cx. param_env ) {
1589+ return false ;
1590+ }
1591+ if cx
1592+ . tcx
1593+ . lang_items ( )
1594+ . drop_trait ( )
1595+ . map_or ( false , |id| !implements_trait ( cx, ty, id, & [ ] ) )
1596+ {
1597+ // This type doesn't implement drop, so no side effects here.
1598+ // Check if any component type has any.
1599+ return match ty. kind ( ) {
1600+ ty:: Tuple ( _) => ty. tuple_fields ( ) . any ( |ty| type_needs_ordered_drop ( cx, ty) ) ,
1601+ ty:: Array ( ty, _) => type_needs_ordered_drop ( cx, ty) ,
1602+ ty:: Adt ( adt, subs) => adt
1603+ . all_fields ( )
1604+ . map ( |f| f. ty ( cx. tcx , subs) )
1605+ . any ( |ty| type_needs_ordered_drop ( cx, ty) ) ,
1606+ _ => true ,
1607+ } ;
1608+ }
1609+
1610+ // Check for std types which implement drop, but only for memory allocation.
1611+ !( ( is_type_diagnostic_item ( cx, ty, sym:: vec_type)
1612+ && !try_get_generic_ty ( ty, 0 ) . map_or ( false , |ty| type_needs_ordered_drop ( cx, ty) ) )
1613+ || ( is_type_lang_item ( cx, ty, LangItem :: OwnedBox )
1614+ && !try_get_generic_ty ( ty, 0 ) . map_or ( false , |ty| type_needs_ordered_drop ( cx, ty) ) )
1615+ || ( is_type_diagnostic_item ( cx, ty, sym:: option_type)
1616+ && !try_get_generic_ty ( ty, 0 ) . map_or ( false , |ty| type_needs_ordered_drop ( cx, ty) ) )
1617+ || ( is_type_diagnostic_item ( cx, ty, sym:: Rc )
1618+ && !try_get_generic_ty ( ty, 0 ) . map_or ( false , |ty| type_needs_ordered_drop ( cx, ty) ) )
1619+ || ( is_type_diagnostic_item ( cx, ty, sym:: Arc )
1620+ && !try_get_generic_ty ( ty, 0 ) . map_or ( false , |ty| type_needs_ordered_drop ( cx, ty) ) ) )
1621+ }
1622+
1623+ // Extract the generic arguments out of a type
1624+ fn try_get_generic_ty ( ty : Ty < ' _ > , index : usize ) -> Option < Ty < ' _ > > {
1625+ if_chain ! {
1626+ if let ty:: Adt ( _, subs) = ty. kind( ) ;
1627+ if let Some ( sub) = subs. get( index) ;
1628+ if let GenericArgKind :: Type ( sub_ty) = sub. unpack( ) ;
1629+ then {
1630+ Some ( sub_ty)
1631+ } else {
1632+ None
1633+ }
1634+ }
1635+ }
1636+
1637+ // Gets all the the temporaries in an expression which need to be dropped afterwards.
1638+ fn get_temporary_tys ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> Vec < Ty < ' tcx > > {
1639+ fn f (
1640+ addr_of : bool ,
1641+ tcx : TyCtxt < ' tcx > ,
1642+ typeck : & TypeckResults < ' tcx > ,
1643+ expr : & ' tcx Expr < ' tcx > ,
1644+ res : & mut Vec < Ty < ' tcx > > ,
1645+ ) {
1646+ match expr. kind {
1647+ ExprKind :: ConstBlock ( _)
1648+ | ExprKind :: Tup ( [ ] )
1649+ | ExprKind :: Array ( [ ] )
1650+ | ExprKind :: Lit ( _)
1651+ | ExprKind :: Path ( _)
1652+ | ExprKind :: Assign ( ..)
1653+ | ExprKind :: AssignOp ( ..)
1654+ | ExprKind :: Break ( ..)
1655+ | ExprKind :: Continue ( _)
1656+ | ExprKind :: Ret ( _)
1657+ | ExprKind :: InlineAsm ( _)
1658+ | ExprKind :: LlvmInlineAsm ( _)
1659+ | ExprKind :: Yield ( ..)
1660+ | ExprKind :: Err
1661+ | ExprKind :: MethodCall ( _, _, [ ] , _) => ( ) ,
1662+ ExprKind :: MethodCall ( _, _, [ self_arg, args @ ..] , _) => {
1663+ if addr_of {
1664+ res. push ( typeck. expr_ty ( expr) ) ;
1665+ }
1666+ let ref_self = typeck
1667+ . type_dependent_def_id ( expr. hir_id )
1668+ . map_or ( false , |id| tcx. fn_sig ( id) . skip_binder ( ) . inputs ( ) [ 0 ] . is_ref ( ) ) ;
1669+ f ( ref_self, tcx, typeck, self_arg, res) ;
1670+ for arg in args {
1671+ f ( false , tcx, typeck, arg, res) ;
1672+ }
1673+ } ,
1674+ ExprKind :: AddrOf ( _, _, expr) | ExprKind :: Field ( expr, _) => f ( true , tcx, typeck, expr, res) ,
1675+ ExprKind :: Box ( expr) | ExprKind :: Cast ( expr, _) => f ( false , tcx, typeck, expr, res) ,
1676+ ExprKind :: Array ( exprs @ [ _, ..] ) | ExprKind :: Tup ( exprs @ [ _, ..] ) | ExprKind :: Call ( _, exprs) => {
1677+ if addr_of {
1678+ res. push ( typeck. expr_ty ( expr) ) ;
1679+ }
1680+ for expr in exprs {
1681+ f ( false , tcx, typeck, expr, res) ;
1682+ }
1683+ } ,
1684+ ExprKind :: Binary ( _, lhs, rhs) => {
1685+ if addr_of {
1686+ res. push ( typeck. expr_ty ( expr) ) ;
1687+ }
1688+ f ( false , tcx, typeck, lhs, res) ;
1689+ f ( false , tcx, typeck, rhs, res) ;
1690+ } ,
1691+ ExprKind :: Unary ( UnOp :: UnDeref , e) => {
1692+ f ( typeck. expr_ty ( e) . is_ref ( ) , tcx, typeck, e, res) ;
1693+ } ,
1694+ ExprKind :: Type ( e, _) | ExprKind :: Unary ( _, e) => {
1695+ if addr_of {
1696+ res. push ( typeck. expr_ty ( expr) ) ;
1697+ }
1698+ f ( false , tcx, typeck, e, res) ;
1699+ } ,
1700+ ExprKind :: Index ( base, index) => {
1701+ f ( true , tcx, typeck, base, res) ;
1702+ f ( false , tcx, typeck, index, res) ;
1703+ } ,
1704+ ExprKind :: DropTemps ( _) | ExprKind :: Closure ( ..) => {
1705+ if addr_of {
1706+ res. push ( typeck. expr_ty ( expr) ) ;
1707+ }
1708+ } ,
1709+ ExprKind :: Match ( e, _, _) => {
1710+ if addr_of {
1711+ res. push ( typeck. expr_ty ( expr) ) ;
1712+ }
1713+ f ( true , tcx, typeck, e, res) ;
1714+ } ,
1715+ ExprKind :: Block ( b, _) | ExprKind :: Loop ( b, _, _) => {
1716+ if addr_of {
1717+ res. push ( typeck. expr_ty ( expr) ) ;
1718+ }
1719+ if let Some ( expr) = b. expr {
1720+ f ( false , tcx, typeck, expr, res) ;
1721+ }
1722+ } ,
1723+ ExprKind :: Struct ( _, fields, _) => {
1724+ if addr_of {
1725+ res. push ( typeck. expr_ty ( expr) ) ;
1726+ }
1727+ for field in fields {
1728+ f ( false , tcx, typeck, field. expr , res) ;
1729+ }
1730+ } ,
1731+ ExprKind :: Repeat ( expr, _) => {
1732+ if addr_of {
1733+ res. push ( typeck. expr_ty ( expr) ) ;
1734+ }
1735+ f ( false , tcx, typeck, expr, res) ;
1736+ } ,
1737+ }
1738+ }
1739+
1740+ let mut res = Vec :: new ( ) ;
1741+ f ( false , cx. tcx , cx. typeck_results ( ) , expr, & mut res) ;
1742+ res
1743+ }
1744+
15751745 fn find_sugg_for_if_let < ' tcx > (
15761746 cx : & LateContext < ' tcx > ,
15771747 expr : & ' tcx Expr < ' _ > ,
1578- op : & Expr < ' _ > ,
1748+ op : & ' tcx Expr < ' tcx > ,
15791749 arm : & Arm < ' _ > ,
15801750 keyword : & ' static str ,
1751+ has_else : bool ,
15811752 ) {
1582- let good_method = match arm. pat . kind {
1753+ let op_ty = cx. typeck_results ( ) . expr_ty ( op) ;
1754+ let ( good_method, inner_ty) = match arm. pat . kind {
15831755 PatKind :: TupleStruct ( ref path, ref patterns, _) if patterns. len ( ) == 1 => {
15841756 if let PatKind :: Wild = patterns[ 0 ] . kind {
1585- if cx. typeck_results ( ) . expr_ty ( op) . needs_drop ( cx. tcx , cx. param_env ) {
1586- // if let doesn't drop the value until after the block ends
1587- return ;
1588- } else if match_qpath ( path, & paths:: RESULT_OK ) {
1589- "is_ok()"
1757+ if match_qpath ( path, & paths:: RESULT_OK ) {
1758+ ( "is_ok()" , try_get_generic_ty ( op_ty, 0 ) . unwrap_or ( op_ty) )
15901759 } else if match_qpath ( path, & paths:: RESULT_ERR ) {
1591- "is_err()"
1760+ ( "is_err()" , try_get_generic_ty ( op_ty , 1 ) . unwrap_or ( op_ty ) )
15921761 } else if match_qpath ( path, & paths:: OPTION_SOME ) {
1593- "is_some()"
1762+ ( "is_some()" , op_ty )
15941763 } else if match_qpath ( path, & paths:: POLL_READY ) {
1595- "is_ready()"
1764+ ( "is_ready()" , op_ty )
15961765 } else if match_qpath ( path, & paths:: IPADDR_V4 ) {
1597- "is_ipv4()"
1766+ ( "is_ipv4()" , op_ty )
15981767 } else if match_qpath ( path, & paths:: IPADDR_V6 ) {
1599- "is_ipv6()"
1768+ ( "is_ipv6()" , op_ty )
16001769 } else {
16011770 return ;
16021771 }
@@ -1605,17 +1774,37 @@ mod redundant_pattern_match {
16051774 }
16061775 } ,
16071776 PatKind :: Path ( ref path) => {
1608- if match_qpath ( path, & paths:: OPTION_NONE ) {
1777+ let method = if match_qpath ( path, & paths:: OPTION_NONE ) {
16091778 "is_none()"
16101779 } else if match_qpath ( path, & paths:: POLL_PENDING ) {
16111780 "is_pending()"
16121781 } else {
16131782 return ;
1614- }
1783+ } ;
1784+ // drop for `None` and `Pending` do nothing
1785+ ( method, cx. tcx . types . unit )
16151786 } ,
16161787 _ => return ,
16171788 } ;
16181789
1790+ // if this is the last expression in a block then the lifetime of the expression is extended
1791+ // past the end of the block
1792+ let check_ty = if has_else
1793+ || ( keyword == "if" && {
1794+ let map = cx. tcx . hir ( ) ;
1795+ let id = map. get_parent_node ( expr. hir_id ) ;
1796+ id != expr. hir_id && matches ! ( map. find( id) , Some ( Node :: Block ( ..) ) )
1797+ } ) {
1798+ op_ty
1799+ } else {
1800+ inner_ty
1801+ } ;
1802+
1803+ let needs_drop = type_needs_ordered_drop ( cx, check_ty)
1804+ || get_temporary_tys ( cx, op)
1805+ . iter ( )
1806+ . any ( |ty| type_needs_ordered_drop ( cx, ty) ) ;
1807+
16191808 // check that `while_let_on_iterator` lint does not trigger
16201809 if_chain ! {
16211810 if keyword == "while" ;
@@ -1648,12 +1837,20 @@ mod redundant_pattern_match {
16481837 // while let ... = ... { ... }
16491838 // ^^^^^^^^^^^^^^^^^^^
16501839 let span = expr_span. until ( op_span. shrink_to_hi ( ) ) ;
1651- diag. span_suggestion (
1652- span,
1653- "try this" ,
1654- format ! ( "{} {}.{}" , keyword, snippet( cx, op_span, "_" ) , good_method) ,
1655- Applicability :: MachineApplicable , // snippet
1656- ) ;
1840+
1841+ let mut app = if needs_drop {
1842+ Applicability :: MaybeIncorrect
1843+ } else {
1844+ Applicability :: MachineApplicable
1845+ } ;
1846+ let sugg = snippet_with_applicability ( cx, op_span, "_" , & mut app) ;
1847+
1848+ diag. span_suggestion ( span, "try this" , format ! ( "{} {}.{}" , keyword, sugg, good_method) , app) ;
1849+
1850+ if needs_drop {
1851+ diag. note ( "this will change drop order of the result, as well as all temporaries" ) ;
1852+ diag. note ( "add `#[allow(clippy::redundant_pattern_matching)]` if this is important" ) ;
1853+ }
16571854 } ,
16581855 ) ;
16591856 }
0 commit comments