Skip to content

fix(parsers): stop Ruby nested classes deleting their parent, and extract method parameters - #1581

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/parser-ruby-nesting
Aug 5, 2026
Merged

Shashankss1205 merged 1 commit into
mainfrom
fix/parser-ruby-nesting

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

Fixes #1523 and the Ruby half of #1527.

Full suite: 1128 passed, 7 skipped, no failures.

#1523 — a nested class deleted its parent

Class names were matched to class nodes by byte range: for each @name capture, walk the class list and assign the name to the first class whose range contains it, then break. An outer class always contains an inner class's name node, and dict order is document order — so the inner name landed on the outer class, and the inner entry, never given a name, was dropped at line 323.

class Client
  class TimeoutError < StandardError
  end
end

produced exactly one class: TimeoutError, carrying Client's line range, with StandardError lost. Client vanished from the graph entirely.

Each class now reads its own name field. One subtlety worth recording: tree-sitter-ruby gives the class keyword token the same node type as the definition, so nodes without a name field are skipped rather than treated as anonymous classes.

Namespaced error classes, inner value objects and Struct subclasses are all idiomatic Ruby, so this affected a lot of real files.

#1527 (Ruby) — every method reported zero parameters

_parse_method_parameters scanned the method node's own children for identifiers. Parameters live inside a method_parameters child; the only direct identifier child is the method's name, which the code explicitly skipped. So the result was always [] and no Ruby function ever got a HAS_PARAMETER edge.

Now handles every Ruby parameter form:

def complex(a, b = 1, *rest, key:, **opts, &blk)
# -> ['a', 'b', '*rest', 'key', '**opts', '&blk']

Golden

sample_project_ruby regenerated. Verified semantically rather than by line count:

Parameter   2 -> 13  (+11)
TOTAL      75 -> 86  (+11)
classes gained: []      classes lost: none

Purely additive. The fixture happens to contain no nested classes, so #1523's effect isn't visible in the golden — that case is covered by the unit tests instead.

tests/unit/parsers/test_ruby_parser.py — 7 tests covering nested classes, siblings, class-in-module, and all parameter forms.

Remaining Ruby items

#1538 still tracks the rest of the Ruby findings — def self.x singleton methods never captured, nested calls stealing each other's receiver, @@class_vars/$globals missing, and variable value always None due to id() on freshly-allocated node wrappers. Those are separate fixes and not in this PR.

🤖 Generated with Claude Code

