Commit Graph
1839 Commits
Author SHA1 Message Date
ayushcodes10 f97cf99b87 Add a regression test for the PHP JS id collision edge drop
Covers a same name PHP/JS function pair in one file: exactly one
surviving node, the JS call to the colliding name absent as a calls
edge entirely rather than misattached, and the unrelated JS symbol's
own contains edge from the shared file node still present.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit f59ce06c8c)
2026-09-18 15:15:58 +01:00
ayushcodes10 96e653cc7c Drop edges of a JS node dropped for colliding with a PHP node
A reviewer on this PR pointed out that when a JS symbol's id collides
with an existing PHP node (same name, one file, resolved in favor of
the PHP node), the merge still appended every JS edge unconditionally.
An edge meant for the discarded JS symbol silently attached to the
unrelated PHP node sharing its id instead of vanishing with it. The
file node's own always on collision (both passes deliberately reuse
its id) is excluded from this so an unrelated JS symbol's own edges
sourced from the file node are unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit c96169b320)
2026-09-18 15:15:58 +01:00
ayushcodes10 cc80351872 Add regression tests for the PHP inline script extraction
Covers the issue's exact reproduction (two functions plus the call
edge between them), that line numbers survive the masking pass, that
a script tag with only a src attribute contributes nothing without
crashing, and that a plain PHP file with no script tag is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 2d408de529)
2026-09-18 15:15:58 +01:00
ayushcodes10 01ae1edc03 Index JavaScript in inline script blocks of a PHP file
The PHP tree sitter grammar treats a span that is not PHP, such as an
inline script block, as opaque markup, so any JavaScript declared
there was entirely absent from the graph: functions, calls, and
everything else in that region.

extract_php now runs a second pass after the PHP extraction. Every
part of the source that is not a script body is blanked to spaces,
preserving newlines so line numbers stay accurate, and the masked
source is parsed with the JS grammar the same way the Vue extractor
already masks everything outside its own script block. The two node
sets are merged onto the one file node, so a symbol declared in the
script block looks like any other symbol in the file. A script tag
with only a src attribute and no body masks to an empty region and
contributes nothing, and a file with no script tag at all skips the
second pass entirely.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 2114766c91)
2026-09-18 15:15:57 +01:00
oleksii-tumanov bdb605fd8a fix: preserve Markdown links on parse failure
(cherry picked from commit 3a9cd7b8dd)
2026-09-18 15:15:57 +01:00
oleksii-tumanov e96235cb1f fix: reconcile Markdown link suffixes
(cherry picked from commit 4da224338c)
2026-09-18 15:15:57 +01:00
safishamsiandClaude Opus 4.8 41c0407504 docs(diagnostics): note ref_ external refs and external=True stubs are distinct (#3590 follow-up)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-17 11:10:31 +01:00
safishamsiandClaude Opus 4.8 4e811363a6 test(kotlin): cover companion members + incremental persistence (#1698 follow-up)
#3598 resolves Kotlin object/class-qualified member calls across files. Adds
two regression tests the PR omitted:

- a `companion object` member call (`Service.create()`), pinning the #2565
  enclosing-class attribution the resolver relies on;
- an incremental-rebuild test routed through the real watch._rebuild_code, so
  it actually exercises the context builder's allow-list carrying _callable_class
  + method edges (a hand-fed resolution_context_nodes list would bypass exactly
  the path that silently regressed in sibling PRs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-17 11:09:52 +01:00
ayushcodes10 4afdfcbaaa Add regression tests for the cross file Kotlin object member call fix
Covers three cases: the new cross file resolution (Config.load() called
from another file resolves to object Config), a same file control to
prove the existing in file resolution is unaffected, and an ambiguous
receiver defined in two packages that must yield no edge under the
exactly one candidate guard.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit f22f17b575)
2026-09-17 11:05:12 +01:00
ayushcodes10 095530a335 Add and register the Kotlin object member call resolver
Fixes issue 1698.

Mirrors _resolve_kotlin_qualified_calls just above it and the same
type_by_name plus methods_by_type shape _resolve_swift_member_calls
uses for its own type qualified case: index every Kotlin sourced
class or object by its bare name, resolve the receiver against that
index, then look up the callee among that type's methods. Both steps
bail on anything but exactly one candidate, the same god node guard
every cross file resolver in this module already uses.

Verified end to end: the issue's own cross file repro now produces an
EXTRACTED calls edge, the same file control case is unaffected, and
two objects sharing the same name in different packages correctly
produce zero edges instead of an ambiguous guess.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit a6c89df809)
2026-09-17 11:05:12 +01:00
ayushcodes10 492ea4fda8 Thread the object receiver into raw_calls for Kotlin
Still toward issue 1698. Still no observable behavior change on its
own: nothing reads the new kotlin_object_receiver field yet, so
existing edges are unaffected. The resolver that consumes it lands
in the next commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit aed98d6395)
2026-09-17 11:03:41 +01:00
ayushcodes10 2595d62859 Capture the receiver for a two segment Kotlin navigation call
Still toward issue 1698, still no behavior change: the variable is
computed but nothing reads it yet.

