github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/syntax/nodes_test.go (about)

     1  // Copyright 2017 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  package syntax
     6  
     7  import (
     8  	"fmt"
     9  	"strings"
    10  	"testing"
    11  )
    12  
    13  // A test is a source code snippet of a particular node type.
    14  // In the snippet, a '@' indicates the position recorded by
    15  // the parser when creating the respective node.
    16  type test struct {
    17  	nodetyp string
    18  	snippet string
    19  }
    20  
    21  var decls = []test{
    22  	// The position of declarations is always the
    23  	// position of the first token of an individual
    24  	// declaration, independent of grouping.
    25  	{"ImportDecl", `import @"math"`},
    26  	{"ImportDecl", `import @mymath "math"`},
    27  	{"ImportDecl", `import @. "math"`},
    28  	{"ImportDecl", `import (@"math")`},
    29  	{"ImportDecl", `import (@mymath "math")`},
    30  	{"ImportDecl", `import (@. "math")`},
    31  
    32  	{"ConstDecl", `const @x`},
    33  	{"ConstDecl", `const @x = 0`},
    34  	{"ConstDecl", `const @x, y, z = 0, 1, 2`},
    35  	{"ConstDecl", `const (@x)`},
    36  	{"ConstDecl", `const (@x = 0)`},
    37  	{"ConstDecl", `const (@x, y, z = 0, 1, 2)`},
    38  
    39  	{"TypeDecl", `type @T int`},
    40  	{"TypeDecl", `type @T = int`},
    41  	{"TypeDecl", `type (@T int)`},
    42  	{"TypeDecl", `type (@T = int)`},
    43  
    44  	{"VarDecl", `var @x int`},
    45  	{"VarDecl", `var @x, y, z int`},
    46  	{"VarDecl", `var @x int = 0`},
    47  	{"VarDecl", `var @x, y, z int = 1, 2, 3`},
    48  	{"VarDecl", `var @x = 0`},
    49  	{"VarDecl", `var @x, y, z = 1, 2, 3`},
    50  	{"VarDecl", `var (@x int)`},
    51  	{"VarDecl", `var (@x, y, z int)`},
    52  	{"VarDecl", `var (@x int = 0)`},
    53  	{"VarDecl", `var (@x, y, z int = 1, 2, 3)`},
    54  	{"VarDecl", `var (@x = 0)`},
    55  	{"VarDecl", `var (@x, y, z = 1, 2, 3)`},
    56  
    57  	{"FuncDecl", `func @f() {}`},
    58  	{"FuncDecl", `func @(T) f() {}`},
    59  	{"FuncDecl", `func @(x T) f() {}`},
    60  }
    61  
    62  var exprs = []test{
    63  	// The position of an expression is the position
    64  	// of the left-most token that identifies the
    65  	// kind of expression.
    66  	{"Name", `@x`},
    67  
    68  	{"BasicLit", `@0`},
    69  	{"BasicLit", `@0x123`},
    70  	{"BasicLit", `@3.1415`},
    71  	{"BasicLit", `@.2718`},
    72  	{"BasicLit", `@1i`},
    73  	{"BasicLit", `@'a'`},
    74  	{"BasicLit", `@"abc"`},
    75  	{"BasicLit", "@`abc`"},
    76  
    77  	{"CompositeLit", `@{}`},
    78  	{"CompositeLit", `T@{}`},
    79  	{"CompositeLit", `struct{x, y int}@{}`},
    80  
    81  	{"KeyValueExpr", `"foo"@: true`},
    82  	{"KeyValueExpr", `"a"@: b`},
    83  
    84  	{"FuncLit", `@func (){}`},
    85  	{"ParenExpr", `@(x)`},
    86  	{"SelectorExpr", `a@.b`},
    87  	{"IndexExpr", `a@[i]`},
    88  
    89  	{"SliceExpr", `a@[:]`},
    90  	{"SliceExpr", `a@[i:]`},
    91  	{"SliceExpr", `a@[:j]`},
    92  	{"SliceExpr", `a@[i:j]`},
    93  	{"SliceExpr", `a@[i:j:k]`},
    94  
    95  	{"AssertExpr", `x@.(T)`},
    96  
    97  	{"Operation", `@*b`},
    98  	{"Operation", `@+b`},
    99  	{"Operation", `@-b`},
   100  	{"Operation", `@!b`},
   101  	{"Operation", `@^b`},
   102  	{"Operation", `@&b`},
   103  	{"Operation", `@<-b`},
   104  
   105  	{"Operation", `a @|| b`},
   106  	{"Operation", `a @&& b`},
   107  	{"Operation", `a @== b`},
   108  	{"Operation", `a @+ b`},
   109  	{"Operation", `a @* b`},
   110  
   111  	{"CallExpr", `f@()`},
   112  	{"CallExpr", `f@(x, y, z)`},
   113  	{"CallExpr", `obj.f@(1, 2, 3)`},
   114  	{"CallExpr", `func(x int) int { return x + 1 }@(y)`},
   115  
   116  	// ListExpr: tested via multi-value const/var declarations
   117  }
   118  
   119  var types = []test{
   120  	{"Operation", `@*T`},
   121  	{"Operation", `@*struct{}`},
   122  
   123  	{"ArrayType", `@[10]T`},
   124  	{"ArrayType", `@[...]T`},
   125  
   126  	{"SliceType", `@[]T`},
   127  	{"DotsType", `@...T`},
   128  	{"StructType", `@struct{}`},
   129  	{"InterfaceType", `@interface{}`},
   130  	{"FuncType", `func@()`},
   131  	{"MapType", `@map[T]T`},
   132  
   133  	{"ChanType", `@chan T`},
   134  	{"ChanType", `@chan<- T`},
   135  	{"ChanType", `@<-chan T`},
   136  }
   137  
   138  var fields = []test{
   139  	{"Field", `@T`},
   140  	{"Field", `@(T)`},
   141  	{"Field", `@x T`},
   142  	{"Field", `@x *(T)`},
   143  	{"Field", `@x, y, z T`},
   144  	{"Field", `@x, y, z (*T)`},
   145  }
   146  
   147  var stmts = []test{
   148  	{"EmptyStmt", `@`},
   149  
   150  	{"LabeledStmt", `L@:`},
   151  	{"LabeledStmt", `L@: ;`},
   152  	{"LabeledStmt", `L@: f()`},
   153  
   154  	{"BlockStmt", `@{}`},
   155  
   156  	// The position of an ExprStmt is the position of the expression.
   157  	{"ExprStmt", `@<-ch`},
   158  	{"ExprStmt", `f@()`},
   159  	{"ExprStmt", `append@(s, 1, 2, 3)`},
   160  
   161  	{"SendStmt", `ch @<- x`},
   162  
   163  	{"DeclStmt", `@const x = 0`},
   164  	{"DeclStmt", `@const (x = 0)`},
   165  	{"DeclStmt", `@type T int`},
   166  	{"DeclStmt", `@type T = int`},
   167  	{"DeclStmt", `@type (T1 = int; T2 = float32)`},
   168  	{"DeclStmt", `@var x = 0`},
   169  	{"DeclStmt", `@var x, y, z int`},
   170  	{"DeclStmt", `@var (a, b = 1, 2)`},
   171  
   172  	{"AssignStmt", `x @= y`},
   173  	{"AssignStmt", `a, b, x @= 1, 2, 3`},
   174  	{"AssignStmt", `x @+= y`},
   175  	{"AssignStmt", `x @:= y`},
   176  	{"AssignStmt", `x, ok @:= f()`},
   177  	{"AssignStmt", `x@++`},
   178  	{"AssignStmt", `a[i]@--`},
   179  
   180  	{"BranchStmt", `@break`},
   181  	{"BranchStmt", `@break L`},
   182  	{"BranchStmt", `@continue`},
   183  	{"BranchStmt", `@continue L`},
   184  	{"BranchStmt", `@fallthrough`},
   185  	{"BranchStmt", `@goto L`},
   186  
   187  	{"CallStmt", `@defer f()`},
   188  	{"CallStmt", `@go f()`},
   189  
   190  	{"ReturnStmt", `@return`},
   191  	{"ReturnStmt", `@return x`},
   192  	{"ReturnStmt", `@return a, b, a + b*f(1, 2, 3)`},
   193  
   194  	{"IfStmt", `@if cond {}`},
   195  	{"IfStmt", `@if cond { f() } else {}`},
   196  	{"IfStmt", `@if cond { f() } else { g(); h() }`},
   197  	{"ForStmt", `@for {}`},
   198  	{"ForStmt", `@for { f() }`},
   199  	{"SwitchStmt", `@switch {}`},
   200  	{"SwitchStmt", `@switch { default: }`},
   201  	{"SwitchStmt", `@switch { default: x++ }`},
   202  	{"SelectStmt", `@select {}`},
   203  	{"SelectStmt", `@select { default: }`},
   204  	{"SelectStmt", `@select { default: ch <- false }`},
   205  }
   206  
   207  var ranges = []test{
   208  	{"RangeClause", `@range s`},
   209  	{"RangeClause", `i = @range s`},
   210  	{"RangeClause", `i := @range s`},
   211  	{"RangeClause", `_, x = @range s`},
   212  	{"RangeClause", `i, x = @range s`},
   213  	{"RangeClause", `_, x := @range s.f`},
   214  	{"RangeClause", `i, x := @range f(i)`},
   215  }
   216  
   217  var guards = []test{
   218  	{"TypeSwitchGuard", `x@.(type)`},
   219  	{"TypeSwitchGuard", `x := x@.(type)`},
   220  }
   221  
   222  var cases = []test{
   223  	{"CaseClause", `@case x:`},
   224  	{"CaseClause", `@case x, y, z:`},
   225  	{"CaseClause", `@case x == 1, y == 2:`},
   226  	{"CaseClause", `@default:`},
   227  }
   228  
   229  var comms = []test{
   230  	{"CommClause", `@case <-ch:`},
   231  	{"CommClause", `@case x <- ch:`},
   232  	{"CommClause", `@case x = <-ch:`},
   233  	{"CommClause", `@case x := <-ch:`},
   234  	{"CommClause", `@case x, ok = <-ch: f(1, 2, 3)`},
   235  	{"CommClause", `@case x, ok := <-ch: x++`},
   236  	{"CommClause", `@default:`},
   237  	{"CommClause", `@default: ch <- true`},
   238  }
   239  
   240  func TestPos(t *testing.T) {
   241  	// TODO(gri) Once we have a general tree walker, we can use that to find
   242  	// the first occurrence of the respective node and we don't need to hand-
   243  	// extract the node for each specific kind of construct.
   244  
   245  	testPos(t, decls, "package p; ", "",
   246  		func(f *File) Node { return f.DeclList[0] },
   247  	)
   248  
   249  	// embed expressions in a composite literal so we can test key:value and naked composite literals
   250  	testPos(t, exprs, "package p; var _ = T{ ", " }",
   251  		func(f *File) Node { return f.DeclList[0].(*VarDecl).Values.(*CompositeLit).ElemList[0] },
   252  	)
   253  
   254  	// embed types in a function  signature so we can test ... types
   255  	testPos(t, types, "package p; func f(", ")",
   256  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0].Type },
   257  	)
   258  
   259  	testPos(t, fields, "package p; func f(", ")",
   260  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0] },
   261  	)
   262  
   263  	testPos(t, stmts, "package p; func _() { ", "; }",
   264  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0] },
   265  	)
   266  
   267  	testPos(t, ranges, "package p; func _() { for ", " {} }",
   268  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*ForStmt).Init.(*RangeClause) },
   269  	)
   270  
   271  	testPos(t, guards, "package p; func _() { switch ", " {} }",
   272  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*SwitchStmt).Tag.(*TypeSwitchGuard) },
   273  	)
   274  
   275  	testPos(t, cases, "package p; func _() { switch { ", " } }",
   276  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*SwitchStmt).Body[0] },
   277  	)
   278  
   279  	testPos(t, comms, "package p; func _() { select { ", " } }",
   280  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*SelectStmt).Body[0] },
   281  	)
   282  }
   283  
   284  func testPos(t *testing.T, list []test, prefix, suffix string, extract func(*File) Node) {
   285  	for _, test := range list {
   286  		// complete source, compute @ position, and strip @ from source
   287  		src, index := stripAt(prefix + test.snippet + suffix)
   288  		if index < 0 {
   289  			t.Errorf("missing @: %s (%s)", src, test.nodetyp)
   290  			continue
   291  		}
   292  
   293  		// build syntax tree
   294  		file, err := Parse(nil, strings.NewReader(src), nil, nil, 0)
   295  		if err != nil {
   296  			t.Errorf("parse error: %s: %v (%s)", src, err, test.nodetyp)
   297  			continue
   298  		}
   299  
   300  		// extract desired node
   301  		node := extract(file)
   302  		if typ := typeOf(node); typ != test.nodetyp {
   303  			t.Errorf("type error: %s: type = %s, want %s", src, typ, test.nodetyp)
   304  			continue
   305  		}
   306  
   307  		// verify node position with expected position as indicated by @
   308  		if pos := int(node.Pos().Col()); pos != index+colbase {
   309  			t.Errorf("pos error: %s: pos = %d, want %d (%s)", src, pos, index+colbase, test.nodetyp)
   310  			continue
   311  		}
   312  	}
   313  }
   314  
   315  func stripAt(s string) (string, int) {
   316  	if i := strings.Index(s, "@"); i >= 0 {
   317  		return s[:i] + s[i+1:], i
   318  	}
   319  	return s, -1
   320  }
   321  
   322  func typeOf(n Node) string {
   323  	const prefix = "*syntax."
   324  	k := fmt.Sprintf("%T", n)
   325  	return strings.TrimPrefix(k, prefix)
   326  }