Skip to content

Commit 5fa08ea

Browse files
committed
Evaluate constant expressions in suspicious_splitn
1 parent 898b6a0 commit 5fa08ea

File tree

4 files changed

+34
-9
lines changed

4 files changed

+34
-9
lines changed

clippy_lints/src/methods/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,8 +1635,9 @@ declare_clippy_lint! {
16351635
}
16361636

16371637
declare_clippy_lint! {
1638-
/// **What it does:** Checks for calls to `splitn` and related functions with
1639-
/// either zero or one splits.
1638+
/// **What it does:** Checks for calls to [`splitn`]
1639+
/// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
1640+
/// related functions with either zero or one splits.
16401641
///
16411642
/// **Why is this bad?** These calls don't actually split the value and are
16421643
/// likely to be intended as a different number.

clippy_lints/src/methods/suspicious_splitn.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use clippy_utils::consts::{constant, Constant};
12
use clippy_utils::diagnostics::span_lint_and_note;
23
use if_chain::if_chain;
34
use rustc_ast::LitKind;
@@ -15,18 +16,21 @@ pub(super) fn check(
1516
count_arg: &Expr<'_>,
1617
) {
1718
if_chain! {
18-
// Ignore empty slice literal
19-
if !matches!(self_arg.kind, ExprKind::Array([]));
20-
// Ignore empty string literal
21-
if !matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty());
22-
if let ExprKind::Lit(count_lit) = &count_arg.kind;
23-
if let LitKind::Int(count, _) = count_lit.node;
19+
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg);
2420
if count <= 1;
2521
if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
2622
if let Some(impl_id) = cx.tcx.impl_of_method(call_id);
2723
let lang_items = cx.tcx.lang_items();
2824
if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id);
2925
then {
26+
// Ignore empty slice and string literals when used with a literal count.
27+
if (matches!(self_arg.kind, ExprKind::Array([]))
28+
|| matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty())
29+
) && matches!(count_arg.kind, ExprKind::Lit(_))
30+
{
31+
return;
32+
}
33+
3034
let (msg, note_msg) = if count == 0 {
3135
(format!("`{}` called with `0` splits", method_name),
3236
"the resulting iterator will always return `None`")

tests/ui/suspicious_splitn.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,8 @@ fn main() {
1313
let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1);
1414
let _ = [0, 1, 2].splitn(1, |&x| x == 1);
1515
let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1);
16+
17+
const X: usize = 0;
18+
let _ = "a,b".splitn(X + 1, ',');
19+
let _ = "a,b".splitn(X, ',');
1620
}

tests/ui/suspicious_splitn.stderr

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,21 @@ LL | let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1);
5555
|
5656
= note: the resulting iterator will always return the entire slice followed by `None`
5757

58-
error: aborting due to 7 previous errors
58+
error: `splitn` called with `1` split
59+
--> $DIR/suspicious_splitn.rs:18:13
60+
|
61+
LL | let _ = "a,b".splitn(X + 1, ',');
62+
| ^^^^^^^^^^^^^^^^^^^^^^^^
63+
|
64+
= note: the resulting iterator will always return the entire string followed by `None`
65+
66+
error: `splitn` called with `0` splits
67+
--> $DIR/suspicious_splitn.rs:19:13
68+
|
69+
LL | let _ = "a,b".splitn(X, ',');
70+
| ^^^^^^^^^^^^^^^^^^^^
71+
|
72+
= note: the resulting iterator will always return `None`
73+
74+
error: aborting due to 9 previous errors
5975

0 commit comments

Comments
 (0)