Skip to content

Commit c634158

Browse files
committed
wip
1 parent b69304a commit c634158

6 files changed

+440
-156
lines changed

clippy_lints/src/default_constructed_unit_structs.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,30 +46,23 @@ declare_clippy_lint! {
4646
}
4747
declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]);
4848

49-
fn is_alias(ty: hir::Ty<'_>) -> bool {
50-
if let hir::TyKind::Path(ref qpath) = ty.kind {
51-
is_ty_alias(qpath)
52-
} else {
53-
false
54-
}
55-
}
56-
5749
impl LateLintPass<'_> for DefaultConstructedUnitStructs {
5850
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
5951
if let ExprKind::Call(fn_expr, &[]) = expr.kind
6052
// make sure we have a call to `Default::default`
6153
&& let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind
6254
// make sure this isn't a type alias:
6355
// `<Foo as Bar>::Assoc` cannot be used as a constructor
64-
&& !is_alias(*base)
56+
&& !matches!(base.kind, hir::TyKind::Path(ref qpath) if is_ty_alias(qpath))
6557
&& let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
6658
&& cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
6759
// make sure we have a struct with no fields (unit struct)
6860
&& let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind()
6961
&& def.is_struct()
7062
&& let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant()
7163
&& !var.is_field_list_non_exhaustive()
72-
&& !expr.span.from_expansion() && !qpath.span().from_expansion()
64+
&& !expr.span.from_expansion()
65+
&& !qpath.span().from_expansion()
7366
{
7467
span_lint_and_sugg(
7568
cx,

clippy_lints/src/new_without_default.rs

Lines changed: 72 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use clippy_utils::diagnostics::span_lint_hir_and_then;
2-
use clippy_utils::source::snippet;
2+
use clippy_utils::source::{snippet, trim_span};
33
use clippy_utils::sugg::DiagExt;
44
use clippy_utils::{is_default_equivalent_call, return_ty};
55
use rustc_errors::Applicability;
66
use rustc_hir as hir;
77
use rustc_hir::HirIdMap;
88
use rustc_lint::{LateContext, LateLintPass, LintContext};
9-
use rustc_middle::ty::Ty;
9+
use rustc_middle::ty::{Adt, Ty, VariantDef};
1010
use rustc_session::impl_lint_pass;
11-
use rustc_span::sym;
11+
use rustc_span::{BytePos, Pos as _, Span, sym};
1212

1313
declare_clippy_lint! {
1414
/// ### What it does
@@ -199,30 +199,13 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
199199
return;
200200
}
201201
suggest_new_without_default(cx, item, impl_item, id, self_ty, generics, impl_self_ty);
202-
} else if let hir::ExprKind::Block(block, _) = cx.tcx.hir().body(body_id).value.kind {
202+
} else if let hir::ExprKind::Block(block, _) = cx.tcx.hir().body(body_id).value.kind
203+
&& !is_unit_struct(cx, self_ty)
204+
{
203205
// this type has an automatically derived `Default` implementation
204206
// check if `new` and `default` are equivalent
205-
if !check_block_calls_default(cx, block) {
206-
let self_ty_fmt = self_ty.to_string();
207-
let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt);
208-
span_lint_hir_and_then(
209-
cx,
210-
DEFAULT_MISMATCHES_NEW,
211-
id.into(),
212-
impl_item.span,
213-
format!(
214-
"you should consider delegating to the auto-derived `Default` for `{self_type_snip}`"
215-
),
216-
|diag| {
217-
diag.suggest_prepend_item(
218-
cx,
219-
block.span,
220-
"try using this",
221-
&"Self::default()",
222-
Applicability::MaybeIncorrect,
223-
);
224-
},
225-
);
207+
if let Some(span) = check_block_calls_default(cx, block) {
208+
suggest_default_mismatch_new(cx, span, id, block, self_ty, impl_self_ty);
226209
}
227210
}
228211
}
@@ -233,36 +216,56 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
233216
}
234217
}
235218

