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

220 lines
6.9 KiB
Go

package languages
import (
"strings"
"testing"
"github.com/zzet/gortex/internal/graph"
)
// scalaRefEdge is a flattened view of a reference-form edge for assertions.
type scalaRefEdge struct {
to string // canonical type name (after the unresolved:: prefix)
kind graph.EdgeKind
refCtx string
}
// extractScalaRefEdges runs the Scala extractor over src and returns every
// instantiate / structural-reference edge keyed by canonical target type.
func extractScalaRefEdges(t *testing.T, src string) []scalaRefEdge {
t.Helper()
res, err := NewScalaExtractor().Extract("demo.scala", []byte(src))
if err != nil {
t.Fatalf("Extract: %v", err)
}
var out []scalaRefEdge
for _, e := range res.Edges {
if e.Kind != graph.EdgeInstantiates && e.Kind != graph.EdgeReferences {
continue
}
to := strings.TrimPrefix(e.To, "unresolved::")
ctx, _ := e.Meta["ref_context"].(string)
out = append(out, scalaRefEdge{to: to, kind: e.Kind, refCtx: ctx})
// Every reference-form edge must be stamped OriginASTResolved so the
// cross-package guard does not revert the bare unresolved target.
if e.Origin != graph.OriginASTResolved {
t.Errorf("edge to %s kind=%s origin=%q, want %q", e.To, e.Kind, e.Origin, graph.OriginASTResolved)
}
}
return out
}
// scalaHasRefEdge reports whether edges contains one matching to/kind/refCtx.
func scalaHasRefEdge(edges []scalaRefEdge, to string, kind graph.EdgeKind, refCtx string) bool {
for _, e := range edges {
if e.to == to && e.kind == kind && e.refCtx == refCtx {
return true
}
}
return false
}
// countRefEdgesTo counts reference-form edges whose target is `to`.
func countRefEdgesTo(edges []scalaRefEdge, to string) int {
n := 0
for _, e := range edges {
if e.to == to {
n++
}
}
return n
}
func TestScalaRefFormInstantiation(t *testing.T) {
src := `
package demo
class Widget
object Cat
class Factory {
def make(): Widget = new Widget()
def cat(): Cat = Cat()
}
`
edges := extractScalaRefEdges(t, src)
if !scalaHasRefEdge(edges, "Widget", graph.EdgeInstantiates, "") {
t.Errorf("expected instantiate edge to Widget (new Widget()), got %+v", edges)
}
if !scalaHasRefEdge(edges, "Cat", graph.EdgeInstantiates, "") {
t.Errorf("expected instantiate edge to Cat (Cat() apply), got %+v", edges)
}
}
func TestScalaRefFormInheritance(t *testing.T) {
src := `
package demo
class Base
trait T1
trait T2
class X extends Base with T1 with T2
object O extends Foo
`
edges := extractScalaRefEdges(t, src)
for _, want := range []string{"Base", "T1", "T2"} {
if !scalaHasRefEdge(edges, want, graph.EdgeReferences, graph.RefContextInherit) {
t.Errorf("expected inherit ref to %s, got %+v", want, edges)
}
}
if !scalaHasRefEdge(edges, "Foo", graph.EdgeReferences, graph.RefContextInherit) {
t.Errorf("expected inherit ref to Foo (object O extends Foo), got %+v", edges)
}
}
func TestScalaRefFormCasts(t *testing.T) {
src := `
package demo
class Checker {
def is(x: Any): Boolean = x.isInstanceOf[Foo]
def as(x: Any): Foo = x.asInstanceOf[Foo]
def m(x: Any): String = x match {
case _: Bar => "b"
case d: Baz => "z"
case _ => "?"
}
}
`
edges := extractScalaRefEdges(t, src)
for _, want := range []string{"Foo", "Bar", "Baz"} {
if !scalaHasRefEdge(edges, want, graph.EdgeReferences, graph.RefContextCast) {
t.Errorf("expected cast ref to %s, got %+v", want, edges)
}
}
}
func TestScalaRefFormStaticAccess(t *testing.T) {
src := `
package demo
class User {
def color(): String = Color.Red
def max(): Int = Config.MAX
}
`
edges := extractScalaRefEdges(t, src)
if !scalaHasRefEdge(edges, "Color", graph.EdgeReferences, graph.RefContextStaticAccess) {
t.Errorf("expected static_access ref to Color, got %+v", edges)
}
if !scalaHasRefEdge(edges, "Config", graph.EdgeReferences, graph.RefContextStaticAccess) {
t.Errorf("expected static_access ref to Config, got %+v", edges)
}
}
// TestScalaRefFormGenericArgs covers Scala's square-bracket generic arguments
// (`List[Foo]`, `Map[String, Bar]`, `Future[Seq[Repo]]`, parameterized
// supertypes) across every position. The type-annotation pass keeps only the
// container head or the single unwrapped element, so an argument in a non-unwrap
// slot or nested deeper would never surface to find_usages without these
// generic_arg reference edges.
func TestScalaRefFormGenericArgs(t *testing.T) {
src := `
package demo
class Repo extends Base[Scope] with Mixin[Region] {
val one: List[Widget] = Nil
var lookup: Map[String, Account] = Map()
def f(a: Set[Audit], b: Seq[Log]): Option[Result] = None
val deep: Map[String, List[Invoice]] = Map()
val nested: Future[Seq[Tenant]] = null
}
`
edges := extractScalaRefEdges(t, src)
// Every element type, in every position, surfaces as a generic_arg ref.
for _, want := range []string{
"Scope", // supertype type-arg: extends Base[Scope]
"Region", // mixin type-arg: with Mixin[Region]
"Widget", // val annotation: List[Widget]
"Account", // var annotation slot: Map[String, Account]
"Audit", // parameter: Set[Audit]
"Log", // parameter: Seq[Log]
"Result", // return type: Option[Result]
"Invoice", // deep-nested: Map[String, List[Invoice]]
"Tenant", // nested: Future[Seq[Tenant]]
} {
if !scalaHasRefEdge(edges, want, graph.EdgeReferences, graph.RefContextGenericArg) {
t.Errorf("expected generic_arg ref to %s, got %+v", want, edges)
}
}
// The String key of every Map is a primitive — filtered everywhere.
if countRefEdgesTo(edges, "String") != 0 {
t.Errorf("primitive String key must not emit a generic_arg ref, got %+v", edges)
}
}
func TestScalaRefFormNegatives(t *testing.T) {
src := `
package demo
class Thing {
val owner: Person = null
def lower(): Int = foo()
def self(): Int = this.x
def low(): Int = lowercaseObj.field
def num(p: Int): Int = p
def prim(): Boolean = p.isInstanceOf[Int]
}
`
edges := extractScalaRefEdges(t, src)
// `foo()` is a lowercase apply — not construction.
if scalaHasRefEdge(edges, "foo", graph.EdgeInstantiates, "") {
t.Errorf("lowercase apply foo() must not instantiate, got %+v", edges)
}
// `this.x` head is `this` — never static access.
if countRefEdgesTo(edges, "this") != 0 {
t.Errorf("this.x must not emit static_access, got %+v", edges)
}
// `lowercaseObj.field` head is lowercase — not static access.
if scalaHasRefEdge(edges, "lowercaseObj", graph.EdgeReferences, graph.RefContextStaticAccess) {
t.Errorf("lowercase select must not emit static_access, got %+v", edges)
}
// `Int` is a primitive — filtered everywhere (annotation, cast, etc.).
if countRefEdgesTo(edges, "Int") != 0 {
t.Errorf("primitive Int must not emit any reference-form edge, got %+v", edges)
}
// `Person` here is only a val type annotation — owned by #143's typed_as
// pass, never re-emitted as instantiate/inherit/cast/static_access.
for _, e := range edges {
if e.to == "Person" {
t.Errorf("val type annotation Person must not produce a reference-form edge, got %+v", e)
}
}
}