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