Skip to content

Commit fbb9e56

Browse files
committed
Auto merge of #8754 - guerinoni:no_effect_replace, r=llogiq
New lint `no_effect_replace` Closes #1595 Signed-off-by: Federico Guerinoni <[email protected]> changelog: Add [`no_effect_replace`] lint.
2 parents 050cdd6 + ea62347 commit fbb9e56

8 files changed

+177
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3613,6 +3613,7 @@ Released 2018-09-13
36133613
[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
36143614
[`new_without_default_derive`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default_derive
36153615
[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
3616+
[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace
36163617
[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
36173618
[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
36183619
[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions

clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
189189
LintId::of(methods::NEEDLESS_OPTION_TAKE),
190190
LintId::of(methods::NEEDLESS_SPLITN),
191191
LintId::of(methods::NEW_RET_NO_SELF),
192+
LintId::of(methods::NO_EFFECT_REPLACE),
192193
LintId::of(methods::OK_EXPECT),
193194
LintId::of(methods::OPTION_AS_REF_DEREF),
194195
LintId::of(methods::OPTION_FILTER_MAP),

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ store.register_lints(&[
330330
methods::NEEDLESS_OPTION_TAKE,
331331
methods::NEEDLESS_SPLITN,
332332
methods::NEW_RET_NO_SELF,
333+
methods::NO_EFFECT_REPLACE,
333334
methods::OK_EXPECT,
334335
methods::OPTION_AS_REF_DEREF,
335336
methods::OPTION_FILTER_MAP,

clippy_lints/src/lib.register_suspicious.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
2323
LintId::of(loops::EMPTY_LOOP),
2424
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
2525
LintId::of(loops::MUT_RANGE_BOUND),
26+
LintId::of(methods::NO_EFFECT_REPLACE),
2627
LintId::of(methods::SUSPICIOUS_MAP),
2728
LintId::of(mut_key::MUTABLE_KEY_TYPE),
2829
LintId::of(octal_escapes::OCTAL_ESCAPES),

clippy_lints/src/methods/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ mod map_identity;
4444
mod map_unwrap_or;
4545
mod needless_option_as_deref;
4646
mod needless_option_take;
47+
mod no_effect_replace;
4748
mod ok_expect;
4849
mod option_as_ref_deref;
4950
mod option_map_or_none;
@@ -2195,6 +2196,24 @@ declare_clippy_lint! {
21952196
"using `.as_ref().take()` on a temporary value"
21962197
}
21972198

2199+
declare_clippy_lint! {
2200+
/// ### What it does
2201+
/// Checks for `replace` statements which have no effect.
2202+
///
2203+
/// ### Why is this bad?
2204+
/// It's either a mistake or confusing.
2205+
///
2206+
/// ### Example
2207+
/// ```rust
2208+
/// "1234".replace("12", "12");
2209+
/// "1234".replacen("12", "12", 1);
2210+
/// ```
2211+
#[clippy::version = "1.62.0"]
2212+
pub NO_EFFECT_REPLACE,
2213+
suspicious,
2214+
"replace with no effect"
2215+
}
2216+
21982217
pub struct Methods {
21992218
avoid_breaking_exported_api: bool,
22002219
msrv: Option<RustcVersion>,
@@ -2294,6 +2313,7 @@ impl_lint_pass!(Methods => [
22942313
NEEDLESS_OPTION_AS_DEREF,
22952314
IS_DIGIT_ASCII_RADIX,
22962315
NEEDLESS_OPTION_TAKE,
2316+
NO_EFFECT_REPLACE,
22972317
]);
22982318

22992319
/// Extracts a method call name, args, and `Span` of the method name.
@@ -2705,6 +2725,9 @@ impl Methods {
27052725
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
27062726
},
27072727
},
2728+
("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => {
2729+
no_effect_replace::check(cx, expr, arg1, arg2);
2730+
},
27082731
_ => {},
27092732
}
27102733
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
use clippy_utils::diagnostics::span_lint;
2+
use clippy_utils::ty::is_type_diagnostic_item;
3+
use clippy_utils::SpanlessEq;
4+
use if_chain::if_chain;
5+
use rustc_ast::LitKind;
6+
use rustc_hir::ExprKind;
7+
use rustc_lint::LateContext;
8+
use rustc_span::sym;
9+
10+
use super::NO_EFFECT_REPLACE;
11+
12+
pub(super) fn check<'tcx>(
13+
cx: &LateContext<'tcx>,
14+
expr: &'tcx rustc_hir::Expr<'_>,
15+
arg1: &'tcx rustc_hir::Expr<'_>,
16+
arg2: &'tcx rustc_hir::Expr<'_>,
17+
) {
18+
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
19+
if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) {
20+
return;
21+
}
22+
23+
if_chain! {
24+
if let ExprKind::Lit(spanned) = &arg1.kind;
25+
if let Some(param1) = lit_string_value(&spanned.node);
26+
27+
if let ExprKind::Lit(spanned) = &arg2.kind;
28+
if let LitKind::Str(param2, _) = &spanned.node;
29+
if param1 == param2.as_str();
30+
31+
then {
32+
span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself");
33+
}
34+
}
35+
36+
if SpanlessEq::new(cx).eq_expr(arg1, arg2) {
37+
span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself");
38+
}
39+
}
40+
41+
fn lit_string_value(node: &LitKind) -> Option<String> {
42+
match node {
43+
LitKind::Char(value) => Some(value.to_string()),
44+
LitKind::Str(value, _) => Some(value.as_str().to_owned()),
45+
_ => None,
46+
}
47+
}

tests/ui/no_effect_replace.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#![warn(clippy::no_effect_replace)]
2+
3+
fn main() {
4+
let _ = "12345".replace('1', "1");
5+
let _ = "12345".replace("12", "12");
6+
let _ = String::new().replace("12", "12");
7+
8+
let _ = "12345".replacen('1', "1", 1);
9+
let _ = "12345".replacen("12", "12", 1);
10+
let _ = String::new().replacen("12", "12", 1);
11+
12+
let _ = "12345".replace("12", "22");
13+
let _ = "12345".replacen("12", "22", 1);
14+
15+
let mut x = X::default();
16+
let _ = "hello".replace(&x.f(), &x.f());
17+
let _ = "hello".replace(&x.f(), &x.ff());
18+
19+
let _ = "hello".replace(&y(), &y());
20+
let _ = "hello".replace(&y(), &z());
21+
22+
let _ = Replaceme.replace("a", "a");
23+
}
24+
25+
#[derive(Default)]
26+
struct X {}
27+
28+
impl X {
29+
fn f(&mut self) -> String {
30+
"he".to_string()
31+
}
32+
33+
fn ff(&mut self) -> String {
34+
"hh".to_string()
35+
}
36+
}
37+
38+
fn y() -> String {
39+
"he".to_string()
40+
}
41+
42+
fn z() -> String {
43+
"hh".to_string()
44+
}
45+
46+
struct Replaceme;
47+
impl Replaceme {
48+
pub fn replace(&mut self, a: &str, b: &str) -> Self {
49+
Self
50+
}
51+
}

tests/ui/no_effect_replace.stderr

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: replacing text with itself
2+
--> $DIR/no_effect_replace.rs:4:13
3+
|
4+
LL | let _ = "12345".replace('1', "1");
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::no-effect-replace` implied by `-D warnings`
8+
9+
error: replacing text with itself
10+
--> $DIR/no_effect_replace.rs:5:13
11+
|
12+
LL | let _ = "12345".replace("12", "12");
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: replacing text with itself
16+
--> $DIR/no_effect_replace.rs:6:13
17+
|
18+
LL | let _ = String::new().replace("12", "12");
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
21+
error: replacing text with itself
22+
--> $DIR/no_effect_replace.rs:8:13
23+
|
24+
LL | let _ = "12345".replacen('1', "1", 1);
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
27+
error: replacing text with itself
28+
--> $DIR/no_effect_replace.rs:9:13
29+
|
30+
LL | let _ = "12345".replacen("12", "12", 1);
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
33+
error: replacing text with itself
34+
--> $DIR/no_effect_replace.rs:10:13
35+
|
36+
LL | let _ = String::new().replacen("12", "12", 1);
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38+
39+
error: replacing text with itself
40+
--> $DIR/no_effect_replace.rs:16:13
41+
|
42+
LL | let _ = "hello".replace(&x.f(), &x.f());
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44+
45+
error: replacing text with itself
46+
--> $DIR/no_effect_replace.rs:19:13
47+
|
48+
LL | let _ = "hello".replace(&y(), &y());
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
50+
51+
error: aborting due to 8 previous errors
52+

0 commit comments

Comments
 (0)