use std::cell::Cell;
thread_local! {
static WARNING_COUNTER: Cell<Option<usize>> = const { Cell::new(None) };
static WARNING_SUPPRESS: Cell<u32> = const { Cell::new(0) };
}
pub(crate) struct WarningCounterGuard {
_priv: (),
}
impl WarningCounterGuard {
pub(crate) fn begin() -> Self {
WARNING_COUNTER.with(|c| {
assert!(
c.get().is_none(),
"WarningCounterGuard nested: already counting deprecations on this thread \
(nested scopes would silently miscount; see deprecation_warnings.rs)"
);
c.set(Some(0));
});
Self { _priv: () }
}
pub(crate) fn finish(self) -> usize {
let n = WARNING_COUNTER.with(|c| c.take().unwrap_or(0));
std::mem::forget(self);
n
}
}
impl Drop for WarningCounterGuard {
fn drop(&mut self) {
WARNING_COUNTER.with(|c| c.set(None));
}
}
pub(crate) struct WarningSuppressionGuard {
_priv: (),
}
impl WarningSuppressionGuard {
pub(crate) fn begin() -> Self {
WARNING_SUPPRESS.with(|c| c.set(c.get().saturating_add(1)));
Self { _priv: () }
}
}
impl Drop for WarningSuppressionGuard {
fn drop(&mut self) {
WARNING_SUPPRESS.with(|c| c.set(c.get().saturating_sub(1)));
}
}
pub(crate) fn note_deprecation() {
WARNING_COUNTER.with(|c| {
if let Some(n) = c.get() {
c.set(Some(n.saturating_add(1)));
}
});
}
pub(crate) fn is_suppressed() -> bool {
WARNING_SUPPRESS.with(|c| c.get() > 0)
}
#[cfg(test)]
mod tests {
use super::*;
fn reset_thread_locals() {
WARNING_COUNTER.with(|c| c.set(None));
WARNING_SUPPRESS.with(|c| c.set(0));
}
#[test]
fn warning_counter_guard_drops_to_none_on_finish() {
reset_thread_locals();
let g = WarningCounterGuard::begin();
WARNING_COUNTER.with(|c| assert_eq!(c.get(), Some(0)));
let _ = g.finish();
WARNING_COUNTER.with(|c| assert_eq!(c.get(), None));
}
#[test]
fn warning_counter_guard_drops_to_none_on_implicit_drop() {
reset_thread_locals();
{
let _g = WarningCounterGuard::begin();
WARNING_COUNTER.with(|c| assert_eq!(c.get(), Some(0)));
}
WARNING_COUNTER.with(|c| assert_eq!(c.get(), None));
}
#[test]
#[should_panic(expected = "WarningCounterGuard nested")]
fn warning_counter_guard_nested_begin_panics_in_release_too() {
reset_thread_locals();
let _outer = WarningCounterGuard::begin();
let _inner = WarningCounterGuard::begin();
}
#[test]
fn warning_suppression_guard_returns_to_zero_on_drop() {
reset_thread_locals();
{
let _g = WarningSuppressionGuard::begin();
WARNING_SUPPRESS.with(|c| assert_eq!(c.get(), 1));
assert!(is_suppressed());
}
WARNING_SUPPRESS.with(|c| assert_eq!(c.get(), 0));
assert!(!is_suppressed());
}
#[test]
fn warning_suppression_guard_nests_to_correct_depth() {
reset_thread_locals();
let outer = WarningSuppressionGuard::begin();
WARNING_SUPPRESS.with(|c| assert_eq!(c.get(), 1));
{
let inner = WarningSuppressionGuard::begin();
WARNING_SUPPRESS.with(|c| assert_eq!(c.get(), 2));
drop(inner);
}
WARNING_SUPPRESS.with(|c| assert_eq!(c.get(), 1));
drop(outer);
WARNING_SUPPRESS.with(|c| assert_eq!(c.get(), 0));
}
#[test]
fn note_deprecation_increments_only_inside_counter_scope() {
reset_thread_locals();
note_deprecation();
WARNING_COUNTER.with(|c| assert_eq!(c.get(), None));
let g = WarningCounterGuard::begin();
note_deprecation();
note_deprecation();
let n = g.finish();
assert_eq!(n, 2);
}
#[test]
fn is_suppressed_is_false_outside_scope() {
reset_thread_locals();
assert!(!is_suppressed());
}
}