Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6770,6 +6770,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 @@ -132,6 +132,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
56 changes: 56 additions & 0 deletions clippy_lints/src/drop_for_static.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use clippy_utils::diagnostics::span_lint;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
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 perform actions with
/// observable side effects beyond process exit — such as flushing buffered
/// writes to a file or cleanly terminating a network connection — those
/// actions will never occur for a `static` instance.
///
/// ### 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.
///
/// ### Example
/// ```rust
/// struct Logger;
///
/// impl Drop for Logger {
/// fn drop(&mut self) {
/// println!("Closing log file...");
/// }
/// }
///
/// static GLOBAL_LOGGER: Logger = Logger;
/// ```
#[clippy::version = "1.93.0"]
pub DROP_FOR_STATIC,
restriction,
"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 ty = cx
.tcx
.type_of(item.owner_id.def_id)
.instantiate_identity()
.skip_norm_wip()
&& ty.needs_drop(cx.tcx, cx.typing_env())
{
span_lint(cx, DROP_FOR_STATIC, ident.span, "static items with drop implementation");
}
}
}
3 changes: 2 additions & 1 deletion clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,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 @@ -466,7 +467,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
Box::new(CombinedEarlyLintPass::new(conf, format_args.clone(), attrs.clone()))
}));
}

store.late_passes.push(Box::new(move |tcx: TyCtxt<'_>| {
let dont_need = tcx.lints_that_dont_need_to_run(());
let is_active = |lints: &rustc_lint::LintVec| {
Expand Down Expand Up @@ -862,6 +862,7 @@ rustc_lint::late_lint_methods!(
WithCapacityZero: with_capacity_zero::WithCapacityZero = with_capacity_zero::WithCapacityZero,
RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns,
RedundantElse: redundant_else::RedundantElse = redundant_else::RedundantElse,
DropForStatic: drop_for_static::DropForStatic = drop_for_static::DropForStatic,
// add late passes here, used by `cargo dev new_lint`
]]
);
146 changes: 146 additions & 0 deletions tests/ui/drop_for_static.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#![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: (FooWithoutDrop, FooWithDrop) = (FooWithoutDrop, FooWithDrop);
//~^ drop_for_static
static A4: (FooWithoutDrop, FooWithoutDrop) = (FooWithoutDrop, FooWithoutDrop);

static A5: [FooWithDrop; 1] = [FooWithDrop];
//~^ drop_for_static
static A6: [FooWithoutDrop; 1] = [FooWithoutDrop];

// ----------------------
// generic type scenarios
// ----------------------

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

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

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

static B7: Generic<[FooWithDrop; 1]> = Generic([FooWithDrop], ());
//~^ drop_for_static
static B8: Generic<[FooWithoutDrop; 1]> = Generic([FooWithoutDrop], ());

// ----------------------
// type alias
// ----------------------

type BarWithDrop = FooWithDrop;
type BarWithoutDrop = FooWithoutDrop;

static C1: BarWithDrop = FooWithDrop;
//~^ drop_for_static
static C2: BarWithoutDrop = FooWithoutDrop;

static C3: (BarWithoutDrop, BarWithDrop) = (FooWithoutDrop, FooWithDrop);
//~^ drop_for_static
static C4: (BarWithoutDrop, BarWithoutDrop) = (FooWithoutDrop, FooWithoutDrop);

static C5: [BarWithDrop; 1] = [FooWithDrop];
//~^ drop_for_static
static C6: [BarWithoutDrop; 1] = [FooWithoutDrop];

// ----------------------
// Generic type with alias
// ----------------------

static D1: Generic<BarWithDrop> = Generic(FooWithDrop, ());
//~^ drop_for_static
static D2: Generic<BarWithoutDrop> = Generic(FooWithoutDrop, ());

static D3: Generic<BarWithoutDrop, Generic<BarWithDrop>> = Generic(FooWithoutDrop, Generic(FooWithDrop, ()));
//~^ drop_for_static
static D4: Generic<BarWithoutDrop, Generic<BarWithoutDrop>> = Generic(FooWithoutDrop, Generic(FooWithoutDrop, ()));

static D5: Generic<(BarWithoutDrop, BarWithDrop)> = Generic((FooWithoutDrop, FooWithDrop), ());
//~^ drop_for_static
static D6: Generic<(BarWithoutDrop, BarWithoutDrop)> = Generic((FooWithoutDrop, FooWithoutDrop), ());

static D7: Generic<[BarWithDrop; 1]> = Generic([FooWithDrop], ());
//~^ drop_for_static
static D8: Generic<[BarWithoutDrop; 1]> = Generic([FooWithoutDrop], ());

// ----------------------
// associated type
// ----------------------

trait FooTrait {
type FooAssoc;
}

struct FooImplWithDrop;
impl FooTrait for FooImplWithDrop {
type FooAssoc = FooWithDrop;
}
struct FooImplWithoutDrop;
impl FooTrait for FooImplWithoutDrop {
type FooAssoc = FooWithoutDrop;
}

static F1: <FooImplWithDrop as FooTrait>::FooAssoc = FooWithDrop;
//~^ drop_for_static
static F2: <FooImplWithoutDrop as FooTrait>::FooAssoc = FooWithoutDrop;

static F3: (
//~^ drop_for_static
<FooImplWithoutDrop as FooTrait>::FooAssoc,
<FooImplWithDrop as FooTrait>::FooAssoc,
) = (FooWithoutDrop, FooWithDrop);
static F4: (
<FooImplWithoutDrop as FooTrait>::FooAssoc,
<FooImplWithoutDrop as FooTrait>::FooAssoc,
) = (FooWithoutDrop, FooWithoutDrop);

static F5: [<FooImplWithDrop as FooTrait>::FooAssoc; 1] = [FooWithDrop];
//~^ drop_for_static
static F6: [<FooImplWithoutDrop as FooTrait>::FooAssoc; 1] = [FooWithoutDrop];

// ----------------------
// generic with associated type scenarios
// ----------------------

static E1: Generic<<FooImplWithDrop as FooTrait>::FooAssoc> = Generic(FooWithDrop, ());
//~^ drop_for_static
static E2: Generic<<FooImplWithoutDrop as FooTrait>::FooAssoc> = Generic(FooWithoutDrop, ());

static E3: Generic<<FooImplWithoutDrop as FooTrait>::FooAssoc, Generic<<FooImplWithDrop as FooTrait>::FooAssoc>> =
//~^ drop_for_static
Generic(FooWithoutDrop, Generic(FooWithDrop, ()));
static E4: Generic<<FooImplWithoutDrop as FooTrait>::FooAssoc, Generic<FooWithoutDrop>> =
Generic(FooWithoutDrop, Generic(FooWithoutDrop, ()));

static E5: Generic<(
//~^ drop_for_static
<FooImplWithoutDrop as FooTrait>::FooAssoc,
<FooImplWithDrop as FooTrait>::FooAssoc,
)> = Generic((FooWithoutDrop, FooWithDrop), ());
static E6: Generic<(
<FooImplWithoutDrop as FooTrait>::FooAssoc,
<FooImplWithoutDrop as FooTrait>::FooAssoc,
)> = Generic((FooWithoutDrop, FooWithoutDrop), ());

static E7: Generic<[<FooImplWithDrop as FooTrait>::FooAssoc; 1]> = Generic([FooWithDrop], ());
//~^ drop_for_static
static E8: Generic<[<FooImplWithoutDrop as FooTrait>::FooAssoc; 1]> = Generic([FooWithoutDrop], ());

fn main() {}
131 changes: 131 additions & 0 deletions tests/ui/drop_for_static.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
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: (FooWithoutDrop, FooWithDrop) = (FooWithoutDrop, FooWithDrop);
| ^^

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

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

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

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

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

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:51:8
|
LL | static C1: BarWithDrop = FooWithDrop;
| ^^

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

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

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:67:8
|
LL | static D1: Generic<BarWithDrop> = Generic(FooWithDrop, ());
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:71:8
|
LL | static D3: Generic<BarWithoutDrop, Generic<BarWithDrop>> = Generic(FooWithoutDrop, Generic(FooWithDrop, ()));
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:75:8
|
LL | static D5: Generic<(BarWithoutDrop, BarWithDrop)> = Generic((FooWithoutDrop, FooWithDrop), ());
| ^^

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

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:100:8
|
LL | static F1: <FooImplWithDrop as FooTrait>::FooAssoc = FooWithDrop;
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:104:8
|
LL | static F3: (
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:114:8
|
LL | static F5: [<FooImplWithDrop as FooTrait>::FooAssoc; 1] = [FooWithDrop];
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:122:8
|
LL | static E1: Generic<<FooImplWithDrop as FooTrait>::FooAssoc> = Generic(FooWithDrop, ());
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:126:8
|
LL | static E3: Generic<<FooImplWithoutDrop as FooTrait>::FooAssoc, Generic<<FooImplWithDrop as FooTrait>::FooAssoc>> =
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:132:8
|
LL | static E5: Generic<(
| ^^

error: static items with drop implementation
--> tests/ui/drop_for_static.rs:142:8
|
LL | static E7: Generic<[<FooImplWithDrop as FooTrait>::FooAssoc; 1]> = Generic([FooWithDrop], ());
| ^^

error: aborting due to 21 previous errors