aboutsummaryrefslogtreecommitdiff
path: root/vendor/honnef.co/go/tools/ssa/source.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/honnef.co/go/tools/ssa/source.go')
-rw-r--r--vendor/honnef.co/go/tools/ssa/source.go293
1 files changed, 293 insertions, 0 deletions
diff --git a/vendor/honnef.co/go/tools/ssa/source.go b/vendor/honnef.co/go/tools/ssa/source.go
new file mode 100644
index 0000000..8d9cca1
--- /dev/null
+++ b/vendor/honnef.co/go/tools/ssa/source.go
@@ -0,0 +1,293 @@
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
5package ssa
6
7// This file defines utilities for working with source positions
8// or source-level named entities ("objects").
9
10// TODO(adonovan): test that {Value,Instruction}.Pos() positions match
11// the originating syntax, as specified.
12
13import (
14 "go/ast"
15 "go/token"
16 "go/types"
17)
18
19// EnclosingFunction returns the function that contains the syntax
20// node denoted by path.
21//
22// Syntax associated with package-level variable specifications is
23// enclosed by the package's init() function.
24//
25// Returns nil if not found; reasons might include:
26// - the node is not enclosed by any function.
27// - the node is within an anonymous function (FuncLit) and
28// its SSA function has not been created yet
29// (pkg.Build() has not yet been called).
30//
31func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
32 // Start with package-level function...
33 fn := findEnclosingPackageLevelFunction(pkg, path)
34 if fn == nil {
35 return nil // not in any function
36 }
37
38 // ...then walk down the nested anonymous functions.
39 n := len(path)
40outer:
41 for i := range path {
42 if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
43 for _, anon := range fn.AnonFuncs {
44 if anon.Pos() == lit.Type.Func {
45 fn = anon
46 continue outer
47 }
48 }
49 // SSA function not found:
50 // - package not yet built, or maybe
51 // - builder skipped FuncLit in dead block
52 // (in principle; but currently the Builder
53 // generates even dead FuncLits).
54 return nil
55 }
56 }
57 return fn
58}
59
60// HasEnclosingFunction returns true if the AST node denoted by path
61// is contained within the declaration of some function or
62// package-level variable.
63//
64// Unlike EnclosingFunction, the behaviour of this function does not
65// depend on whether SSA code for pkg has been built, so it can be
66// used to quickly reject check inputs that will cause
67// EnclosingFunction to fail, prior to SSA building.
68//
69func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
70 return findEnclosingPackageLevelFunction(pkg, path) != nil
71}
72
73// findEnclosingPackageLevelFunction returns the Function
74// corresponding to the package-level function enclosing path.
75//
76func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
77 if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
78 switch decl := path[n-2].(type) {
79 case *ast.GenDecl:
80 if decl.Tok == token.VAR && n >= 3 {
81 // Package-level 'var' initializer.
82 return pkg.init
83 }
84
85 case *ast.FuncDecl:
86 if decl.Recv == nil && decl.Name.Name == "init" {
87 // Explicit init() function.
88 for _, b := range pkg.init.Blocks {
89 for _, instr := range b.Instrs {
90 if instr, ok := instr.(*Call); ok {
91 if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos {
92 return callee
93 }
94 }
95 }
96 }
97 // Hack: return non-nil when SSA is not yet
98 // built so that HasEnclosingFunction works.
99 return pkg.init
100 }
101 // Declared function/method.
102 return findNamedFunc(pkg, decl.Name.NamePos)
103 }
104 }
105 return nil // not in any function
106}
107
108// findNamedFunc returns the named function whose FuncDecl.Ident is at
109// position pos.
110//
111func findNamedFunc(pkg *Package, pos token.Pos) *Function {
112 // Look at all package members and method sets of named types.
113 // Not very efficient.
114 for _, mem := range pkg.Members {
115 switch mem := mem.(type) {
116 case *Function:
117 if mem.Pos() == pos {
118 return mem
119 }
120 case *Type:
121 mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
122 for i, n := 0, mset.Len(); i < n; i++ {
123 // Don't call Program.Method: avoid creating wrappers.
124 obj := mset.At(i).Obj().(*types.Func)
125 if obj.Pos() == pos {
126 return pkg.values[obj].(*Function)
127 }
128 }
129 }
130 }
131 return nil
132}
133
134// ValueForExpr returns the SSA Value that corresponds to non-constant
135// expression e.
136//
137// It returns nil if no value was found, e.g.
138// - the expression is not lexically contained within f;
139// - f was not built with debug information; or
140// - e is a constant expression. (For efficiency, no debug
141// information is stored for constants. Use
142// go/types.Info.Types[e].Value instead.)
143// - e is a reference to nil or a built-in function.
144// - the value was optimised away.
145//
146// If e is an addressable expression used in an lvalue context,
147// value is the address denoted by e, and isAddr is true.
148//
149// The types of e (or &e, if isAddr) and the result are equal
150// (modulo "untyped" bools resulting from comparisons).
151//
152// (Tip: to find the ssa.Value given a source position, use
153// astutil.PathEnclosingInterval to locate the ast.Node, then
154// EnclosingFunction to locate the Function, then ValueForExpr to find
155// the ssa.Value.)
156//
157func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
158 if f.debugInfo() { // (opt)
159 e = unparen(e)
160 for _, b := range f.Blocks {
161 for _, instr := range b.Instrs {
162 if ref, ok := instr.(*DebugRef); ok {
163 if ref.Expr == e {
164 return ref.X, ref.IsAddr
165 }
166 }
167 }
168 }
169 }
170 return
171}
172
173// --- Lookup functions for source-level named entities (types.Objects) ---
174
175// Package returns the SSA Package corresponding to the specified
176// type-checker package object.
177// It returns nil if no such SSA package has been created.
178//
179func (prog *Program) Package(obj *types.Package) *Package {
180 return prog.packages[obj]
181}
182
183// packageLevelValue returns the package-level value corresponding to
184// the specified named object, which may be a package-level const
185// (*Const), var (*Global) or func (*Function) of some package in
186// prog. It returns nil if the object is not found.
187//
188func (prog *Program) packageLevelValue(obj types.Object) Value {
189 if pkg, ok := prog.packages[obj.Pkg()]; ok {
190 return pkg.values[obj]
191 }
192 return nil
193}
194
195// FuncValue returns the concrete Function denoted by the source-level
196// named function obj, or nil if obj denotes an interface method.
197//
198// TODO(adonovan): check the invariant that obj.Type() matches the
199// result's Signature, both in the params/results and in the receiver.
200//
201func (prog *Program) FuncValue(obj *types.Func) *Function {
202 fn, _ := prog.packageLevelValue(obj).(*Function)
203 return fn
204}
205
206// ConstValue returns the SSA Value denoted by the source-level named
207// constant obj.
208//
209func (prog *Program) ConstValue(obj *types.Const) *Const {
210 // TODO(adonovan): opt: share (don't reallocate)
211 // Consts for const objects and constant ast.Exprs.
212
213 // Universal constant? {true,false,nil}
214 if obj.Parent() == types.Universe {
215 return NewConst(obj.Val(), obj.Type())
216 }
217 // Package-level named constant?
218 if v := prog.packageLevelValue(obj); v != nil {
219 return v.(*Const)
220 }
221 return NewConst(obj.Val(), obj.Type())
222}
223
224// VarValue returns the SSA Value that corresponds to a specific
225// identifier denoting the source-level named variable obj.
226//
227// VarValue returns nil if a local variable was not found, perhaps
228// because its package was not built, the debug information was not
229// requested during SSA construction, or the value was optimized away.
230//
231// ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
232// and that ident must resolve to obj.
233//
234// pkg is the package enclosing the reference. (A reference to a var
235// always occurs within a function, so we need to know where to find it.)
236//
237// If the identifier is a field selector and its base expression is
238// non-addressable, then VarValue returns the value of that field.
239// For example:
240// func f() struct {x int}
241// f().x // VarValue(x) returns a *Field instruction of type int
242//
243// All other identifiers denote addressable locations (variables).
244// For them, VarValue may return either the variable's address or its
245// value, even when the expression is evaluated only for its value; the
246// situation is reported by isAddr, the second component of the result.
247//
248// If !isAddr, the returned value is the one associated with the
249// specific identifier. For example,
250// var x int // VarValue(x) returns Const 0 here
251// x = 1 // VarValue(x) returns Const 1 here
252//
253// It is not specified whether the value or the address is returned in
254// any particular case, as it may depend upon optimizations performed
255// during SSA code generation, such as registerization, constant
256// folding, avoidance of materialization of subexpressions, etc.
257//
258func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
259 // All references to a var are local to some function, possibly init.
260 fn := EnclosingFunction(pkg, ref)
261 if fn == nil {
262 return // e.g. def of struct field; SSA not built?
263 }
264
265 id := ref[0].(*ast.Ident)
266
267 // Defining ident of a parameter?
268 if id.Pos() == obj.Pos() {
269 for _, param := range fn.Params {
270 if param.Object() == obj {
271 return param, false
272 }
273 }
274 }
275
276 // Other ident?
277 for _, b := range fn.Blocks {
278 for _, instr := range b.Instrs {
279 if dr, ok := instr.(*DebugRef); ok {
280 if dr.Pos() == id.Pos() {
281 return dr.X, dr.IsAddr
282 }
283 }
284 }
285 }
286
287 // Defining ident of package-level var?
288 if v := prog.packageLevelValue(obj); v != nil {
289 return v.(*Global), true
290 }
291
292 return // e.g. debug info not requested, or var optimized away
293}