@@ -1712,9 +1712,12 @@ mod redundant_pattern_match {
1712
1712
use if_chain:: if_chain;
1713
1713
use rustc_ast:: ast:: LitKind ;
1714
1714
use rustc_errors:: Applicability ;
1715
- use rustc_hir:: { Arm , Expr , ExprKind , LangItem , MatchSource , Node , PatKind , QPath , UnOp } ;
1715
+ use rustc_hir:: {
1716
+ intravisit:: { walk_expr, ErasedMap , NestedVisitorMap , Visitor } ,
1717
+ Arm , Block , Expr , ExprKind , LangItem , MatchSource , Node , PatKind , QPath ,
1718
+ } ;
1716
1719
use rustc_lint:: LateContext ;
1717
- use rustc_middle:: ty:: { self , subst:: GenericArgKind , Ty , TyCtxt , TypeckResults } ;
1720
+ use rustc_middle:: ty:: { self , subst:: GenericArgKind , Ty } ;
1718
1721
use rustc_span:: sym;
1719
1722
1720
1723
pub fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
@@ -1733,34 +1736,42 @@ mod redundant_pattern_match {
1733
1736
// Check if the drop order for a type matters
1734
1737
fn type_needs_ordered_drop ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
1735
1738
if !ty. needs_drop ( cx. tcx , cx. param_env ) {
1736
- return false ;
1737
- }
1738
- if cx
1739
+ false
1740
+ } else if cx
1739
1741
. tcx
1740
1742
. lang_items ( )
1741
1743
. drop_trait ( )
1742
1744
. map_or ( false , |id| !implements_trait ( cx, ty, id, & [ ] ) )
1743
1745
{
1744
1746
// This type doesn't implement drop, so no side effects here.
1745
1747
// Check if any component type has any.
1746
- return match ty. kind ( ) {
1748
+ match ty. kind ( ) {
1747
1749
ty:: Tuple ( _) => ty. tuple_fields ( ) . any ( |ty| type_needs_ordered_drop ( cx, ty) ) ,
1748
1750
ty:: Array ( ty, _) => type_needs_ordered_drop ( cx, ty) ,
1749
1751
ty:: Adt ( adt, subs) => adt
1750
1752
. all_fields ( )
1751
1753
. map ( |f| f. ty ( cx. tcx , subs) )
1752
1754
. any ( |ty| type_needs_ordered_drop ( cx, ty) ) ,
1753
1755
_ => true ,
1754
- } ;
1756
+ }
1755
1757
}
1756
-
1757
1758
// Check for std types which implement drop, but only for memory allocation.
1758
- if is_type_diagnostic_item ( cx, ty, sym:: vec_type)
1759
+ else if is_type_diagnostic_item ( cx, ty, sym:: vec_type)
1759
1760
|| is_type_lang_item ( cx, ty, LangItem :: OwnedBox )
1760
1761
|| is_type_diagnostic_item ( cx, ty, sym:: Rc )
1761
1762
|| is_type_diagnostic_item ( cx, ty, sym:: Arc )
1763
+ || is_type_diagnostic_item ( cx, ty, sym:: cstring_type)
1764
+ || match_type ( cx, ty, & paths:: BTREEMAP )
1765
+ || match_type ( cx, ty, & paths:: LINKED_LIST )
1766
+ || match_type ( cx, ty, & paths:: WEAK_RC )
1767
+ || match_type ( cx, ty, & paths:: WEAK_ARC )
1762
1768
{
1763
- try_get_generic_ty ( ty, 0 ) . map_or ( true , |ty| type_needs_ordered_drop ( cx, ty) )
1769
+ // Check all of the generic arguments.
1770
+ if let ty:: Adt ( _, subs) = ty. kind ( ) {
1771
+ subs. types ( ) . any ( |ty| type_needs_ordered_drop ( cx, ty) )
1772
+ } else {
1773
+ true
1774
+ }
1764
1775
} else {
1765
1776
true
1766
1777
}
@@ -1780,113 +1791,84 @@ mod redundant_pattern_match {
1780
1791
}
1781
1792
}
1782
1793
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
- } ,
1794
+ // Checks if there are any temporaries created in the given expression for which drop order
1795
+ // matters.
1796
+ fn temporaries_need_ordered_drop ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
1797
+ struct V < ' a , ' tcx > {
1798
+ cx : & ' a LateContext < ' tcx > ,
1799
+ res : bool ,
1800
+ }
1801
+ impl < ' a , ' tcx > Visitor < ' tcx > for V < ' a , ' tcx > {
1802
+ type Map = ErasedMap < ' tcx > ;
1803
+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
1804
+ NestedVisitorMap :: None
1805
+ }
1806
+
1807
+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
1808
+ match expr. kind {
1809
+ // Taking the reference of a value leaves a temporary
1810
+ // e.g. In `&String::new()` the string is a temporary value.
1811
+ // Remaining fields are temporary values
1812
+ // e.g. In `(String::new(), 0).1` the string is a temporary value.
1813
+ ExprKind :: AddrOf ( _, _, expr) | ExprKind :: Field ( expr, _) => {
1814
+ if !matches ! ( expr. kind, ExprKind :: Path ( _) ) {
1815
+ if type_needs_ordered_drop ( self . cx , self . cx . typeck_results ( ) . expr_ty ( expr) ) {
1816
+ self . res = true ;
1817
+ } else {
1818
+ self . visit_expr ( expr) ;
1819
+ }
1820
+ }
1821
+ } ,
1822
+ // the base type is alway taken by reference.
1823
+ // e.g. In `(vec![0])[0]` the vector is a temporary value.
1824
+ ExprKind :: Index ( base, index) => {
1825
+ if !matches ! ( base. kind, ExprKind :: Path ( _) ) {
1826
+ if type_needs_ordered_drop ( self . cx , self . cx . typeck_results ( ) . expr_ty ( base) ) {
1827
+ self . res = true ;
1828
+ } else {
1829
+ self . visit_expr ( base) ;
1830
+ }
1831
+ }
1832
+ self . visit_expr ( index) ;
1833
+ } ,
1834
+ // Method calls can take self by reference.
1835
+ // e.g. In `String::new().len()` the string is a temporary value.
1836
+ ExprKind :: MethodCall ( _, _, [ self_arg, args @ ..] , _) => {
1837
+ if !matches ! ( self_arg. kind, ExprKind :: Path ( _) ) {
1838
+ let self_by_ref = self
1839
+ . cx
1840
+ . typeck_results ( )
1841
+ . type_dependent_def_id ( expr. hir_id )
1842
+ . map_or ( false , |id| self . cx . tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ 0 ] . is_ref ( ) ) ;
1843
+ if self_by_ref
1844
+ && type_needs_ordered_drop ( self . cx , self . cx . typeck_results ( ) . expr_ty ( self_arg) )
1845
+ {
1846
+ self . res = true ;
1847
+ } else {
1848
+ self . visit_expr ( self_arg)
1849
+ }
1850
+ }
1851
+ args. iter ( ) . for_each ( |arg| self . visit_expr ( arg) ) ;
1852
+ } ,
1853
+ // Either explicitly drops values, or changes control flow.
1854
+ ExprKind :: DropTemps ( _)
1855
+ | ExprKind :: Ret ( _)
1856
+ | ExprKind :: Break ( ..)
1857
+ | ExprKind :: Yield ( ..)
1858
+ | ExprKind :: Block ( Block { expr : None , .. } , _)
1859
+ | ExprKind :: Loop ( ..) => ( ) ,
1860
+
1861
+ // Only consider the final expression.
1862
+ ExprKind :: Block ( Block { expr : Some ( expr) , .. } , _) => self . visit_expr ( expr) ,
1863
+
1864
+ _ => walk_expr ( self , expr) ,
1865
+ }
1884
1866
}
1885
1867
}
1886
1868
1887
- let mut res = Vec :: new ( ) ;
1888
- f ( false , cx . tcx , cx . typeck_results ( ) , expr, & mut res ) ;
1889
- res
1869
+ let mut v = V { cx , res : false } ;
1870
+ v . visit_expr ( expr) ;
1871
+ v . res
1890
1872
}
1891
1873
1892
1874
fn find_sugg_for_if_let < ' tcx > (
@@ -1904,6 +1886,8 @@ mod redundant_pattern_match {
1904
1886
kind = & inner. kind ;
1905
1887
}
1906
1888
let op_ty = cx. typeck_results ( ) . expr_ty ( op) ;
1889
+ // Determine which function should be used, and the type contained by the corresponding
1890
+ // variant.
1907
1891
let ( good_method, inner_ty) = match kind {
1908
1892
PatKind :: TupleStruct ( ref path, ref patterns, _) if patterns. len ( ) == 1 => {
1909
1893
if let PatKind :: Wild = patterns[ 0 ] . kind {
@@ -1934,29 +1918,28 @@ mod redundant_pattern_match {
1934
1918
} else {
1935
1919
return ;
1936
1920
} ;
1937
- // drop for `None` and `Pending` do nothing
1921
+ // `None` and `Pending` don't have an inner type.
1938
1922
( method, cx. tcx . types . unit )
1939
1923
} ,
1940
1924
_ => return ,
1941
1925
} ;
1942
1926
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
1927
+ // If this is the last expression in a block or there is an else clause then the whole
1928
+ // type needs to be considered, not just the inner type of the branch being matched on.
1929
+ // Note the last expression in a block is dropped after all local bindings.
1945
1930
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
- } ) {
1931
+ || ( keyword == "if" && matches ! ( cx. tcx. hir( ) . parent_iter( expr. hir_id) . next( ) , Some ( ( _, Node :: Block ( ..) ) ) ) )
1932
+ {
1951
1933
op_ty
1952
1934
} else {
1953
1935
inner_ty
1954
1936
} ;
1955
1937
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) ) ;
1938
+ // All temporaries created in the scrutinee expression are dropped at the same time as the
1939
+ // scrutinee would be, so they have to be considered as well.
1940
+ // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
1941
+ // for the duration if body.
1942
+ let needs_drop = type_needs_ordered_drop ( cx, check_ty) || temporaries_need_ordered_drop ( cx, op) ;
1960
1943
1961
1944
// check that `while_let_on_iterator` lint does not trigger
1962
1945
if_chain ! {
0 commit comments