…ract parameters (#1523, #1527)

Nested classes overwrote their parent (#1523)

Class names were matched to class nodes by byte range: for each @name
capture the code walked the class list and assigned the name to the first
class whose range contained it. An outer class always contains an inner
class's name node, so the inner name landed on the outer class, and the
inner entry — never given a name — was dropped.

    class Client
      class TimeoutError < StandardError
      end
    end

yielded exactly one class: TimeoutError, carrying Client's line range,
with StandardError lost. Client vanished from the graph entirely.

Each class now reads its own `name` field. tree-sitter-ruby gives the
`class` keyword token the same node type as the definition, so nodes
without a name field are skipped.

Method parameters were always empty (#1527)

`_parse_method_parameters` scanned the method node's own children for
identifiers, but parameters live inside a `method_parameters` child; the
only direct identifier child is the method name, which the code
explicitly skipped. Every Ruby method reported no parameters, so no Ruby
function ever got a HAS_PARAMETER edge.

Now reads the parameters node and handles every Ruby form:

    def complex(a, b = 1, *rest, key:, **opts, &blk)
    -> ['a', 'b', '*rest', 'key', '**opts', '&blk']

Golden for sample_project_ruby regenerated: +11 Parameter nodes, no nodes
lost, no classes gained or lost (the fixture has no nested classes — that
case is covered by unit tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
code-graph-context-pack Ready Ready Preview Aug 5, 2026 5:43pm

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Hi! 👋 Join our CodeGraphContext Discord channel to collaborate: https://discord.gg/dR4QY32uYQ

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Code Graph Analysis

fix(parsers): stop Ruby nested classes deleting their parent, and extract method parameters (#1581)

📊 Interactive Visualization

View the blast radius graph: PR Reviewer Dashboard

📦 Artifacts

The graph JSON has been uploaded as a build artifact: pr-code-graph-1581


Generated by CodeGraphContext using FalkorDB Lite

@Shashankss1205
Shashankss1205 merged commit b1e459d into main Aug 5, 2026
19 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog tasks to Done in CGC Progress Board Aug 5, 2026
@Shashankss1205
Shashankss1205 deleted the fix/parser-ruby-nesting branch August 5, 2026 17:50
Shashankss1205 added a commit that referenced this pull request Aug 8, 2026
Ten PRs since 0.5.6 — mostly parser correctness, all with regression tests.

Parsers
- Ruby nested classes no longer delete their parent; method parameters
  extracted at all (#1523, #1527, #1581)
- C/C++ functions returning a pointer or reference are extracted, and
  registered in pre_scan so they resolve as call targets (#1524, #1582)
- C# fields and locals parsed, restoring receiver-type inference for
  `var x = new T(); x.M();` (#1525, #1584)
- JavaScript destructured, array and rest parameters extracted, recording
  the binding names rather than a placeholder (#1527, #1591)

Queries and analysis
- Five defects that returned wrong answers without erroring: inflated
  repository stats, misattributed callers, discarded graph_name,
  count(*) after OPTIONAL MATCH, an ignored tool argument (#1577)
- Dead-code detection made correct and self-consistent, including the
  JS module-level false positive (#600, #1559, #1578)

Indexing
- add_package_to_graph on a flat module no longer indexes the whole
  virtualenv (#1528, #1586)
- SCIP job progress no longer exceeds 100% (#1535, #1583)

Docs and tests
- Database Options table columns realigned (#1590)
- MCP tool definition contract validation (#1580)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Shashankss1205 added a commit that referenced this pull request Aug 18, 2026
…issues; finish the #1527 parameter audit (#1648)

* perf(kuzu): batch relationship-only UNWIND MERGEs; scope the per-row fallback to node-MERGE shapes (#1605)

Kuzu 0.11.3's MERGE pipeline mis-binds when a merged NODE's key repeats
non-adjacently in one UNWIND batch ([(A,P),(B,X),(C,P)] binds C to X) —
reproduced minimally and pinned in tests. Relationship-only MERGEs
(MATCH..MATCH..MERGE rel) do not exhibit the bug, verified with interleaved
duplicate endpoint keys and duplicate pairs, so they now run batched: the
full integration suite drops from ~5:06 to ~4:09 (-20%) and a sample-project
index halves. Inheritance batches are also deduped writer-side so a repeated
(child, parent) record cannot double an INHERITS edge on embedded backends.

* fix(indexer): retry a failed per-file graph write once before recording it (#1612)

A transient write failure under runner load silently cost the graph a whole
file's edges (LadybugDB intermittently 51 CONTAINS short in the parity run).
Writes are MERGE-idempotent, so one retry converts the blip into success;
persistent failures still land in write_failures and the error log. Together
with the #1605 batching change (which removes most of the per-row write
pressure the flake correlated with), this addresses the observed drop.

* test(cgcignore): make the CLI cases hermetic and move them to the integration suite (#1422)

The TC-12+ cases shared one fixed /tmp/cgc_test/_home database across every
test and run, wiped it mid-suite, and were gated on a live Neo4j — so they
were permanently skipped on most machines and intermittently failed with a
different name each full-suite run wherever they did run. Each test now gets
a fresh HOME and an explicit embedded backend (no server, no gate, no wipe),
and the subprocess-driven cases live in tests/integration where that cost
belongs. tc01-tc11 (pure PathSpec logic) stay in the unit suite.

* fix(cpp): extract lambda parameters through the shared param helper (#1527)

The inline version first clobbered its correctly-walked parameter_list with
a nonexistent 'parameters' field on lambda_expression, then filtered for
bare identifiers over parameter_declaration nodes — every lambda assignment
got args: []. lambda_expression carries the same declarator→parameters field
chain as function_definition, so the shared extractor handles it, rendering
args in the project's established 'type name' form. Last remaining case of
the #1527 parameter audit (Ruby #1581, PHP key + #1643, JS #1591 all landed
previously).

* regen cpp golden: lambda Parameter nodes now extracted
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

bug(indexer): Ruby nested classes overwrite their parent's identity — outer class disappears from the graph entirely

1 participant