Skip to content

Commit 5198941

Browse files
authored
chore: use multipart_suggestions for str_splitn (#13789)
This addresses #13099 for the manual_split_once test, using the str_splitn lint. changelog: [str_splitn]: Updated str_splitn to use multipart_suggestions where appropriate
2 parents f83a227 + dcc4025 commit 5198941

File tree

4 files changed

+196
-80
lines changed

4 files changed

+196
-80
lines changed

clippy_lints/src/methods/str_splitn.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ fn check_manual_split_once_indirect(
129129
let ctxt = expr.span.ctxt();
130130
let mut parents = cx.tcx.hir().parent_iter(expr.hir_id);
131131
if let (_, Node::LetStmt(local)) = parents.next()?
132-
&& let PatKind::Binding(BindingMode::MUT, iter_binding_id, iter_ident, None) = local.pat.kind
132+
&& let PatKind::Binding(BindingMode::MUT, iter_binding_id, _, None) = local.pat.kind
133133
&& let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
134134
&& let (_, Node::Block(enclosing_block)) = parents.next()?
135135
&& let mut stmts = enclosing_block
@@ -162,16 +162,20 @@ fn check_manual_split_once_indirect(
162162
UnwrapKind::Unwrap => ".unwrap()",
163163
UnwrapKind::QuestionMark => "?",
164164
};
165-
diag.span_suggestion_verbose(
166-
local.span,
167-
format!("try `{r}split_once`"),
168-
format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"),
165+
166+
// Add a multipart suggestion
167+
diag.multipart_suggestion(
168+
format!("replace with `{r}split_once`"),
169+
vec![
170+
(
171+
local.span,
172+
format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"),
173+
),
174+
(first.span, String::new()), // Remove the first usage
175+
(second.span, String::new()), // Remove the second usage
176+
],
169177
app,
170178
);
171-
172-
let remove_msg = format!("remove the `{iter_ident}` usages");
173-
diag.span_suggestion(first.span, remove_msg.clone(), "", app);
174-
diag.span_suggestion(second.span, remove_msg, "", app);
175179
});
176180
}
177181

tests/ui/manual_split_once.fixed

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#![warn(clippy::manual_split_once)]
2+
#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
3+
4+
extern crate itertools;
5+
6+
#[allow(unused_imports)]
7+
use itertools::Itertools;
8+
9+
fn main() {
10+
let _ = "key=value".splitn(2, '=').nth(2);
11+
let _ = "key=value".split_once('=').unwrap().1;
12+
let _ = "key=value".split_once('=').unwrap().1;
13+
let (_, _) = "key=value".split_once('=').unwrap();
14+
15+
let s = String::from("key=value");
16+
let _ = s.split_once('=').unwrap().1;
17+
18+
let s = Box::<str>::from("key=value");
19+
let _ = s.split_once('=').unwrap().1;
20+
21+
let s = &"key=value";
22+
let _ = s.split_once('=').unwrap().1;
23+
24+
fn _f(s: &str) -> Option<&str> {
25+
let _ = s.split_once('=')?.1;
26+
let _ = s.split_once('=')?.1;
27+
let _ = s.rsplit_once('=')?.0;
28+
let _ = s.rsplit_once('=')?.0;
29+
None
30+
}
31+
32+
// Don't lint, slices don't have `split_once`
33+
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
34+
35+
// `rsplitn` gives the results in the reverse order of `rsplit_once`
36+
let _ = "key=value".rsplit_once('=').unwrap().0;
37+
let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
38+
let _ = s.rsplit_once('=').map(|x| x.0);
39+
}
40+
41+
fn indirect() -> Option<()> {
42+
let (l, r) = "a.b.c".split_once('.').unwrap();
43+
44+
45+
46+
let (l, r) = "a.b.c".split_once('.')?;
47+
48+
49+
50+
let (l, r) = "a.b.c".rsplit_once('.').unwrap();
51+
52+
53+
54+
let (l, r) = "a.b.c".rsplit_once('.')?;
55+
56+
57+
58+
// could lint, currently doesn't
59+
60+
let mut iter = "a.b.c".splitn(2, '.');
61+
let other = 1;
62+
let l = iter.next()?;
63+
let r = iter.next()?;
64+
65+
let mut iter = "a.b.c".splitn(2, '.');
66+
let mut mut_binding = iter.next()?;
67+
let r = iter.next()?;
68+
69+
let mut iter = "a.b.c".splitn(2, '.');
70+
let tuple = (iter.next()?, iter.next()?);
71+
72+
// should not lint
73+
74+
let mut missing_unwrap = "a.b.c".splitn(2, '.');
75+
let l = missing_unwrap.next();
76+
let r = missing_unwrap.next();
77+
78+
let mut mixed_unrap = "a.b.c".splitn(2, '.');
79+
let unwrap = mixed_unrap.next().unwrap();
80+
let question_mark = mixed_unrap.next()?;
81+
82+
let mut iter = "a.b.c".splitn(2, '.');
83+
let same_name = iter.next()?;
84+
let same_name = iter.next()?;
85+
86+
let mut iter = "a.b.c".splitn(2, '.');
87+
let shadows_existing = "d";
88+
let shadows_existing = iter.next()?;
89+
let r = iter.next()?;
90+
91+
let mut iter = "a.b.c".splitn(2, '.');
92+
let becomes_shadowed = iter.next()?;
93+
let becomes_shadowed = "d";
94+
let r = iter.next()?;
95+
96+
let mut iter = "a.b.c".splitn(2, '.');
97+
let l = iter.next()?;
98+
let r = iter.next()?;
99+
let third_usage = iter.next()?;
100+
101+
let mut n_three = "a.b.c".splitn(3, '.');
102+
let l = n_three.next()?;
103+
let r = n_three.next()?;
104+
105+
let mut iter = "a.b.c".splitn(2, '.');
106+
{
107+
let in_block = iter.next()?;
108+
}
109+
let r = iter.next()?;
110+
111+
let mut lacks_binding = "a.b.c".splitn(2, '.');
112+
let _ = lacks_binding.next()?;
113+
let r = lacks_binding.next()?;
114+
115+
let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
116+
let l = iter.next()?;
117+
let r = iter.next()?;
118+
119+
let mut assigned = "";
120+
let mut iter = "a.b.c".splitn(2, '.');
121+
let l = iter.next()?;
122+
assigned = iter.next()?;
123+
124+
None
125+
}
126+
127+
#[clippy::msrv = "1.51"]
128+
fn _msrv_1_51() {
129+
// `str::split_once` was stabilized in 1.52. Do not lint this
130+
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
131+
132+
let mut iter = "a.b.c".splitn(2, '.');
133+
let a = iter.next().unwrap();
134+
let b = iter.next().unwrap();
135+
}
136+
137+
#[clippy::msrv = "1.52"]
138+
fn _msrv_1_52() {
139+
let _ = "key=value".split_once('=').unwrap().1;
140+
141+
let (a, b) = "a.b.c".split_once('.').unwrap();
142+
143+
144+
}

tests/ui/manual_split_once.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#![warn(clippy::manual_split_once)]
22
#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
33

4-
//@no-rustfix: need to change the suggestion to a multipart suggestion
5-
64
extern crate itertools;
75

86
#[allow(unused_imports)]

0 commit comments

Comments
 (0)