Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6460,6 +6460,7 @@ Released 2018-09-13
[`drain_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#drain_collect
[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
[`drop_for_static`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_for_static
[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
crate::doc::TOO_LONG_FIRST_DOC_PARAGRAPH_INFO,
crate::doc::UNNECESSARY_SAFETY_DOC_INFO,
crate::double_parens::DOUBLE_PARENS_INFO,
crate::drop_for_static::DROP_FOR_STATIC_INFO,
crate::drop_forget_ref::DROP_NON_DROP_INFO,
crate::drop_forget_ref::FORGET_NON_DROP_INFO,
crate::drop_forget_ref::MEM_FORGET_INFO,
Expand Down
87 changes: 87 additions & 0 deletions clippy_lints/src/drop_for_static.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::has_drop;
use rustc_hir::intravisit::{Visitor, walk_path};
use rustc_hir::{HirId, Item, ItemKind, Path};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_session::declare_lint_pass;

declare_clippy_lint! {
/// ### What it does
/// Checks for `static` variables whose types implement `Drop`.
///
/// ### Why restrict this?
/// Rust does not call `Drop::drop` for `static` variables at the end of a program's
/// execution. If a type relies on its `Drop` implementation to release resources
/// (like closing files, releasing locks, or deleting temporary files), these
/// actions will never occur for a `static` instance, potentially leading to
/// resource leaks or inconsistent state.
///
/// ### Example
/// ```rust
/// struct Logger;
///
/// impl Drop for Logger {
/// fn drop(&mut self) {
/// println!("Closing log file...");
/// }
/// }
///
/// static GLOBAL_LOGGER: Logger = Logger;
/// ```
///
/// ### Known problems
/// If the type is intended to exist for the lifetime of the program and the
/// resource is automatically reclaimed by the operating system (like memory),
/// this lint may be noisy. However, it still serves as a useful reminder that
/// the `drop` logic will not execute.
#[clippy::version = "1.93.0"]
pub DROP_FOR_STATIC,
nursery,
"static items with a type that implements 'Drop'"
}
declare_lint_pass!(DropForStatic => [DROP_FOR_STATIC]);

impl LateLintPass<'_> for DropForStatic {
fn check_item<'a>(&mut self, cx: &LateContext<'a>, item: &'a Item<'a>) {
if let ItemKind::Static(_, ident, _, _) = item.kind {
let mut visitor = DropForStaticVisitor::new(cx);
visitor.visit_item(item);
if visitor.drop_for_static_found {
span_lint(cx, DROP_FOR_STATIC, ident.span, "static items with drop implementation");
}
}
}
}

struct DropForStaticVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
drop_for_static_found: bool,
}
impl<'a, 'tcx> DropForStaticVisitor<'a, 'tcx> {
fn new(cx: &'a LateContext<'tcx>) -> Self {
Self {
cx,
drop_for_static_found: false,
}
}
}

impl<'tcx> Visitor<'tcx> for DropForStaticVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::All;

fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) {
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. using Diag::span_label

Copy link
Author

@Mr-Leshiy Mr-Leshiy Jan 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for suggestion !
Already applied it.
But using span_label makes it like

LL | static A1: FooWithDrop = FooWithDrop;
   |        ^^  ----------- type with drop implementation here

Have not found an alternative to print exactly how you've mentioned.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

} else {
walk_path(self, path);
}
}
}

fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.cx.tcx
}
}
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ mod disallowed_script_idents;
mod disallowed_types;
mod doc;
mod double_parens;
mod drop_for_static;
mod drop_forget_ref;
mod duplicate_mod;
mod duration_suboptimal_units;
Expand Down Expand Up @@ -708,6 +709,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
},
Box::new(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))),
Box::new(|_| Box::new(empty_drop::EmptyDrop)),
Box::new(|_| Box::new(drop_for_static::DropForStatic)),
Box::new(|_| Box::new(strings::StrToString)),
Box::new(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues)),
Box::new(|_| Box::<vec_init_then_push::VecInitThenPush>::default()),
Expand Down
60 changes: 60 additions & 0 deletions tests/ui/drop_for_static.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#![warn(clippy::drop_for_static)]
#![allow(unused)]

struct FooWithDrop;
struct FooWithoutDrop;

