// Test to ensure that gcexportdata can read files produced by App // Engine Go runtime v1.6. func TestAppEngine16(t *testing.T) { // Open and read the file. f, err := os.Open("testdata/errors-ae16.a") if err != nil { t.Fatal(err) } defer f.Close() r, err := gcexportdata.NewReader(f) if err != nil { log.Fatalf("reading export data: %v", err) } // Decode the export data. fset := token.NewFileSet() imports := make(map[string]*types.Package) pkg, err := gcexportdata.Read(r, fset, imports, "errors") if err != nil { log.Fatal(err) } // Print package information. got := pkg.Scope().Lookup("New").Type().String() want := "func(text string) error" if got != want { t.Errorf("New.Type = %s, want %s", got, want) } }
// ExampleRead uses gcexportdata.Read to load type information for the // "fmt" package from the fmt.a file produced by the gc compiler. func ExampleRead() { // Find the export data file. filename, path := gcexportdata.Find("fmt", "") if filename == "" { log.Fatalf("can't find export data for fmt") } fmt.Printf("Package path: %s\n", path) fmt.Printf("Export data: %s\n", filepath.Base(filename)) // Open and read the file. f, err := os.Open(filename) if err != nil { log.Fatal(err) } defer f.Close() r, err := gcexportdata.NewReader(f) if err != nil { log.Fatalf("reading export data %s: %v", filename, err) } // Decode the export data. fset := token.NewFileSet() imports := make(map[string]*types.Package) pkg, err := gcexportdata.Read(r, fset, imports, path) if err != nil { log.Fatal(err) } // Print package information. fmt.Printf("Package members: %s...\n", pkg.Scope().Names()[:5]) println := pkg.Scope().Lookup("Println") posn := fset.Position(println.Pos()) posn.Line = 123 // make example deterministic fmt.Printf("Println type: %s\n", println.Type()) fmt.Printf("Println location: %s\n", slashify(posn)) // Output: // // Package path: fmt // Export data: fmt.a // Package members: [Errorf Formatter Fprint Fprintf Fprintln]... // Println type: func(a ...interface{}) (n int, err error) // Println location: $GOROOT/src/fmt/print.go:123:1 }
// Resolve resolves the package information for unit and its dependencies. On // success the package corresponding to unit is located via ImportPath in the // Packages map of the returned value. // // If info != nil, it is used to populate the Info field of the return value // and will contain the output of the type checker in each user-provided map // field. func Resolve(unit *apb.CompilationUnit, f Fetcher, info *types.Info) (*PackageInfo, error) { isSource := make(map[string]bool) for _, src := range unit.SourceFile { isSource[src] = true } deps := make(map[string]*types.Package) // import path → package imap := make(map[string]*spb.VName) // import path → vname srcs := make(map[string]string) // file path → text fset := token.NewFileSet() // location info for the parser var files []*ast.File // parsed sources // Classify the required inputs as either sources, which are to be parsed, // or dependencies, which are to be "imported" via the type-checker's // import mechanism. If successful, this populates fset and files with the // lexical and syntactic content of the package's own sources. for _, ri := range unit.RequiredInput { if ri.Info == nil { return nil, errors.New("required input file info missing") } // Fetch the contents of each required input. fpath := ri.Info.Path data, err := f.Fetch(fpath, ri.Info.Digest) if err != nil { return nil, fmt.Errorf("fetching %q (%s): %v", fpath, ri.Info.Digest, err) } // Source inputs need to be parsed, so we can give their ASTs to the // type checker later on. if isSource[fpath] { srcs[fpath] = string(data) parsed, err := parser.ParseFile(fset, fpath, data, parser.AllErrors) if err != nil { return nil, fmt.Errorf("parsing %q: %v", fpath, err) } files = append(files, parsed) continue } // For archives, recover the import path from the VName and read the // archive header for type information. If the VName is not set, the // import is considered bogus. if ri.VName == nil { return nil, fmt.Errorf("missing vname for %q", fpath) } r, err := gcexportdata.NewReader(bytes.NewReader(data)) if err != nil { return nil, fmt.Errorf("scanning export data in %q: %v", fpath, err) } ipath := path.Join(ri.VName.Corpus, ri.VName.Path) if govname.IsStandardLibrary(ri.VName) { ipath = ri.VName.Path } imap[ipath] = ri.VName // Populate deps with package ipath and its prerequisites. if _, err := gcexportdata.Read(r, fset, deps, ipath); err != nil { return nil, fmt.Errorf("importing %q: %v", ipath, err) } } // Fill in the mapping from packages to vnames. vmap := make(map[*types.Package]*spb.VName) for ip, vname := range imap { if pkg := deps[ip]; pkg != nil { vmap[pkg] = vname } } pi := &PackageInfo{ Name: files[0].Name.Name, ImportPath: path.Join(unit.VName.Corpus, unit.VName.Path), VNames: vmap, FileSet: fset, Files: files, SourceText: srcs, Dependencies: deps, Info: info, sigs: make(map[types.Object]string), } // Run the type-checker and collect any errors it generates. Errors in the // type checker are not returned directly; the caller can read them from // the Errors field. c := &types.Config{ FakeImportC: true, // so we can handle cgo DisableUnusedImportCheck: true, // this is not fatal to type-checking Importer: pi, Error: func(err error) { pi.Errors = append(pi.Errors, err) }, } if pkg, _ := c.Check(pi.Name, pi.FileSet, pi.Files, pi.Info); pkg != nil { pi.Package = pkg vmap[pkg] = unit.VName } return pi, nil }