parser: emit REFERENCES edges for Python callback arguments (#363)#424
Merged
tirth8205 merged 1 commit intotirth8205:mainfrom May 7, 2026
Merged
Conversation
…5#363) The dispatcher in `_extract_value_references` only matched the `arguments` AST node type, which is the JS/TS shape. Python's tree-sitter grammar uses `argument_list` for the same construct, so function references in callback positions — `executor.submit(handler)`, `filter(predicate, xs)`, `map(transform, xs)`, `df.apply(fn)`, etc. — silently produced no REFERENCES edges. Downstream effect: `find_dead_code` already checks `has_references` (refactor.py), but for Python the edge was never created, so callback functions were systematically flagged as dead — exactly the high false positive rate users report in tirth8205#363 (executor pools, pandas .apply, higher-order functions, returned closures). Fix is one line of intent: introduce `_ARGUMENTS_TYPES = {"arguments", "argument_list"}` and dispatch on set membership instead of string equality. The existing `_ref_from_arguments` walker already handles identifier children correctly and `_emit_reference_if_known` still gates emission on the name being locally defined or imported, so this does not introduce noise from arbitrary identifiers. Scoped to the dispatcher only: JS/TS shorthand-property, pair-value, and array-element handling are unchanged.
There was a problem hiding this comment.
Pull request overview
Updates the Tree-sitter parser’s “function-as-value” reference extraction so Python call argument containers (argument_list) are handled the same way as JS/TS (arguments), enabling REFERENCES edges for common callback patterns and preventing false-positive dead-code reports.
Changes:
- Expand
_extract_value_referencesdispatch to treat bothargumentsandargument_listas callback-argument containers. - Add a Python fixture demonstrating callbacks passed as bare identifiers (executor.submit / filter / map).
- Add regression tests asserting
REFERENCESedges are emitted and thatfind_dead_codedoes not flag those callbacks as dead.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
code_review_graph/parser.py |
Adds _ARGUMENTS_TYPES and updates value-reference dispatch so Python argument_list triggers _ref_from_arguments. |
tests/fixtures/sample_callback_refs.py |
New fixture covering typical Python callback-as-argument usage patterns. |
tests/test_parser.py |
New tests verifying REFERENCES edge emission and end-to-end dead-code behavior via GraphStore + find_dead_code. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
Closes #363 (Python portion).
Summary
find_dead_codealready considers REFERENCES edges incoming to a node when deciding whether to flag it as dead. But for Python, those edges were never being produced for the most common callback shapes —executor.submit(handler),filter(predicate, xs),map(transform, xs),df.apply(fn), returned inner functions, etc. — so callback functions were systematically reported as unused.Root cause
In
code_review_graph/parser.py,_extract_value_referencesdispatches on the AST node type wrapping a call's arguments:Python's tree-sitter grammar uses
argument_list. The dispatcher never fired for Python, so_ref_from_arguments(which already knows how to walk identifier children) was never invoked.Fix
One-line change in intent: introduce a frozenset of accepted argument-container node types and dispatch on membership.
Mirrors the existing
_PAIR_TYPES/_ARRAY_TYPESstyle in the same class.Why this doesn't add noise
_ref_from_argumentsonly emits when the argument is a bareidentifier, and_emit_reference_if_knownfurther gates emission on the name being locally defined or imported. Soprint(x)wherexis a local variable does not produce a spurious REFERENCES edge to a function namedxin some other file.What this does not fix
executor.submit(callable=h)) —keyword_argumentis a different child shape; out of scope for this PRHANDLERS = my_func) — handled by_ref_from_assignmentalreadySymbol#to_proc, Go function values, etc.) — separate workTest plan
New fixture
tests/fixtures/sample_callback_refs.pycovers the three patterns called out in the issue body (executor.submit, filter, map). Two regression tests intests/test_parser.py:test_python_callback_references_emitted— asserts REFERENCES edges land onexecutor_callback,filter_callback,map_callbacktest_python_callback_references_not_treated_as_dead— end-to-end check: parses the fixture, stores in a realGraphStore, runsfind_dead_code, verifies the three callback functions are NOT in the dead listVerified locally:
uv run pytest tests/test_parser.py -k python_callback -v— 2/2 passinguv run pytest tests/test_parser.py -k \"value_ref or callback or reference\" -q— 13/13 passing (existing JS/TS REFERENCES tests unchanged)uv run ruff check code_review_graph/parser.py tests/fixtures/sample_callback_refs.py— cleanmainbaseline (Windows-onlyPermissionErrorcluster pre-exists on both)