// CheckPackages checks packages for errors. // ignore is a map of package names to regular expressions. Identifiers from a package are // checked against its regular expressions and if any of the expressions match the call // is not checked. // If blank is true then assignments to the blank identifier are also considered to be // ignored errors. func CheckPackages(pkgPaths []string, ignore map[string]*regexp.Regexp, blank bool) error { var loadcfg = loader.Config{ SourceImports: true, AllowErrors: false, } for _, p := range pkgPaths { loadcfg.Import(p) } program, err := loadcfg.Load() if err != nil { return fmt.Errorf("could not type check: %s", err) } visitor := &checker{program, nil, ignore, blank, make(map[string][]string), []error{}} for _, p := range pkgPaths { if p == "unsafe" { // not a real package continue } visitor.pkg = program.Imported[p] for _, astFile := range visitor.pkg.Files { ast.Walk(visitor, astFile) } } if len(visitor.errors) > 0 { return UncheckedErrors{visitor.errors} } return nil }
func TestTransitivelyErrorFreeFlag(t *testing.T) { // Create an minimal custom build.Context // that fakes the following packages: // // a --> b --> c! c has an error // \ d and e are transitively error-free. // e --> d // // Each package [a-e] consists of one file, x.go. pkgs := map[string]string{ "a": `package a; import (_ "b"; _ "e")`, "b": `package b; import _ "c"`, "c": `package c; func f() { _ = int(false) }`, // type error within function body "d": `package d;`, "e": `package e; import _ "d"`, } conf := loader.Config{ AllowErrors: true, SourceImports: true, Build: fakeContext(pkgs), } conf.Import("a") prog, err := conf.Load() if err != nil { t.Errorf("Load failed: %s", err) } if prog == nil { t.Fatalf("Load returned nil *Program") } for pkg, info := range prog.AllPackages { var wantErr, wantTEF bool switch pkg.Path() { case "a", "b": case "c": wantErr = true case "d", "e": wantTEF = true default: t.Errorf("unexpected package: %q", pkg.Path()) continue } if (info.Errors != nil) != wantErr { if wantErr { t.Errorf("Package %q.Error = nil, want error", pkg.Path()) } else { t.Errorf("Package %q has unexpected Errors: %v", pkg.Path(), info.Errors) } } if info.TransitivelyErrorFree != wantTEF { t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t", pkg.Path(), info.TransitivelyErrorFree, wantTEF) } } }
// Test that both syntax (scan/parse) and type errors are both recorded // (in PackageInfo.Errors) and reported (via Config.TypeChecker.Error). func TestErrorReporting(t *testing.T) { pkgs := map[string]string{ "a": `package a; import _ "b"; var x int = false`, "b": `package b; 'syntax error!`, } conf := loader.Config{ AllowErrors: true, SourceImports: true, Build: fakeContext(pkgs), } var allErrors []error conf.TypeChecker.Error = func(err error) { allErrors = append(allErrors, err) } conf.Import("a") prog, err := conf.Load() if err != nil { t.Errorf("Load failed: %s", err) } if prog == nil { t.Fatalf("Load returned nil *Program") } hasError := func(errors []error, substr string) bool { for _, err := range errors { if strings.Contains(err.Error(), substr) { return true } } return false } // TODO(adonovan): test keys of ImportMap. // Check errors recorded in each PackageInfo. for pkg, info := range prog.AllPackages { switch pkg.Path() { case "a": if !hasError(info.Errors, "cannot convert false") { t.Errorf("a.Errors = %v, want bool conversion (type) error", info.Errors) } case "b": if !hasError(info.Errors, "rune literal not terminated") { t.Errorf("b.Errors = %v, want unterminated literal (syntax) error", info.Errors) } } } // Check errors reported via error handler. if !hasError(allErrors, "cannot convert false") || !hasError(allErrors, "rune literal not terminated") { t.Errorf("allErrors = %v, want both syntax and type errors", allErrors) } }
// Oracle annotates `pkg` using go.tools/oracle interface implements detector. // It uses `scopes` as analysis scope. // If `scopes` is none of one of `scopes` is zero string, it uses unit tests as scope. func Oracle(pkg *ast.Package, scopes ...string) error { settings := build.Default settings.BuildTags = []string{} // TODO conf := loader.Config{Build: &settings, SourceImports: true} withTests := false if len(scopes) == 0 { withTests = true } for _, scope := range scopes { if scope == "" { withTests = true } else { conf.Import(scope) } } if withTests { conf.ImportWithTests(pkg.Name) } else { conf.Import(pkg.Name) } iprog, err := conf.Load() if err != nil { return fmt.Errorf("oracle annotator: conf load error: %+v", err) } o, err := oracle.New(iprog, nil, false) if err != nil { return fmt.Errorf("oracle annotator: create error: %+v", err) } for _, class := range pkg.Classes { qpos, err := oracle.ParseQueryPos(iprog, string(class.Pos), false) if err != nil { log.Printf("oracle annotator: parse query pos error: %+v, %+v", err, class.Pos) continue } res, err := o.Query("implements", qpos) if err != nil { return fmt.Errorf("oracle annotator: query error: %+v, %v", err, class.Pos) } impls := res.Serial().Implements for _, target := range impls.AssignableFromPtr { addImplements(class, target) } for _, target := range impls.AssignableFrom { addImplements(class, target) } } return nil }
func doTestable(args []string) error { conf := loader.Config{ Build: &build.Default, SourceImports: true, } // TODO(adonovan): make go/types choose its default Sizes from // build.Default or a specified *build.Context. var wordSize int64 = 8 switch conf.Build.GOARCH { case "386", "arm": wordSize = 4 } wordSize = 4 // TARDIS Go addition to force default int size to 32 bits //conf.Build.GOARCH = "tardisgo" // TARDIS Go addition to ensure no architecure-specific code will compile //conf.Build.GOOS = "tardisgo" // TARDIS Go addition to ensure no OS-specific code will compile conf.TypeChecker.Sizes = &types.StdSizes{ MaxAlign: 8, WordSize: wordSize, } var mode ssa.BuilderMode for _, c := range *buildFlag { switch c { case 'D': mode |= ssa.GlobalDebug case 'P': mode |= ssa.LogPackages | ssa.BuildSerially case 'F': mode |= ssa.LogFunctions | ssa.BuildSerially case 'S': mode |= ssa.LogSource | ssa.BuildSerially case 'C': mode |= ssa.SanityCheckFunctions case 'N': mode |= ssa.NaiveForm case 'G': conf.SourceImports = false case 'L': mode |= ssa.BuildSerially default: log.Fatalf("Unknown -build option: '%c'.", c) } } var interpMode interp.Mode for _, c := range *interpFlag { switch c { case 'T': interpMode |= interp.EnableTracing case 'R': interpMode |= interp.DisableRecover default: log.Fatalf("Unknown -interp option: '%c'.", c) } } if len(args) == 0 { //fmt.Fprint(os.Stderr, usage) return fmt.Errorf("%v", usage) } // Profiling support. if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { return err } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } // TARDIS Go TEST // Really need to find a way to replace entire packages, this experiment did not work... /* conf.Fset = token.NewFileSet() f, err := parser.ParseFile(conf.Fset, conf.Build.GOPATH+"/src/github.com/tardisgo/tardisgo/golibruntime/runtime/runtime.go", nil, 0) if err != nil { fmt.Println(err) return err } conf.CreateFromFiles("", f) fmt.Printf("DEBUG %s %s\n", f.Name.Name, "") //, f.Name.Obj.Name) */ // end TARDIS Go TEST // Use the initial packages from the command line. args, err := conf.FromArgs(args, *testFlag) if err != nil { return err } // The interpreter needs the runtime package. if *runFlag { conf.Import("runtime") conf.Import("github.com/tardisgo/tardisgo/golibruntime/runtime") // This required for TARDIS go to run runtime } // TARDIS GO additional line to add the language specific go runtime code conf.Import(pogo.LanguageList[pogo.TargetLang].Goruntime) // TODO add code to set pogo.TargetLang when more than one of them // Load, parse and type-check the whole program. iprog, err := conf.Load() if err != nil { return err } // Create and build SSA-form program representation. prog := ssa.Create(iprog, mode) prog.BuildAll() // Run the interpreter. if *runFlag { var main *ssa.Package pkgs := prog.AllPackages() if *testFlag { // If -test, run all packages' tests. if len(pkgs) > 0 { main = prog.CreateTestMainPackage(pkgs...) } if main == nil { return fmt.Errorf("no tests") } } else { // Otherwise, run main.main. for _, pkg := range pkgs { if pkg.Object.Name() == "main" { main = pkg if main.Func("main") == nil { return fmt.Errorf("no func main() in main package") } break } } if main == nil { return fmt.Errorf("no main package") } } // NOTE TARDIS Go removal of this test required if we alter the GOARCH to stop architecture-specific code if runtime.GOARCH != build.Default.GOARCH { return fmt.Errorf("cross-interpretation is not yet supported (target has GOARCH %s, interpreter has %s)", build.Default.GOARCH, runtime.GOARCH) } interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Object.Path(), args) } // TARDIS Go additions: copy run interpreter code above, but call pogo class if true { var main *ssa.Package pkgs := prog.AllPackages() if *testFlag { // If -test, run all packages' tests. if len(pkgs) > 0 { main = prog.CreateTestMainPackage(pkgs...) } if main == nil { return fmt.Errorf("no tests") } } else { // Otherwise, run main.main. for _, pkg := range pkgs { if pkg.Object.Name() == "main" { main = pkg if main.Func("main") == nil { return fmt.Errorf("no func main() in main package") } break } } if main == nil { return fmt.Errorf("no main package") } } /* if runtime.GOARCH != build.Default.GOARCH { return fmt.Errorf("cross-interpretation is not yet supported (target has GOARCH %s, interpreter has %s)", build.Default.GOARCH, runtime.GOARCH) } interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Object.Path(), args) */ err = pogo.EntryPoint(main) // TARDIS Go entry point, returns an error if err != nil { return err } if *allFlag { targets := [][][]string{ [][]string{ []string{"haxe", "-main", "tardis.Go", "-dce", "full", "-cpp", "cpp"}, []string{"echo", `"CPP:"`}, []string{"./cpp/Go"}, }, [][]string{ []string{"haxe", "-main", "tardis.Go", "-dce", "full", "-java", "java"}, []string{"echo", `"Java:"`}, []string{"java", "-jar", "java/Go.jar"}, }, [][]string{ []string{"haxe", "-main", "tardis.Go", "-dce", "full", "-cs", "cs"}, []string{"echo", `"CS:"`}, []string{"mono", "./cs/bin/Go.exe"}, }, [][]string{ []string{"haxe", "-main", "tardis.Go", "-dce", "full", "-neko", "tardisgo.n"}, []string{"echo", `"Neko:"`}, []string{"neko", "tardisgo.n"}, }, [][]string{ []string{"haxe", "-main", "tardis.Go", "-dce", "full", "-js", "tardisgo.js"}, []string{"echo", `"Node/JS:"`}, []string{"node", "tardisgo.js"}, }, [][]string{ []string{"haxe", "-main", "tardis.Go", "-dce", "full", "-swf", "tardisgo.swf"}, []string{"echo", `"Opening swf file (Chrome as a file association for swf works to test on OSX):"` + "\n"}, []string{"open", "tardisgo.swf"}, }, [][]string{ []string{"haxe", "-main", "tardis.Go", "-dce", "full", "-php", "php", "--php-prefix", "tgo"}, []string{"echo", `"PHP:"`}, []string{"php", "php/index.php"}, }, [][]string{ []string{"echo", ``}, // Output from this line is ignored []string{"echo", `"Neko (haxe --interp):"`}, []string{"haxe", "-main", "tardis.Go", "--interp"}, }, } results := make(chan string, len(targets)) for id, cmd := range targets { go func(i int, cl [][]string) { res := "" for j, c := range cl { exe := c[0] if exe == "echo" { res += c[1] } else { _, err := exec.LookPath(exe) if err != nil { switch exe { case "node": exe = "nodejs" // for Ubuntu default: res += "TARDISgo error - executable not found: " + exe + "\n" exe = "" // nothing to execute } } if exe != "" { out, err := exec.Command(exe, c[1:]...).CombinedOutput() if err != nil { out = append(out, []byte(err.Error())...) } if j > 0 { // ignore the output from the compile phase res += string(out) } } } } results <- res }(id, cmd) } for t := 0; t < len(targets); t++ { fmt.Println(<-results) } } } return nil }
func doMain() error { flag.Parse() args := flag.Args() conf := loader.Config{ Build: &build.Default, SourceImports: true, } // TODO(adonovan): make go/types choose its default Sizes from // build.Default or a specified *build.Context. var wordSize int64 = 8 switch conf.Build.GOARCH { case "386", "arm": wordSize = 4 } conf.TypeChecker.Sizes = &types.StdSizes{ MaxAlign: 8, WordSize: wordSize, } var mode ssa.BuilderMode for _, c := range *buildFlag { switch c { case 'D': mode |= ssa.GlobalDebug case 'P': mode |= ssa.LogPackages | ssa.BuildSerially case 'F': mode |= ssa.LogFunctions | ssa.BuildSerially case 'S': mode |= ssa.LogSource | ssa.BuildSerially case 'C': mode |= ssa.SanityCheckFunctions case 'N': mode |= ssa.NaiveForm case 'G': conf.SourceImports = false case 'L': mode |= ssa.BuildSerially default: return fmt.Errorf("unknown -build option: '%c'", c) } } var interpMode interp.Mode for _, c := range *interpFlag { switch c { case 'T': interpMode |= interp.EnableTracing case 'R': interpMode |= interp.DisableRecover default: fmt.Fprintf(os.Stderr, "ssadump: unknown -interp option: '%c'.", c) os.Exit(1) } } if len(args) == 0 { fmt.Fprint(os.Stderr, usage) os.Exit(1) } // Profiling support. if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } // Use the initial packages from the command line. args, err := conf.FromArgs(args, *testFlag) if err != nil { return err } // The interpreter needs the runtime package. if *runFlag { conf.Import("runtime") } // Load, parse and type-check the whole program. iprog, err := conf.Load() if err != nil { return err } // Create and build SSA-form program representation. prog := ssa.Create(iprog, mode) prog.BuildAll() // Run the interpreter. if *runFlag { var main *ssa.Package pkgs := prog.AllPackages() if *testFlag { // If -test, run all packages' tests. if len(pkgs) > 0 { main = prog.CreateTestMainPackage(pkgs...) } if main == nil { return fmt.Errorf("no tests") } } else { // Otherwise, run main.main. for _, pkg := range pkgs { if pkg.Object.Name() == "main" { main = pkg if main.Func("main") == nil { return fmt.Errorf("no func main() in main package") } break } } if main == nil { return fmt.Errorf("no main package") } } if runtime.GOARCH != build.Default.GOARCH { return fmt.Errorf("cross-interpretation is not yet supported (target has GOARCH %s, interpreter has %s)", build.Default.GOARCH, runtime.GOARCH) } interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Object.Path(), args) } return nil }
func TestTransitivelyErrorFreeFlag(t *testing.T) { conf := loader.Config{ AllowErrors: true, SourceImports: true, } // Create an minimal custom build.Context // that fakes the following packages: // // a --> b --> c! c has an error // \ d and e are transitively error-free. // e --> d // // Each package [a-e] consists of one file, x.go. pkgs := map[string]string{ "a": `package a; import (_ "b"; _ "e")`, "b": `package b; import _ "c"`, "c": `package c; func f() { _ = int(false) }`, // type error within function body "d": `package d;`, "e": `package e; import _ "d"`, } ctxt := build.Default // copy ctxt.GOROOT = "/go" ctxt.GOPATH = "" ctxt.IsDir = func(path string) bool { return true } ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) { return justXgo[:], nil } ctxt.OpenFile = func(path string) (io.ReadCloser, error) { path = path[len("/go/src/pkg/"):] return nopCloser{bytes.NewBufferString(pkgs[path[0:1]])}, nil } conf.Build = &ctxt conf.Import("a") prog, err := conf.Load() if err != nil { t.Errorf("Load failed: %s", err) } if prog == nil { t.Fatalf("Load returned nil *Program") } for pkg, info := range prog.AllPackages { var wantErr, wantTEF bool switch pkg.Path() { case "a", "b": case "c": wantErr = true case "d", "e": wantTEF = true default: t.Errorf("unexpected package: %q", pkg.Path()) continue } if (info.Errors != nil) != wantErr { if wantErr { t.Errorf("Package %q.Error = nil, want error", pkg.Path()) } else { t.Errorf("Package %q has unexpected Errors: %v", pkg.Path(), info.Errors) } } if info.TransitivelyErrorFree != wantTEF { t.Errorf("Package %q.TransitivelyErrorFree=%t, want %t", pkg.Path(), info.TransitivelyErrorFree, wantTEF) } } }
func TestCgoOption(t *testing.T) { switch runtime.GOOS { // On these systems, the net and os/user packages don't use cgo. case "plan9", "solaris", "windows": return } // In nocgo builds (e.g. linux-amd64-nocgo), // there is no "runtime/cgo" package, // so cgo-generated Go files will have a failing import. if !build.Default.CgoEnabled { return } // Test that we can load cgo-using packages with // CGO_ENABLED=[01], which causes go/build to select pure // Go/native implementations, respectively, based on build // tags. // // Each entry specifies a package-level object and the generic // file expected to define it when cgo is disabled. // When cgo is enabled, the exact file is not specified (since // it varies by platform), but must differ from the generic one. // // The test also loads the actual file to verify that the // object is indeed defined at that location. for _, test := range []struct { pkg, name, genericFile string }{ {"net", "cgoLookupHost", "cgo_stub.go"}, {"os/user", "lookupId", "lookup_stubs.go"}, } { ctxt := build.Default for _, ctxt.CgoEnabled = range []bool{false, true} { conf := loader.Config{Build: &ctxt} conf.Import(test.pkg) prog, err := conf.Load() if err != nil { t.Errorf("Load failed: %v", err) continue } info := prog.Imported[test.pkg] if info == nil { t.Errorf("package %s not found", test.pkg) continue } obj := info.Pkg.Scope().Lookup(test.name) if obj == nil { t.Errorf("no object %s.%s", test.pkg, test.name) continue } posn := prog.Fset.Position(obj.Pos()) t.Logf("%s: %s (CgoEnabled=%t)", posn, obj, ctxt.CgoEnabled) gotFile := filepath.Base(posn.Filename) filesMatch := gotFile == test.genericFile if ctxt.CgoEnabled && filesMatch { t.Errorf("CGO_ENABLED=1: %s found in %s, want native file", obj, gotFile) } else if !ctxt.CgoEnabled && !filesMatch { t.Errorf("CGO_ENABLED=0: %s found in %s, want %s", obj, gotFile, test.genericFile) } // Load the file and check the object is declared at the right place. b, err := ioutil.ReadFile(posn.Filename) if err != nil { t.Errorf("can't read %s: %s", posn.Filename, err) continue } line := string(bytes.Split(b, []byte("\n"))[posn.Line-1]) ident := line[posn.Column-1:] if !strings.HasPrefix(ident, test.name) { t.Errorf("%s: %s not declared here (looking at %q)", posn, obj, ident) } } } }
func run(t *testing.T, dir, input string, success successPredicate) bool { fmt.Printf("Input: %s\n", input) start := time.Now() var inputs []string for _, i := range strings.Split(input, " ") { if strings.HasSuffix(i, ".go") { i = dir + i } inputs = append(inputs, i) } conf := loader.Config{SourceImports: true} if _, err := conf.FromArgs(inputs, true); err != nil { t.Errorf("FromArgs(%s) failed: %s", inputs, err) return false } conf.Import("runtime") // Print a helpful hint if we don't make it to the end. var hint string defer func() { if hint != "" { fmt.Println("FAIL") fmt.Println(hint) } else { fmt.Println("PASS") } interp.CapturedOutput = nil }() hint = fmt.Sprintf("To dump SSA representation, run:\n%% go build code.google.com/p/go.tools/cmd/ssadump && ./ssadump -build=CFP %s\n", input) iprog, err := conf.Load() if err != nil { t.Errorf("conf.Load(%s) failed: %s", inputs, err) return false } prog := ssa.Create(iprog, ssa.SanityCheckFunctions) prog.BuildAll() var mainPkg *ssa.Package var initialPkgs []*ssa.Package for _, info := range iprog.InitialPackages() { if info.Pkg.Path() == "runtime" { continue // not an initial package } p := prog.Package(info.Pkg) initialPkgs = append(initialPkgs, p) if mainPkg == nil && p.Func("main") != nil { mainPkg = p } } if mainPkg == nil { testmainPkg := prog.CreateTestMainPackage(initialPkgs...) if testmainPkg == nil { t.Errorf("CreateTestMainPackage(%s) returned nil", mainPkg) return false } if testmainPkg.Func("main") == nil { t.Errorf("synthetic testmain package has no main") return false } mainPkg = testmainPkg } var out bytes.Buffer interp.CapturedOutput = &out hint = fmt.Sprintf("To trace execution, run:\n%% go build code.google.com/p/go.tools/cmd/ssadump && ./ssadump -build=C -run --interp=T %s\n", input) exitCode := interp.Interpret(mainPkg, 0, &types.StdSizes{8, 8}, inputs[0], []string{}) // The definition of success varies with each file. if err := success(exitCode, out.String()); err != nil { t.Errorf("interp.Interpret(%s) failed: %s", inputs, err) return false } hint = "" // call off the hounds if false { fmt.Println(input, time.Since(start)) // test profiling } return true }