feat(kotlin): index @Composable as component, record annotations - #1653
Open
aabolfazl wants to merge 1 commit into
Open
feat(kotlin): index @Composable as component, record annotations#1653aabolfazl wants to merge 1 commit into
aabolfazl wants to merge 1 commit into
Conversation
On a Compose/Hilt/Room codebase none of "which functions are @composable", "which classes are @hiltviewmodel", or "find every @dao" could be answered: `codegraph_search kind=component` returned zero for Kotlin, and annotations left no trace on the graph. Framework annotations are declared in libraries outside the index, so the unresolved `decorates` reference extraction emitted was the only artifact and it never resolved. Annotation simple names now persist onto the annotated node and surface in `codegraph_node`, and a declarative annotation->NodeKind map classifies `@Composable` functions and methods as `component` — the Kotlin analogue of the function-level component nodes the React resolver already creates for JSX-returning functions. Kotlin routes to the native kernel, so the Rust walker and the wasm extractor both change: the parity gate compares the two arms and fails on either one alone. In both, annotation collection moved ahead of node creation, because a node's kind and its decorators list are fixed when the node is built. Three extraction bugs fall out of the same collector. Arg-bearing annotations (`@Preview(showBackground = true)`) parse as `constructor_invocation`, which neither arm unwrapped, so they emitted nothing at all. Annotations on interfaces and enums were never collected either — that is where Room puts `@Dao`. And Kotlin's bracket form (`@[Suppress("x") JvmStatic]`) yielded only its first entry. All of it is opt-in per language via `LanguageExtractor.extendedAnnotations`, which only Kotlin sets. Each of the 13 kernel walkers owns its own decorator logic, so widening the shared engine alone would break parity for every other routed language; and collecting past the first target is Kotlin-specific — Swift carries argument expressions inside the attribute node, so `@Siblings(through: Pivot.self, from: \.$left)` would harvest `self` and `$left` as annotation names. Reclassifying to `component` has a blast radius: such a node silently drops out of every gate keyed on function/method. The ones widened alongside it are `flushFnRefCandidates`/`defined_fn_names` (a real dropped-edge bug, pinned by a test), `captureValueRefScope`/`value_scopes`, `enclosingScopeStartLine`, `matchFuzzy`'s callable kinds, the function-ref candidate filter, `findDeadCode`'s defaults, and `kmpKindsCompatible` for an asymmetrically annotated expect/actual pair. Node ids hash the kind, so an existing Kotlin index needs a re-index before the component nodes appear. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Annotation-driven Kotlin was unqueryable:
codegraph_search kind=componentreturned zero, and@HiltViewModel/@Dao/@Entityleft no trace on the graph. Framework annotations live in libraries outside the index, so the unresolveddecoratesref was the only artifact and it never resolved.What changed
@Composablefunctions and methods get thecomponentkind, and annotation names persist onto the node and show incodegraph_node:Three extraction bugs fall out of the same collector — all silent before:
@Preview(showBackground = true)constructor_invocationnever unwrapped)Preview@Dao interface/@Serializable enum classDao/Serializable@[Suppress("x") JvmStatic]SuppressonlyOpt-in per language via
LanguageExtractor.extendedAnnotations, which only Kotlin sets: each of the 13 kernel walkers owns its decorator logic, so widening the shared engine alone breaks parity elsewhere — and collecting past the first target is Kotlin-specific, since Swift's@Siblings(through: Pivot.self, from: \.$left)would harvestselfand$leftas names.Kotlin routes to the kernel, so
codegraph-kernel/src/kotlin.rsand the wasm walker both change; annotation collection moved ahead of node creation in both, because a node's kind and decorators are fixed when it is built.Blast radius. A
componentnode silently drops out of gates keyed on function/method. Widened alongside:flushFnRefCandidates/defined_fn_names(a real dropped-edge bug —register(::Header)lost itsfunction_ref, pinned by a test),captureValueRefScope/value_scopes,enclosingScopeStartLine,matchFuzzy's callable kinds, the function-ref candidate filter,findDeadCode's defaults, andkmpKindsCompatiblefor an asymmetrically annotated expect/actual pair.Tested
Kernel↔wasm sweep, fresh
--depth 1clones. Before is a worktree atmainwith its owndist/and its own kernel built from that tree'skotlin.rs, so deltas are attributable:0 diffs on 2,756 files; deferral and node counts unchanged (the +1/+2 vs the recorded 23/49/51 is repo drift — the before-tree reproduces 24/51/51 on these checkouts). The three non-Compose libraries correctly produce zero components while still gaining annotation coverage (okhttp 0 → 3,895 annotated symbols).
31 tests in
__tests__/kotlin-annotations.test.ts, every guarantee asserted on both arms (CODEGRAPH_KERNEL=0for wasm,tryKernelExtractfor the kernel) — a bareextractFromSourceonly exercises the kernel for Kotlin.torture.ktextended with the new shapes. Full suite: 3,254 passed, 0 failed.Not run: an agent A/B. An earlier eval found census questions ("how many
@Composable…") don't make the agent reach for codegraph — 3/3 runs chose grep. Thecomponentkind and thecodegraph_nodeline ride tools the agent already calls; neither has a measured retrieval win yet. Recorded indocs/design/kotlin-annotation-extraction.md.Node ids hash the kind, so an existing Kotlin index needs a re-index before component nodes appear.
🤖 Generated with Claude Code