github.com/gotranspile/cxgo@v0.3.8-0.20240118201721-29871598a6a2/parse_test.go (about) 1 package cxgo 2 3 import ( 4 "bytes" 5 "runtime/debug" 6 "strings" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 "modernc.org/cc/v3" 12 13 "github.com/gotranspile/cxgo/libs" 14 "github.com/gotranspile/cxgo/types" 15 ) 16 17 const testPkg = "lib" 18 19 type configFunc func(c *Config) 20 type envFunc func(c *types.Config) 21 22 type parseCase struct { 23 name string 24 inc string 25 src string 26 exp string 27 skipExp string 28 skip bool 29 builtins bool 30 configFuncs []configFunc 31 envFuncs []envFunc 32 } 33 34 func (c parseCase) shouldSkip() bool { 35 return c.skip || c.skipExp != "" 36 } 37 38 func withIdent(ic IdentConfig) configFunc { 39 return func(c *Config) { 40 c.Idents = append(c.Idents, ic) 41 } 42 } 43 44 func withIdentField(name string, f IdentConfig) configFunc { 45 return withIdent(IdentConfig{Name: name, Fields: []IdentConfig{f}}) 46 } 47 48 func withAlias(name string) configFunc { 49 return func(c *Config) { 50 c.Idents = append(c.Idents, IdentConfig{Name: name, Alias: true}) 51 } 52 } 53 54 func withRename(from, to string) configFunc { 55 return func(c *Config) { 56 c.Idents = append(c.Idents, IdentConfig{Name: from, Rename: to}) 57 } 58 } 59 60 func withDoNotEdit(val bool) configFunc { 61 return func(c *Config) { 62 c.DoNotEdit = val 63 } 64 } 65 66 var casesTranslate = []parseCase{ 67 { 68 name: "empty", 69 src: " ", 70 }, 71 { 72 name: "push defines", 73 src: ` 74 #define BLAH 10 75 76 int v1 = BLAH; 77 78 #pragma push_macro("BLAH") 79 #undef BLAH 80 #define BLAH 5 81 82 int v2 = BLAH; 83 84 #pragma pop_macro("BLAH") 85 86 int v3 = BLAH; 87 88 #pragma push_macro("NON_EXISTENT") 89 #pragma pop_macro("NON_EXISTENT") 90 `, 91 exp: ` 92 const BLAH = 10 93 const BLAH = 5 94 95 var v1 int32 = BLAH 96 var v2 int32 = BLAH 97 var v3 int32 = BLAH 98 `, 99 skipExp: ` 100 const BLAH = 10 101 102 var v1 int32 = BLAH 103 var v2 int32 = BLAH 104 var v3 int32 = BLAH 105 `, 106 }, 107 { 108 name: "switch", 109 src: ` 110 void foo(int a) { 111 switch (a) { 112 case 1: 113 foo(1); 114 break; 115 case 2: 116 foo(2); 117 default: 118 foo(0); 119 case 3: 120 foo(3); 121 break; 122 case 4: 123 case 5: 124 foo(5); 125 return; 126 case 6: 127 foo(6); 128 } 129 } 130 `, 131 exp: ` 132 func foo(a int32) { 133 switch a { 134 case 1: 135 foo(1) 136 case 2: 137 foo(2) 138 fallthrough 139 default: 140 foo(0) 141 fallthrough 142 case 3: 143 foo(3) 144 case 4: 145 fallthrough 146 case 5: 147 foo(5) 148 return 149 case 6: 150 foo(6) 151 } 152 } 153 `, 154 }, 155 { 156 skip: true, 157 name: "switch cases everywhere", 158 src: ` 159 void foo(int p, char s) { 160 switch (p) { 161 case 0: 162 if (s == 'a') { 163 case 1: 164 s = 'c'; 165 } 166 break; 167 } 168 } 169 `, 170 exp: ` 171 func foo(p int32, s int8) { 172 switch p { 173 case 1: 174 goto case_1 175 case 0: 176 if s == 'a' { 177 case_1: 178 s = 'c' 179 } 180 } 181 } 182 `, 183 }, 184 { 185 skip: true, 186 name: "switch cases everywhere 2", 187 src: ` 188 void foo(int p, char s) { 189 switch (p) { 190 if (s) { 191 case 0: 192 if (s == 'a') { 193 case 1: 194 s = 'c'; 195 } 196 break; 197 } 198 case 2: 199 s = 'd'; 200 break; 201 } 202 } 203 `, 204 exp: ` 205 func foo(p int32, s int8) { 206 switch { 207 case s != 0 && p == 1: 208 goto case_1 209 case s != 0 && p == 0: 210 if s == 'a' { 211 case_1: 212 s = 'c' 213 } 214 case p == 2 215 s = 'd' 216 } 217 } 218 `, 219 }, 220 { 221 name: "revert last if", 222 skip: !optimizeStatements, 223 src: ` 224 int foo(int* a, int* b) { 225 if (a) { 226 foo(a); 227 foo(a); 228 foo(a); 229 } 230 foo(b); 231 return 1; 232 } 233 `, 234 exp: ` 235 func foo(a *int32, b *int32) int32 { 236 if a == nil { 237 foo(b) 238 return 1 239 } 240 foo(a) 241 foo(a) 242 foo(a) 243 foo(b) 244 return 1 245 } 246 `, 247 }, 248 { 249 name: "revert last if goto", 250 skip: !optimizeStatements, 251 src: ` 252 int foo(int* a, int* b) { 253 if (a) { 254 foo(a); 255 foo(a); 256 foo(a); 257 } 258 LABEL_X: 259 foo(b); 260 return 1; 261 } 262 `, 263 exp: ` 264 func foo(a *int32, b *int32) int32 { 265 if a == nil { 266 foo(b) 267 return 1 268 } 269 foo(a) 270 foo(a) 271 foo(a) 272 foo(b) 273 return 1 274 } 275 `, 276 }, 277 { 278 name: "revert last if void", 279 skip: !optimizeStatements, 280 src: ` 281 void foo(int* a) { 282 if (a) { 283 foo(a); 284 foo(a); 285 foo(a); 286 } 287 } 288 `, 289 exp: ` 290 func foo(a *int32) { 291 if a == nil { 292 return 293 } 294 foo(a) 295 foo(a) 296 foo(a) 297 } 298 `, 299 }, 300 { 301 name: "move return to if", 302 skip: !optimizeStatements, 303 src: ` 304 int foo(int* a) { 305 if (a) { 306 foo(a); 307 } else { 308 foo(a); 309 } 310 return 1; 311 } 312 `, 313 exp: ` 314 func foo(a *int32) int32 { 315 if a != nil { 316 foo(a) 317 return 1 318 } 319 foo(a) 320 return 1 321 } 322 `, 323 }, 324 { 325 name: "move return to if nested", 326 skip: !optimizeStatements, 327 src: ` 328 int foo(int a) { 329 if (a == 1) { 330 foo(2); 331 if (a == 3) { 332 foo(4); 333 } else if (a == 5) { 334 foo(6); 335 } 336 } 337 return 1; 338 } 339 `, 340 exp: ` 341 func foo(a int32) int32 { 342 if a != 1 { 343 return 1 344 } 345 foo(2) 346 if a == 3 { 347 foo(4) 348 return 1 349 } else if a == 5 { 350 foo(6) 351 return 1 352 } 353 return 1 354 } 355 `, 356 }, 357 { 358 name: "revert last if cost", 359 skip: !optimizeStatements, 360 src: ` 361 int foo(int* a) { 362 if (a) { 363 foo(a); 364 foo(a); 365 if (a) { 366 foo(a); 367 } 368 foo(a); 369 return 1; 370 } 371 foo(a); 372 foo(a); 373 foo(a); 374 return 0; 375 } 376 `, 377 exp: ` 378 func foo(a *int32) int32 { 379 if a == nil { 380 foo(a) 381 foo(a) 382 foo(a) 383 return 0 384 } 385 foo(a) 386 foo(a) 387 if a != nil { 388 foo(a) 389 } 390 foo(a) 391 return 1 392 } 393 `, 394 }, 395 { 396 name: "sub_40BC10", 397 inc: `typedef unsigned int _DWORD;`, 398 src: ` 399 unsigned char blob[10]; 400 char* foo(int a) { 401 return (char*)(*(_DWORD*)& blob[3] + 160 * a); 402 } 403 `, 404 exp: ` 405 var blob [10]uint8 406 407 func foo(a int32) *byte { 408 return (*byte)(unsafe.Pointer(uintptr(*(*_DWORD)(unsafe.Pointer(&blob[3])) + _DWORD(a*160)))) 409 } 410 `, 411 }, 412 { 413 name: "inline gotos", 414 skip: !optimizeStatements, 415 src: ` 416 int foo(int* a) { 417 if (a) { 418 foo(a); 419 LABEL_2: 420 foo(a); 421 if (a) { 422 foo(a); 423 goto LABEL_1; 424 } 425 LABEL_1: 426 foo(a); 427 return 1; 428 } 429 foo(a); 430 foo(a); 431 goto LABEL_2; 432 } 433 `, 434 exp: ` 435 func foo(a *int32) int32 { 436 if a == nil { 437 foo(a) 438 foo(a) 439 goto LABEL_2 440 } 441 foo(a) 442 LABEL_2: 443 foo(a) 444 if a == nil { 445 foo(a) 446 return 1 447 } 448 foo(a) 449 foo(a) 450 return 1 451 } 452 `, 453 }, 454 { 455 name: "inline gotos chain", 456 skip: !optimizeStatements, 457 src: ` 458 int foo(int* a) { 459 if (a) { 460 foo(a); 461 foo(a); 462 if (a) { 463 LABEL_2: 464 foo(a); 465 goto LABEL_1; 466 } 467 LABEL_1: 468 foo(a); 469 return 1; 470 } 471 foo(a); 472 foo(a); 473 goto LABEL_2; 474 } 475 `, 476 exp: ` 477 func foo(a *int32) int32 { 478 if a == nil { 479 foo(a) 480 foo(a) 481 foo(a) 482 foo(a) 483 return 1 484 } 485 foo(a) 486 foo(a) 487 if a == nil { 488 foo(a) 489 return 1 490 } 491 foo(a) 492 foo(a) 493 return 1 494 } 495 `, 496 }, 497 { 498 name: "blocks with vars", 499 src: ` 500 #define set(s) \ 501 { int t = s;\ 502 } 503 504 void main() { 505 int s; 506 if (0) { 507 set(s) 508 set(s) 509 set(s) 510 } 511 } 512 `, 513 exp: ` 514 func main() { 515 var s int32 516 if false { 517 { 518 var t int32 = s 519 _ = t 520 } 521 { 522 var t int32 = s 523 _ = t 524 } 525 { 526 var t int32 = s 527 _ = t 528 } 529 } 530 } 531 `, 532 }, 533 { 534 name: "blocks no vars", 535 src: ` 536 #define set(s) \ 537 { t = s;\ 538 } 539 540 void main() { 541 int s, t; 542 if (0) { 543 set(s) 544 set(s) 545 set(s) 546 } 547 } 548 `, 549 exp: ` 550 func main() { 551 var ( 552 s int32 553 t int32 554 ) 555 _ = t 556 if false { 557 t = s 558 t = s 559 t = s 560 } 561 } 562 `, 563 }, 564 { 565 name: "sizeof only", 566 builtins: true, 567 src: ` 568 int a = sizeof(int); 569 `, 570 exp: ` 571 var a int32 = int32(unsafe.Sizeof(int32(0))) 572 `, 573 }, 574 { 575 name: "sizeof", 576 builtins: true, 577 src: ` 578 void foo() { 579 size_t a; 580 int b[10]; 581 unsigned char b2[10]; 582 void* c; 583 int* d; 584 int (*e)(void); 585 a = sizeof(b); 586 a = sizeof(b2); 587 a = sizeof(c); 588 a = sizeof(d); 589 a = sizeof(e); 590 } 591 `, 592 exp: ` 593 func foo() { 594 var a size_t 595 _ = a 596 var b [10]int32 597 _ = b 598 var b2 [10]uint8 599 _ = b2 600 var c unsafe.Pointer 601 _ = c 602 var d *int32 603 _ = d 604 var e func() int32 605 _ = e 606 a = size_t(unsafe.Sizeof([10]int32{})) 607 a = size_t(10) 608 a = size_t(unsafe.Sizeof(unsafe.Pointer(nil))) 609 a = size_t(unsafe.Sizeof((*int32)(nil))) 610 a = size_t(unsafe.Sizeof(uintptr(0))) 611 } 612 `, 613 }, 614 { 615 name: "assign expr", 616 src: ` 617 void foo() { 618 int a; 619 int b; 620 b = a = 1; 621 } 622 `, 623 exp: ` 624 func foo() { 625 var ( 626 a int32 627 b int32 628 ) 629 _ = b 630 b = func() int32 { 631 a = 1 632 return a 633 }() 634 } 635 `, 636 }, 637 { 638 name: "stdint override", 639 src: ` 640 #include <stdint.h> 641 642 void foo() { 643 int16_t a1; 644 uint32_t a2; 645 } 646 `, 647 exp: ` 648 func foo() { 649 var a1 int16 650 _ = a1 651 var a2 uint32 652 _ = a2 653 } 654 `, 655 }, 656 { 657 name: "void cast", 658 src: ` 659 void foo(int a, int* b) { 660 int c; 661 (void)a; 662 (void)b; 663 (void)c; 664 } 665 `, 666 exp: ` 667 func foo(a int32, b *int32) { 668 var c int32 669 _ = a 670 _ = b 671 _ = c 672 } 673 `, 674 }, 675 { 676 name: "assign ternary", 677 src: ` 678 void foo(int a) { 679 a = a ? 1 : 0; 680 a = -(a ? 1 : 0); 681 int b = a ? 1 : 0; 682 } 683 `, 684 exp: ` 685 func foo(a int32) { 686 if a != 0 { 687 a = 1 688 } else { 689 a = 0 690 } 691 if a != 0 { 692 a = -1 693 } else { 694 a = 0 695 } 696 var b int32 697 _ = b 698 if a != 0 { 699 b = 1 700 } else { 701 b = 0 702 } 703 } 704 `, 705 }, 706 { 707 name: "return ternary", 708 src: ` 709 int foo(int a) { 710 return a ? 1 : 0; 711 } 712 `, 713 exp: ` 714 func foo(a int32) int32 { 715 if a != 0 { 716 return 1 717 } 718 return 0 719 } 720 `, 721 }, 722 { 723 skip: true, 724 name: "multiple assignments", 725 src: ` 726 void foo(int a) { 727 int b, c; 728 c = b = a; 729 c = b = a++; 730 c = b = ++a; 731 } 732 `, 733 exp: ` 734 func foo(a int32) { 735 var b, c int32 736 b, c = a, a 737 b, c = a, a 738 a++ 739 a++ 740 b, c = a, a 741 } 742 `, 743 }, 744 { 745 skip: true, 746 name: "if init 1", 747 src: ` 748 void foo(int a) { 749 if (a = 1, a) { 750 a = 0; 751 } 752 } 753 `, 754 exp: ` 755 func foo(a int32) { 756 if a = 1; a != 0 { 757 a = 0 758 } 759 } 760 `, 761 }, 762 { 763 skip: true, 764 name: "if init 2", 765 src: ` 766 void foo(int a) { 767 if (a = 1) { 768 a = 0; 769 } 770 } 771 `, 772 exp: ` 773 func foo(a int32) { 774 if a = 1; a != 0 { 775 a = 0 776 } 777 } 778 `, 779 }, 780 { 781 skip: true, 782 name: "if init 3", 783 src: ` 784 void foo(int a) { 785 if ((a = 1) != 0) { 786 a = 0; 787 } 788 } 789 `, 790 exp: ` 791 func foo(a int32) { 792 if a = 1; a != 0 { 793 a = 0 794 } 795 } 796 `, 797 }, 798 { 799 name: "multiple stmt splits", 800 src: ` 801 typedef struct list_t list_t; 802 typedef struct list_t { 803 list_t* next; 804 } list_t; 805 806 void foo() { 807 list_t *elt, *list; 808 for (elt=list; elt ? (list=elt->next, elt->next=0), 1 : 0; elt=list) {} 809 } 810 `, 811 exp: ` 812 type list_t struct { 813 Next *list_t 814 } 815 816 func foo() { 817 var ( 818 elt *list_t 819 list *list_t 820 ) 821 for elt = list; func() int { 822 if elt != nil { 823 return func() int { 824 list = elt.Next 825 elt.Next = nil 826 return 1 827 }() 828 } 829 return 0 830 }() != 0; elt = list { 831 } 832 } 833 `, 834 envFuncs: []envFunc{func(c *types.Config) { 835 c.UseGoInt = true 836 }}, 837 }, 838 { 839 name: "do not edit", 840 src: ` 841 842 void foo() {} 843 `, 844 exp: ` 845 // Code generated by cxgo. DO NOT EDIT. 846 847 package lib 848 849 func foo() { 850 } 851 `, 852 configFuncs: []configFunc{withDoNotEdit(true)}, 853 }, 854 } 855 856 func TestTranslate(t *testing.T) { 857 runTestTranslate(t, casesTranslate) 858 } 859 860 func runTestTranslateCase(t *testing.T, c parseCase) { 861 if c.shouldSkip() { 862 defer func() { 863 if r := recover(); r != nil { 864 defer debug.PrintStack() 865 t.Skip(r) 866 } 867 }() 868 } 869 fname := strings.ReplaceAll(c.name, " ", "_") + ".c" 870 var srcs []cc.Source 871 if c.inc != "" { 872 srcs = append(srcs, cc.Source{ 873 Name: strings.TrimSuffix(fname, ".c") + "_predef.h", 874 Value: c.inc, 875 }) 876 } 877 srcs = append(srcs, cc.Source{ 878 Name: fname, 879 Value: c.src, 880 }) 881 econf := types.Config32() 882 for _, f := range c.envFuncs { 883 f(&econf) 884 } 885 env := libs.NewEnv(econf) 886 ast, err := ParseSource(env, ParseConfig{ 887 WorkDir: "", 888 Predefines: c.builtins, 889 Sources: srcs, 890 }) 891 if c.skip { 892 t.SkipNow() 893 } else { 894 require.NoError(t, err) 895 } 896 897 tconf := Config{ForwardDecl: true} 898 for _, f := range c.configFuncs { 899 f(&tconf) 900 } 901 decls, err := TranslateAST(fname, ast, env, tconf) 902 require.NoError(t, err) 903 904 buf := bytes.NewBuffer(nil) 905 err = PrintGo(buf, testPkg, decls, tconf.DoNotEdit) 906 assert.NoError(t, err) 907 908 exp := c.exp 909 a, b := strings.TrimSpace(exp), strings.TrimSpace(strings.TrimPrefix(buf.String(), "package lib")) 910 if a != b { 911 if c.skipExp != "" { 912 a = strings.TrimSpace(c.skipExp) 913 require.Equal(t, a, b) 914 t.SkipNow() 915 } else if c.skip { 916 t.SkipNow() 917 } else { 918 require.Equal(t, a, b) 919 } 920 } 921 if c.skip && !t.Failed() { 922 require.Fail(t, "skipped test passes") 923 } 924 } 925 926 func runTestTranslate(t *testing.T, cases []parseCase) { 927 for _, c := range cases { 928 t.Run(c.name, func(t *testing.T) { 929 runTestTranslateCase(t, c) 930 }) 931 } 932 } 933 934 func TestTypeResolution(t *testing.T) { 935 const ( 936 fname = "resolve.c" 937 ) 938 env := libs.NewEnv(types.Config32()) 939 ast, err := ParseSource(env, ParseConfig{ 940 WorkDir: "", 941 Sources: []cc.Source{ 942 // { 943 // Name: "resolve.h", 944 // Value:` 945 //void unused(char*,int); 946 //`, 947 // }, 948 { 949 Name: fname, 950 Value: ` 951 typedef int int_t; 952 int_t a, b; 953 struct bar { 954 int_t x; 955 int_t y; 956 }; 957 struct bar *f1(int_t c, struct bar e) { 958 int_t b; 959 int_t d = a + b + c + e.x + e.y; 960 } 961 `, 962 }, 963 }, 964 }) 965 require.NoError(t, err) 966 967 decls, err := TranslateCAST(fname, ast, env, Config{}) 968 require.NoError(t, err) 969 require.Len(t, decls, 5) 970 971 // find the first int typedef 972 d1, ok := decls[0].(*CTypeDef) 973 require.True(t, ok) 974 // check the named type that we get 975 intT := d1.Named 976 assert.Equal(t, "int_t", intT.Name().Name) 977 assert.Equal(t, types.IntT(4), intT.Underlying()) 978 979 // both variable declarations should have exactly the same type (by pointer) 980 a1d, ok := decls[1].(*CVarDecl) 981 require.True(t, ok) 982 assert.Equal(t, "a", a1d.Names[0].Name) 983 assert.True(t, intT == a1d.Type, "invalid type in global") 984 985 b1d, ok := decls[2].(*CVarDecl) 986 require.True(t, ok) 987 assert.Equal(t, "b", b1d.Names[0].Name) 988 assert.True(t, intT == b1d.Type, "invalid type in global 2") 989 990 // test struct definition 991 bard, ok := decls[3].(*CTypeDef) 992 require.True(t, ok) 993 assert.Equal(t, "bar", bard.Name().Name) 994 bar, ok := bard.Underlying().(*types.StructType) 995 require.True(t, ok) 996 require.Len(t, bar.Fields(), 2) 997 xf := bar.Fields()[0] 998 yf := bar.Fields()[1] 999 assert.True(t, intT == xf.Type(), "invalid type in struct field 1") 1000 assert.True(t, intT == yf.Type(), "invalid type in struct field 2") 1001 1002 // check that the same type works in function args 1003 f1d, ok := decls[4].(*CFuncDecl) 1004 require.True(t, ok) 1005 assert.Equal(t, "f1", f1d.Name.Name) 1006 f1t := f1d.Type 1007 require.Len(t, f1t.Args(), 2) 1008 assert.True(t, intT == f1t.Args()[0].Type(), "invalid type in func arg") 1009 assert.True(t, bard.Named == f1t.Args()[1].Type(), "invalid type in func arg") 1010 p1, ok := f1t.Return().(types.PtrType) 1011 require.True(t, ok) 1012 assert.True(t, bard.Named == p1.Elem(), "invalid type in func return") 1013 1014 require.Len(t, f1d.Body.Stmts, 3) 1015 // first decl is always __func__ 1016 s1, ok := f1d.Body.Stmts[0].(*CDeclStmt) 1017 require.True(t, ok) 1018 fnc_, ok := s1.Decl.(*CVarDecl) 1019 require.True(t, ok) 1020 require.Equal(t, "__func__", fnc_.Names[0].Name) 1021 1022 // check if the type works in function body 1023 s1, ok = f1d.Body.Stmts[1].(*CDeclStmt) 1024 require.True(t, ok) 1025 b2d, ok := s1.Decl.(*CVarDecl) 1026 require.True(t, ok) 1027 assert.Equal(t, "b", b2d.Names[0].Name) 1028 assert.True(t, intT == b2d.Type, "invalid type in local") 1029 // and that we can distinguish globals from locals 1030 assert.True(t, b1d != b2d) 1031 assert.True(t, b1d.Names[0] != b2d.Names[0]) 1032 1033 // check same conditions for a second local decl 1034 s1, ok = f1d.Body.Stmts[2].(*CDeclStmt) 1035 require.True(t, ok) 1036 d1d, ok := s1.Decl.(*CVarDecl) 1037 require.True(t, ok) 1038 assert.Equal(t, "d", d1d.Names[0].Name) 1039 assert.True(t, intT == d1d.Type, "invalid type in local 2") 1040 // check variables - one is global, second is local, third is an arg 1041 e1, ok := d1d.Inits[0].(*CBinaryExpr) 1042 require.True(t, ok, "%T", d1d.Inits[0]) 1043 e2, ok := e1.Right.(*CSelectExpr) 1044 assert.True(t, ok, "%T", e1.Right) 1045 assert.True(t, e2.Sel == yf.Name, "invalid field ref 5") // arg 1046 assert.True(t, e2.Expr == IdentExpr{f1t.Args()[1].Name}, "invalid ref 5") // arg 1047 1048 e1, ok = e1.Left.(*CBinaryExpr) 1049 require.True(t, ok, "%T", e1.Left) 1050 e2, ok = e1.Right.(*CSelectExpr) 1051 assert.True(t, ok, "%T", e1.Right) 1052 assert.True(t, e2.Sel == xf.Name, "invalid field ref 4") // arg 1053 assert.True(t, e2.Expr == IdentExpr{f1t.Args()[1].Name}, "invalid ref 4") // arg 1054 1055 e1, ok = e1.Left.(*CBinaryExpr) 1056 require.True(t, ok, "%T", e1.Left) 1057 assert.True(t, IdentExpr{f1t.Args()[0].Name} == e1.Right, "invalid ref 3") // arg 1058 1059 e1, ok = e1.Left.(*CBinaryExpr) 1060 require.True(t, ok) 1061 assert.True(t, IdentExpr{a1d.Names[0]} == e1.Left, "invalid ref 1") // global 1062 assert.True(t, IdentExpr{b2d.Names[0]} == e1.Right, "invalid ref 2") // local 1063 }