A capitalized two segment receiver (MessageHelper in
MessageHelper.isRoomClosed()) names an object singleton or a class or
companion member, statically unambiguous in source. Captured
separately from member_receiver so the existing in file bare name
lookup and its capitalized receiver deferral guard are untouched;
threading this into raw_calls for a dedicated cross file resolver
lands next.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 7728d99295)
2026-09-17 11:03:41 +01:00
ayushcodes10 3507b4a774 Add a variable for the Kotlin object member call receiver
Toward issue 1698: a call like MessageHelper.isRoomClosed() has
exactly two navigation segments, so the existing fully qualified
prefix capture (which only fires at three or more segments) never
sees it, and the code deliberately never stamps member_receiver for
this shape either, since that would defer the in file bare name
lookup that already resolves the same file case correctly.

This adds the new variable with no behavior change yet; capturing
into it and threading it through to raw_calls lands in the next
commits.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 91550dd055)
2026-09-17 11:03:41 +01:00
ayushcodes10 ffa0f529e9 Add a regression test for the god node guard on the fallback
Two different classes that both fail header parsing and happen to
share a class name and a method name must not let a call resolve to
either one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 9e26952d8b)
2026-09-17 11:03:41 +01:00
ayushcodes10 d77141ec85 Add a regression test for the qualified only fallback
The issue's own repro, an Unreal style UCLASS whose method
declaration never survives header parsing, must still get a calls
edge from a site that invokes it through the qualified name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 3d4dec6b9a)
2026-09-17 11:03:41 +01:00
ayushcodes10 64fa746b43 Wire the second chance lookup into the scoped call branch
Only the Foo colon colon bar branch tags a fallback label, since it
is the only one with a class name in source to build one from. The
lookup only runs when the primary method index lookup already came
back empty, and only accepts an exactly one candidate match, the
same guard every other resolver in this module already applies.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit b5e066c7d4)
2026-09-17 11:03:41 +01:00
ayushcodes10 835abadf7a Index qualified labeled C++ nodes for a second chance call lookup
Prep for resolving a scoped call whose callee exists only as a
qualified labeled fallback node: builds a label to node id index
for every node whose label looks like a qualified method
(Class colon colon method with parens), restricted to nodes with a
real source file. Nothing reads it yet, so no behavior change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 1c1f1336e3)
2026-09-17 11:03:41 +01:00
ayushcodes10 1ca2e4b1f3 Recompute god node data directly on the stale sidecar path
god_nodes ranks purely by graph degree, independent of the community
partition, so clearing it to an empty list on the stale path and
relying on the wiki subcommand's own recompute-if-empty fallback
further down worked today only because wiki happens to be the only
current reader of this value. Recompute it directly here instead, so
a future reader that does not carry the same fallback cannot silently
lose real data.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit b071d6e788)
2026-09-17 11:01:38 +01:00
ayushcodes10 9d747f4da5 Compare partition structure, not just the node id set, for staleness
The node id set comparison missed a whole class of staleness: a merge,
a split, or a single node moving from one community to another can
leave the overall node set exactly unchanged while still describing a
different clustering, so a stale sidecar that happened to still cover
the same nodes kept winning. Compare each side's set of community
blocks instead, which catches a partition change even when the flat
node set stays identical, while still ignoring a pure id renumbering
of the same partition per the existing #1667 guard.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 30a5955931)
2026-09-17 11:01:38 +01:00
ayushcodes10 11d91c8e17 Add negative control test for a sidecar that still matches
Guards against a false positive: an up to date sidecar (the normal
case immediately after extract or the clustering command) must not
trip the new staleness warning or take the reconstruction path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 21342bda37)
2026-09-17 11:01:38 +01:00
ayushcodes10 bafe75b86b Add regression test for stale sidecar cohesion recomputation
Confirms the wiki export path specifically, since it is the one that
prints cohesion into article metadata: a stale, impossibly large
cohesion value in the sidecar must not survive into the export once
staleness is detected, proving score_all actually ran on the fresh
reconstruction rather than the fossil value merely being ignored by
coincidence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 12c50b975a)
2026-09-17 11:01:38 +01:00
ayushcodes10 18cae252cb Add regression test for the stale sidecar html export path
Corrupts the analysis sidecar community node id set (the exact
staleness signature the issue describes) and confirms export html
still succeeds, still renders graph.html, and prints the new warning
instead of silently exporting against the fossil.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 4518bae439)
2026-09-17 11:01:38 +01:00
ayushcodes10 06ff9d07dc Prefer the fresh reconstruction when the sidecar is stale
Fixes issue 2386.

