src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

create.go (9294B)


      1 // Copyright 2013 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 ir
      6 
      7 // This file implements the CREATE phase of IR construction.
      8 // See builder.go for explanation.
      9 
     10 import (
     11 	"fmt"
     12 	"go/ast"
     13 	"go/token"
     14 	"go/types"
     15 	"os"
     16 	"sync"
     17 
     18 	"honnef.co/go/tools/internal/xtools-internal/versions"
     19 )
     20 
     21 // NewProgram returns a new IR Program.
     22 //
     23 // mode controls diagnostics and checking during IR construction.
     24 //
     25 // To construct an SSA program:
     26 //
     27 //   - Call NewProgram to create an empty Program.
     28 //   - Call CreatePackage providing typed syntax for each package
     29 //     you want to build, and call it with types but not
     30 //     syntax for each of those package's direct dependencies.
     31 //   - Call [Package.Build] on each syntax package you wish to build,
     32 //     or [Program.Build] to build all of them.
     33 //
     34 // See the Example tests for simple examples.
     35 func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
     36 	return &Program{
     37 		Fset:     fset,
     38 		imported: make(map[string]*Package),
     39 		packages: make(map[*types.Package]*Package),
     40 		mode:     mode,
     41 		canon:    newCanonizer(),
     42 		ctxt:     types.NewContext(),
     43 	}
     44 }
     45 
     46 // memberFromObject populates package pkg with a member for the
     47 // typechecker object obj.
     48 //
     49 // For objects from Go source code, syntax is the associated syntax tree
     50 // (for funcs and vars only) and goversion defines the appropriate
     51 // interpretation; they will be used during the build phase.
     52 func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion string) {
     53 	name := obj.Name()
     54 	switch obj := obj.(type) {
     55 	case *types.Builtin:
     56 		if pkg.Pkg != types.Unsafe {
     57 			panic("unexpected builtin object: " + obj.String())
     58 		}
     59 
     60 	case *types.TypeName:
     61 		if name != "_" {
     62 			pkg.Members[name] = &Type{
     63 				object: obj,
     64 				pkg:    pkg,
     65 			}
     66 		}
     67 
     68 	case *types.Const:
     69 		c := &NamedConst{
     70 			object: obj,
     71 			Value:  NewConst(obj.Val(), obj.Type(), syntax),
     72 			pkg:    pkg,
     73 		}
     74 		pkg.values[obj] = c
     75 		if name != "_" {
     76 			pkg.Members[name] = c
     77 		}
     78 
     79 	case *types.Var:
     80 		g := &Global{
     81 			Pkg:    pkg,
     82 			name:   name,
     83 			object: obj,
     84 			typ:    types.NewPointer(obj.Type()), // address
     85 		}
     86 		g.source = syntax
     87 		pkg.values[obj] = g
     88 		if name != "_" {
     89 			pkg.Members[name] = g
     90 		}
     91 
     92 	case *types.Func:
     93 		sig := obj.Type().(*types.Signature)
     94 		if sig.Recv() == nil && name == "init" {
     95 			pkg.ninit++
     96 			name = fmt.Sprintf("init#%d", pkg.ninit)
     97 		}
     98 		fn := createFunction(pkg.Prog, obj, name, syntax, pkg.info, goversion)
     99 		fn.Pkg = pkg
    100 		pkg.created = append(pkg.created, fn)
    101 		pkg.values[obj] = fn
    102 		pkg.Functions = append(pkg.Functions, fn)
    103 		if name != "_" && sig.Recv() == nil {
    104 			pkg.Members[name] = fn // package-level function
    105 		}
    106 
    107 	default: // (incl. *types.Package)
    108 		panic("unexpected Object type: " + obj.String())
    109 	}
    110 }
    111 
    112 // createFunction creates a function or method. It supports both
    113 // CreatePackage (with or without syntax) and the on-demand creation
    114 // of methods in non-created packages based on their types.Func.
    115 func createFunction(prog *Program, obj *types.Func, name string, syntax ast.Node, info *types.Info, goversion string) *Function {
    116 	sig := obj.Type().(*types.Signature)
    117 
    118 	/* declared function/method (from syntax or export data) */
    119 	fn := &Function{
    120 		name:           name,
    121 		object:         obj,
    122 		Signature:      sig,
    123 		build:          (*builder).buildFromSyntax,
    124 		info:           info,
    125 		goversion:      goversion,
    126 		pos:            obj.Pos(),
    127 		syntax:         syntax,
    128 		Pkg:            nil, // may be set by caller
    129 		Prog:           prog,
    130 		recvtypeparams: sig.RecvTypeParams(),
    131 		typeparams:     sig.TypeParams(),
    132 	}
    133 	if syntax == nil {
    134 		fn.Synthetic = "from type information"
    135 		fn.build = (*builder).buildParamsOnly
    136 	}
    137 	if fn.hasTypeParams() {
    138 		fn.generic = new(generic)
    139 	}
    140 	return fn
    141 }
    142 
    143 // membersFromDecl populates package pkg with members for each
    144 // typechecker object (var, func, const or type) associated with the
    145 // specified decl.
    146 func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) {
    147 	switch decl := decl.(type) {
    148 	case *ast.GenDecl: // import, const, type or var
    149 		switch decl.Tok {
    150 		case token.CONST:
    151 			for _, spec := range decl.Specs {
    152 				for _, id := range spec.(*ast.ValueSpec).Names {
    153 					memberFromObject(pkg, pkg.info.Defs[id], nil, "")
    154 				}
    155 			}
    156 
    157 		case token.VAR:
    158 			for _, spec := range decl.Specs {
    159 				for _, rhs := range spec.(*ast.ValueSpec).Values {
    160 					pkg.initVersion[rhs] = goversion
    161 				}
    162 				for _, id := range spec.(*ast.ValueSpec).Names {
    163 					memberFromObject(pkg, pkg.info.Defs[id], spec, goversion)
    164 				}
    165 			}
    166 
    167 		case token.TYPE:
    168 			for _, spec := range decl.Specs {
    169 				id := spec.(*ast.TypeSpec).Name
    170 				memberFromObject(pkg, pkg.info.Defs[id], nil, "")
    171 			}
    172 		}
    173 
    174 	case *ast.FuncDecl:
    175 		id := decl.Name
    176 		memberFromObject(pkg, pkg.info.Defs[id], decl, goversion)
    177 	}
    178 }
    179 
    180 // CreatePackage creates and returns an IR Package from the
    181 // specified type-checked, error-free file ASTs, and populates its
    182 // Members mapping.
    183 //
    184 // importable determines whether this package should be returned by a
    185 // subsequent call to ImportedPackage(pkg.Path()).
    186 //
    187 // The real work of building IR form for each function is not done
    188 // until a subsequent call to Package.Build.
    189 func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
    190 	if pkg == nil {
    191 		panic("nil pkg") // otherwise pkg.Scope below returns types.Universe!
    192 	}
    193 	p := &Package{
    194 		Prog:    prog,
    195 		Members: make(map[string]Member),
    196 		values:  make(map[types.Object]Member),
    197 		Pkg:     pkg,
    198 		syntax:  info != nil,
    199 		// transient values (cleared after Package.Build)
    200 		info:        info,
    201 		files:       files,
    202 		initVersion: make(map[ast.Expr]string),
    203 	}
    204 
    205 	/* synthesized package initializer */
    206 	p.init = &Function{
    207 		name:      "init",
    208 		Signature: new(types.Signature),
    209 		Synthetic: "package initializer",
    210 		Pkg:       p,
    211 		Prog:      prog,
    212 		build:     (*builder).buildPackageInit,
    213 		info:      p.info,
    214 		goversion: "", // See Package.build for details.
    215 	}
    216 	p.Members[p.init.name] = p.init
    217 	p.Functions = append(p.Functions, p.init)
    218 	p.created = append(p.created, p.init)
    219 
    220 	// Allocate all package members: vars, funcs, consts and types.
    221 	if len(files) > 0 {
    222 		// Go source package.
    223 		for _, file := range files {
    224 			goversion := versions.Lang(versions.FileVersion(p.info, file))
    225 			for _, decl := range file.Decls {
    226 				membersFromDecl(p, decl, goversion)
    227 			}
    228 		}
    229 	} else {
    230 		// GC-compiled binary package (or "unsafe")
    231 		// No code.
    232 		// No position information.
    233 		scope := p.Pkg.Scope()
    234 		for _, name := range scope.Names() {
    235 			obj := scope.Lookup(name)
    236 			memberFromObject(p, obj, nil, "")
    237 			if obj, ok := obj.(*types.TypeName); ok {
    238 				// No Unalias: aliases should not duplicate methods.
    239 				if named, ok := obj.Type().(*types.Named); ok {
    240 					for i, n := 0, named.NumMethods(); i < n; i++ {
    241 						memberFromObject(p, named.Method(i), nil, "")
    242 					}
    243 				}
    244 			}
    245 		}
    246 	}
    247 
    248 	if prog.mode&BareInits == 0 {
    249 		// Add initializer guard variable.
    250 		initguard := &Global{
    251 			Pkg:  p,
    252 			name: "init$guard",
    253 			typ:  types.NewPointer(tBool),
    254 		}
    255 		p.Members[initguard.Name()] = initguard
    256 	}
    257 
    258 	if prog.mode&GlobalDebug != 0 {
    259 		p.SetDebugMode(true)
    260 	}
    261 
    262 	if prog.mode&PrintPackages != 0 {
    263 		printMu.Lock()
    264 		p.WriteTo(os.Stdout)
    265 		printMu.Unlock()
    266 	}
    267 
    268 	if importable {
    269 		prog.imported[p.Pkg.Path()] = p
    270 	}
    271 	prog.packages[p.Pkg] = p
    272 
    273 	return p
    274 }
    275 
    276 // printMu serializes printing of Packages/Functions to stdout.
    277 var printMu sync.Mutex
    278 
    279 // AllPackages returns a new slice containing all packages created by
    280 // prog.CreatePackage in unspecified order.
    281 func (prog *Program) AllPackages() []*Package {
    282 	pkgs := make([]*Package, 0, len(prog.packages))
    283 	for _, pkg := range prog.packages {
    284 		pkgs = append(pkgs, pkg)
    285 	}
    286 	return pkgs
    287 }
    288 
    289 // ImportedPackage returns the importable Package whose PkgPath
    290 // is path, or nil if no such Package has been created.
    291 //
    292 // A parameter to CreatePackage determines whether a package should be
    293 // considered importable. For example, no import declaration can resolve
    294 // to the ad-hoc main package created by 'go build foo.go'.
    295 //
    296 // TODO(adonovan): rethink this function and the "importable" concept;
    297 // most packages are importable. This function assumes that all
    298 // types.Package.Path values are unique within the ir.Program, which is
    299 // false---yet this function remains very convenient.
    300 // Clients should use (*Program).Package instead where possible.
    301 // IR doesn't really need a string-keyed map of packages.
    302 //
    303 // Furthermore, the graph of packages may contain multiple variants
    304 // (e.g. "p" vs "p as compiled for q.test"), and each has a different
    305 // view of its dependencies.
    306 func (prog *Program) ImportedPackage(path string) *Package {
    307 	return prog.imported[path]
    308 }
    309 
    310 // SetNoReturn sets the predicate used when building the ir.Program
    311 // prog that reports whether a given function cannot return.
    312 // This may be used to prune spurious control flow edges
    313 // after (e.g.) log.Fatal, improving the precision of analyses.
    314 //
    315 // A typical implementation is the [ctrlflow.CFGs.NoReturn] method from
    316 // [golang.org/x/tools/go/analysis/passes/ctrlflow].
    317 func (prog *Program) SetNoReturn(fn func(*types.Func) bool) {
    318 	prog.noReturn = fn
    319 }