Skip to content

Commit f9c1d15

Browse files
committed
Auto merge of #10716 - Icxolu:unitstruct_default_construction, r=Manishearth
Fixes #10609: Adds lint to detect construction of unit struct using `default` Using `default` to construct a unit struct increases code complexity and adds a function call. This can be avoided by simply removing the call to `default` and simply construct by name. changelog: [`default_constructed_unit_structs`]: detects construction of unit structs using `default` fixes #10609
2 parents c2e0d43 + 48ae5a0 commit f9c1d15

16 files changed

+374
-24
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4582,6 +4582,7 @@ Released 2018-09-13
45824582
[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
45834583
[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
45844584
[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
4585+
[`default_constructed_unit_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_constructed_unit_structs
45854586
[`default_instead_of_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_instead_of_iter_empty
45864587
[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
45874588
[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
105105
crate::dbg_macro::DBG_MACRO_INFO,
106106
crate::default::DEFAULT_TRAIT_ACCESS_INFO,
107107
crate::default::FIELD_REASSIGN_WITH_DEFAULT_INFO,
108+
crate::default_constructed_unit_structs::DEFAULT_CONSTRUCTED_UNIT_STRUCTS_INFO,
108109
crate::default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY_INFO,
109110
crate::default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK_INFO,
110111
crate::default_union_representation::DEFAULT_UNION_REPRESENTATION_INFO,
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use clippy_utils::{diagnostics::span_lint_and_sugg, is_from_proc_macro, match_def_path, paths};
2+
use hir::{def::Res, ExprKind};
3+
use rustc_errors::Applicability;
4+
use rustc_hir as hir;
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_middle::ty;
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Check for construction on unit struct using `default`.
12+
///
13+
/// ### Why is this bad?
14+
/// This adds code complexity and an unnecessary function call.
15+
///
16+
/// ### Example
17+
/// ```rust
18+
/// # use std::marker::PhantomData;
19+
/// #[derive(Default)]
20+
/// struct S<T> {
21+
/// _marker: PhantomData<T>
22+
/// }
23+
///
24+
/// let _: S<i32> = S {
25+
/// _marker: PhantomData::default()
26+
/// };
27+
/// ```
28+
/// Use instead:
29+
/// ```rust
30+
/// # use std::marker::PhantomData;
31+
/// struct S<T> {
32+
/// _marker: PhantomData<T>
33+
/// }
34+
///
35+
/// let _: S<i32> = S {
36+
/// _marker: PhantomData
37+
/// };
38+
/// ```
39+
#[clippy::version = "1.71.0"]
40+
pub DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
41+
complexity,
42+
"unit structs can be contructed without calling `default`"
43+
}
44+
declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]);
45+
46+
impl LateLintPass<'_> for DefaultConstructedUnitStructs {
47+
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
48+
if_chain!(
49+
// make sure we have a call to `Default::default`
50+
if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind;
51+
if let ExprKind::Path(ref qpath@ hir::QPath::TypeRelative(_,_)) = fn_expr.kind;
52+
if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
53+
if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
54+
// make sure we have a struct with no fields (unit struct)
55+
if let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind();
56+
if def.is_struct();
57+
if let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant();
58+
if !var.is_field_list_non_exhaustive() && !is_from_proc_macro(cx, expr);
59+
then {
60+
span_lint_and_sugg(
61+
cx,
62+
DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
63+
expr.span.with_lo(qpath.qself_span().hi()),
64+
"use of `default` to create a unit struct",
65+
"remove this call to `default`",
66+
String::new(),
67+
Applicability::MachineApplicable,
68+
)
69+
}
70+
);
71+
}
72+
}

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ mod crate_in_macro_def;
9494
mod create_dir;
9595
mod dbg_macro;
9696
mod default;
97+
mod default_constructed_unit_structs;
9798
mod default_instead_of_iter_empty;
9899
mod default_numeric_fallback;
99100
mod default_union_representation;
@@ -970,6 +971,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
970971
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
971972
store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
972973
store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule));
974+
store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs));
973975
// add lints here, do not remove this comment, it's used in `new_lint`
974976
}
975977