The existing fallback only reconstructed communities from graph.json
when the sidecar was missing entirely; a sidecar that exists but was
written by an earlier clustering pass looked identical from the
outside and kept winning, since update and watch never regenerate
.graphify_analysis.json.

This compares the node id set each side covers, cheap and already in
memory, rather than the community ids themselves, since those can
renumber run to run even for the same partition. A mismatch means the
sidecar is stale: prefer the fresh reconstruction, recompute cohesion
with score_all so wiki articles and the HTML export do not swap one
silent degradation for a smaller one, reset gods_data so the existing
self heal at the wiki export site recomputes it, and print a warning
naming the exact command that refreshes the sidecar, since the whole
point of this issue is that nothing currently signals the divergence.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit d4eb7af599)
2026-09-17 11:01:38 +01:00
ayushcodes10 a5ab2001a8 Compute the graph.json community reconstruction unconditionally
Toward issue 2386: this hoists the reconstruction from graph.json's
per node community attribute out of the "sidecar is missing" branch
so it runs every time, with no behavior change yet since it is still
only used when the sidecar produced nothing. The next commit compares
it against the sidecar to also catch a sidecar that exists but is
stale.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017qfdzgbA5KedGEjD1AayNh
(cherry picked from commit 0932abc4e0)
2026-09-17 11:01:38 +01:00
ayushcodes10 23d8a8798d Add an end to end regression test for the reporter's exact repro
A file that only imports a package subpath must resolve just like one
that imports the bare package, using the exact package.json plus two
file shape from the issue, instead of coming out fully disconnected
from the framework it depends on.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 5fb7fc34c1)
2026-09-17 11:01:38 +01:00
ayushcodes10 7aebd99372 Add a unit test for scoped package subpath resolution
A scoped package subpath lands on the scope plus package only, never
on a different package that happens to share the same scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 02183335e3)
2026-09-17 11:01:38 +01:00
ayushcodes10 3e9d22faf5 Add a unit test for plain package subpath resolution
A plain package subpath now lands on the same resolver target as its
bare import.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit b703d3126e)
2026-09-17 11:01:38 +01:00
ayushcodes10 7d2344f846 Namespace an external import on its package root, not the specifier
An unresolved bare specifier fell through to a ref namespaced id built
from the whole raw string, so a package subpath import like
next/image minted a different id than a bare import of next, even
though both refer to the same package. package.json's own dependency
node for that package is keyed off the bare name alone, so a subpath
import could never land on it and produced no edge at all while the
bare import of the same package resolved fine.

