Implement default_mismatches_new lint#16531
Conversation
|
r? @Jarcho rustbot has assigned @Jarcho. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
Lintcheck changes for c705c93
This comment will be updated if you push new changes |
Some of the examples from Lintcheck seem to match this pattern where |
Yes, I think we should not lint in this case. There will be a time where it will be possible to have |
There was a problem hiding this comment.
First pass at the review.
The part about default_constructed_unit_structs seems independent. If this is the case, can you isolate it and make a new PR explaining what it fixes? (you can assign it to me)
Also, it would be beneficial to cleanup the content of new_without_default.rs in a first commit. It has been written a long time ago, and, for example, we now import struct names from rustc_hir directly instead of using hir::StructName (for example for hir::ExprKind::Ret, we now use ExprKind::Ret).
Keep in mind that those lints, including the new proposed one, are warned by default, so we have to be extra careful in what we put in there.
|
Reminder, once the PR becomes ready for a review, use |
f656707 to
c0e168f
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
c0e168f to
2979df4
Compare
|
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
bf52143 to
98635f0
Compare
Move is_unit_struct into clippy_utils to use it in the new lint default_mismatches_new
If a type has an auto-derived `Default` trait and a `fn new() -> Self`,
this lint checks if the `new()` method performs custom logic rather
than simply calling the `default()` method.
Users expect the `new()` method to be equivalent to `default()`,
so if the `Default` trait is auto-derived, the `new()` method should
not perform custom logic. Otherwise, there is a risk of different
behavior between the two instantiation methods.
```rust
struct MyStruct(i32);
impl MyStruct {
fn new() -> Self {
Self(42)
}
}
```
Users are unlikely to notice that `MyStruct::new()` and `MyStruct::default()` would produce
different results. The `new()` method should use auto-derived `default()` instead to be consistent:
```rust
struct MyStruct(i32);
impl MyStruct {
fn new() -> Self {
Self::default()
}
}
```
Alternatively, if the `new()` method requires a non-default initialization, implement a custom `Default`.
This also allows you to mark the `new()` implementation as `const`:
```rust
struct MyStruct(i32);
impl MyStruct {
const fn new() -> Self {
Self(42)
}
}
impl Default for MyStruct {
fn default() -> Self {
Self::new()
}
}
```
98635f0 to
c705c93
Compare
Re-opening #14234 Implement #14075. Prevents rust-lang/rust#135977.
What it does
If a type has an auto-derived
Defaulttrait and afn new() -> Self, this lint checks if thenew()method performs custom logic rather than callingSelf::default()Why is this bad?
Users expect the
new()method to be equivalent todefault(), so if theDefaulttrait is auto-derived, thenew()method should not perform custom logic. Otherwise, there is a risk of different behavior between the two instantiation methods.Example
Users are unlikely to notice that
MyStruct::new()andMyStruct::default()would produce different results. Thenew()method should use auto-deriveddefault()instead to be consistent:Alternatively, if the
new()method requires non-default initialization, implement a customDefault. This also allows you to mark thenew()implementation asconst:.stderrfile)cargo testpasses locallycargo dev update_lintscargo dev fmtchangelog: [
default_mismatches_new]: new lint to catchfn new() -> Selfmethod not matching theDefaultimplementationNotable cases in the wild
default()andnew()are actually different - likely was a bug that glob team wants to fix (per comments)new()creates cache withVec::with_capacity(), whereasdefault()usesVec::default(). Possibly a bug.Self(())-- should this be ignored?Self { _p: () }forstruct Identity { _p: () }-- probably better to use::default()TODO/TBD
fn new() -> Selfimplementations are OK? (rather than delegating toSelf::default())Selfis a unit typerustc_hir::hir::ImplItemof afn new() -> Self, get the body of that functionreturn Self::default();and no other logic (in all variants likeDefault::default(), etc.clippy_lints/src/default_constructed_unit_structs.rswhile trying to understand its logic - I think we may want to have ais_unit_type(ty)utility function?r? @samueltardieu
See also: non_canonical_partial_ord_impl lint