Skip to content

Broaden cross-language resolution, framework bridges, and reference capture - #177

Merged
zzet merged 18 commits into
mainfrom
feat/resolution-and-bridging-coverage
Jun 27, 2026
Merged

Broaden cross-language resolution, framework bridges, and reference capture#177
zzet merged 18 commits into
mainfrom
feat/resolution-and-bridging-coverage

Conversation

@zzet

@zzet zzet commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Summary

Broadens Gortex's cross-language code intelligence with a batch of independent extractor / resolver / route improvements — one commit each, every one fixture-backed and verified against the live graph. The full go test -race ./... suite is green and golangci-lint is clean on every touched package; no wire-contract change (only Meta keys added).

Function-as-value & reference capture

  • PHP — recognise callbacks passed to 13 more higher-order builtins (array_udiff and friends, forward_static_call, set_exception_handler, ob_start, iterator_apply, is_callable, …); for preg_replace_callback_array, resolve the value side of each pattern => callback element.
  • C++ — capture &fn and &Cls::method (pointer-to-member) as function-value references.
  • Objective-C — capture @selector(...) literals as function-value references to the named method.
  • Lua / Luau — capture function values passed by bare name and tbl.method.

React Native / Expo / mobile

  • React Native — capture the custom sendEvent(ctx, "Name", body) wrapper emit (Swift/ObjC) and mine native→JS emits from Java/Kotlin (getJSModule(...).emit chain + the sendEvent helper), so Android/iOS native emits share the event topic node with JS addListener subscribers.
  • Expo Modules — extract Property / Constants / Events members, tolerate a generic clause (AsyncFunction<Int>("x")), and attribute members of a definition() body with no Name() to its enclosing XxxModule class.

Swift ↔ Objective-C interop

  • @objcMembers — propagate implicit Objective-C exposure to a class's members (with the @nonobjc opt-out).
  • @objc protocol — emit a cross-language implements edge from an Objective-C @interface to the Swift @objc protocol it adopts.

Framework dispatch & resolution

  • NgRx — synthesize a dispatch edge from a createEffect(() => actions$.pipe(ofType(X))) effect to the action X it reacts to.
  • Svelte — resolve a $store auto-subscription in markup to the imported store (runes untouched).
  • C# ASP.NET — prefix-join a controller's class-level [Route("api/[controller]")] onto each relative method route (an absolute /... / ~/... template still ignores the prefix, per ASP.NET), and capture a verb-less method [Route("...")].

