From 541d0c8ab7e84689d2b54419e4b9c2748d83220b Mon Sep 17 00:00:00 2001
From: y21 <30553356+y21@users.noreply.github.com>
Date: Thu, 20 Jul 2023 21:39:32 +0200
Subject: [PATCH 1/3] [`slow_vector_initialization`]: lint `Vec::new()`
---
.../src/slow_vector_initialization.rs | 96 ++++++++++++++-----
tests/ui/read_zero_byte_vec.rs | 6 +-
tests/ui/read_zero_byte_vec.stderr | 20 ++--
tests/ui/slow_vector_initialization.rs | 16 ++++
tests/ui/slow_vector_initialization.stderr | 64 +++++++++----
5 files changed, 145 insertions(+), 57 deletions(-)
diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs
index 858135c8d464..b496185cc726 100644
--- a/clippy_lints/src/slow_vector_initialization.rs
+++ b/clippy_lints/src/slow_vector_initialization.rs
@@ -2,7 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
- get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, SpanlessEq,
+ get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local,
+ path_to_local_id, paths, SpanlessEq,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
@@ -60,7 +61,11 @@ struct VecAllocation<'tcx> {
/// Reference to the expression used as argument on `with_capacity` call. This is used
/// to only match slow zero-filling idioms of the same length than vector initialization.
- len_expr: &'tcx Expr<'tcx>,
+ ///
+ /// Initially set to `None` if initialized with `Vec::new()`, but will always be `Some(_)` by
+ /// the time the visitor has looked through the enclosing block and found a slow
+ /// initialization, so it is safe to unwrap later at lint time.
+ size_expr: Option<&'tcx Expr<'tcx>>,
}
/// Type of slow initialization
@@ -77,18 +82,14 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
// Matches initialization on reassignments. For example: `vec = Vec::with_capacity(100)`
if_chain! {
if let ExprKind::Assign(left, right, _) = expr.kind;
-
- // Extract variable
if let Some(local_id) = path_to_local(left);
-
- // Extract len argument
- if let Some(len_arg) = Self::is_vec_with_capacity(cx, right);
+ if let Some(size_expr) = Self::as_vec_initializer(cx, right);
then {
let vi = VecAllocation {
local_id,
allocation_expr: right,
- len_expr: len_arg,
+ size_expr,
};
Self::search_initialization(cx, vi, expr.hir_id);
@@ -98,17 +99,18 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
// Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
+ // or `Vec::new()`
if_chain! {
if let StmtKind::Local(local) = stmt.kind;
if let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind;
if let Some(init) = local.init;
- if let Some(len_arg) = Self::is_vec_with_capacity(cx, init);
+ if let Some(size_expr) = Self::as_vec_initializer(cx, init);
then {
let vi = VecAllocation {
local_id,
allocation_expr: init,
- len_expr: len_arg,
+ size_expr,
};
Self::search_initialization(cx, vi, stmt.hir_id);
@@ -118,6 +120,25 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
}
impl SlowVectorInit {
+ /// Given an expression, returns:
+ /// - `Some(Some(size))` if it is a function call to `Vec::with_capacity(size)`
+ /// - `Some(None)` if it is a call to `Vec::new()`
+ /// - `None` if it is neither
+ #[allow(
+ clippy::option_option,
+ reason = "outer option is immediately removed at call site and the inner option is kept around, \
+ so extracting into a dedicated enum seems unnecessarily complicated"
+ )]
+ fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option>> {
+ if let Some(len_expr) = Self::is_vec_with_capacity(cx, expr) {
+ Some(Some(len_expr))
+ } else if Self::is_vec_new(cx, expr) {
+ Some(None)
+ } else {
+ None
+ }
+ }
+
/// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
/// of the first argument of `with_capacity` call if it matches or `None` if it does not.
fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
@@ -134,6 +155,14 @@ impl SlowVectorInit {
}
}
+ /// Checks if the given expression is `Vec::new()`
+ fn is_vec_new(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ matches!(
+ expr.kind,
+ ExprKind::Call(func, _) if is_expr_path_def_path(cx, func, &paths::VEC_NEW)
+ )
+ }
+
/// Search initialization for the given vector
fn search_initialization<'tcx>(cx: &LateContext<'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) {
let enclosing_body = get_enclosing_block(cx, parent_node);
@@ -169,12 +198,18 @@ impl SlowVectorInit {
}
fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) {
- let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len");
+ let len_expr = Sugg::hir(
+ cx,
+ vec_alloc
+ .size_expr
+ .expect("length expression must be set by this point"),
+ "len",
+ );
span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| {
diag.span_suggestion(
vec_alloc.allocation_expr.span,
- "consider replace allocation with",
+ "consider replacing this with",
format!("vec![0; {len_expr}]"),
Applicability::Unspecified,
);
@@ -214,36 +249,45 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
}
/// Checks if the given expression is resizing a vector with 0
- fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
+ fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'tcx>) {
if self.initialization_found
&& let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind
&& path_to_local_id(self_arg, self.vec_alloc.local_id)
&& path.ident.name == sym!(resize)
// Check that is filled with 0
- && is_integer_literal(fill_arg, 0) {
- // Check that len expression is equals to `with_capacity` expression
- if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
- self.slow_expression = Some(InitializationType::Resize(expr));
- } else if let ExprKind::MethodCall(path, ..) = len_arg.kind && path.ident.as_str() == "capacity" {
- self.slow_expression = Some(InitializationType::Resize(expr));
- }
+ && is_integer_literal(fill_arg, 0)
+ {
+ let is_matching_resize = if let Some(size_expr) = self.vec_alloc.size_expr {
+ // If we have a size expression, check that it is equal to what's passed to `resize`
+ SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr)
+ || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity")
+ } else {
+ self.vec_alloc.size_expr = Some(len_arg);
+ true
+ };
+
+ if is_matching_resize {
+ self.slow_expression = Some(InitializationType::Resize(expr));
}
+ }
}
/// Returns `true` if give expression is `repeat(0).take(...)`
- fn is_repeat_take(&self, expr: &Expr<'_>) -> bool {
+ fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool {
if_chain! {
if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind;
if take_path.ident.name == sym!(take);
// Check that take is applied to `repeat(0)`
if self.is_repeat_zero(recv);
then {
- // Check that len expression is equals to `with_capacity` expression
- if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
- return true;
- } else if let ExprKind::MethodCall(path, ..) = len_arg.kind && path.ident.as_str() == "capacity" {
- return true;
+ if let Some(size_expr) = self.vec_alloc.size_expr {
+ // Check that len expression is equals to `with_capacity` expression
+ return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr)
+ || matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity")
}
+
+ self.vec_alloc.size_expr = Some(len_arg);
+ return true;
}
}
diff --git a/tests/ui/read_zero_byte_vec.rs b/tests/ui/read_zero_byte_vec.rs
index c6025ef1f4da..ff2ad8644b49 100644
--- a/tests/ui/read_zero_byte_vec.rs
+++ b/tests/ui/read_zero_byte_vec.rs
@@ -1,5 +1,9 @@
#![warn(clippy::read_zero_byte_vec)]
-#![allow(clippy::unused_io_amount, clippy::needless_pass_by_ref_mut)]
+#![allow(
+ clippy::unused_io_amount,
+ clippy::needless_pass_by_ref_mut,
+ clippy::slow_vector_initialization
+)]
use std::fs::File;
use std::io;
use std::io::prelude::*;
diff --git a/tests/ui/read_zero_byte_vec.stderr b/tests/ui/read_zero_byte_vec.stderr
index 08ba9753d7c4..4c7f605f4c2a 100644
--- a/tests/ui/read_zero_byte_vec.stderr
+++ b/tests/ui/read_zero_byte_vec.stderr
@@ -1,5 +1,5 @@
error: reading zero byte data to `Vec`
- --> $DIR/read_zero_byte_vec.rs:17:5
+ --> $DIR/read_zero_byte_vec.rs:21:5
|
LL | f.read_exact(&mut data).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data).unwrap();`
@@ -7,55 +7,55 @@ LL | f.read_exact(&mut data).unwrap();
= note: `-D clippy::read-zero-byte-vec` implied by `-D warnings`
error: reading zero byte data to `Vec`
- --> $DIR/read_zero_byte_vec.rs:21:5
+ --> $DIR/read_zero_byte_vec.rs:25:5
|
LL | f.read_exact(&mut data2)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)?;`
error: reading zero byte data to `Vec`
- --> $DIR/read_zero_byte_vec.rs:25:5
+ --> $DIR/read_zero_byte_vec.rs:29:5
|
LL | f.read_exact(&mut data3)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: reading zero byte data to `Vec`
- --> $DIR/read_zero_byte_vec.rs:29:5
+ --> $DIR/read_zero_byte_vec.rs:33:5
|
LL | let _ = f.read(&mut data4)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: reading zero byte data to `Vec`
- --> $DIR/read_zero_byte_vec.rs:34:9
+ --> $DIR/read_zero_byte_vec.rs:38:9
|
LL | f.read(&mut data5)
| ^^^^^^^^^^^^^^^^^^
error: reading zero byte data to `Vec`
- --> $DIR/read_zero_byte_vec.rs:40:9
+ --> $DIR/read_zero_byte_vec.rs:44:9
|
LL | f.read(&mut data6)
| ^^^^^^^^^^^^^^^^^^
error: reading zero byte data to `Vec`
- --> $DIR/read_zero_byte_vec.rs:70:5
+ --> $DIR/read_zero_byte_vec.rs:74:5
|
LL | r.read(&mut data).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: reading zero byte data to `Vec`
- --> $DIR/read_zero_byte_vec.rs:74:5
+ --> $DIR/read_zero_byte_vec.rs:78:5
|
LL | r.read_exact(&mut data2).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: reading zero byte data to `Vec`
- --> $DIR/read_zero_byte_vec.rs:80:5
+ --> $DIR/read_zero_byte_vec.rs:84:5
|
LL | r.read(&mut data).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: reading zero byte data to `Vec`
- --> $DIR/read_zero_byte_vec.rs:84:5
+ --> $DIR/read_zero_byte_vec.rs:88:5
|
LL | r.read_exact(&mut data2).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs
index 16be9f6d203a..cfb856861b8c 100644
--- a/tests/ui/slow_vector_initialization.rs
+++ b/tests/ui/slow_vector_initialization.rs
@@ -4,6 +4,7 @@ fn main() {
resize_vector();
extend_vector();
mixed_extend_resize_vector();
+ from_empty_vec();
}
fn extend_vector() {
@@ -59,6 +60,21 @@ fn resize_vector() {
vec1.resize(10, 0);
}
+fn from_empty_vec() {
+ // Resize with constant expression
+ let len = 300;
+ let mut vec1 = Vec::new();
+ vec1.resize(len, 0);
+
+ // Resize with len expression
+ let mut vec3 = Vec::new();
+ vec3.resize(len - 10, 0);
+
+ // Reinitialization should be warned
+ vec1 = Vec::new();
+ vec1.resize(10, 0);
+}
+
fn do_stuff(vec: &mut [u8]) {}
fn extend_vector_with_manipulations_between() {
diff --git a/tests/ui/slow_vector_initialization.stderr b/tests/ui/slow_vector_initialization.stderr
index 22376680a8e6..532ce4ac1911 100644
--- a/tests/ui/slow_vector_initialization.stderr
+++ b/tests/ui/slow_vector_initialization.stderr
@@ -1,84 +1,108 @@
error: slow zero-filling initialization
- --> $DIR/slow_vector_initialization.rs:13:5
+ --> $DIR/slow_vector_initialization.rs:14:5
|
LL | let mut vec1 = Vec::with_capacity(len);
- | ----------------------- help: consider replace allocation with: `vec![0; len]`
+ | ----------------------- help: consider replacing this with: `vec![0; len]`
LL | vec1.extend(repeat(0).take(len));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::slow-vector-initialization` implied by `-D warnings`
error: slow zero-filling initialization
- --> $DIR/slow_vector_initialization.rs:17:5
+ --> $DIR/slow_vector_initialization.rs:18:5
|
LL | let mut vec2 = Vec::with_capacity(len - 10);
- | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]`
+ | ---------------------------- help: consider replacing this with: `vec![0; len - 10]`
LL | vec2.extend(repeat(0).take(len - 10));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: slow zero-filling initialization
- --> $DIR/slow_vector_initialization.rs:24:5
+ --> $DIR/slow_vector_initialization.rs:25:5
|
LL | let mut vec4 = Vec::with_capacity(len);
- | ----------------------- help: consider replace allocation with: `vec![0; len]`
+ | ----------------------- help: consider replacing this with: `vec![0; len]`
LL | vec4.extend(repeat(0).take(vec4.capacity()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: slow zero-filling initialization
- --> $DIR/slow_vector_initialization.rs:34:5
+ --> $DIR/slow_vector_initialization.rs:35:5
|
LL | let mut resized_vec = Vec::with_capacity(30);
- | ---------------------- help: consider replace allocation with: `vec![0; 30]`
+ | ---------------------- help: consider replacing this with: `vec![0; 30]`
LL | resized_vec.resize(30, 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: slow zero-filling initialization
- --> $DIR/slow_vector_initialization.rs:37:5
+ --> $DIR/slow_vector_initialization.rs:38:5
|
LL | let mut extend_vec = Vec::with_capacity(30);
- | ---------------------- help: consider replace allocation with: `vec![0; 30]`
+ | ---------------------- help: consider replacing this with: `vec![0; 30]`
LL | extend_vec.extend(repeat(0).take(30));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: slow zero-filling initialization
- --> $DIR/slow_vector_initialization.rs:44:5
+ --> $DIR/slow_vector_initialization.rs:45:5
|
LL | let mut vec1 = Vec::with_capacity(len);
- | ----------------------- help: consider replace allocation with: `vec![0; len]`
+ | ----------------------- help: consider replacing this with: `vec![0; len]`
LL | vec1.resize(len, 0);
| ^^^^^^^^^^^^^^^^^^^
error: slow zero-filling initialization
- --> $DIR/slow_vector_initialization.rs:52:5
+ --> $DIR/slow_vector_initialization.rs:53:5
|
LL | let mut vec3 = Vec::with_capacity(len - 10);
- | ---------------------------- help: consider replace allocation with: `vec![0; len - 10]`
+ | ---------------------------- help: consider replacing this with: `vec![0; len - 10]`
LL | vec3.resize(len - 10, 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^
error: slow zero-filling initialization
- --> $DIR/slow_vector_initialization.rs:55:5
+ --> $DIR/slow_vector_initialization.rs:56:5
|
LL | let mut vec4 = Vec::with_capacity(len);
- | ----------------------- help: consider replace allocation with: `vec![0; len]`
+ | ----------------------- help: consider replacing this with: `vec![0; len]`
LL | vec4.resize(vec4.capacity(), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: slow zero-filling initialization
- --> $DIR/slow_vector_initialization.rs:59:5
+ --> $DIR/slow_vector_initialization.rs:60:5
|
LL | vec1 = Vec::with_capacity(10);
- | ---------------------- help: consider replace allocation with: `vec![0; 10]`
+ | ---------------------- help: consider replacing this with: `vec![0; 10]`
+LL | vec1.resize(10, 0);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:67:5
+ |
+LL | let mut vec1 = Vec::new();
+ | ---------- help: consider replacing this with: `vec![0; len]`
+LL | vec1.resize(len, 0);
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:71:5
+ |
+LL | let mut vec3 = Vec::new();
+ | ---------- help: consider replacing this with: `vec![0; len - 10]`
+LL | vec3.resize(len - 10, 0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+ --> $DIR/slow_vector_initialization.rs:75:5
+ |
+LL | vec1 = Vec::new();
+ | ---------- help: consider replacing this with: `vec![0; 10]`
LL | vec1.resize(10, 0);
| ^^^^^^^^^^^^^^^^^^
error: this argument is a mutable reference, but not used mutably
- --> $DIR/slow_vector_initialization.rs:62:18
+ --> $DIR/slow_vector_initialization.rs:78:18
|
LL | fn do_stuff(vec: &mut [u8]) {}
| ^^^^^^^^^ help: consider changing to: `&[u8]`
|
= note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
-error: aborting due to 10 previous errors
+error: aborting due to 13 previous errors
From 1fe27629005f15d51bfa770ab4ba330b47e7d9ed Mon Sep 17 00:00:00 2001
From: y21 <30553356+y21@users.noreply.github.com>
Date: Tue, 25 Jul 2023 18:21:00 +0200
Subject: [PATCH 2/3] use a dedicated enum for vec initializer
---
.../src/slow_vector_initialization.rs | 63 +++++++++----------
1 file changed, 30 insertions(+), 33 deletions(-)
diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs
index b496185cc726..01da63d2b006 100644
--- a/clippy_lints/src/slow_vector_initialization.rs
+++ b/clippy_lints/src/slow_vector_initialization.rs
@@ -61,11 +61,24 @@ struct VecAllocation<'tcx> {
/// Reference to the expression used as argument on `with_capacity` call. This is used
/// to only match slow zero-filling idioms of the same length than vector initialization.
- ///
- /// Initially set to `None` if initialized with `Vec::new()`, but will always be `Some(_)` by
- /// the time the visitor has looked through the enclosing block and found a slow
- /// initialization, so it is safe to unwrap later at lint time.
- size_expr: Option<&'tcx Expr<'tcx>>,
+ size_expr: InitializedSize<'tcx>,
+}
+
+/// Initializer for the creation of the vector.
+///
+/// When `Vec::with_capacity(size)` is found, the `size` expression will be in
+/// `InitializedSize::Initialized`.
+///
+/// Otherwise, for `Vec::new()` calls, there is no allocation initializer yet, so
+/// `InitializedSize::Uninitialized` is used.
+/// Later, when a call to `.resize(size, 0)` or similar is found, it's set
+/// to `InitializedSize::Initialized(size)`.
+///
+/// Since it will be set to `InitializedSize::Initialized(size)` when a slow initialization is
+/// found, it is always safe to "unwrap" it at lint time.
+enum InitializedSize<'tcx> {
+ Initialized(&'tcx Expr<'tcx>),
+ Uninitialized,
}
/// Type of slow initialization
@@ -120,20 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
}
impl SlowVectorInit {
- /// Given an expression, returns:
- /// - `Some(Some(size))` if it is a function call to `Vec::with_capacity(size)`
- /// - `Some(None)` if it is a call to `Vec::new()`
- /// - `None` if it is neither
- #[allow(
- clippy::option_option,
- reason = "outer option is immediately removed at call site and the inner option is kept around, \
- so extracting into a dedicated enum seems unnecessarily complicated"
- )]
- fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option >> {
+ fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option> {
if let Some(len_expr) = Self::is_vec_with_capacity(cx, expr) {
- Some(Some(len_expr))
- } else if Self::is_vec_new(cx, expr) {
- Some(None)
+ Some(InitializedSize::Initialized(len_expr))
+ } else if matches!(expr.kind, ExprKind::Call(func, _) if is_expr_path_def_path(cx, func, &paths::VEC_NEW)) {
+ Some(InitializedSize::Uninitialized)
} else {
None
}
@@ -155,14 +159,6 @@ impl SlowVectorInit {
}
}
- /// Checks if the given expression is `Vec::new()`
- fn is_vec_new(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- matches!(
- expr.kind,
- ExprKind::Call(func, _) if is_expr_path_def_path(cx, func, &paths::VEC_NEW)
- )
- }
-
/// Search initialization for the given vector
fn search_initialization<'tcx>(cx: &LateContext<'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) {
let enclosing_body = get_enclosing_block(cx, parent_node);
@@ -200,9 +196,10 @@ impl SlowVectorInit {
fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) {
let len_expr = Sugg::hir(
cx,
- vec_alloc
- .size_expr
- .expect("length expression must be set by this point"),
+ match vec_alloc.size_expr {
+ InitializedSize::Initialized(expr) => expr,
+ InitializedSize::Uninitialized => unreachable!("size expression must be set by this point"),
+ },
"len",
);
@@ -257,12 +254,12 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
// Check that is filled with 0
&& is_integer_literal(fill_arg, 0)
{
- let is_matching_resize = if let Some(size_expr) = self.vec_alloc.size_expr {
+ let is_matching_resize = if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr {
// If we have a size expression, check that it is equal to what's passed to `resize`
SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr)
|| matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity")
} else {
- self.vec_alloc.size_expr = Some(len_arg);
+ self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg);
true
};
@@ -280,13 +277,13 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
// Check that take is applied to `repeat(0)`
if self.is_repeat_zero(recv);
then {
- if let Some(size_expr) = self.vec_alloc.size_expr {
+ if let InitializedSize::Initialized(size_expr) = self.vec_alloc.size_expr {
// Check that len expression is equals to `with_capacity` expression
return SpanlessEq::new(self.cx).eq_expr(len_arg, size_expr)
|| matches!(len_arg.kind, ExprKind::MethodCall(path, ..) if path.ident.as_str() == "capacity")
}
- self.vec_alloc.size_expr = Some(len_arg);
+ self.vec_alloc.size_expr = InitializedSize::Initialized(len_arg);
return true;
}
}
From c0484b74f79d2e648083a6c52a697aae66202707 Mon Sep 17 00:00:00 2001
From: y21 <30553356+y21@users.noreply.github.com>
Date: Tue, 25 Jul 2023 18:56:57 +0200
Subject: [PATCH 3/3] simplify looking for `Vec::with_capacity` exprs
---
.../src/slow_vector_initialization.rs | 28 ++++++-------------
clippy_utils/src/paths.rs | 1 +
2 files changed, 10 insertions(+), 19 deletions(-)
diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs
index 01da63d2b006..54a33eb2986d 100644
--- a/clippy_lints/src/slow_vector_initialization.rs
+++ b/clippy_lints/src/slow_vector_initialization.rs
@@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local,
path_to_local_id, paths, SpanlessEq,
@@ -8,7 +7,7 @@ use clippy_utils::{
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor};
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind};
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
@@ -133,8 +132,15 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
}
impl SlowVectorInit {
+ /// Looks for `Vec::with_capacity(size)` or `Vec::new()` calls and returns the initialized size,
+ /// if any. More specifically, it returns:
+ /// - `Some(InitializedSize::Initialized(size))` for `Vec::with_capacity(size)`
+ /// - `Some(InitializedSize::Uninitialized)` for `Vec::new()`
+ /// - `None` for other, unrelated kinds of expressions
fn as_vec_initializer<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option> {
- if let Some(len_expr) = Self::is_vec_with_capacity(cx, expr) {
+ if let ExprKind::Call(func, [len_expr]) = expr.kind
+ && is_expr_path_def_path(cx, func, &paths::VEC_WITH_CAPACITY)
+ {
Some(InitializedSize::Initialized(len_expr))
} else if matches!(expr.kind, ExprKind::Call(func, _) if is_expr_path_def_path(cx, func, &paths::VEC_NEW)) {
Some(InitializedSize::Uninitialized)
@@ -143,22 +149,6 @@ impl SlowVectorInit {
}
}
- /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
- /// of the first argument of `with_capacity` call if it matches or `None` if it does not.
- fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
- if_chain! {
- if let ExprKind::Call(func, [arg]) = expr.kind;
- if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind;
- if name.ident.as_str() == "with_capacity";
- if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec);
- then {
- Some(arg)
- } else {
- None
- }
- }
- }
-
/// Search initialization for the given vector
fn search_initialization<'tcx>(cx: &LateContext<'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) {
let enclosing_body = get_enclosing_block(cx, parent_node);
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index f3677e6f6141..ad3e1ce8108b 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -149,6 +149,7 @@ pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"];
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
+pub const VEC_WITH_CAPACITY: [&str; 4] = ["alloc", "vec", "Vec", "with_capacity"];
pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];