Skip to content

Commit 9940d5f

Browse files
committed
new lint: numbered-fields
1 parent 547efad commit 9940d5f

File tree

9 files changed

+146
-2
lines changed

9 files changed

+146
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3171,6 +3171,7 @@ Released 2018-09-13
31713171
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
31723172
[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
31733173
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
3174+
[`numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#numbered_fields
31743175
[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
31753176
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
31763177
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref

clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
221221
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
222222
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
223223
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
224+
LintId::of(numbered_fields::NUMBERED_FIELDS),
224225
LintId::of(octal_escapes::OCTAL_ESCAPES),
225226
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
226227
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ store.register_lints(&[
382382
non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
383383
non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
384384
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
385+
numbered_fields::NUMBERED_FIELDS,
385386
octal_escapes::OCTAL_ESCAPES,
386387
open_options::NONSENSICAL_OPEN_OPTIONS,
387388
option_env_unwrap::OPTION_ENV_UNWRAP,

clippy_lints/src/lib.register_style.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
8888
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
8989
LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
9090
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
91+
LintId::of(numbered_fields::NUMBERED_FIELDS),
9192
LintId::of(ptr::CMP_NULL),
9293
LintId::of(ptr::PTR_ARG),
9394
LintId::of(ptr_eq::PTR_EQ),

clippy_lints/src/lib.rs

Lines changed: 6 additions & 2 deletions
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)]
@@ -312,6 +314,7 @@ mod non_expressive_names;
312314
mod non_octal_unix_permissions;
313315
mod non_send_fields_in_send_ty;
314316
mod nonstandard_macro_braces;
317+
mod numbered_fields;
315318
mod octal_escapes;
316319
mod open_options;
317320
mod option_env_unwrap;
@@ -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(numbered_fields::NumberedFields));
857861
// add lints here, do not remove this comment, it's used in `new_lint`
858862
}
859863

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet;
3+
use rustc_errors::Applicability;
4+
use rustc_hir::{Expr, ExprKind};
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_session::{declare_lint_pass, declare_tool_lint};
7+
use std::borrow::Cow;
8+
use std::cmp::Reverse;
9+
use std::collections::BinaryHeap;
10+
11+
declare_clippy_lint! {
12+
/// ### What it does
13+
/// Checks for tuple structs initialized with field syntax.
14+
/// It will however not lint if a base initializer is present.
15+
///
16+
/// ### Why is this bad?
17+
/// This may be confusing to the uninitiated and adds no
18+
/// benefit as opposed to tuple initializers
19+
///
20+
/// ### Example
21+
/// ```rust
22+
/// struct TupleStruct(u8, u16);
23+
///
24+
/// let _ = TupleStruct {
25+
/// 0: 1,
26+
/// 1: 23,
27+
/// };
28+
///
29+
/// // should be written as
30+
/// let base = TupleStruct(1, 23);
31+
///
32+
/// // This is OK however
33+
/// let _ = TupleStruct { 0: 42, ..base };
34+
/// ```
35+
#[clippy::version = "1.59.0"]
36+
pub NUMBERED_FIELDS,
37+
style,
38+
"numbered fields in tuple struct initializer"
39+
}
40+
41+
declare_lint_pass!(NumberedFields => [NUMBERED_FIELDS]);
42+
43+
impl<'tcx> LateLintPass<'tcx> for NumberedFields {
44+
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
45+
if let ExprKind::Struct(path, fields, None) = e.kind {
46+
if !fields.is_empty()
47+
&& fields
48+
.iter()
49+
.all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
50+
{
51+
let expr_spans = fields
52+
.iter()
53+
.map(|f| (Reverse(f.ident.as_str().parse::<usize>().unwrap()), f.expr.span))
54+
.collect::<BinaryHeap<_>>();
55+
let snippet = format!(
56+
"{}({})",
57+
//TODO: use snippet_with_applicability throughout
58+
snippet(cx, path.span(), ".."),
59+
expr_spans
60+
.into_iter_sorted()
61+
.map(|(_, span)| snippet(cx, span, ".."))
62+
.intersperse(Cow::Borrowed(", "))
63+
.collect::<String>()
64+
);
65+
span_lint_and_sugg(
66+
cx,
67+
NUMBERED_FIELDS,
68+
e.span,
69+
"used a field initializer for a tuple struct",
70+
"try this instead",
71+
snippet,
72+
Applicability::MachineApplicable,
73+
);
74+
}
75+
}
76+
}
77+
}

tests/ui/numbered_fields.fixed

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//run-rustfix
2+
#![warn(clippy::numbered_fields)]
3+
4+
#[derive(Default)]
5+
struct TupleStruct(u32, u32, u8);
6+
7+
fn main() {
8+
let tuple_struct = TupleStruct::default();
9+
10+
// This should lint
11+
let _ = TupleStruct(1u32, 42, 23u8);
12+
13+
// Ok because of default initializer
14+
let _ = TupleStruct { 0: 42, ..tuple_struct };
15+
16+
let _ = TupleStruct {
17+
1: 23,
18+
..TupleStruct::default()
19+
};
20+
}

tests/ui/numbered_fields.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//run-rustfix
2+
#![warn(clippy::numbered_fields)]
3+
4+
#[derive(Default)]
5+
struct TupleStruct(u32, u32, u8);
6+
7+
fn main() {
8+
let tuple_struct = TupleStruct::default();
9+
10+
// This should lint
11+
let _ = TupleStruct {
12+
0: 1u32,
13+
1: 42,
14+
2: 23u8,
15+
};
16+
17+
// Ok because of default initializer
18+
let _ = TupleStruct { 0: 42, ..tuple_struct };
19+
20+
let _ = TupleStruct {
21+
1: 23,
22+
..TupleStruct::default()
23+
};
24+
}

tests/ui/numbered_fields.stderr

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: used a field initializer for a tuple struct
2+
--> $DIR/numbered_fields.rs:11: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::numbered-fields` implied by `-D warnings`
13+
14+
error: aborting due to previous error
15+

0 commit comments

Comments
 (0)