1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
- use clippy_utils:: { match_def_path, meets_msrv, msrvs, paths, visitors:: expr_visitor_no_bodies} ;
2
+ use clippy_utils:: msrvs:: { self , Msrv } ;
3
+ use clippy_utils:: { match_def_path, paths, visitors:: for_each_expr, SpanlessEq } ;
4
+ use core:: ops:: ControlFlow ;
3
5
use rustc_errors:: Applicability ;
4
- use rustc_hir:: { intravisit :: Visitor , Block , ExprKind , QPath , StmtKind } ;
6
+ use rustc_hir:: { Block , ExprKind , PathSegment , StmtKind } ;
5
7
use rustc_lint:: { LateContext , LateLintPass } ;
6
8
use rustc_middle:: ty;
7
- use rustc_semver:: RustcVersion ;
8
9
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
9
10
use rustc_span:: sym;
10
11
11
12
declare_clippy_lint ! {
12
13
/// ### What it does
14
+ ///
13
15
/// This lint checks for a call to `reserve` before `extend` on a `Vec` or `VecDeque`.
14
16
/// ### Why is this bad?
15
17
/// Since Rust 1.62, `extend` implicitly calls `reserve`
18
+ ///
16
19
/// ### Example
17
20
/// ```rust
18
21
/// let mut vec: Vec<usize> = vec![];
@@ -32,31 +35,27 @@ declare_clippy_lint! {
32
35
"calling `reserve` before `extend` on a `Vec` or `VecDeque`, when it will be called implicitly"
33
36
}
34
37
38
+ impl_lint_pass ! ( UnnecessaryReserve => [ UNNECESSARY_RESERVE ] ) ;
39
+
35
40
pub struct UnnecessaryReserve {
36
- msrv : Option < RustcVersion > ,
41
+ msrv : Msrv ,
37
42
}
38
-
39
43
impl UnnecessaryReserve {
40
- #[ must_use]
41
- pub fn new ( msrv : Option < RustcVersion > ) -> Self {
44
+ pub fn new ( msrv : Msrv ) -> Self {
42
45
Self { msrv }
43
46
}
44
47
}
45
48
46
- impl_lint_pass ! ( UnnecessaryReserve => [ UNNECESSARY_RESERVE ] ) ;
47
-
48
49
impl < ' tcx > LateLintPass < ' tcx > for UnnecessaryReserve {
49
50
fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & Block < ' tcx > ) {
50
- if !meets_msrv ( self . msrv , msrvs:: UNNECESSARY_RESERVE ) {
51
+ if !self . msrv . meets ( msrvs:: CHECK_UNNECESSARY_RESERVE ) {
51
52
return ;
52
53
}
53
54
54
55
for ( idx, stmt) in block. stmts . iter ( ) . enumerate ( ) {
55
56
if let StmtKind :: Semi ( semi_expr) = stmt. kind
56
- && let ExprKind :: MethodCall ( _, [ struct_calling_on, _] , _) = semi_expr. kind
57
- && let Some ( expr_def_id) = cx. typeck_results ( ) . type_dependent_def_id ( semi_expr. hir_id )
58
- && ( match_def_path ( cx, expr_def_id, & paths:: VEC_RESERVE ) ||
59
- match_def_path ( cx, expr_def_id, & paths:: VEC_DEQUE_RESERVE ) )
57
+ && let ExprKind :: MethodCall ( PathSegment { ident : method, .. } , struct_calling_on, _, _) = semi_expr. kind
58
+ && method. name . as_str ( ) == "reserve"
60
59
&& acceptable_type ( cx, struct_calling_on)
61
60
&& let Some ( next_stmt_span) = check_extend_method ( cx, block, idx, struct_calling_on)
62
61
&& !next_stmt_span. from_expansion ( )
@@ -102,50 +101,36 @@ fn check_extend_method(
102
101
) -> Option < rustc_span:: Span > {
103
102
let mut read_found = false ;
104
103
let next_stmt_span;
104
+ let mut spanless_eq = SpanlessEq :: new ( cx) ;
105
105
106
- let mut visitor = expr_visitor_no_bodies ( |expr| {
107
- if let ExprKind :: MethodCall ( _, [ struct_calling_on, _] , _) = expr. kind
106
+ let _ : Option < ! > = for_each_expr ( block , |expr| {
107
+ if let ExprKind :: MethodCall ( _, struct_calling_on, _, _) = expr. kind
108
108
&& let Some ( expr_def_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
109
109
&& match_def_path ( cx, expr_def_id, & paths:: ITER_EXTEND )
110
110
&& acceptable_type ( cx, struct_calling_on)
111
- && equal_ident ( struct_calling_on, struct_expr)
111
+ // Check that both expr are equal
112
+ && spanless_eq. eq_expr ( struct_calling_on, struct_expr)
112
113
{
113
114
read_found = true ;
114
115
}
115
- !read_found
116
+ let _ = !read_found;
117
+ ControlFlow :: Continue ( ( ) )
116
118
} ) ;
117
119
118
120
if idx == block. stmts . len ( ) - 1 {
119
121
if let Some ( e) = block. expr {
120
- visitor. visit_expr ( e) ;
121
122
next_stmt_span = e. span ;
122
123
} else {
123
124
return None ;
124
125
}
125
126
} else {
126
127
let next_stmt = & block. stmts [ idx + 1 ] ;
127
- visitor. visit_stmt ( next_stmt) ;
128
128
next_stmt_span = next_stmt. span ;
129
129
}
130
- drop ( visitor) ;
131
130
132
131
if read_found {
133
132
return Some ( next_stmt_span) ;
134
133
}
135
134
136
135
None
137
136
}
138
-
139
- #[ must_use]
140
- fn equal_ident ( left : & rustc_hir:: Expr < ' _ > , right : & rustc_hir:: Expr < ' _ > ) -> bool {
141
- fn ident_name ( expr : & rustc_hir:: Expr < ' _ > ) -> Option < rustc_span:: Symbol > {
142
- if let ExprKind :: Path ( QPath :: Resolved ( None , inner_path) ) = expr. kind
143
- && let [ inner_seg] = inner_path. segments
144
- {
145
- return Some ( inner_seg. ident . name ) ;
146
- }
147
- None
148
- }
149
-
150
- ident_name ( left) == ident_name ( right)
151
- }
0 commit comments