@@ -424,12 +424,7 @@ declare_clippy_lint! {
424424 /// **Why is this bad?** It's more concise and clear to just use the proper
425425 /// utility function
426426 ///
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.
433428 ///
434429 /// **Example:**
435430 ///
@@ -1707,206 +1702,55 @@ where
17071702mod redundant_pattern_match {
17081703 use super :: REDUNDANT_PATTERN_MATCHING ;
17091704 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;
17121706 use clippy_utils:: { is_lang_ctor, is_qpath_def_path, is_trait_method, paths} ;
17131707 use if_chain:: if_chain;
17141708 use rustc_ast:: ast:: LitKind ;
17151709 use rustc_errors:: Applicability ;
17161710 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 } ;
17211712 use rustc_lint:: LateContext ;
1722- use rustc_middle:: ty:: { self , subst:: GenericArgKind , Ty } ;
17231713 use rustc_span:: sym;
17241714
17251715 pub fn check < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
17261716 if let ExprKind :: Match ( op, arms, ref match_source) = & expr. kind {
17271717 match match_source {
17281718 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" ) ,
17331721 _ => { } ,
17341722 }
17351723 }
17361724 }
17371725
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-
18781726 fn find_sugg_for_if_let < ' tcx > (
18791727 cx : & LateContext < ' tcx > ,
18801728 expr : & ' tcx Expr < ' _ > ,
1881- op : & ' tcx Expr < ' tcx > ,
1882- arm : & Arm < ' _ > ,
1729+ op : & Expr < ' _ > ,
1730+ arms : & [ Arm < ' _ > ] ,
18831731 keyword : & ' static str ,
1884- has_else : bool ,
18851732 ) {
18861733 // also look inside refs
1887- let mut kind = & arm . pat . kind ;
1734+ let mut kind = & arms [ 0 ] . pat . kind ;
18881735 // if we have &None for example, peel it so we can detect "if let None = x"
18891736 if let PatKind :: Ref ( inner, _mutability) = kind {
18901737 kind = & inner. kind ;
18911738 }
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 {
18961740 PatKind :: TupleStruct ( ref path, [ sub_pat] , _) => {
18971741 if let PatKind :: Wild = sub_pat. kind {
18981742 if is_lang_ctor ( cx, path, ResultOk ) {
1899- ( "is_ok()" , try_get_generic_ty ( op_ty , 0 ) . unwrap_or ( op_ty ) )
1743+ "is_ok()"
19001744 } 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()"
19021746 } else if is_lang_ctor ( cx, path, OptionSome ) {
1903- ( "is_some()" , op_ty )
1747+ "is_some()"
19041748 } else if is_lang_ctor ( cx, path, PollReady ) {
1905- ( "is_ready()" , op_ty )
1749+ "is_ready()"
19061750 } else if is_qpath_def_path ( cx, path, sub_pat. hir_id , & paths:: IPADDR_V4 ) {
1907- ( "is_ipv4()" , op_ty )
1751+ "is_ipv4()"
19081752 } else if is_qpath_def_path ( cx, path, sub_pat. hir_id , & paths:: IPADDR_V6 ) {
1909- ( "is_ipv6()" , op_ty )
1753+ "is_ipv6()"
19101754 } else {
19111755 return ;
19121756 }
@@ -1915,36 +1759,17 @@ mod redundant_pattern_match {
19151759 }
19161760 } ,
19171761 PatKind :: Path ( ref path) => {
1918- let method = if is_lang_ctor ( cx, path, OptionNone ) {
1762+ if is_lang_ctor ( cx, path, OptionNone ) {
19191763 "is_none()"
19201764 } else if is_lang_ctor ( cx, path, PollPending ) {
19211765 "is_pending()"
19221766 } else {
19231767 return ;
1924- } ;
1925- // `None` and `Pending` don't have an inner type.
1926- ( method, cx. tcx . types . unit )
1768+ }
19271769 } ,
19281770 _ => return ,
19291771 } ;
19301772
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-
19481773 // check that `while_let_on_iterator` lint does not trigger
19491774 if_chain ! {
19501775 if keyword == "while" ;
@@ -1963,7 +1788,7 @@ mod redundant_pattern_match {
19631788 span_lint_and_then (
19641789 cx,
19651790 REDUNDANT_PATTERN_MATCHING ,
1966- arm . pat . span ,
1791+ arms [ 0 ] . pat . span ,
19671792 & format ! ( "redundant pattern matching, consider using `{}`" , good_method) ,
19681793 |diag| {
19691794 // while let ... = ... { ... }
@@ -1977,20 +1802,12 @@ mod redundant_pattern_match {
19771802 // while let ... = ... { ... }
19781803 // ^^^^^^^^^^^^^^^^^^^
19791804 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+ ) ;
19941811 } ,
19951812 ) ;
19961813 }
0 commit comments