cuelang.org/go@v0.13.0/cue/parser/parser_test.go (about) 1 // Copyright 2018 The CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package parser 16 17 import ( 18 "fmt" 19 "strings" 20 "testing" 21 22 "cuelang.org/go/cue/ast" 23 "cuelang.org/go/internal/astinternal" 24 ) 25 26 func TestParse(t *testing.T) { 27 testCases := []struct{ desc, in, out string }{{ 28 29 "ellipsis in structs", 30 `#Def: { 31 b: "2" 32 ... 33 } 34 ... 35 36 #Def2: { 37 ... 38 b: "2" 39 } 40 #Def3: {... 41 _} 42 ... 43 `, 44 `#Def: {b: "2", ...}, ..., #Def2: {..., b: "2"}, #Def3: {..., _}, ...`, 45 }, { 46 47 "empty file", "", "", 48 }, { 49 "empty struct", "{}", "{}", 50 }, { 51 "empty structs", "{},{},", "{}, {}", 52 }, { 53 "empty structs; elided comma", "{}\n{}", "{}, {}", 54 }, { 55 "basic lits", `"a","b", 3,3.4,5,2_3`, `"a", "b", 3, 3.4, 5, 2_3`, 56 }, { 57 "keyword basic lits", `true,false,null,for,in,if,let,if`, `true, false, null, for, in, if, let, if`, 58 }, { 59 "keyword basic newline", ` 60 true 61 false 62 null 63 for 64 in 65 if 66 let 67 if 68 `, `true, false, null, for, in, if, let, if`, 69 }, { 70 "keywords as labels", 71 `if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5, func: 6 72 for: if: func: let: 3 73 `, 74 `if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5, func: 6, for: {if: {func: {let: 3}}}`, 75 }, { 76 "keywords as optional labels", 77 `if?: 0, for?: 1, in?: 2, where?: 3, div?: 4, quo?: 5, func?: 6 78 for?: if?: func?: let?: 3 79 `, 80 `if?: 0, for?: 1, in?: 2, where?: 3, div?: 4, quo?: 5, func?: 6, for?: {if?: {func?: {let?: 3}}}`, 81 }, { 82 "keywords as required labels", 83 `if!: 0, for!: 1, in!: 2, where!: 3, div!: 4, quo!: 5, func!: 6 84 for!: if!: func!: let!: 3 85 `, 86 `if!: 0, for!: 1, in!: 2, where!: 3, div!: 4, quo!: 5, func!: 6, for!: {if!: {func!: {let!: 3}}}`, 87 }, { 88 "keywords as alias", 89 `if=foo: 0 90 for=bar: 2 91 let=bar: 3 92 func=baz: 4 93 `, 94 `if=foo: 0, for=bar: 2, let=bar: 3, func=baz: 4`, 95 }, { 96 "keywords as selector", 97 `a : { 98 if: 0 99 for: 1 100 in: 2 101 where: 3 102 div: 4 103 quo: 5 104 func: 6 105 float: 7 106 null: if: func: let: 3 107 }, b: [ 108 a.if, 109 a.for, 110 a.in, 111 a.where, 112 a.div, 113 a.quo, 114 a.func, 115 a.float, 116 a.null.if.func.let, 117 ]`, 118 `a: {if: 0, for: 1, in: 2, where: 3, div: 4, quo: 5, func: 6, float: 7, null: {if: {func: {let: 3}}}}, b: [a.if, a.for, a.in, a.where, a.div, a.quo, a.func, a.float, a.null.if.func.let]`, 119 }, {}, { 120 "json", 121 `{ 122 "a": 1, 123 "b": "2", 124 "c": 3 125 }`, 126 `{"a": 1, "b": "2", "c": 3}`, 127 }, { 128 "json:extra comma", 129 `{ 130 "a": 1, 131 "b": "2", 132 "c": 3, 133 }`, 134 `{"a": 1, "b": "2", "c": 3}`, 135 }, { 136 "json:simplified", 137 `{ 138 a: 1 139 b: "2" 140 c: 3 141 }`, 142 `{a: 1, b: "2", c: 3}`, 143 }, { 144 "attributes", 145 `a: 1 @xml(,attr) 146 b: 2 @foo(a,b=4) @go(Foo) 147 c: { 148 d: "x" @go(D) @json(,omitempty) 149 e: "y" @ts(,type=string,"str") 150 }`, 151 `a: 1 @xml(,attr), b: 2 @foo(a,b=4) @go(Foo), c: {d: "x" @go(D) @json(,omitempty), e: "y" @ts(,type=string,"str")}`, 152 }, { 153 "not emitted", 154 `a: true 155 b?: "2" 156 c?: 3 157 158 d!: 2 159 e: f!: 3 160 161 "g\("en")"?: 4 162 "h\("en")"!: 4 163 `, 164 `a: true, b?: "2", c?: 3, d!: 2, e: {f!: 3}, "g\("en")"?: 4, "h\("en")"!: 4`, 165 }, { 166 "definition", 167 `#Def: { 168 b: "2" 169 c: 3 170 171 embedding 172 } 173 #Def: {} 174 `, 175 `#Def: {b: "2", c: 3, embedding}, #Def: {}`, 176 }, { 177 "one-line embedding", 178 `{ V1, V2 }`, 179 `{V1, V2}`, 180 }, { 181 "selectors", 182 `a.b. "str"`, 183 `a.b."str"`, 184 }, { 185 "selectors", 186 `a.b. "str"`, 187 `a.b."str"`, 188 }, { 189 "faulty bytes selector", 190 `a.b.'str'`, 191 "a.b._\nexpected selector, found 'STRING' 'str'", 192 }, { 193 "faulty multiline string selector", 194 `a.b.""" 195 """`, 196 "a.b._\nexpected selector, found 'STRING' \"\"\"\n\t\t\t\"\"\"", 197 }, { 198 "expression embedding", 199 `#Def: { 200 a.b.c 201 a > b < c 202 -1<2 203 204 foo: 2 205 }`, 206 `#Def: {a.b.c, a>b<c, -1<2, foo: 2}`, 207 }, { 208 "ellipsis in structs", 209 `#Def: { 210 b: "2" 211 ... 212 } 213 ... 214 215 #Def2: { 216 ... 217 b: "2" 218 } 219 #Def3: {... 220 _} 221 ... 222 `, 223 `#Def: {b: "2", ...}, ..., #Def2: {..., b: "2"}, #Def3: {..., _}, ...`, 224 }, { 225 "emitted referencing non-emitted", 226 `a: 1 227 b: "2" 228 c: 3 229 { name: b, total: a + b }`, 230 `a: 1, b: "2", c: 3, {name: b, total: a+b}`, 231 }, { 232 "package file", 233 `package k8s 234 {} 235 `, 236 `package k8s, {}`, 237 }, { 238 "imports group", 239 `package k8s 240 241 import ( 242 a "foo" 243 "bar/baz" 244 ) 245 `, 246 `package k8s, import ( a "foo", "bar/baz" )`, 247 }, { 248 "imports single", 249 `package k8s 250 251 import a "foo" 252 import "bar/baz" 253 `, 254 `package k8s, import a "foo", import "bar/baz"`, 255 }, { 256 "collapsed fields", 257 `a: #b: c?: [Name=_]: d: 1 258 "g\("en")"?: 4 259 // job foo { bar: 1 } // TODO error after foo 260 job: "foo": [_]: { bar: 1 } 261 `, 262 `a: {#b: {c?: {[Name=_]: {d: 1}}}}, "g\("en")"?: 4, job: {"foo": {[_]: {bar: 1}}}`, 263 }, { 264 "identifiers", 265 `// $_: 1, 266 a: {b: {c: d}} 267 c: a 268 d: a.b 269 // e: a."b" // TODO: is an error 270 e: a.b.c 271 "f": f, 272 [X=_]: X 273 `, 274 "a: {b: {c: d}}, c: a, d: a.b, e: a.b.c, \"f\": f, [X=_]: X", 275 }, { 276 "predeclared identifiers", 277 `a: __string 278 __int: 2`, 279 "a: __string, __int: 2\nidentifiers starting with '__' are reserved", 280 }, { 281 "empty fields", 282 ` 283 "": 3 284 `, 285 `"": 3`, 286 }, { 287 "expressions", 288 ` a: (2 + 3) * 5 289 b: (2 + 3) + 4 290 c: 2 + 3 + 4 291 d: -1 292 e: !foo 293 f: _|_ 294 `, 295 "a: (2+3)*5, b: (2+3)+4, c: 2+3+4, d: -1, e: !foo, f: _|_", 296 }, { 297 "pseudo keyword expressions", 298 ` a: (2 div 3) mod 5 299 b: (2 quo 3) rem 4 300 c: 2 div 3 div 4 301 `, 302 "a: (2 div 3) mod 5, b: (2 quo 3) rem 4, c: 2 div 3 div 4", 303 }, { 304 "ranges", 305 ` a: >=1 & <=2 306 b: >2.0 & <= 40.0 307 c: >"a" & <="b" 308 v: (>=1 & <=2) & <=(>=5 & <=10) 309 w: >1 & <=2 & <=3 310 d: >=3T & <=5M 311 `, 312 "a: >=1&<=2, b: >2.0&<=40.0, c: >\"a\"&<=\"b\", v: (>=1&<=2)&<=(>=5&<=10), w: >1&<=2&<=3, d: >=3T&<=5M", 313 }, { 314 "indices", 315 `{ 316 a: b[2] 317 b: c[1:2] 318 c: "asdf" 319 d: c ["a"] 320 }`, 321 `{a: b[2], b: c[1:2], c: "asdf", d: c["a"]}`, 322 }, { 323 "calls", 324 `{ 325 a: b(a.b, c.d) 326 b: a.b(c) 327 }`, 328 `{a: b(a.b, c.d), b: a.b(c)}`, 329 }, { 330 "lists", 331 `{ 332 a: [ 1, 2, 3, b, c, ... ] 333 b: [ 1, 2, 3, ], 334 c: [ 1, 335 2, 336 3 337 ], 338 d: [ 1+2, 2, 4,] 339 }`, 340 `{a: [1, 2, 3, b, c, ...], b: [1, 2, 3], c: [1, 2, 3], d: [1+2, 2, 4]}`, 341 }, { 342 "list types", 343 `{ 344 a: 4*[int] 345 b: <=5*[{a: 5}] 346 c1: [...int] 347 c2: [...] 348 c3: [1, 2, ...int,] 349 }`, 350 `{a: 4*[int], b: <=5*[{a: 5}], c1: [...int], c2: [...], c3: [1, 2, ...int]}`, 351 }, { 352 "list comprehensions", 353 `{ 354 y: [1,2,3] 355 b: [for x in y if x == 1 { x }], 356 }`, 357 `{y: [1, 2, 3], b: [for x in y if x==1 {x}]}`, 358 }, { 359 "field comprehensions", 360 `{ 361 y: { a: 1, b: 2} 362 a: { 363 for k, v in y if v > 2 { 364 "\(k)": v 365 } 366 } 367 }`, 368 `{y: {a: 1, b: 2}, a: {for k: v in y if v>2 {"\(k)": v}}}`, 369 }, { 370 "nested comprehensions", 371 `{ 372 y: { a: 1, b: 2} 373 a: { 374 for k, v in y let x = v+2 if x > 2 { 375 "\(k)": v 376 } 377 } 378 }`, 379 `{y: {a: 1, b: 2}, a: {for k: v in y let x=v+2 if x>2 {"\(k)": v}}}`, 380 }, { 381 "let declaration", 382 `{ 383 let X = 42 384 let Y = "42", 385 let Z = 10 + 12 386 }`, 387 `{let X=42, let Y="42", let Z=10+12}`, 388 }, { 389 "duplicates allowed", 390 `{ 391 a: b: 3 392 a: { b: 3 } 393 }`, 394 "{a: {b: 3}, a: {b: 3}}", 395 }, { 396 "templates", // TODO: remove 397 `{ 398 [foo=_]: { a: int } 399 a: { a: 1 } 400 }`, 401 "{[foo=_]: {a: int}, a: {a: 1}}", 402 }, { 403 "value alias", 404 ` 405 { 406 a: X=foo 407 b: Y={foo} 408 c: d: e: X=5 409 } 410 `, 411 `{a: X=foo, b: Y={foo}, c: {d: {e: X=5}}}`, 412 }, { 413 "dynamic labels", 414 `{ 415 (x): a: int 416 x: "foo" 417 a: { 418 (a.b) 419 } 420 421 (x)?: 1 422 y: (x)!: 2 423 }`, 424 `{(x): {a: int}, x: "foo", a: {(a.b)}, (x)?: 1, y: {(x)!: 2}}`, 425 }, { 426 "foo", 427 `[ 428 [1], 429 [1, 2], 430 [1, 2, 3], 431 ]`, 432 "[[1], [1, 2], [1, 2, 3]]", 433 }, { 434 "interpolation", 435 `a: "foo \(ident)" 436 b: "bar \(bar) $$$ " 437 c: "nest \( { a: "\( nest ) "}.a ) \(5)" 438 m1: """ 439 multi \(bar) 440 """ 441 m2: ''' 442 \(bar) multi 443 '''`, 444 `a: "foo \(ident)", b: "bar \(bar) $$$ ", c: "nest \({a: "\(nest) "}.a) \(5)", ` + "m1: \"\"\"\n\t\t\t multi \\(bar)\n\t\t\t \"\"\", m2: '''\n\t\t\t \\(bar) multi\n\t\t\t '''", 445 }, { 446 "file comments", 447 `// foo 448 449 // uni 450 package foo // uniline 451 452 // file.1 453 // file.2 454 455 `, 456 "<[0// foo] <[d0// uni] [l3// uniline] [3// file.1 // file.2] package foo>>", 457 }, { 458 "line comments", 459 `// doc 460 a: 5 // line 461 b: 6 // lineb 462 // next 463 `, // next is followed by EOF. Ensure it doesn't move to file. 464 "<[d0// doc] [l5// line] a: 5>, " + 465 "<[l5// lineb] [5// next] b: 6>", 466 }, { 467 "alt comments", 468 `// a ... 469 a: 5 // line a 470 471 // about a 472 473 // b ... 474 b: // lineb 475 6 476 477 // about b 478 479 c: 7 480 481 // about c 482 483 // about d 484 d: 485 // about e 486 e: 3 487 `, 488 "<[d0// a ...] [l5// line a] [5// about a] a: 5>, " + 489 "<[d0// b ...] [l2// lineb] [5// about b] b: 6>, " + 490 "<[5// about c] c: 7>, " + 491 "<[d0// about d] d: {<[d0// about e] e>: 3}>", 492 }, { 493 "expr comments", 494 ` 495 a: 2 + // 2 + 496 3 + // 3 + 497 4 // 4 498 `, 499 "<[l5// 4] a: <[l2// 3 +] <[l2// 2 +] 2+3>+4>>", 500 }, { 501 "composit comments", 502 `a : { 503 a: 1, b: 2, c: 3, d: 4 504 // end 505 } 506 b: [ 507 1, 2, 3, 4, 5, 508 // end 509 ] 510 c: [ 1, 2, 3, 4, // here 511 { a: 3 }, // here 512 5, 6, 7, 8 // and here 513 ] 514 d: { 515 a: 1 // Hello 516 // Doc 517 b: 2 518 } 519 e1: [ 520 // comment in list body 521 ] 522 e2: { 523 // comment in struct body 524 } 525 `, 526 "a: {a: 1, b: 2, c: 3, <[d5// end] d: 4>}, " + 527 "b: [1, 2, 3, 4, <[d2// end] 5>], " + 528 "c: [1, 2, 3, <[l2// here] 4>, <[l4// here] {a: 3}>, 5, 6, 7, <[l2// and here] 8>], " + 529 "d: {<[l5// Hello] a: 1>, <[d0// Doc] b: 2>}, " + 530 "e1: <[d1// comment in list body] []>, " + 531 "e2: <[d1// comment in struct body] {}>", 532 }, { 533 "attribute comments", 534 ` 535 a: 1 @a() @b() // d 536 `, 537 `<[l5// d] a: 1 @a() @b()>`, 538 }, { 539 "attribute declarations", 540 ` 541 @foo() 542 543 package bar 544 545 @bar() 546 547 import "strings" 548 549 @baz() 550 `, 551 `@foo(), package bar, @bar(), import "strings", @baz()`, 552 }, { 553 "comprehension comments", 554 ` 555 if X { 556 // Comment 1 557 Field: 2 558 // Comment 2 559 } 560 `, 561 `if X <[d2// Comment 2] {<[d0// Comment 1] Field: 2>}>`, 562 }, { 563 "let comments", 564 `let X = foo // Comment 1`, 565 `<[5// Comment 1] let X=foo>`, 566 }, { 567 "emit comments", 568 `// a comment at the beginning of the file 569 570 // a second comment 571 572 // comment 573 a: 5 574 575 {} 576 577 // a comment at the end of the file 578 `, 579 "<[0// a comment at the beginning of the file] [0// a second comment] <[d0// comment] a: 5>, <[2// a comment at the end of the file] {}>>", 580 }, { 581 "composite comments 2", 582 ` 583 { 584 // foo 585 586 // fooo 587 foo: 1 588 589 bar: 2 590 } 591 592 [ 593 {"name": "value"}, // each element has a long 594 {"name": "next"} // optional next element 595 ] 596 `, 597 `{<[0// foo] [d0// fooo] foo: 1>, bar: 2}, [<[l4// each element has a long] {"name": "value"}>, <[l4// optional next element] {"name": "next"}>]`, 598 }, { 599 desc: "field aliasing", 600 in: ` 601 I="\(k)": v 602 S="foo-bar": w 603 L=foo: x 604 X=[0]: { 605 foo: X | null 606 } 607 [Y=string]: { name: Y } 608 X1=[X2=<"d"]: { name: X2 } 609 Y1=foo: Y2=bar: [Y1, Y2] 610 `, 611 out: `I="\(k)": v, ` + 612 `S="foo-bar": w, ` + 613 `L=foo: x, ` + 614 `X=[0]: {foo: X|null}, ` + 615 `[Y=string]: {name: Y}, ` + 616 `X1=[X2=<"d"]: {name: X2}, ` + 617 `Y1=foo: {Y2=bar: [Y1, Y2]}`, 618 }, { 619 desc: "allow keyword in expression", 620 in: ` 621 foo: in & 2 622 `, 623 out: "foo: in&2", 624 }, { 625 desc: "dot import", 626 in: ` 627 import . "foo" 628 `, 629 out: "import , \"foo\"\nexpected 'STRING', found '.'", 630 }, { 631 desc: "attributes", 632 in: ` 633 package name 634 635 @t1(v1) 636 637 { 638 @t2(v2) 639 } 640 a: { 641 a: 1 642 @t3(v3) 643 @t4(v4) 644 c: 2 645 } 646 `, 647 out: "package name, @t1(v1), {@t2(v2)}, a: {a: 1, @t3(v3), @t4(v4), c: 2}", 648 }, { 649 desc: "Issue #276", 650 in: ` 651 a: int=>2 652 `, 653 out: "a: int=>2", 654 }, { 655 desc: "struct comments", 656 in: ` 657 struct: { 658 // This is a comment 659 660 // This is a comment 661 662 // Another comment 663 something: { 664 } 665 666 // extra comment 667 }`, 668 out: `struct: {<[0// This is a comment] [0// This is a comment] [d0// Another comment] [d5// extra comment] something: {}>}`, 669 }, { 670 desc: "list comments", 671 in: ` 672 list: [ 673 // Comment1 674 675 // Comment2 676 677 // Another comment 678 { 679 }, 680 681 // Comment 3 682 ]`, 683 out: "list: [<[0// Comment1] [0// Comment2] [d0// Another comment] [d3// Comment 3] {}>]", 684 }, { 685 desc: "call comments", 686 in: ` 687 funcArg1: foo( 688 {}, 689 690 // Comment1 691 692 // Comment2 693 {} 694 695 // Comment3 696 )`, 697 out: "funcArg1: foo(<[1// Comment1] {}>, <[d0// Comment2] [d1// Comment3] {}>)", 698 }, { 699 desc: "front-style commas", 700 in: ` 701 frontStyle: { "key": "value" 702 , "key2": "value2" 703 , "foo" : bar 704 } 705 `, 706 out: "frontStyle: {\"key\": \"value\", \"key2\": \"value2\", \"foo\": bar}", 707 }, { 708 desc: "function types", 709 in: ` 710 f0: func(): int 711 f1: func(int): int 712 f2: func(int, string): int 713 f3: func({a: int, b: string}): bool 714 f4: func(bool, func(int, string): int): string 715 f5: func(int, int): func(bool, bool): bool 716 f6: func(func(bool, bool): bool, func(string, string): string): func(int, func(int, string): int): func(int, string): int 717 `, 718 out: "f0: func(): int, f1: func(int): int, f2: func(int, string): int, f3: func({a: int, b: string}): bool, f4: func(bool, func(int, string): int): string, f5: func(int, int): func(bool, bool): bool, f6: func(func(bool, bool): bool, func(string, string): string): func(int, func(int, string): int): func(int, string): int", 719 }} 720 for _, tc := range testCases { 721 t.Run(tc.desc, func(t *testing.T) { 722 mode := []Option{AllErrors} 723 if strings.Contains(tc.desc, "comments") { 724 mode = append(mode, ParseComments) 725 } 726 if strings.Contains(tc.desc, "function") { 727 mode = append(mode, ParseFuncs) 728 } 729 f, err := ParseFile("input", tc.in, mode...) 730 got := astinternal.DebugStr(f) 731 if err != nil { 732 got += "\n" + err.Error() 733 } 734 if got != tc.out { 735 t.Errorf("\ngot %q;\nwant %q", got, tc.out) 736 } 737 }) 738 } 739 } 740 741 func TestStrict(t *testing.T) { 742 testCases := []struct{ desc, in string }{ 743 {"block comments", 744 `a: 1 /* a */`}, 745 {"space separator", 746 `a b c: 2`}, 747 {"reserved identifiers", 748 `__foo: 3`}, 749 {"old-style alias 1", 750 `X=3`}, 751 {"old-style alias 2", 752 `X={}`}, 753 754 // Not yet supported 755 {"additional typed not yet supported", 756 `{...int}`}, 757 } 758 for _, tc := range testCases { 759 t.Run(tc.desc, func(t *testing.T) { 760 mode := []Option{AllErrors, ParseComments, FromVersion(Latest)} 761 _, err := ParseFile("input", tc.in, mode...) 762 if err == nil { 763 t.Errorf("unexpected success: %v", tc.in) 764 } 765 }) 766 } 767 } 768 769 func TestParseExpr(t *testing.T) { 770 // just kicking the tires: 771 // a valid arithmetic expression 772 src := "a + b" 773 x, err := parseExprString(src) 774 if err != nil { 775 t.Errorf("ParseExpr(%q): %v", src, err) 776 } 777 // sanity check 778 if _, ok := x.(*ast.BinaryExpr); !ok { 779 t.Errorf("ParseExpr(%q): got %T, want *BinaryExpr", src, x) 780 } 781 782 // an invalid expression 783 src = "a + *" 784 if _, err := parseExprString(src); err == nil { 785 t.Errorf("ParseExpr(%q): got no error", src) 786 } 787 788 // a comma is not permitted unless automatically inserted 789 src = "a + b\n" 790 if _, err := parseExprString(src); err != nil { 791 t.Errorf("ParseExpr(%q): got error %s", src, err) 792 } 793 src = "a + b;" 794 if _, err := parseExprString(src); err == nil { 795 t.Errorf("ParseExpr(%q): got no error", src) 796 } 797 798 // check resolution 799 src = "{ foo: bar, bar: foo }" 800 x, err = parseExprString(src) 801 if err != nil { 802 t.Fatalf("ParseExpr(%q): %v", src, err) 803 } 804 for _, d := range x.(*ast.StructLit).Elts { 805 v := d.(*ast.Field).Value.(*ast.Ident) 806 if v.Scope == nil { 807 t.Errorf("ParseExpr(%q): scope of field %v not set", src, v.Name) 808 } 809 if v.Node == nil { 810 t.Errorf("ParseExpr(%q): scope of node %v not set", src, v.Name) 811 } 812 } 813 814 // various other stuff following a valid expression 815 const validExpr = "a + b" 816 const anything = "dh3*#D)#_" 817 for _, c := range "!)]};," { 818 src := validExpr + string(c) + anything 819 if _, err := parseExprString(src); err == nil { 820 t.Errorf("ParseExpr(%q): got no error", src) 821 } 822 } 823 824 // ParseExpr must not crash 825 for _, src := range valids { 826 _, _ = parseExprString(src) 827 } 828 } 829 830 func TestImports(t *testing.T) { 831 var imports = map[string]bool{ 832 `"a"`: true, 833 `"a/b"`: true, 834 `"a.b"`: true, 835 `'m\x61th'`: true, 836 `"greek/αβ"`: true, 837 `""`: false, 838 839 // Each of these pairs tests both #""# vs "" strings 840 // and also use of invalid characters spelled out as 841 // escape sequences and written directly. 842 // For example `"\x00"` tests import "\x00" 843 // while "`\x00`" tests import `<actual-NUL-byte>`. 844 `#"a"#`: true, 845 `"\x00"`: false, 846 "'\x00'": false, 847 `"\x7f"`: false, 848 "`\x7f`": false, 849 `"a!"`: false, 850 "#'a!'#": false, 851 `"a b"`: false, 852 `#"a b"#`: false, 853 `"a\\b"`: false, 854 "#\"a\\b\"#": false, 855 "\"`a`\"": false, 856 "#'\"a\"'#": false, 857 `"\x80\x80"`: false, 858 "#'\x80\x80'#": false, 859 `"\xFFFD"`: false, 860 "#'\xFFFD'#": false, 861 } 862 for path, isValid := range imports { 863 t.Run(path, func(t *testing.T) { 864 src := fmt.Sprintf("package p, import %s", path) 865 _, err := ParseFile("", src) 866 switch { 867 case err != nil && isValid: 868 t.Errorf("ParseFile(%s): got %v; expected no error", src, err) 869 case err == nil && !isValid: 870 t.Errorf("ParseFile(%s): got no error; expected one", src) 871 } 872 }) 873 } 874 } 875 876 // TestIncompleteSelection ensures that an incomplete selector 877 // expression is parsed as a (blank) *SelectorExpr, not a 878 // *BadExpr. 879 func TestIncompleteSelection(t *testing.T) { 880 for _, src := range []string{ 881 "{ a: fmt. }", // at end of object 882 "{ a: fmt.\n0.0: x }", // not at end of struct 883 } { 884 t.Run("", func(t *testing.T) { 885 f, err := ParseFile("", src) 886 if err == nil { 887 t.Fatalf("ParseFile(%s) succeeded unexpectedly", src) 888 } 889 890 const wantErr = "expected selector" 891 if !strings.Contains(err.Error(), wantErr) { 892 t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr) 893 } 894 895 var sel *ast.SelectorExpr 896 ast.Walk(f, func(n ast.Node) bool { 897 if n, ok := n.(*ast.SelectorExpr); ok { 898 sel = n 899 } 900 return true 901 }, nil) 902 if sel == nil { 903 t.Fatalf("found no *SelectorExpr: %#v %s", f.Decls[0], astinternal.DebugStr(f)) 904 } 905 const wantSel = "&{fmt _ {<nil>} {{}}}" 906 if fmt.Sprint(sel) != wantSel { 907 t.Fatalf("found selector %v, want %s", sel, wantSel) 908 } 909 }) 910 } 911 } 912 913 // For debugging, do not delete. 914 func TestX(t *testing.T) { 915 t.Skip() 916 917 f, err := ParseFile("input", ` 918 `, ParseComments) 919 if err != nil { 920 t.Errorf("unexpected error: %v", err) 921 } 922 t.Error(astinternal.DebugStr(f)) 923 }