Skip to content

Commit adba132

Browse files
committed
Auto merge of #8170 - rust-lang:numbered-fields, r=xFrednet
new lint: `init-numbered-fields` This fixes #7985. r? `@xFrednet` --- changelog: new lint: [`init_numbered_fields`]
2 parents 44719b5 + 3ebd2bc commit adba132

11 files changed

+198
-7
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3026,6 +3026,7 @@ Released 2018-09-13
30263026
[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
30273027
[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
30283028
[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
3029+
[`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields
30293030
[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
30303031
[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
30313032
[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::in_macro;
3+
use clippy_utils::source::snippet_with_applicability;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Expr, ExprKind};
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
use std::borrow::Cow;
9+
use std::cmp::Reverse;
10+
use std::collections::BinaryHeap;
11+
12+
declare_clippy_lint! {
13+
/// ### What it does
14+
/// Checks for tuple structs initialized with field syntax.
15+
/// It will however not lint if a base initializer is present.
16+
/// The lint will also ignore code in macros.
17+
///
18+
/// ### Why is this bad?
19+
/// This may be confusing to the uninitiated and adds no
20+
/// benefit as opposed to tuple initializers
21+
///
22+
/// ### Example
23+
/// ```rust
24+
/// struct TupleStruct(u8, u16);
25+
///
26+
/// let _ = TupleStruct {
27+
/// 0: 1,
28+
/// 1: 23,
29+
/// };
30+
///
31+
/// // should be written as
32+
/// let base = TupleStruct(1, 23);
33+
///
34+
/// // This is OK however
35+
/// let _ = TupleStruct { 0: 42, ..base };
36+
/// ```
37+
#[clippy::version = "1.59.0"]
38+
pub INIT_NUMBERED_FIELDS,
39+
style,
40+
"numbered fields in tuple struct initializer"
41+
}
42+
43+
declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
44+
45+
impl<'tcx> LateLintPass<'tcx> for NumberedFields {
46+
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+
&& !in_macro(e.span)
50+
&& fields
51+
.iter()
52+
.all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
53+
{
54+
let expr_spans = fields
55+
.iter()
56+
.map(|f| (Reverse(f.ident.as_str().parse::<usize>().unwrap()), f.expr.span))
57+
.collect::<BinaryHeap<_>>();
58+
let mut appl = Applicability::MachineApplicable;
59+
let snippet = format!(
60+
"{}({})",
61+
snippet_with_applicability(cx, path.span(), "..", &mut appl),
62+
expr_spans
63+
.into_iter_sorted()
64+
.map(|(_, span)| snippet_with_applicability(cx, span, "..", &mut appl))
65+
.intersperse(Cow::Borrowed(", "))
66+
.collect::<String>()
67+
);
68+
span_lint_and_sugg(
69+
cx,
70+
INIT_NUMBERED_FIELDS,
71+
e.span,
72+
"used a field initializer for a tuple struct",
73+
"try this instead",
74+
snippet,
75+
appl,
76+
);
77+
}
78+
}
79+
}
80+
}

clippy_lints/src/lib.register_all.rs

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
8181
LintId::of(infinite_iter::INFINITE_ITER),
8282
LintId::of(inherent_to_string::INHERENT_TO_STRING),
8383
LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
84+
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
8485
LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
8586
LintId::of(int_plus_one::INT_PLUS_ONE),
8687
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),

clippy_lints/src/lib.register_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ store.register_lints(&[
178178
inherent_impl::MULTIPLE_INHERENT_IMPL,
179179
inherent_to_string::INHERENT_TO_STRING,
180180
inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
181+
init_numbered_fields::INIT_NUMBERED_FIELDS,
181182
inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
182183
int_plus_one::INT_PLUS_ONE,
183184
integer_division::INTEGER_DIVISION,

clippy_lints/src/lib.register_style.rs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
2929
LintId::of(functions::MUST_USE_UNIT),
3030
LintId::of(functions::RESULT_UNIT_ERR),
3131
LintId::of(inherent_to_string::INHERENT_TO_STRING),
32+
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
3233
LintId::of(len_zero::COMPARISON_TO_EMPTY),
3334
LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
3435
LintId::of(len_zero::LEN_ZERO),

clippy_lints/src/lib.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
// error-pattern:cargo-clippy
22

3+
#![feature(binary_heap_into_iter_sorted)]
34
#![feature(box_patterns)]
5+
#![feature(control_flow_enum)]
46
#![feature(drain_filter)]
57
#![feature(in_band_lifetimes)]
8+
#![feature(iter_intersperse)]
9+
#![feature(let_else)]
610
#![feature(once_cell)]
711
#![feature(rustc_private)]
812
#![feature(stmt_expr_attributes)]
9-
#![feature(control_flow_enum)]
10-
#![feature(let_else)]
1113
#![recursion_limit = "512"]
1214
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
1315
#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
@@ -242,6 +244,7 @@ mod indexing_slicing;
242244
mod infinite_iter;
243245
mod inherent_impl;
244246
mod inherent_to_string;
247+
mod init_numbered_fields;
245248
mod inline_fn_without_body;
246249
mod int_plus_one;
247250
mod integer_division;
@@ -854,6 +857,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
854857
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
855858
store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
856859
store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
860+
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
857861
// add lints here, do not remove this comment, it's used in `new_lint`
858862
}
859863

clippy_lints/src/macro_use.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::in_macro;
23
use clippy_utils::source::snippet;
34
use hir::def::{DefKind, Res};
45
use if_chain::if_chain;
@@ -8,7 +9,6 @@ use rustc_errors::Applicability;
89
use rustc_hir as hir;
910
use rustc_lint::{LateContext, LateLintPass, LintContext};
1011
use rustc_session::{declare_tool_lint, impl_lint_pass};
11-
use rustc_span::hygiene::ExpnKind;
1212
use rustc_span::{edition::Edition, sym, Span};
1313

1414
declare_clippy_lint! {
@@ -214,7 +214,3 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
214214
}
215215
}
216216
}
217-
218-
fn in_macro(span: Span) -> bool {
219-
span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
220-
}