impl Drop for FooWithDrop {
fn drop(&mut self) {}
}

static A1: FooWithDrop = FooWithDrop;
//~^ drop_for_static
static A2: FooWithoutDrop = FooWithoutDrop;

static A3: &FooWithDrop = &FooWithDrop;
//~^ drop_for_static
static A4: &FooWithoutDrop = &FooWithoutDrop;

static A5: (FooWithoutDrop, FooWithDrop) = (FooWithoutDrop, FooWithDrop);
//~^ drop_for_static
static A6: (FooWithoutDrop, FooWithoutDrop) = (FooWithoutDrop, FooWithoutDrop);

static A7: [FooWithDrop; 1] = [FooWithDrop];
//~^ drop_for_static
static A8: [FooWithoutDrop; 1] = [FooWithoutDrop];

static A9: &[FooWithDrop] = &[FooWithDrop];
//~^ drop_for_static
static A10: &[FooWithoutDrop] = &[FooWithoutDrop];

// ----------------------
// nested types scenarios
// ----------------------

struct Nested<T1, T2 = ()>(T1, T2);
static B1: Nested<FooWithDrop> = Nested(FooWithDrop, ());
//~^ drop_for_static
static B2: Nested<FooWithoutDrop> = Nested(FooWithoutDrop, ());

static B3: Nested<FooWithoutDrop, Nested<FooWithDrop>> = Nested(FooWithoutDrop, Nested(FooWithDrop, ()));
//~^ drop_for_static
static B4: Nested<FooWithoutDrop, Nested<FooWithoutDrop>> = Nested(FooWithoutDrop, Nested(FooWithoutDrop, ()));

static B5: Nested<&FooWithDrop> = Nested(&FooWithDrop, ());
//~^ drop_for_static
static B6: Nested<&FooWithoutDrop> = Nested(&FooWithoutDrop, ());

static B7: Nested<(FooWithoutDrop, FooWithDrop)> = Nested((FooWithoutDrop, FooWithDrop), ());
//~^ drop_for_static
static B8: Nested<(FooWithoutDrop, FooWithoutDrop)> = Nested((FooWithoutDrop, FooWithoutDrop), ());

static B9: Nested<[FooWithDrop; 1]> = Nested([FooWithDrop], ());
//~^ drop_for_static
static B10: Nested<[FooWithoutDrop; 1]> = Nested([FooWithoutDrop], ());

static B11: Nested<&[FooWithDrop]> = Nested(&[FooWithDrop], ());
//~^ drop_for_static
static B12: Nested<&[FooWithoutDrop]> = Nested(&[FooWithoutDrop], ());

fn main() {}
71 changes: 71 additions & 0 deletions tests/ui/drop_for_static.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
error: static items with drop implementation
--> tests/ui/drop_for_static.rs:11:8
|
LL | static A1: FooWithDrop = FooWithDrop;
| ^^
|
= note: `-D clippy::drop-for-static` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::drop_for_static)]`

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:15:8
|
LL | static A3: &FooWithDrop = &FooWithDrop;
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:19:8
|
LL | static A5: (FooWithoutDrop, FooWithDrop) = (FooWithoutDrop, FooWithDrop);
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:23:8
|
LL | static A7: [FooWithDrop; 1] = [FooWithDrop];
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:27:8
|
LL | static A9: &[FooWithDrop] = &[FooWithDrop];
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:36:8
|
LL | static B1: Nested<FooWithDrop> = Nested(FooWithDrop, ());
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:40:8
|
LL | static B3: Nested<FooWithoutDrop, Nested<FooWithDrop>> = Nested(FooWithoutDrop, Nested(FooWithDrop, ()));
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:44:8
|
LL | static B5: Nested<&FooWithDrop> = Nested(&FooWithDrop, ());
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:48:8
|
LL | static B7: Nested<(FooWithoutDrop, FooWithDrop)> = Nested((FooWithoutDrop, FooWithDrop), ());
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:52:8
|
LL | static B9: Nested<[FooWithDrop; 1]> = Nested([FooWithDrop], ());
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:56:8
|
LL | static B11: Nested<&[FooWithDrop]> = Nested(&[FooWithDrop], ());
| ^^^

error: aborting due to 11 previous errors

Loading