Source file src/cmd/vendor/golang.org/x/tools/go/analysis/unitchecker/unitchecker.go

     1  // Copyright 2018 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  // The unitchecker package defines the main function for an analysis
     6  // driver that analyzes a single compilation unit during a build.
     7  // It is invoked by a build system such as "go vet":
     8  //
     9  //   $ go vet -vettool=$(which vet)
    10  //
    11  // It supports the following command-line protocol:
    12  //
    13  //      -V=full         describe executable               (to the build tool)
    14  //      -flags          describe flags                    (to the build tool)
    15  //      foo.cfg         description of compilation unit (from the build tool)
    16  //
    17  // This package does not depend on go/packages.
    18  // If you need a standalone tool, use multichecker,
    19  // which supports this mode but can also load packages
    20  // from source using go/packages.
    21  package unitchecker
    22  
    23  // TODO(adonovan):
    24  // - with gccgo, go build does not build standard library,
    25  //   so we will not get to analyze it. Yet we must in order
    26  //   to create base facts for, say, the fmt package for the
    27  //   printf checker.
    28  
    29  import (
    30  	"encoding/gob"
    31  	"encoding/json"
    32  	"flag"
    33  	"fmt"
    34  	"go/ast"
    35  	"go/build"
    36  	"go/importer"
    37  	"go/parser"
    38  	"go/token"
    39  	"go/types"
    40  	"io"
    41  	"io/ioutil"
    42  	"log"
    43  	"os"
    44  	"path/filepath"
    45  	"reflect"
    46  	"sort"
    47  	"strings"
    48  	"sync"
    49  	"time"
    50  
    51  	"golang.org/x/tools/go/analysis"
    52  	"golang.org/x/tools/go/analysis/internal/analysisflags"
    53  	"golang.org/x/tools/go/analysis/internal/facts"
    54  	"golang.org/x/tools/internal/typeparams"
    55  )
    56  
    57  // A Config describes a compilation unit to be analyzed.
    58  // It is provided to the tool in a JSON-encoded file
    59  // whose name ends with ".cfg".
    60  type Config struct {
    61  	ID                        string // e.g. "fmt [fmt.test]"
    62  	Compiler                  string
    63  	Dir                       string
    64  	ImportPath                string
    65  	GoFiles                   []string
    66  	NonGoFiles                []string
    67  	IgnoredFiles              []string
    68  	ImportMap                 map[string]string
    69  	PackageFile               map[string]string
    70  	Standard                  map[string]bool
    71  	PackageVetx               map[string]string
    72  	VetxOnly                  bool
    73  	VetxOutput                string
    74  	SucceedOnTypecheckFailure bool
    75  }
    76  
    77  // Main is the main function of a vet-like analysis tool that must be
    78  // invoked by a build system to analyze a single package.
    79  //
    80  // The protocol required by 'go vet -vettool=...' is that the tool must support:
    81  //
    82  //      -flags          describe flags in JSON
    83  //      -V=full         describe executable for build caching
    84  //      foo.cfg         perform separate modular analyze on the single
    85  //                      unit described by a JSON config file foo.cfg.
    86  //
    87  func Main(analyzers ...*analysis.Analyzer) {
    88  	progname := filepath.Base(os.Args[0])
    89  	log.SetFlags(0)
    90  	log.SetPrefix(progname + ": ")
    91  
    92  	if err := analysis.Validate(analyzers); err != nil {
    93  		log.Fatal(err)
    94  	}
    95  
    96  	flag.Usage = func() {
    97  		fmt.Fprintf(os.Stderr, `%[1]s is a tool for static analysis of Go programs.
    98  
    99  Usage of %[1]s:
   100  	%.16[1]s unit.cfg	# execute analysis specified by config file
   101  	%.16[1]s help    	# general help, including listing analyzers and flags
   102  	%.16[1]s help name	# help on specific analyzer and its flags
   103  `, progname)
   104  		os.Exit(1)
   105  	}
   106  
   107  	analyzers = analysisflags.Parse(analyzers, true)
   108  
   109  	args := flag.Args()
   110  	if len(args) == 0 {
   111  		flag.Usage()
   112  	}
   113  	if args[0] == "help" {
   114  		analysisflags.Help(progname, analyzers, args[1:])
   115  		os.Exit(0)
   116  	}
   117  	if len(args) != 1 || !strings.HasSuffix(args[0], ".cfg") {
   118  		log.Fatalf(`invoking "go tool vet" directly is unsupported; use "go vet"`)
   119  	}
   120  	Run(args[0], analyzers)
   121  }
   122  
   123  // Run reads the *.cfg file, runs the analysis,
   124  // and calls os.Exit with an appropriate error code.
   125  // It assumes flags have already been set.
   126  func Run(configFile string, analyzers []*analysis.Analyzer) {
   127  	cfg, err := readConfig(configFile)
   128  	if err != nil {
   129  		log.Fatal(err)
   130  	}
   131  
   132  	fset := token.NewFileSet()
   133  	results, err := run(fset, cfg, analyzers)
   134  	if err != nil {
   135  		log.Fatal(err)
   136  	}
   137  
   138  	// In VetxOnly mode, the analysis is run only for facts.
   139  	if !cfg.VetxOnly {
   140  		if analysisflags.JSON {
   141  			// JSON output
   142  			tree := make(analysisflags.JSONTree)
   143  			for _, res := range results {
   144  				tree.Add(fset, cfg.ID, res.a.Name, res.diagnostics, res.err)
   145  			}
   146  			tree.Print()
   147  		} else {
   148  			// plain text
   149  			exit := 0
   150  			for _, res := range results {
   151  				if res.err != nil {
   152  					log.Println(res.err)
   153  					exit = 1
   154  				}
   155  			}
   156  			for _, res := range results {
   157  				for _, diag := range res.diagnostics {
   158  					analysisflags.PrintPlain(fset, diag)
   159  					exit = 1
   160  				}
   161  			}
   162  			os.Exit(exit)
   163  		}
   164  	}
   165  
   166  	os.Exit(0)
   167  }
   168  
   169  func readConfig(filename string) (*Config, error) {
   170  	data, err := ioutil.ReadFile(filename)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	cfg := new(Config)
   175  	if err := json.Unmarshal(data, cfg); err != nil {
   176  		return nil, fmt.Errorf("cannot decode JSON config file %s: %v", filename, err)
   177  	}
   178  	if len(cfg.GoFiles) == 0 {
   179  		// The go command disallows packages with no files.
   180  		// The only exception is unsafe, but the go command
   181  		// doesn't call vet on it.
   182  		return nil, fmt.Errorf("package has no files: %s", cfg.ImportPath)
   183  	}
   184  	return cfg, nil
   185  }
   186  
   187  var importerForCompiler = func(_ *token.FileSet, compiler string, lookup importer.Lookup) types.Importer {
   188  	// broken legacy implementation (https://golang.org/issue/28995)
   189  	return importer.For(compiler, lookup)
   190  }
   191  
   192  func run(fset *token.FileSet, cfg *Config, analyzers []*analysis.Analyzer) ([]result, error) {
   193  	// Load, parse, typecheck.
   194  	var files []*ast.File
   195  	for _, name := range cfg.GoFiles {
   196  		f, err := parser.ParseFile(fset, name, nil, parser.ParseComments)
   197  		if err != nil {
   198  			if cfg.SucceedOnTypecheckFailure {
   199  				// Silently succeed; let the compiler
   200  				// report parse errors.
   201  				err = nil
   202  			}
   203  			return nil, err
   204  		}
   205  		files = append(files, f)
   206  	}
   207  	compilerImporter := importerForCompiler(fset, cfg.Compiler, func(path string) (io.ReadCloser, error) {
   208  		// path is a resolved package path, not an import path.
   209  		file, ok := cfg.PackageFile[path]
   210  		if !ok {
   211  			if cfg.Compiler == "gccgo" && cfg.Standard[path] {
   212  				return nil, nil // fall back to default gccgo lookup
   213  			}
   214  			return nil, fmt.Errorf("no package file for %q", path)
   215  		}
   216  		return os.Open(file)
   217  	})
   218  	importer := importerFunc(func(importPath string) (*types.Package, error) {
   219  		path, ok := cfg.ImportMap[importPath] // resolve vendoring, etc
   220  		if !ok {
   221  			return nil, fmt.Errorf("can't resolve import %q", path)
   222  		}
   223  		return compilerImporter.Import(path)
   224  	})
   225  	tc := &types.Config{
   226  		Importer: importer,
   227  		Sizes:    types.SizesFor("gc", build.Default.GOARCH), // assume gccgo ≡ gc?
   228  	}
   229  	info := &types.Info{
   230  		Types:      make(map[ast.Expr]types.TypeAndValue),
   231  		Defs:       make(map[*ast.Ident]types.Object),
   232  		Uses:       make(map[*ast.Ident]types.Object),
   233  		Implicits:  make(map[ast.Node]types.Object),
   234  		Scopes:     make(map[ast.Node]*types.Scope),
   235  		Selections: make(map[*ast.SelectorExpr]*types.Selection),
   236  	}
   237  	typeparams.InitInstanceInfo(info)
   238  
   239  	pkg, err := tc.Check(cfg.ImportPath, fset, files, info)
   240  	if err != nil {
   241  		if cfg.SucceedOnTypecheckFailure {
   242  			// Silently succeed; let the compiler
   243  			// report type errors.
   244  			err = nil
   245  		}
   246  		return nil, err
   247  	}
   248  
   249  	// Register fact types with gob.
   250  	// In VetxOnly mode, analyzers are only for their facts,
   251  	// so we can skip any analysis that neither produces facts
   252  	// nor depends on any analysis that produces facts.
   253  	// Also build a map to hold working state and result.
   254  	type action struct {
   255  		once        sync.Once
   256  		result      interface{}
   257  		err         error
   258  		usesFacts   bool // (transitively uses)
   259  		diagnostics []analysis.Diagnostic
   260  	}
   261  	actions := make(map[*analysis.Analyzer]*action)
   262  	var registerFacts func(a *analysis.Analyzer) bool
   263  	registerFacts = func(a *analysis.Analyzer) bool {
   264  		act, ok := actions[a]
   265  		if !ok {
   266  			act = new(action)
   267  			var usesFacts bool
   268  			for _, f := range a.FactTypes {
   269  				usesFacts = true
   270  				gob.Register(f)
   271  			}
   272  			for _, req := range a.Requires {
   273  				if registerFacts(req) {
   274  					usesFacts = true
   275  				}
   276  			}
   277  			act.usesFacts = usesFacts
   278  			actions[a] = act
   279  		}
   280  		return act.usesFacts
   281  	}
   282  	var filtered []*analysis.Analyzer
   283  	for _, a := range analyzers {
   284  		if registerFacts(a) || !cfg.VetxOnly {
   285  			filtered = append(filtered, a)
   286  		}
   287  	}
   288  	analyzers = filtered
   289  
   290  	// Read facts from imported packages.
   291  	read := func(path string) ([]byte, error) {
   292  		if vetx, ok := cfg.PackageVetx[path]; ok {
   293  			return ioutil.ReadFile(vetx)
   294  		}
   295  		return nil, nil // no .vetx file, no facts
   296  	}
   297  	facts, err := facts.Decode(pkg, read)
   298  	if err != nil {
   299  		return nil, err
   300  	}
   301  
   302  	// In parallel, execute the DAG of analyzers.
   303  	var exec func(a *analysis.Analyzer) *action
   304  	var execAll func(analyzers []*analysis.Analyzer)
   305  	exec = func(a *analysis.Analyzer) *action {
   306  		act := actions[a]
   307  		act.once.Do(func() {
   308  			execAll(a.Requires) // prefetch dependencies in parallel
   309  
   310  			// The inputs to this analysis are the
   311  			// results of its prerequisites.
   312  			inputs := make(map[*analysis.Analyzer]interface{})
   313  			var failed []string
   314  			for _, req := range a.Requires {
   315  				reqact := exec(req)
   316  				if reqact.err != nil {
   317  					failed = append(failed, req.String())
   318  					continue
   319  				}
   320  				inputs[req] = reqact.result
   321  			}
   322  
   323  			// Report an error if any dependency failed.
   324  			if failed != nil {
   325  				sort.Strings(failed)
   326  				act.err = fmt.Errorf("failed prerequisites: %s", strings.Join(failed, ", "))
   327  				return
   328  			}
   329  
   330  			factFilter := make(map[reflect.Type]bool)
   331  			for _, f := range a.FactTypes {
   332  				factFilter[reflect.TypeOf(f)] = true
   333  			}
   334  
   335  			pass := &analysis.Pass{
   336  				Analyzer:          a,
   337  				Fset:              fset,
   338  				Files:             files,
   339  				OtherFiles:        cfg.NonGoFiles,
   340  				IgnoredFiles:      cfg.IgnoredFiles,
   341  				Pkg:               pkg,
   342  				TypesInfo:         info,
   343  				TypesSizes:        tc.Sizes,
   344  				ResultOf:          inputs,
   345  				Report:            func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) },
   346  				ImportObjectFact:  facts.ImportObjectFact,
   347  				ExportObjectFact:  facts.ExportObjectFact,
   348  				AllObjectFacts:    func() []analysis.ObjectFact { return facts.AllObjectFacts(factFilter) },
   349  				ImportPackageFact: facts.ImportPackageFact,
   350  				ExportPackageFact: facts.ExportPackageFact,
   351  				AllPackageFacts:   func() []analysis.PackageFact { return facts.AllPackageFacts(factFilter) },
   352  			}
   353  
   354  			t0 := time.Now()
   355  			act.result, act.err = a.Run(pass)
   356  			if false {
   357  				log.Printf("analysis %s = %s", pass, time.Since(t0))
   358  			}
   359  		})
   360  		return act
   361  	}
   362  	execAll = func(analyzers []*analysis.Analyzer) {
   363  		var wg sync.WaitGroup
   364  		for _, a := range analyzers {
   365  			wg.Add(1)
   366  			go func(a *analysis.Analyzer) {
   367  				_ = exec(a)
   368  				wg.Done()
   369  			}(a)
   370  		}
   371  		wg.Wait()
   372  	}
   373  
   374  	execAll(analyzers)
   375  
   376  	// Return diagnostics and errors from root analyzers.
   377  	results := make([]result, len(analyzers))
   378  	for i, a := range analyzers {
   379  		act := actions[a]
   380  		results[i].a = a
   381  		results[i].err = act.err
   382  		results[i].diagnostics = act.diagnostics
   383  	}
   384  
   385  	data := facts.Encode()
   386  	if err := ioutil.WriteFile(cfg.VetxOutput, data, 0666); err != nil {
   387  		return nil, fmt.Errorf("failed to write analysis facts: %v", err)
   388  	}
   389  
   390  	return results, nil
   391  }
   392  
   393  type result struct {
   394  	a           *analysis.Analyzer
   395  	diagnostics []analysis.Diagnostic
   396  	err         error
   397  }
   398  
   399  type importerFunc func(path string) (*types.Package, error)
   400  
   401  func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
   402  

View as plain text