Skip to content

fix: exclude parameters and interface/type members from dead-role classification#1811

Closed
carlos-alm wants to merge 1 commit into
fix/issue-1721-batch-complexity-file-targetfrom
fix/issue-1723-roles-dead-params-interfaces
Closed

fix: exclude parameters and interface/type members from dead-role classification#1811
carlos-alm wants to merge 1 commit into
fix/issue-1721-batch-complexity-file-targetfrom
fix/issue-1723-roles-dead-params-interfaces

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

  • codegraph roles --role dead force-assigned dead-leaf to every function parameter unconditionally (via a WHERE kind IN ('parameter', 'property') bypass), regardless of whether the parameter was used.
  • Interface/type members (e.g. Owner.member declarations with no call edges by construction) fell through to dead-unresolved with no way to distinguish them from a genuinely dead class method.
  • Fixed both the TS/WASM classifier (src/graph/classifiers/roles.ts, src/features/structure.ts) and the mirrored native Rust classifier (crates/codegraph-core/src/graph/classifiers/roles.rs) so parameters get no dead-role at all and interface/type members classify as leaf.

Closes #1723

Test plan

  • npm test — full suite green (3356 passed, 0 failed)
  • npm run lint — clean
  • Repro: roles --role dead --file src/extractors/helpers.ts -T --json now returns 0 false positives (was 56)
  • Whole-repo check on both WASM and a locally rebuilt native .node binary: identical results, 0 dead-role false positives for parameters/interface members across 18.9k nodes; 64 genuinely dead functions still correctly flagged

Note: this PR is stacked on #1808 (base branch fix/issue-1721-batch-complexity-file-target) — only the roles.ts/structure.ts/roles.rs/test diff is this issue's actual change.

…ssification

codegraph roles --role dead flagged every function parameter as dead-leaf and
every interface/type member as dead-unresolved, regardless of actual usage.

Root cause: the role classifier's fan-in-based "no callers = dead" heuristic
is meaningless for these two symbol categories, since neither can ever have
inbound call edges by construction:

- Parameters: `kind = 'parameter'` nodes were unconditionally force-assigned
  dead-leaf via a fast-path bypass in classifyNodeRolesFull/Incremental (TS)
  and do_classify_full/do_classify_incremental (native), before any fan-in
  was even computed. A parameter's liveness is a local dataflow question (is
  it referenced within its own function body), not a call-graph reachability
  question, so this produced ~90% noise in --role dead output.

- Interface/type members: every language extractor qualifies interface/type
  members as `Owner.member` top-level definitions (mirroring class method
  qualification), and they never receive inbound call edges (they're
  consumed via type annotations, not calls). Lacking any recognition of
  this, they fell through the normal fan-in/fan-out path and landed in
  dead-unresolved.

Fix:
- Parameters are now fully excluded from role classification (role stays
  NULL), the same treatment already given to file/directory nodes.
- Interface/type members are now recognized in the classifier by resolving
  the Owner.member prefix against same-file TYPE_DEF_KINDS declarations
  (interface/type/struct/enum/trait/record) and classified `leaf`
  unconditionally. Class methods use the identical Owner.member naming
  convention but are unaffected since `class` is not in TYPE_DEF_KINDS, so
  real dead methods/functions remain detected.

Applied to both the TS/WASM classifier (graph/classifiers/roles.ts,
features/structure.ts) and the native Rust classifier
(graph/classifiers/roles.rs) per the dual-engine parity requirement; verified
both engines produce identical results end-to-end against this repo's own
graph.

Fixes #1723

Impact: 6 functions changed, 5 affected
@greptile-apps

