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 5package irutil 6 7// This file defines utility functions for constructing programs in IR form. 8 9import ( 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 "honnef.co/go/tools/ir" 17) 18 19type Options struct { 20 // Which function, if any, to print in HTML form 21 PrintFunc string 22} 23 24// Packages creates an IR program for a set of packages. 25// 26// The packages must have been loaded from source syntax using the 27// golang.org/x/tools/go/packages.Load function in LoadSyntax or 28// LoadAllSyntax mode. 29// 30// Packages creates an IR package for each well-typed package in the 31// initial list, plus all their dependencies. The resulting list of 32// packages corresponds to the list of initial packages, and may contain 33// a nil if IR code could not be constructed for the corresponding initial 34// package due to type errors. 35// 36// Code for bodies of functions is not built until Build is called on 37// the resulting Program. IR code is constructed only for the initial 38// packages with well-typed syntax trees. 39// 40// The mode parameter controls diagnostics and checking during IR construction. 41// 42func Packages(initial []*packages.Package, mode ir.BuilderMode, opts *Options) (*ir.Program, []*ir.Package) { 43 return doPackages(initial, mode, false, opts) 44} 45 46// AllPackages creates an IR program for a set of packages plus all 47// their dependencies. 48// 49// The packages must have been loaded from source syntax using the 50// golang.org/x/tools/go/packages.Load function in LoadAllSyntax mode. 51// 52// AllPackages creates an IR package for each well-typed package in the 53// initial list, plus all their dependencies. The resulting list of 54// packages corresponds to the list of initial packages, and may contain 55// a nil if IR code could not be constructed for the corresponding 56// initial package due to type errors. 57// 58// Code for bodies of functions is not built until Build is called on 59// the resulting Program. IR code is constructed for all packages with 60// well-typed syntax trees. 61// 62// The mode parameter controls diagnostics and checking during IR construction. 63// 64func AllPackages(initial []*packages.Package, mode ir.BuilderMode, opts *Options) (*ir.Program, []*ir.Package) { 65 return doPackages(initial, mode, true, opts) 66} 67 68func 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// 115func CreateProgram(lprog *loader.Program, mode ir.BuilderMode) *ir.Program { 116 prog := ir.NewProgram(lprog.Fset, mode) 117 118 for _, info := range lprog.AllPackages { 119 if info.TransitivelyErrorFree { 120 prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable) 121 } 122 } 123 124 return prog 125} 126 127// BuildPackage builds an IR program with IR for a single package. 128// 129// It populates pkg by type-checking the specified file ASTs. All 130// dependencies are loaded using the importer specified by tc, which 131// typically loads compiler export data; IR code cannot be built for 132// those packages. BuildPackage then constructs an ir.Program with all 133// dependency packages created, and builds and returns the IR package 134// corresponding to pkg. 135// 136// The caller must have set pkg.Path() to the import path. 137// 138// The operation fails if there were any type-checking or import errors. 139// 140// See ../ir/example_test.go for an example. 141// 142func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ir.BuilderMode) (*ir.Package, *types.Info, error) { 143 if fset == nil { 144 panic("no token.FileSet") 145 } 146 if pkg.Path() == "" { 147 panic("package has no import path") 148 } 149 150 info := &types.Info{ 151 Types: make(map[ast.Expr]types.TypeAndValue), 152 Defs: make(map[*ast.Ident]types.Object), 153 Uses: make(map[*ast.Ident]types.Object), 154 Implicits: make(map[ast.Node]types.Object), 155 Scopes: make(map[ast.Node]*types.Scope), 156 Selections: make(map[*ast.SelectorExpr]*types.Selection), 157 } 158 if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil { 159 return nil, nil, err 160 } 161 162 prog := ir.NewProgram(fset, mode) 163 164 // Create IR packages for all imports. 165 // Order is not significant. 166 created := make(map[*types.Package]bool) 167 var createAll func(pkgs []*types.Package) 168 createAll = func(pkgs []*types.Package) { 169 for _, p := range pkgs { 170 if !created[p] { 171 created[p] = true 172 prog.CreatePackage(p, nil, nil, true) 173 createAll(p.Imports()) 174 } 175 } 176 } 177 createAll(pkg.Imports()) 178 179 // Create and build the primary package. 180 irpkg := prog.CreatePackage(pkg, files, info, false) 181 irpkg.Build() 182 return irpkg, info, nil 183} 184