Skip to content

Commit f65a67c

Browse files
bm1549claude
andcommitted
fix: address code review findings — hardcoded SHA, incremental checks, FreezingArchRule
- UnnecessaryElseLinter: replace hardcoded SHA with origin/master...HEAD - JavadocLinter, AssertJPreferenceLinter: use origin/master...HEAD instead of HEAD~1 - EmptyInstrumentationLinter: make incremental (only check changed files) - ArchitectureTest: wrap rules with FreezingArchRule to baseline existing violations - EmptyInstrumentationLinter: fix duplicate-line bug in brace tracking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e142783 commit f65a67c

5 files changed

Lines changed: 83 additions & 55 deletions

File tree

buildSrc/src/main/kotlin/datadog/gradle/plugin/lint/AssertJPreferenceLinter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class AssertJPreferenceLinter : Plugin<Project> {
4646
return try {
4747
val stdout = ByteArrayOutputStream()
4848
project.exec {
49-
commandLine("git", "diff", "--name-only", "--diff-filter=A", "HEAD~1")
49+
commandLine("git", "diff", "--name-only", "--diff-filter=A", "origin/master...HEAD")
5050
standardOutput = stdout
5151
isIgnoreExitValue = true
5252
}

buildSrc/src/main/kotlin/datadog/gradle/plugin/lint/EmptyInstrumentationLinter.kt

Lines changed: 71 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package datadog.gradle.plugin.lint
33
import org.gradle.api.GradleException
44
import org.gradle.api.Plugin
55
import org.gradle.api.Project
6+
import java.io.ByteArrayOutputStream
67

78
class EmptyInstrumentationLinter : Plugin<Project> {
89
override fun apply(target: Project) {
@@ -19,74 +20,99 @@ class EmptyInstrumentationLinter : Plugin<Project> {
1920
)
2021
}
2122

23+
// Only check files changed on this branch to avoid flagging existing stubs
24+
val changedFiles = getChangedInstrumentationFiles(target, instrumentationsDir)
25+
2226
val violations = mutableListOf<String>()
2327
val hasAdvicePattern = Regex("""implements\s+[^{]*\b(?:HasMethodAdvice|HasAdvice)\b""")
2428
val methodAdviceStartPattern = Regex("""(?:public\s+)?(?:\S+\s+)?methodAdvice\s*\(""")
2529
val transformCallPattern = Regex("""\btransform\s*\(""")
2630

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")
31+
val filesToCheck = if (changedFiles != null) {
32+
changedFiles.filter { it.isFile && it.name.endsWith(".java") }
33+
} else {
34+
// Fallback: check all files if git diff fails
35+
instrumentationsDir.walk()
36+
.filter { it.isFile && it.name.endsWith(".java") }
37+
.toList()
38+
}
3239

33-
if (!hasAdvicePattern.containsMatchIn(content)) return@forEach
40+
filesToCheck.forEach { file ->
41+
val lines = file.readLines()
42+
val content = lines.joinToString("\n")
3443

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-
}
44+
if (!hasAdvicePattern.containsMatchIn(content)) return@forEach
45+
46+
var methodAdviceLineIndex = -1
47+
for (i in lines.indices) {
48+
if (methodAdviceStartPattern.containsMatchIn(lines[i])) {
49+
methodAdviceLineIndex = i
50+
break
4251
}
52+
}
4353

44-
if (methodAdviceLineIndex < 0) return@forEach
54+
if (methodAdviceLineIndex < 0) return@forEach
4555

46-
// Extract method body by tracking brace depth
47-
var braceDepth = 0
48-
var methodBodyStart = -1
49-
val methodBodyLines = mutableListOf<String>()
56+
var braceDepth = 0
57+
var methodBodyStart = -1
58+
val methodBodyLines = mutableListOf<String>()
5059

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-
}
60+
for (i in methodAdviceLineIndex until lines.size) {
61+
val line = lines[i]
62+
for (ch in line) {
63+
when (ch) {
64+
'{' -> {
65+
braceDepth++
66+
if (braceDepth == 1) methodBodyStart = i
67+
}
68+
'}' -> {
69+
braceDepth--
70+
if (braceDepth == 0 && methodBodyStart >= 0) {
71+
break
6572
}
6673
}
6774
}
68-
if (methodBodyStart >= 0) {
69-
methodBodyLines.add(line)
70-
}
71-
if (braceDepth == 0 && methodBodyStart >= 0) break
7275
}
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")
76+
if (methodBodyStart >= 0) {
77+
methodBodyLines.add(line)
8078
}
79+
if (braceDepth == 0 && methodBodyStart >= 0) break
80+
}
81+
82+
val methodBody = methodBodyLines.joinToString("\n")
83+
if (!transformCallPattern.containsMatchIn(methodBody)) {
84+
val classNamePattern = Regex("""class\s+(\w+)""")
85+
val className = classNamePattern.find(content)?.groupValues?.get(1) ?: "<unknown>"
86+
val relPath = file.relativeTo(target.rootProject.projectDir).path
87+
violations.add("EMPTY STUB: $relPath:${methodAdviceLineIndex + 1}$className.methodAdvice() contains no transform() calls")
8188
}
89+
}
8290

8391
if (violations.isNotEmpty()) {
8492
violations.forEach { target.logger.error(it) }
85-
throw GradleException("Empty instrumentation stubs found! See errors above.")
93+
throw GradleException("Found ${violations.size} new empty instrumentation stub(s)! See errors above.")
8694
} else {
87-
target.logger.lifecycle("✓ No empty instrumentation stubs found")
95+
target.logger.lifecycle("checkEmptyInstrumentations: no new empty stubs found")
8896
}
8997
}
9098
}
9199
}
100+
101+
private fun getChangedInstrumentationFiles(project: Project, instrumentationsDir: java.io.File): List<java.io.File>? {
102+
return try {
103+
val stdout = ByteArrayOutputStream()
104+
project.exec {
105+
commandLine("git", "diff", "--name-only", "--diff-filter=ACM", "origin/master...HEAD")
106+
standardOutput = stdout
107+
isIgnoreExitValue = true
108+
}
109+
stdout.toString().trim().lines()
110+
.filter { it.endsWith(".java") && it.startsWith("dd-java-agent/instrumentation/") }
111+
.map { project.rootProject.file(it) }
112+
.filter { it.exists() }
113+
} catch (e: Exception) {
114+
project.logger.warn("checkEmptyInstrumentations: could not get changed files, checking all — ${e.message}")
115+
null
116+
}
117+
}
92118
}

