func makePkg(t *testing.T, src string) (*Package, error) { fset := token.NewFileSet() file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors) if err != nil { return nil, err } // use the package name as package path conf := Config{Importer: importer.Default([]*ast.File{file})} return conf.Check(file.Name.Name, fset, []*ast.File{file}, nil) }
func pkgFor(path, source string, info *Info) (*Package, error) { fset := token.NewFileSet() f, err := parser.ParseFile(fset, path, source, 0) if err != nil { return nil, err } conf := Config{Importer: importer.Default([]*ast.File{f}), AllowUseUninitializedVars: true, AllowUninitializedExprs: true} return conf.Check(f.Name.Name, fset, []*ast.File{f}, info) }
func checkFiles(t *testing.T, testfiles []string) { // parse files and collect parser errors files, errlist := parseFiles(t, testfiles) pkgName := "<no package>" if len(files) > 0 { pkgName = files[0].Name.Name } if *listErrors && len(errlist) > 0 { t.Errorf("--- %s:", pkgName) for _, err := range errlist { t.Error(err) } } // typecheck and collect typechecker errors conf := Config{AllowUseUninitializedVars: true, AllowUninitializedExprs: true} // special case for importC.src if len(testfiles) == 1 && testfiles[0] == "testdata/importC.src" { conf.FakeImportC = true } conf.Importer = importer.Default(files) conf.Error = func(err error) { if *listErrors { t.Error(err) return } // Ignore secondary error messages starting with "\t"; // they are clarifying messages for a primary error. if !strings.Contains(err.Error(), ": \t") { errlist = append(errlist, err) } } conf.Check(pkgName, fset, files, nil) if *listErrors { return } // match and eliminate errors; // we are expecting the following errors errmap := errMap(t, pkgName, files) eliminate(t, errmap, errlist) // there should be no expected errors left if len(errmap) > 0 { t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap)) for pos, list := range errmap { for _, rx := range list { t.Errorf("%s: %q", pos, rx) } } } }
func TestIssue5770(t *testing.T) { src := `package p; type S struct{T}` f, err := parser.ParseFile(fset, "", src, 0) if err != nil { t.Fatal(err) } conf := Config{Importer: importer.Default([]*ast.File{f})} _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash want := "undeclared name: T" if err == nil || !strings.Contains(err.Error(), want) { t.Errorf("got: %v; want: %s", err, want) } }
// ExampleMethodSet prints the method sets of various types. func ExampleMethodSet() { // Parse a single source file. const input = ` package temperature import "fmt" type Celsius float64 func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } func (c *Celsius) SetF(f float64) { *c = Celsius(f - 32 / 9 * 5) } ` fset := token.NewFileSet() f, err := parser.ParseFile(fset, "celsius.go", input, 0) if err != nil { log.Fatal(err) } // Type-check a package consisting of this file. // Type information for the imported packages // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. conf := types.Config{Importer: importer.Default([]*ast.File{f})} pkg, err := conf.Check("temperature", fset, []*ast.File{f}, nil) if err != nil { log.Fatal(err) } // Print the method sets of Celsius and *Celsius. celsius := pkg.Scope().Lookup("Celsius").Type() for _, t := range []types.Type{celsius, types.NewPointer(celsius)} { fmt.Printf("Method set of %s:\n", t) mset := types.NewMethodSet(t) for i := 0; i < mset.Len(); i++ { fmt.Println(mset.At(i)) } fmt.Println() } // Output: // Method set of temperature.Celsius: // method (temperature.Celsius) String() string // // Method set of *temperature.Celsius: // method (*temperature.Celsius) SetF(f float64) // method (*temperature.Celsius) String() string }
func TestHilbert(t *testing.T) { // generate source src := program(*H, *out) if *out != "" { ioutil.WriteFile(*out, src, 0666) return } // parse source fset := token.NewFileSet() f, err := parser.ParseFile(fset, "hilbert.go", src, 0) if err != nil { t.Fatal(err) } // type-check file DefPredeclaredTestFuncs() // define assert built-in conf := Config{Importer: importer.Default([]*ast.File{f})} _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) if err != nil { t.Fatal(err) } }
func testBuiltinSignature(t *testing.T, name, src0, want string) { src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0) f, err := parser.ParseFile(fset, "", src, 0) if err != nil { t.Errorf("%s: %s", src0, err) return } conf := Config{Importer: importer.Default([]*ast.File{f}), AllowUseUninitializedVars: true} uses := make(map[*ast.Ident]Object) types := make(map[ast.Expr]TypeAndValue) _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Uses: uses, Types: types}) if err != nil { t.Errorf("%s: %s", src0, err) return } // find called function n := 0 var fun ast.Expr for x := range types { if call, _ := x.(*ast.CallExpr); call != nil { fun = call.Fun n++ } } if n != 1 { t.Errorf("%s: got %d CallExprs; want 1", src0, n) return } // check recorded types for fun and descendents (may be parenthesized) for { // the recorded type for the built-in must match the wanted signature typ := types[fun].Type if typ == nil { t.Errorf("%s: no type recorded for %s", src0, ExprString(fun)) return } if got := typ.String(); got != want { t.Errorf("%s: got type %s; want %s", src0, got, want) return } // called function must be a (possibly parenthesized, qualified) // identifier denoting the expected built-in switch p := fun.(type) { case *ast.Ident: obj := uses[p] if obj == nil { t.Errorf("%s: no object found for %s", src0, p) return } bin, _ := obj.(*Builtin) if bin == nil { t.Errorf("%s: %s does not denote a built-in", src0, p) return } if bin.Name() != name { t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name) return } return // we're done case *ast.ParenExpr: fun = p.X // unpack case *ast.SelectorExpr: // built-in from package unsafe - ignore details return // we're done default: t.Errorf("%s: invalid function call", src0) return } } }
func TestEvalPos(t *testing.T) { testenv.MustHaveGoBuild(t) // The contents of /*-style comments are of the form // expr => value, type // where value may be the empty string. // Each expr is evaluated at the position of the comment // and the result is compared with the expected value // and type. var sources = []string{ ` package p import "fmt" import m "math" const c = 3.0 type T []int func f(a int, s string) float64 { fmt.Println("calling f") _ = m.Pi // use package math const d int = c + 1 var x int x = a + len(s) return float64(x) /* true => true, untyped bool */ /* fmt.Println => , func(a ...interface{}) (n int, err ?error) */ /* c => 3, untyped float */ /* T => , p.T */ /* a => , int */ /* s => , string */ /* d => 4, int */ /* x => , int */ /* d/c => 1, int */ /* c/2 => 3/2, untyped float */ /* m.Pi < m.E => false, untyped bool */ } `, ` package p /* c => 3, untyped float */ type T1 /* T1 => , p.T1 */ struct {} var v1 /* v1 => , int */ = 42 func /* f1 => , func(v1 float64) */ f1(v1 float64) { /* f1 => , func(v1 float64) */ /* v1 => , float64 */ var c /* c => 3, untyped float */ = "foo" /* c => , string */ { var c struct { c /* c => , string */ int } /* c => , struct{c int} */ _ = c } _ = func(a, b, c int) /* c => , string */ { /* c => , int */ } _ = c type FT /* FT => , p.FT */ interface{} } `, ` package p /* T => , p.T */ `, } fset := token.NewFileSet() var files []*ast.File for i, src := range sources { file, err := parser.ParseFile(fset, "p", src, parser.ParseComments) if err != nil { t.Fatalf("could not parse file %d: %s", i, err) } files = append(files, file) } conf := Config{Importer: importer.Default(files)} pkg, err := conf.Check("p", fset, files, nil) if err != nil { t.Fatal(err) } for _, file := range files { for _, group := range file.Comments { for _, comment := range group.List { s := comment.Text if len(s) >= 4 && s[:2] == "/*" && s[len(s)-2:] == "*/" { str, typ := split(s[2:len(s)-2], ", ") str, val := split(str, "=>") testEval(t, fset, pkg, comment.Pos(), str, nil, typ, val) } } } } }
// This tests that the package associated with the types.Object.Pkg method // is the type's package independent of the order in which the imports are // listed in the sources src1, src2 below. // The actual issue is in go/internal/gcimporter which has a corresponding // test; we leave this test here to verify correct behavior at the go/types // level. func TestIssue13898(t *testing.T) { testenv.MustHaveGoBuild(t) const src0 = ` package main import "github.com/tcard/sgo/sgo/types" func main() { var info types.Info for _, obj := range info.Uses { _ = obj.Pkg() } } ` // like src0, but also imports go/importer const src1 = ` package main import ( "github.com/tcard/sgo/sgo/types" _ "github.com/tcard/sgo/sgo/importer" ) func main() { var info types.Info for _, obj := range info.Uses { _ = obj.Pkg() } } ` // like src1 but with different import order // (used to fail with this issue) const src2 = ` package main import ( _ "github.com/tcard/sgo/sgo/importer" "github.com/tcard/sgo/sgo/types" ) func main() { var info types.Info for _, obj := range info.Uses { _ = obj.Pkg() } } ` f := func(test, src string) { f, err := parser.ParseFile(fset, "", src, 0) if err != nil { t.Fatal(err) } cfg := Config{Importer: importer.Default([]*ast.File{f})} info := Info{Uses: make(map[*ast.Ident]Object)} _, err = cfg.Check("main", fset, []*ast.File{f}, &info) if err != nil { t.Fatal(err) } var pkg *Package count := 0 for id, obj := range info.Uses { if id.Name == "Pkg" { pkg = obj.Pkg() count++ } } if count != 1 { t.Fatalf("%s: got %d entries named Pkg; want 1", test, count) } if pkg.Name() != "types" { t.Fatalf("%s: got %v; want package types", test, pkg) } } f("src0", src0) f("src1", src1) f("src2", src2) }
// ExampleScope prints the tree of Scopes of a package created from a // set of parsed files. func ExampleScope() { // Parse the source files for a package. fset := token.NewFileSet() var files []*ast.File for _, file := range []struct{ name, input string }{ {"main.go", ` package main import "fmt" func main() { freezing := FToC(-18) fmt.Println(freezing, Boiling) } `}, {"celsius.go", ` package main import "fmt" type Celsius float64 func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } func FToC(f float64) Celsius { return Celsius(f - 32 / 9 * 5) } const Boiling Celsius = 100 `}, } { f, err := parser.ParseFile(fset, file.name, file.input, 0) if err != nil { log.Fatal(err) } files = append(files, f) } // Type-check a package consisting of these files. // Type information for the imported "fmt" package // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. conf := types.Config{Importer: importer.Default(files)} pkg, err := conf.Check("temperature", fset, files, nil) if err != nil { log.Fatal(err) } // Print the tree of scopes. // For determinism, we redact addresses. var buf bytes.Buffer pkg.Scope().WriteTo(&buf, 0, true) rx := regexp.MustCompile(` 0x[a-fA-F0-9]*`) fmt.Println(rx.ReplaceAllString(buf.String(), "")) // Output: // package "temperature" scope { // . const temperature.Boiling temperature.Celsius // . type temperature.Celsius float64 // . func temperature.FToC(f float64) temperature.Celsius // . func temperature.main() // // . main.go scope { // . . package fmt // // . . function scope { // . . . var freezing temperature.Celsius // . . }. } // . celsius.go scope { // . . package fmt // // . . function scope { // . . . var c temperature.Celsius // . . } // . . function scope { // . . . var f float64 // . . }. }} }