Skip to content

Commit 1691b1d

Browse files
committed
add [unnecessary_reserve] lint
1 parent 4198013 commit 1691b1d

9 files changed

+285
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3813,6 +3813,7 @@ Released 2018-09-13
38133813
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
38143814
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
38153815
[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
3816+
[`unnecessary_reserve`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_reserve
38163817
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
38173818
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
38183819
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned

clippy_lints/src/lib.register_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@ store.register_lints(&[
554554
unnamed_address::FN_ADDRESS_COMPARISONS,
555555
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
556556
unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
557+
unnecessary_reserve::UNNECESSARY_RESERVE,
557558
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
558559
unnecessary_sort_by::UNNECESSARY_SORT_BY,
559560
unnecessary_wraps::UNNECESSARY_WRAPS,

clippy_lints/src/lib.register_pedantic.rs

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
9090
LintId::of(types::LINKEDLIST),
9191
LintId::of(types::OPTION_OPTION),
9292
LintId::of(unicode::UNICODE_NOT_NFC),
93+
LintId::of(unnecessary_reserve::UNNECESSARY_RESERVE),
9394
LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
9495
LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
9596
LintId::of(unused_async::UNUSED_ASYNC),

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ mod unit_return_expecting_ord;
386386
mod unit_types;
387387
mod unnamed_address;
388388
mod unnecessary_owned_empty_strings;
389+
mod unnecessary_reserve;
389390
mod unnecessary_self_imports;
390391
mod unnecessary_sort_by;
391392
mod unnecessary_wraps;
@@ -913,6 +914,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
913914
store.register_late_pass(move || Box::new(manual_retain::ManualRetain::new(msrv)));
914915
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
915916
store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
917+
store.register_late_pass(move || Box::new(unnecessary_reserve::UnnecessaryReserve::new(msrv)));
916918
// add lints here, do not remove this comment, it's used in `new_lint`
917919
}
918920

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use clippy_utils::{match_def_path, meets_msrv, msrvs, paths, visitors::expr_visitor_no_bodies};
3+
use rustc_hir::{intravisit::Visitor, Block, ExprKind, QPath, StmtKind};
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_middle::ty;
6+
use rustc_semver::RustcVersion;
7+
use rustc_session::{declare_tool_lint, impl_lint_pass};
8+
use rustc_span::sym;
9+
10+
declare_clippy_lint! {
11+
/// ### What it does
12+
/// This lint checks for `reserve` before calling the `extend` method.
13+
/// ### Why is this bad?
14+
/// vec::reserve method before vec::extend is no longer makes sense in rustc version >= 1.62
15+
/// ### Example
16+
/// ```rust
17+
/// let mut vec: Vec<usize> = vec![];
18+
/// let b: &[usize] = [1];
19+
/// vec.reserve(b.len());
20+
/// vec.extend(b);
21+
/// ```
22+
/// Use instead:
23+
/// ```rust
24+
/// let mut vec: Vec<usize> = vec![];
25+
/// let b: &[usize] = [1];
26+
/// vec.extend(b);
27+
/// ```
28+
#[clippy::version = "1.64.0"]
29+
pub UNNECESSARY_RESERVE,
30+
pedantic,
31+
"`reserve` method before `extend` is no longer makes sense in rustc version >= 1.62"
32+
}
33+
34+
pub struct UnnecessaryReserve {
35+
msrv: Option<RustcVersion>,
36+
}
37+
38+
impl UnnecessaryReserve {
39+
#[must_use]
40+
pub fn new(msrv: Option<RustcVersion>) -> Self {
41+
Self { msrv }
42+
}
43+
}
44+
45+
impl_lint_pass!(UnnecessaryReserve => [UNNECESSARY_RESERVE]);
46+
47+
impl<'tcx> LateLintPass<'tcx> for UnnecessaryReserve {
48+
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
49+
if !meets_msrv(self.msrv, msrvs::UNNECESSARY_RESERVE) {
50+
return;
51+
}
52+
53+
for (idx, stmt) in block.stmts.iter().enumerate() {
54+
if let StmtKind::Semi(semi_expr) = stmt.kind
55+
&& let ExprKind::MethodCall(_, [struct_calling_on, _], _) = semi_expr.kind
56+
&& let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(semi_expr.hir_id)
57+
&& (match_def_path(cx, expr_def_id, &paths::VEC_RESERVE) ||
58+
match_def_path(cx, expr_def_id, &paths::VEC_DEQUE_RESERVE))
59+
&& acceptable_type(cx, struct_calling_on)
60+
&& let Some(next_stmt_span) = check_extend_method(cx, block, idx, struct_calling_on)
61+
&& !next_stmt_span.from_expansion()
62+
{
63+
span_lint(
64+
cx,
65+
UNNECESSARY_RESERVE,
66+
next_stmt_span,
67+
"this `reserve` no longer makes sense in rustc version >= 1.62",
68+
);
69+
}
70+
}
71+
}
72+
73+
extract_msrv_attr!(LateContext);
74+
}
75+
76+
#[must_use]
77+
fn acceptable_type(cx: &LateContext<'_>, struct_calling_on: &rustc_hir::Expr<'_>) -> bool {
78+
let acceptable_types = [sym::Vec, sym::VecDeque];
79+
acceptable_types.iter().any(|&acceptable_ty| {
80+
match cx.typeck_results().expr_ty(struct_calling_on).peel_refs().kind() {
81+
ty::Adt(def, _) => cx.tcx.is_diagnostic_item(acceptable_ty, def.did()),
82+
_ => false,
83+
}
84+
})
85+
}
86+
87+
#[must_use]
88+
fn check_extend_method(
89+
cx: &LateContext<'_>,
90+
block: &Block<'_>,
91+
idx: usize,
92+
struct_expr: &rustc_hir::Expr<'_>,
93+
) -> Option<rustc_span::Span> {
94+
let mut read_found = false;
95+
let next_stmt_span;
96+
97+
let mut visitor = expr_visitor_no_bodies(|expr| {
98+
if let ExprKind::MethodCall(_, [struct_calling_on, _], _) = expr.kind
99+
&& let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
100+
&& match_def_path(cx, expr_def_id, &paths::ITER_EXTEND)
101+
&& acceptable_type(cx, struct_calling_on)
102+
&& equal_ident(struct_calling_on, struct_expr)
103+
{
104+
read_found = true;
105+
}
106+
!read_found
107+
});
108+
109+
if idx == block.stmts.len() - 1 {
110+
if let Some(e) = block.expr {
111+
visitor.visit_expr(e);
112+
next_stmt_span = e.span;
113+
} else {
114+
return None;
115+
}
116+
} else {
117+
let next_stmt = &block.stmts[idx + 1];
118+
visitor.visit_stmt(next_stmt);
119+
next_stmt_span = next_stmt.span;
120+
}
121+
drop(visitor);
122+
123+
if read_found {
124+
return Some(next_stmt_span);
125+
}
126+
127+
None
128+
}
129+
130+
#[must_use]
131+
fn equal_ident(left: &rustc_hir::Expr<'_>, right: &rustc_hir::Expr<'_>) -> bool {
132+
fn ident_name(expr: &rustc_hir::Expr<'_>) -> Option<rustc_span::Symbol> {
133+
if let ExprKind::Path(QPath::Resolved(None, inner_path)) = expr.kind
134+
&& let [inner_seg] = inner_path.segments {
135+
return Some(inner_seg.ident.name);
136+
}
137+
None
138+
}
139+
140+
ident_name(left) == ident_name(right)
141+
}

clippy_utils/src/msrvs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ macro_rules! msrv_aliases {
1212

1313
// names may refer to stabilized feature flags or library items
1414
msrv_aliases! {
15+
1,62,0 { UNNECESSARY_RESERVE }
1516
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN }
1617
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
1718
1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }

clippy_utils/src/paths.rs

+3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
7070
pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
7171
pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
7272
pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
73+
pub const ITER_EXTEND: [&str; 6] = ["core", "iter", "traits", "collect", "Extend", "extend"];
7374
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
7475
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
7576
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
@@ -190,6 +191,8 @@ pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "Vec
190191
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
191192
pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
192193
pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
194+
pub const VEC_RESERVE: [&str; 4] = ["alloc", "vec", "Vec", "reserve"];
195+
pub const VEC_DEQUE_RESERVE: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "reserve"];
193196
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
194197
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
195198
pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];

