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
101 lines
3.4 KiB
Go
101 lines
3.4 KiB
Go
package languages
|
|
|
|
import (
|
|
"strings"
|
|
|
|
sitter "github.com/zzet/gortex/internal/parser/tsitter"
|
|
|
|
"github.com/zzet/gortex/internal/parser"
|
|
)
|
|
|
|
// Go-side event pub/sub detection (F15). The Go extractor already walks
|
|
// every selector-style call (`callm.expr`); this file recognises the
|
|
// subset of those calls that publish to or subscribe from a message
|
|
// broker — NATS (`nc.Publish` / `nc.Subscribe` / `nc.QueueSubscribe`),
|
|
// Kafka (`w.WriteMessages` / `p.Produce` / `producer.SendMessage`),
|
|
// RabbitMQ (`ch.PublishWithContext` / `ch.Consume`), Redis go-redis
|
|
// (`rdb.Publish` / `rdb.Subscribe` / `rdb.PSubscribe`). Classification
|
|
// and graph-artifact emission live in the shared pubsub.go module; the
|
|
// Go-specific part is pulling the first string-literal argument out of
|
|
// a tree-sitter call_expression.
|
|
|
|
// goPubsubCandidate is a deferred pub/sub call site. Transport
|
|
// resolution + role classification happen in emitGoPubsubEvents once
|
|
// the file's full import set is known.
|
|
type goPubsubCandidate struct {
|
|
method string
|
|
topic string
|
|
line int
|
|
}
|
|
|
|
// detectGoPubsubCall inspects a callm.expr capture and, when the method
|
|
// is a known pub/sub publish/subscribe operation with a string-literal
|
|
// topic argument, returns the candidate. The transport and role aren't
|
|
// resolved here — that needs the file's imports, which the extractor
|
|
// finishes collecting on the same tree walk.
|
|
func detectGoPubsubCall(callExpr *sitter.Node, method string, src []byte) (goPubsubCandidate, bool) {
|
|
if callExpr == nil {
|
|
return goPubsubCandidate{}, false
|
|
}
|
|
if _, ok := pubsubMethods[method]; !ok {
|
|
return goPubsubCandidate{}, false
|
|
}
|
|
topic := firstGoStringLiteralArg(callExpr, src)
|
|
if topic == "" {
|
|
return goPubsubCandidate{}, false
|
|
}
|
|
return goPubsubCandidate{
|
|
method: method,
|
|
topic: topic,
|
|
line: int(callExpr.StartPoint().Row) + 1,
|
|
}, true
|
|
}
|
|
|
|
// firstGoStringLiteralArg returns the content of the first
|
|
// string-literal argument of a Go call_expression, or "" when no
|
|
// argument is a string literal. NATS / Redis pass the subject or
|
|
// channel as a positional string after a context argument
|
|
// (`rdb.Publish(ctx, "channel", payload)`), so scanning for the first
|
|
// literal anywhere in the argument list — rather than requiring it in
|
|
// position zero — catches the common broker call shapes.
|
|
func firstGoStringLiteralArg(callExpr *sitter.Node, src []byte) string {
|
|
args := callExpr.ChildByFieldName("arguments")
|
|
if args == nil {
|
|
return ""
|
|
}
|
|
for i, _nc := 0, int(args.NamedChildCount()); i < _nc; i++ {
|
|
c := args.NamedChild(i)
|
|
if c == nil {
|
|
continue
|
|
}
|
|
if c.Type() != "interpreted_string_literal" && c.Type() != "raw_string_literal" {
|
|
continue
|
|
}
|
|
text := strings.Trim(c.Content(src), "\"`")
|
|
if text == "" {
|
|
continue
|
|
}
|
|
return text
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// emitGoPubsubEvents resolves each deferred candidate against the
|
|
// file's imports and emits the KindEvent topic nodes + EdgeEmits /
|
|
// EdgeListensOn edges. callerLookup maps a 1-based line to the
|
|
// enclosing function ID.
|
|
func emitGoPubsubEvents(candidates []goPubsubCandidate, importPaths []string, callerLookup func(line int) string, filePath string, result *parser.ExtractionResult) {
|
|
if len(candidates) == 0 {
|
|
return
|
|
}
|
|
events := make([]pubsubEvent, 0, len(candidates))
|
|
for _, c := range candidates {
|
|
ev, ok := classifyPubsubCall(c.method, c.topic, importPaths, c.line)
|
|
if !ok {
|
|
continue
|
|
}
|
|
events = append(events, ev)
|
|
}
|
|
emitPubsubEvents(events, callerLookup, filePath, "go", result)
|
|
}
|