New lint: drop_for_static #16464
Conversation
|
This could be implemented as just: if let ItemKind::Static(_, ident, ..) = item.kind
&& cx.tcx.type_of(item.owner_id.def_id).instantiate_identity().has_drop(cx.tcx, cx.typing_env())
{
// lint
}Note you should be using the ident's span when emitting the lint to avoid an overly large span being printed with the diagnostic. |
|
@Jarcho thank you for your quick response and suggestions ! Just tried your approach, but it does work recursively as I originally intended. static A1: FooWithDrop = FooWithDrop;and does not identify the presence of the static A3: &FooWithDrop = &FooWithDrop;
static A5: (FooWithoutDrop, FooWithDrop) = (FooWithoutDrop, FooWithDrop);
static A7: [FooWithDrop; 1] = [FooWithDrop];
static A9: &[FooWithDrop] = &[FooWithDrop];
etc.Suggested implementation. if let ItemKind::Static(_, _, _, _) = item.kind
&& let ty = cx.tcx.type_of(item.owner_id.def_id).instantiate_identity()
&& has_drop(cx, ty)
{
// lint
}Or maybe I am missing something. |
| if let Some(def_id) = path.res.opt_def_id() { | ||
| let ty = self.cx.tcx.type_of(def_id).instantiate_identity(); | ||
| if has_drop(self.cx, ty) { | ||
| self.drop_for_static_found = true; |
There was a problem hiding this comment.
It could be nice to store the span of at least the first such type, so that the final lint message could put a label1 on it -- something like:
error: static items with drop implementation
--> tests/ui/drop_for_static.rs:36:8
|
LL | static B1: Nested<FooWithDrop> = Nested(FooWithDrop, ());
| ^^ ^^^^^^^^^^^ type with drop implementation here
Or maybe just nest all the way and collect at all of them -- after all, that's what we end up doing when there isn't a type-with-drop. This would reduce the annoyance of the lint triggering again after you fix it
Footnotes
-
using
Diag::span_label↩
There was a problem hiding this comment.
Thank you for suggestion !
Already applied it.
But using span_label makes it like
LL | static A1: FooWithDrop = FooWithDrop;
| ^^ ----------- type with drop implementation hereHave not found an alternative to print exactly how you've mentioned.
There was a problem hiding this comment.
Well done!
Don't worry about the underline type, I just misremembered it:)
Or maybe just nest all the way and collect at all of them -- after all, that's what we end up doing when there isn't a type-with-drop. This would reduce the annoyance of the lint triggering again after you fix it
I still think this would be a good idea.. let's see what the maintainers think
There was a problem hiding this comment.
Yea, I was also thinking about that. And didn't do that because was concerned to not bee too annoying with the lint messages.
But in any case would be happy to add it as you mentioned.
|
Sorry, had had the wrong name. You want |
Co-authored-by: Ada Alakbarova <58857108+ada4a@users.noreply.github.com>
|
@Jarcho got it, will take a look how to use
I am not entirely agree, because it could be the following scenario, for which one we want to emit lint in my opinion. struct FooWithDrop;
impl Drop for FooWithDrop {
fn drop(&mut self) {}
}
static A: &FooWithDrop = &FooWithDrop; |
|
r? clippy |
|
Lintcheck changes for a634456
This comment will be updated if you push new changes |
This case is not due to the type of the static, but due to lifetime extension in the initializer's body. Currently you'll lint on things like static FOO: NeedsDrop = NeedsDrop; // should lint
static FOO_REF: &NeedsDrop = &FOO; // shouldn't lintLinting undropped temporaries in the initializer is fine, but that should probably be it's own lint. |
|
@Jarcho I see - agree, missed that. |
|
@Jarcho so just to clarify static A1: FooWithDrop = FooWithDrop;
static A2: (FooWithoutDrop, FooWithDrop) = (FooWithoutDrop, FooWithDrop);
static A3: [FooWithDrop; 1] = [FooWithDrop];
static A4: Nested<FooWithDrop> = Nested(FooWithDrop, ());
static A5: Nested<FooWithoutDrop, Nested<FooWithDrop>> = Nested(FooWithoutDrop, Nested(FooWithDrop, ()));
static A6: Nested<(FooWithoutDrop, FooWithDrop)> = Nested((FooWithoutDrop, FooWithDrop), ());
static A7: Nested<[FooWithDrop; 1]> = Nested([FooWithDrop], ());And in these we dont static A1: &FooWithDrop = &FooWithDrop;
static A2: &[FooWithDrop] = &[FooWithDrop]; |
|
@Jarcho updated implementation as you suggested, excluding raising a lint for references. But want to add my final word on that one to my original position of raising a lint for references as well, because its still little bit concerns me. static FOO: Mutex<Option<&NeedsDrop>> = Mutex::new(None); // shouldn't lint
static FOO: NeedsDrop = NeedsDrop; // should lint
static FOO_REF: &NeedsDrop = &FOO; // shouldn't lintBut doing so, we are would miss to raise a lint for the following cases: static FOO: Mutex<Option<&NeedsDrop>> = Mutex::new(Some(&NeedsDrop)); // should lint
static FOO_REF: &NeedsDrop = & NeedsDrop; // should lintAnd in such circumstances, in my opinion, as a user, I would choose to sacrifice by raising false positives , but still catching problematic places. Because dealing with false positive is easy, you could just suppress it and that's it, and on the opposite not catching problematic place, could lead to far worse consequences. On the other hand making a separate lint for just this case, not sure that it makes sense, because from my perspective its still tied up with the let foo: &NeedsDrop = &NeedsDrop;But I dont have a strong position here, to fight to the death 😊 Just wanted to raise my concerns and position about it as a active |
|
@Jarcho @ada4a sorry for bothering you, but maybe you can help with it 🙏 I've been trying to raise a lint in such scenarios as well trait FooTrait {
type FooAssoc;
}
struct FooImpl;
impl FooTrait for FooImpl {
type FooAssoc = FooWithDrop;
}
static FOO: <FooImpl as FooTrait>::FooAssoc = FooWithDrop; // should lint
fn main() {}And I am trying to do so by effectively extend matching expression to precess if let Res::Def(... | DefKind::AssocTy, def_id) = path.res {
let ty = self.cx.tcx.type_of(def_id).instantiate_identity();
if has_drop(self.cx, ty) {
...
}
...
}but it becomes panicking inside Is it a correct behaviour ? So what I am trying to say, is it a bug inside |
|
Once the static's type does not need to be dropped using the type is the wrong way to get the result you want. You want to check if the initializer contains a value which both had is lifetime extended to You should be using |
|
@Jarcho Got it.
Could it be an issue that it could return false positive ? Or its not for this case ? |
|
Not in this case. The "might" part has to do with generic parameters which isn't a problem for statics. |
|
@Jarcho updated |
|
Fwiw the visitor allowed us to pinpoint the exact part of the type that has Drop, which made the diagnostics nicer imo. You can see the lost labels on the latest diff. WDYT @Jarcho? |
|
That doesn't work in general. |
|
Ok that's fair. Oh well |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
8729ad3 to
a634456
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. |
View all comments
Description
This PR introduces a new lint to notify users when a static item contains a type that implements the
Droptrait.As per the Rust Reference, static variables have a 'static lifetime and are never destroyed. Consequently, the
Drop::dropmethod is never executed for these items.Rational
In scenarios where
dropis used for critical resource management. Such as closing file handles, releasing external locks, or performing a "graceful" shutdown of a subsystem, relying on a static item can lead to subtle resource leaks or unexpected behaviour. Since the programmer might expect the destructor to run at the end of the program's execution, providing a diagnostic when this won't happen helps prevent "silent" failures in resource cleanup.changelog: new lint:
drop_for_static