11use 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 ;
35use rustc_errors:: Applicability ;
4- use rustc_hir:: { intravisit :: Visitor , Block , ExprKind , QPath , StmtKind } ;
6+ use rustc_hir:: { Block , ExprKind , PathSegment , StmtKind } ;
57use rustc_lint:: { LateContext , LateLintPass } ;
68use rustc_middle:: ty;
7- use rustc_semver:: RustcVersion ;
89use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
910use rustc_span:: sym;
1011
1112declare_clippy_lint ! {
1213 /// ### What it does
14+ ///
1315 /// This lint checks for a call to `reserve` before `extend` on a `Vec` or `VecDeque`.
1416 /// ### Why is this bad?
1517 /// Since Rust 1.62, `extend` implicitly calls `reserve`
18+ ///
1619 /// ### Example
1720 /// ```rust
1821 /// let mut vec: Vec<usize> = vec![];
@@ -32,31 +35,27 @@ declare_clippy_lint! {
3235 "calling `reserve` before `extend` on a `Vec` or `VecDeque`, when it will be called implicitly"
3336}
3437
38+ impl_lint_pass ! ( UnnecessaryReserve => [ UNNECESSARY_RESERVE ] ) ;
39+
3540pub struct UnnecessaryReserve {
36- msrv : Option < RustcVersion > ,
41+ msrv : Msrv ,
3742}
38-
3943impl UnnecessaryReserve {
40- #[ must_use]
41- pub fn new ( msrv : Option < RustcVersion > ) -> Self {
44+ pub fn new ( msrv : Msrv ) -> Self {
4245 Self { msrv }
4346 }
4447}
4548
46- impl_lint_pass ! ( UnnecessaryReserve => [ UNNECESSARY_RESERVE ] ) ;
47-
4849impl < ' tcx > LateLintPass < ' tcx > for UnnecessaryReserve {
4950 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 ) {
5152 return ;
5253 }
5354
5455 for ( idx, stmt) in block. stmts . iter ( ) . enumerate ( ) {
5556 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"
6059 && acceptable_type ( cx, struct_calling_on)
6160 && let Some ( next_stmt_span) = check_extend_method ( cx, block, idx, struct_calling_on)
6261 && !next_stmt_span. from_expansion ( )
@@ -102,50 +101,36 @@ fn check_extend_method(
102101) -> Option < rustc_span:: Span > {
103102 let mut read_found = false ;
104103 let next_stmt_span;
104+ let mut spanless_eq = SpanlessEq :: new ( cx) ;
105105
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
108108 && let Some ( expr_def_id) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id )
109109 && match_def_path ( cx, expr_def_id, & paths:: ITER_EXTEND )
110110 && 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)
112113 {
113114 read_found = true ;
114115 }
115- !read_found
116+ let _ = !read_found;
117+ ControlFlow :: Continue ( ( ) )
116118 } ) ;
117119
118120 if idx == block. stmts . len ( ) - 1 {
119121 if let Some ( e) = block. expr {
120- visitor. visit_expr ( e) ;
121122 next_stmt_span = e. span ;
122123 } else {
123124 return None ;
124125 }
125126 } else {
126127 let next_stmt = & block. stmts [ idx + 1 ] ;
127- visitor. visit_stmt ( next_stmt) ;
128128 next_stmt_span = next_stmt. span ;
129129 }
130- drop ( visitor) ;
131130
132131 if read_found {
133132 return Some ( next_stmt_span) ;
134133 }
135134
136135 None
137136}
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