From a3feeb3afe075898e4112baa90d948ef57ce4401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= Date: Sun, 9 Jun 2024 14:13:01 +0000 Subject: [PATCH] run-make-support: add drop bomb module --- .../run-make-support/src/drop_bomb/mod.rs | 50 +++++++++++++++++++ .../run-make-support/src/drop_bomb/tests.rs | 15 ++++++ src/tools/run-make-support/src/lib.rs | 1 + 3 files changed, 66 insertions(+) create mode 100644 src/tools/run-make-support/src/drop_bomb/mod.rs create mode 100644 src/tools/run-make-support/src/drop_bomb/tests.rs diff --git a/src/tools/run-make-support/src/drop_bomb/mod.rs b/src/tools/run-make-support/src/drop_bomb/mod.rs new file mode 100644 index 000000000000..2fc84892c1bd --- /dev/null +++ b/src/tools/run-make-support/src/drop_bomb/mod.rs @@ -0,0 +1,50 @@ +//! This module implements "drop bombs" intended for use by command wrappers to ensure that the +//! constructed commands are *eventually* executed. This is exactly like `rustc_errors::Diag` where +//! we force every `Diag` to be consumed or we emit a bug, but we panic instead. +//! +//! This is adapted from and simplified for our +//! purposes. + +use std::ffi::{OsStr, OsString}; +use std::panic; + +#[cfg(test)] +mod tests; + +#[derive(Debug)] +pub(crate) struct DropBomb { + command: OsString, + defused: bool, + armed_line: u32, +} + +impl DropBomb { + /// Arm a [`DropBomb`]. If the value is dropped without being [`defused`][Self::defused], then + /// it will panic. It is expected that the command wrapper uses `#[track_caller]` to help + /// propagate the caller info from rmake.rs. + #[track_caller] + pub(crate) fn arm>(command: S) -> DropBomb { + DropBomb { + command: command.as_ref().into(), + defused: false, + armed_line: panic::Location::caller().line(), + } + } + + /// Defuse the [`DropBomb`]. This will prevent the drop bomb from panicking when dropped. + pub(crate) fn defuse(&mut self) { + self.defused = true; + } +} + +impl Drop for DropBomb { + fn drop(&mut self) { + if !self.defused && !std::thread::panicking() { + panic!( + "command constructed but not executed at line {}: `{}`", + self.armed_line, + self.command.to_string_lossy() + ) + } + } +} diff --git a/src/tools/run-make-support/src/drop_bomb/tests.rs b/src/tools/run-make-support/src/drop_bomb/tests.rs new file mode 100644 index 000000000000..4a488c0f6708 --- /dev/null +++ b/src/tools/run-make-support/src/drop_bomb/tests.rs @@ -0,0 +1,15 @@ +use super::DropBomb; + +#[test] +#[should_panic] +fn test_arm() { + let bomb = DropBomb::arm("hi :3"); + drop(bomb); // <- armed bomb should explode when not defused +} + +#[test] +fn test_defuse() { + let mut bomb = DropBomb::arm("hi :3"); + bomb.defuse(); + drop(bomb); // <- defused bomb should not explode +} diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 93e41b593d12..20ad25efaf00 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -7,6 +7,7 @@ pub mod cc; pub mod clang; mod command; pub mod diff; +mod drop_bomb; pub mod llvm_readobj; pub mod run; pub mod rustc;