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

94 lines
2.7 KiB
Go

package languages
import (
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/parser"
sitter "github.com/zzet/gortex/internal/parser/tsitter"
)
// Django ORM descriptor hint. A QuerySet sets `self._iterable_class =
// ModelIterable` and later iterates `self._iterable_class(self)`, which runs
// the iterable class's `__iter__`. The static graph cannot follow the
// descriptor, so this pass records which class a QuerySet assigns to
// `_iterable_class` by stamping the enclosing class node; the resolver's
// Django claiming resolver reads the hint to bind the descriptor reference
// to `<IterableClass>.__iter__`.
// captureDjangoDescriptors stamps Meta["django_iterable_class"] on a class
// that assigns `_iterable_class = <Class>`. Runs at the tail of Extract.
func captureDjangoDescriptors(result *parser.ExtractionResult, root *sitter.Node, _ string, src []byte) {
if root == nil || result == nil {
return
}
classByName := map[string]*graph.Node{}
for _, n := range result.Nodes {
if n != nil && n.Kind == graph.KindType {
classByName[n.Name] = n
}
}
if len(classByName) == 0 {
return
}
djangoWalk(root, func(n *sitter.Node) {
if n.Type() != "assignment" {
return
}
left := n.ChildByFieldName("left")
if left == nil || djangoAssignTarget(left, src) != "_iterable_class" {
return
}
right := n.ChildByFieldName("right")
if right == nil || right.Type() != "identifier" {
return
}
cls := djangoEnclosingClassName(n, src)
cn := classByName[cls]
if cn == nil {
return
}
if cn.Meta == nil {
cn.Meta = map[string]any{}
}
cn.Meta["django_iterable_class"] = right.Content(src)
})
}
// djangoAssignTarget returns the attribute/variable name an assignment
// targets: `_iterable_class` for both `self._iterable_class = X` and a
// class-level `_iterable_class = X`.
func djangoAssignTarget(left *sitter.Node, src []byte) string {
switch left.Type() {
case "attribute":
if a := left.ChildByFieldName("attribute"); a != nil {
return a.Content(src)
}
case "identifier":
return left.Content(src)
}
return ""
}
// djangoEnclosingClassName walks up to the nearest class_definition name.
func djangoEnclosingClassName(n *sitter.Node, src []byte) string {
for cur := n.Parent(); cur != nil; cur = cur.Parent() {
if cur.Type() == "class_definition" {
if nm := cur.ChildByFieldName("name"); nm != nil {
return nm.Content(src)
}
return ""
}
}
return ""
}
// djangoWalk visits n and all its named descendants.
func djangoWalk(n *sitter.Node, fn func(*sitter.Node)) {
if n == nil {
return
}
fn(n)
for i, _nc := 0, int(n.NamedChildCount()); i < _nc; i++ {
djangoWalk(n.NamedChild(i), fn)
}
}