golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/go/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  	"golang.org/x/tools/go/ssa"
    17  	"golang.org/x/tools/internal/versions"
    18  )
    19  
    20  // Packages creates an SSA program for a set of packages.
    21  //
    22  // The packages must have been loaded from source syntax using the
    23  // [packages.Load] function in [packages.LoadSyntax] or
    24  // [packages.LoadAllSyntax] mode.
    25  //
    26  // Packages creates an SSA package for each well-typed package in the
    27  // initial list, plus all their dependencies. The resulting list of
    28  // packages corresponds to the list of initial packages, and may contain
    29  // a nil if SSA code could not be constructed for the corresponding initial
    30  // package due to type errors.
    31  //
    32  // Code for bodies of functions is not built until [Program.Build] is
    33  // called on the resulting Program. SSA code is constructed only for
    34  // the initial packages with well-typed syntax trees.
    35  //
    36  // The mode parameter controls diagnostics and checking during SSA construction.
    37  func Packages(initial []*packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Package) {
    38  	// TODO(adonovan): opt: this calls CreatePackage far more than
    39  	// necessary: for all dependencies, not just the (non-initial)
    40  	// direct dependencies of the initial packages.
    41  	//
    42  	// But can it reasonably be changed without breaking the
    43  	// spirit and/or letter of the law above? Clients may notice
    44  	// if we call CreatePackage less, as methods like
    45  	// Program.FuncValue will return nil. Or must we provide a new
    46  	// function (and perhaps deprecate this one)? Is it worth it?
    47  	//
    48  	// Tim King makes the interesting point that it would be
    49  	// possible to entirely alleviate the client from the burden
    50  	// of calling CreatePackage for non-syntax packages, if we
    51  	// were to treat vars and funcs lazily in the same way we now
    52  	// treat methods. (In essence, try to move away from the
    53  	// notion of ssa.Packages, and make the Program answer
    54  	// all reasonable questions about any types.Object.)
    55  
    56  	return doPackages(initial, mode, false)
    57  }
    58  
    59  // AllPackages creates an SSA program for a set of packages plus all
    60  // their dependencies.
    61  //
    62  // The packages must have been loaded from source syntax using the
    63  // [packages.Load] function in [packages.LoadAllSyntax] mode.
    64  //
    65  // AllPackages creates an SSA package for each well-typed package in the
    66  // initial list, plus all their dependencies. The resulting list of
    67  // packages corresponds to the list of initial packages, and may contain
    68  // a nil if SSA code could not be constructed for the corresponding
    69  // initial package due to type errors.
    70  //
    71  // Code for bodies of functions is not built until Build is called on
    72  // the resulting Program. SSA code is constructed for all packages with
    73  // well-typed syntax trees.
    74  //
    75  // The mode parameter controls diagnostics and checking during SSA construction.
    76  func AllPackages(initial []*packages.Package, mode ssa.BuilderMode) (*ssa.Program, []*ssa.Package) {
    77  	return doPackages(initial, mode, true)
    78  }
    79  
    80  func doPackages(initial []*packages.Package, mode ssa.BuilderMode, deps bool) (*ssa.Program, []*ssa.Package) {
    81  
    82  	var fset *token.FileSet
    83  	if len(initial) > 0 {
    84  		fset = initial[0].Fset
    85  	}
    86  
    87  	prog := ssa.NewProgram(fset, mode)
    88  
    89  	isInitial := make(map[*packages.Package]bool, len(initial))
    90  	for _, p := range initial {
    91  		isInitial[p] = true
    92  	}
    93  
    94  	ssamap := make(map[*packages.Package]*ssa.Package)
    95  	packages.Visit(initial, nil, func(p *packages.Package) {
    96  		if p.Types != nil && !p.IllTyped {
    97  			var files []*ast.File
    98  			var info *types.Info
    99  			if deps || isInitial[p] {
   100  				files = p.Syntax
   101  				info = p.TypesInfo
   102  			}
   103  			ssamap[p] = prog.CreatePackage(p.Types, files, info, true)
   104  		}
   105  	})
   106  
   107  	var ssapkgs []*ssa.Package
   108  	for _, p := range initial {
   109  		ssapkgs = append(ssapkgs, ssamap[p]) // may be nil
   110  	}
   111  	return prog, ssapkgs
   112  }
   113  
   114  // CreateProgram returns a new program in SSA form, given a program
   115  // loaded from source.  An SSA package is created for each transitively
   116  // error-free package of lprog.
   117  //
   118  // Code for bodies of functions is not built until Build is called
   119  // on the result.
   120  //
   121  // The mode parameter controls diagnostics and checking during SSA construction.
   122  //
   123  // Deprecated: Use [golang.org/x/tools/go/packages] and the [Packages]
   124  // function instead; see ssa.Example_loadPackages.
   125  func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
   126  	prog := ssa.NewProgram(lprog.Fset, mode)
   127  
   128  	for _, info := range lprog.AllPackages {
   129  		if info.TransitivelyErrorFree {
   130  			prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
   131  		}
   132  	}
   133  
   134  	return prog
   135  }
   136  
   137  // BuildPackage builds an SSA program with SSA intermediate
   138  // representation (IR) for all functions of a single package.
   139  //
   140  // It populates pkg by type-checking the specified file syntax trees.  All
   141  // dependencies are loaded using the importer specified by tc, which
   142  // typically loads compiler export data; SSA code cannot be built for
   143  // those packages.  BuildPackage then constructs an [ssa.Program] with all
   144  // dependency packages created, and builds and returns the SSA package
   145  // corresponding to pkg.
   146  //
   147  // The caller must have set pkg.Path to the import path.
   148  //
   149  // The operation fails if there were any type-checking or import errors.
   150  //
   151  // See ../example_test.go for an example.
   152  func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
   153  	if fset == nil {
   154  		panic("no token.FileSet")
   155  	}
   156  	if pkg.Path() == "" {
   157  		panic("package has no import path")
   158  	}
   159  
   160  	info := &types.Info{
   161  		Types:      make(map[ast.Expr]types.TypeAndValue),
   162  		Defs:       make(map[*ast.Ident]types.Object),
   163  		Uses:       make(map[*ast.Ident]types.Object),
   164  		Implicits:  make(map[ast.Node]types.Object),
   165  		Instances:  make(map[*ast.Ident]types.Instance),
   166  		Scopes:     make(map[ast.Node]*types.Scope),
   167  		Selections: make(map[*ast.SelectorExpr]*types.Selection),
   168  	}
   169  	versions.InitFileVersions(info)
   170  	if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
   171  		return nil, nil, err
   172  	}
   173  
   174  	prog := ssa.NewProgram(fset, mode)
   175  
   176  	// Create SSA packages for all imports.
   177  	// Order is not significant.
   178  	created := make(map[*types.Package]bool)
   179  	var createAll func(pkgs []*types.Package)
   180  	createAll = func(pkgs []*types.Package) {
   181  		for _, p := range pkgs {
   182  			if !created[p] {
   183  				created[p] = true
   184  				prog.CreatePackage(p, nil, nil, true)
   185  				createAll(p.Imports())
   186  			}
   187  		}
   188  	}
   189  	createAll(pkg.Imports())
   190  
   191  	// TODO(adonovan): we could replace createAll with just:
   192  	//
   193  	// // Create SSA packages for all imports.
   194  	// for _, p := range pkg.Imports() {
   195  	// 	prog.CreatePackage(p, nil, nil, true)
   196  	// }
   197  	//
   198  	// (with minor changes to changes to ../builder_test.go as
   199  	// shown in CL 511715 PS 10.) But this would strictly violate
   200  	// the letter of the doc comment above, which says "all
   201  	// dependencies created".
   202  	//
   203  	// Tim makes the good point with some extra work we could
   204  	// remove the need for any CreatePackage calls except the
   205  	// ones with syntax (i.e. primary packages). Of course
   206  	// You wouldn't have ssa.Packages and Members for as
   207  	// many things but no-one really uses that anyway.
   208  	// I wish I had done this from the outset.
   209  
   210  	// Create and build the primary package.
   211  	ssapkg := prog.CreatePackage(pkg, files, info, false)
   212  	ssapkg.Build()
   213  	return ssapkg, info, nil
   214  }