Skip to content
Draft
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
17 changes: 17 additions & 0 deletions gradle/spotbugFilters/exclude.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,21 @@
<Package name="com.datadog.appsec.report.raw.events" />
</Or>
</Match>

<!--
Grandfathered findings for the unclosed-resource check that test and JMH sources run
(see gradle/spotbugs.gradle). These predate the check; they are suppressed one method at a time
so the check can be required for everything else, and they are not a licence for new code to
leave resources unclosed. Fix the method and drop its entry rather than adding more.
-->
<Match>
<Class name="com.datadog.profiling.uploader.ProfileUploaderTest" />
<Method name="recordingStream" />
<Bug pattern="OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE" />
</Match>
<Match>
<Class name="com.datadog.profiling.uploader.util.JfrCliHelperTest" />
<Method name="recordingStream" />
<Bug pattern="OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE" />
</Match>
</FindBugsFilter>
43 changes: 38 additions & 5 deletions gradle/spotbugs.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down
Loading