Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

264 lines
12 KiB
Go

package resolver
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zzet/gortex/internal/graph"
)
// fnValueCandidateEdge mirrors what the per-language capture emits: a
// placeholder reference into the fn-value namespace, carrying the captured name
// in Meta for the gate to bind.
func fnValueCandidateEdge(from, name, file string, line int) *graph.Edge {
return &graph.Edge{
From: from,
To: fnValueUnresolvedPrefix + name,
Kind: graph.EdgeReferences,
FilePath: file,
Line: line,
Origin: graph.OriginSpeculative,
Meta: map[string]any{
"via": fnValueCandidateVia,
"fn_value_name": name,
},
}
}
const fnValueUnresolvedPrefix = "unresolved::fnvalue::"
func boundCallbackEdge(g graph.Store, from, to string) *graph.Edge {
for _, e := range g.GetOutEdges(from) {
if e.To != to || e.Meta == nil {
continue
}
if v, _ := e.Meta["via"].(string); v == fnValueRegistrationVia {
return e
}
}
return nil
}
// TestCallbackGateRejectsUnboundIdentifiers is the A3 named test: the gate binds
// a captured value-position identifier that names a same-file function and
// drops one that resolves to nothing, and the bound edge rides a filterable
// provenance tier rather than a flat heuristic flag.
func TestCallbackGateRejectsUnboundIdentifiers(t *testing.T) {
g := graph.New()
// A real same-file function the registration can bind to.
g.AddNode(&graph.Node{
ID: "router.go::handler", Kind: graph.KindFunction, Name: "handler",
FilePath: "router.go", StartLine: 10, Language: "go",
})
g.AddNode(&graph.Node{
ID: "router.go::register", Kind: graph.KindFunction, Name: "register",
FilePath: "router.go", StartLine: 3, Language: "go",
})
// One bindable candidate (handler exists) and one unbound (ghost is a
// local / undefined name — never a function node in this file).
g.AddEdge(fnValueCandidateEdge("router.go::register", "handler", "router.go", 4))
g.AddEdge(fnValueCandidateEdge("router.go::register", "ghost", "router.go", 5))
// A builtin-shaped candidate must also be skipped before any lookup.
g.AddEdge(fnValueCandidateEdge("router.go::register", "nil", "router.go", 6))
landed := ResolveFnValueCallbacks(g)
assert.Equal(t, 1, landed, "only the bound candidate should land")
bound := boundCallbackEdge(g, "router.go::register", "router.go::handler")
require.NotNil(t, bound, "the bound handler should produce a callback-registration edge")
assert.Equal(t, graph.EdgeReferences, bound.Kind)
assert.Equal(t, graph.OriginASTInferred, bound.Origin, "callback edge must ride a filterable tier")
assert.Equal(t, SynthFnValueCallback, bound.Meta[MetaSynthesizedBy])
assert.Equal(t, "handler", bound.Meta["fn_value_name"])
// The unbound and builtin candidates must not have produced any real edge.
for _, e := range g.GetOutEdges("router.go::register") {
if e.Meta == nil {
continue
}
if v, _ := e.Meta["via"].(string); v == fnValueRegistrationVia {
assert.Equal(t, "router.go::handler", e.To, "no registration edge should bind ghost/nil")
}
}
}
// TestCallbackGateIdempotent confirms a second pass lands nothing new — the
// synthesizer is a safe full-recompute.
func TestCallbackGateIdempotent(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{
ID: "h.go::onClick", Kind: graph.KindFunction, Name: "onClick",
FilePath: "h.go", StartLine: 8, Language: "go",
})
g.AddEdge(fnValueCandidateEdge("h.go::wire", "onClick", "h.go", 2))
first := ResolveFnValueCallbacks(g)
second := ResolveFnValueCallbacks(g)
assert.Equal(t, 1, first)
assert.Equal(t, 1, second, "the bound edge re-derives identically; AddEdge dedupes")
}
// ungatedFnValueCandidateEdge is a qualified-path candidate the gate may resolve
// cross-module.
func ungatedFnValueCandidateEdge(from, name, file string, line int) *graph.Edge {
e := fnValueCandidateEdge(from, name, file, line)
e.Meta["fn_value_ungated"] = true
return e
}
// TestCallbackGateSameFileTier pins the high-confidence tier for a same-file
// binding.
func TestCallbackGateSameFileTier(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "a.go::handler", Kind: graph.KindFunction, Name: "handler", FilePath: "a.go", Language: "go"})
g.AddNode(&graph.Node{ID: "a.go::register", Kind: graph.KindFunction, Name: "register", FilePath: "a.go", Language: "go"})
g.AddEdge(fnValueCandidateEdge("a.go::register", "handler", "a.go", 4))
assert.Equal(t, 1, ResolveFnValueCallbacks(g))
e := boundCallbackEdge(g, "a.go::register", "a.go::handler")
require.NotNil(t, e)
assert.Equal(t, 0.6, e.Confidence, "same-file binding rides the high-confidence tier")
}
// TestCallbackGateCrossModuleUngated pins that a qualified-path (ungated)
// candidate binds to a uniquely-named function cross-module at a lower tier.
func TestCallbackGateCrossModuleUngated(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "lib.rs::process", Kind: graph.KindFunction, Name: "process", FilePath: "lib.rs", Language: "rust"})
g.AddNode(&graph.Node{ID: "main.rs::run", Kind: graph.KindFunction, Name: "run", FilePath: "main.rs", Language: "rust"})
g.AddEdge(ungatedFnValueCandidateEdge("main.rs::run", "process", "main.rs", 3))
assert.Equal(t, 1, ResolveFnValueCallbacks(g))
e := boundCallbackEdge(g, "main.rs::run", "lib.rs::process")
require.NotNil(t, e, "cross-module ungated candidate binds")
assert.Equal(t, 0.45, e.Confidence, "cross-module binding rides the lower tier")
}
// TestCallbackGateCrossModuleAmbiguousDropped pins that an ungated candidate
// matching more than one function anywhere is refused.
func TestCallbackGateCrossModuleAmbiguousDropped(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "a.rs::process", Kind: graph.KindFunction, Name: "process", FilePath: "a.rs", Language: "rust"})
g.AddNode(&graph.Node{ID: "b.rs::process", Kind: graph.KindFunction, Name: "process", FilePath: "b.rs", Language: "rust"})
g.AddNode(&graph.Node{ID: "main.rs::run", Kind: graph.KindFunction, Name: "run", FilePath: "main.rs", Language: "rust"})
g.AddEdge(ungatedFnValueCandidateEdge("main.rs::run", "process", "main.rs", 3))
assert.Equal(t, 0, ResolveFnValueCallbacks(g), "ambiguous cross-module candidate dropped")
}
// TestCallbackGateNonUngatedStaysSameFile pins that a non-ungated candidate is
// never resolved cross-module even when a unique match exists elsewhere.
func TestCallbackGateNonUngatedStaysSameFile(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "lib.go::process", Kind: graph.KindFunction, Name: "process", FilePath: "lib.go", Language: "go"})
g.AddNode(&graph.Node{ID: "main.go::run", Kind: graph.KindFunction, Name: "run", FilePath: "main.go", Language: "go"})
g.AddEdge(fnValueCandidateEdge("main.go::run", "process", "main.go", 3)) // not ungated
assert.Equal(t, 0, ResolveFnValueCallbacks(g), "non-ungated candidate never binds cross-module")
}
func specialFnValueEdge(from, name, file string, line int, recvHint string) *graph.Edge {
e := fnValueCandidateEdge(from, name, file, line)
e.Meta["fn_ref_form"] = "special"
if recvHint != "" {
e.Meta["fn_ref_recv_hint"] = recvHint
}
return e
}
// TestCallbackGateSpecialSelfMember pins that a `this.m` reference binds to the
// enclosing type's method, never a coincidentally-named top-level function.
func TestCallbackGateSpecialSelfMember(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "c.ts::C.handle", Kind: graph.KindMethod, Name: "handle", FilePath: "c.ts", Meta: map[string]any{"receiver": "C"}})
g.AddNode(&graph.Node{ID: "c.ts::C.wire", Kind: graph.KindMethod, Name: "wire", FilePath: "c.ts", Meta: map[string]any{"receiver": "C"}})
g.AddNode(&graph.Node{ID: "other.ts::handle", Kind: graph.KindFunction, Name: "handle", FilePath: "other.ts"})
g.AddEdge(specialFnValueEdge("c.ts::C.wire", "handle", "c.ts", 5, "<self>"))
ResolveFnValueCallbacks(g)
bound := boundCallbackEdge(g, "c.ts::C.wire", "c.ts::C.handle")
require.NotNil(t, bound, "this.handle binds to the enclosing class's handle")
assert.Equal(t, "special", bound.Meta["fn_ref_form"])
assert.Equal(t, graph.OriginASTResolved, bound.Origin)
assert.Nil(t, boundCallbackEdge(g, "c.ts::C.wire", "other.ts::handle"), "must not bind the top-level handle")
}
// TestCallbackGateSpecialTypeQualified pins that `Foo::bar` binds to type Foo's
// bar method, not a same-named free function.
func TestCallbackGateSpecialTypeQualified(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "f.java::Foo.bar", Kind: graph.KindMethod, Name: "bar", FilePath: "f.java", Meta: map[string]any{"receiver": "Foo"}})
g.AddNode(&graph.Node{ID: "g.java::bar", Kind: graph.KindFunction, Name: "bar", FilePath: "g.java"})
g.AddNode(&graph.Node{ID: "m.java::M.run", Kind: graph.KindMethod, Name: "run", FilePath: "m.java", Meta: map[string]any{"receiver": "M"}})
e := specialFnValueEdge("m.java::M.run", "bar", "m.java", 3, "Foo")
e.Meta["fn_value_ungated"] = true
g.AddEdge(e)
ResolveFnValueCallbacks(g)
bound := boundCallbackEdge(g, "m.java::M.run", "f.java::Foo.bar")
require.NotNil(t, bound, "Foo::bar binds to Foo's bar method")
assert.Equal(t, graph.OriginASTResolved, bound.Origin)
assert.Equal(t, "special", bound.Meta["fn_ref_form"])
assert.Nil(t, boundCallbackEdge(g, "m.java::M.run", "g.java::bar"), "must not bind the free function")
}
// TestCallbackGateSkipGateUnique pins a curated-HOF string callable binding to
// the sole repo-wide function of that name at the gate-skip tier.
func TestCallbackGateSkipGateUnique(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "lib.php::helper", Kind: graph.KindFunction, Name: "helper", FilePath: "lib.php"})
g.AddNode(&graph.Node{ID: "main.php::run", Kind: graph.KindFunction, Name: "run", FilePath: "main.php"})
e := fnValueCandidateEdge("main.php::run", "helper", "main.php", 3)
e.Meta["skip_gate"] = true
e.Meta["fn_ref_form"] = "php_string_callable"
g.AddEdge(e)
ResolveFnValueCallbacks(g)
bound := boundCallbackEdge(g, "main.php::run", "lib.php::helper")
require.NotNil(t, bound, "unique string callable binds cross-file")
assert.Equal(t, 0.5, bound.Confidence)
assert.Equal(t, "php_string_callable", bound.Meta["fn_ref_form"])
}
// TestCallbackGateSkipGateAmbiguousDropped pins that a string callable whose
// name has two definitions is dropped.
func TestCallbackGateSkipGateAmbiguousDropped(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "a.php::helper", Kind: graph.KindFunction, Name: "helper", FilePath: "a.php"})
g.AddNode(&graph.Node{ID: "b.php::helper", Kind: graph.KindFunction, Name: "helper", FilePath: "b.php"})
g.AddNode(&graph.Node{ID: "main.php::run", Kind: graph.KindFunction, Name: "run", FilePath: "main.php"})
e := fnValueCandidateEdge("main.php::run", "helper", "main.php", 3)
e.Meta["skip_gate"] = true
g.AddEdge(e)
assert.Equal(t, 0, ResolveFnValueCallbacks(g), "ambiguous string callable dropped")
}
// TestCallbackGateSkipGateStaticString pins that a `'Foo::bar'` string callable
// binds to Foo's bar method, not a same-named free function.
func TestCallbackGateSkipGateStaticString(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "f.php::Foo.bar", Kind: graph.KindMethod, Name: "bar", FilePath: "f.php", Meta: map[string]any{"receiver": "Foo"}})
g.AddNode(&graph.Node{ID: "g.php::bar", Kind: graph.KindFunction, Name: "bar", FilePath: "g.php"})
g.AddNode(&graph.Node{ID: "main.php::run", Kind: graph.KindFunction, Name: "run", FilePath: "main.php"})
e := fnValueCandidateEdge("main.php::run", "bar", "main.php", 3)
e.Meta["skip_gate"] = true
e.Meta["fn_ref_recv_hint"] = "Foo"
g.AddEdge(e)
ResolveFnValueCallbacks(g)
require.NotNil(t, boundCallbackEdge(g, "main.php::run", "f.php::Foo.bar"), "Foo::bar binds to Foo.bar")
assert.Nil(t, boundCallbackEdge(g, "main.php::run", "g.php::bar"), "must not bind the free function")
}
func TestResolveMemberByType(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{ID: "a::Foo.m", Kind: graph.KindMethod, Name: "m", Meta: map[string]any{"receiver": "Foo"}})
g.AddNode(&graph.Node{ID: "a::Bar.m", Kind: graph.KindMethod, Name: "m", Meta: map[string]any{"receiver": "Bar"}})
assert.Equal(t, "a::Foo.m", resolveMemberByType(g, "Foo", "m"))
assert.Equal(t, "a::Bar.m", resolveMemberByType(g, "Bar", "m"))
assert.Equal(t, "", resolveMemberByType(g, "Baz", "m"), "unknown type does not bind")
}