The fallback now strips the specifier down to its package root first:
the first segment for an ordinary package, the first two for a scoped
one, matching how the dependency node itself is keyed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit be0eb7f2a5)
2026-09-17 11:01:38 +01:00
Dev Chiniwala 6e761f205a fix(diagnostics): separate external reference edges
(cherry picked from commit ba49e71a98)
2026-09-17 11:01:37 +01:00
safishamsiandClaude Opus 4.8 26b02b5e34 docs: update README header logo to the Graphify card
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-16 20:41:38 +01:00
safishamsiandClaude Opus 4.8 eaaec1abd9 release: 0.9.63 — elixir cross-file alias/import resolution, rust self.method() across files, ruby inherited member calls, external import stub nodes (no more dangling graph.json endpoints), markdown mention edges survive rebuilds
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.9.63
2026-09-16 16:51:37 +01:00
safishamsiandClaude Opus 4.8 778c04726d fix(build): mint typed external stubs for undeclared import endpoints (#2873)
graph.json shipped imports/imports_from/re_exports edges whose target was an
external module (stdlib, a third-party dep) with no entry in nodes[]. Every
loader (networkx node_link_graph) materialises such an endpoint as an
attribute-less phantom, so declared node-count != loaded node-count, and
merge-graphs fragmented the same module into one node per repo.

- build_from_json now mints a typed external stub node (type=external,
  external=True) for an import-family edge whose target is otherwise undeclared,
  instead of dropping the edge. Non-import relations are unchanged, so a
  sourceless external call target stays suppressed (#3156, no phantom god-node).
- The --no-cluster / incremental update path writes the raw merged extraction
  rather than a builder graph, so it applies the same stubbing via the shared
  mint_external_stubs_in_data helper. Idempotent; grows the node set so the
  #479 shrink guard is unaffected.
- prefix_graph_for_global leaves external stubs unprefixed (no repo:: tag), so
  the same stdlib/dependency module unifies into one global node across a
  cross-repo merge instead of fragmenting per repo.

Adds regression tests for build-level stubbing, serialized-graph consistency,
the #3156 non-import drop, idempotency, cross-repo unification, and an
end-to-end `update --no-cluster` integrity check through the real rebuild.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-16 16:20:01 +01:00
safishamsiandClaude Opus 4.8 e3535e942a fix(elixir): guard nested-module capture and same-file contains clobber (#3603 follow-up)
#3603 resolves cross-file alias/import/require/use targets onto the real
defmodule node. Two guards ported from the competing #3566 so the resolver
does not over-reach:

- Only top-level modules are indexed as resolution targets. extract_elixir
  marks them with _elixir_module; a nested defmodule (labeled with its bare
  inner name) is left unmarked so an unrelated `use <Name>` from another file
  cannot latch onto it. The marker rides through incremental rebuilds via the
  resolution-context allow-list in watch.py and cli.py, so the fix does not
  decay on the `graphify update` / watch path.
- A same-file alias is left unresolved: the import edge's source is the file
  node, which already `contains` that module, so retargeting would duplicate
  the pair as a weaker `imports` and clobber `contains` in the non-multi graph.

Adds regression tests for both guards plus an incremental-rebuild test that
routes through the real watch._rebuild_code builder (the path #3566's own
test bypassed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-16 15:37:30 +01:00
Ryan Peterson b117a6e420 fix(watch): keep code-span mention edges through a rebuild over an existing graph
_reconcile_markdown_links treats every AST-tier references edge owned by a re-parsed
Markdown file as an authored [link](file) and prunes any pair it did not author. A code-span
mention is also a references edge owned by its document, so a plain `graphify update` over
an existing graph, with or without changes, dropped every mention edge; only a build from
scratch kept them.

A mention targets a code symbol, never a file representative, so the reconcile now leaves
those edges to the AST ownership rule: re-extracting the document regenerates them and
re-extracting the code side keeps or drops them with the target node.

(cherry picked from commit 73cf2ae56c)
2026-09-16 14:50:54 +01:00
Ryan Peterson 8cfebfdfd2 fix(markdown): honour dotted qualifiers and explicit relative cites in mention resolution
Three findings from the automated review of #3562, all reproduced:

A dotted span with a non-code extension (README.md, pyproject.toml) was classified as a
symbol because only CODE_EXTENSIONS was checked, and every dotted form was reduced to its
last segment, so time.sleep matched a repo's own sleep(). The dotted form now keeps its
whole chain, the file early-exit also checks DOC_EXTENSIONS, and a bare-name match must
carry every qualifier as an owner label or a segment or stem of its source path.

_match_cited_file used lstrip("./"), which strips characters, so a .github/... cite could
never match by suffix. The leading ./ and ../ segments are now stripped as segments, and
the suffix form also matches a root-level file.

An explicit ./ or ../ cite that escaped the corpus fell through to the suffix rule and
could land on an unrelated copy of the file. It now resolves relative to the document
only.

(cherry picked from commit 3f84e7f3e2)
2026-09-16 14:50:54 +01:00
ayushcodes10 7dbc668933 Add a regression test for a genuinely external module reference
A module with no matching definition anywhere in the corpus, stdlib
or a hex dependency, must stay an external reference rather than
getting fabricated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit da5e23a921)
2026-09-16 14:47:21 +01:00
ayushcodes10 50a65891f4 Add a regression test for the god node guard on a duplicate name
Two different files each declaring a module with the same bare name
must leave the reference exactly as extracted rather than guessing
which one the caller meant.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 690d387626)
2026-09-16 14:47:21 +01:00
ayushcodes10 9a5b2dd7f9 Add a same file negative control test
A module importing something declared in the same file already
resolved before this fix, since both ids share the same stem, and
must keep working exactly as before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 8320da005a)
2026-09-16 14:47:21 +01:00
ayushcodes10 7406aae5e6 Add a regression test for the issue's own repro
A module aliased from a different file now resolves to that module's
real, non dangling node.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 53877c6874)
2026-09-16 14:47:21 +01:00
ayushcodes10 4696a55df1 Add and register the Elixir import target resolver
Fixes issue 2556.