tests/ui/unnecessary_reserve.rs

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#![warn(clippy::unnecessary_reserve)]
2+
#![feature(custom_inner_attributes)]
3+
4+
use std::collections::HashMap;
5+
use std::collections::VecDeque;
6+
7+
fn main() {
8+
vec_reserve();
9+
vec_deque_reserve();
10+
hash_map_reserve();
11+
msrv_1_62();
12+
}
13+
14+
fn vec_reserve() {
15+
let mut vec: Vec<usize> = vec![];
16+
let array: &[usize] = &[1, 2];
17+
18+
// do lint
19+
vec.reserve(1);
20+
vec.extend([1]);
21+
22+
// do lint
23+
vec.reserve(array.len());
24+
vec.extend(array);
25+
26+
// do lint
27+
{
28+
vec.reserve(1);
29+
vec.extend([1])
30+
};
31+
32+
// do not lint
33+
vec.reserve(array.len());
34+
vec.push(1);
35+
vec.extend(array);
36+
37+
// do not lint
38+
let mut other_vec: Vec<usize> = vec![];
39+
other_vec.reserve(1);
40+
vec.extend([1])
41+
}
42+
43+
fn vec_deque_reserve() {
44+
let mut vec_deque: VecDeque<usize> = [1].into();
45+
let array: &[usize] = &[1, 2];
46+
47+
// do lint
48+
vec_deque.reserve(1);
49+
vec_deque.extend([1]);
50+
51+
// do lint
52+
vec_deque.reserve(array.len());
53+
vec_deque.extend(array);
54+
55+
// do lint
56+
{
57+
vec_deque.reserve(1);
58+
vec_deque.extend([1])
59+
};
60+
61+
// do not lint
62+
vec_deque.reserve(array.len() + 1);
63+
vec_deque.push_back(1);
64+
vec_deque.extend(array);
65+
66+
// do not lint
67+
let mut other_vec_deque: VecDeque<usize> = [1].into();
68+
other_vec_deque.reserve(1);
69+
vec_deque.extend([1])
70+
}
71+
72+
fn hash_map_reserve() {
73+
let mut map: HashMap<usize, usize> = HashMap::new();
74+
let mut other_map: HashMap<usize, usize> = HashMap::new();
75+
// do not lint
76+
map.reserve(other_map.len());
77+
map.extend(other_map);
78+
}
79+
80+
fn msrv_1_62() {
81+
#![clippy::msrv = "1.61"]
82+
let mut vec: Vec<usize> = vec![];
83+
let array: &[usize] = &[1, 2];
84+
85+
// do not lint
86+
vec.reserve(1);
87+
vec.extend([1]);
88+
89+
let mut vec_deque: VecDeque<usize> = [1].into();
90+
let array: &[usize] = &[1, 2];
91+
92+
// do not lint
93+
vec_deque.reserve(1);
94+
vec_deque.extend([1]);
95+
}

