summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/methods/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/clippy/clippy_lints/src/methods/mod.rs')
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs45
1 files changed, 40 insertions, 5 deletions
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 702df4b28..64bf55ba2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -9,6 +9,7 @@ mod chars_last_cmp;
mod chars_last_cmp_with_unwrap;
mod chars_next_cmp;
mod chars_next_cmp_with_unwrap;
+mod clear_with_drain;
mod clone_on_copy;
mod clone_on_ref_ptr;
mod cloned_instead_of_copied;
@@ -110,7 +111,7 @@ use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_
use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty};
use if_chain::if_chain;
use rustc_hir as hir;
-use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
+use rustc_hir::{Expr, ExprKind, Node, Stmt, StmtKind, TraitItem, TraitItemKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
@@ -340,8 +341,9 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
- /// Checks for methods with certain name prefixes and which
- /// doesn't match how self is taken. The actual rules are:
+ /// Checks for methods with certain name prefixes or suffixes, and which
+ /// do not adhere to standard conventions regarding how `self` is taken.
+ /// The actual rules are:
///
/// |Prefix |Postfix |`self` taken | `self` type |
/// |-------|------------|-------------------------------|--------------|
@@ -3189,6 +3191,31 @@ declare_clippy_lint! {
"single command line argument that looks like it should be multiple arguments"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `.drain(..)` for the sole purpose of clearing a container.
+ ///
+ /// ### Why is this bad?
+ /// This creates an unnecessary iterator that is dropped immediately.
+ ///
+ /// Calling `.clear()` also makes the intent clearer.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let mut v = vec![1, 2, 3];
+ /// v.drain(..);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let mut v = vec![1, 2, 3];
+ /// v.clear();
+ /// ```
+ #[clippy::version = "1.69.0"]
+ pub CLEAR_WITH_DRAIN,
+ nursery,
+ "calling `drain` in order to `clear` a container"
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@@ -3317,6 +3344,7 @@ impl_lint_pass!(Methods => [
SEEK_TO_START_INSTEAD_OF_REWIND,
NEEDLESS_COLLECT,
SUSPICIOUS_COMMAND_ARG_SPACE,
+ CLEAR_WITH_DRAIN,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@@ -3561,8 +3589,15 @@ impl Methods {
Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
_ => {},
},
- ("drain", [arg]) => {
- iter_with_drain::check(cx, expr, recv, span, arg);
+ ("drain", ..) => {
+ if let Node::Stmt(Stmt { hir_id: _, kind, .. }) = cx.tcx.hir().get_parent(expr.hir_id)
+ && matches!(kind, StmtKind::Semi(_))
+ && args.len() <= 1
+ {
+ clear_with_drain::check(cx, expr, recv, span, args.first());
+ } else if let [arg] = args {
+ iter_with_drain::check(cx, expr, recv, span, arg);
+ }
},
("ends_with", [arg]) => {
if let ExprKind::MethodCall(.., span) = expr.kind {