a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
package resolver
|
|
|
|
import (
|
|
"sort"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
)
|
|
|
|
// kmpExpectActualVia marks a synthesized Kotlin Multiplatform expect↔actual
|
|
// pairing edge.
|
|
const kmpExpectActualVia = "kmp.expect-actual"
|
|
|
|
// ResolveKMPExpectActual is the framework-dispatch synthesizer for Kotlin
|
|
// Multiplatform expect/actual declarations. An `expect` declaration in common
|
|
// code is fulfilled by one `actual` per platform; the link is implicit — the
|
|
// compiler matches them by signature — so the static graph leaves the expect
|
|
// declaration looking unreferenced and the per-platform actuals looking
|
|
// orphaned. The Kotlin extractor stamps Meta["kmp_role"]="expect"|"actual"; this
|
|
// pass pairs declarations of the same name and kind and synthesizes an
|
|
// implements edge from each actual to its expect (so find_implementations on
|
|
// the expect returns every platform actual) plus a references edge back for
|
|
// navigation.
|
|
//
|
|
// One expect fans out to several actuals (android / ios / jvm) by design. Full
|
|
// recompute and idempotent; edges ride at ast_inferred with synthesizer
|
|
// provenance. Returns the number of expect↔actual pairs linked.
|
|
func ResolveKMPExpectActual(g graph.Store) int {
|
|
if g == nil {
|
|
return 0
|
|
}
|
|
|
|
type key struct {
|
|
name string
|
|
kind graph.NodeKind
|
|
}
|
|
expects := map[key][]*graph.Node{}
|
|
actuals := map[key][]*graph.Node{}
|
|
for _, n := range g.AllNodes() {
|
|
if n == nil || n.Meta == nil || n.Name == "" {
|
|
continue
|
|
}
|
|
k := key{n.Name, n.Kind}
|
|
switch role, _ := n.Meta["kmp_role"].(string); role {
|
|
case "expect":
|
|
expects[k] = append(expects[k], n)
|
|
case "actual":
|
|
actuals[k] = append(actuals[k], n)
|
|
}
|
|
}
|
|
if len(expects) == 0 || len(actuals) == 0 {
|
|
return 0
|
|
}
|
|
|
|
keys := make([]key, 0, len(expects))
|
|
for k := range expects {
|
|
if len(actuals[k]) > 0 {
|
|
keys = append(keys, k)
|
|
}
|
|
}
|
|
sort.Slice(keys, func(i, j int) bool {
|
|
if keys[i].name != keys[j].name {
|
|
return keys[i].name < keys[j].name
|
|
}
|
|
return keys[i].kind < keys[j].kind
|
|
})
|
|
|
|
var batch []*graph.Edge
|
|
paired := 0
|
|
for _, k := range keys {
|
|
for _, exp := range expects[k] {
|
|
for _, act := range actuals[k] {
|
|
if exp.ID == act.ID {
|
|
continue
|
|
}
|
|
batch = append(batch,
|
|
kmpEdge(act, exp.ID, graph.EdgeImplements),
|
|
kmpEdge(exp, act.ID, graph.EdgeReferences),
|
|
)
|
|
paired++
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, e := range batch {
|
|
g.AddEdge(e)
|
|
}
|
|
return paired
|
|
}
|
|
|
|
// kmpEdge builds one direction of an expect↔actual pairing.
|
|
func kmpEdge(from *graph.Node, toID string, kind graph.EdgeKind) *graph.Edge {
|
|
return &graph.Edge{
|
|
From: from.ID,
|
|
To: toID,
|
|
Kind: kind,
|
|
FilePath: from.FilePath,
|
|
Line: from.StartLine,
|
|
Confidence: 0.8,
|
|
ConfidenceLabel: graph.ConfidenceLabelFor(kind, 0.8),
|
|
Origin: graph.OriginASTInferred,
|
|
Meta: map[string]any{
|
|
"via": kmpExpectActualVia,
|
|
MetaSynthesizedBy: SynthKMPExpectActual,
|
|
MetaProvenance: ProvenanceHeuristic,
|
|
},
|
|
}
|
|
}
|