diff --git a/gradle/spotbugFilters/exclude.xml b/gradle/spotbugFilters/exclude.xml
index 2276ed212e0..16b546cd5c9 100644
--- a/gradle/spotbugFilters/exclude.xml
+++ b/gradle/spotbugFilters/exclude.xml
@@ -23,4 +23,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gradle/spotbugs.gradle b/gradle/spotbugs.gradle
index a69304a7728..794ecf23848 100644
--- a/gradle/spotbugs.gradle
+++ b/gradle/spotbugs.gradle
@@ -5,7 +5,8 @@ spotbugs {
excludeFilter = file("$rootDir/gradle/spotbugFilters/exclude.xml")
}
-// Configure spotbugs for Main tasks and disable it for all other
+// Configure spotbugs for Main tasks, restrict it to the unclosed-resource check on Test and Jmh
+// tasks, and disable it for all other
// Can't use the class here due to being different class
tasks.matching { it.name.startsWith('spotbugs') }.configureEach {
// Needs Java 11+ to run spotbugs
@@ -14,10 +15,42 @@ tasks.matching { it.name.startsWith('spotbugs') }.configureEach {
}
def name = it.name
- if (project.path.startsWith(":dd-smoke-tests")
- || !(name.endsWith("Main")
- || name.endsWith("Main_java11"))
- ) {
+ if (project.path.startsWith(":dd-smoke-tests")) {
+ it.enabled = false
+ return
+ }
+
+ // Unclosed-resource check for test and JMH sources.
+ //
+ // Test and benchmark code regularly allocates an AutoCloseable/Closeable and then fails to close it
+ // on every path - most often the early-return and exceptional ones - because the resource is never
+ // put in a try-with-resources. Reviewers keep catching this by hand, which is slow and unreliable
+ // for a defect the 'FindUnsatisfiedObligation' detector (OBL_UNSATISFIED_OBLIGATION) finds
+ // mechanically. Only that detector runs here, so the check stays scoped to leaked resources and
+ // cannot start failing these sources for unrelated reasons as SpotBugs gains new detectors.
+ //
+ // It flags the obligation types SpotBugs knows: streams, readers, writers and the java.sql handles,
+ // plus any type annotated '@CleanupObligation' with a '@CreatesObligation' constructor and a
+ // '@DischargesObligation' close method. Project-specific handles are only covered once they carry
+ // those annotations; plain 'implements AutoCloseable' is not enough for SpotBugs to track them.
+ //
+ // Violations fail the build, exactly like main sources. Findings that predate this check are
+ // grandfathered in 'gradle/spotbugFilters/exclude.xml'; they are not a precedent for new code.
+ if (name.endsWith("Test") || name.endsWith("Jmh")) {
+ it.showProgress = !providers.environmentVariable("CI").isPresent()
+ it.ignoreFailures = false
+ it.visitors = ['FindUnsatisfiedObligation']
+ it.reports {
+ html {
+ required = true
+ destination(file("$buildDir/reports/spotbugs/${name}.html"))
+ stylesheet = 'fancy-hist.xsl'
+ }
+ }
+ return
+ }
+
+ if (!(name.endsWith("Main") || name.endsWith("Main_java11"))) {
it.enabled = false
return
}