chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
package languages
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/zzet/gortex/internal/parser"
|
||||
sitter "github.com/zzet/gortex/internal/parser/tsitter"
|
||||
)
|
||||
|
||||
// emitScalaAnnotationEdges scans the direct children of a class /
|
||||
// object / trait / function / var-definition for `annotation` nodes
|
||||
// and emits one EdgeAnnotated per annotation onto the synthetic
|
||||
// `annotation::scala::<name>` node. Examples:
|
||||
//
|
||||
// @deprecated("use newF", "1.5") → annotation::scala::deprecated
|
||||
// @inline → annotation::scala::inline
|
||||
// @volatile → annotation::scala::volatile
|
||||
// @tailrec → annotation::scala::tailrec
|
||||
// @main → annotation::scala::main
|
||||
//
|
||||
// The Scala tree-sitter grammar places annotations as direct children
|
||||
// of the annotated definition node, so we only scan the immediate
|
||||
// child set — recursive walks would attribute method annotations to
|
||||
// the enclosing class, which is wrong.
|
||||
//
|
||||
// `seen` deduplicates synthetic annotation nodes within the extraction
|
||||
// pass; the graph layer also dedupes by ID so a miss here is wasted
|
||||
// work, not a correctness bug.
|
||||
func emitScalaAnnotationEdges(
|
||||
defNode *sitter.Node, fromID, filePath string, src []byte,
|
||||
result *parser.ExtractionResult, seen map[string]bool,
|
||||
) {
|
||||
if defNode == nil || fromID == "" {
|
||||
return
|
||||
}
|
||||
for i, _nc := 0, int(defNode.NamedChildCount()); i < _nc; i++ {
|
||||
c := defNode.NamedChild(i)
|
||||
if c == nil || c.Type() != "annotation" {
|
||||
continue
|
||||
}
|
||||
name, args := scalaAnnotationNameAndArgs(c, src)
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
line := int(c.StartPoint().Row) + 1
|
||||
EmitAnnotationEdge(fromID, "scala", name, args, filePath, line, result, seen)
|
||||
}
|
||||
}
|
||||
|
||||
// scalaAnnotationNameAndArgs reads an `annotation` node and returns
|
||||
// (name, args). Name comes from the `type_identifier` child; args is
|
||||
// the raw inner-parentheses text of an `arguments` child (or "" when
|
||||
// the annotation has no argument list). Both return values are
|
||||
// trimmed of surrounding whitespace.
|
||||
//
|
||||
// Multi-segment names like `@a.b.C` (qualified annotation references)
|
||||
// are returned as the trailing segment ("C") so `find_usages` on the
|
||||
// synthetic annotation node still groups every site that uses the
|
||||
// same logical annotation regardless of import alias.
|
||||
func scalaAnnotationNameAndArgs(annot *sitter.Node, src []byte) (string, string) {
|
||||
if annot == nil {
|
||||
return "", ""
|
||||
}
|
||||
var name, args string
|
||||
for i, _nc := 0, int(annot.NamedChildCount()); i < _nc; i++ {
|
||||
c := annot.NamedChild(i)
|
||||
if c == nil {
|
||||
continue
|
||||
}
|
||||
switch c.Type() {
|
||||
case "type_identifier", "identifier":
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(c.Content(src))
|
||||
}
|
||||
case "stable_type_identifier", "projected_type":
|
||||
// Qualified annotation reference like `@scala.deprecated` —
|
||||
// keep only the trailing leaf so the synthetic node
|
||||
// matches every alias.
|
||||
text := strings.TrimSpace(c.Content(src))
|
||||
if idx := strings.LastIndex(text, "."); idx >= 0 {
|
||||
text = text[idx+1:]
|
||||
}
|
||||
if name == "" {
|
||||
name = text
|
||||
}
|
||||
case "arguments":
|
||||
text := strings.TrimSpace(c.Content(src))
|
||||
if len(text) >= 2 && text[0] == '(' && text[len(text)-1] == ')' {
|
||||
text = text[1 : len(text)-1]
|
||||
}
|
||||
args = strings.TrimSpace(text)
|
||||
}
|
||||
}
|
||||
return name, args
|
||||
}
|
||||
Reference in New Issue
Block a user