diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index c91be33b1cd0..c6d4ef5911ee 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -129,7 +129,7 @@ fn check_manual_split_once_indirect( let ctxt = expr.span.ctxt(); let mut parents = cx.tcx.hir().parent_iter(expr.hir_id); if let (_, Node::LetStmt(local)) = parents.next()? - && let PatKind::Binding(BindingMode::MUT, iter_binding_id, iter_ident, None) = local.pat.kind + && let PatKind::Binding(BindingMode::MUT, iter_binding_id, _, None) = local.pat.kind && let (iter_stmt_id, Node::Stmt(_)) = parents.next()? && let (_, Node::Block(enclosing_block)) = parents.next()? && let mut stmts = enclosing_block @@ -162,16 +162,20 @@ fn check_manual_split_once_indirect( UnwrapKind::Unwrap => ".unwrap()", UnwrapKind::QuestionMark => "?", }; - diag.span_suggestion_verbose( - local.span, - format!("try `{r}split_once`"), - format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), + + // Add a multipart suggestion + diag.multipart_suggestion( + format!("replace with `{r}split_once`"), + vec![ + ( + local.span, + format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), + ), + (first.span, String::new()), // Remove the first usage + (second.span, String::new()), // Remove the second usage + ], app, ); - - let remove_msg = format!("remove the `{iter_ident}` usages"); - diag.span_suggestion(first.span, remove_msg.clone(), "", app); - diag.span_suggestion(second.span, remove_msg, "", app); }); } diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed new file mode 100644 index 000000000000..aaac6a048e1d --- /dev/null +++ b/tests/ui/manual_split_once.fixed @@ -0,0 +1,144 @@ +#![warn(clippy::manual_split_once)] +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] + +extern crate itertools; + +#[allow(unused_imports)] +use itertools::Itertools; + +fn main() { + let _ = "key=value".splitn(2, '=').nth(2); + let _ = "key=value".split_once('=').unwrap().1; + let _ = "key=value".split_once('=').unwrap().1; + let (_, _) = "key=value".split_once('=').unwrap(); + + let s = String::from("key=value"); + let _ = s.split_once('=').unwrap().1; + + let s = Box::::from("key=value"); + let _ = s.split_once('=').unwrap().1; + + let s = &"key=value"; + let _ = s.split_once('=').unwrap().1; + + fn _f(s: &str) -> Option<&str> { + let _ = s.split_once('=')?.1; + let _ = s.split_once('=')?.1; + let _ = s.rsplit_once('=')?.0; + let _ = s.rsplit_once('=')?.0; + None + } + + // Don't lint, slices don't have `split_once` + let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); + + // `rsplitn` gives the results in the reverse order of `rsplit_once` + let _ = "key=value".rsplit_once('=').unwrap().0; + let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap(); + let _ = s.rsplit_once('=').map(|x| x.0); +} + +fn indirect() -> Option<()> { + let (l, r) = "a.b.c".split_once('.').unwrap(); + + + + let (l, r) = "a.b.c".split_once('.')?; + + + + let (l, r) = "a.b.c".rsplit_once('.').unwrap(); + + + + let (l, r) = "a.b.c".rsplit_once('.')?; + + + + // could lint, currently doesn't + + let mut iter = "a.b.c".splitn(2, '.'); + let other = 1; + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let mut mut_binding = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let tuple = (iter.next()?, iter.next()?); + + // should not lint + + let mut missing_unwrap = "a.b.c".splitn(2, '.'); + let l = missing_unwrap.next(); + let r = missing_unwrap.next(); + + let mut mixed_unrap = "a.b.c".splitn(2, '.'); + let unwrap = mixed_unrap.next().unwrap(); + let question_mark = mixed_unrap.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let same_name = iter.next()?; + let same_name = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let shadows_existing = "d"; + let shadows_existing = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let becomes_shadowed = iter.next()?; + let becomes_shadowed = "d"; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + let third_usage = iter.next()?; + + let mut n_three = "a.b.c".splitn(3, '.'); + let l = n_three.next()?; + let r = n_three.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + { + let in_block = iter.next()?; + } + let r = iter.next()?; + + let mut lacks_binding = "a.b.c".splitn(2, '.'); + let _ = lacks_binding.next()?; + let r = lacks_binding.next()?; + + let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); + let l = iter.next()?; + let r = iter.next()?; + + let mut assigned = ""; + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + assigned = iter.next()?; + + None +} + +#[clippy::msrv = "1.51"] +fn _msrv_1_51() { + // `str::split_once` was stabilized in 1.52. Do not lint this + let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); +} + +#[clippy::msrv = "1.52"] +fn _msrv_1_52() { + let _ = "key=value".split_once('=').unwrap().1; + + let (a, b) = "a.b.c".split_once('.').unwrap(); + + +} diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs index e13c827468b8..113e1737c97d 100644 --- a/tests/ui/manual_split_once.rs +++ b/tests/ui/manual_split_once.rs @@ -1,8 +1,6 @@ #![warn(clippy::manual_split_once)] #![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] -//@no-rustfix: need to change the suggestion to a multipart suggestion - extern crate itertools; #[allow(unused_imports)] diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index 566204ad8762..366d860f25ef 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -1,5 +1,5 @@ error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:13:13 + --> tests/ui/manual_split_once.rs:11:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` @@ -8,79 +8,79 @@ LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); = help: to override `-D warnings` add `#[allow(clippy::manual_split_once)]` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:14:13 + --> tests/ui/manual_split_once.rs:12:13 | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:15:18 + --> tests/ui/manual_split_once.rs:13:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=')` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:18:13 + --> tests/ui/manual_split_once.rs:16:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:21:13 + --> tests/ui/manual_split_once.rs:19:13 | LL | let _ = s.splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:24:13 + --> tests/ui/manual_split_once.rs:22:13 | LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:27:17 + --> tests/ui/manual_split_once.rs:25:17 | LL | let _ = s.splitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:28:17 + --> tests/ui/manual_split_once.rs:26:17 | LL | let _ = s.splitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.split_once('=')?.1` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:29:17 + --> tests/ui/manual_split_once.rs:27:17 | LL | let _ = s.rsplitn(2, '=').nth(1)?; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:30:17 + --> tests/ui/manual_split_once.rs:28:17 | LL | let _ = s.rsplitn(2, '=').skip(1).next()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=')?.0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:38:13 + --> tests/ui/manual_split_once.rs:36:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:39:18 + --> tests/ui/manual_split_once.rs:37:18 | LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:40:13 + --> tests/ui/manual_split_once.rs:38:13 | LL | let _ = s.rsplitn(2, '=').nth(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.rsplit_once('=').map(|x| x.0)` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:44:5 + --> tests/ui/manual_split_once.rs:42:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -89,21 +89,15 @@ LL | let l = iter.next().unwrap(); LL | let r = iter.next().unwrap(); | ----------------------------- second usage here | -help: try `split_once` - | -LL | let (l, r) = "a.b.c".split_once('.').unwrap(); +help: replace with `split_once` | -help: remove the `iter` usages - | -LL - let l = iter.next().unwrap(); - | -help: remove the `iter` usages - | -LL - let r = iter.next().unwrap(); +LL ~ let (l, r) = "a.b.c".split_once('.').unwrap(); +LL ~ +LL ~ | error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:48:5 + --> tests/ui/manual_split_once.rs:46:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -112,21 +106,15 @@ LL | let l = iter.next()?; LL | let r = iter.next()?; | --------------------- second usage here | -help: try `split_once` - | -LL | let (l, r) = "a.b.c".split_once('.')?; - | -help: remove the `iter` usages - | -LL - let l = iter.next()?; +help: replace with `split_once` | -help: remove the `iter` usages - | -LL - let r = iter.next()?; +LL ~ let (l, r) = "a.b.c".split_once('.')?; +LL ~ +LL ~ | error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:52:5 + --> tests/ui/manual_split_once.rs:50:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -135,21 +123,15 @@ LL | let r = iter.next().unwrap(); LL | let l = iter.next().unwrap(); | ----------------------------- second usage here | -help: try `rsplit_once` - | -LL | let (l, r) = "a.b.c".rsplit_once('.').unwrap(); - | -help: remove the `iter` usages - | -LL - let r = iter.next().unwrap(); +help: replace with `rsplit_once` | -help: remove the `iter` usages - | -LL - let l = iter.next().unwrap(); +LL ~ let (l, r) = "a.b.c".rsplit_once('.').unwrap(); +LL ~ +LL ~ | error: manual implementation of `rsplit_once` - --> tests/ui/manual_split_once.rs:56:5 + --> tests/ui/manual_split_once.rs:54:5 | LL | let mut iter = "a.b.c".rsplitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -158,27 +140,21 @@ LL | let r = iter.next()?; LL | let l = iter.next()?; | --------------------- second usage here | -help: try `rsplit_once` - | -LL | let (l, r) = "a.b.c".rsplit_once('.')?; - | -help: remove the `iter` usages +help: replace with `rsplit_once` | -LL - let r = iter.next()?; - | -help: remove the `iter` usages - | -LL - let l = iter.next()?; +LL ~ let (l, r) = "a.b.c".rsplit_once('.')?; +LL ~ +LL ~ | error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:141:13 + --> tests/ui/manual_split_once.rs:139:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> tests/ui/manual_split_once.rs:143:5 + --> tests/ui/manual_split_once.rs:141:5 | LL | let mut iter = "a.b.c".splitn(2, '.'); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -187,17 +163,11 @@ LL | let a = iter.next().unwrap(); LL | let b = iter.next().unwrap(); | ----------------------------- second usage here | -help: try `split_once` - | -LL | let (a, b) = "a.b.c".split_once('.').unwrap(); - | -help: remove the `iter` usages - | -LL - let a = iter.next().unwrap(); - | -help: remove the `iter` usages +help: replace with `split_once` | -LL - let b = iter.next().unwrap(); +LL ~ let (a, b) = "a.b.c".split_once('.').unwrap(); +LL ~ +LL ~ | error: aborting due to 19 previous errors