1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// No testdata on Android.
//go:build !android
// +build !android
package irutil
import (
"bytes"
"fmt"
"go/parser"
"path/filepath"
"strings"
"testing"
"honnef.co/go/tools/go/ir"
"golang.org/x/tools/go/analysis/analysistest"
//lint:ignore SA1019 go/loader is deprecated, but works fine for our tests
"golang.org/x/tools/go/loader"
)
func TestSwitches(t *testing.T) {
conf := loader.Config{ParserMode: parser.ParseComments}
f, err := conf.ParseFile(filepath.Join(analysistest.TestData(), "switches.go"), nil)
if err != nil {
t.Error(err)
return
}
conf.CreateFromFiles("main", f)
iprog, err := conf.Load()
if err != nil {
t.Error(err)
return
}
prog := CreateProgram(iprog, 0)
mainPkg := prog.Package(iprog.Created[0].Pkg)
mainPkg.Build()
for _, mem := range mainPkg.Members {
if fn, ok := mem.(*ir.Function); ok {
if fn.Synthetic != 0 {
continue // e.g. init()
}
// Each (multi-line) "switch" comment within
// this function must match the printed form
// of a ConstSwitch.
var wantSwitches []string
for _, c := range f.Comments {
if fn.Source().Pos() <= c.Pos() && c.Pos() < fn.Source().End() {
text := strings.TrimSpace(c.Text())
if strings.HasPrefix(text, "switch ") {
wantSwitches = append(wantSwitches, text)
}
}
}
switches := Switches(fn)
if len(switches) != len(wantSwitches) {
t.Errorf("in %s, found %d switches, want %d", fn, len(switches), len(wantSwitches))
}
for i, sw := range switches {
got := sw.testString()
if i >= len(wantSwitches) {
continue
}
want := wantSwitches[i]
if got != want {
t.Errorf("in %s, found switch %d: got <<%s>>, want <<%s>>", fn, i, got, want)
}
}
}
}
}
func (sw *Switch) testString() string {
// same as the actual String method, but use the second to last
// instruction instead, to skip over all the phi and sigma nodes
// that SSI produces.
var buf bytes.Buffer
if sw.ConstCases != nil {
fmt.Fprintf(&buf, "switch %s {\n", sw.X.Name())
for _, c := range sw.ConstCases {
n := len(c.Body.Instrs) - 2
if n < 0 {
n = 0
}
fmt.Fprintf(&buf, "case %s: %s\n", c.Value.Name(), c.Body.Instrs[n])
}
} else {
fmt.Fprintf(&buf, "switch %s.(type) {\n", sw.X.Name())
for _, c := range sw.TypeCases {
n := len(c.Body.Instrs) - 2
if n < 0 {
n = 0
}
fmt.Fprintf(&buf, "case %s %s: %s\n",
c.Binding.Name(), c.Type, c.Body.Instrs[n])
}
}
if sw.Default != nil {
n := len(sw.Default.Instrs) - 2
if n < 0 {
n = 0
}
fmt.Fprintf(&buf, "default: %s\n", sw.Default.Instrs[n])
}
fmt.Fprintf(&buf, "}")
return buf.String()
}
|