tests/ui/box_default.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@run-rustfix
22
#![warn(clippy::box_default)]
3+
#![allow(clippy::default_constructed_unit_structs)]
34

45
#[derive(Default)]
56
struct ImplementsDefault;

tests/ui/box_default.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@run-rustfix
22
#![warn(clippy::box_default)]
3+
#![allow(clippy::default_constructed_unit_structs)]
34

45
#[derive(Default)]
56
struct ImplementsDefault;

tests/ui/box_default.stderr

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,97 @@
11
error: `Box::new(_)` of default value
2-
--> $DIR/box_default.rs:22:32
2+
--> $DIR/box_default.rs:23:32
33
|
44
LL | let _string: Box<String> = Box::new(Default::default());
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
66
|
77
= note: `-D clippy::box-default` implied by `-D warnings`
88

99
error: `Box::new(_)` of default value
10-
--> $DIR/box_default.rs:23:17
10+
--> $DIR/box_default.rs:24:17
1111
|
1212
LL | let _byte = Box::new(u8::default());
1313
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<u8>::default()`
1414

1515
error: `Box::new(_)` of default value
16-
--> $DIR/box_default.rs:24:16
16+
--> $DIR/box_default.rs:25:16
1717
|
1818
LL | let _vec = Box::new(Vec::<u8>::new());
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<Vec<u8>>::default()`
2020

2121
error: `Box::new(_)` of default value
22-
--> $DIR/box_default.rs:25:17
22+
--> $DIR/box_default.rs:26:17
2323
|
2424
LL | let _impl = Box::new(ImplementsDefault::default());
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<ImplementsDefault>::default()`
2626

2727
error: `Box::new(_)` of default value
28-
--> $DIR/box_default.rs:26:18
28+
--> $DIR/box_default.rs:27:18
2929
|
3030
LL | let _impl2 = Box::new(<ImplementsDefault as Default>::default());
3131
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<ImplementsDefault>::default()`
3232

3333
error: `Box::new(_)` of default value
34-
--> $DIR/box_default.rs:27:42
34+
--> $DIR/box_default.rs:28:42
3535
|
3636
LL | let _impl3: Box<ImplementsDefault> = Box::new(Default::default());
3737
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
3838

3939
error: `Box::new(_)` of default value
40-
--> $DIR/box_default.rs:29:28
40+
--> $DIR/box_default.rs:30:28
4141
|
4242
LL | let _in_macro = outer!(Box::new(String::new()));
4343
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<String>::default()`
4444

4545
error: `Box::new(_)` of default value
46-
--> $DIR/box_default.rs:30:34
46+
--> $DIR/box_default.rs:31:34
4747
|
4848
LL | let _string_default = outer!(Box::new(String::from("")));
4949
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<String>::default()`
5050

5151
error: `Box::new(_)` of default value
52-
--> $DIR/box_default.rs:31:46
52+
--> $DIR/box_default.rs:32:46
5353
|
5454
LL | let _vec2: Box<Vec<ImplementsDefault>> = Box::new(vec![]);
5555
| ^^^^^^^^^^^^^^^^ help: try: `Box::default()`
5656

5757
error: `Box::new(_)` of default value
58-
--> $DIR/box_default.rs:32:33
58+
--> $DIR/box_default.rs:33:33
5959
|
6060
LL | let _vec3: Box<Vec<bool>> = Box::new(Vec::from([]));
6161
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
6262

6363
error: `Box::new(_)` of default value
64-
--> $DIR/box_default.rs:33:25
64+
--> $DIR/box_default.rs:34:25
6565
|
6666
LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0]));
6767
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<Vec<bool>>::default()`
6868

6969
error: `Box::new(_)` of default value
70-
--> $DIR/box_default.rs:35:16
70+
--> $DIR/box_default.rs:36:16
7171
|
7272
LL | call_ty_fn(Box::new(u8::default()));
7373
| ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::default()`
7474

