Skip to content

Commit 4502e1f

Browse files
committed
Campaign merge: campaign/pr-review-static-checks/task-006
2 parents 322570b + 1973e62 commit 4502e1f

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

buildSrc/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ gradlePlugin {
5959
id = "dd-trace-java.instrumentation-naming"
6060
implementationClass = "datadog.gradle.plugin.naming.InstrumentationNamingPlugin"
6161
}
62+
63+
create("empty-instrumentation-linter") {
64+
id = "dd-trace-java.empty-instrumentation-linter"
65+
implementationClass = "datadog.gradle.plugin.lint.EmptyInstrumentationLinter"
66+
}
6267
}
6368
}
6469

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package datadog.gradle.plugin.lint
2+
3+
import org.gradle.api.GradleException
4+
import org.gradle.api.Plugin
5+
import org.gradle.api.Project
6+
7+
class EmptyInstrumentationLinter : Plugin<Project> {
8+
override fun apply(target: Project) {
9+
target.tasks.register("checkEmptyInstrumentations") {
10+
group = "verification"
11+
description = "Detects empty instrumentation stub classes with no transform() calls in methodAdvice()"
12+
13+
doLast {
14+
val instrumentationsDir = target.rootProject.file("dd-java-agent/instrumentation")
15+
16+
if (!instrumentationsDir.exists() || !instrumentationsDir.isDirectory) {
17+
throw GradleException(
18+
"Instrumentations directory not found: ${instrumentationsDir.absolutePath}"
19+
)
20+
}
21+
22+
val violations = mutableListOf<String>()
23+
val hasAdvicePattern = Regex("""implements\s+[^{]*\b(?:HasMethodAdvice|HasAdvice)\b""")
24+
val methodAdviceStartPattern = Regex("""(?:public\s+)?(?:\S+\s+)?methodAdvice\s*\(""")
25+
val transformCallPattern = Regex("""\btransform\s*\(""")
26+
27+
instrumentationsDir.walk()
28+
.filter { it.isFile && it.name.endsWith(".java") }
29+
.forEach { file ->
30+
val lines = file.readLines()
31+
val content = lines.joinToString("\n")
32+
33+
if (!hasAdvicePattern.containsMatchIn(content)) return@forEach
34+
35+
// Find methodAdvice( method and check its body for transform( calls
36+
var methodAdviceLineIndex = -1
37+
for (i in lines.indices) {
38+
if (methodAdviceStartPattern.containsMatchIn(lines[i])) {
39+
methodAdviceLineIndex = i
40+
break
41+
}
42+
}
43+
44+
if (methodAdviceLineIndex < 0) return@forEach
45+
46+
// Extract method body by tracking brace depth
47+
var braceDepth = 0
48+
var methodBodyStart = -1
49+
val methodBodyLines = mutableListOf<String>()
50+
51+
for (i in methodAdviceLineIndex until lines.size) {
52+
val line = lines[i]
53+
for (ch in line) {
54+
when (ch) {
55+
'{' -> {
56+
braceDepth++
57+
if (braceDepth == 1) methodBodyStart = i
58+
}
59+
'}' -> {
60+
braceDepth--
61+
if (braceDepth == 0 && methodBodyStart >= 0) {
62+
methodBodyLines.add(line)
63+
break
64+
}
65+
}
66+
}
67+
}
68+
if (methodBodyStart >= 0) {
69+
methodBodyLines.add(line)
70+
}
71+
if (braceDepth == 0 && methodBodyStart >= 0) break
72+
}
73+
74+
val methodBody = methodBodyLines.joinToString("\n")
75+
if (!transformCallPattern.containsMatchIn(methodBody)) {
76+
val classNamePattern = Regex("""class\s+(\w+)""")
77+
val className = classNamePattern.find(content)?.groupValues?.get(1) ?: "<unknown>"
78+
val relPath = file.relativeTo(target.rootProject.projectDir).path
79+
violations.add("EMPTY STUB: $relPath:${methodAdviceLineIndex + 1}$className.methodAdvice() contains no transform() calls")
80+
}
81+
}
82+
83+
if (violations.isNotEmpty()) {
84+
violations.forEach { target.logger.error(it) }
85+
throw GradleException("Empty instrumentation stubs found! See errors above.")
86+
} else {
87+
target.logger.lifecycle("✓ No empty instrumentation stubs found")
88+
}
89+
}
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)