Skip to content

Commit 2636a1b

Browse files
committed
Auto merge of rust-lang#7663 - Jarcho:rsplit_once_order, r=llogiq
Fix result order for `manual_split_once` when `rsplitn` is used fixes: rust-lang#7656 changelog: Fix result order for `manual_split_once` when `rsplitn` is used
2 parents e8cd4e5 + 4c1b6a2 commit 2636a1b

File tree

4 files changed

+72
-38
lines changed

4 files changed

+72
-38
lines changed

clippy_lints/src/methods/manual_split_once.rs

+34-36
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,25 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
1717
}
1818

1919
let ctxt = expr.span.ctxt();
20-
let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) {
20+
let (method_name, msg, reverse) = if method_name == "splitn" {
21+
("split_once", "manual implementation of `split_once`", false)
22+
} else {
23+
("rsplit_once", "manual implementation of `rsplit_once`", true)
24+
};
25+
let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
2126
Some(x) => x,
2227
None => return,
2328
};
24-
let (method_name, msg) = if method_name == "splitn" {
25-
("split_once", "manual implementation of `split_once`")
26-
} else {
27-
("rsplit_once", "manual implementation of `rsplit_once`")
28-
};
2929

3030
let mut app = Applicability::MachineApplicable;
3131
let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
3232
let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
3333

34-
match usage.kind {
34+
let sugg = match usage.kind {
3535
IterUsageKind::NextTuple => {
36-
span_lint_and_sugg(
37-
cx,
38-
MANUAL_SPLIT_ONCE,
39-
usage.span,
40-
msg,
41-
"try this",
42-
format!("{}.{}({})", self_snip, method_name, pat_snip),
43-
app,
44-
);
36+
format!("{}.{}({})", self_snip, method_name, pat_snip)
4537
},
38+
IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
4639
IterUsageKind::Next => {
4740
let self_deref = {
4841
let adjust = cx.typeck_results().expr_adjustments(self_arg);
@@ -58,7 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
5851
"*".repeat(adjust.len() - 2)
5952
}
6053
};
61-
let sugg = if usage.unwrap_kind.is_some() {
54+
if usage.unwrap_kind.is_some() {
6255
format!(
6356
"{}.{}({}).map_or({}{}, |x| x.0)",
6457
&self_snip, method_name, pat_snip, self_deref, &self_snip
@@ -68,33 +61,26 @@ pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, se
6861
"Some({}.{}({}).map_or({}{}, |x| x.0))",
6962
&self_snip, method_name, pat_snip, self_deref, &self_snip
7063
)
71-
};
72-
73-
span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
64+
}
7465
},
7566
IterUsageKind::Second => {
7667
let access_str = match usage.unwrap_kind {
7768
Some(UnwrapKind::Unwrap) => ".unwrap().1",
7869
Some(UnwrapKind::QuestionMark) => "?.1",
7970
None => ".map(|x| x.1)",
8071
};
81-
span_lint_and_sugg(
82-
cx,
83-
MANUAL_SPLIT_ONCE,
84-
usage.span,
85-
msg,
86-
"try this",
87-
format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str),
88-
app,
89-
);
72+
format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str)
9073
},
91-
}
74+
};
75+
76+
span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
9277
}
9378

9479
enum IterUsageKind {
9580
Next,
9681
Second,
9782
NextTuple,
83+
RNextTuple,
9884
}
9985

10086
enum UnwrapKind {
@@ -108,10 +94,12 @@ struct IterUsage {
10894
span: Span,
10995
}
11096

97+
#[allow(clippy::too_many_lines)]
11198
fn parse_iter_usage(
11299
cx: &LateContext<'tcx>,
113100
ctxt: SyntaxContext,
114101
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
102+
reverse: bool,
115103
) -> Option<IterUsage> {
116104
let (kind, span) = match iter.next() {
117105
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
@@ -124,20 +112,30 @@ fn parse_iter_usage(
124112
let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
125113

126114
match (&*name.ident.as_str(), args) {
127-
("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Next, e.span),
115+
("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
116+
if reverse {
117+
(IterUsageKind::Second, e.span)
118+
} else {
119+
(IterUsageKind::Next, e.span)
120+
}
121+
},
128122
("next_tuple", []) => {
129-
if_chain! {
123+
return if_chain! {
130124
if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
131125
if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
132126
if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did);
133127
if let ty::Tuple(subs) = subs.type_at(0).kind();
134128
if subs.len() == 2;
135129
then {
136-
return Some(IterUsage { kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None });
130+
Some(IterUsage {
131+
kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
132+
span: e.span,
133+
unwrap_kind: None
134+
})
137135
} else {
138-
return None;
136+
None
139137
}
140-
}
138+
};
141139
},
142140
("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
143141
if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
@@ -158,7 +156,7 @@ fn parse_iter_usage(
158156
}
159157
}
160158
};
161-
match idx {
159+
match if reverse { idx ^ 1 } else { idx } {
162160
0 => (IterUsageKind::Next, span),
163161
1 => (IterUsageKind::Second, span),
164162
_ => return None,

tests/ui/manual_split_once.fixed

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ fn main() {
3636

3737
// Don't lint, slices don't have `split_once`
3838
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
39+
40+
// `rsplitn` gives the results in the reverse order of `rsplit_once`
41+
let _ = "key=value".rsplit_once('=').unwrap().1;
42+
let _ = "key=value".rsplit_once('=').map_or("key=value", |x| x.0);
43+
let _ = "key=value".rsplit_once('=').map(|x| x.1);
44+
let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
3945
}
4046

4147
fn _msrv_1_51() {

tests/ui/manual_split_once.rs

+6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ fn main() {
3636

3737
// Don't lint, slices don't have `split_once`
3838
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
39+
40+
// `rsplitn` gives the results in the reverse order of `rsplit_once`
41+
let _ = "key=value".rsplitn(2, '=').next().unwrap();
42+
let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
43+
let _ = "key=value".rsplitn(2, '=').nth(0);
44+
let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
3945
}
4046

4147
fn _msrv_1_51() {

tests/ui/manual_split_once.stderr

+26-2
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,35 @@ error: manual implementation of `split_once`
7272
LL | let _ = s.splitn(2, "key=value").skip(1).next()?;
7373
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
7474

75+
error: manual implementation of `rsplit_once`
76+
--> $DIR/manual_split_once.rs:41:13
77+
|
78+
LL | let _ = "key=value".rsplitn(2, '=').next().unwrap();
79+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().1`
80+
81+
error: manual implementation of `rsplit_once`
82+
--> $DIR/manual_split_once.rs:42:13
83+
|
84+
LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
85+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map_or("key=value", |x| x.0)`
86+
87+
error: manual implementation of `rsplit_once`
88+
--> $DIR/manual_split_once.rs:43:13
89+
|
90+
LL | let _ = "key=value".rsplitn(2, '=').nth(0);
91+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)`
92+
93+
error: manual implementation of `rsplit_once`
94+
--> $DIR/manual_split_once.rs:44:18
95+
|
96+
LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
97+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
98+
7599
error: manual implementation of `split_once`
76-
--> $DIR/manual_split_once.rs:49:13
100+
--> $DIR/manual_split_once.rs:55:13
77101
|
78102
LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
79103
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
80104

81-
error: aborting due to 13 previous errors
105+
error: aborting due to 17 previous errors
82106

0 commit comments

Comments
 (0)