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 }