Skip to content

Commit

Permalink
Fix false positive for needless_character_iteration lint
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeGomez committed Jun 3, 2024
1 parent 4f3180a commit 312c7f3
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 12 deletions.
3 changes: 2 additions & 1 deletion clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4485,7 +4485,7 @@ impl Methods {
},
("all", [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
needless_character_iteration::check(cx, expr, recv, arg);
needless_character_iteration::check(cx, expr, recv, arg, true);
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
iter_overeager_cloned::check(
cx,
Expand All @@ -4506,6 +4506,7 @@ impl Methods {
},
("any", [arg]) => {
unused_enumerate_index::check(cx, expr, recv, arg);
needless_character_iteration::check(cx, expr, recv, arg, false);
match method_call(recv) {
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
cx,
Expand Down
22 changes: 19 additions & 3 deletions clippy_lints/src/methods/needless_character_iteration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ fn handle_expr(
span: Span,
before_chars: Span,
revert: bool,
is_all: bool,
) {
match expr.kind {
ExprKind::MethodCall(method, receiver, [], _) => {
Expand All @@ -32,6 +33,9 @@ fn handle_expr(
&& let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs()
&& *char_arg_ty.kind() == ty::Char
&& let Some(snippet) = snippet_opt(cx, before_chars)
// If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
// `is_ascii`, then only `.all()` should warn.
&& revert != is_all
{
span_lint_and_sugg(
cx,
Expand All @@ -55,16 +59,27 @@ fn handle_expr(
&& let Some(last_chain_binding_id) =
get_last_chain_binding_hir_id(first_param, block.stmts)
{
handle_expr(cx, block_expr, last_chain_binding_id, span, before_chars, revert);
handle_expr(
cx,
block_expr,
last_chain_binding_id,
span,
before_chars,
revert,
is_all,
);
}
},
ExprKind::Unary(UnOp::Not, expr) => handle_expr(cx, expr, first_param, span, before_chars, !revert),
ExprKind::Unary(UnOp::Not, expr) => handle_expr(cx, expr, first_param, span, before_chars, !revert, is_all),
ExprKind::Call(fn_path, [arg]) => {
if let ExprKind::Path(path) = fn_path.kind
&& let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
&& match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
&& path_to_local_id(peels_expr_ref(arg), first_param)
&& let Some(snippet) = snippet_opt(cx, before_chars)
// If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
// `is_ascii`, then only `.all()` should warn.
&& revert != is_all
{
span_lint_and_sugg(
cx,
Expand All @@ -81,7 +96,7 @@ fn handle_expr(
}
}

pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) {
pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>, is_all: bool) {
if let ExprKind::Closure(&Closure { body, .. }) = closure_arg.kind
&& let body = cx.tcx.hir().body(body)
&& let Some(first_param) = body.params.first()
Expand All @@ -103,6 +118,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
recv.span.with_hi(call_expr.span.hi()),
recv.span.with_hi(expr_start.hi()),
false,
is_all,
);
}
}
6 changes: 6 additions & 0 deletions tests/ui/needless_character_iteration.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,10 @@ fn main() {

// Should not lint!
"foo".chars().map(|c| c).all(|c| !char::is_ascii(&c));

// Should not lint!
"foo".chars().all(|c| !c.is_ascii());

// Should not lint!
"foo".chars().any(|c| c.is_ascii());
}
14 changes: 10 additions & 4 deletions tests/ui/needless_character_iteration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,25 @@ fn magic(_: char) {}
fn main() {
"foo".chars().all(|c| c.is_ascii());
//~^ ERROR: checking if a string is ascii using iterators
"foo".chars().all(|c| !c.is_ascii());
"foo".chars().any(|c| !c.is_ascii());
//~^ ERROR: checking if a string is ascii using iterators
"foo".chars().all(|c| char::is_ascii(&c));
//~^ ERROR: checking if a string is ascii using iterators
"foo".chars().all(|c| !char::is_ascii(&c));
"foo".chars().any(|c| !char::is_ascii(&c));
//~^ ERROR: checking if a string is ascii using iterators

let s = String::new();
s.chars().all(|c| c.is_ascii());
//~^ ERROR: checking if a string is ascii using iterators
"foo".to_string().chars().all(|c| !c.is_ascii());
"foo".to_string().chars().any(|c| !c.is_ascii());
//~^ ERROR: checking if a string is ascii using iterators

"foo".chars().all(|c| {
//~^ ERROR: checking if a string is ascii using iterators
let x = c;
x.is_ascii()
});
"foo".chars().all(|c| {
"foo".chars().any(|c| {
//~^ ERROR: checking if a string is ascii using iterators
let x = c;
!x.is_ascii()
Expand All @@ -56,4 +56,10 @@ fn main() {

// Should not lint!
"foo".chars().map(|c| c).all(|c| !char::is_ascii(&c));

// Should not lint!
"foo".chars().all(|c| !c.is_ascii());

// Should not lint!
"foo".chars().any(|c| c.is_ascii());
}
8 changes: 4 additions & 4 deletions tests/ui/needless_character_iteration.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ LL | "foo".chars().all(|c| c.is_ascii());
error: checking if a string is ascii using iterators
--> tests/ui/needless_character_iteration.rs:20:5
|
LL | "foo".chars().all(|c| !c.is_ascii());
LL | "foo".chars().any(|c| !c.is_ascii());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!"foo".is_ascii()`

error: checking if a string is ascii using iterators
Expand All @@ -22,7 +22,7 @@ LL | "foo".chars().all(|c| char::is_ascii(&c));
error: checking if a string is ascii using iterators
--> tests/ui/needless_character_iteration.rs:24:5
|
LL | "foo".chars().all(|c| !char::is_ascii(&c));
LL | "foo".chars().any(|c| !char::is_ascii(&c));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!"foo".is_ascii()`

error: checking if a string is ascii using iterators
Expand All @@ -34,7 +34,7 @@ LL | s.chars().all(|c| c.is_ascii());
error: checking if a string is ascii using iterators
--> tests/ui/needless_character_iteration.rs:30:5
|
LL | "foo".to_string().chars().all(|c| !c.is_ascii());
LL | "foo".to_string().chars().any(|c| !c.is_ascii());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!"foo".to_string().is_ascii()`

error: checking if a string is ascii using iterators
Expand All @@ -50,7 +50,7 @@ LL | | });
error: checking if a string is ascii using iterators
--> tests/ui/needless_character_iteration.rs:38:5
|
LL | / "foo".chars().all(|c| {
LL | / "foo".chars().any(|c| {
LL | |
LL | | let x = c;
LL | | !x.is_ascii()
Expand Down

0 comments on commit 312c7f3

Please sign in to comment.