extract_elixir mints a module's own node id with the defining file's
stem but an alias/import/require/use target with just the bare
module name, so the two only ever match when a module refers to
itself. Every reference to a module declared in a different file was
silently dropped as dangling at build time, thirteen percent of
extracted edges on the real project measured in the issue.

Matches a target against the id of a module across the whole corpus,
which needs no heuristics since both sides already run through the
same id builder. Zero or two or more matches leave the edge exactly
as extracted rather than guessing, the same god node guard every
other cross file resolver in this module already applies; the issue's
own testing found id suffix matching produced wrong resolutions on a
real corpus, which is why this matches the full id, not a fragment
of it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 909310cfe7)
2026-09-16 14:47:05 +01:00
ayushcodes10 8c7b6675e8 Refuse to pool methods across two unrelated same named types
Pooling methods across every node sharing a type's bare label is
what makes a split impl block work, but it is only safe when every
one of those nodes really is an impl block for the SAME type. Two
unrelated structs that happen to share a bare name, each with its
own different methods, could still link a caller to a method that
only exists on the wrong one, most concretely when one type
overrides a trait default the other relies on and so never gets an
explicit method node for it.

A struct, enum, or trait declaration gets a contains edge from its
file; an impl block never does. So the type name itself is now
treated as ambiguous, and pooling skipped entirely, whenever two or
more nodes sharing the bare label are real declarations rather than
impl blocks. A single declaration with any number of impl blocks
spread across files, the shape this pass exists for, is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 141662b0eb)
2026-09-16 14:43:12 +01:00
ayushcodes10 58c497d2da Keep every method a duplicate index key maps to, not just the last one
A plain dict assignment silently overwrote an earlier target whenever
two method nodes shared both a source impl node and a stripped
label, which would have kept whichever one was seen last instead of
surfacing the case as the ambiguity it is. The index now keeps a set
per key and folds every match into the candidate set before the
exactly one candidate check runs, so a genuine duplicate still bails
instead of guessing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 2cbbf1a670)
2026-09-16 14:43:12 +01:00
ayushcodes10 4f13b17aa2 Scope the self call dedup check to existing calls edges only
The check against a pair already having an edge matched on ANY
relation, so a caller that already had, say, a references edge to
the exact same target (from also naming the type elsewhere) silently
suppressed a real call to it. The two relations are not mutually
exclusive and an existing non calls edge says nothing about whether
a call was resolved. Scoped the check to calls edges only, matching
an existing precedent elsewhere in this module for the identical
class of mistake.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 94735ce241)
2026-09-16 14:43:12 +01:00
ayushcodes10 5483e5222c Add regression tests for the god node guard on self calls
Two different structs sharing a bare type name with a colliding
method name must yield no edge rather than a guess, and a self call
to a method that genuinely does not exist anywhere in the corpus
must not fabricate a target.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit afeeefa109)
2026-09-16 14:43:12 +01:00
ayushcodes10 d77dcb9a67 Add regression tests for the split impl block self call fix
Covers the issue's own shape (impl Foo split across two files, a
self call in one block reaching a method defined in the other) and a
same file negative control proving the pre existing bare name
resolution is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 2d161af639)
2026-09-16 14:43:12 +01:00
ayushcodes10 0a92b56bd1 Add and register the Rust self member call resolver
Fixes issue 2234.

