Skip to content

[pull] v8 from safishamsi:v8#98

Merged
pull[bot] merged 6 commits into
miqdigital:v8from
safishamsi:v8
Jun 30, 2026
Merged

[pull] v8 from safishamsi:v8#98
pull[bot] merged 6 commits into
miqdigital:v8from
safishamsi:v8

Conversation

@pull

@pull pull Bot commented Jun 30, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

safishamsi and others added 6 commits June 30, 2026 16:58
#1561)

A hyperedge's member list is canonically keyed `nodes`, but producers
(LLM/subagent drift, externally-supplied graph.json) sometimes emit
`members` or `node_ids` — graphify only read `nodes`, so those hyperedges
silently lost their members, and semantic_cleanup's prune dropped them
entirely. Normalize the member key to `nodes` at one ingest chokepoint in
build_from_json (and in semantic_cleanup, which runs pre-build), deduping
and warning, so every downstream consumer sees the canonical key. Mirrors
the existing from/to edge-endpoint aliasing.

Reported by @askalot-io.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ph (#1553)

The cross-file call resolver bailed (#543/#1219 god-node guard) whenever a
bare callee name had 2+ definitions without unique import evidence — so a
single same-named test mock (or any same-named symbol) dropped the real
`calls` edge, erasing the call graph wherever a mock existed (the reporter
saw a 76-stub Pester suite wipe everything).

Replace the blunt bail with a smarter guard: when a name is ambiguous and
import evidence doesn't resolve it, apply tie-breakers — non-test
preference (a shared, segment-aware _is_test_path classifier) then path
proximity — and emit an INFERRED edge ONLY if exactly one candidate
survives, else keep bailing. A real def + a test mock resolves to the real
def; two genuine non-test defs still bail (god-node guard intact, no
fan-out). Wired into both the extract.py pass and the symbol_resolution.py
copy via the shared classifier.

Reported by @Schweinehund.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… routing (#1556, #1547)

A class declared in a header (Foo.h/@interface) and defined in its impl
(Foo.cpp/Foo.m/@implementation) fragmented into two nodes: _file_stem
drops the extension so Foo.h and Foo.cpp share a node id, which
_disambiguate_colliding_node_ids then split apart by path — and the two
"defs" tripped every resolver's single-definition god-node guard,
cascading into missing .h<->.m/.cpp linkage and cross-file/cross-language
edges.

- Routing: a `.h` using `#import` now routes to extract_objc (#1556 bridging
  headers — extract_c drops `#import` as a preproc_call), and a `.h` with
  C++-only signals (class/namespace/template/::/access-specifiers) routes
  to extract_cpp (#1547 — the C grammar has no class_specifier, so a C++
  header previously yielded a junk node and lost every method). ObjC sniff
  keeps priority; a plain C header still routes to extract_c.
- Merge: a new _merge_decl_def_classes post-pass collapses the header/impl
  id-collision onto the header (declaration) variant, modeled on
  _merge_swift_extensions, gated so it fires ONLY for a clean sibling
  header/impl pair (same dir, same base stem, exactly one header) — two
  same-named classes in different directories have different stems and
  never collide, so they are never merged (god-node guard verified). C++
  method definitions retain their `Foo::` qualifier so a `Foo::bar` def
  keys onto the header declaration (one method node, not two); free
  functions keep their bare-name ids.

Result: one canonical class node per .h/.m or .h/.cpp pair with methods
unified, which unblocks the existing member-call resolvers (verified
Swift->ObjC calls and Swift `extension` folding now resolve). Strict
improvement over v8 (which produced junk/fragmented nodes here, verified).
Still open as follow-ups: cross-file C++ #include edge resolution and a
C++/ObjC cross-file member-call resolver (a pre-existing gap, not a
regression).

Reported by @JabberYQ (#1556) and @c0dezer019 (#1547).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
, #1556)

Connects paired classes across files: Main.cpp's `Foo f; f.bar()` now resolves
to Foo::bar, and ObjC `Foo *f = [[Foo alloc] init]; [f doThing]` to Foo's
doThing — the "connect with other classes" goal of #1547/#1556.

Design grounded in prior-art research (ctags qualified-name matching, Doxygen's
name-keyed false-edge failure modes, PAIGE's receiver-type approach, Clang USR):
resolve by RECEIVER TYPE, never bare name, and skip when the type can't be
inferred rather than guess (a false call edge / god-node is worse than a missing
one). Mirrors the existing Swift/Python/Ruby/TS member-call resolvers.

- C++ extractor now captures the member-call receiver (field_expression /
  qualified_identifier / pointer access) and builds a per-file type table from
  local declarations (`Foo f;`, `Foo* f;`, `Foo *f = ...;`); emits raw_calls.
- ObjC extractor emits raw_calls for message sends with the receiver + selector
  and a type table from `Foo *f = ...;` locals (existing in-file selector /
  alloc-init / dot-syntax / @selector matching preserved).
- New _resolve_cpp_member_calls / _resolve_objc_member_calls, registered for
  their suffixes. Receiver tiers: `Foo::bar()` / capitalized ObjC receiver and
  this/self/super (enclosing class) -> EXTRACTED; local-var-typed -> INFERRED.
  Single-definition god-node guard (skip unless exactly one type def matches);
  the just-shipped decl/def class merge makes a paired class one def so the
  guard resolves it. Verified: a.run() -> A::run only (not a same-named B::run);
  an uninferable receiver with run() in two classes emits zero edges (no
  fan-out); ObjC [f doThing] -> Foo only.
- build.py: the cross-language INFERRED-call prune treated .h/.cpp/.m as
  different families and dropped header/impl interop calls; unified the C family
  (.c .h .cc .cpp .hpp .cxx .hh .hxx .cu .cuh .metal .m .mm) so a .cpp/.m call to
  a .h-declared method survives.

Still open (tracked on #1547/#1556): the file-level `#include` edge can stay
uncanonicalized when the project root isn't symlink-resolved (the extract()
id-remap `continue`s on a /var-vs-/private/var mismatch) — the class connection
above is robust to it; include-reachability candidate narrowing and ObjC
dynamic-dispatch/id-typed receivers also deferred (expected low ObjC recall, per
the research).

Reported by @c0dezer019 (#1547) and @JabberYQ (#1556).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends the C# type-reference resolver (#1466) to be namespace-aware,
advancing the #1318 shadow-node umbrella for C#:

- The namespace is folded into the C# node id (_make_id(stem, namespace,
  name)), so two same-named types in different namespaces in one file no
  longer collapse — replacing #1466's detect-and-skip workaround for
  multi-namespace files.
- Lexical per-block using-scope: a `using` applies only where it is in
  scope (file-level, or the enclosing namespace block via a scope chain),
  so sibling namespace blocks no longer share each other's usings.
- Qualified references (`Namespace.Type`) resolve via in-scope aliases
  (`using Q = X.Y`) then exact known namespaces; generics are stripped.

Preserves (and tightens) the refuse-rather-than-guess discipline: a bare
reference resolves only when exactly one in-scope namespace provides the
type; an ambiguous reference (e.g. `using A; using B;` both defining
`Widget`) resolves to nothing rather than fanning out. Verified: `using A`
-> A.Widget only; ambiguous -> no edge; qualified `B.Widget` -> B.Widget
regardless of usings; sibling-block using-scope isolated; no dangling
edges or fan-out.

Reconciled onto current v8 (the PR predated the C++/ObjC member-call
resolvers); full suite green, the C++/ObjC resolution coexists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cross-file member-call resolution for C++/ObjC (#1547/#1556) and
namespace-aware C# type resolution (#1562), the work-memory overlay
(#1441), test-mock call-graph fix (#1553), hyperedge member-key aliases
(#1561), plus the TS/JS/ObjC resolution fixes (#1316/#1544/#1552/#1475).
See CHANGELOG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pull pull Bot locked and limited conversation to collaborators Jun 30, 2026
@pull pull Bot added the ⤵️ pull label Jun 30, 2026
@pull pull Bot merged commit 0c551ac into miqdigital:v8 Jun 30, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants