Home | History | Annotate | Download | only in syntax
      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 syntaxt tree
    294 		file, err := ParseBytes(nil, []byte(src), nil, 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 	if strings.HasPrefix(k, prefix) {
    326 		return k[len(prefix):]
    327 	}
    328 	return k
    329 }
    330