Pools methods by the bare type name across every node that carries
it, rather than requiring the name to resolve to exactly one node
like the Kotlin and Swift equivalents: Rust routinely splits an impl
block for one type across as many files as it likes, and rust.py
mints a separate graph node per file and type name pair rather than
merging them, so requiring a single node would refuse the exact
split impl block shape this issue is about. A call resolves only
when exactly one method across the whole pool matches the callee
name, so two impl blocks defining the same method name still bail,
a real ambiguity rather than a false one.

Verified end to end: a self call in one file now reaches a method
defined in a different impl block of the same type in another file,
the same file control case is unaffected, an ambiguous type name
shared by two unrelated structs with a colliding method name yields
no edge, and a call to a genuinely undefined method yields no edge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 7a5ab00ff5)
2026-09-16 14:43:11 +01:00
ayushcodes10 55d84bbc3c Tag a self dot method call with its receiver's syntactic type
Detect when a member call's receiver is literally self (a distinct
tree sitter node type, not an identifier) and, when the enclosing
impl block's bare type name is known, carry it on the raw_call as
rust_self_type. Nothing consumes this yet, so the extracted edges
are unchanged; it is the signal the next commit's resolver needs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit 200081cf9b)
2026-09-16 14:43:11 +01:00
ayushcodes10 53e89d6cf7 Thread the enclosing impl block's bare type name through function bodies
Prep for resolving a self dot method call across files: extract_rust
already tracks which impl block a function body belongs to via
parent_impl_nid, but not the block's own bare type name, which is
what self is typed as syntactically inside that block. No behavior
change yet, this just carries the name down to where a call
expression is walked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit a49fd9139a)
2026-09-16 14:43:11 +01:00
oleksii-tumanov c5f57d3d61 fix: resolve inherited Ruby member calls
(cherry picked from commit 8f576afacc)
2026-09-16 14:43:05 +01:00
safishamsiandClaude Opus 4.8 c7ec108208 release: 0.9.62 — terraform local modules, Ruby inherited implicit-self calls, markdown code-span references, unicode ids, self-loop cohesion, atomic watch writes, semantic-cache basename recovery, destructured export nodes, thin file-only communities, GRAPHIFY_OUT hook gates
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v0.9.62
2026-09-15 18:57:53 +01:00