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

259 lines
8.1 KiB
Go

package languages
import (
"testing"
"github.com/zzet/gortex/internal/graph"
)
// refEdgeUseKind returns the ref_context Meta tag of a reference edge, or "".
func refEdgeUseKind(e *graph.Edge) string {
if e.Meta == nil {
return ""
}
if v, ok := e.Meta["ref_context"].(string); ok {
return v
}
return ""
}
// hasInstantiate reports whether an EdgeInstantiates to unresolved::<typ>
// exists, and that it is stamped OriginASTResolved (so the cross-package
// guard never reverts it).
func hasInstantiate(t *testing.T, edges []*graph.Edge, typ string) bool {
t.Helper()
want := "unresolved::" + typ
for _, e := range edges {
if e.Kind == graph.EdgeInstantiates && e.To == want {
if e.Origin != graph.OriginASTResolved {
t.Errorf("EdgeInstantiates → %s Origin = %q; want ast_resolved", want, e.Origin)
}
return true
}
}
return false
}
// hasRef reports whether an EdgeReferences to unresolved::<typ> with the
// given ref_context exists, stamped OriginASTResolved.
func hasRef(t *testing.T, edges []*graph.Edge, typ, useKind string) bool {
t.Helper()
want := "unresolved::" + typ
for _, e := range edges {
if e.Kind == graph.EdgeReferences && e.To == want && refEdgeUseKind(e) == useKind {
if e.Origin != graph.OriginASTResolved {
t.Errorf("EdgeReferences(%s) → %s Origin = %q; want ast_resolved", useKind, want, e.Origin)
}
return true
}
}
return false
}
// TestRustRefForm_Construction covers the construction surface:
// associated-function constructors, struct-expression literals, and
// tuple-struct / enum-variant calls.
func TestRustRefForm_Construction(t *testing.T) {
src := `fn run() {
let a = Foo::new();
let b = Bar { x: 1 };
let c = Variant(1, 2);
}
`
_, edges := runRustExtract(t, "src/lib.rs", src)
if !hasInstantiate(t, edges, "Foo") {
t.Errorf("Foo::new() must emit EdgeInstantiates → unresolved::Foo")
}
if !hasInstantiate(t, edges, "Bar") {
t.Errorf("Bar { x: 1 } struct expression must emit EdgeInstantiates → unresolved::Bar")
}
if !hasInstantiate(t, edges, "Variant") {
t.Errorf("Variant(1, 2) tuple-struct/variant call must emit EdgeInstantiates → unresolved::Variant")
}
}
// TestRustRefForm_TraitImpls covers inheritance / trait edges: inherent
// impl, trait impl (both the trait and the type), trait bound, where
// predicate, supertrait, and dyn Trait.
func TestRustRefForm_TraitImpls(t *testing.T) {
src := `struct S;
impl Inherent for S {}
trait Greeter: Base {
fn hi(&self);
}
fn run<T: Bound>(x: T) -> Box<dyn Animal> where T: Where {
todo!()
}
`
_, edges := runRustExtract(t, "src/lib.rs", src)
// impl Inherent for S — both the trait and the implementing type.
if !hasRef(t, edges, "Inherent", "inherit") {
t.Errorf("impl Inherent for S must emit inherit → Inherent")
}
if !hasRef(t, edges, "S", "inherit") {
t.Errorf("impl Inherent for S must emit inherit → S (the implementing type)")
}
// trait Greeter: Base — supertrait.
if !hasRef(t, edges, "Base", "inherit") {
t.Errorf("supertrait `: Base` must emit inherit → Base")
}
// T: Bound — type-parameter bound.
if !hasRef(t, edges, "Bound", "inherit") {
t.Errorf("trait bound T: Bound must emit inherit → Bound")
}
// where T: Where — where-clause bound.
if !hasRef(t, edges, "Where", "inherit") {
t.Errorf("where T: Where must emit inherit → Where")
}
// Box<dyn Animal> — dynamic trait object.
if !hasRef(t, edges, "Animal", "inherit") {
t.Errorf("dyn Animal must emit inherit → Animal")
}
}
// TestRustRefForm_InherentImpl checks a bare `impl Foo` (no trait) emits a
// single inherit edge to the implementing type.
func TestRustRefForm_InherentImpl(t *testing.T) {
src := `struct Foo;
impl Foo {
fn m(&self) {}
}
`
_, edges := runRustExtract(t, "src/lib.rs", src)
if !hasRef(t, edges, "Foo", "inherit") {
t.Errorf("impl Foo must emit inherit → Foo")
}
}
// TestRustRefForm_Cast checks `x as Foo` emits a cast reference.
func TestRustRefForm_Cast(t *testing.T) {
src := `fn run(x: u64) {
let y = x as Widget;
let z = x as u32;
}
`
_, edges := runRustExtract(t, "src/lib.rs", src)
if !hasRef(t, edges, "Widget", "cast") {
t.Errorf("x as Widget must emit cast → Widget")
}
// Primitive cast target must not emit an edge.
for _, e := range edges {
if e.To == "unresolved::u32" {
t.Errorf("x as u32 (primitive) must not emit a reference edge")
}
}
}
// TestRustRefForm_PathAccess covers static / path access: a constant, an
// enum variant, a non-constructor associated function, and a
// module-qualified path whose trailing segment is a type.
func TestRustRefForm_PathAccess(t *testing.T) {
src := `fn run() {
let a = Config::CONST;
let b = Color::Red;
let c = Helper::compute();
let d = std::io::Error::last();
}
`
_, edges := runRustExtract(t, "src/lib.rs", src)
if !hasRef(t, edges, "Config", "static_access") {
t.Errorf("Config::CONST must emit static_access → Config")
}
if !hasRef(t, edges, "Color", "static_access") {
t.Errorf("Color::Red must emit static_access → Color")
}
if !hasRef(t, edges, "Helper", "static_access") {
t.Errorf("Helper::compute() must emit static_access → Helper")
}
// std::io::Error::last() — module-qualified, lowercase head; the
// trailing Capitalized segment Error is the type. last() is not a
// constructor, so this is a static_access (not instantiation).
if !hasRef(t, edges, "Error", "static_access") {
t.Errorf("std::io::Error::last() must emit static_access → Error")
}
}
// TestRustRefForm_DeriveAttribute checks `#[derive(Foo, Bar)]` emits a
// static_access reference for each derive macro name.
func TestRustRefForm_DeriveAttribute(t *testing.T) {
src := `#[derive(Serialize, Deserialize)]
struct Payload {
id: u32,
}
`
_, edges := runRustExtract(t, "src/lib.rs", src)
if !hasRef(t, edges, "Serialize", "static_access") {
t.Errorf("#[derive(Serialize, ...)] must emit static_access → Serialize")
}
if !hasRef(t, edges, "Deserialize", "static_access") {
t.Errorf("#[derive(..., Deserialize)] must emit static_access → Deserialize")
}
}
// TestRustRefForm_Negatives checks the false-positive guards: lowercase
// function calls, primitive let annotations, all-lowercase module paths,
// and `self::`/`crate::` paths emit no reference-form edges.
func TestRustRefForm_Negatives(t *testing.T) {
src := `fn run() {
foo();
let x: i32 = 0;
let y = self::helper();
let z = crate::util::compute();
bar::baz();
}
`
_, edges := runRustExtract(t, "src/lib.rs", src)
for _, e := range edges {
if e.Kind != graph.EdgeInstantiates && e.Kind != graph.EdgeReferences {
continue
}
switch e.To {
case "unresolved::foo", "unresolved::i32", "unresolved::helper",
"unresolved::self", "unresolved::crate", "unresolved::util",
"unresolved::compute", "unresolved::bar", "unresolved::baz":
t.Errorf("unexpected reference-form edge %s → %s (ref_context=%q)", e.Kind, e.To, refEdgeUseKind(e))
}
}
}
// TestRustRefForm_NoDoubleEmitForLetType guards against double-counting:
// a `let x: Type = Type::new()` line should NOT emit a reference-form edge
// for the let-annotation type (that's the base extractor's EdgeTypedAs
// territory) — only the construction view from the RHS.
func TestRustRefForm_NoDoubleEmitForLetType(t *testing.T) {
src := `fn run() {
let c: Client = Client::new();
}
`
_, edges := runRustExtract(t, "src/lib.rs", src)
// The let annotation `: Client` stays an EdgeTypedAs (base extractor).
typed := edgesByKind(edges, graph.EdgeTypedAs)
foundTyped := false
for _, e := range typed {
if e.To == "unresolved::Client" {
foundTyped = true
}
}
if !foundTyped {
t.Errorf("let annotation : Client must still emit EdgeTypedAs → Client; got %v", edgeTargets(typed))
}
// The RHS Client::new() is the construction view.
if !hasInstantiate(t, edges, "Client") {
t.Errorf("Client::new() must emit EdgeInstantiates → Client")
}
// No `static_access Client` should leak from the callee path.
for _, e := range edges {
if e.Kind == graph.EdgeReferences && e.To == "unresolved::Client" && refEdgeUseKind(e) == "static_access" {
t.Errorf("Client::new() must not emit a static_access reference (it is a construction)")
}
}
}