Skip to content

Commit 826edd7

Browse files
committed
heavily refactor
1 parent 95b24d4 commit 826edd7

File tree

3 files changed

+89
-98
lines changed

3 files changed

+89
-98
lines changed

book/src/lint_configuration.md

+1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ The minimum rust version that the project supports
149149
* [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid)
150150
* [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain)
151151
* [`type_repetition_in_bounds`](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds)
152+
* [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions)
152153

153154

154155
## `cognitive-complexity-threshold`

clippy_lints/src/tuple_array_conversions.rs

+85-95
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ use clippy_utils::{
44
msrvs::{self, Msrv},
55
path_to_local,
66
};
7-
use itertools::Itertools;
87
use rustc_ast::LitKind;
9-
use rustc_hir::{Expr, ExprKind, Node, Pat};
8+
use rustc_hir::{Expr, ExprKind, HirId, Node, Pat};
109
use rustc_lint::{LateContext, LateLintPass, LintContext};
1110
use rustc_middle::{lint::in_external_macro, ty};
1211
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -17,8 +16,8 @@ declare_clippy_lint! {
1716
/// Checks for tuple<=>array conversions that are not done with `.into()`.
1817
///
1918
/// ### Why is this bad?
20-
/// It's overly complex. `.into()` works for tuples<=>arrays with less than 13 elements and
21-
/// conveys the intent a lot better, while also leaving less room for bugs!
19+
/// It's unnecessary complexity. `.into()` works for tuples<=>arrays at or below 12 elements and
20+
/// conveys the intent a lot better, while also leaving less room for hard to spot bugs!
2221
///
2322
/// ### Example
2423
/// ```rust,ignore
@@ -45,26 +44,28 @@ pub struct TupleArrayConversions {
4544
impl LateLintPass<'_> for TupleArrayConversions {
4645
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
4746
if !in_external_macro(cx.sess(), expr.span) && self.msrv.meets(msrvs::TUPLE_ARRAY_CONVERSIONS) {
48-
_ = check_array(cx, expr) || check_tuple(cx, expr);
47+
match expr.kind {
48+
ExprKind::Array(elements) if (1..=12).contains(&elements.len()) => check_array(cx, expr, elements),
49+
ExprKind::Tup(elements) if (1..=12).contains(&elements.len()) => check_tuple(cx, expr, elements),
50+
_ => {},
51+
}
4952
}
5053
}
5154

5255
extract_msrv_attr!(LateContext);
5356
}
5457

55-
fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
56-
let ExprKind::Array(elements) = expr.kind else {
57-
return false;
58-
};
59-
if !(1..=12).contains(&elements.len()) {
60-
return false;
61-
}
62-
63-
if let Some(locals) = path_to_locals(cx, &elements.iter().collect_vec())
64-
&& let [first, rest @ ..] = &*locals
65-
&& let Node::Pat(first_pat) = first
66-
&& let first_id = parent_pat(cx, first_pat).hir_id
67-
&& rest.iter().chain(once(first)).all(|local| {
58+
#[expect(
59+
clippy::blocks_in_if_conditions,
60+
reason = "not a FP, but this is much easier to understand"
61+
)]
62+
fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) {
63+
if should_lint(
64+
cx,
65+
elements,
66+
// This is cursed.
67+
Some,
68+
|(first_id, local)| {
6869
if let Node::Pat(pat) = local
6970
&& let parent = parent_pat(cx, pat)
7071
&& parent.hir_id == first_id
@@ -76,27 +77,18 @@ fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
7677
}
7778

7879
false
79-
})
80-
{
81-
return emit_lint(cx, expr, ToType::Array);
82-
}
83-
84-
if let Some(elements) = elements
85-
.iter()
86-
.enumerate()
87-
.map(|(i, expr)| {
88-
if let ExprKind::Field(path, field) = expr.kind && field.as_str() == i.to_string() {
89-
return Some(path);
90-
};
91-
92-
None
93-
})
94-
.collect::<Option<Vec<&Expr<'_>>>>()
95-
&& let Some(locals) = path_to_locals(cx, &elements)
96-
&& let [first, rest @ ..] = &*locals
97-
&& let Node::Pat(first_pat) = first
98-
&& let first_id = parent_pat(cx, first_pat).hir_id
99-
&& rest.iter().chain(once(first)).all(|local| {
80+
},
81+
) || should_lint(
82+
cx,
83+
elements,
84+
|(i, expr)| {
85+
if let ExprKind::Field(path, field) = expr.kind && field.as_str() == i.to_string() {
86+
return Some((i, path));
87+
};
88+
89+
None
90+
},
91+
|(first_id, local)| {
10092
if let Node::Pat(pat) = local
10193
&& let parent = parent_pat(cx, pat)
10294
&& parent.hir_id == first_id
@@ -108,29 +100,20 @@ fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
108100
}
109101

110102
false
111-
})
112-
{
113-
return emit_lint(cx, expr, ToType::Array);
103+
},
104+
) {
105+
emit_lint(cx, expr, ToType::Array);
114106
}
115-
116-
false
117107
}
118108

