Skip to content

Swift, Python, TypeScript and C++ member-call resolvers still build unscoped receiver-type indexes #24

Description

@filipechagas

Parent

Spec: #1 (upstream: Graphify-Labs#1682). Direct follow-up of #10, which fixed the Java and C# copies of this defect; the remaining four were left out of #10's acceptance criteria.

Problem

A member-call resolver binds a receiver's type name by looking it up in a type_def_nids index it builds from the node set. #8 (PHP/ObjC) and #10 (Java/C#) scoped those indexes to the sources each resolver owns. Four copies are still unscoped, so they match a receiver type against classes written in any language:

Resolver Index graphify/extract.py
Swift type_def_nids 2325
Python class_def_nids 2436
TypeScript type_def_nids 2584
C++ type_def_nids 2697

As #8 recorded, this cuts both ways:

  • Defect 1 — a foreign class types the receiver, minting a cross-language edge that should not exist.
  • Defect 2 (the damaging one) — a foreign class merely sharing the name pushes the single-definition god-node guard to 2, silently suppressing the correct same-language edge.

This is orthogonal to what #10 fixed. #10 closed raw-call ownership (which resolver may consume a given raw call). This ticket is the definition index (what a resolver may resolve a receiver type against). Both have to hold.

Pre-existing, not a regression.

Repros (verified through the extract() seam at 9cce11c, i.e. with #10 already applied)

Corpus fragments shared below:

# svc.py
class Lead:
    def search(self, filters):
        return []

The Swift and TypeScript resolvers return early unless some file in the corpus produced a swift_type_table / ts_type_table (extract.py:2311 and extract.py:2576), so the Swift/TS corpora below include one live typed-receiver file to keep the resolver awake. Without it the resolver is dormant and proves nothing.

Swift

Defect 1 — Runner.swift with func go() { Lead.search() }, no Swift Lead in the corpus:

Runner.swift:.go() -calls-> svc.py:.search() [EXTRACTED]

Defect 2 — add Lead.swift declaring static func search():

with svc.py     : (no edge from Runner.swift:.go())
without svc.py  : Runner.swift:.go() -calls-> Lead.swift:.search() [EXTRACTED]

TypeScript

Defect 1 — runner.ts with go() { return Lead.search({}); }, no TS Lead, TS resolver live:

runner.ts:.go() -calls-> svc.py:.search() [EXTRACTED]

Defect 2 — lead.ts declaring class Lead { search(f: object) {...} }, widget.ts calling this.lead.search({}) on a Lead-typed constructor parameter property:

with svc.py     : (no edge from widget.ts:.run())
without svc.py  : widget.ts:.run() -calls-> lead.ts:.search() [EXTRACTED]

Python

Defect 1 — runner.py with Lead.search({}) and the class declared only in svc.ts:

runner.py:run() -calls-> svc.ts:.search() [EXTRACTED]

Defect 2 — svc.py holds the real Lead, runner.py calls Lead.search({}), and a lead.ts declares a namesake class Lead:

with lead.ts    : (no edge from runner.py:run())
without lead.ts : runner.py:run() -calls-> svc.py:.search() [EXTRACTED]

C++

Defect 1 — main.cpp with void go() { Lead l; l.search(); }, no C++ Lead:

main.cpp:.go() -calls-> svc.py:.search() [INFERRED]

Defect 2 — add lead.cpp declaring class Lead { public: void search() {} }:

with svc.py     : (no edge from main.cpp:.go())
without svc.py  : main.cpp:.go() -calls-> lead.cpp:.search() [INFERRED]

What to build

Apply the #8/#10 pattern: filter the index by source-file suffix, reusing the per-resolver suffix tuples #10 consolidated at extract.py:2256-2270 (_SWIFT_RESOLVER_SUFFIXES, _PYTHON_RESOLVER_SUFFIXES, _TYPESCRIPT_RESOLVER_SUFFIXES) so the registration and the scoping cannot drift. Prior art to copy verbatim: _resolve_java_member_calls and _resolve_csharp_member_calls at 9cce11c.

Two places need more than a copy-paste:

  • Python is not the same loop shape. class_def_nids is built from the sources of method edges (extract.py:2426-2441), not from a scan over all_nodes, so the suffix test goes on the looked-up owner node rather than on the loop variable. Only the class arm needs scoping; the module arm resolves through the caller's own file node via contains / imports edges.
  • C++ has no suffix tuple yet and needs one that includes .h — a C++ class is routinely declared in a header, so excluding .h would break real resolution. See the dual-routing note at extract.py:2266-2269: .h routes to extract_cpp or extract_objc by content, so it sits in both the C++ and ObjC definition-index sets. That is the accepted compromise (an ObjC @interface in a .h stays visible to the C++ index and vice versa) — scoping still excludes every non-C-family language, which is the leak this ticket is about. Raw-call ownership for C++/ObjC is unaffected: those stay on the extractor-stamped lang, exactly because a suffix cannot separate them.

Acceptance criteria

Adjacent finding — explicitly NOT in this ticket's scope

The Python member-call resolver has no _LANGUAGE_BUILTIN_GLOBALS guard, unlike Swift (extract.py:2374) and TypeScript (extract.py:2626). Since #10 it only sees .py raw calls, so the cross-language exposure is gone, but a Python class Date / class Bundle can still be bound from a Python Date.x() call — the same false-edge class that Graphify-Labs#1726 and Graphify-Labs#2147 fixed for the other two. Recorded here so it is not lost; it belongs in its own ticket.

Blocked by

None. #10 is merged as 9cce11c on fix/member-call-lang-scoping-10; this ticket assumes that branch's suffix-tuple block exists.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingready-for-agentFully specified, ready for an AFK agent

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions