-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add lint for recreation of an entire struct #12772
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @y21 (or someone else) some time within the next two weeks. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
|
Hi, is there a way to tell in a late pass whether one or more |
This might be better as an addition to the In particular, this lint also suffers from the same issues that that other lint does, which is that struct reinitialization only requires the fields to be |
Thanks for the pointer, I’ll look into folding it into
``unnecessary_struct_initialization``!
In particular, this lint also suffers from the same issues that
that other lint does, which is that struct reinitialization
only requires the fields to be `Copy`, whereas this lint's
suggestion forces a move of the whole struct, which doesn't
work if the struct is not `Copy`, and is also the reason why
that other lint is in `nursery` (#10547 is the issue).
Would it be sufficient to restrict the lint to ``Copy`` types?
|
I thought about this for a bit and yeah, I think restricting it to only Maybe what we could do to avoid false positives while still warning on some useful cases would be to not emit a warning if:
My reasoning is that if the struct is not However I think for the purposes of this PR it would be easier to just not worry about the possible false positives that had already existed and only extend the Fixing those false positives could be done separately and the lint is in the |
*However* I think for the purposes of this PR it would be
easier to just not worry about the possible false positives
that had already existed and only extend the
`unnecessary_struct_initialization` lint to also catch `Foo {
bar: foo.bar }` (in addition to what it already does).
I’ll take a stab at this.
Fixing those false positives could be done separately and the
lint is in the `nursery` category, so that's fine.
Understood, I’ll save that for another PR.
Thanks once more for the thorough response!
|
cdbb160
to
b12ccb5
Compare
f9f06b3
to
96d4e0b
Compare
Hi again, I believe the PR is in a state that warrants another
look now.
As discussed I folded the lint into ``unnecessary_struct_initialization``
and integrated some further checks that were present there. It
now also handles situations where the new struct is partially
created from individual fields and the remainder from a base.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really nicely written and looks like this addition fits into this lint nicely, a few things and this should be good
snippet(cx, base.span, "..").into_owned(), | ||
rustc_errors::Applicability::MachineApplicable, | ||
); | ||
for f in fields.iter().filter(|f| !f.is_shorthand) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just ignoring/filtering out shorthand initialization seems like it would introduce false positives -- can you add this test case?
struct S1 {
a: i32,
b: i32
}
let a = 42;
let s = S1 { a: 3, b: 4 };
// don't lint: `a` is not from `s`
let s = S1 { a, b: s.b };
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also add a test case involving ranges?
let r = 0..5;
let r = r.start..r.end;
The desugaring to Range { start: r.start, end: r.end }
might emit a warning here, which might actually be good and applicable (although the message might be confusing if it mentions structs)
/// When some fields are assigned from a base struct and others individually | ||
/// the lint applies only if the source of the field is the same as the base. | ||
/// This is enforced here by comparing the path of the base expression; | ||
/// needless to say the link only applies if it (or whatever expression it is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// needless to say the link only applies if it (or whatever expression it is | |
/// needless to say the lint only applies if it (or whatever expression it is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, good catch!
if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && path_to_local(expr_b).is_some() { | ||
// When the type implements `Copy`, a reference to the new struct works on the | ||
// copy. Using the original would borrow it. | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this is pre-existing code (+ a nursery lint) so we can just keep it as is and you can resolve the comment, but this logic doesn't seem entirely correct I think 🤔.
Only the fields need to be Copy
for S { ..base }
, not the type itself:
struct S1 { a: i32, b: i32 }
let mut s = S1 { a: 0, b: 0 };
(&mut S1 { ..s }).b = 1; // incorrect lint, changing to `&mut s` as suggested fails the assertion
assert_eq!(s.b, 0);
96d4e0b
to
78e2854
Compare
I'll change the label here since it looks like you're still working on this (based on red CI and the wip commit), you can change it back with @rustbot author |
67ca1ca
to
ea51d98
Compare
884edc1
to
833d57c
Compare
Just ignoring/filtering out shorthand initialization seems like
it would introduce false positives
Indeed it does, good catch! I dropped that check.
|
Can you also add a test case involving ranges?
```rs
let r = 0..5;
let r = r.start..r.end;
```
The desugaring to `Range { start: r.start, end: r.end }` might emit a warning here, which might actually be good and applicable (although the message might be confusing if it mentions structs)
You’re right, the lint hits:
+error: unnecessary struct building
+ --> tests/ui/unnecessary_struct_initialization.rs:102:14
+ |
+LL | let r2 = r1.start..r1.end;
+ | ^^^^^^^^^^^^^^^^ help: replace with: `r1`
Haven’t looked into recognizing the desugared range here, perhaps
as a follow-up PR?
|
833d57c
to
7312851
Compare
> + if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && path_to_local(expr_b).is_some() {
+ // When the type implements `Copy`, a reference to the new struct works on the
+ // copy. Using the original would borrow it.
+ return false;
I know this is pre-existing code (+ a nursery lint) so we can just keep it as is and you can resolve the comment, but this logic doesn't seem entirely correct I think 🤔.
Only the fields need to be `Copy` for `S { ..base }`, not the type itself:
```rs
struct S1 { a: i32, b: i32 }
let mut s = S1 { a: 0, b: 0 };
(&mut S1 { ..s }).b = 1; // incorrect lint, changing to `&mut s` as suggested fails the assertion
assert_eq!(s.b, 0);
```
In its current form the lint incorrectly triggers both for `Copy`
fields and `Copy` structs, as this example shows. However in my
tests the clippy code path with the comment above is not actually
hit in the example as it is guarded by ``parent_ty.is_any_ptr()``.
IMO I’d rather leave that cleanup to another PR.
Thanks a lot for the review, that was very helpful! Just a heads
up, I won’t be able to look into this more closely in the coming
weeks.
|
4cdf1f7
to
2b13a87
Compare
…ields This lint makes Clippy warn about situations where an owned struct is essentially recreated by moving all its fields into a new instance of the struct. Until now this lint only triggered for structs recreated from a base struct. NB: The new functionality too will cause false positives for the situation where a non-copy struct consisting of all copy members is touched again in subsequent code.
2b13a87
to
296dcf0
Compare
@rustbot ready |
Sorry for taking so long. This all looks good to me now. And yeah, those other things can be left for follow up PRs. If you're interested in making a specialized lint message for ranges specifically as a followup, we could call Either way, thank you for working on this! @bors r+ |
☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test |
This lint makes Clippy warn about situations where an owned struct is
essentially recreated by moving all its fields into a new instance of
the struct. The lint is not machine-applicable because the source
struct may have been partially moved.
This lint originated in something I spotted during peer review. While
working on their branch a colleague ended up with a commit where a
function returned a struct that 1:1 replicated one of its owned inputs
from its members. Initially I suspected they hadn’t run their code
through Clippy but AFAICS there is no lint for this situation yet.
changelog: new lint: [
redundant_owned_struct_recreation
]New lint checklist
.stderr
file)cargo test
passes locallycargo dev update_lints
cargo dev fmt