From 36cba5198af1a1b5e87b8ee672a2c6b0f32c26ce Mon Sep 17 00:00:00 2001 From: Nahua Kang Date: Sun, 14 Aug 2022 18:33:55 +0200 Subject: [PATCH] Simplify lint logic and address code review comments --- .../src/methods/collapsible_str_replace.rs | 301 ++++-------------- clippy_lints/src/methods/mod.rs | 14 +- tests/ui/collapsible_str_replace.fixed | 70 ++-- tests/ui/collapsible_str_replace.rs | 68 ++-- tests/ui/collapsible_str_replace.stderr | 78 +++-- 5 files changed, 201 insertions(+), 330 deletions(-) diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs index 4c6288e798cd..561033be5b6a 100644 --- a/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/clippy_lints/src/methods/collapsible_str_replace.rs @@ -1,19 +1,12 @@ -// run-rustfix - use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; +use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr; +use clippy_utils::{eq_expr_value, get_parent_expr}; use core::ops::ControlFlow; -use if_chain::if_chain; -use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::{ExprKind, Path, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty; -use rustc_span::source_map::Spanned; -use rustc_span::Span; +use std::collections::VecDeque; use super::method_call; use super::COLLAPSIBLE_STR_REPLACE; @@ -21,255 +14,83 @@ use super::COLLAPSIBLE_STR_REPLACE; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, - name: &str, - recv: &'tcx hir::Expr<'tcx>, + from: &'tcx hir::Expr<'tcx>, + to: &'tcx hir::Expr<'tcx>, ) { - if name == "replace" { - // The receiver of the method call must be `str` type to lint `collapsible_str_replace` - let original_recv = find_original_recv(recv); - let original_recv_ty_kind = cx.typeck_results().expr_ty(original_recv).peel_refs().kind(); - let original_recv_is_str_kind = matches!(original_recv_ty_kind, ty::Str); - - if_chain! { - if original_recv_is_str_kind; - if let Some(parent) = get_parent_expr(cx, expr); - if let Some((name, ..)) = method_call(parent); - if name == "replace"; - - then { - // If the parent node is a `str::replace` call, we've already handled the lint, don't lint again - return; - } + let replace_methods = collect_replace_calls(cx, expr, to); + if replace_methods.methods.len() > 1 { + let from_kind = cx.typeck_results().expr_ty(from).peel_refs().kind(); + // If the parent node's `to` argument is the same as the `to` argument + // of the last replace call in the current chain, don't lint as it was already linted + if let Some(parent) = get_parent_expr(cx, expr) + && let Some(("replace", [_, current_from, current_to], _)) = method_call(parent) + && eq_expr_value(cx, to, current_to) + && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() + { + return; } - if let Some(("replace", ..)) = method_call(recv) { - // Check if there's an earlier `str::replace` call - if original_recv_is_str_kind { - check_consecutive_replace_calls(cx, expr); - } - } + check_consecutive_replace_calls(cx, expr, &replace_methods, to); } } -/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint. -fn check_consecutive_replace_calls<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if_chain! { - if let Some(from_args) = get_replace_call_from_args_if_all_char_ty(cx, expr); - if let Some(to_arg) = get_replace_call_unique_to_arg_repr(expr); - then { - let earliest_replace_call_span = get_earliest_replace_call_span(expr); - - if replace_call_from_args_are_only_lit_chars(&from_args) { - let from_arg_reprs: Vec = from_args.iter().map(|from_arg| { - get_replace_call_char_arg_repr(from_arg).unwrap() - }).collect(); - let app = Applicability::MachineApplicable; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_STR_REPLACE, - expr.span.with_lo(earliest_replace_call_span.lo()), - "used consecutive `str::replace` call", - "replace with", - format!( - "replace(|c| matches!(c, {}), {})", - from_arg_reprs.join(" | "), - to_arg, - ), - app, - ); - } else { - // Use fallback lint - let from_arg_reprs: Vec = from_args.iter().map(|from_arg| { - get_replace_call_char_arg_repr(from_arg).unwrap() - }).collect(); - let app = Applicability::MachineApplicable; - - span_lint_and_sugg( - cx, - COLLAPSIBLE_STR_REPLACE, - expr.span.with_lo(earliest_replace_call_span.lo()), - "used consecutive `str::replace` call", - "replace with", - format!( - "replace(&[{}], {})", - from_arg_reprs.join(" , "), - to_arg, - ), - app, - ); - } - } - } +struct ReplaceMethods<'tcx> { + methods: VecDeque<&'tcx hir::Expr<'tcx>>, + from_args: VecDeque<&'tcx hir::Expr<'tcx>>, } -/// Check if all the `from` arguments of a chain of consecutive calls to `str::replace` -/// are all of `ExprKind::Lit` types. If any is not, return false. -fn replace_call_from_args_are_only_lit_chars<'tcx>(from_args: &[&'tcx hir::Expr<'tcx>]) -> bool { - let mut only_lit_chars = true; - - for from_arg in from_args.iter() { - match from_arg.kind { - ExprKind::Lit(..) => {}, - _ => only_lit_chars = false, - } - } - - only_lit_chars -} - -/// Collect and return all of the `from` arguments of a chain of consecutive `str::replace` calls -/// if these `from` arguments's expressions are of the `ty::Char` kind. Otherwise return `None`. -fn get_replace_call_from_args_if_all_char_ty<'tcx>( +fn collect_replace_calls<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, -) -> Option>> { - let mut all_from_args_are_chars = true; - let mut from_args = Vec::new(); + to_arg: &'tcx hir::Expr<'tcx>, +) -> ReplaceMethods<'tcx> { + let mut methods = VecDeque::new(); + let mut from_args = VecDeque::new(); let _: Option<()> = for_each_expr(expr, |e| { - if let Some((name, [_, args @ ..], _)) = method_call(e) { - match (name, args) { - ("replace", [from, _]) => { - let from_ty_kind = cx.typeck_results().expr_ty(from).peel_refs().kind(); - if matches!(from_ty_kind, ty::Char) { - from_args.push(from); - } else { - all_from_args_are_chars = false; - } - ControlFlow::Continue(()) - }, - _ => ControlFlow::BREAK, - } - } else { - ControlFlow::Continue(()) - } - }); - - if all_from_args_are_chars { - return Some(from_args); - } - - None -} - -/// Return a unique String representation of the `to` argument used in a chain of `str::replace` -/// calls if each `str::replace` call's `to` argument is identical to the other `to` arguments in -/// the chain. Otherwise, return `None`. -fn get_replace_call_unique_to_arg_repr<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option { - let mut to_args = Vec::new(); - - let _: Option<()> = for_each_expr(expr, |e| { - if let Some((name, [_, args @ ..], _)) = method_call(e) { - match (name, args) { - ("replace", [_, to]) => { - to_args.push(to); - ControlFlow::Continue(()) - }, - _ => ControlFlow::BREAK, + if let Some(("replace", [_, from, to], _)) = method_call(e) { + if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { + methods.push_front(e); + from_args.push_front(from); + ControlFlow::Continue(()) + } else { + ControlFlow::BREAK } } else { ControlFlow::Continue(()) } }); - // let mut to_arg_repr_set = FxHashSet::default(); - let mut to_arg_reprs = Vec::new(); - for &to_arg in &to_args { - if let Some(to_arg_repr) = get_replace_call_char_arg_repr(to_arg) { - to_arg_reprs.push(to_arg_repr); - } - } - - let to_arg_repr_set = to_arg_reprs.iter().cloned().collect::>(); - // Check if the set of `to` argument representations has more than one unique value - if to_arg_repr_set.len() != 1 { - return None; - } - - // Return the single representation value - to_arg_reprs.pop() + ReplaceMethods { methods, from_args } } -/// Get the representation of an argument of a `str::replace` call either of the literal char value -/// or variable name, i.e. the resolved path segments `ident`. -/// Return: -/// - the str literal with double quotes, e.g. "\"l\"" -/// - the char literal with single quotes, e.g. "'l'" -/// - the variable as a String, e.g. "l" -fn get_replace_call_char_arg_repr<'tcx>(arg: &'tcx hir::Expr<'tcx>) -> Option { - match arg.kind { - ExprKind::Lit(Spanned { - node: LitKind::Str(to_arg_val, _), - .. - }) => { - let repr = to_arg_val.as_str(); - let double_quote = "\""; - Some(double_quote.to_owned() + repr + double_quote) - }, - ExprKind::Lit(Spanned { - node: LitKind::Char(to_arg_val), - .. - }) => { - let repr = to_arg_val.to_string(); - let double_quote = "\'"; - Some(double_quote.to_owned() + &repr + double_quote) - }, - ExprKind::Path(QPath::Resolved( - _, - Path { - segments: path_segments, - .. - }, - )) => { - // join the path_segments values by "::" - let path_segment_ident_names: Vec<&str> = path_segments - .iter() - .map(|path_seg| path_seg.ident.name.as_str()) - .collect(); - Some(path_segment_ident_names.join("::")) - }, - _ => None, +/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint. +fn check_consecutive_replace_calls<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + replace_methods: &ReplaceMethods<'tcx>, + to_arg: &'tcx hir::Expr<'tcx>, +) { + let from_args = &replace_methods.from_args; + let from_arg_reprs: Vec = from_args + .iter() + .map(|from_arg| snippet(cx, from_arg.span, "..").to_string()) + .collect(); + let app = Applicability::MachineApplicable; + let earliest_replace_call = replace_methods.methods.front().unwrap(); + if let Some((_, [..], span_lo)) = method_call(earliest_replace_call) { + span_lint_and_sugg( + cx, + COLLAPSIBLE_STR_REPLACE, + expr.span.with_lo(span_lo.lo()), + "used consecutive `str::replace` call", + "replace with", + format!( + "replace([{}], {})", + from_arg_reprs.join(", "), + snippet(cx, to_arg.span, ".."), + ), + app, + ); } } - -fn get_earliest_replace_call_span<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Span { - let mut earliest_replace_call_span = expr.span; - - let _: Option<()> = for_each_expr(expr, |e| { - if let Some((name, [_, args @ ..], span)) = method_call(e) { - match (name, args) { - ("replace", [_, _]) => { - earliest_replace_call_span = span; - ControlFlow::Continue(()) - }, - _ => ControlFlow::BREAK, - } - } else { - ControlFlow::Continue(()) - } - }); - - earliest_replace_call_span -} - -/// Find the original receiver of a chain of `str::replace` method calls. -fn find_original_recv<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> &'tcx hir::Expr<'tcx> { - let mut original_recv = recv; - - let _: Option<()> = for_each_expr(recv, |e| { - if let Some((name, [prev_recv, args @ ..], _)) = method_call(e) { - match (name, args) { - ("replace", [_, _]) => { - original_recv = prev_recv; - ControlFlow::Continue(()) - }, - _ => ControlFlow::BREAK, - } - } else { - ControlFlow::Continue(()) - } - }); - - original_recv -} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8b2fa8e94577..49d4900295fd 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -156,7 +156,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// let hello = "hesuo worpd".replace(|c| matches!(c, 's' | 'u' | 'p'), "l"); + /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l"); /// ``` #[clippy::version = "1.64.0"] pub COLLAPSIBLE_STR_REPLACE, @@ -3507,6 +3507,14 @@ impl Methods { ("repeat", [arg]) => { repeat_once::check(cx, expr, recv, arg); }, + (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => { + no_effect_replace::check(cx, expr, arg1, arg2); + + // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint + if name == "replace" && let Some(("replace", ..)) = method_call(recv) { + collapsible_str_replace::check(cx, expr, arg1, arg2); + } + }, ("resize", [count_arg, default_arg]) => { vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); }, @@ -3519,10 +3527,6 @@ impl Methods { ("sort_unstable_by", [arg]) => { unnecessary_sort_by::check(cx, expr, recv, arg, true); }, - ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => { - no_effect_replace::check(cx, expr, arg1, arg2); - collapsible_str_replace::check(cx, expr, name, recv); - }, ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); diff --git a/tests/ui/collapsible_str_replace.fixed b/tests/ui/collapsible_str_replace.fixed index 0bf857d9837e..376396cb1b5d 100644 --- a/tests/ui/collapsible_str_replace.fixed +++ b/tests/ui/collapsible_str_replace.fixed @@ -2,64 +2,72 @@ #![warn(clippy::collapsible_str_replace)] -fn get_filter() -> &'static str { - "u" +fn get_filter() -> char { + 'u' } fn main() { - let misspelled = "hesuo worpd"; - + let d = 'd'; let p = 'p'; let s = 's'; let u = 'u'; let l = "l"; + let mut iter = ["l", "z"].iter(); + // LINT CASES - let _ = misspelled.replace(|c| matches!(c, 'u' | 's'), "l"); + let _ = "hesuo worpd".replace(['s', 'u'], "l"); - let _ = misspelled.replace(|c| matches!(c, 'u' | 's'), l); + let _ = "hesuo worpd".replace(['s', 'u'], l); - let _ = misspelled.replace(|c| matches!(c, 'p' | 'u' | 's'), "l"); + let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l"); - let _ = misspelled - .replace(|c| matches!(c, 'd' | 'p' | 'u' | 's'), "l"); + let _ = "hesuo worpd" + .replace(['s', 'u', 'p', 'd'], "l"); - // FALLBACK CASES - // If there are consecutive calls to `str::replace` and all or any chars are variables, - // recommend the fallback `misspelled.replace(&[s, u, p], "l")` - let _ = misspelled.replace(&['u' , s], "l"); + let _ = "hesuo world".replace([s, 'u'], "l"); - let _ = misspelled.replace(&['p' , 'u' , s], "l"); + let _ = "hesuo worpd".replace([s, 'u', 'p'], "l"); - let _ = misspelled.replace(&['p' , u , s], "l"); + let _ = "hesuo worpd".replace([s, u, 'p'], "l"); - let _ = misspelled.replace(&[p , u , s], "l"); + let _ = "hesuo worpd".replace([s, u, p], "l"); - // NO LINT CASES - let _ = misspelled.replace('s', "l"); + let _ = "hesuo worlp".replace(['s', 'u'], "l").replace('p', "d"); + + let _ = "hesuo worpd".replace('s', "x").replace(['u', 'p'], "l"); - let _ = misspelled.replace(s, "l"); + // Note: Future iterations could lint `replace(|c| matches!(c, "su" | 'd' | 'p'), "l")` + let _ = "hesudo worpd".replace("su", "l").replace(['d', 'p'], "l"); + + let _ = "hesudo worpd".replace([d, 'p'], "l").replace("su", "l"); + + // NO LINT CASES + let _ = "hesuo world".replace('s', "l").replace('u', "p"); - // If the consecutive `str::replace` calls have different `to` arguments, do not lint - let _ = misspelled.replace('s', "l").replace('u', "p"); + let _ = "hesuo worpd".replace('s', "l").replace('p', l); - let _ = misspelled.replace(&get_filter(), "l"); + let _ = "hesuo world".replace([get_filter(), 's'], "l"); - let _ = misspelled.replace(&['s', 'u', 'p'], "l"); + let _ = "hesudo worpd".replace('d', "l").replace("su", "l").replace('p', "l"); - let _ = misspelled.replace(&['s', 'u', 'p'], l); + // Note: Future iterations of `collapsible_str_replace` might lint this and combine to `[s, u, p]` + let _ = "hesuo worpd".replace([s, u], "l").replace([u, p], "l"); - let _ = misspelled.replace(&['s', 'u'], "l").replace(&['u', 'p'], "l"); + let _ = "hesuo worpd".replace(['s', 'u'], "l").replace(['u', 'p'], "l"); - let _ = misspelled.replace('s', "l").replace(&['u', 'p'], "l"); + let _ = "hesuo worpd".replace('s', "l").replace(['u', 'p'], "l"); - let _ = misspelled.replace(&['s', 'u'], "l").replace('p', "l"); + let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l").replace('r', "l"); - let _ = misspelled.replace(&['s', u, 'p'], "l"); + let _ = "hesuo worpd".replace(['s', 'u', 'p'], l).replace('r', l); - let _ = misspelled.replace(&[s, u, 'p'], "l"); + let _ = "hesuo worpd".replace(['s', u, 'p'], "l").replace('r', "l"); - let _ = misspelled.replace(&[s, u, p], "l"); + let _ = "hesuo worpd".replace([s, u], "l").replace(p, "l"); - let _ = misspelled.replace(&[s, u], "l").replace(&[u, p], "l"); + // Regression test + let _ = "hesuo worpd" + .replace('u', iter.next().unwrap()) + .replace('s', iter.next().unwrap()); } diff --git a/tests/ui/collapsible_str_replace.rs b/tests/ui/collapsible_str_replace.rs index 45d9fd87e5e4..52708bc300d4 100644 --- a/tests/ui/collapsible_str_replace.rs +++ b/tests/ui/collapsible_str_replace.rs @@ -2,67 +2,75 @@ #![warn(clippy::collapsible_str_replace)] -fn get_filter() -> &'static str { - "u" +fn get_filter() -> char { + 'u' } fn main() { - let misspelled = "hesuo worpd"; - + let d = 'd'; let p = 'p'; let s = 's'; let u = 'u'; let l = "l"; + let mut iter = ["l", "z"].iter(); + // LINT CASES - let _ = misspelled.replace('s', "l").replace('u', "l"); + let _ = "hesuo worpd".replace('s', "l").replace('u', "l"); - let _ = misspelled.replace('s', l).replace('u', l); + let _ = "hesuo worpd".replace('s', l).replace('u', l); - let _ = misspelled.replace('s', "l").replace('u', "l").replace('p', "l"); + let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l"); - let _ = misspelled + let _ = "hesuo worpd" .replace('s', "l") .replace('u', "l") .replace('p', "l") .replace('d', "l"); - // FALLBACK CASES - // If there are consecutive calls to `str::replace` and all or any chars are variables, - // recommend the fallback `misspelled.replace(&[s, u, p], "l")` - let _ = misspelled.replace(s, "l").replace('u', "l"); + let _ = "hesuo world".replace(s, "l").replace('u', "l"); - let _ = misspelled.replace(s, "l").replace('u', "l").replace('p', "l"); + let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l"); - let _ = misspelled.replace(s, "l").replace(u, "l").replace('p', "l"); + let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l"); - let _ = misspelled.replace(s, "l").replace(u, "l").replace(p, "l"); + let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l"); - // NO LINT CASES - let _ = misspelled.replace('s', "l"); + let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d"); + + let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l"); - let _ = misspelled.replace(s, "l"); + // Note: Future iterations could lint `replace(|c| matches!(c, "su" | 'd' | 'p'), "l")` + let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l"); + + let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l"); + + // NO LINT CASES + let _ = "hesuo world".replace('s', "l").replace('u', "p"); - // If the consecutive `str::replace` calls have different `to` arguments, do not lint - let _ = misspelled.replace('s', "l").replace('u', "p"); + let _ = "hesuo worpd".replace('s', "l").replace('p', l); - let _ = misspelled.replace(&get_filter(), "l"); + let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l"); - let _ = misspelled.replace(&['s', 'u', 'p'], "l"); + let _ = "hesudo worpd".replace('d', "l").replace("su", "l").replace('p', "l"); - let _ = misspelled.replace(&['s', 'u', 'p'], l); + // Note: Future iterations of `collapsible_str_replace` might lint this and combine to `[s, u, p]` + let _ = "hesuo worpd".replace([s, u], "l").replace([u, p], "l"); - let _ = misspelled.replace(&['s', 'u'], "l").replace(&['u', 'p'], "l"); + let _ = "hesuo worpd".replace(['s', 'u'], "l").replace(['u', 'p'], "l"); - let _ = misspelled.replace('s', "l").replace(&['u', 'p'], "l"); + let _ = "hesuo worpd".replace('s', "l").replace(['u', 'p'], "l"); - let _ = misspelled.replace(&['s', 'u'], "l").replace('p', "l"); + let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l").replace('r', "l"); - let _ = misspelled.replace(&['s', u, 'p'], "l"); + let _ = "hesuo worpd".replace(['s', 'u', 'p'], l).replace('r', l); - let _ = misspelled.replace(&[s, u, 'p'], "l"); + let _ = "hesuo worpd".replace(['s', u, 'p'], "l").replace('r', "l"); - let _ = misspelled.replace(&[s, u, p], "l"); + let _ = "hesuo worpd".replace([s, u], "l").replace(p, "l"); - let _ = misspelled.replace(&[s, u], "l").replace(&[u, p], "l"); + // Regression test + let _ = "hesuo worpd" + .replace('u', iter.next().unwrap()) + .replace('s', iter.next().unwrap()); } diff --git a/tests/ui/collapsible_str_replace.stderr b/tests/ui/collapsible_str_replace.stderr index 372fe1da4480..08007bba5f71 100644 --- a/tests/ui/collapsible_str_replace.stderr +++ b/tests/ui/collapsible_str_replace.stderr @@ -1,56 +1,86 @@ error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:18:24 + --> $DIR/collapsible_str_replace.rs:19:27 | -LL | let _ = misspelled.replace('s', "l").replace('u', "l"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(|c| matches!(c, 'u' | 's'), "l")` +LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")` | = note: `-D clippy::collapsible-str-replace` implied by `-D warnings` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:20:24 + --> $DIR/collapsible_str_replace.rs:21:27 | -LL | let _ = misspelled.replace('s', l).replace('u', l); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(|c| matches!(c, 'u' | 's'), l)` +LL | let _ = "hesuo worpd".replace('s', l).replace('u', l); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], l)` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:22:24 + --> $DIR/collapsible_str_replace.rs:23:27 | -LL | let _ = misspelled.replace('s', "l").replace('u', "l").replace('p', "l"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(|c| matches!(c, 'p' | 'u' | 's'), "l")` +LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:25:10 + --> $DIR/collapsible_str_replace.rs:26:10 | LL | .replace('s', "l") | __________^ LL | | .replace('u', "l") LL | | .replace('p', "l") LL | | .replace('d', "l"); - | |__________________________^ help: replace with: `replace(|c| matches!(c, 'd' | 'p' | 'u' | 's'), "l")` + | |__________________________^ help: replace with: `replace(['s', 'u', 'p', 'd'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:33:24 + --> $DIR/collapsible_str_replace.rs:31:27 | -LL | let _ = misspelled.replace(s, "l").replace('u', "l"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(&['u' , s], "l")` +LL | let _ = "hesuo world".replace(s, "l").replace('u', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:35:24 + --> $DIR/collapsible_str_replace.rs:33:27 | -LL | let _ = misspelled.replace(s, "l").replace('u', "l").replace('p', "l"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(&['p' , 'u' , s], "l")` +LL | let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u', 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:37:24 + --> $DIR/collapsible_str_replace.rs:35:27 | -LL | let _ = misspelled.replace(s, "l").replace(u, "l").replace('p', "l"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(&['p' , u , s], "l")` +LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, 'p'], "l")` error: used consecutive `str::replace` call - --> $DIR/collapsible_str_replace.rs:39:24 + --> $DIR/collapsible_str_replace.rs:37:27 | -LL | let _ = misspelled.replace(s, "l").replace(u, "l").replace(p, "l"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(&[p , u , s], "l")` +LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, p], "l")` -error: aborting due to 8 previous errors +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:39:27 + | +LL | let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:41:45 + | +LL | let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['u', 'p'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:44:47 + | +LL | let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['d', 'p'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:46:28 + | +LL | let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([d, 'p'], "l")` + +error: used consecutive `str::replace` call + --> $DIR/collapsible_str_replace.rs:53:27 + | +LL | let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([get_filter(), 's'], "l")` + +error: aborting due to 13 previous errors