Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -163,21 +163,6 @@ public K.ClassDeclaration visitClassDeclaration(K.ClassDeclaration classDeclarat
DependencyVisitorLogic.recordClassLocation(state, owningFqn, sourcePath);
}

// Delegate J-level class declaration processing to shared logic
// Note: Kotlin doesn't have record components in the same way
var snapshot = DependencyVisitorLogic.enterClassDeclaration(
state,
jcd,
false, // processRecordComponents = false for Kotlin
cursor -> {
J.CompilationUnit jcu = cursor.firstEnclosing(J.CompilationUnit.class);
if (jcu != null) {
return jcu.getSourcePath().toUri().toString();
}
K.CompilationUnit kcu = cursor.firstEnclosing(K.CompilationUnit.class);
return kcu != null ? kcu.getSourcePath().toUri().toString() : null;
});

// Process Kotlin-specific: type constraints
if (classDeclaration.getTypeConstraints() != null) {
for (J.TypeParameter typeParameter :
Expand All @@ -186,7 +171,6 @@ public K.ClassDeclaration visitClassDeclaration(K.ClassDeclaration classDeclarat
}
}

DependencyVisitorLogic.leaveClassDeclaration(state, snapshot);
return result;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ void parseKotlinSourceDirectoryTest() throws IOException {
"com.ideacrest.parser.testclasses.D"));
}

@DisplayName("Kotlin class-header dependencies are recorded exactly once.")
@Test
void classHeaderDependencyHasWeightOne() throws IOException {
File srcDirectory = new File("src/test/resources/kotlinClassHeaderSrcDirectory");
CodebaseGraphDTO dto = compositeGraphBuilder.getCodebaseGraphDTO(srcDirectory.getAbsolutePath(), false, "");
Graph<String, DefaultWeightedEdge> classReferencesGraph = dto.getClassReferencesGraph();
String source = "com.ideacrest.parser.classheader.HeaderImplementation";
String target = "com.ideacrest.parser.classheader.HeaderContract";

assertTrue(classReferencesGraph.containsVertex(source));
assertTrue(classReferencesGraph.containsVertex(target));
assertTrue(classReferencesGraph.containsEdge(source, target));
assertEquals(1.0, getEdgeWeight(classReferencesGraph, source, target));
}

@DisplayName("Kotlin callable references produce edges between caller and target's declaring class.")
@Test
void parseKotlinCallableReferenceTest() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.ideacrest.parser.classheader

interface HeaderContract

class HeaderImplementation : HeaderContract
20 changes: 11 additions & 9 deletions plans/kotlin-implementation-plan-glm-5-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ based on the existing Java analysis architecture in the `codebase-graph-builder`
- **KotlinParser.builder().build()** - Analogous to `JavaParser.fromJavaVersion().build()`
- Returns `Stream<SourceFile>` from `parseInputs()`
- Supports `.kt` and `.kts` file extensions
- Uses `org.openrewrite.kotlin.KotlinParser.KotlinLanguageLevel` enum (default: `KOTLIN_2_2`)
- Uses `org.openrewrite.kotlin.KotlinParser.KotlinLanguageLevel` enum (configured as `KOTLIN_2_4`)

### Visitor Architecture

Expand All @@ -28,8 +28,9 @@ based on the existing Java analysis architecture in the `codebase-graph-builder`

### Dependency Management

- `rewrite-bom:8.86.0` (imported via `rewrite-recipe-bom:3.34.0`) manages `org.openrewrite:rewrite-kotlin:8.86.0`
- Kotlin compiler-embeddable is a transitive dependency (~30 MB) — make optional
- `rewrite-bom:8.90.4` manages `org.openrewrite:rewrite-kotlin:8.90.4`
- `rewrite-kotlin` is a required compile dependency of `codebase-graph-builder`; its Kotlin compiler transitives are
therefore present for every consumer
- Java 17 compatible (Kotlin stdlib targets JVM 1.8)

## Design Strategy
Expand Down Expand Up @@ -63,15 +64,15 @@ based on the existing Java analysis architecture in the `codebase-graph-builder`
**Production**:

- `CompositeGraphBuilder` walks `.java` and `.kt` files
- Reflectively probes `KotlinParser` presence (optional jar support)
- `GraphBuilderConfig.analyzeKotlin` default `true`
- Runs Java and Kotlin analysis unconditionally and merges both results
- Uses `rewrite-kotlin` directly; there is no reflective parser probe or `GraphBuilderConfig.analyzeKotlin` switch

### Phase 3 — Kotlin Metrics Collection

**Red Test**: `KotlinMetricsCollectionTest` — LOC/NOM/NOA/WMC/ATFD/TCC on Kotlin fixtures
**Production**:

- Refactor `MetricsCollectingVisitor` → `AbstractMetricsCollectingVisitor` (protected hooks)
- Extract shared metric handling into `MetricsVisitorLogic` with traversal state held by `MetricsVisitorState`
- Create `KotlinMetricsCollectingVisitor extends KotlinIsoVisitor<ExecutionContext>`
- Handle `K.Property` (top-level properties, extension properties)

Expand Down Expand Up @@ -149,7 +150,8 @@ Four new disharmony types:
## Locked Design Decisions

1. **Refactor** J-level logic into protected hooks on abstract bases (no fork-and-drift)
2. **Optional Maven dependency** — `rewrite-kotlin` marked `<optional>true</optional>`
3. **Kotlin language level**: `KOTLIN_2_2` (parser default), configurable via `GraphBuilderConfig`
2. **Required Maven dependency** — `rewrite-kotlin` is a non-optional compile dependency of
`codebase-graph-builder`; `CompositeGraphBuilder` runs Kotlin analysis unconditionally
3. **Kotlin language level**: `KOTLIN_2_4`, configurable via `GraphBuilderConfig`
4. **Kotlin disharmonies as ClassDisharmony** — reuses existing downstream plumbing
5. **Callable references & type parameters feed BOTH graph edges AND metrics**
5. **Callable references & type parameters feed BOTH graph edges AND metrics**
Loading