@@ -6,15 +6,16 @@ mod for_mut_range_bound;
6
6
mod for_single_element_loop;
7
7
mod infinite_loop;
8
8
mod manual_flatten;
9
+ mod needless_collect;
9
10
mod utils;
10
11
11
12
use crate :: utils:: sugg:: Sugg ;
12
13
use crate :: utils:: usage:: mutated_variables;
13
14
use crate :: utils:: {
14
15
get_enclosing_block, get_parent_expr, get_trait_def_id, higher, implements_trait, is_in_panic_handler,
15
16
is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
16
- match_type , path_to_local, path_to_local_id, paths, snippet, snippet_with_applicability,
17
- snippet_with_macro_callsite , span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then , sugg,
17
+ path_to_local, path_to_local_id, paths, snippet, snippet_with_applicability, snippet_with_macro_callsite ,
18
+ span_lint, span_lint_and_help, span_lint_and_sugg, sugg,
18
19
} ;
19
20
use if_chain:: if_chain;
20
21
use rustc_ast:: ast;
@@ -23,16 +24,16 @@ use rustc_errors::Applicability;
23
24
use rustc_hir:: def:: { DefKind , Res } ;
24
25
use rustc_hir:: intravisit:: { walk_block, walk_expr, walk_pat, walk_stmt, NestedVisitorMap , Visitor } ;
25
26
use rustc_hir:: {
26
- BinOpKind , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , GenericArg , HirId , InlineAsmOperand , Local ,
27
- LoopSource , MatchSource , Mutability , Node , Pat , PatKind , QPath , Stmt , StmtKind ,
27
+ BinOpKind , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , HirId , InlineAsmOperand , LoopSource , MatchSource ,
28
+ Mutability , Node , Pat , PatKind , Stmt , StmtKind ,
28
29
} ;
29
30
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
30
31
use rustc_middle:: hir:: map:: Map ;
31
32
use rustc_middle:: lint:: in_external_macro;
32
33
use rustc_middle:: ty:: { self , Ty } ;
33
34
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
34
35
use rustc_span:: source_map:: Span ;
35
- use rustc_span:: symbol:: { sym, Ident , Symbol } ;
36
+ use rustc_span:: symbol:: { sym, Symbol } ;
36
37
use std:: iter:: { once, Iterator } ;
37
38
use utils:: { get_span_of_entire_for_loop, make_iterator_snippet} ;
38
39
@@ -686,7 +687,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
686
687
infinite_loop:: check_infinite_loop ( cx, cond, body) ;
687
688
}
688
689
689
- check_needless_collect ( expr, cx) ;
690
+ needless_collect :: check_needless_collect ( expr, cx) ;
690
691
}
691
692
}
692
693
@@ -1894,272 +1895,3 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
1894
1895
NestedVisitorMap :: None
1895
1896
}
1896
1897
}
1897
-
1898
- const NEEDLESS_COLLECT_MSG : & str = "avoid using `collect()` when not needed" ;
1899
-
1900
- fn check_needless_collect < ' tcx > ( expr : & ' tcx Expr < ' _ > , cx : & LateContext < ' tcx > ) {
1901
- check_needless_collect_direct_usage ( expr, cx) ;
1902
- check_needless_collect_indirect_usage ( expr, cx) ;
1903
- }
1904
- fn check_needless_collect_direct_usage < ' tcx > ( expr : & ' tcx Expr < ' _ > , cx : & LateContext < ' tcx > ) {
1905
- if_chain ! {
1906
- if let ExprKind :: MethodCall ( ref method, _, ref args, _) = expr. kind;
1907
- if let ExprKind :: MethodCall ( ref chain_method, _, _, _) = args[ 0 ] . kind;
1908
- if chain_method. ident. name == sym!( collect) && match_trait_method( cx, & args[ 0 ] , & paths:: ITERATOR ) ;
1909
- if let Some ( ref generic_args) = chain_method. args;
1910
- if let Some ( GenericArg :: Type ( ref ty) ) = generic_args. args. get( 0 ) ;
1911
- then {
1912
- let ty = cx. typeck_results( ) . node_type( ty. hir_id) ;
1913
- if is_type_diagnostic_item( cx, ty, sym:: vec_type) ||
1914
- is_type_diagnostic_item( cx, ty, sym!( vecdeque_type) ) ||
1915
- match_type( cx, ty, & paths:: BTREEMAP ) ||
1916
- is_type_diagnostic_item( cx, ty, sym!( hashmap_type) ) {
1917
- if method. ident. name == sym!( len) {
1918
- let span = shorten_needless_collect_span( expr) ;
1919
- span_lint_and_sugg(
1920
- cx,
1921
- NEEDLESS_COLLECT ,
1922
- span,
1923
- NEEDLESS_COLLECT_MSG ,
1924
- "replace with" ,
1925
- "count()" . to_string( ) ,
1926
- Applicability :: MachineApplicable ,
1927
- ) ;
1928
- }
1929
- if method. ident. name == sym!( is_empty) {
1930
- let span = shorten_needless_collect_span( expr) ;
1931
- span_lint_and_sugg(
1932
- cx,
1933
- NEEDLESS_COLLECT ,
1934
- span,
1935
- NEEDLESS_COLLECT_MSG ,
1936
- "replace with" ,
1937
- "next().is_none()" . to_string( ) ,
1938
- Applicability :: MachineApplicable ,
1939
- ) ;
1940
- }
1941
- if method. ident. name == sym!( contains) {
1942
- let contains_arg = snippet( cx, args[ 1 ] . span, "??" ) ;
1943
- let span = shorten_needless_collect_span( expr) ;
1944
- span_lint_and_then(
1945
- cx,
1946
- NEEDLESS_COLLECT ,
1947
- span,
1948
- NEEDLESS_COLLECT_MSG ,
1949
- |diag| {
1950
- let ( arg, pred) = contains_arg
1951
- . strip_prefix( '&' )
1952
- . map_or( ( "&x" , & * contains_arg) , |s| ( "x" , s) ) ;
1953
- diag. span_suggestion(
1954
- span,
1955
- "replace with" ,
1956
- format!(
1957
- "any(|{}| x == {})" ,
1958
- arg, pred
1959
- ) ,
1960
- Applicability :: MachineApplicable ,
1961
- ) ;
1962
- }
1963
- ) ;
1964
- }
1965
- }
1966
- }
1967
- }
1968
- }
1969
-
1970
- fn check_needless_collect_indirect_usage < ' tcx > ( expr : & ' tcx Expr < ' _ > , cx : & LateContext < ' tcx > ) {
1971
- if let ExprKind :: Block ( ref block, _) = expr. kind {
1972
- for ref stmt in block. stmts {
1973
- if_chain ! {
1974
- if let StmtKind :: Local (
1975
- Local { pat: Pat { hir_id: pat_id, kind: PatKind :: Binding ( _, _, ident, .. ) , .. } ,
1976
- init: Some ( ref init_expr) , .. }
1977
- ) = stmt. kind;
1978
- if let ExprKind :: MethodCall ( ref method_name, _, & [ ref iter_source] , ..) = init_expr. kind;
1979
- if method_name. ident. name == sym!( collect) && match_trait_method( cx, & init_expr, & paths:: ITERATOR ) ;
1980
- if let Some ( ref generic_args) = method_name. args;
1981
- if let Some ( GenericArg :: Type ( ref ty) ) = generic_args. args. get( 0 ) ;
1982
- if let ty = cx. typeck_results( ) . node_type( ty. hir_id) ;
1983
- if is_type_diagnostic_item( cx, ty, sym:: vec_type) ||
1984
- is_type_diagnostic_item( cx, ty, sym!( vecdeque_type) ) ||
1985
- match_type( cx, ty, & paths:: LINKED_LIST ) ;
1986
- if let Some ( iter_calls) = detect_iter_and_into_iters( block, * ident) ;
1987
- if iter_calls. len( ) == 1 ;
1988
- then {
1989
- let mut used_count_visitor = UsedCountVisitor {
1990
- cx,
1991
- id: * pat_id,
1992
- count: 0 ,
1993
- } ;
1994
- walk_block( & mut used_count_visitor, block) ;
1995
- if used_count_visitor. count > 1 {
1996
- return ;
1997
- }
1998
-
1999
- // Suggest replacing iter_call with iter_replacement, and removing stmt
2000
- let iter_call = & iter_calls[ 0 ] ;
2001
- span_lint_and_then(
2002
- cx,
2003
- NEEDLESS_COLLECT ,
2004
- stmt. span. until( iter_call. span) ,
2005
- NEEDLESS_COLLECT_MSG ,
2006
- |diag| {
2007
- let iter_replacement = format!( "{}{}" , Sugg :: hir( cx, iter_source, ".." ) , iter_call. get_iter_method( cx) ) ;
2008
- diag. multipart_suggestion(
2009
- iter_call. get_suggestion_text( ) ,
2010
- vec![
2011
- ( stmt. span, String :: new( ) ) ,
2012
- ( iter_call. span, iter_replacement)
2013
- ] ,
2014
- Applicability :: MachineApplicable , // MaybeIncorrect,
2015
- ) . emit( ) ;
2016
- } ,
2017
- ) ;
2018
- }
2019
- }
2020
- }
2021
- }
2022
- }
2023
-
2024
- struct IterFunction {
2025
- func : IterFunctionKind ,
2026
- span : Span ,
2027
- }
2028
- impl IterFunction {
2029
- fn get_iter_method ( & self , cx : & LateContext < ' _ > ) -> String {
2030
- match & self . func {
2031
- IterFunctionKind :: IntoIter => String :: new ( ) ,
2032
- IterFunctionKind :: Len => String :: from ( ".count()" ) ,
2033
- IterFunctionKind :: IsEmpty => String :: from ( ".next().is_none()" ) ,
2034
- IterFunctionKind :: Contains ( span) => {
2035
- let s = snippet ( cx, * span, ".." ) ;
2036
- if let Some ( stripped) = s. strip_prefix ( '&' ) {
2037
- format ! ( ".any(|x| x == {})" , stripped)
2038
- } else {
2039
- format ! ( ".any(|x| x == *{})" , s)
2040
- }
2041
- } ,
2042
- }
2043
- }
2044
- fn get_suggestion_text ( & self ) -> & ' static str {
2045
- match & self . func {
2046
- IterFunctionKind :: IntoIter => {
2047
- "use the original Iterator instead of collecting it and then producing a new one"
2048
- } ,
2049
- IterFunctionKind :: Len => {
2050
- "take the original Iterator's count instead of collecting it and finding the length"
2051
- } ,
2052
- IterFunctionKind :: IsEmpty => {
2053
- "check if the original Iterator has anything instead of collecting it and seeing if it's empty"
2054
- } ,
2055
- IterFunctionKind :: Contains ( _) => {
2056
- "check if the original Iterator contains an element instead of collecting then checking"
2057
- } ,
2058
- }
2059
- }
2060
- }
2061
- enum IterFunctionKind {
2062
- IntoIter ,
2063
- Len ,
2064
- IsEmpty ,
2065
- Contains ( Span ) ,
2066
- }
2067
-
2068
- struct IterFunctionVisitor {
2069
- uses : Vec < IterFunction > ,
2070
- seen_other : bool ,
2071
- target : Ident ,
2072
- }
2073
- impl < ' tcx > Visitor < ' tcx > for IterFunctionVisitor {
2074
- fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
2075
- // Check function calls on our collection
2076
- if_chain ! {
2077
- if let ExprKind :: MethodCall ( method_name, _, ref args, _) = & expr. kind;
2078
- if let Some ( Expr { kind: ExprKind :: Path ( QPath :: Resolved ( _, ref path) ) , .. } ) = args. get( 0 ) ;
2079
- if let & [ name] = & path. segments;
2080
- if name. ident == self . target;
2081
- then {
2082
- let len = sym!( len) ;
2083
- let is_empty = sym!( is_empty) ;
2084
- let contains = sym!( contains) ;
2085
- match method_name. ident. name {
2086
- sym:: into_iter => self . uses. push(
2087
- IterFunction { func: IterFunctionKind :: IntoIter , span: expr. span }
2088
- ) ,
2089
- name if name == len => self . uses. push(
2090
- IterFunction { func: IterFunctionKind :: Len , span: expr. span }
2091
- ) ,
2092
- name if name == is_empty => self . uses. push(
2093
- IterFunction { func: IterFunctionKind :: IsEmpty , span: expr. span }
2094
- ) ,
2095
- name if name == contains => self . uses. push(
2096
- IterFunction { func: IterFunctionKind :: Contains ( args[ 1 ] . span) , span: expr. span }
2097
- ) ,
2098
- _ => self . seen_other = true ,
2099
- }
2100
- return
2101
- }
2102
- }
2103
- // Check if the collection is used for anything else
2104
- if_chain ! {
2105
- if let Expr { kind: ExprKind :: Path ( QPath :: Resolved ( _, ref path) ) , .. } = expr;
2106
- if let & [ name] = & path. segments;
2107
- if name. ident == self . target;
2108
- then {
2109
- self . seen_other = true ;
2110
- } else {
2111
- walk_expr( self , expr) ;
2112
- }
2113
- }
2114
- }
2115
-
2116
- type Map = Map < ' tcx > ;
2117
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
2118
- NestedVisitorMap :: None
2119
- }
2120
- }
2121
-
2122
- struct UsedCountVisitor < ' a , ' tcx > {
2123
- cx : & ' a LateContext < ' tcx > ,
2124
- id : HirId ,
2125
- count : usize ,
2126
- }
2127
-
2128
- impl < ' a , ' tcx > Visitor < ' tcx > for UsedCountVisitor < ' a , ' tcx > {
2129
- type Map = Map < ' tcx > ;
2130
-
2131
- fn visit_expr ( & mut self , expr : & ' tcx Expr < ' _ > ) {
2132
- if path_to_local_id ( expr, self . id ) {
2133
- self . count += 1 ;
2134
- } else {
2135
- walk_expr ( self , expr) ;
2136
- }
2137
- }
2138
-
2139
- fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
2140
- NestedVisitorMap :: OnlyBodies ( self . cx . tcx . hir ( ) )
2141
- }
2142
- }
2143
-
2144
- /// Detect the occurrences of calls to `iter` or `into_iter` for the
2145
- /// given identifier
2146
- fn detect_iter_and_into_iters < ' tcx > ( block : & ' tcx Block < ' tcx > , identifier : Ident ) -> Option < Vec < IterFunction > > {
2147
- let mut visitor = IterFunctionVisitor {
2148
- uses : Vec :: new ( ) ,
2149
- target : identifier,
2150
- seen_other : false ,
2151
- } ;
2152
- visitor. visit_block ( block) ;
2153
- if visitor. seen_other { None } else { Some ( visitor. uses ) }
2154
- }
2155
-
2156
- fn shorten_needless_collect_span ( expr : & Expr < ' _ > ) -> Span {
2157
- if_chain ! {
2158
- if let ExprKind :: MethodCall ( .., args, _) = & expr. kind;
2159
- if let ExprKind :: MethodCall ( _, span, ..) = & args[ 0 ] . kind;
2160
- then {
2161
- return expr. span. with_lo( span. lo( ) ) ;
2162
- }
2163
- }
2164
- unreachable ! ( ) ;
2165
- }
0 commit comments