Skip to content

Commit ea1c96a

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]> Thank you for making Clippy better! We're collecting our changelog from pull request descriptions. If your PR only includes internal changes, you can just write `changelog: none`. Otherwise, please write a short comment explaining your change. Also, it's helpful for us that the lint name is put into brackets `[]` and backticks `` ` ` ``, e.g. ``[`lint_name`]``. If your PR fixes an issue, you can add "fixes #issue_number" into this PR description. This way the issue will be automatically closed when your PR is merged. If you added a new lint, here's a checklist for things that will be checked during review or continuous integration. - \[x] Followed [lint naming conventions][lint_naming] - \[x] Added passing UI tests (including committed `.stderr` file) - \[x] `cargo test` passes locally - \[x] Executed `cargo dev update_lints` - \[x] Added lint documentation - \[x] Run `cargo dev fmt` [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints Note that you can skip the above if you are just opening a WIP PR in order to get feedback. Delete this line and everything above before opening your PR. --- *Please write a short comment explaining your change (or "none" for internal only changes)* changelog: Add `no_effect_replace` lint.
2 parents 38b7a04 + 96d18b6 commit ea1c96a

8 files changed

+198
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3593,6 +3593,7 @@ Released 2018-09-13
35933593
[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
35943594
[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
35953595
[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
3596+
[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace
35963597
[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
35973598
[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
35983599
[`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
@@ -243,6 +243,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
243243
LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
244244
LintId::of(no_effect::NO_EFFECT),
245245
LintId::of(no_effect::UNNECESSARY_OPERATION),
246+
LintId::of(no_effect_replace::NO_EFFECT_REPLACE),
246247
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
247248
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
248249
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,7 @@ store.register_lints(&[
408408
no_effect::NO_EFFECT,
409409
no_effect::NO_EFFECT_UNDERSCORE_BINDING,
410410
no_effect::UNNECESSARY_OPERATION,
411+
no_effect_replace::NO_EFFECT_REPLACE,
411412
non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
412413
non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
413414
non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,

clippy_lints/src/lib.register_suspicious.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
2525
LintId::of(loops::MUT_RANGE_BOUND),
2626
LintId::of(methods::SUSPICIOUS_MAP),
2727
LintId::of(mut_key::MUTABLE_KEY_TYPE),
28+
LintId::of(no_effect_replace::NO_EFFECT_REPLACE),
2829
LintId::of(octal_escapes::OCTAL_ESCAPES),
2930
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
3031
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ mod neg_cmp_op_on_partial_ord;
321321
mod neg_multiply;
322322
mod new_without_default;
323323
mod no_effect;
324+
mod no_effect_replace;
324325
mod non_copy_const;
325326
mod non_expressive_names;
326327
mod non_octal_unix_permissions;
@@ -889,6 +890,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
889890
store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
890891
let max_include_file_size = conf.max_include_file_size;
891892
store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
893+
store.register_late_pass(|| Box::new(no_effect_replace::NoEffectReplace));
892894
// add lints here, do not remove this comment, it's used in `new_lint`
893895
}
894896

clippy_lints/src/no_effect_replace.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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::{Expr, ExprKind};
7+
use rustc_lint::{LateContext, LateLintPass};
8+
use rustc_session::{declare_lint_pass, declare_tool_lint};
9+
use rustc_span::sym;
10+
11+
declare_clippy_lint! {
12+
/// ### What it does
13+
/// Checks for `replace` statements which have no effect.
14+
///
15+
/// ### Why is this bad?
16+
/// It's either a mistake or confusing.
17+
///
18+
/// ### Example
19+
/// ```rust
20+
/// "1234".replace("12", "12");
21+
/// "1234".replacen("12", "12", 1);
22+
/// ```
23+
#[clippy::version = "1.62.0"]
24+
pub NO_EFFECT_REPLACE,
25+
suspicious,
26+
"replace with no effect"
27+
}
28+
declare_lint_pass!(NoEffectReplace => [NO_EFFECT_REPLACE]);
29+
30+
impl LateLintPass<'_> for NoEffectReplace {
31+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
32+
let (path, args) = match expr.kind {
33+
ExprKind::MethodCall(path, args, _) => (path, args),
34+
_ => return,
35+
};
36+
37+
if !(args.len() == 3 || args.len() == 4)
38+
|| !(path.ident.name.as_str() == "replace" || path.ident.name.as_str() == "replacen")
39+
{
40+
return;
41+
}
42+
43+
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
44+
if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) {
45+
return;
46+
}
47+
48+
if_chain! {
49+
if let ExprKind::Lit(spanned) = &args[1].kind;
50+
if let Some(param1) = lit_string_value(&spanned.node);
51+
52+
if let ExprKind::Lit(spanned) = &args[2].kind;
53+
if let LitKind::Str(param2, _) = &spanned.node;
54+
if param1 == param2.as_str();
55+
56+
then {
57+
span_lint(
58+
cx,
59+
NO_EFFECT_REPLACE,
60+
expr.span,
61+
"you are using replace with same element",
62+
);
63+
}
64+
}
65+
66+
if_chain! {
67+
if let ExprKind::MethodCall(path, args, _) = expr.kind;
68+
if (args.len() == 3 && path.ident.name.as_str() == "replace") || (args.len() == 4 && path.ident.name.as_str() == "replacen");
69+
if SpanlessEq::new(cx).eq_expr(&args[1], &args[2]);
70+
71+
then {
72+
span_lint(
73+
cx,
74+
NO_EFFECT_REPLACE,
75+
expr.span,
76+
"you are using replace with same element",
77+
);
78+
}
79+
}
80+
}
81+
}
82+
83+
fn lit_string_value(node: &LitKind) -> Option<String> {
84+
match node {
85+
LitKind::Char(value) => Some(value.to_string()),
86+
LitKind::Str(value, _) => Some(value.as_str().to_owned()),
87+
_ => None,
88+
}
89+
}

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: you are using replace with same element
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: you are using replace with same element
10+
--> $DIR/no_effect_replace.rs:5:13
11+
|
12+
LL | let _ = "12345".replace("12", "12");
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
15+
error: you are using replace with same element
16+
--> $DIR/no_effect_replace.rs:6:13
17+
|
18+
LL | let _ = String::new().replace("12", "12");
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
21+
error: you are using replace with same element
22+
--> $DIR/no_effect_replace.rs:8:13
23+
|
24+
LL | let _ = "12345".replacen('1', "1", 1);
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
27+
error: you are using replace with same element
28+
--> $DIR/no_effect_replace.rs:9:13
29+
|
30+
LL | let _ = "12345".replacen("12", "12", 1);
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
33+
error: you are using replace with same element
34+
--> $DIR/no_effect_replace.rs:10:13
35+
|
36+
LL | let _ = String::new().replacen("12", "12", 1);
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38+
39+
error: you are using replace with same element
40+
--> $DIR/no_effect_replace.rs:16:13
41+
|
42+
LL | let _ = "hello".replace(&x.f(), &x.f());
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44+
45+
error: you are using replace with same element
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)