Skip to content

Commit

Permalink
added range to extend check
Browse files Browse the repository at this point in the history
  • Loading branch information
Valentine-Mario committed May 27, 2021
1 parent 64341cc commit 8f6972e
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 39 deletions.
68 changes: 38 additions & 30 deletions clippy_lints/src/append_instead_of_extend.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
use rustc_session::{declare_lint_pass, declare_tool_lint};
// use rustc_ast::ast::*;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;

declare_clippy_lint! {
/// **What it does:**
/// **What it does:** finds occurrences of `vec.extend(other_vec.drain(..))
///
/// **Why is this bad?**
/// **Why is this bad?** Slightly more succinct code and faster code
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// // example code where clippy issues a warning
/// vec![].extend(vec![1, 2, 3].drain(..))
/// ```
/// Use instead:
/// ```rust
/// // example code which does not raise clippy warning
/// vec![].append(&mut vec![])
/// ```
pub APPEND_INSTEAD_OF_EXTEND,
perf,
Expand All @@ -32,30 +32,38 @@ declare_lint_pass!(AppendInsteadOfExtend => [APPEND_INSTEAD_OF_EXTEND]);

impl<'tcx> LateLintPass<'tcx> for AppendInsteadOfExtend {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if is_vec_an_append(cx, expr) {
span_lint_and_help(
cx,
APPEND_INSTEAD_OF_EXTEND,
expr.span,
"use of `vec.extend(other_vec.drain(..))`",
None,
"consider using `vec![].append(&mut vec![]);` instead",
);
}
}
}
//validate target object
if let ExprKind::MethodCall(method_name, _, exprs, _) = expr.kind {
//check source object
if let ExprKind::MethodCall(src_method, _, src_args, _) = &exprs[1].kind {
//check drain range
let ty = cx.typeck_results().expr_ty(&src_args[1]);
let ty = ty.peel_refs();
let range_check = is_type_lang_item(cx, ty, LangItem::RangeFull);

//check types
let ty = cx.typeck_results().expr_ty(&exprs[0]).peel_refs();
let vector_1 = is_type_diagnostic_item(cx, ty, sym::vec_type);

let src_ty = cx.typeck_results().expr_ty(&src_args[0]).peel_refs();
let vector_2 = is_type_diagnostic_item(cx, src_ty, sym::vec_type);

fn is_vec_an_append<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
if_chain! {
if let ExprKind::MethodCall(method_name, _, exprs, _) = expr.kind;
if method_name.ident.as_str() == "extend";
let ty = cx.typeck_results().expr_ty(&exprs[0]).peel_refs();
if is_type_diagnostic_item(cx, ty, sym::vec_type);
if let ExprKind::MethodCall(method_name2, _, _exprs2, _) = &exprs[1].kind;
if method_name2.ident.as_str() == "drain";
then {
return true
if method_name.ident.as_str() == "extend"
&& src_method.ident.as_str() == "drain"
&& range_check
&& vector_1
&& vector_2
{
span_lint_and_help(
cx,
APPEND_INSTEAD_OF_EXTEND,
expr.span,
"use of `vec.extend(other_vec.drain(..))`",
None,
"consider using `vec![].append(&mut vec![]);` instead",
);
}
}
}
}
false
}
27 changes: 21 additions & 6 deletions tests/ui/append_instead_of_extend.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
#![warn(clippy::append_instead_of_extend)]

fn main() {
let mut a = vec![0u8; 1024];
let mut b = Vec::new();
let mut vec1 = vec![0u8; 1024];
let mut vec2: std::vec::Vec<u8> = Vec::new();

b.extend(a.drain(..));
vec2.extend(vec1.drain(..));

let mut c = vec![0u8, 10];
let mut d: std::vec::Vec<u8> = Vec::new();
let mut test1 = vec![0u8, 10];
let mut test2: std::vec::Vec<u8> = Vec::new();

c.extend(d.drain(1..3))
test2.extend(test1.drain(4..10));

let mut vec3 = vec![0u8; 104];
let mut vec7: std::vec::Vec<u8> = Vec::new();

vec3.append(&mut vec7);

let mut vec3 = vec![0u8; 1024];
let mut vec4: std::vec::Vec<u8> = Vec::new();

vec4.extend(vec3.drain(..));

let mut vec5 = vec![0u8; 1024];
let mut vec6: std::vec::Vec<u8> = Vec::new();

vec5.extend(vec6.drain(..4));
}
14 changes: 11 additions & 3 deletions tests/ui/append_instead_of_extend.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
error: use of `vec.extend(other_vec.drain(..))`
--> $DIR/append_instead_of_extend.rs:7:5
|
LL | b.extend(a.drain(..));
| ^^^^^^^^^^^^^^^^^^^^^
LL | vec2.extend(vec1.drain(..));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::append-instead-of-extend` implied by `-D warnings`
= help: consider using `vec![].append(&mut vec![]);` instead

error: aborting due to previous error
error: use of `vec.extend(other_vec.drain(..))`
--> $DIR/append_instead_of_extend.rs:22:5
|
LL | vec4.extend(vec3.drain(..));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using `vec![].append(&mut vec![]);` instead

error: aborting due to 2 previous errors

0 comments on commit 8f6972e

Please sign in to comment.