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
113 lines
3.5 KiB
Go
113 lines
3.5 KiB
Go
package resolver
|
|
|
|
import "github.com/zzet/gortex/internal/graph"
|
|
|
|
// languageFamily maps a language to the family within which cross-language
|
|
// symbol resolution is legitimate (a `@model Foo` / `<Counter/>` reference may
|
|
// bind across languages of the same family). "" means the language belongs to
|
|
// no multi-language family, so any cross-language bind for it is coincidental.
|
|
func languageFamily(lang string) string {
|
|
switch lang {
|
|
case "java", "kotlin", "scala":
|
|
return "jvm"
|
|
case "swift", "objc", "objective-c", "objectivec":
|
|
return "apple"
|
|
case "typescript", "ts", "tsx", "javascript", "js", "jsx":
|
|
return "web"
|
|
case "c", "cpp", "c++", "cxx":
|
|
return "c"
|
|
case "csharp", "c#", "fsharp", "f#", "razor":
|
|
return "dotnet"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// sameLanguageFamily reports whether a and b are the same language or belong to
|
|
// the same multi-language family (so a within-family cross-language bind is
|
|
// permitted): csharp↔razor, ts↔tsx, java↔kotlin, swift↔objc.
|
|
func sameLanguageFamily(a, b string) bool {
|
|
if a == b {
|
|
return a != ""
|
|
}
|
|
fa := languageFamily(a)
|
|
return fa != "" && fa == languageFamily(b)
|
|
}
|
|
|
|
// frameworkBridgeSynths are the synthesizers whose entire purpose is to bridge
|
|
// two language families (JS→native, Swift→ObjC, KMP expect/actual). Their
|
|
// edges are exempt from the cross-family gate.
|
|
var frameworkBridgeSynths = map[string]bool{
|
|
SynthSwiftObjC: true,
|
|
SynthReactNative: true,
|
|
SynthReactNativePair: true,
|
|
SynthExpoModules: true,
|
|
SynthFabric: true,
|
|
SynthKMPExpectActual: true,
|
|
}
|
|
|
|
// gateFrameworkResult reports whether a framework-synthesized reference/import
|
|
// result should be dropped: it crosses two known, different language families
|
|
// (a coincidental PascalCase collision) and was not produced by a bridge
|
|
// synthesizer. An unknown family on either side, the same family, or a bridge
|
|
// synthesizer all permit the result.
|
|
func gateFrameworkResult(synth, fromLang, toLang string) bool {
|
|
if frameworkBridgeSynths[synth] {
|
|
return false
|
|
}
|
|
fa, fb := languageFamily(fromLang), languageFamily(toLang)
|
|
if fa == "" || fb == "" {
|
|
return false
|
|
}
|
|
return fa != fb
|
|
}
|
|
|
|
// applyFrameworkFamilyGate drops framework-synthesized reference / import edges
|
|
// that cross two known, different language families (e.g. a Razor reference
|
|
// that coincidentally bound a TypeScript component), keeping bridge-synthesizer
|
|
// edges and call/config edges. Returns the number of edges dropped.
|
|
func applyFrameworkFamilyGate(g graph.Store) int {
|
|
type cand struct {
|
|
edge *graph.Edge
|
|
synth string
|
|
}
|
|
var cands []cand
|
|
endpointIDs := map[string]struct{}{}
|
|
for _, kind := range []graph.EdgeKind{graph.EdgeReferences, graph.EdgeImports} {
|
|
for e := range g.EdgesByKind(kind) {
|
|
if e == nil || e.Meta == nil {
|
|
continue
|
|
}
|
|
synth, _ := e.Meta[MetaSynthesizedBy].(string)
|
|
if synth == "" || frameworkBridgeSynths[synth] {
|
|
continue
|
|
}
|
|
cands = append(cands, cand{edge: e, synth: synth})
|
|
endpointIDs[e.From] = struct{}{}
|
|
endpointIDs[e.To] = struct{}{}
|
|
}
|
|
}
|
|
if len(cands) == 0 {
|
|
return 0
|
|
}
|
|
ids := make([]string, 0, len(endpointIDs))
|
|
for id := range endpointIDs {
|
|
ids = append(ids, id)
|
|
}
|
|
nodes := g.GetNodesByIDs(ids)
|
|
langOf := func(id string) string {
|
|
if n := nodes[id]; n != nil {
|
|
return n.Language
|
|
}
|
|
return ""
|
|
}
|
|
dropped := 0
|
|
for _, c := range cands {
|
|
if gateFrameworkResult(c.synth, langOf(c.edge.From), langOf(c.edge.To)) {
|
|
if g.RemoveEdge(c.edge.From, c.edge.To, c.edge.Kind) {
|
|
dropped++
|
|
}
|
|
}
|
|
}
|
|
return dropped
|
|
}
|