-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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`]
- Loading branch information
Showing
7 changed files
with
262 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
use clippy_utils::diagnostics::span_lint_and_sugg; | ||
use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; | ||
use clippy_utils::source::snippet; | ||
use clippy_utils::{is_from_proc_macro, path_to_local_id}; | ||
use rustc_errors::Applicability; | ||
use rustc_hir::def::Res; | ||
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind}; | ||
use rustc_lint::{LateContext, LateLintPass, LintContext}; | ||
use rustc_middle::lint::in_external_macro; | ||
use rustc_session::{declare_tool_lint, impl_lint_pass}; | ||
use rustc_span::Span; | ||
|
||
declare_clippy_lint! { | ||
/// ### What it does | ||
/// Informs the user about a more concise way to create a vector with a known capacity. | ||
/// | ||
/// ### Why is this bad? | ||
/// The `Vec::with_capacity` constructor is less complex. | ||
/// | ||
/// ### Example | ||
/// ```rust | ||
/// let mut v: Vec<usize> = vec![]; | ||
/// v.reserve(10); | ||
/// ``` | ||
/// Use instead: | ||
/// ```rust | ||
/// let mut v: Vec<usize> = Vec::with_capacity(10); | ||
/// ``` | ||
#[clippy::version = "1.73.0"] | ||
pub RESERVE_AFTER_INITIALIZATION, | ||
complexity, | ||
"`reserve` called immediatly after `Vec` creation" | ||
} | ||
impl_lint_pass!(ReserveAfterInitialization => [RESERVE_AFTER_INITIALIZATION]); | ||
|
||
#[derive(Default)] | ||
pub struct ReserveAfterInitialization { | ||
searcher: Option<VecReserveSearcher>, | ||
} | ||
|
||
struct VecReserveSearcher { | ||
local_id: HirId, | ||
err_span: Span, | ||
init_part: String, | ||
space_hint: String, | ||
} | ||
impl VecReserveSearcher { | ||
fn display_err(&self, cx: &LateContext<'_>) { | ||
if self.space_hint.is_empty() { | ||
return; | ||
} | ||
|
||
let s = format!("{}Vec::with_capacity({});", self.init_part, self.space_hint); | ||
|
||
span_lint_and_sugg( | ||
cx, | ||
RESERVE_AFTER_INITIALIZATION, | ||
self.err_span, | ||
"call to `reserve` immediately after creation", | ||
"consider using `Vec::with_capacity(/* Space hint */)`", | ||
s, | ||
Applicability::HasPlaceholders, | ||
); | ||
} | ||
} | ||
|
||
impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization { | ||
fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { | ||
self.searcher = None; | ||
} | ||
|
||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { | ||
if let Some(init_expr) = local.init | ||
&& let PatKind::Binding(BindingAnnotation::MUT, id, _, None) = local.pat.kind | ||
&& !in_external_macro(cx.sess(), local.span) | ||
&& let Some(init) = get_vec_init_kind(cx, init_expr) | ||
&& !matches!(init, VecInitKind::WithExprCapacity(_) | VecInitKind::WithConstCapacity(_)) | ||
{ | ||
self.searcher = Some(VecReserveSearcher { | ||
local_id: id, | ||
err_span: local.span, | ||
init_part: snippet(cx, local.span.shrink_to_lo() | ||
.to(init_expr.span.source_callsite().shrink_to_lo()), "..") | ||
.into_owned(), | ||
space_hint: String::new() | ||
}); | ||
} | ||
} | ||
|
||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | ||
if self.searcher.is_none() | ||
&& let ExprKind::Assign(left, right, _) = expr.kind | ||
&& let ExprKind::Path(QPath::Resolved(None, path)) = left.kind | ||
&& let Res::Local(id) = path.res | ||
&& !in_external_macro(cx.sess(), expr.span) | ||
&& let Some(init) = get_vec_init_kind(cx, right) | ||
&& !matches!(init, VecInitKind::WithExprCapacity(_) | VecInitKind::WithConstCapacity(_)) | ||
{ | ||
self.searcher = Some(VecReserveSearcher { | ||
local_id: id, | ||
err_span: expr.span, | ||
init_part: snippet(cx, left.span.shrink_to_lo() | ||
.to(right.span.source_callsite().shrink_to_lo()), "..") | ||
.into_owned(), // see `assign_expression` test | ||
space_hint: String::new() | ||
}); | ||
} | ||
} | ||
|
||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { | ||
if let Some(searcher) = self.searcher.take() { | ||
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind | ||
&& let ExprKind::MethodCall(name, self_arg, [space_hint], _) = expr.kind | ||
&& path_to_local_id(self_arg, searcher.local_id) | ||
&& name.ident.as_str() == "reserve" | ||
&& !is_from_proc_macro(cx, expr) | ||
{ | ||
self.searcher = Some(VecReserveSearcher { | ||
err_span: searcher.err_span.to(stmt.span), | ||
space_hint: snippet(cx, space_hint.span, "..").into_owned(), | ||
.. searcher | ||
}); | ||
} else { | ||
searcher.display_err(cx); | ||
} | ||
} | ||
} | ||
|
||
fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { | ||
if let Some(searcher) = self.searcher.take() { | ||
searcher.display_err(cx); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
//@aux-build:proc_macros.rs | ||
#![warn(clippy::reserve_after_initialization)] | ||
#![no_main] | ||
|
||
extern crate proc_macros; | ||
use proc_macros::{external, with_span}; | ||
|
||
// Should lint | ||
fn standard() { | ||
let mut v1: Vec<usize> = Vec::with_capacity(10); | ||
} | ||
|
||
// Should lint | ||
fn capacity_as_expr() { | ||
let capacity = 10; | ||
let mut v2: Vec<usize> = Vec::with_capacity(capacity); | ||
} | ||
|
||
// Shouldn't lint | ||
fn vec_init_with_argument() { | ||
let mut v3 = vec![1]; | ||
v3.reserve(10); | ||
} | ||
|
||
// Shouldn't lint | ||
fn called_with_capacity() { | ||
let _v4: Vec<usize> = Vec::with_capacity(10); | ||
} | ||
|
||
// Should lint | ||
fn assign_expression() { | ||
let mut v5: Vec<usize> = Vec::new(); | ||
v5 = Vec::with_capacity(10); | ||
} | ||
|
||
fn in_macros() { | ||
external! { | ||
let mut v: Vec<usize> = vec![]; | ||
v.reserve(10); | ||
} | ||
|
||
with_span! { | ||
span | ||
|
||
let mut v: Vec<usize> = vec![]; | ||
v.reserve(10); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
//@aux-build:proc_macros.rs | ||
#![warn(clippy::reserve_after_initialization)] | ||
#![no_main] | ||
|
||
extern crate proc_macros; | ||
use proc_macros::{external, with_span}; | ||
|
||
// Should lint | ||
fn standard() { | ||
let mut v1: Vec<usize> = vec![]; | ||
v1.reserve(10); | ||
} | ||
|
||
// Should lint | ||
fn capacity_as_expr() { | ||
let capacity = 10; | ||
let mut v2: Vec<usize> = vec![]; | ||
v2.reserve(capacity); | ||
} | ||
|
||
// Shouldn't lint | ||
fn vec_init_with_argument() { | ||
let mut v3 = vec![1]; | ||
v3.reserve(10); | ||
} | ||
|
||
// Shouldn't lint | ||
fn called_with_capacity() { | ||
let _v4: Vec<usize> = Vec::with_capacity(10); | ||
} | ||
|
||
// Should lint | ||
fn assign_expression() { | ||
let mut v5: Vec<usize> = Vec::new(); | ||
v5 = Vec::new(); | ||
v5.reserve(10); | ||
} | ||
|
||
fn in_macros() { | ||
external! { | ||
let mut v: Vec<usize> = vec![]; | ||
v.reserve(10); | ||
} | ||
|
||
with_span! { | ||
span | ||
|
||
let mut v: Vec<usize> = vec![]; | ||
v.reserve(10); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
error: call to `reserve` immediately after creation | ||
--> $DIR/reserve_after_initialization.rs:10:5 | ||
| | ||
LL | / let mut v1: Vec<usize> = vec![]; | ||
LL | | v1.reserve(10); | ||
| |___________________^ help: consider using `Vec::with_capacity(/* Space hint */)`: `let mut v1: Vec<usize> = Vec::with_capacity(10);` | ||
| | ||
= note: `-D clippy::reserve-after-initialization` implied by `-D warnings` | ||
|
||
error: call to `reserve` immediately after creation | ||
--> $DIR/reserve_after_initialization.rs:17:5 | ||
| | ||
LL | / let mut v2: Vec<usize> = vec![]; | ||
LL | | v2.reserve(capacity); | ||
| |_________________________^ help: consider using `Vec::with_capacity(/* Space hint */)`: `let mut v2: Vec<usize> = Vec::with_capacity(capacity);` | ||
|
||
error: call to `reserve` immediately after creation | ||
--> $DIR/reserve_after_initialization.rs:35:5 | ||
| | ||
LL | / v5 = Vec::new(); | ||
LL | | v5.reserve(10); | ||
| |___________________^ help: consider using `Vec::with_capacity(/* Space hint */)`: `v5 = Vec::with_capacity(10);` | ||
|
||
error: aborting due to 3 previous errors | ||
|