github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/ir/irutil/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 irutil 6 7 // This file defines utility functions for constructing programs in IR form. 8 9 import ( 10 "go/ast" 11 "go/token" 12 "go/types" 13 14 "github.com/amarpal/go-tools/go/ir" 15 16 //lint:ignore SA1019 go/loader is deprecated, but works fine for our tests 17 "golang.org/x/tools/go/loader" 18 "golang.org/x/tools/go/packages" 19 ) 20 21 type Options struct { 22 // Which function, if any, to print in HTML form 23 PrintFunc string 24 } 25 26 // Packages creates an IR program for a set of packages. 27 // 28 // The packages must have been loaded from source syntax using the 29 // golang.org/x/tools/go/packages.Load function in LoadSyntax or 30 // LoadAllSyntax mode. 31 // 32 // Packages creates an IR package for each well-typed package in the 33 // initial list, plus all their dependencies. The resulting list of 34 // packages corresponds to the list of initial packages, and may contain 35 // a nil if IR code could not be constructed for the corresponding initial 36 // package due to type errors. 37 // 38 // Code for bodies of functions is not built until Build is called on 39 // the resulting Program. IR code is constructed only for the initial 40 // packages with well-typed syntax trees. 41 // 42 // The mode parameter controls diagnostics and checking during IR construction. 43 func Packages(initial []*packages.Package, mode ir.BuilderMode, opts *Options) (*ir.Program, []*ir.Package) { 44 return doPackages(initial, mode, false, opts) 45 } 46 47 // AllPackages creates an IR program for a set of packages plus all 48 // their dependencies. 49 // 50 // The packages must have been loaded from source syntax using the 51 // golang.org/x/tools/go/packages.Load function in LoadAllSyntax mode. 52 // 53 // AllPackages creates an IR package for each well-typed package in the 54 // initial list, plus all their dependencies. The resulting list of 55 // packages corresponds to the list of initial packages, and may contain 56 // a nil if IR code could not be constructed for the corresponding 57 // initial package due to type errors. 58 // 59 // Code for bodies of functions is not built until Build is called on 60 // the resulting Program. IR code is constructed for all packages with 61 // well-typed syntax trees. 62 // 63 // The mode parameter controls diagnostics and checking during IR construction. 64 func AllPackages(initial []*packages.Package, mode ir.BuilderMode, opts *Options) (*ir.Program, []*ir.Package) { 65 return doPackages(initial, mode, true, opts) 66 } 67 68 func doPackages(initial []*packages.Package, mode ir.BuilderMode, deps bool, opts *Options) (*ir.Program, []*ir.Package) { 69 70 var fset *token.FileSet 71 if len(initial) > 0 { 72 fset = initial[0].Fset 73 } 74 75 prog := ir.NewProgram(fset, mode) 76 if opts != nil { 77 prog.PrintFunc = opts.PrintFunc 78 } 79 80 isInitial := make(map[*packages.Package]bool, len(initial)) 81 for _, p := range initial { 82 isInitial[p] = true 83 } 84 85 irmap := make(map[*packages.Package]*ir.Package) 86 packages.Visit(initial, nil, func(p *packages.Package) { 87 if p.Types != nil && !p.IllTyped { 88 var files []*ast.File 89 if deps || isInitial[p] { 90 files = p.Syntax 91 } 92 irmap[p] = prog.CreatePackage(p.Types, files, p.TypesInfo, true) 93 } 94 }) 95 96 var irpkgs []*ir.Package 97 for _, p := range initial { 98 irpkgs = append(irpkgs, irmap[p]) // may be nil 99 } 100 return prog, irpkgs 101 } 102 103 // CreateProgram returns a new program in IR form, given a program 104 // loaded from source. An IR package is created for each transitively 105 // error-free package of lprog. 106 // 107 // Code for bodies of functions is not built until Build is called 108 // on the result. 109 // 110 // The mode parameter controls diagnostics and checking during IR construction. 111 // 112 // Deprecated: use golang.org/x/tools/go/packages and the Packages 113 // function instead; see ir.ExampleLoadPackages. 114 func CreateProgram(lprog *loader.Program, mode ir.BuilderMode) *ir.Program { 115 prog := ir.NewProgram(lprog.Fset, mode) 116 117 for _, info := range lprog.AllPackages { 118 if info.TransitivelyErrorFree { 119 prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable) 120 } 121 } 122 123 return prog 124 } 125 126 // BuildPackage builds an IR program with IR for a single package. 127 // 128 // It populates pkg by type-checking the specified file ASTs. All 129 // dependencies are loaded using the importer specified by tc, which 130 // typically loads compiler export data; IR code cannot be built for 131 // those packages. BuildPackage then constructs an ir.Program with all 132 // dependency packages created, and builds and returns the IR package 133 // corresponding to pkg. 134 // 135 // The caller must have set pkg.Path() to the import path. 136 // 137 // The operation fails if there were any type-checking or import errors. 138 // 139 // See ../ir/example_test.go for an example. 140 func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ir.BuilderMode) (*ir.Package, *types.Info, error) { 141 if fset == nil { 142 panic("no token.FileSet") 143 } 144 if pkg.Path() == "" { 145 panic("package has no import path") 146 } 147 148 info := &types.Info{ 149 Types: make(map[ast.Expr]types.TypeAndValue), 150 Defs: make(map[*ast.Ident]types.Object), 151 Uses: make(map[*ast.Ident]types.Object), 152 Implicits: make(map[ast.Node]types.Object), 153 Scopes: make(map[ast.Node]*types.Scope), 154 Selections: make(map[*ast.SelectorExpr]*types.Selection), 155 } 156 if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil { 157 return nil, nil, err 158 } 159 160 prog := ir.NewProgram(fset, mode) 161 162 // Create IR packages for all imports. 163 // Order is not significant. 164 created := make(map[*types.Package]bool) 165 var createAll func(pkgs []*types.Package) 166 createAll = func(pkgs []*types.Package) { 167 for _, p := range pkgs { 168 if !created[p] { 169 created[p] = true 170 prog.CreatePackage(p, nil, nil, true) 171 createAll(p.Imports()) 172 } 173 } 174 } 175 createAll(pkg.Imports()) 176 177 // Create and build the primary package. 178 irpkg := prog.CreatePackage(pkg, files, info, false) 179 irpkg.Build() 180 return irpkg, info, nil 181 }