Skip to content

Commit 92768ee

Browse files
committed
Auto merge of #13053 - nyurik:rename-set_contains_or_insert, r=llogiq
Add `BTreeSet` detection to the `set_contains_or_insert` lint * Detect `BTreeSet::contains` + `BTreeSet::insert` usage in the same way as with the `HashSet`. CC: `@lochetti` `@bitfield` ---- changelog: [`set_contains_or_insert`]: Handle `BTreeSet` in addition to `HashSet`
2 parents 1ec5025 + 9964b4e commit 92768ee

File tree

4 files changed

+160
-30
lines changed

4 files changed

+160
-30
lines changed

clippy_lints/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
908908
store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf)));
909909
store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf)));
910910
store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
911-
store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains));
911+
store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert));
912912
store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
913913
store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
914914
// add lints here, do not remove this comment, it's used in `new_lint`

clippy_lints/src/set_contains_or_insert.rs

+19-14
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use rustc_span::{sym, Span};
1212

1313
declare_clippy_lint! {
1414
/// ### What it does
15-
/// Checks for usage of `contains` to see if a value is not
16-
/// present on `HashSet` followed by a `insert`.
15+
/// Checks for usage of `contains` to see if a value is not present
16+
/// in a set like `HashSet` or `BTreeSet`, followed by an `insert`.
1717
///
1818
/// ### Why is this bad?
1919
/// Using just `insert` and checking the returned `bool` is more efficient.
@@ -45,27 +45,27 @@ declare_clippy_lint! {
4545
#[clippy::version = "1.80.0"]
4646
pub SET_CONTAINS_OR_INSERT,
4747
nursery,
48-
"call to `HashSet::contains` followed by `HashSet::insert`"
48+
"call to `<set>::contains` followed by `<set>::insert`"
4949
}
5050

51-
declare_lint_pass!(HashsetInsertAfterContains => [SET_CONTAINS_OR_INSERT]);
51+
declare_lint_pass!(SetContainsOrInsert => [SET_CONTAINS_OR_INSERT]);
5252

53-
impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains {
53+
impl<'tcx> LateLintPass<'tcx> for SetContainsOrInsert {
5454
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
5555
if !expr.span.from_expansion()
5656
&& let Some(higher::If {
5757
cond: cond_expr,
5858
then: then_expr,
5959
..
6060
}) = higher::If::hir(expr)
61-
&& let Some(contains_expr) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr)
61+
&& let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr)
6262
&& let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr)
6363
{
6464
span_lint(
6565
cx,
6666
SET_CONTAINS_OR_INSERT,
6767
vec![contains_expr.span, insert_expr.span],
68-
"usage of `HashSet::insert` after `HashSet::contains`",
68+
format!("usage of `{sym}::insert` after `{sym}::contains`"),
6969
);
7070
}
7171
}
@@ -77,7 +77,11 @@ struct OpExpr<'tcx> {
7777
span: Span,
7878
}
7979