clippy_utils/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ macro_rules! extract_msrv_attr {
142142
};
143143
}
144144

145+
/// Returns `true` if the span comes from a macro expansion, no matter if from a
146+
/// macro by example or from a procedural macro
147+
#[must_use]
148+
pub fn in_macro(span: Span) -> bool {
149+
span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
150+
}
151+
145152
/// Returns `true` if the two spans come from differing expansions (i.e., one is
146153
/// from a macro and one isn't).
147154
#[must_use]

tests/ui/numbered_fields.fixed

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//run-rustfix
2+
#![warn(clippy::init_numbered_fields)]
3+
4+
#[derive(Default)]
5+
struct TupleStruct(u32, u32, u8);
6+
7+
// This shouldn't lint because it's in a macro
8+
macro_rules! tuple_struct_init {
9+
() => {
10+
TupleStruct { 0: 0, 1: 1, 2: 2 }
11+
};
12+
}
13+
14+
fn main() {
15+
let tuple_struct = TupleStruct::default();
16+
17+
// This should lint
18+
let _ = TupleStruct(1u32, 42, 23u8);
19+
20+
// This should also lint and order the fields correctly
21+
let _ = TupleStruct(1u32, 3u32, 2u8);
22+
23+
// Ok because of default initializer
24+
let _ = TupleStruct { 0: 42, ..tuple_struct };
25+
26+
let _ = TupleStruct {
27+
1: 23,
28+
..TupleStruct::default()
29+
};
30+
31+
// Ok because it's in macro
32+
let _ = tuple_struct_init!();
33+
}

tests/ui/numbered_fields.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//run-rustfix
2+
#![warn(clippy::init_numbered_fields)]
3+
4+
#[derive(Default)]
5+
struct TupleStruct(u32, u32, u8);
6+
7+
// This shouldn't lint because it's in a macro
8+
macro_rules! tuple_struct_init {
9+
() => {
10+
TupleStruct { 0: 0, 1: 1, 2: 2 }
11+
};
12+
}
13+
14+
fn main() {
15+
let tuple_struct = TupleStruct::default();
16+
17+
// This should lint
18+
let _ = TupleStruct {
19+
0: 1u32,
20+
1: 42,
21+
2: 23u8,
22+
};
23+
24+
// This should also lint and order the fields correctly
25+
let _ = TupleStruct {
26+
0: 1u32,
27+
2: 2u8,
28+
1: 3u32,
29+
};
30+
31+
// Ok because of default initializer
32+
let _ = TupleStruct { 0: 42, ..tuple_struct };
33+
34+
let _ = TupleStruct {
35+
1: 23,
36+
..TupleStruct::default()
37+
};
38+
39+
// Ok because it's in macro
40+
let _ = tuple_struct_init!();
41+
}

tests/ui/numbered_fields.stderr

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error: used a field initializer for a tuple struct
2+
--> $DIR/numbered_fields.rs:18:13
3+
|
4+
LL | let _ = TupleStruct {
5+
| _____________^
6+
LL | | 0: 1u32,
7+
LL | | 1: 42,
8+
LL | | 2: 23u8,
9+
LL | | };
10+
| |_____^ help: try this instead: `TupleStruct(1u32, 42, 23u8)`
11+
|
12+
= note: `-D clippy::init-numbered-fields` implied by `-D warnings`
13+
14+
error: used a field initializer for a tuple struct
15+
--> $DIR/numbered_fields.rs:25:13
16+
|
17+
LL | let _ = TupleStruct {
18+
| _____________^
19+
LL | | 0: 1u32,
20+
LL | | 2: 2u8,
21+
LL | | 1: 3u32,
22+
LL | | };
23+
| |_____^ help: try this instead: `TupleStruct(1u32, 3u32, 2u8)`
24+
25+
error: aborting due to 2 previous errors
26+

0 commit comments

Comments
 (0)