diff options
Diffstat (limited to 'src/cmd/compile/internal/syntax/nodes_test.go')
-rw-r--r-- | src/cmd/compile/internal/syntax/nodes_test.go | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/syntax/nodes_test.go b/src/cmd/compile/internal/syntax/nodes_test.go new file mode 100644 index 0000000..a39f08c --- /dev/null +++ b/src/cmd/compile/internal/syntax/nodes_test.go @@ -0,0 +1,329 @@ +// Copyright 2017 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. + +package syntax + +import ( + "fmt" + "strings" + "testing" +) + +// A test is a source code snippet of a particular node type. +// In the snippet, a '@' indicates the position recorded by +// the parser when creating the respective node. +type test struct { + nodetyp string + snippet string +} + +var decls = []test{ + // The position of declarations is always the + // position of the first token of an individual + // declaration, independent of grouping. + {"ImportDecl", `import @"math"`}, + {"ImportDecl", `import @mymath "math"`}, + {"ImportDecl", `import @. "math"`}, + {"ImportDecl", `import (@"math")`}, + {"ImportDecl", `import (@mymath "math")`}, + {"ImportDecl", `import (@. "math")`}, + + {"ConstDecl", `const @x`}, + {"ConstDecl", `const @x = 0`}, + {"ConstDecl", `const @x, y, z = 0, 1, 2`}, + {"ConstDecl", `const (@x)`}, + {"ConstDecl", `const (@x = 0)`}, + {"ConstDecl", `const (@x, y, z = 0, 1, 2)`}, + + {"TypeDecl", `type @T int`}, + {"TypeDecl", `type @T = int`}, + {"TypeDecl", `type (@T int)`}, + {"TypeDecl", `type (@T = int)`}, + + {"VarDecl", `var @x int`}, + {"VarDecl", `var @x, y, z int`}, + {"VarDecl", `var @x int = 0`}, + {"VarDecl", `var @x, y, z int = 1, 2, 3`}, + {"VarDecl", `var @x = 0`}, + {"VarDecl", `var @x, y, z = 1, 2, 3`}, + {"VarDecl", `var (@x int)`}, + {"VarDecl", `var (@x, y, z int)`}, + {"VarDecl", `var (@x int = 0)`}, + {"VarDecl", `var (@x, y, z int = 1, 2, 3)`}, + {"VarDecl", `var (@x = 0)`}, + {"VarDecl", `var (@x, y, z = 1, 2, 3)`}, + + {"FuncDecl", `func @f() {}`}, + {"FuncDecl", `func @(T) f() {}`}, + {"FuncDecl", `func @(x T) f() {}`}, +} + +var exprs = []test{ + // The position of an expression is the position + // of the left-most token that identifies the + // kind of expression. + {"Name", `@x`}, + + {"BasicLit", `@0`}, + {"BasicLit", `@0x123`}, + {"BasicLit", `@3.1415`}, + {"BasicLit", `@.2718`}, + {"BasicLit", `@1i`}, + {"BasicLit", `@'a'`}, + {"BasicLit", `@"abc"`}, + {"BasicLit", "@`abc`"}, + + {"CompositeLit", `@{}`}, + {"CompositeLit", `T@{}`}, + {"CompositeLit", `struct{x, y int}@{}`}, + + {"KeyValueExpr", `"foo"@: true`}, + {"KeyValueExpr", `"a"@: b`}, + + {"FuncLit", `@func (){}`}, + {"ParenExpr", `@(x)`}, + {"SelectorExpr", `a@.b`}, + {"IndexExpr", `a@[i]`}, + + {"SliceExpr", `a@[:]`}, + {"SliceExpr", `a@[i:]`}, + {"SliceExpr", `a@[:j]`}, + {"SliceExpr", `a@[i:j]`}, + {"SliceExpr", `a@[i:j:k]`}, + + {"AssertExpr", `x@.(T)`}, + + {"Operation", `@*b`}, + {"Operation", `@+b`}, + {"Operation", `@-b`}, + {"Operation", `@!b`}, + {"Operation", `@^b`}, + {"Operation", `@&b`}, + {"Operation", `@<-b`}, + + {"Operation", `a @|| b`}, + {"Operation", `a @&& b`}, + {"Operation", `a @== b`}, + {"Operation", `a @+ b`}, + {"Operation", `a @* b`}, + + {"CallExpr", `f@()`}, + {"CallExpr", `f@(x, y, z)`}, + {"CallExpr", `obj.f@(1, 2, 3)`}, + {"CallExpr", `func(x int) int { return x + 1 }@(y)`}, + + // ListExpr: tested via multi-value const/var declarations +} + +var types = []test{ + {"Operation", `@*T`}, + {"Operation", `@*struct{}`}, + + {"ArrayType", `@[10]T`}, + {"ArrayType", `@[...]T`}, + + {"SliceType", `@[]T`}, + {"DotsType", `@...T`}, + {"StructType", `@struct{}`}, + {"InterfaceType", `@interface{}`}, + {"FuncType", `func@()`}, + {"MapType", `@map[T]T`}, + + {"ChanType", `@chan T`}, + {"ChanType", `@chan<- T`}, + {"ChanType", `@<-chan T`}, +} + +var fields = []test{ + {"Field", `@T`}, + {"Field", `@(T)`}, + {"Field", `@x T`}, + {"Field", `@x *(T)`}, + {"Field", `@x, y, z T`}, + {"Field", `@x, y, z (*T)`}, +} + +var stmts = []test{ + {"EmptyStmt", `@`}, + + {"LabeledStmt", `L@:`}, + {"LabeledStmt", `L@: ;`}, + {"LabeledStmt", `L@: f()`}, + + {"BlockStmt", `@{}`}, + + // The position of an ExprStmt is the position of the expression. + {"ExprStmt", `@<-ch`}, + {"ExprStmt", `f@()`}, + {"ExprStmt", `append@(s, 1, 2, 3)`}, + + {"SendStmt", `ch @<- x`}, + + {"DeclStmt", `@const x = 0`}, + {"DeclStmt", `@const (x = 0)`}, + {"DeclStmt", `@type T int`}, + {"DeclStmt", `@type T = int`}, + {"DeclStmt", `@type (T1 = int; T2 = float32)`}, + {"DeclStmt", `@var x = 0`}, + {"DeclStmt", `@var x, y, z int`}, + {"DeclStmt", `@var (a, b = 1, 2)`}, + + {"AssignStmt", `x @= y`}, + {"AssignStmt", `a, b, x @= 1, 2, 3`}, + {"AssignStmt", `x @+= y`}, + {"AssignStmt", `x @:= y`}, + {"AssignStmt", `x, ok @:= f()`}, + {"AssignStmt", `x@++`}, + {"AssignStmt", `a[i]@--`}, + + {"BranchStmt", `@break`}, + {"BranchStmt", `@break L`}, + {"BranchStmt", `@continue`}, + {"BranchStmt", `@continue L`}, + {"BranchStmt", `@fallthrough`}, + {"BranchStmt", `@goto L`}, + + {"CallStmt", `@defer f()`}, + {"CallStmt", `@go f()`}, + + {"ReturnStmt", `@return`}, + {"ReturnStmt", `@return x`}, + {"ReturnStmt", `@return a, b, a + b*f(1, 2, 3)`}, + + {"IfStmt", `@if cond {}`}, + {"IfStmt", `@if cond { f() } else {}`}, + {"IfStmt", `@if cond { f() } else { g(); h() }`}, + {"ForStmt", `@for {}`}, + {"ForStmt", `@for { f() }`}, + {"SwitchStmt", `@switch {}`}, + {"SwitchStmt", `@switch { default: }`}, + {"SwitchStmt", `@switch { default: x++ }`}, + {"SelectStmt", `@select {}`}, + {"SelectStmt", `@select { default: }`}, + {"SelectStmt", `@select { default: ch <- false }`}, +} + +var ranges = []test{ + {"RangeClause", `@range s`}, + {"RangeClause", `i = @range s`}, + {"RangeClause", `i := @range s`}, + {"RangeClause", `_, x = @range s`}, + {"RangeClause", `i, x = @range s`}, + {"RangeClause", `_, x := @range s.f`}, + {"RangeClause", `i, x := @range f(i)`}, +} + +var guards = []test{ + {"TypeSwitchGuard", `x@.(type)`}, + {"TypeSwitchGuard", `x := x@.(type)`}, +} + +var cases = []test{ + {"CaseClause", `@case x:`}, + {"CaseClause", `@case x, y, z:`}, + {"CaseClause", `@case x == 1, y == 2:`}, + {"CaseClause", `@default:`}, +} + +var comms = []test{ + {"CommClause", `@case <-ch:`}, + {"CommClause", `@case x <- ch:`}, + {"CommClause", `@case x = <-ch:`}, + {"CommClause", `@case x := <-ch:`}, + {"CommClause", `@case x, ok = <-ch: f(1, 2, 3)`}, + {"CommClause", `@case x, ok := <-ch: x++`}, + {"CommClause", `@default:`}, + {"CommClause", `@default: ch <- true`}, +} + +func TestPos(t *testing.T) { + // TODO(gri) Once we have a general tree walker, we can use that to find + // the first occurrence of the respective node and we don't need to hand- + // extract the node for each specific kind of construct. + + testPos(t, decls, "package p; ", "", + func(f *File) Node { return f.DeclList[0] }, + ) + + // embed expressions in a composite literal so we can test key:value and naked composite literals + testPos(t, exprs, "package p; var _ = T{ ", " }", + func(f *File) Node { return f.DeclList[0].(*VarDecl).Values.(*CompositeLit).ElemList[0] }, + ) + + // embed types in a function signature so we can test ... types + testPos(t, types, "package p; func f(", ")", + func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0].Type }, + ) + + testPos(t, fields, "package p; func f(", ")", + func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0] }, + ) + + testPos(t, stmts, "package p; func _() { ", "; }", + func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0] }, + ) + + testPos(t, ranges, "package p; func _() { for ", " {} }", + func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*ForStmt).Init.(*RangeClause) }, + ) + + testPos(t, guards, "package p; func _() { switch ", " {} }", + func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*SwitchStmt).Tag.(*TypeSwitchGuard) }, + ) + + testPos(t, cases, "package p; func _() { switch { ", " } }", + func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*SwitchStmt).Body[0] }, + ) + + testPos(t, comms, "package p; func _() { select { ", " } }", + func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body.List[0].(*SelectStmt).Body[0] }, + ) +} + +func testPos(t *testing.T, list []test, prefix, suffix string, extract func(*File) Node) { + for _, test := range list { + // complete source, compute @ position, and strip @ from source + src, index := stripAt(prefix + test.snippet + suffix) + if index < 0 { + t.Errorf("missing @: %s (%s)", src, test.nodetyp) + continue + } + + // build syntax tree + file, err := Parse(nil, strings.NewReader(src), nil, nil, 0) + if err != nil { + t.Errorf("parse error: %s: %v (%s)", src, err, test.nodetyp) + continue + } + + // extract desired node + node := extract(file) + if typ := typeOf(node); typ != test.nodetyp { + t.Errorf("type error: %s: type = %s, want %s", src, typ, test.nodetyp) + continue + } + + // verify node position with expected position as indicated by @ + if pos := int(node.Pos().Col()); pos != index+colbase { + t.Errorf("pos error: %s: pos = %d, want %d (%s)", src, pos, index+colbase, test.nodetyp) + continue + } + } +} + +func stripAt(s string) (string, int) { + if i := strings.Index(s, "@"); i >= 0 { + return s[:i] + s[i+1:], i + } + return s, -1 +} + +func typeOf(n Node) string { + const prefix = "*syntax." + k := fmt.Sprintf("%T", n) + if strings.HasPrefix(k, prefix) { + return k[len(prefix):] + } + return k +} |