diff options
Diffstat (limited to 'vendor/honnef.co/go/tools/ssa/source.go')
-rw-r--r-- | vendor/honnef.co/go/tools/ssa/source.go | 293 |
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 | |||
5 | package 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 | |||
13 | import ( | ||
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 | // | ||
31 | func 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) | ||
40 | outer: | ||
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 | // | ||
69 | func 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 | // | ||
76 | func 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 | // | ||
111 | func 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 | // | ||
157 | func (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 | // | ||
179 | func (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 | // | ||
188 | func (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 | // | ||
201 | func (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 | // | ||
209 | func (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 | // | ||
258 | func (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 | } | ||