From e1179e9d4a96e8c2317a7c2af09fa32bbb89be6e Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Tue, 1 Sep 2026 23:03:50 -0400 Subject: [PATCH] Add null-safe static ContextScope.close(scope) helper Many advice classes attach a ContextScope on enter and close it unconditionally on exit; if the enter advice throws before assigning the scope (its exception typically suppressed by suppress = Throwable.class), the exit advice NPEs on the null scope, masking the real failure. This adds a static helper that call sites can use instead of scope.close() to avoid that class of bug without a null check at every site. Co-Authored-By: Claude Sonnet 5 --- .../java/datadog/context/ContextScope.java | 15 ++++++++ .../datadog/context/ContextScopeTest.java | 35 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 components/context/src/test/java/datadog/context/ContextScopeTest.java diff --git a/components/context/src/main/java/datadog/context/ContextScope.java b/components/context/src/main/java/datadog/context/ContextScope.java index 7788a077615..d21d32a5146 100644 --- a/components/context/src/main/java/datadog/context/ContextScope.java +++ b/components/context/src/main/java/datadog/context/ContextScope.java @@ -8,4 +8,19 @@ public interface ContextScope extends AutoCloseable { /** Detaches the context from the execution unit. */ @Override void close(); + + /** + * Closes the given scope, tolerating a {@code null} scope. + * + *

Useful in advice that attaches a scope on enter and closes it on exit: if the enter advice + * throws before assigning the scope, the exit advice would otherwise NPE on a null scope, masking + * the original failure. + * + * @param scope the scope to close; can be {@code null}. + */ + static void close(ContextScope scope) { + if (scope != null) { + scope.close(); + } + } } diff --git a/components/context/src/test/java/datadog/context/ContextScopeTest.java b/components/context/src/test/java/datadog/context/ContextScopeTest.java new file mode 100644 index 00000000000..00299a57b23 --- /dev/null +++ b/components/context/src/test/java/datadog/context/ContextScopeTest.java @@ -0,0 +1,35 @@ +package datadog.context; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class ContextScopeTest { + + private static final class RecordingScope implements ContextScope { + private boolean closed; + + @Override + public Context context() { + return null; + } + + @Override + public void close() { + closed = true; + } + } + + @Test + void staticCloseToleratesNullScope() { + assertDoesNotThrow(() -> ContextScope.close(null)); + } + + @Test + void staticCloseClosesNonNullScope() { + RecordingScope scope = new RecordingScope(); + ContextScope.close(scope); + assertTrue(scope.closed); + } +}