src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

util.go (2998B)


      1 // Copyright 2013 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package loader
      6 
      7 import (
      8 	"go/ast"
      9 	"go/build"
     10 	"go/parser"
     11 	"go/token"
     12 	"io"
     13 	"os"
     14 	"strconv"
     15 	"sync"
     16 
     17 	"golang.org/x/tools/go/buildutil"
     18 )
     19 
     20 // We use a counting semaphore to limit
     21 // the number of parallel I/O calls per process.
     22 var ioLimit = make(chan bool, 10)
     23 
     24 // parseFiles parses the Go source files within directory dir and
     25 // returns the ASTs of the ones that could be at least partially parsed,
     26 // along with a list of I/O and parse errors encountered.
     27 //
     28 // I/O is done via ctxt, which may specify a virtual file system.
     29 // displayPath is used to transform the filenames attached to the ASTs.
     30 func parseFiles(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, files []string, mode parser.Mode) ([]*ast.File, []error) {
     31 	if displayPath == nil {
     32 		displayPath = func(path string) string { return path }
     33 	}
     34 	var wg sync.WaitGroup
     35 	n := len(files)
     36 	parsed := make([]*ast.File, n)
     37 	errors := make([]error, n)
     38 	for i, file := range files {
     39 		if !buildutil.IsAbsPath(ctxt, file) {
     40 			file = buildutil.JoinPath(ctxt, dir, file)
     41 		}
     42 		wg.Add(1)
     43 		go func(i int, file string) {
     44 			ioLimit <- true // wait
     45 			defer func() {
     46 				wg.Done()
     47 				<-ioLimit // signal
     48 			}()
     49 			var rd io.ReadCloser
     50 			var err error
     51 			if ctxt.OpenFile != nil {
     52 				rd, err = ctxt.OpenFile(file)
     53 			} else {
     54 				rd, err = os.Open(file)
     55 			}
     56 			if err != nil {
     57 				errors[i] = err // open failed
     58 				return
     59 			}
     60 
     61 			// ParseFile may return both an AST and an error.
     62 			parsed[i], errors[i] = parser.ParseFile(fset, displayPath(file), rd, mode)
     63 			rd.Close()
     64 		}(i, file)
     65 	}
     66 	wg.Wait()
     67 
     68 	// Eliminate nils, preserving order.
     69 	var o int
     70 	for _, f := range parsed {
     71 		if f != nil {
     72 			parsed[o] = f
     73 			o++
     74 		}
     75 	}
     76 	parsed = parsed[:o]
     77 
     78 	o = 0
     79 	for _, err := range errors {
     80 		if err != nil {
     81 			errors[o] = err
     82 			o++
     83 		}
     84 	}
     85 	errors = errors[:o]
     86 
     87 	return parsed, errors
     88 }
     89 
     90 // scanImports returns the set of all import paths from all
     91 // import specs in the specified files.
     92 func scanImports(files []*ast.File) map[string]bool {
     93 	imports := make(map[string]bool)
     94 	for _, f := range files {
     95 		for _, decl := range f.Decls {
     96 			if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
     97 				for _, spec := range decl.Specs {
     98 					spec := spec.(*ast.ImportSpec)
     99 
    100 					// NB: do not assume the program is well-formed!
    101 					path, err := strconv.Unquote(spec.Path.Value)
    102 					if err != nil {
    103 						continue // quietly ignore the error
    104 					}
    105 					if path == "C" {
    106 						continue // skip pseudopackage
    107 					}
    108 					imports[path] = true
    109 				}
    110 			}
    111 		}
    112 	}
    113 	return imports
    114 }
    115 
    116 // ---------- Internal helpers ----------
    117 
    118 // TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos)
    119 func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
    120 	p := int(pos)
    121 	base := f.Base()
    122 	return base <= p && p < base+f.Size()
    123 }