github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/cmd/compile/internal/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, c`},
   193  
   194  	{"IfStmt", `@if cond {}`},
   195  	{"ForStmt", `@for {}`},
   196  	{"SwitchStmt", `@switch {}`},
   197  	{"SelectStmt", `@select {}`},
   198  }
   199  
   200  var ranges = []test{
   201  	{"RangeClause", `for @range s {}`},
   202  	{"RangeClause", `for _, i = @range s {}`},
   203  	{"RangeClause", `for x, i = @range s {}`},
   204  	{"RangeClause", `for _, i := @range s {}`},
   205  	{"RangeClause", `for x, i := @range s {}`},
   206  }
   207  
   208  var guards = []test{
   209  	{"TypeSwitchGuard", `switch x@.(type) {}`},
   210  	{"TypeSwitchGuard", `switch x := x@.(type) {}`},
   211  	{"TypeSwitchGuard", `switch a = b; x@.(type) {}`},
   212  	{"TypeSwitchGuard", `switch a := b; x := x@.(type) {}`},
   213  }
   214  
   215  var cases = []test{
   216  	{"CaseClause", ` switch { @case x: }`},
   217  	{"CaseClause", ` switch { @case x, y, z: }`},
   218  	{"CaseClause", ` switch { @case x == 1, y == 2: }`},
   219  	{"CaseClause", ` switch { @default: }`},
   220  }
   221  
   222  var comms = []test{
   223  	{"CommClause", `select { @case <-ch: }`},
   224  	{"CommClause", `select { @case x <- ch: }`},
   225  	{"CommClause", `select { @case x = <-ch: }`},
   226  	{"CommClause", `select { @case x := <-ch: }`},
   227  	{"CommClause", `select { @case x, ok = <-ch: }`},
   228  	{"CommClause", `select { @case x, ok := <-ch: }`},
   229  	{"CommClause", `select { @default: }`},
   230  }
   231  
   232  func TestPos(t *testing.T) {
   233  	// TODO(gri) Once we have a general tree walker, we can use that to find
   234  	// the first occurence of the respective node and we don't need to hand-
   235  	// extract the node for each specific kind of construct.
   236  
   237  	testPos(t, decls, "package p; ", "",
   238  		func(f *File) Node { return f.DeclList[0] },
   239  	)
   240  
   241  	// embed expressions in a composite literal so we can test key:value and naked composite literals
   242  	testPos(t, exprs, "package p; var _ = T{ ", " }",
   243  		func(f *File) Node { return f.DeclList[0].(*VarDecl).Values.(*CompositeLit).ElemList[0] },
   244  	)
   245  
   246  	// embed types in a function  signature so we can test ... types
   247  	testPos(t, types, "package p; func f(", ")",
   248  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0].Type },
   249  	)
   250  
   251  	testPos(t, fields, "package p; func f(", ")",
   252  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0] },
   253  	)
   254  
   255  	testPos(t, stmts, "package p; func _() { ", " } ",
   256  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0] },
   257  	)
   258  
   259  	testPos(t, ranges, "package p; func _() { ", " } ",
   260  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*ForStmt).Init.(*RangeClause) },
   261  	)
   262  
   263  	testPos(t, guards, "package p; func _() { ", " } ",
   264  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*SwitchStmt).Tag.(*TypeSwitchGuard) },
   265  	)
   266  
   267  	testPos(t, cases, "package p; func _() { ", " } ",
   268  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*SwitchStmt).Body[0] },
   269  	)
   270  
   271  	testPos(t, comms, "package p; func _() { ", " } ",
   272  		func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*SelectStmt).Body[0] },
   273  	)
   274  }
   275  
   276  func testPos(t *testing.T, list []test, prefix, suffix string, extract func(*File) Node) {
   277  	for _, test := range list {
   278  		// complete source, compute @ position, and strip @ from source
   279  		src, index := stripAt(prefix + test.snippet + suffix)
   280  		if index < 0 {
   281  			t.Errorf("missing @: %s", src)
   282  			continue
   283  		}
   284  
   285  		// build syntaxt tree
   286  		file, err := ParseBytes(nil, []byte(src), nil, nil, 0)
   287  		if err != nil {
   288  			t.Errorf("parse error: %s: %v", src, err)
   289  			continue
   290  		}
   291  
   292  		// extract desired node
   293  		node := extract(file)
   294  		if typ := typeOf(node); typ != test.nodetyp {
   295  			t.Errorf("type error: %s: type = %s, want %s", src, typ, test.nodetyp)
   296  			continue
   297  		}
   298  
   299  		// verify node position with expected position as indicated by @
   300  		if col := int(node.Pos().Col()); col != index {
   301  			t.Errorf("pos error: %s: col = %d, want %d", src, col, index)
   302  			continue
   303  		}
   304  	}
   305  }
   306  
   307  func stripAt(s string) (string, int) {
   308  	if i := strings.Index(s, "@"); i >= 0 {
   309  		return s[:i] + s[i+1:], i
   310  	}
   311  	return s, -1
   312  }
   313  
   314  func typeOf(n Node) string {
   315  	const prefix = "*syntax."
   316  	k := fmt.Sprintf("%T", n)
   317  	if strings.HasPrefix(k, prefix) {
   318  		return k[len(prefix):]
   319  	}
   320  	return k
   321  }