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

144 lines
4.2 KiB
Go

package resolver
import (
"github.com/zzet/gortex/internal/graph"
)
// Speculative dynamic-dispatch synthesis. Some call shapes are genuine static
// blind spots that no precision-first framework rule covers: computed member
// calls `obj["foo"]()`, getattr-style dispatch `getattr(o, "foo")()`, and
// decorator registries. The extractor stamps these dropped calls with
// Meta["dyn_shape"] + Meta["dyn_key"] (the method name when it is a literal),
// and this opt-in pass mints LOW-confidence best-guess `calls` edges to the
// plausible same-name targets — tagged OriginSpeculative + Meta[speculative]
// so they are hidden from every default query and surfaced only on demand.
//
// This is what codegraph's playbook refused as "partial coverage worse than
// none": gortex can ship it because the edges are present-but-hidden-by-default
// (zero pollution) and explicitly auditable via `analyze kind=speculative`.
const (
// speculativeFanoutCap: above this many candidates the confidence floors;
// above the hard cap the whole set is dropped as noise (codegraph's rule).
speculativeFanoutCap = 12
speculativeHardCap = 40
)
// ResolveSpeculativeDispatch mints speculative call edges for the tagged
// blind-spot call shapes. No-op when disabled. Returns the edge count.
func ResolveSpeculativeDispatch(g graph.Store, enabled bool) int {
if g == nil || !enabled {
return 0
}
// Existing resolved call pairs, so a speculative edge never duplicates a
// real one from the same caller to the same target.
existing := map[string]bool{}
for e := range g.EdgesByKind(graph.EdgeCalls) {
if e == nil || e.IsSpeculative() {
continue
}
if !graph.IsUnresolvedTarget(e.To) {
existing[e.From+"\x00"+e.To] = true
}
}
type spec struct {
from, to, file, shape, key string
line, n int
}
var specs []spec
seen := map[string]bool{}
for e := range g.EdgesByKind(graph.EdgeCalls) {
if e == nil || e.Meta == nil {
continue
}
shape, _ := e.Meta["dyn_shape"].(string)
if shape == "" {
continue
}
// A precision-first synthesizer (object-registry) already bound this
// computed-member dispatch site; do not also mint a hidden guess.
if claimed, _ := e.Meta["registry_claimed"].(bool); claimed {
continue
}
key, _ := e.Meta["dyn_key"].(string)
if key == "" {
continue // v1: literal-key shapes only (variable-key is unbounded)
}
callerLang := ""
if c := g.GetNode(e.From); c != nil {
callerLang = c.Language
}
cands := speculativeCandidates(g, key, callerLang)
if len(cands) == 0 || len(cands) > speculativeHardCap {
continue
}
for _, c := range cands {
if existing[e.From+"\x00"+c.ID] {
continue
}
k := e.From + "\x00" + c.ID
if seen[k] {
continue
}
seen[k] = true
specs = append(specs, spec{from: e.From, to: c.ID, file: e.FilePath, line: e.Line, shape: shape, key: key, n: len(cands)})
}
}
resolved := 0
for _, s := range specs {
conf := 1.0 / float64(s.n)
if s.n > speculativeFanoutCap {
conf = 0.05
}
if conf > 0.45 {
conf = 0.45
}
if conf < 0.05 {
conf = 0.05
}
g.AddEdge(&graph.Edge{
From: s.from, To: s.to, Kind: graph.EdgeCalls,
FilePath: s.file, Line: s.line,
Origin: graph.OriginSpeculative,
Confidence: conf,
ConfidenceLabel: graph.ConfidenceLabelFor(graph.EdgeCalls, conf),
Meta: map[string]any{
graph.MetaSpeculative: true,
MetaSynthesizedBy: SynthSpeculative,
MetaProvenance: ProvenanceHeuristic,
"via": "speculative." + s.shape,
"candidate_count": s.n,
"dyn_key": s.key,
},
})
resolved++
}
return resolved
}
// speculativeCandidates returns the plausible targets for a literal dispatch
// key: function/method definitions of that exact name, in the caller's
// language family, excluding stubs.
func speculativeCandidates(g graph.Store, key, callerLang string) []*graph.Node {
var out []*graph.Node
for _, n := range g.FindNodesByName(key) {
if n == nil {
continue
}
if n.Kind != graph.KindFunction && n.Kind != graph.KindMethod {
continue
}
if graph.IsStub(n.ID) || graph.IsUnresolvedTarget(n.ID) {
continue
}
if callerLang != "" && n.Language != "" && n.Language != callerLang {
continue
}
out = append(out, n)
}
return out
}