blob: a705717198ca95745bdb05a98c728e53384d4af2 [file] [log] [blame]
// 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 loader
import (
"go/ast"
"go/build"
"go/parser"
"go/token"
"io"
"os"
"path/filepath"
"sync"
)
// parseFiles parses the Go source files files within directory dir
// and returns their ASTs, or the first parse error if any.
//
// I/O is done via ctxt, which may specify a virtual file system.
// displayPath is used to transform the filenames attached to the ASTs.
//
func parseFiles(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, files []string, mode parser.Mode) ([]*ast.File, error) {
if displayPath == nil {
displayPath = func(path string) string { return path }
}
isAbs := filepath.IsAbs
if ctxt.IsAbsPath != nil {
isAbs = ctxt.IsAbsPath
}
joinPath := filepath.Join
if ctxt.JoinPath != nil {
joinPath = ctxt.JoinPath
}
var wg sync.WaitGroup
n := len(files)
parsed := make([]*ast.File, n)
errors := make([]error, n)
for i, file := range files {
if !isAbs(file) {
file = joinPath(dir, file)
}
wg.Add(1)
go func(i int, file string) {
defer wg.Done()
var rd io.ReadCloser
var err error
if ctxt.OpenFile != nil {
rd, err = ctxt.OpenFile(file)
} else {
rd, err = os.Open(file)
}
defer rd.Close()
if err != nil {
errors[i] = err
return
}
parsed[i], errors[i] = parser.ParseFile(fset, displayPath(file), rd, mode)
}(i, file)
}
wg.Wait()
for _, err := range errors {
if err != nil {
return nil, err
}
}
return parsed, nil
}
// ---------- Internal helpers ----------
// TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos)
func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
p := int(pos)
base := f.Base()
return base <= p && p < base+f.Size()
}