Skip to content

Function-valued consts inside a function body are never indexed (React handlers invisible to callers/impact) #1669

Description

@wenmin-wu

Summary

Function-valued consts are indexed at module scope but not inside a function
body. In a React codebase that means most event handlers are absent from the
graph, and callers / callees / impact answer Symbol not found for them
— which reads identically to "this symbol has no callers".

The boundary is scope, not syntax: a top-level const fn = () => {} indexes
fine, and the same declaration inside a component does not. useCallback is
not involved — a bare arrow inside the component is missing too.

Reproduction

Two files, no dependencies, codegraph init:

src/widget.jsx

import { useCallback } from "react";

export function formatLabel(value) {          // 1. top-level declaration
  return String(value).trim();
}

export const parseLabel = (raw) => raw.split(":")[1];   // 2. top-level arrow const

export class Formatter {                       // 3. class + method
  render(value) { return formatLabel(value); }
}

export default function Widget({ items, onPick }) {
  const handleSelect = useCallback((item) => {  // 4. const in a component, via hook
    onPick(formatLabel(item), parseLabel(item));
  }, [onPick]);

  const handleClear = () => {                   // 5. plain const in a component
    onPick(null, null);
  };

  return items.map((i) => (
    <button key={i} onClick={() => handleSelect(i)} onDoubleClick={handleClear}>
      {formatLabel(i)}
    </button>
  ));
}

src/page.jsx

import Widget, { formatLabel, parseLabel } from "./widget";

export default function Page() {
  const label = formatLabel("a:b");
  const parsed = parseLabel("a:b");
  return <Widget items={[label, parsed]} onPick={() => {}} />;
}

Result

codegraph init reports 10 nodes. Every node in the index:

file         page.jsx         src/page.jsx:1
import       ./widget         src/page.jsx:1
function     Page             src/page.jsx:3
file         widget.jsx       src/widget.jsx:1
import       react            src/widget.jsx:1
function     formatLabel      src/widget.jsx:4
function     parseLabel       src/widget.jsx:9
class        Formatter        src/widget.jsx:12
method       render           src/widget.jsx:13
function     Widget           src/widget.jsx:18

handleSelect (line 19) and handleClear (line 23) are absent.

codegraph callers formatLabel   → 4 callers
codegraph callers parseLabel    → 3 callers
codegraph callers Widget        → 1 caller
codegraph callers handleSelect  → Symbol "handleSelect" not found
codegraph callers handleClear   → Symbol "handleClear"  not found

Note line 9: parseLabel is an arrow function and indexes correctly. Only the
two declared inside Widget are missing.

Expected

handleSelect and handleClear are declared functions with a name, a body and
call sites, and should appear as nodes the same way parseLabel does.

Why it matters

This is the dominant shape for handlers in React, so on a real front end a
large share of the functions an agent needs are invisible. The failure is also
silent in the worst direction: Symbol not found is indistinguishable from
"nothing calls this", so an agent asked to update every call site of a handler
gets an empty answer and concludes there are none. Grep finds them, which is
the fallback CodeGraph is meant to replace.

The same gap presumably affects const x = function () {}, object-literal
methods, and IIFE-scoped helpers in plain JS/TS, though I only measured the
React case.

Environment

  • codegraph 1.6.0 (npm @colbymchenry/codegraph)
  • node v26.7.0
  • macOS 26.4.1, arm64

Possibly related

#1656 (route callers/callees/impact through one symbol resolver) looks adjacent
— though this appears to be extraction rather than resolution, since the nodes
never reach the database at all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions