Skip to content

Commit 4932d05

Browse files
committed
Auto merge of #11373 - Red-Rapious:master, r=blyxyas,y21
Added new lint: `reserve_after_initialization` Closes #11330. A new lint that informs the user about a more concise way to create a vector with a known capacity. Example: ```rust let mut v: Vec<usize> = vec![]; v.reserve(10); ``` Produces the following help: ```rust | 2 | / let mut v: Vec<usize> = vec![]; 3 | | v.reserve(10); | |__________________^ help: consider using `Vec::with_capacity(space_hint)`: `let v: Vec<usize> = Vec::with_capacity(10);` | ``` And can be rewritten as: ```rust let v: Vec<usize> = Vec::with_capacity(10); ``` changelog: new lint [`reserve_after_initialization`]
2 parents edfee16 + f3c5877 commit 4932d05

7 files changed

+262
-0
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5299,6 +5299,7 @@ Released 2018-09-13
52995299
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
53005300
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
53015301
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
5302+
[`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization
53025303
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
53035304
[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
53045305
[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err

Diff for: clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
579579
crate::reference::DEREF_ADDROF_INFO,
580580
crate::regex::INVALID_REGEX_INFO,
581581
crate::regex::TRIVIAL_REGEX_INFO,
582+
crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO,
582583
crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO,
583584
crate::returns::LET_AND_RETURN_INFO,
584585
crate::returns::NEEDLESS_RETURN_INFO,

Diff for: clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ mod ref_option_ref;
285285
mod ref_patterns;
286286
mod reference;
287287
mod regex;
288+
mod reserve_after_initialization;
288289
mod return_self_not_must_use;
289290
mod returns;
290291
mod same_name_method;
@@ -1095,6 +1096,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10951096
});
10961097
store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals));
10971098
store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns));
1099+
store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default());
10981100
// add lints here, do not remove this comment, it's used in `new_lint`
10991101
}
11001102

Diff for: clippy_lints/src/reserve_after_initialization.rs

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
3+
use clippy_utils::source::snippet;
4+
use clippy_utils::{is_from_proc_macro, path_to_local_id};
5+
use rustc_errors::Applicability;
6+
use rustc_hir::def::Res;
7+
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind};
8+
use rustc_lint::{LateContext, LateLintPass, LintContext};
9+
use rustc_middle::lint::in_external_macro;
10+
use rustc_session::{declare_tool_lint, impl_lint_pass};
11+
use rustc_span::Span;
12+
13+
declare_clippy_lint! {
14+
/// ### What it does
15+
/// Informs the user about a more concise way to create a vector with a known capacity.
16+
///
17+
/// ### Why is this bad?
18+
/// The `Vec::with_capacity` constructor is less complex.
19+
///
20+
/// ### Example
21+
/// ```rust
22+
/// let mut v: Vec<usize> = vec![];
23+
/// v.reserve(10);
24+
/// ```
25+
/// Use instead:
26+
/// ```rust
27+
/// let mut v: Vec<usize> = Vec::with_capacity(10);
28+
/// ```
29+
#[clippy::version = "1.73.0"]
30+
pub RESERVE_AFTER_INITIALIZATION,
31+
complexity,
32+
"`reserve` called immediatly after `Vec` creation"
33+
}
34+
impl_lint_pass!(ReserveAfterInitialization => [RESERVE_AFTER_INITIALIZATION]);
35+
36+
#[derive(Default)]
37+
pub struct ReserveAfterInitialization {
38+
searcher: Option<VecReserveSearcher>,
39+
}
40+
41+
struct VecReserveSearcher {
42+
local_id: HirId,
43+
err_span: Span,
44+
init_part: String,
45+
space_hint: String,
46+
}
47+
impl VecReserveSearcher {
48+
fn display_err(&self, cx: &LateContext<'_>) {
49+
if self.space_hint.is_empty() {
50+
return;
51+
}
52+
53+
let s = format!("{}Vec::with_capacity({});", self.init_part, self.space_hint);
54+
55+
span_lint_and_sugg(
56+
cx,
57+
RESERVE_AFTER_INITIALIZATION,
58+
self.err_span,
59+
"call to `reserve` immediately after creation",
60+
"consider using `Vec::with_capacity(/* Space hint */)`",
61+
s,
62+
Applicability::HasPlaceholders,
63+
);
64+
}
65+
}
66+
67+
impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization {
68+
fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
69+
self.searcher = None;
70+
}
71+
72+
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
73+
if let Some(init_expr) = local.init
74+
&& let PatKind::Binding(BindingAnnotation::MUT, id, _, None) = local.pat.kind
75+
&& !in_external_macro(cx.sess(), local.span)
76+
&& let Some(init) = get_vec_init_kind(cx, init_expr)
77+
&& !matches!(init, VecInitKind::WithExprCapacity(_) | VecInitKind::WithConstCapacity(_))
78+
{
79+
self.searcher = Some(VecReserveSearcher {
80+
local_id: id,
81+
err_span: local.span,
82+
init_part: snippet(cx, local.span.shrink_to_lo()
83+
.to(init_expr.span.source_callsite().shrink_to_lo()), "..")
84+
.into_owned(),
85+
space_hint: String::new()
86+
});
87+
}
88+
}
89+
90+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
91+
if self.searcher.is_none()
92+
&& let ExprKind::Assign(left, right, _) = expr.kind
93+
&& let ExprKind::Path(QPath::Resolved(None, path)) = left.kind
94+
&& let Res::Local(id) = path.res
95+
&& !in_external_macro(cx.sess(), expr.span)
96+
&& let Some(init) = get_vec_init_kind(cx, right)
97+
&& !matches!(init, VecInitKind::WithExprCapacity(_) | VecInitKind::WithConstCapacity(_))
98+
{
99+
self.searcher = Some(VecReserveSearcher {
100+
local_id: id,
101+
err_span: expr.span,
102+
init_part: snippet(cx, left.span.shrink_to_lo()
103+
.to(right.span.source_callsite().shrink_to_lo()), "..")
104+
.into_owned(), // see `assign_expression` test
105+
space_hint: String::new()
106+
});
107+
}
108+
}
109+
110+
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
111+
if let Some(searcher) = self.searcher.take() {
112+
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
113+
&& let ExprKind::MethodCall(name, self_arg, [space_hint], _) = expr.kind
114+
&& path_to_local_id(self_arg, searcher.local_id)
115+
&& name.ident.as_str() == "reserve"
116+
&& !is_from_proc_macro(cx, expr)
117+
{
118+
self.searcher = Some(VecReserveSearcher {
119+
err_span: searcher.err_span.to(stmt.span),
120+
space_hint: snippet(cx, space_hint.span, "..").into_owned(),
121+
.. searcher
122+
});
123+
} else {
124+
searcher.display_err(cx);
125+
}
126+
}
127+
}
128+
129+
fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
130+
if let Some(searcher) = self.searcher.take() {
131+
searcher.display_err(cx);
132+
}
133+
}
134+
}

Diff for: tests/ui/reserve_after_initialization.fixed

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//@aux-build:proc_macros.rs
2+
#![warn(clippy::reserve_after_initialization)]
3+
#![no_main]
4+
5+
extern crate proc_macros;
6+
use proc_macros::{external, with_span};
7+
8+
// Should lint
9+
fn standard() {
10+
let mut v1: Vec<usize> = Vec::with_capacity(10);
11+
}
12+
13+
// Should lint
14+
fn capacity_as_expr() {
15+
let capacity = 10;
16+
let mut v2: Vec<usize> = Vec::with_capacity(capacity);
17+
}
18+
19+
// Shouldn't lint
20+
fn vec_init_with_argument() {
21+
let mut v3 = vec![1];
22+
v3.reserve(10);
23+
}
24+
25+
// Shouldn't lint
26+
fn called_with_capacity() {
27+
let _v4: Vec<usize> = Vec::with_capacity(10);
28+
}
29+
30+
// Should lint
31+
fn assign_expression() {
32+
let mut v5: Vec<usize> = Vec::new();
33+
v5 = Vec::with_capacity(10);
34+
}
35+
36+
fn in_macros() {
37+
external! {
38+
let mut v: Vec<usize> = vec![];
39+
v.reserve(10);
40+
}
41+
42+
with_span! {
43+
span
44+
45+
let mut v: Vec<usize> = vec![];
46+
v.reserve(10);
47+
}
48+
}

Diff for: tests/ui/reserve_after_initialization.rs

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//@aux-build:proc_macros.rs
2+
#![warn(clippy::reserve_after_initialization)]
3+
#![no_main]
4+
5+
extern crate proc_macros;
6+
use proc_macros::{external, with_span};
7+
8+
// Should lint
9+
fn standard() {
10+
let mut v1: Vec<usize> = vec![];
11+
v1.reserve(10);
12+
}
13+
14+
// Should lint
15+
fn capacity_as_expr() {
16+
let capacity = 10;
17+
let mut v2: Vec<usize> = vec![];
18+
v2.reserve(capacity);
19+
}
20+
21+
// Shouldn't lint
22+
fn vec_init_with_argument() {
23+
let mut v3 = vec![1];
24+
v3.reserve(10);
25+
}
26+
27+
// Shouldn't lint
28+
fn called_with_capacity() {
29+
let _v4: Vec<usize> = Vec::with_capacity(10);
30+
}
31+
32+
// Should lint
33+
fn assign_expression() {
34+
let mut v5: Vec<usize> = Vec::new();
35+
v5 = Vec::new();
36+
v5.reserve(10);
37+
}
38+
39+
fn in_macros() {
40+
external! {
41+
let mut v: Vec<usize> = vec![];
42+
v.reserve(10);
43+
}
44+
45+
with_span! {
46+
span
47+
48+
let mut v: Vec<usize> = vec![];
49+
v.reserve(10);
50+
}
51+
}

Diff for: tests/ui/reserve_after_initialization.stderr

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error: call to `reserve` immediately after creation
2+
--> $DIR/reserve_after_initialization.rs:10:5
3+
|
4+
LL | / let mut v1: Vec<usize> = vec![];
5+
LL | | v1.reserve(10);
6+
| |___________________^ help: consider using `Vec::with_capacity(/* Space hint */)`: `let mut v1: Vec<usize> = Vec::with_capacity(10);`
7+
|
8+
= note: `-D clippy::reserve-after-initialization` implied by `-D warnings`
9+
10+
error: call to `reserve` immediately after creation
11+
--> $DIR/reserve_after_initialization.rs:17:5
12+
|
13+
LL | / let mut v2: Vec<usize> = vec![];
14+
LL | | v2.reserve(capacity);
15+
| |_________________________^ help: consider using `Vec::with_capacity(/* Space hint */)`: `let mut v2: Vec<usize> = Vec::with_capacity(capacity);`
16+
17+
error: call to `reserve` immediately after creation
18+
--> $DIR/reserve_after_initialization.rs:35:5
19+
|
20+
LL | / v5 = Vec::new();
21+
LL | | v5.reserve(10);
22+
| |___________________^ help: consider using `Vec::with_capacity(/* Space hint */)`: `v5 = Vec::with_capacity(10);`
23+
24+
error: aborting due to 3 previous errors
25+

0 commit comments

Comments
 (0)