@@ -424,12 +424,7 @@ declare_clippy_lint! {
424
424
/// **Why is this bad?** It's more concise and clear to just use the proper
425
425
/// utility function
426
426
///
427
- /// **Known problems:** This will change the drop order for the matched type. Both `if let` and
428
- /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
429
- /// value before entering the block. For most types this change will not matter, but for a few
430
- /// types this will not be an acceptable change (e.g. locks). See the
431
- /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
432
- /// drop order.
427
+ /// **Known problems:** None.
433
428
///
434
429
/// **Example:**
435
430
///
@@ -1707,206 +1702,55 @@ where
1707
1702
mod redundant_pattern_match {
1708
1703
use super :: REDUNDANT_PATTERN_MATCHING ;
1709
1704
use clippy_utils:: diagnostics:: span_lint_and_then;
1710
- use clippy_utils:: source:: { snippet, snippet_with_applicability} ;
1711
- use clippy_utils:: ty:: { implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type} ;
1705
+ use clippy_utils:: source:: snippet;
1712
1706
use clippy_utils:: { is_lang_ctor, is_qpath_def_path, is_trait_method, paths} ;
1713
1707
use if_chain:: if_chain;
1714
1708
use rustc_ast:: ast:: LitKind ;
1715
1709
use rustc_errors:: Applicability ;
1716
1710
use rustc_hir:: LangItem :: { OptionNone , OptionSome , PollPending , PollReady , ResultErr , ResultOk } ;
1717
- use rustc_hir:: {
1718
- intravisit:: { walk_expr, ErasedMap , NestedVisitorMap , Visitor } ,
1719
- Arm , Block , Expr , ExprKind , LangItem , MatchSource , Node , PatKind , QPath ,
1720
- } ;
1711
+ use rustc_hir:: { Arm , Expr , ExprKind , MatchSource , PatKind , QPath } ;
1721
1712
use rustc_lint:: LateContext ;
1722
- use rustc_middle:: ty:: { self , subst:: GenericArgKind , Ty } ;
1723
1713
use rustc_span:: sym;
1724
1714
1725
1715
pub fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
1726
1716
if let ExprKind :: Match ( op, arms, ref match_source) = & expr. kind {
1727
1717
match match_source {
1728
1718
MatchSource :: Normal => find_sugg_for_match ( cx, expr, op, arms) ,
1729
- MatchSource :: IfLetDesugar { contains_else_clause } => {
1730
- find_sugg_for_if_let ( cx, expr, op, & arms[ 0 ] , "if" , * contains_else_clause)
1731
- } ,
1732
- MatchSource :: WhileLetDesugar => find_sugg_for_if_let ( cx, expr, op, & arms[ 0 ] , "while" , false ) ,
1719
+ MatchSource :: IfLetDesugar { .. } => find_sugg_for_if_let ( cx, expr, op, arms, "if" ) ,
1720
+ MatchSource :: WhileLetDesugar => find_sugg_for_if_let ( cx, expr, op, arms, "while" ) ,
1733
1721
_ => { } ,
1734
1722
}
1735
1723
}
1736
1724
}
1737
1725
1738
- /// Checks if the drop order for a type matters. Some std types implement drop solely to
1739
- /// deallocate memory. For these types, and composites containing them, changing the drop order
1740
- /// won't result in any observable side effects.
1741
- fn type_needs_ordered_drop ( cx : & LateContext < ' tcx > , ty : Ty < ' tcx > ) -> bool {
1742
- if !ty. needs_drop ( cx. tcx , cx. param_env ) {
1743
- false
1744
- } else if !cx
1745
- . tcx
1746
- . lang_items ( )
1747
- . drop_trait ( )
1748
- . map_or ( false , |id| implements_trait ( cx, ty, id, & [ ] ) )
1749
- {
1750
- // This type doesn't implement drop, so no side effects here.
1751
- // Check if any component type has any.
1752
- match ty. kind ( ) {
1753
- ty:: Tuple ( _) => ty. tuple_fields ( ) . any ( |ty| type_needs_ordered_drop ( cx, ty) ) ,
1754
- ty:: Array ( ty, _) => type_needs_ordered_drop ( cx, ty) ,
1755
- ty:: Adt ( adt, subs) => adt
1756
- . all_fields ( )
1757
- . map ( |f| f. ty ( cx. tcx , subs) )
1758
- . any ( |ty| type_needs_ordered_drop ( cx, ty) ) ,
1759
- _ => true ,
1760
- }
1761
- }
1762
- // Check for std types which implement drop, but only for memory allocation.
1763
- else if is_type_diagnostic_item ( cx, ty, sym:: vec_type)
1764
- || is_type_lang_item ( cx, ty, LangItem :: OwnedBox )
1765
- || is_type_diagnostic_item ( cx, ty, sym:: Rc )
1766
- || is_type_diagnostic_item ( cx, ty, sym:: Arc )
1767
- || is_type_diagnostic_item ( cx, ty, sym:: cstring_type)
1768
- || match_type ( cx, ty, & paths:: BTREEMAP )
1769
- || match_type ( cx, ty, & paths:: LINKED_LIST )
1770
- || match_type ( cx, ty, & paths:: WEAK_RC )
1771
- || match_type ( cx, ty, & paths:: WEAK_ARC )
1772
- {
1773
- // Check all of the generic arguments.
1774
- if let ty:: Adt ( _, subs) = ty. kind ( ) {
1775
- subs. types ( ) . any ( |ty| type_needs_ordered_drop ( cx, ty) )
1776
- } else {
1777
- true
1778
- }
1779
- } else {
1780
- true
1781
- }
1782
- }
1783
-
1784
- // Extract the generic arguments out of a type
1785
- fn try_get_generic_ty ( ty : Ty < ' _ > , index : usize ) -> Option < Ty < ' _ > > {
1786
- if_chain ! {
1787
- if let ty:: Adt ( _, subs) = ty. kind( ) ;
1788
- if let Some ( sub) = subs. get( index) ;
1789
- if let GenericArgKind :: Type ( sub_ty) = sub. unpack( ) ;
1790
- then {
1791
- Some ( sub_ty)
1792
- } else {
1793
- None
1794
- }
1795
- }
1796
- }
1797
-
1798
- // Checks if there are any temporaries created in the given expression for which drop order
1799
- // matters.
1800
- fn temporaries_need_ordered_drop ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
1801
- struct V < ' a , ' tcx > {
1802
- cx : & ' a LateContext < ' tcx > ,
1803
- res : bool ,
1804
- }
1805
- impl < ' a , ' tcx > Visitor < ' tcx > for V < ' a , ' tcx > {
1806
- type Map = ErasedMap < ' tcx > ;
1807
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
1808
- NestedVisitorMap :: None
1809
- }
1810
-
1811
- fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
1812
- match expr. kind {
1813
- // Taking the reference of a value leaves a temporary
1814
- // e.g. In `&String::new()` the string is a temporary value.
1815
- // Remaining fields are temporary values
1816
- // e.g. In `(String::new(), 0).1` the string is a temporary value.
1817
- ExprKind :: AddrOf ( _, _, expr) | ExprKind :: Field ( expr, _) => {
1818
- if !matches ! ( expr. kind, ExprKind :: Path ( _) ) {
1819
- if type_needs_ordered_drop ( self . cx , self . cx . typeck_results ( ) . expr_ty ( expr) ) {
1820
- self . res = true ;
1821
- } else {
1822
- self . visit_expr ( expr) ;
1823
- }
1824
- }
1825
- } ,
1826
- // the base type is alway taken by reference.
1827
- // e.g. In `(vec![0])[0]` the vector is a temporary value.
1828
- ExprKind :: Index ( base, index) => {
1829
- if !matches ! ( base. kind, ExprKind :: Path ( _) ) {
1830
- if type_needs_ordered_drop ( self . cx , self . cx . typeck_results ( ) . expr_ty ( base) ) {
1831
- self . res = true ;
1832
- } else {
1833
- self . visit_expr ( base) ;
1834
- }
1835
- }
1836
- self . visit_expr ( index) ;
1837
- } ,
1838
- // Method calls can take self by reference.
1839
- // e.g. In `String::new().len()` the string is a temporary value.
1840
- ExprKind :: MethodCall ( _, _, [ self_arg, args @ ..] , _) => {
1841
- if !matches ! ( self_arg. kind, ExprKind :: Path ( _) ) {
1842
- let self_by_ref = self
1843
- . cx
1844
- . typeck_results ( )
1845
- . type_dependent_def_id ( expr. hir_id )
1846
- . map_or ( false , |id| self . cx . tcx . fn_sig ( id) . skip_binder ( ) . inputs ( ) [ 0 ] . is_ref ( ) ) ;
1847
- if self_by_ref
1848
- && type_needs_ordered_drop ( self . cx , self . cx . typeck_results ( ) . expr_ty ( self_arg) )
1849
- {
1850
- self . res = true ;
1851
- } else {
1852
- self . visit_expr ( self_arg)
1853
- }
1854
- }
1855
- args. iter ( ) . for_each ( |arg| self . visit_expr ( arg) ) ;
1856
- } ,
1857
- // Either explicitly drops values, or changes control flow.
1858
- ExprKind :: DropTemps ( _)
1859
- | ExprKind :: Ret ( _)
1860
- | ExprKind :: Break ( ..)
1861
- | ExprKind :: Yield ( ..)
1862
- | ExprKind :: Block ( Block { expr : None , .. } , _)
1863
- | ExprKind :: Loop ( ..) => ( ) ,
1864
-
1865
- // Only consider the final expression.
1866
- ExprKind :: Block ( Block { expr : Some ( expr) , .. } , _) => self . visit_expr ( expr) ,
1867
-
1868
- _ => walk_expr ( self , expr) ,
1869
- }
1870
- }
1871
- }
1872
-
1873
- let mut v = V { cx, res : false } ;
1874
- v. visit_expr ( expr) ;
1875
- v. res
1876
- }
1877
-
1878
1726
fn find_sugg_for_if_let < ' tcx > (
1879
1727
cx : & LateContext < ' tcx > ,
1880
1728
expr : & ' tcx Expr < ' _ > ,
1881
- op : & ' tcx Expr < ' tcx > ,
1882
- arm : & Arm < ' _ > ,
1729
+ op : & Expr < ' _ > ,
1730
+ arms : & [ Arm < ' _ > ] ,
1883
1731
keyword : & ' static str ,
1884
- has_else : bool ,
1885
1732
) {
1886
1733
// also look inside refs
1887
- let mut kind = & arm . pat . kind ;
1734
+ let mut kind = & arms [ 0 ] . pat . kind ;
1888
1735
// if we have &None for example, peel it so we can detect "if let None = x"
1889
1736
if let PatKind :: Ref ( inner, _mutability) = kind {
1890
1737
kind = & inner. kind ;
1891
1738
}
1892
- let op_ty = cx. typeck_results ( ) . expr_ty ( op) ;
1893
- // Determine which function should be used, and the type contained by the corresponding
1894
- // variant.
1895
- let ( good_method, inner_ty) = match kind {
1739
+ let good_method = match kind {
1896
1740
PatKind :: TupleStruct ( ref path, [ sub_pat] , _) => {
1897
1741
if let PatKind :: Wild = sub_pat. kind {
1898
1742
if is_lang_ctor ( cx, path, ResultOk ) {
1899
- ( "is_ok()" , try_get_generic_ty ( op_ty , 0 ) . unwrap_or ( op_ty ) )
1743
+ "is_ok()"
1900
1744
} else if is_lang_ctor ( cx, path, ResultErr ) {
1901
- ( "is_err()" , try_get_generic_ty ( op_ty , 1 ) . unwrap_or ( op_ty ) )
1745
+ "is_err()"
1902
1746
} else if is_lang_ctor ( cx, path, OptionSome ) {
1903
- ( "is_some()" , op_ty )
1747
+ "is_some()"
1904
1748
} else if is_lang_ctor ( cx, path, PollReady ) {
1905
- ( "is_ready()" , op_ty )
1749
+ "is_ready()"
1906
1750
} else if is_qpath_def_path ( cx, path, sub_pat. hir_id , & paths:: IPADDR_V4 ) {
1907
- ( "is_ipv4()" , op_ty )
1751
+ "is_ipv4()"
1908
1752
} else if is_qpath_def_path ( cx, path, sub_pat. hir_id , & paths:: IPADDR_V6 ) {
1909
- ( "is_ipv6()" , op_ty )
1753
+ "is_ipv6()"
1910
1754
} else {
1911
1755
return ;
1912
1756
}
@@ -1915,36 +1759,17 @@ mod redundant_pattern_match {
1915
1759
}
1916
1760
} ,
1917
1761
PatKind :: Path ( ref path) => {
1918
- let method = if is_lang_ctor ( cx, path, OptionNone ) {
1762
+ if is_lang_ctor ( cx, path, OptionNone ) {
1919
1763
"is_none()"
1920
1764
} else if is_lang_ctor ( cx, path, PollPending ) {
1921
1765
"is_pending()"
1922
1766
} else {
1923
1767
return ;
1924
- } ;
1925
- // `None` and `Pending` don't have an inner type.
1926
- ( method, cx. tcx . types . unit )
1768
+ }
1927
1769
} ,
1928
1770
_ => return ,
1929
1771
} ;
1930
1772
1931
- // If this is the last expression in a block or there is an else clause then the whole
1932
- // type needs to be considered, not just the inner type of the branch being matched on.
1933
- // Note the last expression in a block is dropped after all local bindings.
1934
- let check_ty = if has_else
1935
- || ( keyword == "if" && matches ! ( cx. tcx. hir( ) . parent_iter( expr. hir_id) . next( ) , Some ( ( _, Node :: Block ( ..) ) ) ) )
1936
- {
1937
- op_ty
1938
- } else {
1939
- inner_ty
1940
- } ;
1941
-
1942
- // All temporaries created in the scrutinee expression are dropped at the same time as the
1943
- // scrutinee would be, so they have to be considered as well.
1944
- // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
1945
- // for the duration if body.
1946
- let needs_drop = type_needs_ordered_drop ( cx, check_ty) || temporaries_need_ordered_drop ( cx, op) ;
1947
-
1948
1773
// check that `while_let_on_iterator` lint does not trigger
1949
1774
if_chain ! {
1950
1775
if keyword == "while" ;
@@ -1963,7 +1788,7 @@ mod redundant_pattern_match {
1963
1788
span_lint_and_then (
1964
1789
cx,
1965
1790
REDUNDANT_PATTERN_MATCHING ,
1966
- arm . pat . span ,
1791
+ arms [ 0 ] . pat . span ,
1967
1792
& format ! ( "redundant pattern matching, consider using `{}`" , good_method) ,
1968
1793
|diag| {
1969
1794
// while let ... = ... { ... }
@@ -1977,20 +1802,12 @@ mod redundant_pattern_match {
1977
1802
// while let ... = ... { ... }
1978
1803
// ^^^^^^^^^^^^^^^^^^^
1979
1804
let span = expr_span. until ( op_span. shrink_to_hi ( ) ) ;
1980
-
1981
- let mut app = if needs_drop {
1982
- Applicability :: MaybeIncorrect
1983
- } else {
1984
- Applicability :: MachineApplicable
1985
- } ;
1986
- let sugg = snippet_with_applicability ( cx, op_span, "_" , & mut app) ;
1987
-
1988
- diag. span_suggestion ( span, "try this" , format ! ( "{} {}.{}" , keyword, sugg, good_method) , app) ;
1989
-
1990
- if needs_drop {
1991
- diag. note ( "this will change drop order of the result, as well as all temporaries" ) ;
1992
- diag. note ( "add `#[allow(clippy::redundant_pattern_matching)]` if this is important" ) ;
1993
- }
1805
+ diag. span_suggestion (
1806
+ span,
1807
+ "try this" ,
1808
+ format ! ( "{} {}.{}" , keyword, snippet( cx, op_span, "_" ) , good_method) ,
1809
+ Applicability :: MachineApplicable , // snippet
1810
+ ) ;
1994
1811
} ,
1995
1812
) ;
1996
1813
}
0 commit comments