Kotlin: confirmed CLEAN (DFS.VisitedWithSet throughout, existing marker valid) Scala3: confirmed CLEAN (BaseDataBuilder.addAll deduplicates, existing marker valid) GHC: CLEAN — NameSet/UniqSet/ExpansionFuel tracking in all superclass expansion paths (checkClassCycles, mk_strict_superclasses, transSuperClasses, closeWrtFunDeps) Groovy: CLEAN for diamond — getAllInterfaces/collectAllInterfacesReverseOrder/addAllInterfaces all use if(set.add(node)) guards (GROOVY-11036) groovy-0001/0002 patch docs created (O(N²) membership scans, pre-existing UNDF assigned) Clojure: CLEAN (set-based BFS, supers/ancestors use set-based worklist)
2.1 KiB
Groovy Compiler — Diamond Recursion CWE-407 Scan: CLEAN
Pattern: Recursive O(2^D) traversal of interface/trait hierarchy without visited set
Scan date: 2026-03-29
Scope: src/main/java/org/codehaus/groovy/
Method
Searched for recursive ClassNode hierarchy traversal without proper if (set.add()) guards.
Traced all callers of getInterfaces(), getSuperClass(), getAllInterfaces(),
getInterfacesAndSuperInterfaces(), and collectAllInterfacesReverseOrder().
Key candidates reviewed
ClassNode.getAllInterfaces() — private getAllInterfaces(Set)
src/main/java/org/codehaus/groovy/ast/ClassNode.java lines 441–452.
Fixed in GROOVY-11036: now uses if (set.add(face)) face.getAllInterfaces(set).
The guard on set.add() return value prevents re-traversal of shared ancestors.
CLEAN (post-fix).
GeneralUtils.addAllInterfaces() / getInterfacesAndSuperInterfaces()
src/main/java/org/codehaus/groovy/ast/tools/GeneralUtils.java lines 491–518.
Uses if (result.add(in)) { addAllInterfaces(result, in); } for interface branches.
Superclass branch is linear (Java single-inheritance), so no diamond possible there.
CLEAN.
Traits.collectAllInterfacesReverseOrder()
src/main/java/org/codehaus/groovy/transform/trait/Traits.java lines 307–316.
Uses if (interfaces.add(iNode)) collectAllInterfacesReverseOrder(iNode, interfaces).
Correct guard. CLEAN.
ResolveVisitor cycle detection
src/main/java/org/codehaus/groovy/control/ResolveVisitor.java lines 1363–1381.
Uses BFS with done.add(next) as the loop guard. CLEAN.
WideningCategories.lowestUpperBound()
src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java lines 319–447.
Recursion on superclass chain (linear, no diamond). Interface sets pre-computed via
getInterfacesAndSuperInterfaces() which is guarded. CLEAN.
Verdict
CLEAN for O(2^D) diamond recursion. Groovy uses if (set.add(node)) guards on all
recursive interface hierarchy traversal. The two existing defects (groovy-0001,
groovy-0002) are O(N²) list-membership scans, not diamond recursion.