Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / ir / source_test.go
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 //lint:file-ignore SA1019 go/ssa's test suite is built around the deprecated go/loader. We'll leave fixing that to upstream.
6
7 package ir_test
8
9 // This file defines tests of source-level debugging utilities.
10
11 import (
12         "fmt"
13         "go/ast"
14         "go/constant"
15         "go/parser"
16         "go/token"
17         "go/types"
18         "io/ioutil"
19         "os"
20         "runtime"
21         "strings"
22         "testing"
23
24         "golang.org/x/tools/go/ast/astutil"
25         "golang.org/x/tools/go/expect"
26         "golang.org/x/tools/go/loader"
27         "honnef.co/go/tools/ir"
28         "honnef.co/go/tools/ir/irutil"
29 )
30
31 func TestObjValueLookup(t *testing.T) {
32         if runtime.GOOS == "android" {
33                 t.Skipf("no testdata directory on %s", runtime.GOOS)
34         }
35
36         conf := loader.Config{ParserMode: parser.ParseComments}
37         src, err := ioutil.ReadFile("testdata/objlookup.go")
38         if err != nil {
39                 t.Fatal(err)
40         }
41         readFile := func(filename string) ([]byte, error) { return src, nil }
42         f, err := conf.ParseFile("testdata/objlookup.go", src)
43         if err != nil {
44                 t.Fatal(err)
45         }
46         conf.CreateFromFiles("main", f)
47
48         // Maps each var Ident (represented "name:linenum") to the
49         // kind of ir.Value we expect (represented "Constant", "&Alloc").
50         expectations := make(map[string]string)
51
52         // Each note of the form @ir(x, "BinOp") in testdata/objlookup.go
53         // specifies an expectation that an object named x declared on the
54         // same line is associated with an an ir.Value of type *ir.BinOp.
55         notes, err := expect.Extract(conf.Fset, f)
56         if err != nil {
57                 t.Fatal(err)
58         }
59         for _, n := range notes {
60                 if n.Name != "ir" {
61                         t.Errorf("%v: unexpected note type %q, want \"ir\"", conf.Fset.Position(n.Pos), n.Name)
62                         continue
63                 }
64                 if len(n.Args) != 2 {
65                         t.Errorf("%v: ir has %d args, want 2", conf.Fset.Position(n.Pos), len(n.Args))
66                         continue
67                 }
68                 ident, ok := n.Args[0].(expect.Identifier)
69                 if !ok {
70                         t.Errorf("%v: got %v for arg 1, want identifier", conf.Fset.Position(n.Pos), n.Args[0])
71                         continue
72                 }
73                 exp, ok := n.Args[1].(string)
74                 if !ok {
75                         t.Errorf("%v: got %v for arg 2, want string", conf.Fset.Position(n.Pos), n.Args[1])
76                         continue
77                 }
78                 p, _, err := expect.MatchBefore(conf.Fset, readFile, n.Pos, string(ident))
79                 if err != nil {
80                         t.Error(err)
81                         continue
82                 }
83                 pos := conf.Fset.Position(p)
84                 key := fmt.Sprintf("%s:%d", ident, pos.Line)
85                 expectations[key] = exp
86         }
87
88         iprog, err := conf.Load()
89         if err != nil {
90                 t.Error(err)
91                 return
92         }
93
94         prog := irutil.CreateProgram(iprog, 0 /*|ir.PrintFunctions*/)
95         mainInfo := iprog.Created[0]
96         mainPkg := prog.Package(mainInfo.Pkg)
97         mainPkg.SetDebugMode(true)
98         mainPkg.Build()
99
100         var varIds []*ast.Ident
101         var varObjs []*types.Var
102         for id, obj := range mainInfo.Defs {
103                 // Check invariants for func and const objects.
104                 switch obj := obj.(type) {
105                 case *types.Func:
106                         checkFuncValue(t, prog, obj)
107
108                 case *types.Const:
109                         checkConstValue(t, prog, obj)
110
111                 case *types.Var:
112                         if id.Name == "_" {
113                                 continue
114                         }
115                         varIds = append(varIds, id)
116                         varObjs = append(varObjs, obj)
117                 }
118         }
119         for id, obj := range mainInfo.Uses {
120                 if obj, ok := obj.(*types.Var); ok {
121                         varIds = append(varIds, id)
122                         varObjs = append(varObjs, obj)
123                 }
124         }
125
126         // Check invariants for var objects.
127         // The result varies based on the specific Ident.
128         for i, id := range varIds {
129                 obj := varObjs[i]
130                 ref, _ := astutil.PathEnclosingInterval(f, id.Pos(), id.Pos())
131                 pos := prog.Fset.Position(id.Pos())
132                 exp := expectations[fmt.Sprintf("%s:%d", id.Name, pos.Line)]
133                 if exp == "" {
134                         t.Errorf("%s: no expectation for var ident %s ", pos, id.Name)
135                         continue
136                 }
137                 wantAddr := false
138                 if exp[0] == '&' {
139                         wantAddr = true
140                         exp = exp[1:]
141                 }
142                 checkVarValue(t, prog, mainPkg, ref, obj, exp, wantAddr)
143         }
144 }
145
146 func checkFuncValue(t *testing.T, prog *ir.Program, obj *types.Func) {
147         fn := prog.FuncValue(obj)
148         // fmt.Printf("FuncValue(%s) = %s\n", obj, fn) // debugging
149         if fn == nil {
150                 if obj.Name() != "interfaceMethod" {
151                         t.Errorf("FuncValue(%s) == nil", obj)
152                 }
153                 return
154         }
155         if fnobj := fn.Object(); fnobj != obj {
156                 t.Errorf("FuncValue(%s).Object() == %s; value was %s",
157                         obj, fnobj, fn.Name())
158                 return
159         }
160         if !types.Identical(fn.Type(), obj.Type()) {
161                 t.Errorf("FuncValue(%s).Type() == %s", obj, fn.Type())
162                 return
163         }
164 }
165
166 func checkConstValue(t *testing.T, prog *ir.Program, obj *types.Const) {
167         c := prog.ConstValue(obj)
168         // fmt.Printf("ConstValue(%s) = %s\n", obj, c) // debugging
169         if c == nil {
170                 t.Errorf("ConstValue(%s) == nil", obj)
171                 return
172         }
173         if !types.Identical(c.Type(), obj.Type()) {
174                 t.Errorf("ConstValue(%s).Type() == %s", obj, c.Type())
175                 return
176         }
177         if obj.Name() != "nil" {
178                 if !constant.Compare(c.Value, token.EQL, obj.Val()) {
179                         t.Errorf("ConstValue(%s).Value (%s) != %s",
180                                 obj, c.Value, obj.Val())
181                         return
182                 }
183         }
184 }
185
186 func checkVarValue(t *testing.T, prog *ir.Program, pkg *ir.Package, ref []ast.Node, obj *types.Var, expKind string, wantAddr bool) {
187         // The prefix of all assertions messages.
188         prefix := fmt.Sprintf("VarValue(%s @ L%d)",
189                 obj, prog.Fset.Position(ref[0].Pos()).Line)
190
191         v, gotAddr := prog.VarValue(obj, pkg, ref)
192
193         // Kind is the concrete type of the ir Value.
194         gotKind := "nil"
195         if v != nil {
196                 gotKind = fmt.Sprintf("%T", v)[len("*ir."):]
197         }
198
199         // fmt.Printf("%s = %v (kind %q; expect %q) wantAddr=%t gotAddr=%t\n", prefix, v, gotKind, expKind, wantAddr, gotAddr) // debugging
200
201         // Check the kinds match.
202         // "nil" indicates expected failure (e.g. optimized away).
203         if expKind != gotKind {
204                 t.Errorf("%s concrete type == %s, want %s", prefix, gotKind, expKind)
205         }
206
207         // Check the types match.
208         // If wantAddr, the expected type is the object's address.
209         if v != nil {
210                 expType := obj.Type()
211                 if wantAddr {
212                         expType = types.NewPointer(expType)
213                         if !gotAddr {
214                                 t.Errorf("%s: got value, want address", prefix)
215                         }
216                 } else if gotAddr {
217                         t.Errorf("%s: got address, want value", prefix)
218                 }
219                 if !types.Identical(v.Type(), expType) {
220                         t.Errorf("%s.Type() == %s, want %s", prefix, v.Type(), expType)
221                 }
222         }
223 }
224
225 // Ensure that, in debug mode, we can determine the ir.Value
226 // corresponding to every ast.Expr.
227 func TestValueForExpr(t *testing.T) {
228         testValueForExpr(t, "testdata/valueforexpr.go")
229 }
230
231 func testValueForExpr(t *testing.T, testfile string) {
232         if runtime.GOOS == "android" {
233                 t.Skipf("no testdata dir on %s", runtime.GOOS)
234         }
235
236         conf := loader.Config{ParserMode: parser.ParseComments}
237         f, err := conf.ParseFile(testfile, nil)
238         if err != nil {
239                 t.Error(err)
240                 return
241         }
242         conf.CreateFromFiles("main", f)
243
244         iprog, err := conf.Load()
245         if err != nil {
246                 t.Error(err)
247                 return
248         }
249
250         mainInfo := iprog.Created[0]
251
252         prog := irutil.CreateProgram(iprog, 0)
253         mainPkg := prog.Package(mainInfo.Pkg)
254         mainPkg.SetDebugMode(true)
255         mainPkg.Build()
256
257         if false {
258                 // debugging
259                 for _, mem := range mainPkg.Members {
260                         if fn, ok := mem.(*ir.Function); ok {
261                                 fn.WriteTo(os.Stderr)
262                         }
263                 }
264         }
265
266         var parenExprs []*ast.ParenExpr
267         ast.Inspect(f, func(n ast.Node) bool {
268                 if n != nil {
269                         if e, ok := n.(*ast.ParenExpr); ok {
270                                 parenExprs = append(parenExprs, e)
271                         }
272                 }
273                 return true
274         })
275
276         notes, err := expect.Extract(prog.Fset, f)
277         if err != nil {
278                 t.Fatal(err)
279         }
280         for _, n := range notes {
281                 want := n.Name
282                 if want == "nil" {
283                         want = "<nil>"
284                 }
285                 position := prog.Fset.Position(n.Pos)
286                 var e ast.Expr
287                 for _, paren := range parenExprs {
288                         if paren.Pos() > n.Pos {
289                                 e = paren.X
290                                 break
291                         }
292                 }
293                 if e == nil {
294                         t.Errorf("%s: note doesn't precede ParenExpr: %q", position, want)
295                         continue
296                 }
297
298                 path, _ := astutil.PathEnclosingInterval(f, n.Pos, n.Pos)
299                 if path == nil {
300                         t.Errorf("%s: can't find AST path from root to comment: %s", position, want)
301                         continue
302                 }
303
304                 fn := ir.EnclosingFunction(mainPkg, path)
305                 if fn == nil {
306                         t.Errorf("%s: can't find enclosing function", position)
307                         continue
308                 }
309
310                 v, gotAddr := fn.ValueForExpr(e) // (may be nil)
311                 got := strings.TrimPrefix(fmt.Sprintf("%T", v), "*ir.")
312                 if got != want {
313                         t.Errorf("%s: got value %q, want %q", position, got, want)
314                 }
315                 if v != nil {
316                         T := v.Type()
317                         if gotAddr {
318                                 T = T.Underlying().(*types.Pointer).Elem() // deref
319                         }
320                         if !types.Identical(T, mainInfo.TypeOf(e)) {
321                                 t.Errorf("%s: got type %s, want %s", position, mainInfo.TypeOf(e), T)
322                         }
323                 }
324         }
325 }
326
327 // findInterval parses input and returns the [start, end) positions of
328 // the first occurrence of substr in input.  f==nil indicates failure;
329 // an error has already been reported in that case.
330 //
331 func findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *ast.File, start, end token.Pos) {
332         f, err := parser.ParseFile(fset, "<input>", input, 0)
333         if err != nil {
334                 t.Errorf("parse error: %s", err)
335                 return
336         }
337
338         i := strings.Index(input, substr)
339         if i < 0 {
340                 t.Errorf("%q is not a substring of input", substr)
341                 f = nil
342                 return
343         }
344
345         filePos := fset.File(f.Package)
346         return f, filePos.Pos(i), filePos.Pos(i + len(substr))
347 }
348
349 func TestEnclosingFunction(t *testing.T) {
350         tests := []struct {
351                 input  string // the input file
352                 substr string // first occurrence of this string denotes interval
353                 fn     string // name of expected containing function
354         }{
355                 // We use distinctive numbers as syntactic landmarks.
356
357                 // Ordinary function:
358                 {`package main
359                   func f() { println(1003) }`,
360                         "100", "main.f"},
361                 // Methods:
362                 {`package main
363                   type T int
364                   func (t T) f() { println(200) }`,
365                         "200", "(main.T).f"},
366                 // Function literal:
367                 {`package main
368                   func f() { println(func() { print(300) }) }`,
369                         "300", "main.f$1"},
370                 // Doubly nested
371                 {`package main
372                   func f() { println(func() { print(func() { print(350) })})}`,
373                         "350", "main.f$1$1"},
374                 // Implicit init for package-level var initializer.
375                 {"package main; var a = 400", "400", "main.init"},
376                 // No code for constants:
377                 {"package main; const a = 500", "500", "(none)"},
378                 // Explicit init()
379                 {"package main; func init() { println(600) }", "600", "main.init#1"},
380                 // Multiple explicit init functions:
381                 {`package main
382                   func init() { println("foo") }
383                   func init() { println(800) }`,
384                         "800", "main.init#2"},
385                 // init() containing FuncLit.
386                 {`package main
387                   func init() { println(func(){print(900)}) }`,
388                         "900", "main.init#1$1"},
389         }
390         for _, test := range tests {
391                 conf := loader.Config{Fset: token.NewFileSet()}
392                 f, start, end := findInterval(t, conf.Fset, test.input, test.substr)
393                 if f == nil {
394                         continue
395                 }
396                 path, exact := astutil.PathEnclosingInterval(f, start, end)
397                 if !exact {
398                         t.Errorf("EnclosingFunction(%q) not exact", test.substr)
399                         continue
400                 }
401
402                 conf.CreateFromFiles("main", f)
403
404                 iprog, err := conf.Load()
405                 if err != nil {
406                         t.Error(err)
407                         continue
408                 }
409                 prog := irutil.CreateProgram(iprog, 0)
410                 pkg := prog.Package(iprog.Created[0].Pkg)
411                 pkg.Build()
412
413                 name := "(none)"
414                 fn := ir.EnclosingFunction(pkg, path)
415                 if fn != nil {
416                         name = fn.String()
417                 }
418
419                 if name != test.fn {
420                         t.Errorf("EnclosingFunction(%q in %q) got %s, want %s",
421                                 test.substr, test.input, name, test.fn)
422                         continue
423                 }
424
425                 // While we're here: test HasEnclosingFunction.
426                 if has := ir.HasEnclosingFunction(pkg, path); has != (fn != nil) {
427                         t.Errorf("HasEnclosingFunction(%q in %q) got %v, want %v",
428                                 test.substr, test.input, has, fn != nil)
429                         continue
430                 }
431         }
432 }