80-
fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol: Symbol) -> Option<OpExpr<'tcx>> {
80+
fn try_parse_op_call<'tcx>(
81+
cx: &LateContext<'tcx>,
82+
expr: &'tcx Expr<'_>,
83+
symbol: Symbol,
84+
) -> Option<(OpExpr<'tcx>, Symbol)> {
8185
let expr = peel_hir_expr_while(expr, |e| {
8286
if let ExprKind::Unary(UnOp::Not, e) = e.kind {
8387
Some(e)
@@ -97,11 +101,12 @@ fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol:
97101
});
98102
let receiver = receiver.peel_borrows();
99103
let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
100-
if value.span.eq_ctxt(expr.span)
101-
&& is_type_diagnostic_item(cx, receiver_ty, sym::HashSet)
102-
&& path.ident.name == symbol
103-
{
104-
return Some(OpExpr { receiver, value, span });
104+
if value.span.eq_ctxt(expr.span) && path.ident.name == symbol {
105+
for sym in &[sym::HashSet, sym::BTreeSet] {
106+
if is_type_diagnostic_item(cx, receiver_ty, *sym) {
107+
return Some((OpExpr { receiver, value, span }, *sym));
108+
}
109+
}
105110
}
106111
}
107112
None
@@ -113,7 +118,7 @@ fn find_insert_calls<'tcx>(
113118
expr: &'tcx Expr<'_>,
114119
) -> Option<OpExpr<'tcx>> {
115120
for_each_expr(cx, expr, |e| {
116-
if let Some(insert_expr) = try_parse_op_call(cx, e, sym!(insert))
121+
if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym!(insert))
117122
&& SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver)
118123
&& SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value)
119124
{

tests/ui/set_contains_or_insert.rs

+76-7
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,78 @@
33
#![allow(clippy::needless_borrow)]
44
#![warn(clippy::set_contains_or_insert)]
55

6-
use std::collections::HashSet;
6+
use std::collections::{BTreeSet, HashSet};
77

8-
fn main() {
9-
should_warn_cases();
8+
fn should_warn_hashset() {
9+
let mut set = HashSet::new();
10+
let value = 5;
11+
12+
if !set.contains(&value) {
13+
set.insert(value);
14+
println!("Just a comment");
15+
}
16+
17+
if set.contains(&value) {
18+
set.insert(value);
19+
println!("Just a comment");
20+
}
21+
22+
if !set.contains(&value) {
23+
set.insert(value);
24+
}
1025

11-
should_not_warn_cases();
26+
if !!set.contains(&value) {
27+
set.insert(value);
28+
println!("Just a comment");
29+
}
30+
31+
if (&set).contains(&value) {
32+
set.insert(value);
33+
}
34+
35+
let borrow_value = &6;
36+
if !set.contains(borrow_value) {
37+
set.insert(*borrow_value);
38+
}
39+
40+
let borrow_set = &mut set;
41+
if !borrow_set.contains(&value) {
42+
borrow_set.insert(value);
43+
}
1244
}
1345

14-
fn should_warn_cases() {
46+
fn should_not_warn_hashset() {
1547
let mut set = HashSet::new();
1648
let value = 5;
49+
let another_value = 6;
50+
51+
if !set.contains(&value) {
52+
set.insert(another_value);
53+
}
54+
55+
if !set.contains(&value) {
56+
println!("Just a comment");
57+
}
58+
59+
if simply_true() {
60+
set.insert(value);
61+
}
62+
63+
if !set.contains(&value) {
64+
set.replace(value); //it is not insert
65+
println!("Just a comment");
66+
}
67+
68+
if set.contains(&value) {
69+
println!("value is already in set");
70+
} else {
71+
set.insert(value);
72+
}
73+
}
74+
75+
fn should_warn_btreeset() {
76+
let mut set = BTreeSet::new();
77+
let value = 5;
1778

1879
if !set.contains(&value) {
1980
set.insert(value);
@@ -49,8 +110,8 @@ fn should_warn_cases() {
49110
}
50111
}
51112

52-
fn should_not_warn_cases() {
53-
let mut set = HashSet::new();
113+
fn should_not_warn_btreeset() {
114+
let mut set = BTreeSet::new();
54115
let value = 5;
55116
let another_value = 6;
56117

@@ -81,3 +142,11 @@ fn should_not_warn_cases() {
81142
fn simply_true() -> bool {
82143
true
83144
}
145+
146+
// This is placed last in order to be able to add new tests without changing line numbers
147+
fn main() {
148+
should_warn_hashset();
149+
should_warn_btreeset();
150+
should_not_warn_hashset();
151+
should_not_warn_btreeset();
152+
}
+64-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: usage of `HashSet::insert` after `HashSet::contains`
2-
--> tests/ui/set_contains_or_insert.rs:18:13
2+
--> tests/ui/set_contains_or_insert.rs:12:13
33
|
44
LL | if !set.contains(&value) {
55
| ^^^^^^^^^^^^^^^^
@@ -10,52 +10,108 @@ LL | set.insert(value);
1010
= help: to override `-D warnings` add `#[allow(clippy::set_contains_or_insert)]`
1111

1212
error: usage of `HashSet::insert` after `HashSet::contains`
13-
--> tests/ui/set_contains_or_insert.rs:23:12
13+
--> tests/ui/set_contains_or_insert.rs:17:12
1414
|
1515
LL | if set.contains(&value) {
1616
| ^^^^^^^^^^^^^^^^
1717
LL | set.insert(value);
1818
| ^^^^^^^^^^^^^
1919

2020
error: usage of `HashSet::insert` after `HashSet::contains`
21-
--> tests/ui/set_contains_or_insert.rs:28:13
21+
--> tests/ui/set_contains_or_insert.rs:22:13
2222
|
2323
LL | if !set.contains(&value) {
2424
| ^^^^^^^^^^^^^^^^
2525
LL | set.insert(value);
2626
| ^^^^^^^^^^^^^
2727

2828
error: usage of `HashSet::insert` after `HashSet::contains`
29-
--> tests/ui/set_contains_or_insert.rs:32:14
29+
--> tests/ui/set_contains_or_insert.rs:26:14
3030
|
3131
LL | if !!set.contains(&value) {
3232
| ^^^^^^^^^^^^^^^^
3333
LL | set.insert(value);
3434
| ^^^^^^^^^^^^^
3535

3636
error: usage of `HashSet::insert` after `HashSet::contains`
37-
--> tests/ui/set_contains_or_insert.rs:37:15
37+
--> tests/ui/set_contains_or_insert.rs:31:15
3838
|
3939
LL | if (&set).contains(&value) {
4040
| ^^^^^^^^^^^^^^^^
4141
LL | set.insert(value);
4242
| ^^^^^^^^^^^^^
4343

4444
error: usage of `HashSet::insert` after `HashSet::contains`
45-
--> tests/ui/set_contains_or_insert.rs:42:13
45+
--> tests/ui/set_contains_or_insert.rs:36:13
4646
|
4747
LL | if !set.contains(borrow_value) {
4848
| ^^^^^^^^^^^^^^^^^^^^^^
4949
LL | set.insert(*borrow_value);
5050
| ^^^^^^^^^^^^^^^^^^^^^
5151

5252
error: usage of `HashSet::insert` after `HashSet::contains`
53-
--> tests/ui/set_contains_or_insert.rs:47:20
53+
--> tests/ui/set_contains_or_insert.rs:41:20
5454
|
5555
LL | if !borrow_set.contains(&value) {
5656
| ^^^^^^^^^^^^^^^^
5757
LL | borrow_set.insert(value);
5858
| ^^^^^^^^^^^^^
5959

60-
error: aborting due to 7 previous errors
60+
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
61+
--> tests/ui/set_contains_or_insert.rs:79:13
62+
|
63+
LL | if !set.contains(&value) {
64+
| ^^^^^^^^^^^^^^^^
65+
LL | set.insert(value);
66+
| ^^^^^^^^^^^^^
67+
68+
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
69+
--> tests/ui/set_contains_or_insert.rs:84:12
70+
|
71+
LL | if set.contains(&value) {
72+
| ^^^^^^^^^^^^^^^^
73+
LL | set.insert(value);
74+
| ^^^^^^^^^^^^^
75+
76+
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
77+
--> tests/ui/set_contains_or_insert.rs:89:13
78+
|
79+
LL | if !set.contains(&value) {
80+
| ^^^^^^^^^^^^^^^^
81+
LL | set.insert(value);
82+
| ^^^^^^^^^^^^^
83+
84+
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
85+
--> tests/ui/set_contains_or_insert.rs:93:14
86+
|
87+
LL | if !!set.contains(&value) {
88+
| ^^^^^^^^^^^^^^^^
89+
LL | set.insert(value);
90+
| ^^^^^^^^^^^^^
91+
92+
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
93+
--> tests/ui/set_contains_or_insert.rs:98:15
94+
|
95+
LL | if (&set).contains(&value) {
96+
| ^^^^^^^^^^^^^^^^
97+
LL | set.insert(value);
98+
| ^^^^^^^^^^^^^
99+
100+
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
101+
--> tests/ui/set_contains_or_insert.rs:103:13
102+
|
103+
LL | if !set.contains(borrow_value) {
104+
| ^^^^^^^^^^^^^^^^^^^^^^
105+
LL | set.insert(*borrow_value);
106+
| ^^^^^^^^^^^^^^^^^^^^^
107+
108+
error: usage of `BTreeSet::insert` after `BTreeSet::contains`
109+
--> tests/ui/set_contains_or_insert.rs:108:20
110+
|
111+
LL | if !borrow_set.contains(&value) {
112+
| ^^^^^^^^^^^^^^^^
113+
LL | borrow_set.insert(value);
114+
| ^^^^^^^^^^^^^
115+
116+
error: aborting due to 14 previous errors
61117

0 commit comments

Comments
 (0)