Skip to content

v0.9.3

Latest

Choose a tag to compare

@safishamsi safishamsi released this 30 Jun 19:40

Cross-file member-call resolution for C++/Objective-C (#1547/#1556) and namespace-aware C# type resolution (#1562), the work-memory overlay (#1441), and a batch of TS/JS/ObjC resolution + call-graph fixes.

  • Feat: cross-file member-call resolution for C++ and Objective-C (#1547, #1556). A class declared in a header and defined in its .cpp/.m no longer fragments into two nodes (a decl/def merge pass collapses the sibling header/impl pair, gated to same-directory same-name so unrelated classes never merge), and a member call now resolves across files by the receiver's inferred type: C++ Foo f; f.bar() / Foo::bar() / this->bar() and ObjC Foo *f = [[Foo alloc] init]; [f doThing] / [self render] link to the owning class's method. Resolution is by receiver type, never bare name, with the single-definition god-node guard — an uninferable or ambiguous receiver produces no edge (high precision over recall, grounded in how compiler-free indexers like ctags/Doxygen mis-resolve by name). Also routes C++ headers to the C++ extractor and ObjC #import bridging headers to the ObjC extractor. Reported by @c0dezer019 and @JabberYQ. (Residual cross-file #include edge resolution under symlinked roots and ObjC dynamic-dispatch receivers remain follow-ups.)
  • Feat: namespace-aware C# cross-file type resolution (#1562, thanks @TheFedaikin). The namespace is folded into the C# node id (so same-named types in different namespaces stay distinct), using directives are honored with lexical per-block scope, and qualified references (Namespace.Type, using aliases) resolve — disambiguating a bare reference to the one in-scope namespace that provides it, and refusing (no edge) when ambiguous. Advances the #1318 shadow-node umbrella for C#.
  • Fix: test mocks no longer erase the real cross-file call graph (#1553, thanks @Schweinehund). When a bare callee name had 2+ definitions without unique import evidence, the god-node guard dropped the edge entirely — so a single same-named test mock wiped the real call graph (a 76-stub Pester suite erased everything). The guard now applies tie-breakers — non-test preference (a shared, segment-aware path classifier) then path proximity — and resolves only when exactly one candidate survives, else still bails. A real def plus a test mock resolves to the real def; two genuine non-test defs still bail (no fan-out).
  • Fix: hyperedge member lists keyed members or node_ids are now accepted, not silently dropped (#1561, thanks @askalot-io). Normalized to the canonical nodes at ingest (in build_from_json and semantic_cleanup), deduped, with a warning — mirroring the existing from/to edge-endpoint aliasing.
  • Feat: work-memory overlay — graphify reflect now projects the verdicts it distills (preferred / tentative / contested, recency-weighted) into a .graphify_learning.json sidecar next to graph.json, and graphify explain / query / GRAPH_REPORT.md / the HTML viewer surface them where you look (a Lesson: hint, a colored node ring). Builds on the idea in #1441/#1542 (thanks @TPAteeq), implemented as a sidecar rather than stamping graph.json: structural truth stays separate (no learning_* in graph.json or GraphML exports, no rebuild churn). Each verdict carries the source questions that produced it (provenance) and a content fingerprint of the cited code, so a verdict on a file that has changed since is flagged "code changed — re-verify" instead of shown as still-authoritative. Dead-ends stay query-scoped (a report section, never a node attribute). Letting verdicts influence query traversal is deliberately deferred (it needs propensity correction + exploration to avoid a self-reinforcing feedback loop).
  • Feat: type-aware this.field.method() resolution for TypeScript/JS (#1316, thanks @guyoron1). A member call through a constructor-injected dependency (constructor(private db: Database) then this.db.query()) now produces a calls edge to the field type's method, resolved by the field's declared type and gated by the single-definition god-node guard (an ambiguous or untyped field produces no edge — no global name-match fan-out). EXTRACTED confidence; constructor parameter-property injection scope.
  • Feat: resolve TypeScript wildcard path aliases (#1544, thanks @oleksii-tumanov). A compilerOptions.paths pattern like @app/* or @*/interfaces now captures the matched segment and substitutes it into each target in order, honoring tsc's longest-prefix / exact-wins specificity, baseUrl, and the first-existing-target fallback. Extends the #1531 resolver.
  • Feat: resolve JS namespace re-export bindings (#1552, thanks @oleksii-tumanov). export * as ns from './mod' now creates a real symbol node for ns, registers it as a named export (so a downstream import { ns } resolves to it), and emits a file-level re_exports edge — treated as a single opaque binding, so ns.member accesses don't fan out into false per-symbol edges. Includes cycle and deep-chain guards.
  • Feat: Objective-C dot-syntax property accesses and @selector() call edges (#1475, #1543, thanks @guyoron1). self.product.name now emits an accesses edge and @selector(method) a calls edge, each resolved only to an unambiguous in-scope definition by exact method-id match (a sibling of the same class for dot-syntax; exactly one method by exact selector name for @selector) — so self.name can't mis-resolve to a -surname sibling and same-named methods across classes don't fan out. Completes the #1475 ObjC follow-ups.