mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-23 13:13:34 +00:00
Add BTreeSet
to set_contains_or_insert
* Detect `BTreeSet::contains` + `BTreeSet::insert` usage in the same way as with the `HashSet`.
This commit is contained in:
parent
6713631b0f
commit
9964b4e053
4 changed files with 162 additions and 32 deletions
|
@ -908,7 +908,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
||||||
store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf)));
|
store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf)));
|
||||||
store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf)));
|
store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf)));
|
||||||
store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
|
store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
|
||||||
store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains));
|
store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert));
|
||||||
store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
|
store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
|
||||||
store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
|
store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
|
||||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||||
|
|
|
@ -12,8 +12,8 @@ use rustc_span::{sym, Span};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
/// Checks for usage of `contains` to see if a value is not
|
/// Checks for usage of `contains` to see if a value is not present
|
||||||
/// present on `HashSet` followed by a `insert`.
|
/// in a set like `HashSet` or `BTreeSet`, followed by an `insert`.
|
||||||
///
|
///
|
||||||
/// ### Why is this bad?
|
/// ### Why is this bad?
|
||||||
/// Using just `insert` and checking the returned `bool` is more efficient.
|
/// Using just `insert` and checking the returned `bool` is more efficient.
|
||||||
|
@ -45,12 +45,12 @@ declare_clippy_lint! {
|
||||||
#[clippy::version = "1.80.0"]
|
#[clippy::version = "1.80.0"]
|
||||||
pub SET_CONTAINS_OR_INSERT,
|
pub SET_CONTAINS_OR_INSERT,
|
||||||
nursery,
|
nursery,
|
||||||
"call to `HashSet::contains` followed by `HashSet::insert`"
|
"call to `<set>::contains` followed by `<set>::insert`"
|
||||||
}
|
}
|
||||||
|
|
||||||
declare_lint_pass!(HashsetInsertAfterContains => [SET_CONTAINS_OR_INSERT]);
|
declare_lint_pass!(SetContainsOrInsert => [SET_CONTAINS_OR_INSERT]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains {
|
impl<'tcx> LateLintPass<'tcx> for SetContainsOrInsert {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
if !expr.span.from_expansion()
|
if !expr.span.from_expansion()
|
||||||
&& let Some(higher::If {
|
&& let Some(higher::If {
|
||||||
|
@ -58,14 +58,14 @@ impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains {
|
||||||
then: then_expr,
|
then: then_expr,
|
||||||
..
|
..
|
||||||
}) = higher::If::hir(expr)
|
}) = higher::If::hir(expr)
|
||||||
&& let Some(contains_expr) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr)
|
&& let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr)
|
||||||
&& let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr)
|
&& let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr)
|
||||||
{
|
{
|
||||||
span_lint(
|
span_lint(
|
||||||
cx,
|
cx,
|
||||||
SET_CONTAINS_OR_INSERT,
|
SET_CONTAINS_OR_INSERT,
|
||||||
vec![contains_expr.span, insert_expr.span],
|
vec![contains_expr.span, insert_expr.span],
|
||||||
"usage of `HashSet::insert` after `HashSet::contains`",
|
format!("usage of `{sym}::insert` after `{sym}::contains`"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,11 @@ struct OpExpr<'tcx> {
|
||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol: Symbol) -> Option<OpExpr<'tcx>> {
|
fn try_parse_op_call<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
expr: &'tcx Expr<'_>,
|
||||||
|
symbol: Symbol,
|
||||||
|
) -> Option<(OpExpr<'tcx>, Symbol)> {
|
||||||
let expr = peel_hir_expr_while(expr, |e| {
|
let expr = peel_hir_expr_while(expr, |e| {
|
||||||
if let ExprKind::Unary(UnOp::Not, e) = e.kind {
|
if let ExprKind::Unary(UnOp::Not, e) = e.kind {
|
||||||
Some(e)
|
Some(e)
|
||||||
|
@ -97,11 +101,12 @@ fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol:
|
||||||
});
|
});
|
||||||
let receiver = receiver.peel_borrows();
|
let receiver = receiver.peel_borrows();
|
||||||
let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
|
let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
|
||||||
if value.span.eq_ctxt(expr.span)
|
if value.span.eq_ctxt(expr.span) && path.ident.name == symbol {
|
||||||
&& is_type_diagnostic_item(cx, receiver_ty, sym::HashSet)
|
for sym in &[sym::HashSet, sym::BTreeSet] {
|
||||||
&& path.ident.name == symbol
|
if is_type_diagnostic_item(cx, receiver_ty, *sym) {
|
||||||
{
|
return Some((OpExpr { receiver, value, span }, *sym));
|
||||||
return Some(OpExpr { receiver, value, span });
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
|
@ -113,7 +118,7 @@ fn find_insert_calls<'tcx>(
|
||||||
expr: &'tcx Expr<'_>,
|
expr: &'tcx Expr<'_>,
|
||||||
) -> Option<OpExpr<'tcx>> {
|
) -> Option<OpExpr<'tcx>> {
|
||||||
for_each_expr(cx, expr, |e| {
|
for_each_expr(cx, expr, |e| {
|
||||||
if let Some(insert_expr) = try_parse_op_call(cx, e, sym!(insert))
|
if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym!(insert))
|
||||||
&& SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver)
|
&& SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver)
|
||||||
&& SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value)
|
&& SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,15 +3,9 @@
|
||||||
#![allow(clippy::needless_borrow)]
|
#![allow(clippy::needless_borrow)]
|
||||||
#![warn(clippy::set_contains_or_insert)]
|
#![warn(clippy::set_contains_or_insert)]
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::{BTreeSet, HashSet};
|
||||||
|
|
||||||
fn main() {
|
fn should_warn_hashset() {
|
||||||
should_warn_cases();
|
|
||||||
|
|
||||||
should_not_warn_cases();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_warn_cases() {
|
|
||||||
let mut set = HashSet::new();
|
let mut set = HashSet::new();
|
||||||
let value = 5;
|
let value = 5;
|
||||||
|
|
||||||
|
@ -49,7 +43,7 @@ fn should_warn_cases() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_not_warn_cases() {
|
fn should_not_warn_hashset() {
|
||||||
let mut set = HashSet::new();
|
let mut set = HashSet::new();
|
||||||
let value = 5;
|
let value = 5;
|
||||||
let another_value = 6;
|
let another_value = 6;
|
||||||
|
@ -78,6 +72,81 @@ fn should_not_warn_cases() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn should_warn_btreeset() {
|
||||||
|
let mut set = BTreeSet::new();
|
||||||
|
let value = 5;
|
||||||
|
|
||||||
|
if !set.contains(&value) {
|
||||||
|
set.insert(value);
|
||||||
|
println!("Just a comment");
|
||||||
|
}
|
||||||
|
|
||||||
|
if set.contains(&value) {
|
||||||
|
set.insert(value);
|
||||||
|
println!("Just a comment");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !set.contains(&value) {
|
||||||
|
set.insert(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !!set.contains(&value) {
|
||||||
|
set.insert(value);
|
||||||
|
println!("Just a comment");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (&set).contains(&value) {
|
||||||
|
set.insert(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let borrow_value = &6;
|
||||||
|
if !set.contains(borrow_value) {
|
||||||
|
set.insert(*borrow_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let borrow_set = &mut set;
|
||||||
|
if !borrow_set.contains(&value) {
|
||||||
|
borrow_set.insert(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_not_warn_btreeset() {
|
||||||
|
let mut set = BTreeSet::new();
|
||||||
|
let value = 5;
|
||||||
|
let another_value = 6;
|
||||||
|
|
||||||
|
if !set.contains(&value) {
|
||||||
|
set.insert(another_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !set.contains(&value) {
|
||||||
|
println!("Just a comment");
|
||||||
|
}
|
||||||
|
|
||||||
|
if simply_true() {
|
||||||
|
set.insert(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !set.contains(&value) {
|
||||||
|
set.replace(value); //it is not insert
|
||||||
|
println!("Just a comment");
|
||||||
|
}
|
||||||
|
|
||||||
|
if set.contains(&value) {
|
||||||
|
println!("value is already in set");
|
||||||
|
} else {
|
||||||
|
set.insert(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn simply_true() -> bool {
|
fn simply_true() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is placed last in order to be able to add new tests without changing line numbers
|
||||||
|
fn main() {
|
||||||
|
should_warn_hashset();
|
||||||
|
should_warn_btreeset();
|
||||||
|
should_not_warn_hashset();
|
||||||
|
should_not_warn_btreeset();
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error: usage of `HashSet::insert` after `HashSet::contains`
|
error: usage of `HashSet::insert` after `HashSet::contains`
|
||||||
--> tests/ui/set_contains_or_insert.rs:18:13
|
--> tests/ui/set_contains_or_insert.rs:12:13
|
||||||
|
|
|
|
||||||
LL | if !set.contains(&value) {
|
LL | if !set.contains(&value) {
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
@ -10,7 +10,7 @@ LL | set.insert(value);
|
||||||
= help: to override `-D warnings` add `#[allow(clippy::set_contains_or_insert)]`
|
= help: to override `-D warnings` add `#[allow(clippy::set_contains_or_insert)]`
|
||||||
|
|
||||||
error: usage of `HashSet::insert` after `HashSet::contains`
|
error: usage of `HashSet::insert` after `HashSet::contains`
|
||||||
--> tests/ui/set_contains_or_insert.rs:23:12
|
--> tests/ui/set_contains_or_insert.rs:17:12
|
||||||
|
|
|
|
||||||
LL | if set.contains(&value) {
|
LL | if set.contains(&value) {
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
@ -18,7 +18,7 @@ LL | set.insert(value);
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: usage of `HashSet::insert` after `HashSet::contains`
|
error: usage of `HashSet::insert` after `HashSet::contains`
|
||||||
--> tests/ui/set_contains_or_insert.rs:28:13
|
--> tests/ui/set_contains_or_insert.rs:22:13
|
||||||
|
|
|
|
||||||
LL | if !set.contains(&value) {
|
LL | if !set.contains(&value) {
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
@ -26,7 +26,7 @@ LL | set.insert(value);
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: usage of `HashSet::insert` after `HashSet::contains`
|
error: usage of `HashSet::insert` after `HashSet::contains`
|
||||||
--> tests/ui/set_contains_or_insert.rs:32:14
|
--> tests/ui/set_contains_or_insert.rs:26:14
|
||||||
|
|
|
|
||||||
LL | if !!set.contains(&value) {
|
LL | if !!set.contains(&value) {
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
@ -34,7 +34,7 @@ LL | set.insert(value);
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: usage of `HashSet::insert` after `HashSet::contains`
|
error: usage of `HashSet::insert` after `HashSet::contains`
|
||||||
--> tests/ui/set_contains_or_insert.rs:37:15
|
--> tests/ui/set_contains_or_insert.rs:31:15
|
||||||
|
|
|
|
||||||
LL | if (&set).contains(&value) {
|
LL | if (&set).contains(&value) {
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
@ -42,7 +42,7 @@ LL | set.insert(value);
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: usage of `HashSet::insert` after `HashSet::contains`
|
error: usage of `HashSet::insert` after `HashSet::contains`
|
||||||
--> tests/ui/set_contains_or_insert.rs:42:13
|
--> tests/ui/set_contains_or_insert.rs:36:13
|
||||||
|
|
|
|
||||||
LL | if !set.contains(borrow_value) {
|
LL | if !set.contains(borrow_value) {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -50,12 +50,68 @@ LL | set.insert(*borrow_value);
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: usage of `HashSet::insert` after `HashSet::contains`
|
error: usage of `HashSet::insert` after `HashSet::contains`
|
||||||
--> tests/ui/set_contains_or_insert.rs:47:20
|
--> tests/ui/set_contains_or_insert.rs:41:20
|
||||||
|
|
|
|
||||||
LL | if !borrow_set.contains(&value) {
|
LL | if !borrow_set.contains(&value) {
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
LL | borrow_set.insert(value);
|
LL | borrow_set.insert(value);
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to 7 previous errors
|
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
|
||||||
|
--> tests/ui/set_contains_or_insert.rs:79:13
|
||||||
|
|
|
||||||
|
LL | if !set.contains(&value) {
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
LL | set.insert(value);
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
|
||||||
|
--> tests/ui/set_contains_or_insert.rs:84:12
|
||||||
|
|
|
||||||
|
LL | if set.contains(&value) {
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
LL | set.insert(value);
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
|
||||||
|
--> tests/ui/set_contains_or_insert.rs:89:13
|
||||||
|
|
|
||||||
|
LL | if !set.contains(&value) {
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
LL | set.insert(value);
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
|
||||||
|
--> tests/ui/set_contains_or_insert.rs:93:14
|
||||||
|
|
|
||||||
|
LL | if !!set.contains(&value) {
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
LL | set.insert(value);
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
|
||||||
|
--> tests/ui/set_contains_or_insert.rs:98:15
|
||||||
|
|
|
||||||
|
LL | if (&set).contains(&value) {
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
LL | set.insert(value);
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
|
||||||
|
--> tests/ui/set_contains_or_insert.rs:103:13
|
||||||
|
|
|
||||||
|
LL | if !set.contains(borrow_value) {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
LL | set.insert(*borrow_value);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
|
||||||
|
--> tests/ui/set_contains_or_insert.rs:108:20
|
||||||
|
|
|
||||||
|
LL | if !borrow_set.contains(&value) {
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
LL | borrow_set.insert(value);
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 14 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue