Skip to content

Objective-C member calls are never parked for the cross-repo merge (#3152) #3384

Description

@xiongjianxu

What happens

merge-graphs / global add finish a member call whose receiver type lives in
another repository for Java, C++, C# and Swift (#3152). Objective-C has a member-call
resolver with full receiver typing — self/super, an explicit class name, the
file's Foo *f local table, and the class's @property/ivar table with the
inherits walk from #3151 — but it parks nothing, so an ObjC corpus split across
repositories gets no cross-repo call edges at all.

The reason is one bail. _resolve_objc_member_calls ends each typing arm with

type_defs = type_def_nids.get(_key(type_name), [])
if len(type_defs) != 1:  # ambiguous or absent -> bail (god-node guard)
    continue

so "declared nowhere in this corpus" — the parking case — is indistinguishable from
"declared more than once", which must stay dropped. Java splits the same guard into
a park arm and an ambiguity arm; the three ObjC arms do not.

Minimal reproduction

repo app/src/App.m
    @interface App : NSObject
    @property (nonatomic, strong) Greeter *greeter;
    @end
    @implementation App
    - (void)run { [self.greeter greet]; }
    @end

repo lib/src/Greeter.m
    @interface Greeter : NSObject
    - (void)greet;
    @end
    @implementation Greeter
    - (void)greet {}
    @end

Build each repo, then merge-graphs app/graphify-out/graph.json lib/graphify-out/graph.json.

Observed: grep unresolved_calls app/graphify-out/graph.json finds nothing, and the
merged graph has no context: "cross_repo" edge. The same two files in one corpus do
produce the -run -> -greet call, so the receiver typing is not what is missing.

Expected: App.m parks {"callee": "greet", "receiver_type": "Greeter", "lang": "objc"}
and the merge emits the calls edge, as the Java/C++/C#/Swift equivalents do.

Two ObjC-specific things the merge side has to handle

  1. _callable_class / _callable are absent from extractors/objc.py (ObjC declarations carry no _callable / _callable_class markers, so marker-gated passes skip ObjC entirely #3228), and
    cross_repo_calls._index_declarations indexes declarations by those markers, so
    every ObjC type is skipped even once parking exists.

  2. An ObjC method label keeps its +/- sigil (-greet), where every other
    extractor writes .greet(). _key strips . and () only, so a parked selector
    greet never matches -greet. Normalizing the sigil away is the wrong fix: .h is
    in the C++ suffix set and would be in the ObjC one, so keeping the sigil is what
    keeps an ObjC -greet and a C++ .greet() from answering for each other inside one
    shared header. Trying both sigils and treating a class that declares +greet and
    -greet as ambiguous keeps them disjoint without a new field in the parked payload.

Scope note

Measured on a 15,754-file ObjC corpus split into 26 units: of 12,866 sends whose
receiver is typed to a class absent from its own unit, 5,127 are framework classes
(UIKit/Foundation, in no unit), 1,911 are business classes declared in no unit at all,
2,634 find the class but the selector is not on it, 2,769 are ambiguous across units,
and 425 are genuinely cross-unit resolvable. The builtin-globals filter the Swift
and C++ resolvers already apply keeps the framework names out of the parked payload.

PR: paired with this issue.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions