// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package importer // This file defines various utility functions exposed by the package // and used by it. import ( "go/ast" "go/build" "go/parser" "go/token" "os" "path/filepath" "strconv" "sync" ) var cwd string func init() { var err error cwd, err = os.Getwd() if err != nil { panic("getcwd failed: " + err.Error()) } } // parsePackageFiles enumerates the files belonging to package path, // then loads, parses and returns them. // // 'which' is a list of flags indicating which files to include: // 'g': include non-test *.go source files (GoFiles) // 't': include in-package *_test.go source files (TestGoFiles) // 'x': include external *_test.go source files. (XTestGoFiles) // func parsePackageFiles(ctxt *build.Context, fset *token.FileSet, path string, which string) ([]*ast.File, error) { // Set the "!cgo" go/build tag, preferring (dummy) Go to // native C implementations of net.cgoLookupHost et al. ctxt2 := *ctxt ctxt2.CgoEnabled = false // TODO(adonovan): fix: Do we need cwd? Shouldn't // ImportDir(path) / $GOROOT suffice? bp, err := ctxt2.Import(path, cwd, 0) if _, ok := err.(*build.NoGoError); ok { return nil, nil // empty directory } if err != nil { return nil, err // import failed } var filenames []string for _, c := range which { var s []string switch c { case 'g': s = bp.GoFiles case 't': s = bp.TestGoFiles case 'x': s = bp.XTestGoFiles default: panic(c) } filenames = append(filenames, s...) } return ParseFiles(fset, bp.Dir, filenames...) } // ParseFiles parses the Go source files files within directory dir // and returns their ASTs, or the first parse error if any. // func ParseFiles(fset *token.FileSet, dir string, files ...string) ([]*ast.File, error) { var wg sync.WaitGroup n := len(files) parsed := make([]*ast.File, n, n) errors := make([]error, n, n) for i, file := range files { if !filepath.IsAbs(file) { file = filepath.Join(dir, file) } wg.Add(1) go func(i int, file string) { parsed[i], errors[i] = parser.ParseFile(fset, file, nil, parser.DeclarationErrors) wg.Done() }(i, file) } wg.Wait() for _, err := range errors { if err != nil { return nil, err } } return parsed, nil } // ---------- Internal helpers ---------- // unparen returns e with any enclosing parentheses stripped. func unparen(e ast.Expr) ast.Expr { for { p, ok := e.(*ast.ParenExpr) if !ok { break } e = p.X } return e } func unreachable() { panic("unreachable") } // importsOf returns the set of paths imported by the specified files. func importsOf(p string, files []*ast.File) map[string]bool { imports := make(map[string]bool) outer: for _, file := range files { for _, decl := range file.Decls { if decl, ok := decl.(*ast.GenDecl); ok { if decl.Tok != token.IMPORT { break outer // stop at the first non-import } for _, spec := range decl.Specs { spec := spec.(*ast.ImportSpec) if path, _ := strconv.Unquote(spec.Path.Value); path != "C" { imports[path] = true } } } else { break outer // stop at the first non-import } } } return imports }