diff --git a/clippy_lints/src/append_instead_of_extend.rs b/clippy_lints/src/append_instead_of_extend.rs index bdf804a4fec5..070d61e3ebd3 100644 --- a/clippy_lints/src/append_instead_of_extend.rs +++ b/clippy_lints/src/append_instead_of_extend.rs @@ -1,16 +1,14 @@ -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. /// @@ -18,10 +16,12 @@ declare_clippy_lint! { /// /// ```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, @@ -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 } diff --git a/tests/ui/append_instead_of_extend.rs b/tests/ui/append_instead_of_extend.rs index 2831f1adfeda..c4e79febbbef 100644 --- a/tests/ui/append_instead_of_extend.rs +++ b/tests/ui/append_instead_of_extend.rs @@ -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 = Vec::new(); - b.extend(a.drain(..)); + vec2.extend(vec1.drain(..)); - let mut c = vec![0u8, 10]; - let mut d: std::vec::Vec = Vec::new(); + let mut test1 = vec![0u8, 10]; + let mut test2: std::vec::Vec = 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 = Vec::new(); + + vec3.append(&mut vec7); + + let mut vec3 = vec![0u8; 1024]; + let mut vec4: std::vec::Vec = Vec::new(); + + vec4.extend(vec3.drain(..)); + + let mut vec5 = vec![0u8; 1024]; + let mut vec6: std::vec::Vec = Vec::new(); + + vec5.extend(vec6.drain(..4)); } diff --git a/tests/ui/append_instead_of_extend.stderr b/tests/ui/append_instead_of_extend.stderr index 9772c223e35d..d857efdf4b8b 100644 --- a/tests/ui/append_instead_of_extend.stderr +++ b/tests/ui/append_instead_of_extend.stderr @@ -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