@@ -421,7 +421,12 @@ declare_clippy_lint! {
421421 /// **Why is this bad?** It's more concise and clear to just use the proper
422422 /// utility function
423423 ///
424- /// **Known problems:** None.
424+ /// **Known problems:** This will change the drop order for the matched type. Both `if let` and
425+ /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
426+ /// value before entering the block. For most types this change will not matter, but for a few
427+ /// types this will not be an acceptable change (e.g. locks). See the
428+ /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
429+ /// drop order.
425430 ///
426431 /// **Example:**
427432 ///
@@ -1701,57 +1706,219 @@ where
17011706mod redundant_pattern_match {
17021707 use super :: REDUNDANT_PATTERN_MATCHING ;
17031708 use clippy_utils:: diagnostics:: span_lint_and_then;
1704- use clippy_utils:: source:: snippet;
1705- use clippy_utils:: { is_trait_method, match_qpath, paths} ;
1709+ use clippy_utils:: source:: { snippet, snippet_with_applicability} ;
1710+ use clippy_utils:: ty:: { implements_trait, is_type_diagnostic_item, is_type_lang_item} ;
1711+ use clippy_utils:: { is_trait_method, match_qpath, match_trait_method, paths} ;
17061712 use if_chain:: if_chain;
17071713 use rustc_ast:: ast:: LitKind ;
17081714 use rustc_errors:: Applicability ;
1709- use rustc_hir:: { Arm , Expr , ExprKind , MatchSource , PatKind , QPath } ;
1715+ use rustc_hir:: { Arm , Expr , ExprKind , LangItem , MatchSource , Node , PatKind , QPath , UnOp } ;
17101716 use rustc_lint:: LateContext ;
1717+ use rustc_middle:: ty:: { self , subst:: GenericArgKind , Ty , TyCtxt , TypeckResults } ;
17111718 use rustc_span:: sym;
17121719
17131720 pub fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
17141721 if let ExprKind :: Match ( op, arms, ref match_source) = & expr. kind {
17151722 match match_source {
17161723 MatchSource :: Normal => find_sugg_for_match ( cx, expr, op, arms) ,
1717- MatchSource :: IfLetDesugar { .. } => find_sugg_for_if_let ( cx, expr, op, & arms[ 0 ] , "if" ) ,
1718- MatchSource :: WhileLetDesugar => find_sugg_for_if_let ( cx, expr, op, & arms[ 0 ] , "while" ) ,
1724+ MatchSource :: IfLetDesugar { contains_else_clause } => {
1725+ find_sugg_for_if_let ( cx, expr, op, & arms[ 0 ] , "if" , * contains_else_clause)
1726+ } ,
1727+ MatchSource :: WhileLetDesugar => find_sugg_for_if_let ( cx, expr, op, & arms[ 0 ] , "while" , false ) ,
17191728 _ => { } ,
17201729 }
17211730 }
17221731 }
17231732
1733+ // Check if the drop order for a type matters
1734+ fn type_needs_ordered_drop ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
1735+ if !ty. needs_drop ( cx. tcx , cx. param_env ) {
1736+ return false ;
1737+ }
1738+ if cx
1739+ . tcx
1740+ . lang_items ( )
1741+ . drop_trait ( )
1742+ . map_or ( false , |id| !implements_trait ( cx, ty, id, & [ ] ) )
1743+ {
1744+ // This type doesn't implement drop, so no side effects here.
1745+ // Check if any component type has any.
1746+ return match ty. kind ( ) {
1747+ ty:: Tuple ( _) => ty. tuple_fields ( ) . any ( |ty| type_needs_ordered_drop ( cx, ty) ) ,
1748+ ty:: Array ( ty, _) => type_needs_ordered_drop ( cx, ty) ,
1749+ ty:: Adt ( adt, subs) => adt
1750+ . all_fields ( )
1751+ . map ( |f| f. ty ( cx. tcx , subs) )
1752+ . any ( |ty| type_needs_ordered_drop ( cx, ty) ) ,
1753+ _ => true ,
1754+ } ;
1755+ }
1756+
1757+ // Check for std types which implement drop, but only for memory allocation.
1758+ if is_type_diagnostic_item ( cx, ty, sym:: vec_type)
1759+ || is_type_lang_item ( cx, ty, LangItem :: OwnedBox )
1760+ || is_type_diagnostic_item ( cx, ty, sym:: Rc )
1761+ || is_type_diagnostic_item ( cx, ty, sym:: Arc )
1762+ {
1763+ try_get_generic_ty ( ty, 0 ) . map_or ( true , |ty| type_needs_ordered_drop ( cx, ty) )
1764+ } else {
1765+ true
1766+ }
1767+ }
1768+
1769+ // Extract the generic arguments out of a type
1770+ fn try_get_generic_ty ( ty : Ty < ' _ > , index : usize ) -> Option < Ty < ' _ > > {
1771+ if_chain ! {
1772+ if let ty:: Adt ( _, subs) = ty. kind( ) ;
1773+ if let Some ( sub) = subs. get( index) ;
1774+ if let GenericArgKind :: Type ( sub_ty) = sub. unpack( ) ;
1775+ then {
1776+ Some ( sub_ty)
1777+ } else {
1778+ None
1779+ }
1780+ }
1781+ }
1782+
1783+ // Gets all the the temporaries in an expression which need to be dropped afterwards.
1784+ #[ allow( clippy:: too_many_lines) ]
1785+ fn get_temporary_tys ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> Vec < Ty < ' tcx > > {
1786+ fn f (
1787+ addr_of : bool ,
1788+ tcx : TyCtxt < ' tcx > ,
1789+ typeck : & TypeckResults < ' tcx > ,
1790+ expr : & ' tcx Expr < ' tcx > ,
1791+ result : & mut Vec < Ty < ' tcx > > ,
1792+ ) {
1793+ match expr. kind {
1794+ ExprKind :: ConstBlock ( _)
1795+ | ExprKind :: Tup ( [ ] )
1796+ | ExprKind :: Array ( [ ] )
1797+ | ExprKind :: Lit ( _)
1798+ | ExprKind :: Path ( _)
1799+ | ExprKind :: Assign ( ..)
1800+ | ExprKind :: AssignOp ( ..)
1801+ | ExprKind :: Break ( ..)
1802+ | ExprKind :: Continue ( _)
1803+ | ExprKind :: Ret ( _)
1804+ | ExprKind :: InlineAsm ( _)
1805+ | ExprKind :: LlvmInlineAsm ( _)
1806+ | ExprKind :: Yield ( ..)
1807+ | ExprKind :: Err
1808+ | ExprKind :: MethodCall ( _, _, [ ] , _) => ( ) ,
1809+ ExprKind :: MethodCall ( _, _, [ self_arg, args @ ..] , _) => {
1810+ if addr_of {
1811+ result. push ( typeck. expr_ty ( expr) ) ;
1812+ }
1813+ let ref_self = typeck
1814+ . type_dependent_def_id ( expr. hir_id )
1815+ . map_or ( false , |id| tcx. fn_sig ( id) . skip_binder ( ) . inputs ( ) [ 0 ] . is_ref ( ) ) ;
1816+ f ( ref_self, tcx, typeck, self_arg, result) ;
1817+ for arg in args {
1818+ f ( false , tcx, typeck, arg, result) ;
1819+ }
1820+ } ,
1821+ ExprKind :: AddrOf ( _, _, expr) | ExprKind :: Field ( expr, _) => f ( true , tcx, typeck, expr, result) ,
1822+ ExprKind :: Box ( expr) | ExprKind :: Cast ( expr, _) => f ( false , tcx, typeck, expr, result) ,
1823+ ExprKind :: Array ( exprs @ [ _, ..] ) | ExprKind :: Tup ( exprs @ [ _, ..] ) | ExprKind :: Call ( _, exprs) => {
1824+ if addr_of {
1825+ result. push ( typeck. expr_ty ( expr) ) ;
1826+ }
1827+ for expr in exprs {
1828+ f ( false , tcx, typeck, expr, result) ;
1829+ }
1830+ } ,
1831+ ExprKind :: Binary ( _, lhs, rhs) => {
1832+ if addr_of {
1833+ result. push ( typeck. expr_ty ( expr) ) ;
1834+ }
1835+ f ( false , tcx, typeck, lhs, result) ;
1836+ f ( false , tcx, typeck, rhs, result) ;
1837+ } ,
1838+ ExprKind :: Unary ( UnOp :: UnDeref , e) => {
1839+ f ( typeck. expr_ty ( e) . is_ref ( ) , tcx, typeck, e, result) ;
1840+ } ,
1841+ ExprKind :: Type ( e, _) | ExprKind :: Unary ( _, e) => {
1842+ if addr_of {
1843+ result. push ( typeck. expr_ty ( expr) ) ;
1844+ }
1845+ f ( false , tcx, typeck, e, result) ;
1846+ } ,
1847+ ExprKind :: Index ( base, index) => {
1848+ f ( true , tcx, typeck, base, result) ;
1849+ f ( false , tcx, typeck, index, result) ;
1850+ } ,
1851+ ExprKind :: DropTemps ( _) | ExprKind :: Closure ( ..) => {
1852+ if addr_of {
1853+ result. push ( typeck. expr_ty ( expr) ) ;
1854+ }
1855+ } ,
1856+ ExprKind :: Match ( e, _, _) => {
1857+ if addr_of {
1858+ result. push ( typeck. expr_ty ( expr) ) ;
1859+ }
1860+ f ( true , tcx, typeck, e, result) ;
1861+ } ,
1862+ ExprKind :: Block ( b, _) | ExprKind :: Loop ( b, _, _) => {
1863+ if addr_of {
1864+ result. push ( typeck. expr_ty ( expr) ) ;
1865+ }
1866+ if let Some ( expr) = b. expr {
1867+ f ( false , tcx, typeck, expr, result) ;
1868+ }
1869+ } ,
1870+ ExprKind :: Struct ( _, fields, _) => {
1871+ if addr_of {
1872+ result. push ( typeck. expr_ty ( expr) ) ;
1873+ }
1874+ for field in fields {
1875+ f ( false , tcx, typeck, field. expr , result) ;
1876+ }
1877+ } ,
1878+ ExprKind :: Repeat ( expr, _) => {
1879+ if addr_of {
1880+ result. push ( typeck. expr_ty ( expr) ) ;
1881+ }
1882+ f ( false , tcx, typeck, expr, result) ;
1883+ } ,
1884+ }
1885+ }
1886+
1887+ let mut res = Vec :: new ( ) ;
1888+ f ( false , cx. tcx , cx. typeck_results ( ) , expr, & mut res) ;
1889+ res
1890+ }
1891+
17241892 fn find_sugg_for_if_let < ' tcx > (
17251893 cx : & LateContext < ' tcx > ,
17261894 expr : & ' tcx Expr < ' _ > ,
1727- op : & Expr < ' _ > ,
1895+ op : & ' tcx Expr < ' tcx > ,
17281896 arm : & Arm < ' _ > ,
17291897 keyword : & ' static str ,
1898+ has_else : bool ,
17301899 ) {
17311900 // also look inside refs
1732- let mut kind = & arms [ 0 ] . pat . kind ;
1901+ let mut kind = arm . pat . kind ;
17331902 // if we have &None for example, peel it so we can detect "if let None = x"
17341903 if let PatKind :: Ref ( inner, _mutability) = kind {
17351904 kind = & inner. kind ;
17361905 }
1737- let good_method = match kind {
1906+ let op_ty = cx. typeck_results ( ) . expr_ty ( op) ;
1907+ let ( good_method, inner_ty) = match kind {
17381908 PatKind :: TupleStruct ( ref path, ref patterns, _) if patterns. len ( ) == 1 => {
17391909 if let PatKind :: Wild = patterns[ 0 ] . kind {
1740- if cx. typeck_results ( ) . expr_ty ( op) . needs_drop ( cx. tcx , cx. param_env ) {
1741- // if let doesn't drop the value until after the block ends
1742- return ;
1743- } else if match_qpath ( path, & paths:: RESULT_OK ) {
1744- "is_ok()"
1910+ if match_qpath ( path, & paths:: RESULT_OK ) {
1911+ ( "is_ok()" , try_get_generic_ty ( op_ty, 0 ) . unwrap_or ( op_ty) )
17451912 } else if match_qpath ( path, & paths:: RESULT_ERR ) {
1746- "is_err()"
1913+ ( "is_err()" , try_get_generic_ty ( op_ty , 1 ) . unwrap_or ( op_ty ) )
17471914 } else if match_qpath ( path, & paths:: OPTION_SOME ) {
1748- "is_some()"
1915+ ( "is_some()" , op_ty )
17491916 } else if match_qpath ( path, & paths:: POLL_READY ) {
1750- "is_ready()"
1917+ ( "is_ready()" , op_ty )
17511918 } else if match_qpath ( path, & paths:: IPADDR_V4 ) {
1752- "is_ipv4()"
1919+ ( "is_ipv4()" , op_ty )
17531920 } else if match_qpath ( path, & paths:: IPADDR_V6 ) {
1754- "is_ipv6()"
1921+ ( "is_ipv6()" , op_ty )
17551922 } else {
17561923 return ;
17571924 }
@@ -1760,17 +1927,37 @@ mod redundant_pattern_match {
17601927 }
17611928 } ,
17621929 PatKind :: Path ( ref path) => {
1763- if match_qpath ( path, & paths:: OPTION_NONE ) {
1930+ let method = if match_qpath ( path, & paths:: OPTION_NONE ) {
17641931 "is_none()"
17651932 } else if match_qpath ( path, & paths:: POLL_PENDING ) {
17661933 "is_pending()"
17671934 } else {
17681935 return ;
1769- }
1936+ } ;
1937+ // drop for `None` and `Pending` do nothing
1938+ ( method, cx. tcx . types . unit )
17701939 } ,
17711940 _ => return ,
17721941 } ;
17731942
1943+ // if this is the last expression in a block then the lifetime of the expression is extended
1944+ // past the end of the block
1945+ let check_ty = if has_else
1946+ || ( keyword == "if" && {
1947+ let map = cx. tcx . hir ( ) ;
1948+ let id = map. get_parent_node ( expr. hir_id ) ;
1949+ id != expr. hir_id && matches ! ( map. find( id) , Some ( Node :: Block ( ..) ) )
1950+ } ) {
1951+ op_ty
1952+ } else {
1953+ inner_ty
1954+ } ;
1955+
1956+ let needs_drop = type_needs_ordered_drop ( cx, check_ty)
1957+ || get_temporary_tys ( cx, op)
1958+ . iter ( )
1959+ . any ( |ty| type_needs_ordered_drop ( cx, ty) ) ;
1960+
17741961 // check that `while_let_on_iterator` lint does not trigger
17751962 if_chain ! {
17761963 if keyword == "while" ;
@@ -1803,12 +1990,20 @@ mod redundant_pattern_match {
18031990 // while let ... = ... { ... }
18041991 // ^^^^^^^^^^^^^^^^^^^
18051992 let span = expr_span. until ( op_span. shrink_to_hi ( ) ) ;
1806- diag. span_suggestion (
1807- span,
1808- "try this" ,
1809- format ! ( "{} {}.{}" , keyword, snippet( cx, op_span, "_" ) , good_method) ,
1810- Applicability :: MachineApplicable , // snippet
1811- ) ;
1993+
1994+ let mut app = if needs_drop {
1995+ Applicability :: MaybeIncorrect
1996+ } else {
1997+ Applicability :: MachineApplicable
1998+ } ;
1999+ let sugg = snippet_with_applicability ( cx, op_span, "_" , & mut app) ;
2000+
2001+ diag. span_suggestion ( span, "try this" , format ! ( "{} {}.{}" , keyword, sugg, good_method) , app) ;
2002+
2003+ if needs_drop {
2004+ diag. note ( "this will change drop order of the result, as well as all temporaries" ) ;
2005+ diag. note ( "add `#[allow(clippy::redundant_pattern_matching)]` if this is important" ) ;
2006+ }
18122007 } ,
18132008 ) ;
18142009 }
0 commit comments