Repository navigation
Conversation
DeclIdRegistry reused the id of any declaration that normalized alike, so class Foo and function foo collapsed into one node. Reuse now needs the same kind or the same spelling, which keeps TS class + namespace merging. Bump the AST cache schema.
|
Thanks for the pull request, @bercedev. A maintainer will review it soon. Want to talk it through while it is in review? Come join us on our Discord server. For longer-form discussion there is also GitHub Discussions. A couple of things that speed up review: make sure the test suite passes on Python 3.10 and 3.13, and that the change keeps extraction deterministic. |
There was a problem hiding this comment.
Graphify reviewed this change.
No issues found by static checks — no coupling regressions or blocking issues in the code graph. That is not the same as safe to merge: see what was not checked.
Not checked: tests were not run; formal verification proved 0 of 2 changed function(s) (2 sampled, not proven).
Formal verification. PR-changed functions: 2/2 verified (0 proven, 2 may-equivalent, 0 distinguished) · 0 not verified.
Graphify review — findings
Fixes node-id collisions where a type and a function whose names differ only in letter case were merged into one node. They now get separate ids (#4904), while the same spelling across kinds still resolves to a single declaration and a call stays attached to the function rather than the class. To make that work, the id helpers gain a DeclIdRegistry that tracks declarations and hands out case-distinct ids. Bumping _AST_CACHE_SCHEMA to 32 forces cached extractions to re-run, so existing caches are rebuilt once on first use.
No blocking issues surfaced. 2 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 5126 functions depend on the 111 functions this change touches.
Health — this change adds coupling hotspots:
- worse:
extract()— 1081 callers, 63 callees
Verification — 5126 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 2256 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
180 of 480 test file(s) selected (38%) via static blast radius.
tests/test_alias_folding.py— impacttests/test_analyze.py— impacttests/test_astro_extraction.py— impacttests/test_astro_import_ids.py— impacttests/test_atomic_writes.py— impacttests/test_benchmark.py— impacttests/test_benchmark_raw_graph.py— impacttests/test_benchmark_real_query.py— impacttests/test_binary_media_words.py— impacttests/test_build.py— impacttests/test_build_located_semantic_identity.py— impacttests/test_build_merge_dedup_scope.py— impacttests/test_build_merge_hyperedges_and_prune.py— impacttests/test_build_merge_shrink_guard.py— impacttests/test_c_include_resolution.py— impacttests/test_cache.py— impacttests/test_cache_long_paths.py— impacttests/test_cache_stale_import_target.py— impacttests/test_carried_hyperedge_remap.py— impacttests/test_case_only_decl_ids_4904.py— impact, changed-testtests/test_charmap_encoding.py— impacttests/test_chunking.py— impacttests/test_cjs_module_extension.py— impacttests/test_cli_export.py— impacttests/test_cluster.py— impacttests/test_community_labels_skill.py— impacttests/test_compact_graph_json.py— impacttests/test_confidence.py— impacttests/test_confidence_score_output.py— impacttests/test_corrupt_graph_json.py— impacttests/test_cpp_nested_and_cli.py— impacttests/test_cpp_objc_cross_file_calls.py— impacttests/test_cross_extension_reexport_self_cycle.py— impacttests/test_csharp_method_owner_labels.py— impacttests/test_csharp_object_creation.py— impacttests/test_csharp_partial_classes.py— impacttests/test_csharp_type_resolution.py— impacttests/test_dart.py— impacttests/test_dedup.py— impacttests/test_dedup_remaps_hyperedges.py— impacttests/test_dedup_shrink_refuses_force_write.py— impacttests/test_dedup_survivor_richness.py— impacttests/test_definition_file_portability.py— impacttests/test_detect.py— impacttests/test_dotnet.py— impacttests/test_duplicate_annotation_edges.py— impacttests/test_edge_also_seen.py— impacttests/test_ejs_extractor.py— impacttests/test_evidence_binding.py— impacttests/test_export.py— impact- … and 130 more
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
No difference found (not proven): No behavior difference found in \_\_init\_\_ (not a proof).
The verifier ran both versions of \_\_init\_\_ on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
No difference found (not proven): No behavior difference found in resolve (not a proof).
The verifier ran both versions of resolve on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.
Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.
Note: An input the sampler did not try could still differ.
· 1 more finding(s) on lines outside this diff (see the check run).
…for video + extractors The four @ayushcodes10 extractor fixes (#4902 R, #4900 elixir, #4899 dart, #4897 fortran) change per-file AST output and are AST-cached; they share the global 31->32 bump already landed via #4908/#4895. Extend the comment to credit them, and add the 0.9.100 changelog entries for #4842 and the four. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Landed in v0.9.100, now on PyPI (pip install graphifyy==0.9.100). A type and a function differing only in case get distinct node ids instead of colliding. Thanks for the fix, @bercedev. |
What does this PR do?
Closes #4904
class Fooandfunction fooin one file normalize to the same id, andDeclIdRegistry.resolvetreated the second one as a duplicate of the first. One node got dropped and its edges went to the survivor, sofoo()callingFooshowed up as aFoo -> Fooself loop.Now an id is only reused when the declaration has the same kind or the exact same spelling. The other one gets a salted id, and the AST cache schema goes to 32 so old cache entries don't hide the fix.
This is a bit different from the diff in the issue. Kind alone also splits
class Foo+namespace Fooin TS, which is one merged declaration, so same spelling across kinds still counts as a duplicate. There's a test for it and it fails with the kind-only check.Limitation, also in the issue: two declarations of the same kind still collapse (
class Foo/class FOO,function foo/function Foo). Should cover theFallListe/fallListepair from #3726 too since that's a type and a function, I didn't run that file. #3738 is a pre-scan for that one in TS and JS only, this doesn't touch it.Type of change
Verification & Invariants
Invariant: a type and a function that differ only in case keep their own nodes, a real redeclaration or merged declaration stays one node.
How was this tested?
tests/test_case_only_decl_ids_4904.pyhas the JS, TS, Kotlin, C++, PHP, Ruby and Swift cases from the issue:Full suite,
--network=none, no.git: 18 failed, 9166 passed, 34 skipped, 1 xfailed. The 18 are thetest_security.pyDNS andtest_skillgen.pyorigin/v8ones that #4894 fixes, they fail on a clean v8 in that setup too.ruff checkis clean on the changed files.Salted ids mean a node id can change for files that have this pair, that's what the schema bump is for.
Graphify-specific checklist
uv run python -m tools.skillgen --bless) when changing their source fragments.Note: I used Claude Code to write this and ran the tests above locally.