Skip to content

Commit 1de41b1

Browse files
committed
Auto merge of #13068 - Jarcho:init_numbered, r=Alexendoo
Rework `init_numbered_fields` Two behaviour changes: * Not linting in macros * Not linting when side effects might be reordered changelog: `init_numbered_fields`: Don't suggest reordering side effects.
2 parents a413281 + 5b7ffa1 commit 1de41b1

File tree

5 files changed

+77
-41
lines changed

5 files changed

+77
-41
lines changed
+52-35
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
use clippy_utils::diagnostics::span_lint_and_sugg;
1+
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
33
use rustc_errors::Applicability;
44
use rustc_hir::def::{DefKind, Res};
55
use rustc_hir::{Expr, ExprKind};
66
use rustc_lint::{LateContext, LateLintPass};
77
use rustc_session::declare_lint_pass;
8+
use rustc_span::SyntaxContext;
89
use std::borrow::Cow;
9-
use std::cmp::Reverse;
10-
use std::collections::BinaryHeap;
1110

1211
declare_clippy_lint! {
1312
/// ### What it does
@@ -44,38 +43,56 @@ declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
4443

4544
impl<'tcx> LateLintPass<'tcx> for NumberedFields {
4645
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
47-
if let ExprKind::Struct(path, fields, None) = e.kind {
48-
if !fields.is_empty()
49-
&& !e.span.from_expansion()
50-
&& fields
51-
.iter()
52-
.all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
53-
&& !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..))
54-
{
55-
let expr_spans = fields
56-
.iter()
57-
.map(|f| (Reverse(f.ident.as_str().parse::<usize>().unwrap()), f.expr.span))
58-
.collect::<BinaryHeap<_>>();
59-
let mut appl = Applicability::MachineApplicable;
60-
let snippet = format!(
61-
"{}({})",
62-
snippet_with_applicability(cx, path.span(), "..", &mut appl),
63-
expr_spans
64-
.into_iter_sorted()
65-
.map(|(_, span)| snippet_with_context(cx, span, path.span().ctxt(), "..", &mut appl).0)
66-
.intersperse(Cow::Borrowed(", "))
67-
.collect::<String>()
68-
);
69-
span_lint_and_sugg(
70-
cx,
71-
INIT_NUMBERED_FIELDS,
72-
e.span,
73-
"used a field initializer for a tuple struct",
74-
"try",
75-
snippet,
76-
appl,
77-
);
78-
}
46+
if let ExprKind::Struct(path, fields @ [field, ..], None) = e.kind
47+
// If the first character of any field is a digit it has to be a tuple.
48+
&& field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit)
49+
// Type aliases can't be used as functions.
50+
&& !matches!(
51+
cx.qpath_res(path, e.hir_id),
52+
Res::Def(DefKind::TyAlias | DefKind::AssocTy, _)
53+
)
54+
// This is the only syntax macros can use that works for all struct types.
55+
&& !e.span.from_expansion()
56+
&& let mut has_side_effects = false
57+
&& let Ok(mut expr_spans) = fields
58+
.iter()
59+
.map(|f| {
60+
has_side_effects |= f.expr.can_have_side_effects();
61+
f.ident.as_str().parse::<usize>().map(|x| (x, f.expr.span))
62+
})
63+
.collect::<Result<Vec<_>, _>>()
64+
// We can only reorder the expressions if there are no side effects.
65+
&& (!has_side_effects || expr_spans.is_sorted_by_key(|&(idx, _)| idx))
66+
{
67+
span_lint_and_then(
68+
cx,
69+
INIT_NUMBERED_FIELDS,
70+
e.span,
71+
"used a field initializer for a tuple struct",
72+
|diag| {
73+
if !has_side_effects {
74+
// We already checked the order if there are side effects.
75+
expr_spans.sort_by_key(|&(idx, _)| idx);
76+
}
77+
let mut app = Applicability::MachineApplicable;
78+
diag.span_suggestion(
79+
e.span,
80+
"use tuple initialization",
81+
format!(
82+
"{}({})",
83+
snippet_with_applicability(cx, path.span(), "..", &mut app),
84+
expr_spans
85+
.into_iter()
86+
.map(
87+
|(_, span)| snippet_with_context(cx, span, SyntaxContext::root(), "..", &mut app).0
88+
)
89+
.intersperse(Cow::Borrowed(", "))
90+
.collect::<String>()
91+
),
92+
app,
93+
);
94+
},
95+
);
7996
}
8097
}
8198
}