Factory-chain receiver typing

  • Capture the receiver of a chained call so builder().withX().build() resolves the chained receiver type (or preserves the receiver expression for the graph-aware resolver when a hop isn't locally typed), across C++, PHP, Swift, Scala, Dart, and Pascal. The factory-chain work covers the languages whose call syntax fits the shared .-chain machinery; C (no member-call chains) and Objective-C (bracket message sends) are not applicable.

Verification

  • Every commit is behavior-only and independently builds + tests green.
  • Full go test -race ./...: green (0 failures / panics / races).
  • golangci-lint run: clean on all touched packages.

zzet added 18 commits June 27, 2026 02:00
Extend the string-callable higher-order-function set (array_udiff and
friends, forward_static_call, set_exception_handler, ob_start,
iterator_apply, is_callable, and more). For preg_replace_callback_array,
resolve the value side of each pattern => callback array element rather
than scanning the array flat, so regex-pattern keys are not mistaken for
callables.
…s, and class-name fallback

Widen the Expo Modules DSL scan beyond Function/AsyncFunction to Property,
Events, and Constants members, tolerate a generic clause between the
keyword and its argument list (AsyncFunction<Int>("x")), and attribute
members of a definition() body that never calls Name() to their enclosing
class XxxModule (the Module suffix stripped) instead of dropping them.
…jective-C

Recognize a paren-form sendEvent(reactContext, "Name", body) helper as a
React Native native event emit, not just the labelled sendEvent(withName:)
/ sendEventWithName: forms. Swift reuses the lenient wrapper regex (a
superset of the labelled form); Objective-C mines it as a separate pass
since the paren and bracket forms are syntactically disjoint and so do not
double-count.
Wire the React Native native-event miner into the Java and Kotlin
extractors. Captures the Android device-event emitter chain
getJSModule(RCTDeviceEventEmitter::class.java).emit("Name", ...) (and the
Java .class form) and the sendEvent(reactContext, "Name", ...) helper
wrapper, so Android native emits share the rn_native_event topic node with
the JS NativeEventEmitter addListener subscribers.
Route the C++ extractor through the function-as-value capture pass and give
C++ its own value-position spec: peel the address-of pointer_expression and
recognize a qualified_identifier so register(&handler) and bind(&Foo::method)
each yield a function-value reference (the pointer-to-member resolving
ungated, since it names a method of another class).
Scan Objective-C source for @selector(sel) literals and record each whose
selector names a same-file method as a function-as-value reference, so the
targeted method is reachable through performSelector and target/action wiring
even though no direct call edge exists.
Route the Lua and Luau extractors through the function-as-value capture pass
and give them a value-position spec, so a bare setCallback(onClick) and a
table-member register(handlers.onTick) each yield a function-value reference
(the dot_index_expression resolving to its trailing field name).
An @objcMembers class exposes every member to Objective-C implicitly. Stamp
each member's objc_selector as if it were @objc unless it opts out with
@nonobjc, deriving method and property accessor selectors the same way the
explicit @objc path does.
Capture the adopted-protocol list of an Objective-C @interface and the @objc
flag of a Swift protocol, then teach the Swift<->ObjC bridge synthesizer to
emit a cross-language EdgeImplements from an ObjC class to the Swift @objc
protocol it conforms to. Gated on the protocol being @objc.
Detect createEffect(() => this.actions$.pipe(ofType(SomeAction), ...)) in the
JS/TS extractors, tag the effect node, and stamp a placeholder call per ofType
action; a new framework synthesizer resolves each to the action creator / type
it reacts to. Lets navigation and find_usages link a registered effect to the
action that triggers it.
A $foo read in Svelte markup where foo is an imported store now binds the component to that store import (its subscribe contract) instead of dangling. Svelte 5 runes are excluded since they are framework macros, not stores, and the existing rune path is untouched.
…te attributes

Read a C# controller class-level [Route("api/[controller]")] (expanding the [controller]/[action] tokens) and prefix-join it onto each relative method route, while an absolute /... or ~/... template still ignores the prefix per ASP.NET. Also capture a verb-less method [Route("...")] as a route with an unconstrained verb.
Capture the receiver expression of a C++ member call and feed it through the
shared chained-receiver walker, so a factory chain builder().withX().build()
stamps the resolved receiver_type when the hop is locally typed and preserves
receiver_expr for the graph-aware resolver otherwise.
Carry the receiver object of a PHP member call (-> normalised to .) through the
shared chained-receiver walker, so a factory chain builder()->withX()->build()
stamps the resolved receiver_type when locally typed and preserves receiver_expr
for the graph-aware resolver otherwise.
…eiver type

Detect a Swift navigation-expression member call (the .method() of a chain) in
addition to the bare-identifier call the query already matched, gated to chains
whose receiver is itself a call so ordinary obj.method() is untouched. Feed the
receiver through the shared walker so builder().withX().build() resolves the
chained receiver type.
Carry the object of a Scala field-expression call (the receiver the method is
selected on) through the shared chained-receiver walker, so a factory chain
builder().withX().build() stamps the resolved receiver_type when locally typed
and preserves receiver_expr for the graph-aware resolver otherwise.
Add a factory-chain pass that emits a member call per chained .method(...) whose
receiver is itself a call (builder().withX().build()) and carries the receiver
text through the shared chained-receiver walker. The main call walk stops at the
first call and dropped these segments; gated on a call-bearing receiver so plain
recv.method() is left to it and not double-counted.
Emit a member call for a Pascal exprCall whose callee is an exprDot
(recv.Method()) -- the chained segments the bare-callee path dropped -- gated to a
call-bearing receiver, and carry the receiver text through the shared
chained-receiver walker so Builder().WithX().Build() resolves the chain.
@zzet
zzet merged commit d4efdb4 into main Jun 27, 2026
9 checks passed
@zzet
zzet deleted the feat/resolution-and-bridging-coverage branch June 28, 2026 08:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant