Skip to content

Commit 85c6663

Browse files
committed
struct_fields_rest_default will reject any base value used.
1 parent b11506a commit 85c6663

File tree

4 files changed

+80
-34
lines changed

4 files changed

+80
-34
lines changed

clippy_lints/src/struct_fields_rest_default.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
use clippy_utils::diagnostics::span_lint_and_help;
2+
use clippy_utils::source::snippet;
23
use rustc_ast::Expr;
34
use rustc_ast::ast::{ExprKind, StructRest};
45
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
56
use rustc_session::declare_lint_pass;
67

78
declare_clippy_lint! {
89
/// ### What it does
9-
/// Check struct initialization uses `..Default::default()` pattern to skip rest of struct field initialization.
10+
/// Check struct initialization uses `..base` pattern to skip rest of struct field initialization.
1011
///
1112
/// ### Why restrict this?
12-
/// Using `..Default::default()`` can hide field initialization when new fields are added to structs,
13+
/// Using `..base` can hide field initialization when new fields are added to structs,
1314
/// potentially leading to bugs where developers forget to explicitly set values for new fields.
1415
///
1516
/// ### Example
@@ -55,20 +56,18 @@ declare_lint_pass!(StructFieldsDefault => [STRUCT_FIELDS_REST_DEFAULT]);
5556

5657
impl EarlyLintPass for StructFieldsDefault {
5758
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
58-
if let ExprKind::Struct(struct_expr) = &expr.kind
59+
if !expr.span.in_external_macro(cx.sess().source_map())
60+
&& let ExprKind::Struct(struct_expr) = &expr.kind
5961
&& let StructRest::Base(base) = &struct_expr.rest
60-
&& !base.span.in_external_macro(cx.sess().source_map())
61-
&& let ExprKind::Call(call, _) = &base.kind
62-
&& let ExprKind::Path(_, path) = &call.kind
63-
&& let [part1, part2] = path.segments.as_slice()
64-
&& part1.ident.name == rustc_span::sym::Default
65-
&& part2.ident.name == rustc_span::kw::Default
6662
{
6763
span_lint_and_help(
6864
cx,
6965
STRUCT_FIELDS_REST_DEFAULT,
7066
base.span,
71-
"should not use `..Default::default()` to omit rest of struct field initialization",
67+
format!(
68+
"should not use `..{}` to omit rest of struct field initialization",
69+
snippet(cx, base.span, "..")
70+
),
7271
Some(expr.span),
7372
"each field's initial value should be explicitly specified",
7473
);

tests/ui/auxiliary/external_macro.rs

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/ui/struct_fields_rest_default.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
1-
//@aux-build:external_macro.rs
1+
//@aux-build:proc_macros.rs
22
#![warn(clippy::struct_fields_rest_default)]
3-
4-
extern crate external_macro;
5-
6-
use external_macro::external_struct_rest_default;
3+
extern crate proc_macros;
74

85
#[derive(Default)]
96
struct Foo {
@@ -12,6 +9,12 @@ struct Foo {
129
c: i32,
1310
}
1411

12+
impl Foo {
13+
fn get_foo() -> Self {
14+
Foo { a: 0, b: 0, c: 0 }
15+
}
16+
}
17+
1518
fn main() {
1619
#[rustfmt::skip]
1720
let _ = Foo {
@@ -20,8 +23,33 @@ fn main() {
2023
//~^ struct_fields_rest_default
2124
};
2225

26+
#[rustfmt::skip]
27+
let _ = Foo {
28+
a: 10,
29+
..Foo::default()
30+
//~^ struct_fields_rest_default
31+
};
32+
33+
#[rustfmt::skip]
34+
let _ = Foo {
35+
a: 10,
36+
..Foo::get_foo()
37+
//~^ struct_fields_rest_default
38+
};
39+
2340
// should not lint in external macro
24-
external_struct_rest_default!();
41+
proc_macros::external! {
42+
#[derive(Default)]
43+
struct ExternalDefault {
44+
a: i32,
45+
b: i32,
46+
}
47+
48+
let _ = ExternalDefault {
49+
a: 10,
50+
..Default::default()
51+
};
52+
}
2553

2654
// should not lint
2755
let _ = Foo {

tests/ui/struct_fields_rest_default.stderr

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error: should not use `..Default::default()` to omit rest of struct field initialization
2-
--> tests/ui/struct_fields_rest_default.rs:19:11
2+
--> tests/ui/struct_fields_rest_default.rs:22:11
33
|
44
LL | ..Default::default()
55
| ^^^^^^^^^^^^^^^^^^
66
|
77
help: each field's initial value should be explicitly specified
8-
--> tests/ui/struct_fields_rest_default.rs:17:13
8+
--> tests/ui/struct_fields_rest_default.rs:20:13
99
|
1010
LL | let _ = Foo {
1111
| _____________^
@@ -17,5 +17,39 @@ LL | | };
1717
= note: `-D clippy::struct-fields-rest-default` implied by `-D warnings`
1818
= help: to override `-D warnings` add `#[allow(clippy::struct_fields_rest_default)]`
1919

20-
error: aborting due to 1 previous error
20+
error: should not use `..Foo::default()` to omit rest of struct field initialization
21+
--> tests/ui/struct_fields_rest_default.rs:29:11
22+
|
23+
LL | ..Foo::default()
24+
| ^^^^^^^^^^^^^^
25+
|
26+
help: each field's initial value should be explicitly specified
27+
--> tests/ui/struct_fields_rest_default.rs:27:13
28+
|
29+
LL | let _ = Foo {
30+
| _____________^
31+
LL | | a: 10,
32+
LL | | ..Foo::default()
33+
LL | |
34+
LL | | };
35+
| |_____^
36+
37+
error: should not use `..Foo::get_foo()` to omit rest of struct field initialization
38+
--> tests/ui/struct_fields_rest_default.rs:36:11
39+
|
40+
LL | ..Foo::get_foo()
41+
| ^^^^^^^^^^^^^^
42+
|
43+
help: each field's initial value should be explicitly specified
44+
--> tests/ui/struct_fields_rest_default.rs:34:13
45+
|
46+
LL | let _ = Foo {
47+
| _____________^
48+
LL | | a: 10,
49+
LL | | ..Foo::get_foo()
50+
LL | |
51+
LL | | };
52+
| |_____^
53+
54+
error: aborting due to 3 previous errors
2155

0 commit comments

Comments
 (0)