+++ /dev/null
-// Copyright 2011 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.
-
-// gotype.go is a copy of the original source maintained
-// in $GOROOT/src/go/types/gotype.go, but with the call
-// to types.SizesFor factored out so we can provide a local
-// implementation when compiling against Go 1.8 and earlier.
-//
-// This code is here for the sole purpose of satisfying historic
-// references to this location, and for making gotype accessible
-// via 'go get'.
-//
-// Do NOT make changes to this version as they will not be maintained
-// (and possibly overwritten). Any changes should be made to the original
-// and then ported to here.
-
-/*
-The gotype command, like the front-end of a Go compiler, parses and
-type-checks a single Go package. Errors are reported if the analysis
-fails; otherwise gotype is quiet (unless -v is set).
-
-Without a list of paths, gotype reads from standard input, which
-must provide a single Go source file defining a complete package.
-
-With a single directory argument, gotype checks the Go files in
-that directory, comprising a single package. Use -t to include the
-(in-package) _test.go files. Use -x to type check only external
-test files.
-
-Otherwise, each path must be the filename of a Go file belonging
-to the same package.
-
-Imports are processed by importing directly from the source of
-imported packages (default), or by importing from compiled and
-installed packages (by setting -c to the respective compiler).
-
-The -c flag must be set to a compiler ("gc", "gccgo") when type-
-checking packages containing imports with relative import paths
-(import "./mypkg") because the source importer cannot know which
-files to include for such packages.
-
-Usage:
- gotype [flags] [path...]
-
-The flags are:
- -t
- include local test files in a directory (ignored if -x is provided)
- -x
- consider only external test files in a directory
- -e
- report all errors (not just the first 10)
- -v
- verbose mode
- -c
- compiler used for installed packages (gc, gccgo, or source); default: source
-
-Flags controlling additional output:
- -ast
- print AST (forces -seq)
- -trace
- print parse trace (forces -seq)
- -comments
- parse comments (ignored unless -ast or -trace is provided)
-
-Examples:
-
-To check the files a.go, b.go, and c.go:
-
- gotype a.go b.go c.go
-
-To check an entire package including (in-package) tests in the directory dir and print the processed files:
-
- gotype -t -v dir
-
-To check the external test package (if any) in the current directory, based on installed packages compiled with
-cmd/compile:
-
- gotype -c=gc -x .
-
-To verify the output of a pipe:
-
- echo "package foo" | gotype
-
-*/
-package main
-
-import (
- "flag"
- "fmt"
- "go/ast"
- "go/build"
- "go/importer"
- "go/parser"
- "go/scanner"
- "go/token"
- "go/types"
- "io/ioutil"
- "os"
- "path/filepath"
- "sync"
- "time"
-)
-
-var (
- // main operation modes
- testFiles = flag.Bool("t", false, "include in-package test files in a directory")
- xtestFiles = flag.Bool("x", false, "consider only external test files in a directory")
- allErrors = flag.Bool("e", false, "report all errors, not just the first 10")
- verbose = flag.Bool("v", false, "verbose mode")
- compiler = flag.String("c", defaultCompiler, "compiler used for installed packages (gc, gccgo, or source)")
-
- // additional output control
- printAST = flag.Bool("ast", false, "print AST (forces -seq)")
- printTrace = flag.Bool("trace", false, "print parse trace (forces -seq)")
- parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)")
-)
-
-var (
- fset = token.NewFileSet()
- errorCount = 0
- sequential = false
- parserMode parser.Mode
-)
-
-func initParserMode() {
- if *allErrors {
- parserMode |= parser.AllErrors
- }
- if *printAST {
- sequential = true
- }
- if *printTrace {
- parserMode |= parser.Trace
- sequential = true
- }
- if *parseComments && (*printAST || *printTrace) {
- parserMode |= parser.ParseComments
- }
-}
-
-const usageString = `usage: gotype [flags] [path ...]
-
-The gotype command, like the front-end of a Go compiler, parses and
-type-checks a single Go package. Errors are reported if the analysis
-fails; otherwise gotype is quiet (unless -v is set).
-
-Without a list of paths, gotype reads from standard input, which
-must provide a single Go source file defining a complete package.
-
-With a single directory argument, gotype checks the Go files in
-that directory, comprising a single package. Use -t to include the
-(in-package) _test.go files. Use -x to type check only external
-test files.
-
-Otherwise, each path must be the filename of a Go file belonging
-to the same package.
-
-Imports are processed by importing directly from the source of
-imported packages (default), or by importing from compiled and
-installed packages (by setting -c to the respective compiler).
-
-The -c flag must be set to a compiler ("gc", "gccgo") when type-
-checking packages containing imports with relative import paths
-(import "./mypkg") because the source importer cannot know which
-files to include for such packages.
-`
-
-func usage() {
- fmt.Fprintln(os.Stderr, usageString)
- flag.PrintDefaults()
- os.Exit(2)
-}
-
-func report(err error) {
- scanner.PrintError(os.Stderr, err)
- if list, ok := err.(scanner.ErrorList); ok {
- errorCount += len(list)
- return
- }
- errorCount++
-}
-
-// parse may be called concurrently
-func parse(filename string, src interface{}) (*ast.File, error) {
- if *verbose {
- fmt.Println(filename)
- }
- file, err := parser.ParseFile(fset, filename, src, parserMode) // ok to access fset concurrently
- if *printAST {
- ast.Print(fset, file)
- }
- return file, err
-}
-
-func parseStdin() (*ast.File, error) {
- src, err := ioutil.ReadAll(os.Stdin)
- if err != nil {
- return nil, err
- }
- return parse("<standard input>", src)
-}
-
-func parseFiles(dir string, filenames []string) ([]*ast.File, error) {
- files := make([]*ast.File, len(filenames))
- errors := make([]error, len(filenames))
-
- var wg sync.WaitGroup
- for i, filename := range filenames {
- wg.Add(1)
- go func(i int, filepath string) {
- defer wg.Done()
- files[i], errors[i] = parse(filepath, nil)
- }(i, filepath.Join(dir, filename))
- if sequential {
- wg.Wait()
- }
- }
- wg.Wait()
-
- // if there are errors, return the first one for deterministic results
- for _, err := range errors {
- if err != nil {
- return nil, err
- }
- }
-
- return files, nil
-}
-
-func parseDir(dir string) ([]*ast.File, error) {
- ctxt := build.Default
- pkginfo, err := ctxt.ImportDir(dir, 0)
- if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
- return nil, err
- }
-
- if *xtestFiles {
- return parseFiles(dir, pkginfo.XTestGoFiles)
- }
-
- filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
- if *testFiles {
- filenames = append(filenames, pkginfo.TestGoFiles...)
- }
- return parseFiles(dir, filenames)
-}
-
-func getPkgFiles(args []string) ([]*ast.File, error) {
- if len(args) == 0 {
- // stdin
- file, err := parseStdin()
- if err != nil {
- return nil, err
- }
- return []*ast.File{file}, nil
- }
-
- if len(args) == 1 {
- // possibly a directory
- path := args[0]
- info, err := os.Stat(path)
- if err != nil {
- return nil, err
- }
- if info.IsDir() {
- return parseDir(path)
- }
- }
-
- // list of files
- return parseFiles("", args)
-}
-
-func checkPkgFiles(files []*ast.File) {
- type bailout struct{}
-
- // if checkPkgFiles is called multiple times, set up conf only once
- conf := types.Config{
- FakeImportC: true,
- Error: func(err error) {
- if !*allErrors && errorCount >= 10 {
- panic(bailout{})
- }
- report(err)
- },
- Importer: importer.ForCompiler(fset, *compiler, nil),
- Sizes: SizesFor(build.Default.Compiler, build.Default.GOARCH),
- }
-
- defer func() {
- switch p := recover().(type) {
- case nil, bailout:
- // normal return or early exit
- default:
- // re-panic
- panic(p)
- }
- }()
-
- const path = "pkg" // any non-empty string will do for now
- conf.Check(path, fset, files, nil)
-}
-
-func printStats(d time.Duration) {
- fileCount := 0
- lineCount := 0
- fset.Iterate(func(f *token.File) bool {
- fileCount++
- lineCount += f.LineCount()
- return true
- })
-
- fmt.Printf(
- "%s (%d files, %d lines, %d lines/s)\n",
- d, fileCount, lineCount, int64(float64(lineCount)/d.Seconds()),
- )
-}
-
-func main() {
- flag.Usage = usage
- flag.Parse()
- initParserMode()
-
- start := time.Now()
-
- files, err := getPkgFiles(flag.Args())
- if err != nil {
- report(err)
- os.Exit(2)
- }
-
- checkPkgFiles(files)
- if errorCount > 0 {
- os.Exit(2)
- }
-
- if *verbose {
- printStats(time.Since(start))
- }
-}