Skip to content

fix(dead-code): count JSX prop function references as usage (closes #294)#295

Merged
Wolfvin merged 1 commit into
mainfrom
fix/issue-294-jsx-handler-references
Jul 14, 2026
Merged

fix(dead-code): count JSX prop function references as usage (closes #294)#295
Wolfvin merged 1 commit into
mainfrom
fix/issue-294-jsx-handler-references

Conversation

@Wolfvin

@Wolfvin Wolfvin commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Root cause

.tsx files are parsed by parsers/tsx_parser.py (TSXParser, routed from commands/scan.py:711) — NOT ts_backend_parser.py. Its tree walk emitted CALLS edges only for call_expression / new_expression / JSX component elements. A function passed as a JSX prop value (onClick={handleClick}) is a bare identifier inside a jsx_expression container — a reference, not a call — so it produced zero edges. ref_count is computed from incoming edges, so every React event handler got rc=0 / status=dead (mass false-positive on the primary React use case).

Fix

scripts/parsers/tsx_parser.py — new _process_jsx_expression, dispatched from the tree walk on jsx_expression nodes. It emits a usage edge (via_jsx_ref) from the enclosing function to a referenced name, but ONLY when that name resolves to a function declared in the same file (declared guard) — arbitrary identifiers / DOM props / non-function names are never counted. Two shapes handled:

  1. Attribute/child reference: onClick={handleClick} (direct identifier).
  2. Callback argument: {items.map(renderItem)} (identifier in a call's arguments).

Double-counting avoided: the function position of a call_expression is skipped (already emitted by _parse_call), nested jsx_expression nodes are left to the outer walk, and member_expression (items.map, this.x) is skipped.

Before / after (repro from the issue)

fn before after
handleClick (onClick={handleClick}) rc=0 dead rc=1 active
handleSubmit (onSubmit={handleSubmit}) rc=0 dead rc=1 active
renderItem ({items.map(renderItem)}) rc=0 dead rc=1 active
directlyCalled (directlyCalled()) rc=1 active rc=1 active (no regress)
reallyUnused / unusedHandler (control) rc=0 dead rc=0 dead (still dead)

Tests

Added 4 golden assertions + a App.tsx fixture to tests/test_graph_accuracy_golden.py: JSX-prop handler rc>=1, callback-arg rc>=1, direct-call not regressed, genuinely-unused handler still dead.

Wide-net run (all pass):

pytest tests/test_graph_accuracy_golden.py tests/test_graph_model.py tests/test_deadcode_engine.py tests/test_dead_code_command.py
63 passed

Also green: test_ts_backend_parser.py, test_js_backend_parser.py.

🤖 Generated with Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@Wolfvin
Wolfvin merged commit a87ec85 into main Jul 14, 2026
2 of 8 checks passed
@Wolfvin
Wolfvin deleted the fix/issue-294-jsx-handler-references branch July 14, 2026 12:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant