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