clippy_lints/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![feature(f128)]
55
#![feature(f16)]
66
#![feature(if_let_guard)]
7+
#![feature(is_sorted)]
78
#![feature(iter_intersperse)]
89
#![feature(let_chains)]
910
#![feature(never_type)]

tests/ui/numbered_fields.fixed renamed to tests/ui/init_numbered_fields.fixed

+9
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,13 @@ fn main() {
3939
struct TupleStructVec(Vec<usize>);
4040

4141
let _ = TupleStructVec(vec![0, 1, 2, 3]);
42+
43+
{
44+
struct S(i32, i32);
45+
let mut iter = [1i32, 1i32].into_iter();
46+
let _ = S {
47+
1: iter.next().unwrap(),
48+
0: iter.next().unwrap(),
49+
};
50+
}
4251
}

tests/ui/numbered_fields.rs renamed to tests/ui/init_numbered_fields.rs

+9
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,13 @@ fn main() {
4747
struct TupleStructVec(Vec<usize>);
4848

4949
let _ = TupleStructVec { 0: vec![0, 1, 2, 3] };
50+
51+
{
52+
struct S(i32, i32);
53+
let mut iter = [1i32, 1i32].into_iter();
54+
let _ = S {
55+
1: iter.next().unwrap(),
56+
0: iter.next().unwrap(),
57+
};
58+
}
5059
}
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
11
error: used a field initializer for a tuple struct
2-
--> tests/ui/numbered_fields.rs:17:13
2+
--> tests/ui/init_numbered_fields.rs:17:13
33
|
44
LL | let _ = TupleStruct {
55
| _____________^
66
LL | | 0: 1u32,
77
LL | | 1: 42,
88
LL | | 2: 23u8,
99
LL | | };
10-
| |_____^ help: try: `TupleStruct(1u32, 42, 23u8)`
10+
| |_____^ help: use tuple initialization: `TupleStruct(1u32, 42, 23u8)`
1111
|
1212
= note: `-D clippy::init-numbered-fields` implied by `-D warnings`
1313
= help: to override `-D warnings` add `#[allow(clippy::init_numbered_fields)]`
1414

1515
error: used a field initializer for a tuple struct
16-
--> tests/ui/numbered_fields.rs:24:13
16+
--> tests/ui/init_numbered_fields.rs:24:13
1717
|
1818
LL | let _ = TupleStruct {
1919
| _____________^
2020
LL | | 0: 1u32,
2121
LL | | 2: 2u8,
2222
LL | | 1: 3u32,
2323
LL | | };
24-
| |_____^ help: try: `TupleStruct(1u32, 3u32, 2u8)`
24+
| |_____^ help: use tuple initialization: `TupleStruct(1u32, 3u32, 2u8)`
2525

2626
error: used a field initializer for a tuple struct
27-
--> tests/ui/numbered_fields.rs:49:13
27+
--> tests/ui/init_numbered_fields.rs:49:13
2828
|
2929
LL | let _ = TupleStructVec { 0: vec![0, 1, 2, 3] };
30-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `TupleStructVec(vec![0, 1, 2, 3])`
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use tuple initialization: `TupleStructVec(vec![0, 1, 2, 3])`
3131

3232
error: aborting due to 3 previous errors
3333

0 commit comments

Comments
 (0)