tests/ui/unnecessary_reserve.stderr

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error: this `reserve` no longer makes sense in rustc version >= 1.62
2+
--> $DIR/unnecessary_reserve.rs:20:5
3+
|
4+
LL | vec.extend([1]);
5+
| ^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::unnecessary-reserve` implied by `-D warnings`
8+
9+
error: this `reserve` no longer makes sense in rustc version >= 1.62
10+
--> $DIR/unnecessary_reserve.rs:24:5
11+
|
12+
LL | vec.extend(array);
13+
| ^^^^^^^^^^^^^^^^^^
14+
15+
error: this `reserve` no longer makes sense in rustc version >= 1.62
16+
--> $DIR/unnecessary_reserve.rs:29:9
17+
|
18+
LL | vec.extend([1])
19+
| ^^^^^^^^^^^^^^^
20+
21+
error: this `reserve` no longer makes sense in rustc version >= 1.62
22+
--> $DIR/unnecessary_reserve.rs:49:5
23+
|
24+
LL | vec_deque.extend([1]);
25+
| ^^^^^^^^^^^^^^^^^^^^^^
26+
27+
error: this `reserve` no longer makes sense in rustc version >= 1.62
28+
--> $DIR/unnecessary_reserve.rs:53:5
29+
|
30+
LL | vec_deque.extend(array);
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^
32+
33+
error: this `reserve` no longer makes sense in rustc version >= 1.62
34+
--> $DIR/unnecessary_reserve.rs:58:9
35+
|
36+
LL | vec_deque.extend([1])
37+
| ^^^^^^^^^^^^^^^^^^^^^
38+
39+
error: aborting due to 6 previous errors
40+

0 commit comments

Comments
 (0)