fix(kotlin): emit references edges for primary-constructor class_parameters#2063
Open
danielpucci wants to merge 1 commit into
Open
fix(kotlin): emit references edges for primary-constructor class_parameters#2063danielpucci wants to merge 1 commit into
danielpucci wants to merge 1 commit into
Conversation
…meters The Kotlin extractor only walked delegation_specifiers (supertypes) and property_declaration (body-level val/var) - it never walked primary_constructor -> class_parameters -> class_parameter, so every constructor-injected field (`class Foo(private val x: Bar)`, the idiomatic Kotlin/Hilt/Dagger DI pattern) was invisible to the graph. Mirrors the existing Scala class_parameters handling a few lines below. Adds a regression test using the existing sample.kt fixture, which already contained the reproducing case (HttpClient(private val config: Config)) but had no assertion covering it.
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.
Title
Kotlin extractor drops constructor-injected fields (
primary_constructor/class_parametersnever walked)Summary
extract_kotlinnever emitsreferencesedges for dependencies declared in aclass's primary constructor (
class Foo(private val x: Bar)) — theidiomatic Kotlin/Hilt/Dagger DI pattern. Only two Kotlin-specific code paths
exist in
graphify/extractors/engine.py:delegation_specifiers→inherits/implementsedges for supertypes.property_declaration→referencesedges for body-levelval/var.Neither walks
primary_constructor → class_parameters → class_parameter, soevery constructor-injected field is invisible to the graph. Scala already has
the equivalent handling for its structurally identical
class_parametersnode(same file, ~30 lines below the Kotlin block) — Kotlin never got the same
treatment.
Reproduction
Using the project's own fixture,
tests/fixtures/sample.kt:No edge from
HttpClienttoConfigis ever emitted, despiteConfigbeinga required, explicit, statically-typed constructor dependency. Existing tests
(
test_kotlin_parameter_return_generic_and_field_contexts,test_kotlin_user_types_still_emit_references) already assertreferencesedges for a body-level
valonDataProcessor, so the gap was masked —nobody had asserted the primary-constructor case.
Why this matters (real-world impact)
Found while running
/graphifyon a ~600-file, multi-module Kotlin/Androidapp that uses constructor-injection (Hilt) everywhere — the standard pattern
for every ViewModel and Repository. Before the fix:
MealDraftStoreclass injected into 5 production ViewModels viaprivate val mealDraftStore: MealDraftStoreshowed zero incoming edgesfrom any of them in the graph — only from test classes that instantiate it
directly (
MealDraftStore()in a test's.setUp(), captured via thecallspath, which is handled).FirestoreSyncServiceinjected into 3 repositories was equally invisible.same corpus: +292 edges (5,452 → 5,744), and the top god-nodes' degree
jumped accordingly (
GlucoseSettingsRepository58→68,HistoryViewModel41→53,
Food63→72) — all constructor-injection edges that were silentlydropped before.
For any Kotlin codebase using DI via primary-constructor (which is close to
all idiomatic modern Kotlin), this means the dependency graph systematically
undercounts how classes actually depend on each other, skewing god-node
rankings, community detection, and "who uses X" queries.
Fix
Mirror the existing Scala
class_parametershandling(
graphify/extractors/engine.py, Scala block a few lines below the Kotlindelegation_specifiersblock) for Kotlin'sprimary_constructor → class_parameters → class_parameternodes. Patch attached (kotlin-primary- constructor-fix.diff, applies cleanly tov8HEAD as ofabff1b1):graphify/extractors/engine.py: +33 lines, new Kotlin-specific blockright after the existing
delegation_specifiersblock.tests/test_languages.py: +14 lines, one new regression test(
test_kotlin_primary_constructor_val_emits_references) using theexisting
sample.ktfixture — no fixture changes needed, the reproducingcase (
HttpClient(private val config: Config)) was already there.Verified:
pytest tests/test_languages.py— 318 passed, 13 skipped (unrelatedoptional deps), 0 failures, including the 13 Kotlin tests (12 existing
pytest tests/test_extract.py tests/test_multilang.py tests/test_cross_language_call_resolution.py— 174 passed, 12 skipped,0 failures.
produces exactly the expected new edges (constructor-injected ViewModels →
their injected repositories/stores) and nothing spurious.
Scope note
This fixes constructor-parameter type references only. It does not
address a related but separate gap:
when (result) { is Result.Success -> ... }pattern-matching inside function bodies also isn't walked for typereferences anywhere in the extractor (any language) — that's a broader,
more expensive change (walking arbitrary expression bodies) and out of scope
here.