buildSrc/src/main/kotlin/datadog/gradle/plugin/lint/JavadocLinter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class JavadocLinter : Plugin<Project> {
5858
return try {
5959
val stdout = ByteArrayOutputStream()
6060
project.exec {
61-
commandLine("git", "diff", "--name-only", "--diff-filter=ACMR", "HEAD~1")
61+
commandLine("git", "diff", "--name-only", "--diff-filter=ACMR", "origin/master...HEAD")
6262
standardOutput = stdout
6363
isIgnoreExitValue = true
6464
}

buildSrc/src/main/kotlin/datadog/gradle/plugin/lint/UnnecessaryElseLinter.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ class UnnecessaryElseLinter : Plugin<Project> {
1010
description = "Scan changed Java files for unnecessary else blocks after return/throw/continue/break"
1111

1212
doLast {
13-
val baseSha = "9b933669729ea8a7af00f5cf3c36b6720ec433bd"
14-
val changedFiles = getChangedJavaFiles(target, baseSha)
13+
val changedFiles = getChangedJavaFiles(target)
1514
val repoRoot = target.rootProject.projectDir.toPath()
1615
val warnings = mutableListOf<String>()
1716

@@ -33,9 +32,9 @@ class UnnecessaryElseLinter : Plugin<Project> {
3332
}
3433
}
3534

36-
private fun getChangedJavaFiles(project: Project, baseSha: String): List<java.io.File> {
35+
private fun getChangedJavaFiles(project: Project): List<java.io.File> {
3736
return try {
38-
val process = ProcessBuilder("git", "diff", "--name-only", baseSha, "HEAD")
37+
val process = ProcessBuilder("git", "diff", "--name-only", "--diff-filter=ACM", "origin/master...HEAD")
3938
.directory(project.rootProject.projectDir)
4039
.redirectErrorStream(true)
4140
.start()

dd-java-agent/src/test/java/datadog/trace/agent/ArchitectureTest.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@
66
import com.tngtech.archunit.core.importer.ClassFileImporter;
77
import com.tngtech.archunit.core.importer.ImportOption;
88
import com.tngtech.archunit.lang.ArchRule;
9+
import com.tngtech.archunit.library.freeze.FreezingArchRule;
910
import org.junit.jupiter.api.BeforeAll;
1011
import org.junit.jupiter.api.Test;
1112

1213
/**
1314
* Architecture fitness tests enforcing module dependency rules. These rules prevent the most common
1415
* violations found during PR reviews: bootstrap depending on core, instrumentations reaching into
1516
* core internals, and forbidden JDK APIs in bootstrap code.
17+
*
18+
* <p>Uses {@link FreezingArchRule} to baseline existing violations so only new violations fail.
1619
*/
1720
class ArchitectureTest {
1821

@@ -37,7 +40,7 @@ void bootstrapShouldNotDependOnCore() {
3740
.resideInAPackage("datadog.trace.core..")
3841
.because("Bootstrap classes load before core and must not depend on core internals");
3942

40-
rule.check(allClasses);
43+
FreezingArchRule.freeze(rule).check(allClasses);
4144
}
4245

4346
@Test
@@ -51,7 +54,7 @@ void instrumentationShouldNotDependOnCoreInternals() {
5154
.resideInAPackage("datadog.trace.core.internal..")
5255
.because("Instrumentations should use internal-api, not core internals directly");
5356

54-
rule.check(allClasses);
57+
FreezingArchRule.freeze(rule).check(allClasses);
5558
}
5659

5760
@Test
@@ -67,7 +70,7 @@ void bootstrapShouldNotUseJavaUtilLogging() {
6770
"java.util.logging locks in the log manager before the app configures it"
6871
+ " (see docs/bootstrap_design_guidelines.md)");
6972

70-
rule.check(allClasses);
73+
FreezingArchRule.freeze(rule).check(allClasses);
7174
}
7275

7376
@Test
@@ -83,6 +86,6 @@ void bootstrapShouldNotUseJavaxManagement() {
8386
"javax.management causes class loading issues in premain"
8487
+ " (see docs/bootstrap_design_guidelines.md)");
8588

86-
rule.check(allClasses);
89+
FreezingArchRule.freeze(rule).check(allClasses);
8790
}
8891
}

0 commit comments

Comments
 (0)