219+
// Check if Self is a unit struct, and avoid any kind of suggestions
220+
// FIXME: this was copied from DefaultConstructedUnitStructs,
221+
// and should be refactored into a common function
222+
fn is_unit_struct(_cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
223+
if let Adt(def, ..) = ty.kind()
224+
&& def.is_struct()
225+
&& let var @ VariantDef {
226+
ctor: Some((hir::def::CtorKind::Const, _)),
227+
..
228+
} = def.non_enum_variant()
229+
&& !var.is_field_list_non_exhaustive()
230+
{
231+
true
232+
} else {
233+
false
234+
}
235+
}
236+
236237
/// Check if a block contains one of these:
237238
/// - Empty block with an expr (e.g., `{ Self::default() }`)
238239
/// - One statement (e.g., `{ return Self::default(); }`)
239-
fn check_block_calls_default(cx: &LateContext<'_>, block: &hir::Block<'_>) -> bool {
240+
fn check_block_calls_default(cx: &LateContext<'_>, block: &hir::Block<'_>) -> Option<Span> {
240241
if let Some(expr) = block.expr
241242
&& block.stmts.is_empty()
242243
{
243244
// Check the block's trailing expression (e.g., `Self::default()`)
244-
check_expr_call_default(cx, expr)
245+
if check_expr_call_default(cx, expr) {
246+
return None;
247+
}
245248
} else if let [hir::Stmt { kind, .. }] = block.stmts
246249
&& let hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr) = kind
250+
&& let hir::ExprKind::Ret(Some(ret_expr)) = expr.kind
247251
{
248-
// Check if the single statement is a return or expr
249-
match expr.kind {
250-
// Case 1: `return Self::default();`
251-
hir::ExprKind::Ret(Some(ret_expr)) => check_expr_call_default(cx, ret_expr),
252-
// Case 2: `Self::default()` (possibly inside a block)
253-
hir::ExprKind::Block(block, _) => check_block_calls_default(cx, block),
254-
// Case 3: Direct call (e.g., `Self::default()` as the entire body)
255-
_ => check_expr_call_default(cx, expr),
252+
if check_expr_call_default(cx, ret_expr) {
253+
return None;
256254
}
257-
} else {
258-
false
259255
}
256+
257+
// trim first and last character, and trim spaces
258+
let mut span = block.span;
259+
span = span.with_lo(span.lo() + BytePos::from_usize(1));
260+
span = span.with_hi(span.hi() - BytePos::from_usize(1));
261+
span = trim_span(cx.sess().source_map(), span);
262+
263+
Some(span)
260264
}
261265

262266
/// Check for `Self::default()` call syntax or equivalent
263267
fn check_expr_call_default(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
264-
if let hir::ExprKind::Call(callee, args) = expr.kind
265-
&& args.is_empty()
268+
if let hir::ExprKind::Call(callee, &[]) = expr.kind
266269
// FIXME: does this include `Self { }` style calls, which is equivalent,
267270
// but not the same as `Self::default()`?
268271
// FIXME: what should the whole_call_expr (3rd arg) be?
@@ -274,6 +277,36 @@ fn check_expr_call_default(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
274277
}
275278
}
276279

280+
fn suggest_default_mismatch_new<'tcx>(
281+
cx: &LateContext<'tcx>,
282+
span: Span,
283+
id: rustc_hir::OwnerId,
284+
block: &rustc_hir::Block<'_>,
285+
self_ty: Ty<'tcx>,
286+
impl_self_ty: &&rustc_hir::Ty<'_>,
287+
) {
288+
let self_ty_fmt = self_ty.to_string();
289+
let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt);
290+
span_lint_hir_and_then(
291+
cx,
292+
DEFAULT_MISMATCHES_NEW,
293+
id.into(),
294+
block.span,
295+
format!("you should consider delegating to the auto-derived `Default` for `{self_type_snip}`"),
296+
|diag| {
297+
// This would replace any comments, and we could work around the first comment,
298+
// but in case of a block of code with multiple statements and comment lines,
299+
// we can't do much. For now, we always mark this as a MaybeIncorrect suggestion.
300+
diag.span_suggestion(
301+
span,
302+
"try using this",
303+
&"Self::default()",
304+
Applicability::MaybeIncorrect,
305+
);
306+
},
307+
);
308+
}
309+
277310
fn suggest_new_without_default<'tcx>(
278311
cx: &LateContext<'tcx>,
279312
item: &hir::Item<'_>,