109+
#[expect(
110+
clippy::blocks_in_if_conditions,
111+
reason = "not a FP, but this is much easier to understand"
112+
)]
119113
#[expect(clippy::cast_possible_truncation)]
120-
fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
121-
let ExprKind::Tup(elements) = expr.kind else {
122-
return false;
123-
};
124-
if !(1..=12).contains(&elements.len()) {
125-
return false;
126-
};
127-
128-
if let Some(locals) = path_to_locals(cx, &elements.iter().collect_vec())
129-
&& let [first, rest @ ..] = &*locals
130-
&& let Node::Pat(first_pat) = first
131-
&& let first_id = parent_pat(cx, first_pat).hir_id
132-
&& rest.iter().chain(once(first)).all(|local| {
133-
if let Node::Pat(pat) = local
114+
fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: &'tcx [Expr<'tcx>]) {
115+
if should_lint(cx, elements, Some, |(first_id, local)| {
116+
if let Node::Pat(pat) = local
134117
&& let parent = parent_pat(cx, pat)
135118
&& parent.hir_id == first_id
136119
{
@@ -140,32 +123,22 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
140123
);
141124
}
142125

143-
false
144-
})
145-
{
146-
return emit_lint(cx, expr, ToType::Tuple);
147-
}
126+
false
127+
}) || should_lint(
128+
cx,
129+
elements,
130+
|(i, expr)| {
131+
if let ExprKind::Index(path, index) = expr.kind
132+
&& let ExprKind::Lit(lit) = index.kind
133+
&& let LitKind::Int(val, _) = lit.node
134+
&& val as usize == i
135+
{
136+
return Some((i, path));
137+
};
148138

149-
if let Some(elements) = elements
150-
.iter()
151-
.enumerate()
152-
.map(|(i, expr)| {
153-
if let ExprKind::Index(path, index) = expr.kind
154-
&& let ExprKind::Lit(lit) = index.kind
155-
&& let LitKind::Int(val, _) = lit.node
156-
&& val as usize == i
157-
{
158-
return Some(path);
159-
};
160-
161-
None
162-
})
163-
.collect::<Option<Vec<&Expr<'_>>>>()
164-
&& let Some(locals) = path_to_locals(cx, &elements)
165-
&& let [first, rest @ ..] = &*locals
166-
&& let Node::Pat(first_pat) = first
167-
&& let first_id = parent_pat(cx, first_pat).hir_id
168-
&& rest.iter().chain(once(first)).all(|local| {
139+
None
140+
},
141+
|(first_id, local)| {
169142
if let Node::Pat(pat) = local
170143
&& let parent = parent_pat(cx, pat)
171144
&& parent.hir_id == first_id
@@ -177,12 +150,10 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
177150
}
178151

179152
false
180-
})
181-
{
182-
return emit_lint(cx, expr, ToType::Tuple);
153+
},
154+
) {
155+
emit_lint(cx, expr, ToType::Tuple);
183156
}
184-
185-
false
186157
}
187158

188159
/// Walks up the `Pat` until it's reached the final containing `Pat`.
@@ -198,13 +169,6 @@ fn parent_pat<'tcx>(cx: &LateContext<'tcx>, start: &'tcx Pat<'tcx>) -> &'tcx Pat
198169
end
199170
}
200171

201-
fn path_to_locals<'tcx>(cx: &LateContext<'tcx>, exprs: &[&'tcx Expr<'tcx>]) -> Option<Vec<Node<'tcx>>> {
202-
exprs
203-
.iter()
204-
.map(|element| path_to_local(element).and_then(|local| cx.tcx.hir().find(local)))
205-
.collect()
206-
}
207-
208172
#[derive(Clone, Copy)]
209173
enum ToType {
210174
Array,
@@ -243,3 +207,29 @@ fn emit_lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, to_type: ToTy
243207

244208
false
245209
}
210+
211+
fn should_lint<'tcx>(
212+
cx: &LateContext<'tcx>,
213+
elements: &'tcx [Expr<'tcx>],
214+
map: impl FnMut((usize, &'tcx Expr<'tcx>)) -> Option<(usize, &Expr<'_>)>,
215+
predicate: impl FnMut((HirId, &Node<'tcx>)) -> bool,
216+
) -> bool {
217+
if let Some(elements) = elements
218+
.iter()
219+
.enumerate()
220+
.map(map)
221+
.collect::<Option<Vec<_>>>()
222+
&& let Some(locals) = elements
223+
.iter()
224+
.map(|(_, element)| path_to_local(element).and_then(|local| cx.tcx.hir().find(local)))
225+
.collect::<Option<Vec<_>>>()
226+
&& let [first, rest @ ..] = &*locals
227+
&& let Node::Pat(first_pat) = first
228+
&& let parent = parent_pat(cx, first_pat).hir_id
229+
&& rest.iter().chain(once(first)).map(|i| (parent, i)).all(predicate)
230+
{
231+
return true;
232+
}
233+
234+
false
235+
}

tests/ui/tuple_array_conversions.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,23 @@ LL | t1.iter().for_each(|&(a, b)| _ = [a, b]);
5656
= help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed
5757

5858
error: it looks like you're trying to convert an array to a tuple
59-
--> $DIR/tuple_array_conversions.rs:71:13
59+
--> $DIR/tuple_array_conversions.rs:69:13
6060
|
6161
LL | let x = (x[0], x[1]);
6262
| ^^^^^^^^^^^^
6363
|
6464
= help: use `.into()` instead, or `<(T0, T1, ..., Tn)>::from` if type annotations are needed
6565

6666
error: it looks like you're trying to convert a tuple to an array
67-
--> $DIR/tuple_array_conversions.rs:72:13
67+
--> $DIR/tuple_array_conversions.rs:70:13
6868
|
6969
LL | let x = [x.0, x.1];
7070
| ^^^^^^^^^^
7171
|
7272
= help: use `.into()` instead, or `<[T; N]>::from` if type annotations are needed
7373

7474
error: it looks like you're trying to convert an array to a tuple
75-
--> $DIR/tuple_array_conversions.rs:74:13
75+
--> $DIR/tuple_array_conversions.rs:72:13
7676
|
7777
LL | let x = (x[0], x[1]);
7878
| ^^^^^^^^^^^^

0 commit comments

Comments
 (0)