feat(compose): flag composables and resolve @Preview into PREVIEWS edges - #1624
feat(compose): flag composables and resolve @Preview into PREVIEWS edges#1624rrodriguesNutrium wants to merge 2 commits into
Conversation
|
@rrodriguesNutrium is attempting to deploy a commit to the shashankss1205's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Android/Kotlin series — merge order Each PR targets Happy to split, reorder, or squash any of these differently if it suits review better. |
Adds `is_composable` to Function nodes and a PREVIEWS relationship from each @Preview-annotated function to the composable it renders. - kotlin.py sets is_composable from a module-level ^@composable\b match - database_embedded_kuzu.py declares the column, the SCHEMA_MAP entry and the migration (all three are required; missing any one silently drops the property on Kuzu while schemaless backends keep it) - build_previews_links rejects annotation-echo phantom calls: the parser records `@Preview` argument expressions as calls, so a preview function appears to call itself and every composable named in its annotation Stacked on the Hilt slice.
ea2e3ba to
db56984
Compare
|
Rebased onto current The conflict came from #1620/#1622/#1623 landing as squash merges: identical content under new SHAs, so the stacked commits read as duplicate changes. Dropped them and replanted just the compose commit on top of Now 1 commit, +772/-2. |
…deGraphContext#1602) An annotation with arguments is `annotation -> @ + constructor_invocation` in the tree-sitter Kotlin grammar, and KOTLIN_QUERIES["calls"] captures `constructor_invocation`, so every such annotation was recorded as a call made by the annotated declaration. `@Preview(showBackground = true)` on `fun GreetingPreview()` became a call from GreetingPreview to "Preview". The echo also reached expressions *inside* the argument list: `@PreviewParameter(provider = FooProvider::class)` contributed a `callable_reference` capture, which is why a preview function appeared to call every type named anywhere in its own annotations. Filtered at source in _parse_calls via _is_within_annotation, an ancestor walk for an annotation node. The parse tree is the discriminator, not the name: `B()` in `class A : B()` is also a `constructor_invocation` but its chain runs through `delegation_specifier`, so it survives, as does a `constructor_delegation_call` under `secondary_constructor`. Keying on the node type alone would have deleted both. The walk accepts two node types rather than one. Declaration-level annotations nest under `annotation` -- functions, classes, objects, interfaces, typealiases, properties, and use-site targets like `@field:ColumnInfo(...)` and `@get:JvmName(...)`. File-level annotations are the exception: `@file:JvmName("Utils")` produces `file_annotation` with no `annotation` in the chain, so checking only `annotation` would have left the phantom at the top of every file using a `@file:` target. Each position has a test. This is the bug CodeGraphContext#1624 previously worked around rather than fixed. build_previews_links keeps its own_decorator_names rejection as a backstop (it is pinned by a test feeding hand-built rows, so it stays meaningful now that parser output no longer carries the echo), with the docstring updated to say the parser filters at source. Golden refresh: the Kotlin golden was already stale on this branch -- AndroidAnnotations.kt gained the compose functions, shifting line numbers for UserViewModel, UserEntity, PlainHelper and findAll -- so the integration golden test was red before this commit and is green after. Regenerating twice, with and without this fix, isolates its own effect to CALLS 28 -> 24 (-4). Node counts and every other edge type -- PREVIEWS, BINDS, INHERITS, CONTAINS, HAS_PARAMETER, IMPORTS, COMPANION_OF -- are identical across the two regenerations. At parser level the fix removes 6 phantom rows across the fixture; 4 had resolved into graph edges. Three are in Annotations.kt (`@Fancy(...)`, a plain non-Android fixture), confirming the issue was never Compose- or Android-specific. tests/unit: 3 failed, 1246 passed (was 3 failed, 1238 passed before this commit -- the same 3 pre-existing macOS /var-vs-/private/var failures of CodeGraphContext#1608, which reproduce on a clean checkout). tests/integration: 45 passed. Closes CodeGraphContext#1602 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…deGraphContext#1602) An annotation with arguments is `annotation -> @ + constructor_invocation` in the tree-sitter Kotlin grammar, and KOTLIN_QUERIES["calls"] captures `constructor_invocation`, so every such annotation was recorded as a call made by the annotated declaration. `@Preview(showBackground = true)` on `fun GreetingPreview()` became a call from GreetingPreview to "Preview". The echo also reached expressions *inside* the argument list: `@PreviewParameter(provider = FooProvider::class)` contributed a `callable_reference` capture, which is why a preview function appeared to call every type named anywhere in its own annotations. Filtered at source in _parse_calls via _is_within_annotation, an ancestor walk for an annotation node. The parse tree is the discriminator, not the name: `B()` in `class A : B()` is also a `constructor_invocation` but its chain runs through `delegation_specifier`, so it survives, as does a `constructor_delegation_call` under `secondary_constructor`. Keying on the node type alone would have deleted both. The walk accepts two node types rather than one. Declaration-level annotations nest under `annotation` -- functions, classes, objects, interfaces, typealiases, properties, and use-site targets like `@field:ColumnInfo(...)` and `@get:JvmName(...)`. File-level annotations are the exception: `@file:JvmName("Utils")` produces `file_annotation` with no `annotation` in the chain, so checking only `annotation` would have left the phantom at the top of every file using a `@file:` target. Each position has a test. This is the bug CodeGraphContext#1624 previously worked around rather than fixed. build_previews_links keeps its own_decorator_names rejection as a backstop (it is pinned by a test feeding hand-built rows, so it stays meaningful now that parser output no longer carries the echo), with the docstring updated to say the parser filters at source. Golden refresh: the Kotlin golden was already stale on this branch -- AndroidAnnotations.kt gained the compose functions, shifting line numbers for UserViewModel, UserEntity, PlainHelper and findAll -- so the integration golden test was red before this commit and is green after. Regenerating twice, with and without this fix, isolates its own effect to CALLS 28 -> 24 (-4). Node counts and every other edge type -- PREVIEWS, BINDS, INHERITS, CONTAINS, HAS_PARAMETER, IMPORTS, COMPANION_OF -- are identical across the two regenerations. At parser level the fix removes 6 phantom rows across the fixture; 4 had resolved into graph edges. Three are in Annotations.kt (`@Fancy(...)`, a plain non-Android fixture), confirming the issue was never Compose- or Android-specific. tests/unit: 3 failed, 1246 passed (was 3 failed, 1238 passed before this commit -- the same 3 pre-existing macOS /var-vs-/private/var failures of CodeGraphContext#1608, which reproduce on a clean checkout). tests/integration: 45 passed. Closes CodeGraphContext#1602 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bbd67db to
8d8d4dc
Compare
Adds two Compose-aware facts to the graph:
is_composableonFunction, and aPREVIEWSedge from each@Previewfunction to the composable it renders. Now also fixes #1602 at source, which this PR previously only worked around.Both Compose facts exist to answer questions that are currently unanswerable. A Compose UI tree is a call graph of
@Composablefunctions and nothing marks them as such; and a@Previewfunction looks exactly like dead code — nothing calls it, because the IDE and the Compose tooling do.is_composableSet from an anchored module-level match,
^@Composable\b. Anchored and word-bounded on purpose: a substring test would also match@NonComposable-style names, and\bkeeps it from matching a longer annotation that merely starts with the same letters.Declared in all three places the Kuzu backend requires — node-table declaration,
SCHEMA_MAPallow-list, andsimple_migrations. Missing any one of the three loses the property with no error:CREATE NODE TABLEon an existing database throws "already exists" and is swallowed, andSCHEMA_MAPis an allow-list whose misses are dropped silently at write time. The failure mode is that Kuzu quietly lacks the property while schemaless backends have it, so the two backends disagree and nothing reports it.PREVIEWSFROM Function TO Function. Source is the@Preview-annotated function, target is the composable it calls.Annotation matching strips arguments down to the bare name and compares for equality against
"Preview". That's the word-boundary-safe equivalent of theis_composableregex, and it's whyPreviewParameterandPreviewScreenSizesare correctly excluded without needing a second annotation-name parser.Resolution is scoped to calls in the preview function's own body, so an unrelated call (a logging helper, say) isn't mistaken for the previewed composable.
Second commit: fixing #1602 rather than working around it
KOTLIN_QUERIES["calls"]capturesconstructor_invocationnodes — and an annotation carrying arguments is aconstructor_invocationin this grammar. So@Preview(showBackground = true)onfun GreetingPreview()was recorded as a call, fromGreetingPreview, namedPreview.The echo also reached expressions inside the argument list:
@PreviewParameter(provider = FooProvider::class)contributes acallable_referencecapture. That's why a preview function appeared to call itself and every type named anywhere in its own annotations.The first version of this PR filtered these annotation-echo rows inside
build_previews_linksand left the parser alone. That was the wrong layer — the phantoms are present infunction_callsfor Kotlin generally, not only under Compose. The second commit filters at source instead.The discriminator is the parse tree, not the name.
_is_within_annotationwalks ancestors looking for an annotation node:@Preview(showBackground = true)constructor_invocation <- annotationFooProvider::classin an annotation argcallable_reference <- value_argument <- … <- annotation@file:JvmName("Utils")constructor_invocation <- file_annotationclass A : B()constructor_invocation <- delegation_specifierconstructor() : this(1)constructor_delegation_call <- secondary_constructorA filter keyed on the node type alone would have deleted the last two — every superclass-construction and constructor-delegation edge in the Kotlin graph. The regression guards for both are in the test class.
Two node types are accepted, not one. Declaration-level annotations nest under
annotation, which covers functions, classes, objects, interfaces, typealiases, properties, and use-site targets like@field:ColumnInfo(...)and@get:JvmName(...). File-level annotations are the exception —@file:JvmName("Utils")producesfile_annotationwith noannotationanywhere in the chain, so checking onlyannotationwould have left the phantom at the top of every file using a@file:target. Each position has a test.build_previews_linkskeeps itsown_decorator_namesrejection as a backstop rather than dropping it now that the parser is clean. It costs one set lookup, and its test feeds hand-built rows rather than parser output, so it still pins a real invariant. The docstring now says the parser filters at source.Golden refresh
The Kotlin golden was already stale on this branch before the fix commit —
AndroidAnnotations.ktgained the compose functions, shifting line numbers forUserViewModel,UserEntity,PlainHelperandfindAll.tests/integration/test_parser_goldens.py -k kotlinwas red on the first commit and is green now. (This is the refresh @Shashankss1205 flagged on #1623 as owed by this PR.)Unlike every previous golden refresh in this series, this one removes edges, so I regenerated twice — once without the fix, once with — to isolate what the fix itself changes:
Only
CALLSmoves. At parser level the fix drops 6 phantom rows across the fixture, of which 4 had resolved into graph edges:Three of the six are in
Annotations.kt— a plain, non-Android Kotlin fixture using@Fancy(...)— which confirms the bug was never Compose- or Android-specific.Verification
Measured on this branch, on macOS:
Baseline on the first commit of this PR was
3 failed, 1238 passed— the same 3 failures, +8 new tests. Those 3 are #1608 (tempfilereturns/var/…while the parser resolves to/private/var/…); they reproduce on a clean checkout ofmainand are unrelated to this PR. Integration was red before the fix commit (stale golden) and is green after.The fixture declares
@Composable/@Previewas local stub annotations, so no Compose compiler or Android SDK is needed on the test path. Tests cover the negative cases directly:@PreviewParametermust not be treated as@Preview, and the self-edge from the annotation echo must not appear.Commits
Last of the five-PR Android/Kotlin series; its three prerequisites (#1620, #1622, #1623) have merged, and #1621 (gradle) was independent and has also merged.
Closes #1602