7575
error: `Box::new(_)` of default value
76-
--> $DIR/box_default.rs:40:5
76+
--> $DIR/box_default.rs:41:5
7777
|
7878
LL | Box::new(bool::default())
7979
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<bool>::default()`
8080

8181
error: `Box::new(_)` of default value
82-
--> $DIR/box_default.rs:57:28
82+
--> $DIR/box_default.rs:58:28
8383
|
8484
LL | let _: Box<dyn Read> = Box::new(ImplementsDefault::default());
8585
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<ImplementsDefault>::default()`
8686

8787
error: `Box::new(_)` of default value
88-
--> $DIR/box_default.rs:66:17
88+
--> $DIR/box_default.rs:67:17
8989
|
9090
LL | let _ = Box::new(WeirdPathed::default());
9191
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<WeirdPathed>::default()`
9292

9393
error: `Box::new(_)` of default value
94-
--> $DIR/box_default.rs:78:18
94+
--> $DIR/box_default.rs:79:18
9595
|
9696
LL | Some(Box::new(Foo::default()))
9797
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<Foo>::default()`
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//@run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::default_constructed_unit_structs)]
5+
use std::marker::PhantomData;
6+
7+
#[derive(Default)]
8+
struct UnitStruct;
9+
10+
impl UnitStruct {
11+
fn new() -> Self {
12+
//should lint
13+
Self
14+
}
15+
}
16+
17+
#[derive(Default)]
18+
struct TupleStruct(usize);
19+
20+
impl TupleStruct {
21+
fn new() -> Self {
22+
// should not lint
23+
Self(Default::default())
24+
}
25+
}
26+
27+
// no lint for derived impl
28+
#[derive(Default)]
29+
struct NormalStruct {
30+
inner: PhantomData<usize>,
31+
}
32+
33+
struct NonDefaultStruct;
34+
35+
impl NonDefaultStruct {
36+
fn default() -> Self {
37+
Self
38+
}
39+
}
40+
41+
#[derive(Default)]
42+
enum SomeEnum {
43+
#[default]
44+
Unit,
45+
Tuple(UnitStruct),
46+
Struct {
47+
inner: usize,
48+
},
49+
}
50+
51+
impl NormalStruct {
52+
fn new() -> Self {
53+
// should lint
54+
Self {
55+
inner: PhantomData,
56+
}
57+
}
58+
59+
fn new2() -> Self {
60+
// should not lint
61+
Self {
62+
inner: Default::default(),
63+
}
64+
}
65+
}
66+
67+
#[derive(Default)]
68+
struct GenericStruct<T> {
69+
t: T,
70+
}
71+
72+
impl<T: Default> GenericStruct<T> {
73+
fn new() -> Self {
74+
// should not lint
75+
Self { t: T::default() }
76+
}
77+
78+
fn new2() -> Self {
79+
// should not lint
80+
Self { t: Default::default() }
81+
}
82+
}
83+
84+
struct FakeDefault;
85+
impl FakeDefault {
86+
fn default() -> Self {
87+
Self
88+
}
89+
}
90+
91+
impl Default for FakeDefault {
92+
fn default() -> Self {
93+
Self
94+
}
95+
}
96+
97+
#[derive(Default)]
98+
struct EmptyStruct {}
99+
100+
#[derive(Default)]
101+
#[non_exhaustive]
102+
struct NonExhaustiveStruct;
103+
104+
fn main() {
105+
// should lint
106+
let _ = PhantomData::<usize>;
107+
let _: PhantomData<i32> = PhantomData;
108+
let _ = UnitStruct;
109+
110+
// should not lint
111+
let _ = TupleStruct::default();
112+
let _ = NormalStruct::default();
113+
let _ = NonExhaustiveStruct::default();
114+
let _ = SomeEnum::default();
115+
let _ = NonDefaultStruct::default();
116+
let _ = EmptyStruct::default();
117+
let _ = FakeDefault::default();
118+
let _ = <FakeDefault as Default>::default();
119+
}

0 commit comments

Comments
 (0)