# UNDF: UNDF-2026-000000268
# UNDF: (pending)
# micronaut-0007: SuperclassAwareTypeVisitor.getInterfaces — O(2^D) diamond interface re-traversal

## CWE-407 — Algorithmic Complexity: O(2^D) recursive diamond re-traversal in annotation processing

| Field | Value |
|-------|-------|
| ID | micronaut-0007 |
| Severity | HIGH |
| Ecosystem | micronaut |
| Package | io.micronaut.annotation.processing |
| File | `inject-java/src/main/java/io/micronaut/annotation/processing/SuperclassAwareTypeVisitor.java` |
| Lines | 143–155 |
| Complexity | O(2^D) on diamond interface hierarchies |
| Hot path | Micronaut compile-time annotation processing (APT) |

## Defect

```java
private List<TypeMirror> getInterfaces(DeclaredType declaredType) {
    Element interfaceElement = declaredType.asElement();
    var interfaces = new ArrayList<TypeMirror>();
    if (interfaceElement instanceof TypeElement interfaceTypeElement) {
        for (TypeMirror anInterface : interfaceTypeElement.getInterfaces()) {
            if (anInterface instanceof DeclaredType interfaceType) {
                interfaces.add(interfaceType);               // no visited guard
                interfaces.addAll(getInterfaces(interfaceType)); // unconditional recursion
            }
        }
    }
    return interfaces;
}
```

The caller (`visitDeclared`, lines 117–136) calls `getInterfaces` recursively to collect the full
transitive interface set, then deduplicates with `.distinct()`. The deduplication happens on the
resulting list, not during traversal. In a diamond topology where interface `I3` is a parent of
both `I1` and `I2`, and `I4` implements both `I1` and `I2`, `I3` is traversed 2^(D-1) times
before `.distinct()` is called — O(2^D) stack depth and list allocations.

## Fix

Pass a visited set through the recursion to guard re-entry:

```java
private List<TypeMirror> getInterfaces(DeclaredType declaredType) {
    var interfaces = new ArrayList<TypeMirror>();
    collectInterfaces(declaredType, interfaces, new LinkedHashSet<>());
    return interfaces;
}

private void collectInterfaces(DeclaredType declaredType,
                               List<TypeMirror> result,
                               Set<TypeMirror> visited) {
    Element interfaceElement = declaredType.asElement();
    if (interfaceElement instanceof TypeElement interfaceTypeElement) {
        for (TypeMirror anInterface : interfaceTypeElement.getInterfaces()) {
            if (anInterface instanceof DeclaredType interfaceType) {
                if (visited.add(interfaceType)) {           // guard: skip if already visited
                    result.add(interfaceType);
                    collectInterfaces(interfaceType, result, visited);
                }
            }
        }
    }
}
```

The `.distinct()` call in the caller can be kept for safety but becomes a no-op.

## Speedup

| D | getInterfaces calls (before) | getInterfaces calls (after) | Speedup |
|---|-----------------------------|-----------------------------|---------|
| 5 | 31 | 5 | 6× |
| 10 | 1,023 | 10 | 102× |
| 15 | 32,767 | 15 | 2,184× |
| 20 | 1,048,575 | 20 | 52,428× |

This method runs during Micronaut's compile-time APT processing for every class visited. Projects
with shared interface hierarchies (e.g., a base `Repository` interface extended by multiple domain
interfaces) directly trigger the diamond case. At D=10 the traversal is 1000× more expensive than
necessary, degrading build times and memory consumption.
