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 mu sync.Mutex var allErrors []error conf.TypeChecker.Error = func(err error) { mu.Lock() allErrors = append(allErrors, err) mu.Unlock() } 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") } // 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) } }
func generateSourceFiles(conf *loader.Config, subcommand string) (tmpDirPath string) { // Make a temp directory. tmpDir := makeTmpDir() if *work { // Print the name of the directory and don't clean it up on exit. fmt.Println(tmpDir) } else { // Clean up the directory on exit. atexit.Run(func() { removeDir(tmpDir) }) } // Load the whole program from source files. This almost certainly causes more work than we need to do, // but it's an easy fix for a few problems I've encountered. Deleting this may be a good target for // future optimization work. conf.SourceImports = true // Mark the extra packages we want to instrument. var pkgs []string *instrument = strings.Trim(*instrument, ", ") if *instrument != "" { pkgs = strings.Split(*instrument, ",") } all := false for _, pkg := range pkgs { // check for the special reserved package names: "main", "all", and "std" // see 'go help packages' switch pkg { case "main": switch subcommand { case "run": // The main package is always instrumented anyway. Carry on. case "test": logFatal(`godebug test: can't pass reserved name "main" in the -instrument flag.`) } case "all": if !all { all = true fmt.Println(`godebug run: heads up: "all" means "all except std". godebug can't step into the standard library yet.` + "\n") } case "std": logFatalf("godebug %s: reserved name \"std\" cannot be passed in the -instrument flag."+ "\ngodebug cannot currently instrument packages in the standard library."+ "\nDo you wish it could? Chime in at https://github.com/mailgun/godebug/issues/12", subcommand) default: for _, path := range gotool.ImportPaths([]string{pkg}) { // wildcard "..." expansion conf.Import(path) } } } // Load the program. prog, err := conf.Load() exitIfErr(err) // If we're in "all" mode, mark all but the standard library packages and godebug itself for instrumenting. stdLib := getStdLibPkgs() if all { markAlmostAllPackages(prog, stdLib) } // Warn the user if they have breakpoints set in files that we are not instrumenting. checkForUnusedBreakpoints(subcommand, prog, stdLib) // Generate debugging-enabled source files. wd := getwd() gen.Generate(prog, ioutil.ReadFile, func(importPath, filename string) io.WriteCloser { if importPath == "main" { filename = filepath.Join(tmpDir, filepath.Base(filename)) } else { importPath, _ = findUnderGopath(wd, importPath) exitIfErr(os.MkdirAll(filepath.Join(tmpDir, "src", importPath), 0770)) filename = filepath.Join(tmpDir, "src", importPath, filepath.Base(filename)) } return createFileHook(filename, tmpDir) }) return tmpDir }
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) } } } }