package semantic_test
import (
"bytes"
"flag"
"go/scanner"
"path/filepath"
"testing"
"git.sr.ht/~mna/snow/pkg/internal/filetest"
"git.sr.ht/~mna/snow/pkg/printer"
"git.sr.ht/~mna/snow/pkg/semantic"
)
var testUpdateTypesTests = flag.Bool("test.update-types-tests", false, "If set, replace expected semantic types test results with actual results.")
func TestTypes(t *testing.T) {
baseDir := "testdata"
expectDir := filepath.Join(baseDir, "types")
for _, fi := range filetest.SourceFiles(t, baseDir) {
t.Run(fi.Name(), func(t *testing.T) {
unit, err := semantic.Run(semantic.TypeAssignPass, filepath.Join(baseDir, fi.Name()))
if unit == nil && err != nil {
t.Fatal(err)
}
var ebuf bytes.Buffer
scanner.PrintError(&ebuf, err)
var buf bytes.Buffer
pp := printer.Printer{W: &buf, Pos: printer.PosNone}
for _, f := range unit.Files {
_ = pp.PrintSemantic(f, unit.FileSet.File(f.Pos()))
}
filetest.DiffOutput(t, fi, buf.String(), expectDir, testUpdateTypesTests)
filetest.DiffErrors(t, fi, ebuf.String(), expectDir, testUpdateTypesTests)
// sanity check invariants:
// - all expressions have a type
// - all declarations have a type
// - all valid (non-tuple) identifiers have a Ref
var check visitorFunc
check = func(n semantic.Node) semantic.Visitor {
if n == nil {
return nil
}
switch n := n.(type) {
case semantic.Typed:
if n.Type() == nil {
lpos := unit.FileSet.Position(n.Pos())
t.Fatalf("%s: no type for %s", lpos, semantic.TypeIdentString(n))
}
if id, ok := n.(*semantic.Ident); ok && id.Type().Valid() && id.Index < 0 {
if id.Ref == nil {
lpos := unit.FileSet.Position(n.Pos())
t.Fatalf("%s: no Ref for identifier %s", lpos, id.Name)
}
}
}
return check
}
semantic.Walk(check, unit)
})
}
}