greptile-apps Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes two false-positive dead-code classification bugs (#1723): function parameters were unconditionally assigned dead-leaf via a WHERE kind IN ('parameter', 'property') SQL bypass, and interface/type members (e.g. Owner.member declarations) fell through to dead-unresolved with no way to distinguish them from genuinely dead class methods. The fix is applied symmetrically across the TS/WASM classifier and the native Rust classifier.

  • Parameters are removed from the SQL leaf queries in both structure.ts and roles.rs and from the main rows queries, so they receive no role at all \u2014 consistent with file/directory nodes.
  • A new isTypeDeclarationMember / is_type_declaration_member helper short-circuits classification for method/property-kind members of interface/type/struct/enum/trait/record owners, returning leaf unconditionally.
  • Both changes are covered by focused unit tests including a regression repro matching the exact issue codegraph roles --role dead flags used function parameters and interface members as dead #1723 scenario.

Confidence Score: 4/5

The fix is correct and well-tested for the practical cases; only minor consistency gaps remain between the pure-function and SQL-backed paths.

The core logic is sound: parameters are excluded from all queries and receive no role, and interface/type members are correctly short-circuited to leaf via the new isTypeDeclarationMember helper in both TS and Rust. Two minor inconsistencies remain: LEAF_KINDS still includes parameter in both roles.ts and roles.rs, and the SQL leaf path unconditionally marks all property-kind nodes as dead-leaf before isTypeDeclarationMember can inspect them.

src/graph/classifiers/roles.ts and crates/codegraph-core/src/graph/classifiers/roles.rs (stale LEAF_KINDS); src/features/structure.ts has the latent pure-vs-SQL divergence for property-kind type members.

Important Files Changed

Filename Overview
src/graph/classifiers/roles.ts Adds computeTypeDefNamesByFile + isTypeDeclarationMember helpers and wires them into classifyNodeRole; parameter removed from SQL queries but LEAF_KINDS still lists it (stale).
src/features/structure.ts Removes parameter from both the full and incremental leaf SQL queries; SQL leaf path still bypasses isTypeDeclarationMember for property-kind type members.
crates/codegraph-core/src/graph/classifiers/roles.rs Mirrors TS changes: removes parameter from leaf queries, adds compute_type_def_names_by_file + is_type_declaration_member, threads is_type_member flag through classify_node; LEAF_KINDS still includes parameter (stale).
tests/graph/classifiers/roles.test.ts Adds four focused tests for interface/type member exemption including the class-method non-exemption guard; coverage is solid for the method-kind path.
tests/unit/roles.test.ts Adds three DB-backed integration tests covering parameter exclusion, interface-member classification, and the full regression repro; well-isolated with clear intent.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Node from DB] --> B{kind?}
    B -->|parameter| C[Excluded from all queries\nrole = NULL]
    B -->|property| D[Leaf SQL query\ndead-leaf]
    B -->|file / directory| E[Excluded from all queries\nrole = NULL]
    B -->|method / function / class / etc.| F[Main rows query]
    F --> G{isTypeDeclarationMember?\nOwner in TYPE_DEF_KINDS\nsame file}
    G -->|yes| H[leaf]
    G -->|no| I{fanIn == 0 &&\nnot exported?}
    I -->|yes| J[classifyDeadSubRole\ndead-leaf / dead-ffi / dead-entry / dead-unresolved]
    I -->|no| K{fanIn > 0?}
    K -->|yes| L[core / utility / adapter / leaf\nbased on fan shape]
    K -->|no, exported| M[entry]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[Node from DB] --> B{kind?}
    B -->|parameter| C[Excluded from all queries\nrole = NULL]
    B -->|property| D[Leaf SQL query\ndead-leaf]
    B -->|file / directory| E[Excluded from all queries\nrole = NULL]
    B -->|method / function / class / etc.| F[Main rows query]
    F --> G{isTypeDeclarationMember?\nOwner in TYPE_DEF_KINDS\nsame file}
    G -->|yes| H[leaf]
    G -->|no| I{fanIn == 0 &&\nnot exported?}
    I -->|yes| J[classifyDeadSubRole\ndead-leaf / dead-ffi / dead-entry / dead-unresolved]
    I -->|no| K{fanIn > 0?}
    K -->|yes| L[core / utility / adapter / leaf\nbased on fan shape]
    K -->|no, exported| M[entry]
Loading

Comments Outside Diff (3)

  1. src/graph/classifiers/roles.ts, line 32 (link)

    P2 Stale LEAF_KINDS entry for parameter — the constant still includes 'parameter', which means any caller that passes a parameter-kind node directly to classifyRoles or classifyDeadSubRole (e.g. an external consumer of the exported classifyRoles, a future unit test, or the existing test file if it adds a parameter node) will get dead-leaf back instead of no role. The top-of-file doc comment states "parameter-kind nodes never reach this module in production", but the constant itself is a public-facing footgun for any caller that isn't the SQL-backed path. The same issue exists at line 17 of roles.rs.

    Fix in Claude Code

  2. crates/codegraph-core/src/graph/classifiers/roles.rs, line 17 (link)

    P2 Same stale LEAF_KINDS issue as in roles.ts: "parameter" is still present, so classify_dead_sub_role would return "dead-leaf" for any parameter-kind node that reaches it (which can't happen today due to the SQL exclusion, but becomes a silent footgun if the exclusion is ever relaxed or bypassed in tests). Removing "parameter" here keeps the constant consistent with the stated design in do_classify_full's comment.

    Fix in Claude Code

  3. src/features/structure.ts, line 785-800 (link)

    P2 SQL leaf path bypasses isTypeDeclarationMember for property-kind type members

    The leaf SQL query (WHERE kind = 'property') assigns dead-leaf to every property-kind node before the classifier runs, so isTypeDeclarationMember never gets a chance to inspect them. The pure classifyRoles function correctly returns leaf for Foo.bar (kind: property) when Foo is an interface/type owner, and the pure-function test in roles.test.ts explicitly covers this case. If the extractor is later fixed to emit property kind for type-alias property signatures (rather than the current method mislabeling tracked separately), the SQL path will silently misclassify those nodes as dead-leaf while the pure function path would return the correct leaf.

    Fix in Claude Code

Fix All in Claude Code

Reviews (1): Last reviewed commit: "fix: exclude parameters and interface/ty..." | Re-trigger Greptile

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Codegraph Impact Analysis

6 functions changed5 callers affected across 2 files

  • classifyNodeRolesFull in src/features/structure.ts:779 (1 transitive callers)
  • classifyNodeRolesIncremental in src/features/structure.ts:939 (1 transitive callers)
  • computeTypeDefNamesByFile in src/graph/classifiers/roles.ts:75 (4 transitive callers)
  • isTypeDeclarationMember in src/graph/classifiers/roles.ts:107 (4 transitive callers)
  • classifyNodeRole in src/graph/classifiers/roles.ts:247 (4 transitive callers)
  • classifyRoles in src/graph/classifiers/roles.ts:287 (3 transitive callers)

@carlos-alm carlos-alm deleted the branch fix/issue-1721-batch-complexity-file-target July 5, 2026 22:29
@carlos-alm carlos-alm closed this Jul 5, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant