.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / go / pointer / pointer_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 // No testdata on Android.
6
7 //go:build !android
8 // +build !android
9
10 package pointer_test
11
12 // This test uses 'expectation' comments embedded within testdata/*.go
13 // files to specify the expected pointer analysis behaviour.
14 // See below for grammar.
15
16 import (
17         "bytes"
18         "errors"
19         "fmt"
20         "go/token"
21         "go/types"
22         "io/ioutil"
23         "os"
24         "regexp"
25         "strconv"
26         "strings"
27         "testing"
28
29         "golang.org/x/tools/go/callgraph"
30         "golang.org/x/tools/go/loader"
31         "golang.org/x/tools/go/pointer"
32         "golang.org/x/tools/go/ssa"
33         "golang.org/x/tools/go/ssa/ssautil"
34         "golang.org/x/tools/go/types/typeutil"
35 )
36
37 var inputs = []string{
38         "testdata/a_test.go",
39         "testdata/another.go",
40         "testdata/arrayreflect.go",
41         "testdata/arrays.go",
42         "testdata/channels.go",
43         "testdata/chanreflect.go",
44         "testdata/context.go",
45         "testdata/conv.go",
46         "testdata/extended.go",
47         "testdata/finalizer.go",
48         "testdata/flow.go",
49         "testdata/fmtexcerpt.go",
50         "testdata/func.go",
51         "testdata/funcreflect.go",
52         "testdata/hello.go", // NB: causes spurious failure of HVN cross-check
53         "testdata/interfaces.go",
54         "testdata/issue9002.go",
55         "testdata/mapreflect.go",
56         "testdata/maps.go",
57         "testdata/panic.go",
58         "testdata/recur.go",
59         "testdata/reflect.go",
60         "testdata/rtti.go",
61         "testdata/structreflect.go",
62         "testdata/structs.go",
63         // "testdata/timer.go", // TODO(adonovan): fix broken assumptions about runtime timers
64 }
65
66 // Expectation grammar:
67 //
68 // @calls f -> g
69 //
70 //   A 'calls' expectation asserts that edge (f, g) appears in the
71 //   callgraph.  f and g are notated as per Function.String(), which
72 //   may contain spaces (e.g. promoted method in anon struct).
73 //
74 // @pointsto a | b | c
75 //
76 //   A 'pointsto' expectation asserts that the points-to set of its
77 //   operand contains exactly the set of labels {a,b,c} notated as per
78 //   labelString.
79 //
80 //   A 'pointsto' expectation must appear on the same line as a
81 //   print(x) statement; the expectation's operand is x.
82 //
83 //   If one of the strings is "...", the expectation asserts that the
84 //   points-to set at least the other labels.
85 //
86 //   We use '|' because label names may contain spaces, e.g.  methods
87 //   of anonymous structs.
88 //
89 //   From a theoretical perspective, concrete types in interfaces are
90 //   labels too, but they are represented differently and so have a
91 //   different expectation, @types, below.
92 //
93 // @types t | u | v
94 //
95 //   A 'types' expectation asserts that the set of possible dynamic
96 //   types of its interface operand is exactly {t,u,v}, notated per
97 //   go/types.Type.String(). In other words, it asserts that the type
98 //   component of the interface may point to that set of concrete type
99 //   literals.  It also works for reflect.Value, though the types
100 //   needn't be concrete in that case.
101 //
102 //   A 'types' expectation must appear on the same line as a
103 //   print(x) statement; the expectation's operand is x.
104 //
105 //   If one of the strings is "...", the expectation asserts that the
106 //   interface's type may point to at least the other types.
107 //
108 //   We use '|' because type names may contain spaces.
109 //
110 // @warning "regexp"
111 //
112 //   A 'warning' expectation asserts that the analysis issues a
113 //   warning that matches the regular expression within the string
114 //   literal.
115 //
116 // @line id
117 //
118 //   A line directive associates the name "id" with the current
119 //   file:line.  The string form of labels will use this id instead of
120 //   a file:line, making @pointsto expectations more robust against
121 //   perturbations in the source file.
122 //   (NB, anon functions still include line numbers.)
123 //
124 type expectation struct {
125         kind     string // "pointsto" | "pointstoquery" | "types" | "calls" | "warning"
126         filename string
127         linenum  int // source line number, 1-based
128         args     []string
129         query    string           // extended query
130         extended *pointer.Pointer // extended query pointer
131         types    []types.Type     // for types
132 }
133
134 func (e *expectation) String() string {
135         return fmt.Sprintf("@%s[%s]", e.kind, strings.Join(e.args, " | "))
136 }
137
138 func (e *expectation) errorf(format string, args ...interface{}) {
139         fmt.Printf("%s:%d: ", e.filename, e.linenum)
140         fmt.Printf(format, args...)
141         fmt.Println()
142 }
143
144 func (e *expectation) needsProbe() bool {
145         return e.kind == "pointsto" || e.kind == "pointstoquery" || e.kind == "types"
146 }
147
148 // Find probe (call to print(x)) of same source file/line as expectation.
149 func findProbe(prog *ssa.Program, probes map[*ssa.CallCommon]bool, queries map[ssa.Value]pointer.Pointer, e *expectation) (site *ssa.CallCommon, pts pointer.PointsToSet) {
150         for call := range probes {
151                 pos := prog.Fset.Position(call.Pos())
152                 if pos.Line == e.linenum && pos.Filename == e.filename {
153                         // TODO(adonovan): send this to test log (display only on failure).
154                         // fmt.Printf("%s:%d: info: found probe for %s: %s\n",
155                         //      e.filename, e.linenum, e, p.arg0) // debugging
156                         return call, queries[call.Args[0]].PointsTo()
157                 }
158         }
159         return // e.g. analysis didn't reach this call
160 }
161
162 func doOneInput(input, filename string) bool {
163         var conf loader.Config
164
165         // Parsing.
166         f, err := conf.ParseFile(filename, input)
167         if err != nil {
168                 fmt.Println(err)
169                 return false
170         }
171
172         // Create single-file main package and import its dependencies.
173         conf.CreateFromFiles("main", f)
174         iprog, err := conf.Load()
175         if err != nil {
176                 fmt.Println(err)
177                 return false
178         }
179         mainPkgInfo := iprog.Created[0].Pkg
180
181         // SSA creation + building.
182         prog := ssautil.CreateProgram(iprog, ssa.SanityCheckFunctions)
183         prog.Build()
184
185         mainpkg := prog.Package(mainPkgInfo)
186         ptrmain := mainpkg // main package for the pointer analysis
187         if mainpkg.Func("main") == nil {
188                 // No main function; assume it's a test.
189                 ptrmain = prog.CreateTestMainPackage(mainpkg)
190         }
191
192         // Find all calls to the built-in print(x).  Analytically,
193         // print is a no-op, but it's a convenient hook for testing
194         // the PTS of an expression, so our tests use it.
195         probes := make(map[*ssa.CallCommon]bool)
196         for fn := range ssautil.AllFunctions(prog) {
197                 if fn.Pkg == mainpkg {
198                         for _, b := range fn.Blocks {
199                                 for _, instr := range b.Instrs {
200                                         if instr, ok := instr.(ssa.CallInstruction); ok {
201                                                 call := instr.Common()
202                                                 if b, ok := call.Value.(*ssa.Builtin); ok && b.Name() == "print" && len(call.Args) == 1 {
203                                                         probes[instr.Common()] = true
204                                                 }
205                                         }
206                                 }
207                         }
208                 }
209         }
210
211         ok := true
212
213         lineMapping := make(map[string]string) // maps "file:line" to @line tag
214
215         // Parse expectations in this input.
216         var exps []*expectation
217         re := regexp.MustCompile("// *@([a-z]*) *(.*)$")
218         lines := strings.Split(input, "\n")
219         for linenum, line := range lines {
220                 linenum++ // make it 1-based
221                 if matches := re.FindAllStringSubmatch(line, -1); matches != nil {
222                         match := matches[0]
223                         kind, rest := match[1], match[2]
224                         e := &expectation{kind: kind, filename: filename, linenum: linenum}
225
226                         if kind == "line" {
227                                 if rest == "" {
228                                         ok = false
229                                         e.errorf("@%s expectation requires identifier", kind)
230                                 } else {
231                                         lineMapping[fmt.Sprintf("%s:%d", filename, linenum)] = rest
232                                 }
233                                 continue
234                         }
235
236                         if e.needsProbe() && !strings.Contains(line, "print(") {
237                                 ok = false
238                                 e.errorf("@%s expectation must follow call to print(x)", kind)
239                                 continue
240                         }
241
242                         switch kind {
243                         case "pointsto":
244                                 e.args = split(rest, "|")
245
246                         case "pointstoquery":
247                                 args := strings.SplitN(rest, " ", 2)
248                                 e.query = args[0]
249                                 e.args = split(args[1], "|")
250                         case "types":
251                                 for _, typstr := range split(rest, "|") {
252                                         var t types.Type = types.Typ[types.Invalid] // means "..."
253                                         if typstr != "..." {
254                                                 tv, err := types.Eval(prog.Fset, mainpkg.Pkg, f.Pos(), typstr)
255                                                 if err != nil {
256                                                         ok = false
257                                                         // Don't print err since its location is bad.
258                                                         e.errorf("'%s' is not a valid type: %s", typstr, err)
259                                                         continue
260                                                 }
261                                                 t = tv.Type
262                                         }
263                                         e.types = append(e.types, t)
264                                 }
265
266                         case "calls":
267                                 e.args = split(rest, "->")
268                                 // TODO(adonovan): eagerly reject the
269                                 // expectation if fn doesn't denote
270                                 // existing function, rather than fail
271                                 // the expectation after analysis.
272                                 if len(e.args) != 2 {
273                                         ok = false
274                                         e.errorf("@calls expectation wants 'caller -> callee' arguments")
275                                         continue
276                                 }
277
278                         case "warning":
279                                 lit, err := strconv.Unquote(strings.TrimSpace(rest))
280                                 if err != nil {
281                                         ok = false
282                                         e.errorf("couldn't parse @warning operand: %s", err.Error())
283                                         continue
284                                 }
285                                 e.args = append(e.args, lit)
286
287                         default:
288                                 ok = false
289                                 e.errorf("unknown expectation kind: %s", e)
290                                 continue
291                         }
292                         exps = append(exps, e)
293                 }
294         }
295
296         var log bytes.Buffer
297         fmt.Fprintf(&log, "Input: %s\n", filename)
298
299         // Run the analysis.
300         config := &pointer.Config{
301                 Reflection:     true,
302                 BuildCallGraph: true,
303                 Mains:          []*ssa.Package{ptrmain},
304                 Log:            &log,
305         }
306 probeLoop:
307         for probe := range probes {
308                 v := probe.Args[0]
309                 pos := prog.Fset.Position(probe.Pos())
310                 for _, e := range exps {
311                         if e.linenum == pos.Line && e.filename == pos.Filename && e.kind == "pointstoquery" {
312                                 var err error
313                                 e.extended, err = config.AddExtendedQuery(v, e.query)
314                                 if err != nil {
315                                         panic(err)
316                                 }
317                                 continue probeLoop
318                         }
319                 }
320                 if pointer.CanPoint(v.Type()) {
321                         config.AddQuery(v)
322                 }
323         }
324
325         // Print the log is there was an error or a panic.
326         complete := false
327         defer func() {
328                 if !complete || !ok {
329                         log.WriteTo(os.Stderr)
330                 }
331         }()
332
333         result, err := pointer.Analyze(config)
334         if err != nil {
335                 panic(err) // internal error in pointer analysis
336         }
337
338         // Check the expectations.
339         for _, e := range exps {
340                 var call *ssa.CallCommon
341                 var pts pointer.PointsToSet
342                 var tProbe types.Type
343                 if e.needsProbe() {
344                         if call, pts = findProbe(prog, probes, result.Queries, e); call == nil {
345                                 ok = false
346                                 e.errorf("unreachable print() statement has expectation %s", e)
347                                 continue
348                         }
349                         if e.extended != nil {
350                                 pts = e.extended.PointsTo()
351                         }
352                         tProbe = call.Args[0].Type()
353                         if !pointer.CanPoint(tProbe) {
354                                 ok = false
355                                 e.errorf("expectation on non-pointerlike operand: %s", tProbe)
356                                 continue
357                         }
358                 }
359
360                 switch e.kind {
361                 case "pointsto", "pointstoquery":
362                         if !checkPointsToExpectation(e, pts, lineMapping, prog) {
363                                 ok = false
364                         }
365
366                 case "types":
367                         if !checkTypesExpectation(e, pts, tProbe) {
368                                 ok = false
369                         }
370
371                 case "calls":
372                         if !checkCallsExpectation(prog, e, result.CallGraph) {
373                                 ok = false
374                         }
375
376                 case "warning":
377                         if !checkWarningExpectation(prog, e, result.Warnings) {
378                                 ok = false
379                         }
380                 }
381         }
382
383         complete = true
384
385         // ok = false // debugging: uncomment to always see log
386
387         return ok
388 }
389
390 func labelString(l *pointer.Label, lineMapping map[string]string, prog *ssa.Program) string {
391         // Functions and Globals need no pos suffix,
392         // nor do allocations in intrinsic operations
393         // (for which we'll print the function name).
394         switch l.Value().(type) {
395         case nil, *ssa.Function, *ssa.Global:
396                 return l.String()
397         }
398
399         str := l.String()
400         if pos := l.Pos(); pos != token.NoPos {
401                 // Append the position, using a @line tag instead of a line number, if defined.
402                 posn := prog.Fset.Position(pos)
403                 s := fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
404                 if tag, ok := lineMapping[s]; ok {
405                         return fmt.Sprintf("%s@%s:%d", str, tag, posn.Column)
406                 }
407                 str = fmt.Sprintf("%s@%s", str, posn)
408         }
409         return str
410 }
411
412 func checkPointsToExpectation(e *expectation, pts pointer.PointsToSet, lineMapping map[string]string, prog *ssa.Program) bool {
413         expected := make(map[string]int)
414         surplus := make(map[string]int)
415         exact := true
416         for _, g := range e.args {
417                 if g == "..." {
418                         exact = false
419                         continue
420                 }
421                 expected[g]++
422         }
423         // Find the set of labels that the probe's
424         // argument (x in print(x)) may point to.
425         for _, label := range pts.Labels() {
426                 name := labelString(label, lineMapping, prog)
427                 if expected[name] > 0 {
428                         expected[name]--
429                 } else if exact {
430                         surplus[name]++
431                 }
432         }
433         // Report multiset difference:
434         ok := true
435         for _, count := range expected {
436                 if count > 0 {
437                         ok = false
438                         e.errorf("value does not alias these expected labels: %s", join(expected))
439                         break
440                 }
441         }
442         for _, count := range surplus {
443                 if count > 0 {
444                         ok = false
445                         e.errorf("value may additionally alias these labels: %s", join(surplus))
446                         break
447                 }
448         }
449         return ok
450 }
451
452 func checkTypesExpectation(e *expectation, pts pointer.PointsToSet, typ types.Type) bool {
453         var expected typeutil.Map
454         var surplus typeutil.Map
455         exact := true
456         for _, g := range e.types {
457                 if g == types.Typ[types.Invalid] {
458                         exact = false
459                         continue
460                 }
461                 expected.Set(g, struct{}{})
462         }
463
464         if !pointer.CanHaveDynamicTypes(typ) {
465                 e.errorf("@types expectation requires an interface- or reflect.Value-typed operand, got %s", typ)
466                 return false
467         }
468
469         // Find the set of types that the probe's
470         // argument (x in print(x)) may contain.
471         for _, T := range pts.DynamicTypes().Keys() {
472                 if expected.At(T) != nil {
473                         expected.Delete(T)
474                 } else if exact {
475                         surplus.Set(T, struct{}{})
476                 }
477         }
478         // Report set difference:
479         ok := true
480         if expected.Len() > 0 {
481                 ok = false
482                 e.errorf("interface cannot contain these types: %s", expected.KeysString())
483         }
484         if surplus.Len() > 0 {
485                 ok = false
486                 e.errorf("interface may additionally contain these types: %s", surplus.KeysString())
487         }
488         return ok
489 }
490
491 var errOK = errors.New("OK")
492
493 func checkCallsExpectation(prog *ssa.Program, e *expectation, cg *callgraph.Graph) bool {
494         found := make(map[string]int)
495         err := callgraph.GraphVisitEdges(cg, func(edge *callgraph.Edge) error {
496                 // Name-based matching is inefficient but it allows us to
497                 // match functions whose names that would not appear in an
498                 // index ("<root>") or which are not unique ("func@1.2").
499                 if edge.Caller.Func.String() == e.args[0] {
500                         calleeStr := edge.Callee.Func.String()
501                         if calleeStr == e.args[1] {
502                                 return errOK // expectation satisfied; stop the search
503                         }
504                         found[calleeStr]++
505                 }
506                 return nil
507         })
508         if err == errOK {
509                 return true
510         }
511         if len(found) == 0 {
512                 e.errorf("didn't find any calls from %s", e.args[0])
513         }
514         e.errorf("found no call from %s to %s, but only to %s",
515                 e.args[0], e.args[1], join(found))
516         return false
517 }
518
519 func checkWarningExpectation(prog *ssa.Program, e *expectation, warnings []pointer.Warning) bool {
520         // TODO(adonovan): check the position part of the warning too?
521         re, err := regexp.Compile(e.args[0])
522         if err != nil {
523                 e.errorf("invalid regular expression in @warning expectation: %s", err.Error())
524                 return false
525         }
526
527         if len(warnings) == 0 {
528                 e.errorf("@warning %q expectation, but no warnings", e.args[0])
529                 return false
530         }
531
532         for _, w := range warnings {
533                 if re.MatchString(w.Message) {
534                         return true
535                 }
536         }
537
538         e.errorf("@warning %q expectation not satisfied; found these warnings though:", e.args[0])
539         for _, w := range warnings {
540                 fmt.Printf("%s: warning: %s\n", prog.Fset.Position(w.Pos), w.Message)
541         }
542         return false
543 }
544
545 func TestInput(t *testing.T) {
546         if testing.Short() {
547                 t.Skip("skipping in short mode; this test requires tons of memory; https://golang.org/issue/14113")
548         }
549         ok := true
550
551         wd, err := os.Getwd()
552         if err != nil {
553                 t.Errorf("os.Getwd: %s", err)
554                 return
555         }
556
557         // 'go test' does a chdir so that relative paths in
558         // diagnostics no longer make sense relative to the invoking
559         // shell's cwd.  We print a special marker so that Emacs can
560         // make sense of them.
561         fmt.Fprintf(os.Stderr, "Entering directory `%s'\n", wd)
562
563         for _, filename := range inputs {
564                 content, err := ioutil.ReadFile(filename)
565                 if err != nil {
566                         t.Errorf("couldn't read file '%s': %s", filename, err)
567                         continue
568                 }
569
570                 if !doOneInput(string(content), filename) {
571                         ok = false
572                 }
573         }
574         if !ok {
575                 t.Fail()
576         }
577 }
578
579 // join joins the elements of multiset with " | "s.
580 func join(set map[string]int) string {
581         var buf bytes.Buffer
582         sep := ""
583         for name, count := range set {
584                 for i := 0; i < count; i++ {
585                         buf.WriteString(sep)
586                         sep = " | "
587                         buf.WriteString(name)
588                 }
589         }
590         return buf.String()
591 }
592
593 // split returns the list of sep-delimited non-empty strings in s.
594 func split(s, sep string) (r []string) {
595         for _, elem := range strings.Split(s, sep) {
596                 elem = strings.TrimSpace(elem)
597                 if elem != "" {
598                         r = append(r, elem)
599                 }
600         }
601         return
602 }