tests/ui/default_constructed_unit_structs.stderr

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
block.expr = {
2+
//should lint
3+
Self::default()
4+
//~^ default_constructed_unit_structs
5+
}
16
error: use of `default` to create a unit struct
27
--> tests/ui/default_constructed_unit_structs.rs:11:13
38
|
@@ -7,12 +12,66 @@ LL | Self::default()
712
= note: `-D clippy::default-constructed-unit-structs` implied by `-D warnings`
813
= help: to override `-D warnings` add `#[allow(clippy::default_constructed_unit_structs)]`
914

15+
block.expr = {
16+
// should not lint
17+
Self(Default::default())
18+
}
19+
error: you should consider delegating to the auto-derived `Default` for `TupleStruct`
20+
--> tests/ui/default_constructed_unit_structs.rs:20:22
21+
|
22+
LL | fn new() -> Self {
23+
| _______________________^
24+
LL | |/ // should not lint
25+
LL | || Self(Default::default())
26+
| ||________________________________- help: try using this: `Self::default()`
27+
LL | | }
28+
| |______^
29+
|
30+
= note: `-D clippy::default-mismatches-new` implied by `-D warnings`
31+
= help: to override `-D warnings` add `#[allow(clippy::default_mismatches_new)]`
32+
33+
block.expr = {
34+
// should lint
35+
Self {
36+
inner: PhantomData::default(),
37+
//~^ default_constructed_unit_structs
38+
}
39+
}
40+
error: you should consider delegating to the auto-derived `Default` for `NormalStruct`
41+
--> tests/ui/default_constructed_unit_structs.rs:51:22
42+
|
43+
LL | fn new() -> Self {
44+
| _______________________^
45+
LL | |/ // should lint
46+
LL | || Self {
47+
LL | || inner: PhantomData::default(),
48+
LL | ||
49+
LL | || }
50+
| ||_________- help: try using this: `Self::default()`
51+
LL | | }
52+
| |______^
53+
1054
error: use of `default` to create a unit struct
1155
--> tests/ui/default_constructed_unit_structs.rs:54:31
1256
|
1357
LL | inner: PhantomData::default(),
1458
| ^^^^^^^^^^^ help: remove this call to `default`
1559

60+
block.expr = {
61+
// should not lint
62+
Self { t: T::default() }
63+
}
64+
error: you should consider delegating to the auto-derived `Default` for `GenericStruct<T>`
65+
--> tests/ui/default_constructed_unit_structs.rs:73:22
66+
|
67+
LL | fn new() -> Self {
68+
| _______________________^
69+
LL | |/ // should not lint
70+
LL | || Self { t: T::default() }
71+
| ||________________________________- help: try using this: `Self::default()`
72+
LL | | }
73+
| |______^
74+
1675
error: use of `default` to create a unit struct
1776
--> tests/ui/default_constructed_unit_structs.rs:128:33
1877
|
@@ -37,5 +96,5 @@ error: use of `default` to create a unit struct
3796
LL | let _ = UnitStruct::default();
3897
| ^^^^^^^^^^^ help: remove this call to `default`
3998

40-
error: aborting due to 6 previous errors
99+
error: aborting due to 9 previous errors
41100

0 commit comments

Comments
 (0)