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  }