github.com/golangci/go-tools@v0.0.0-20190318060251-af6baa5dc196/ssa/ssautil/load.go (about)

     1  // Copyright 2015 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 ssautil
     6  
     7  // This file defines utility functions for constructing programs in SSA form.
     8  
     9  import (
    10  	"go/ast"
    11  	"go/token"
    12  	"go/types"
    13  
    14  	"golang.org/x/tools/go/loader"
    15  	"golang.org/x/tools/go/packages"
    16  	"github.com/golangci/go-tools/ssa"
    17  )
    18  
    19  // Packages creates an SSA program for a set of packages loaded from
    20  // source syntax using the golang.org/x/tools/go/packages.Load function.
    21  // It creates and returns an SSA package for each well-typed package in
    22  // the initial list. The resulting list of packages has the same length
    23  // as initial, and contains a nil if SSA could not be constructed for
    24  // the corresponding initial package.
    25  //
    26  // Code for bodies of functions is not built until Build is called
    27  // on the resulting Program.
    28  //
    29  // The mode parameter controls diagnostics and checking during SSA construction.
    30  //
    31  func Packages(initial []*packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Package) {
    32  	var fset *token.FileSet
    33  	if len(initial) > 0 {
    34  		fset = initial[0].Fset
    35  	}
    36  
    37  	prog := ssa.NewProgram(fset, mode)
    38  	seen := make(map[*packages.Package]*ssa.Package)
    39  	var create func(p *packages.Package) *ssa.Package
    40  	create = func(p *packages.Package) *ssa.Package {
    41  		ssapkg, ok := seen[p]
    42  		if !ok {
    43  			if p.Types == nil || p.IllTyped {
    44  				// not well typed
    45  				seen[p] = nil
    46  				return nil
    47  			}
    48  
    49  			ssapkg = prog.CreatePackage(p.Types, p.Syntax, p.TypesInfo, true)
    50  			seen[p] = ssapkg
    51  
    52  			for _, imp := range p.Imports {
    53  				create(imp)
    54  			}
    55  		}
    56  		return ssapkg
    57  	}
    58  
    59  	var ssapkgs []*ssa.Package
    60  	for _, p := range initial {
    61  		ssapkgs = append(ssapkgs, create(p))
    62  	}
    63  	return prog, ssapkgs
    64  }
    65  
    66  // CreateProgram returns a new program in SSA form, given a program
    67  // loaded from source.  An SSA package is created for each transitively
    68  // error-free package of lprog.
    69  //
    70  // Code for bodies of functions is not built until Build is called
    71  // on the result.
    72  //
    73  // mode controls diagnostics and checking during SSA construction.
    74  //
    75  func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
    76  	prog := ssa.NewProgram(lprog.Fset, mode)
    77  
    78  	for _, info := range lprog.AllPackages {
    79  		if info.TransitivelyErrorFree {
    80  			prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
    81  		}
    82  	}
    83  
    84  	return prog
    85  }
    86  
    87  // BuildPackage builds an SSA program with IR for a single package.
    88  //
    89  // It populates pkg by type-checking the specified file ASTs.  All
    90  // dependencies are loaded using the importer specified by tc, which
    91  // typically loads compiler export data; SSA code cannot be built for
    92  // those packages.  BuildPackage then constructs an ssa.Program with all
    93  // dependency packages created, and builds and returns the SSA package
    94  // corresponding to pkg.
    95  //
    96  // The caller must have set pkg.Path() to the import path.
    97  //
    98  // The operation fails if there were any type-checking or import errors.
    99  //
   100  // See ../ssa/example_test.go for an example.
   101  //
   102  func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
   103  	if fset == nil {
   104  		panic("no token.FileSet")
   105  	}
   106  	if pkg.Path() == "" {
   107  		panic("package has no import path")
   108  	}
   109  
   110  	info := &types.Info{
   111  		Types:      make(map[ast.Expr]types.TypeAndValue),
   112  		Defs:       make(map[*ast.Ident]types.Object),
   113  		Uses:       make(map[*ast.Ident]types.Object),
   114  		Implicits:  make(map[ast.Node]types.Object),
   115  		Scopes:     make(map[ast.Node]*types.Scope),
   116  		Selections: make(map[*ast.SelectorExpr]*types.Selection),
   117  	}
   118  	if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
   119  		return nil, nil, err
   120  	}
   121  
   122  	prog := ssa.NewProgram(fset, mode)
   123  
   124  	// Create SSA packages for all imports.
   125  	// Order is not significant.
   126  	created := make(map[*types.Package]bool)
   127  	var createAll func(pkgs []*types.Package)
   128  	createAll = func(pkgs []*types.Package) {
   129  		for _, p := range pkgs {
   130  			if !created[p] {
   131  				created[p] = true
   132  				prog.CreatePackage(p, nil, nil, true)
   133  				createAll(p.Imports())
   134  			}
   135  		}
   136  	}
   137  	createAll(pkg.Imports())
   138  
   139  	// Create and build the primary package.
   140  	ssapkg := prog.CreatePackage(pkg, files, info, false)
   141  	ssapkg.Build()
   142  	return ssapkg, info, nil
   143  }