github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/internal/cmd/gtrace/writer.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "container/list" 6 "fmt" 7 "go/build" 8 "go/token" 9 "go/types" 10 "io" 11 "os" 12 "path/filepath" 13 "runtime" 14 "sort" 15 "strconv" 16 "strings" 17 "sync" 18 "unicode" 19 "unicode/utf8" 20 ) 21 22 //nolint:maligned 23 type Writer struct { 24 Output io.Writer 25 Context build.Context 26 27 once sync.Once 28 bw *bufio.Writer 29 30 atEOL bool 31 depth int 32 scope *list.List 33 34 pkg *types.Package 35 std map[string]bool 36 } 37 38 func (w *Writer) Write(p Package) error { 39 w.pkg = p.Package 40 41 w.init() 42 w.line(`// Code generated by gtrace. DO NOT EDIT.`) 43 44 for i, line := range p.BuildConstraints { 45 if i == 0 { 46 w.line() 47 } 48 w.line(line) 49 } 50 w.line() 51 w.line(`package `, p.Name()) 52 w.line() 53 54 var deps []dep 55 for _, trace := range p.Traces { 56 deps = w.traceImports(deps, trace) 57 } 58 w.importDeps(deps) 59 60 w.newScope(func() { 61 for _, trace := range p.Traces { 62 w.options(trace) 63 w.compose(trace) 64 if trace.Nested { 65 w.isZero(trace) 66 } 67 for _, hook := range trace.Hooks { 68 w.hook(trace, hook) 69 } 70 } 71 for _, trace := range p.Traces { 72 for _, hook := range trace.Hooks { 73 w.hookShortcut(trace, hook) 74 } 75 } 76 }) 77 78 return w.bw.Flush() 79 } 80 81 func (w *Writer) init() { 82 w.once.Do(func() { 83 w.bw = bufio.NewWriter(w.Output) 84 w.scope = list.New() 85 }) 86 } 87 88 func (w *Writer) mustDeclare(name string) { 89 s, ok := w.scope.Back().Value.(*scope) 90 if !ok { 91 panic(fmt.Sprintf("unsupported type conversion from %T to *scope", s)) 92 } 93 if !s.set(name) { 94 where := s.where(name) 95 panic(fmt.Sprintf( 96 "gtrace: can't declare identifier: %q: already defined at %q", 97 name, where, 98 )) 99 } 100 } 101 102 func (w *Writer) declare(name string) string { 103 if isPredeclared(name) { 104 name = firstChar(name) 105 } 106 s, ok := w.scope.Back().Value.(*scope) 107 if !ok { 108 panic(fmt.Sprintf("unsupported type conversion from %T to *scope", s)) 109 } 110 for i := 0; ; i++ { 111 v := name 112 if i > 0 { 113 v += strconv.Itoa(i) 114 } 115 if token.IsKeyword(v) { 116 continue 117 } 118 if w.isGlobalScope() && w.pkg.Scope().Lookup(v) != nil { 119 continue 120 } 121 if s.set(v) { 122 return v 123 } 124 } 125 } 126 127 func isPredeclared(name string) bool { 128 return types.Universe.Lookup(name) != nil 129 } 130 131 func (w *Writer) isGlobalScope() bool { 132 return w.scope.Back().Prev() == nil 133 } 134 135 func (w *Writer) capture(vars ...string) { 136 s, ok := w.scope.Back().Value.(*scope) 137 if !ok { 138 panic(fmt.Sprintf("unsupported type conversion from %T to *scope", s)) 139 } 140 for _, v := range vars { 141 if !s.set(v) { 142 panic(fmt.Sprintf("can't capture variable %q", v)) 143 } 144 } 145 } 146 147 type dep struct { 148 pkgPath string 149 pkgName string 150 typName string 151 } 152 153 func (w *Writer) typeImports(dst []dep, t types.Type) []dep { 154 if p, ok := t.(*types.Pointer); ok { 155 return w.typeImports(dst, p.Elem()) 156 } 157 n, ok := t.(*types.Named) 158 if !ok { 159 return dst 160 } 161 var ( 162 obj = n.Obj() 163 pkg = obj.Pkg() 164 ) 165 if pkg != nil && pkg.Path() != w.pkg.Path() { 166 return append(dst, dep{ 167 pkgPath: pkg.Path(), 168 pkgName: pkg.Name(), 169 typName: obj.Name(), 170 }) 171 } 172 173 return dst 174 } 175 176 func forEachField(s *types.Struct, fn func(*types.Var)) { 177 for i := 0; i < s.NumFields(); i++ { 178 fn(s.Field(i)) 179 } 180 } 181 182 func unwrapStruct(t types.Type) (n *types.Named, s *types.Struct) { 183 var ok bool 184 n, ok = t.(*types.Named) 185 if ok { 186 s, _ = n.Underlying().(*types.Struct) 187 } 188 189 return 190 } 191 192 func (w *Writer) funcImports(dst []dep, fn *Func) []dep { 193 for i := range fn.Params { 194 dst = w.typeImports(dst, fn.Params[i].Type) 195 if _, s := unwrapStruct(fn.Params[i].Type); s != nil { 196 forEachField(s, func(v *types.Var) { 197 if v.Exported() { 198 dst = w.typeImports(dst, v.Type()) 199 } 200 }) 201 } 202 } 203 for _, x := range fn.Result { 204 if fn, ok := x.(*Func); ok { 205 dst = w.funcImports(dst, fn) 206 } 207 } 208 209 return dst 210 } 211 212 func (w *Writer) traceImports(dst []dep, t *Trace) []dep { 213 for _, h := range t.Hooks { 214 dst = w.funcImports(dst, h.Func) 215 } 216 217 return dst 218 } 219 220 func (w *Writer) importDeps(deps []dep) { 221 seen := map[string]bool{} 222 for i := 0; i < len(deps); { 223 d := deps[i] 224 if seen[d.pkgPath] { 225 n := len(deps) 226 deps[i], deps[n-1] = deps[n-1], deps[i] 227 deps = deps[:n-1] 228 229 continue 230 } 231 seen[d.pkgPath] = true 232 i++ 233 } 234 if len(deps) == 0 { 235 return 236 } 237 sort.Slice(deps, func(i, j int) bool { 238 var ( 239 d0 = deps[i] 240 d1 = deps[j] 241 std0 = w.isStdLib(d0.pkgPath) 242 std1 = w.isStdLib(d1.pkgPath) 243 ) 244 if std0 != std1 { 245 return std0 246 } 247 248 return d0.pkgPath < d1.pkgPath 249 }) 250 w.line(`import (`) 251 var lastStd bool 252 for i := range deps { 253 if w.isStdLib(deps[i].pkgPath) { 254 lastStd = true 255 } else if lastStd { 256 lastStd = false 257 w.line() 258 } 259 w.line("\t", `"`, deps[i].pkgPath, `"`) 260 } 261 w.line(`)`) 262 w.line() 263 } 264 265 func (w *Writer) isStdLib(pkg string) bool { 266 w.ensureStdLibMapping() 267 s := strings.Split(pkg, "/")[0] 268 269 return w.std[s] 270 } 271 272 func (w *Writer) ensureStdLibMapping() { 273 if w.std != nil { 274 return 275 } 276 w.std = make(map[string]bool) 277 278 src := filepath.Join(w.Context.GOROOT, "src") 279 files, err := os.ReadDir(src) 280 if err != nil { 281 panic(fmt.Sprintf("can't list GOROOT's src: %v", err)) 282 } 283 for _, file := range files { 284 if !file.IsDir() { 285 continue 286 } 287 name := filepath.Base(file.Name()) 288 switch name { 289 case "cmd", "internal": 290 // Ignored. 291 292 default: 293 w.std[name] = true 294 } 295 } 296 } 297 298 func (w *Writer) call(args []string) { 299 w.code(`(`) 300 for i, name := range args { 301 if i > 0 { 302 w.code(`, `) 303 } 304 w.code(name) 305 } 306 w.line(`)`) 307 } 308 309 func (w *Writer) isZero(trace *Trace) { 310 w.newScope(func() { 311 t := w.declare("t") 312 w.line(`// isZero checks whether `, t, ` is empty`) 313 w.line(`func (`, t, ` `, trace.Name, `) isZero() bool {`) 314 w.block(func() { 315 for _, hook := range trace.Hooks { 316 w.line(`if `, t, `.`, hook.Name, ` != nil {`) 317 w.block(func() { 318 w.line(`return false`) 319 }) 320 w.line(`}`) 321 } 322 w.line(`return true`) 323 }) 324 w.line(`}`) 325 }) 326 } 327 328 func (w *Writer) compose(trace *Trace) { 329 w.newScope(func() { 330 t := w.declare("t") 331 x := w.declare("x") 332 ret := w.declare("ret") 333 w.line(`// Compose returns a new `, trace.Name, ` which has functional fields composed both from `, 334 t, ` and `, x, `.`, 335 ) 336 w.line(`// Internals: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#internals`) 337 w.code(`func (`, t, ` *`, trace.Name, `) Compose(`, x, ` *`, trace.Name, `, opts ...`+trace.Name+`ComposeOption) `) 338 w.line(`*`, trace.Name, ` {`) 339 w.block(func() { 340 w.line(`if `, t, ` == nil {`) 341 w.block(func() { 342 w.line(`return x`) 343 }) 344 w.line(`}`) 345 w.line(`var `, ret, ` `, trace.Name, ``) 346 if len(trace.Hooks) > 0 { 347 w.line(`options := `, unexported(trace.Name), `ComposeOptions{}`) 348 w.line(`for _, opt := range opts {`) 349 w.block(func() { 350 w.line(`if opt != nil {`) 351 w.block(func() { 352 w.line(`opt(&options)`) 353 }) 354 w.line(`}`) 355 }) 356 w.line(`}`) 357 } 358 for _, hook := range trace.Hooks { 359 w.composeHook(hook, t, x, ret+"."+hook.Name) 360 } 361 w.line(`return &`, ret) 362 }) 363 w.line(`}`) 364 }) 365 } 366 367 func (w *Writer) composeHook(hook Hook, t1, t2, dst string) { 368 w.line(`{`) 369 w.block(func() { 370 h1 := w.declare("h1") 371 h2 := w.declare("h2") 372 w.line(h1, ` := `, t1, `.`, hook.Name) 373 w.line(h2, ` := `, t2, `.`, hook.Name) 374 w.code(dst, ` = `) 375 w.composeHookCall(hook.Func, h1, h2) 376 }) 377 w.line(`}`) 378 } 379 380 //nolint:funlen 381 func (w *Writer) composeHookCall(fn *Func, h1, h2 string) { 382 w.newScope(func() { 383 w.capture(h1, h2) 384 w.block(func() { 385 w.capture(h1, h2) 386 w.code(`func`) 387 args := w.funcParams(fn.Params) 388 if fn.HasResult() { 389 w.code(` `) 390 } 391 w.funcResults(fn) 392 w.line(` {`) 393 w.line(`if options.panicCallback != nil {`) 394 w.block(func() { 395 w.line("defer func() {") 396 w.block(func() { 397 w.line("if e := recover(); e != nil {") 398 w.block(func() { 399 w.line(`options.panicCallback(e)`) 400 }) 401 w.line("}") 402 }) 403 w.line("}()") 404 }) 405 w.line("}") 406 var ( 407 r1 string 408 r2 string 409 rs []string 410 ) 411 if fn.HasResult() { 412 r1 = w.declare("r") 413 r2 = w.declare("r") 414 rs = []string{r1, r2} 415 w.code("var " + r1 + ", " + r2 + " ") 416 w.funcResults(fn) 417 _ = w.bw.WriteByte('\n') 418 w.atEOL = true 419 } 420 for i, h := range []string{h1, h2} { 421 w.line("if " + h + " != nil {") 422 w.block(func() { 423 if fn.HasResult() { 424 w.code(rs[i], ` = `) //nolint:scopelint 425 } 426 w.code(h) //nolint:scopelint 427 w.call(args) 428 }) 429 w.line("}") 430 } 431 if fn.HasResult() { 432 w.code(`return `) 433 switch x := fn.Result[0].(type) { 434 case *Func: 435 w.composeHookCall(x, r1, r2) 436 case *Trace: 437 w.line(r1, `.Compose(`, r2, `)`) 438 default: 439 panic("unknown result type") 440 } 441 } 442 }) 443 w.line(`}`) 444 }) 445 } 446 447 func (w *Writer) options(trace *Trace) { 448 w.newScope(func() { 449 w.line(fmt.Sprintf(`// %sComposeOptions is a holder of options`, unexported(trace.Name))) 450 w.line(fmt.Sprintf(`type %sComposeOptions struct {`, unexported(trace.Name))) 451 w.block(func() { 452 w.line(`panicCallback func(e interface{})`) 453 }) 454 w.line(`}`) 455 _ = w.bw.WriteByte('\n') 456 }) 457 w.newScope(func() { 458 w.line(fmt.Sprintf(`// %sOption specified %s compose option`, trace.Name, trace.Name)) 459 w.line(`// Internals: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#internals`) 460 w.line(fmt.Sprintf(`type %sComposeOption func(o *%sComposeOptions)`, trace.Name, unexported(trace.Name))) 461 _ = w.bw.WriteByte('\n') 462 }) 463 w.newScope(func() { 464 w.line(fmt.Sprintf(`// With%sPanicCallback specified behavior on panic`, trace.Name)) 465 w.line(`// Internals: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#internals`) 466 w.line(fmt.Sprintf(`func With%sPanicCallback(cb func(e interface{})) %sComposeOption {`, trace.Name, trace.Name)) 467 w.block(func() { 468 w.line(fmt.Sprintf(`return func(o *%sComposeOptions) {`, unexported(trace.Name))) 469 w.block(func() { 470 w.line(`o.panicCallback = cb`) 471 }) 472 w.line(`}`) 473 }) 474 w.line(`}`) 475 _ = w.bw.WriteByte('\n') 476 }) 477 } 478 479 func (w *Writer) hook(trace *Trace, hook Hook) { 480 w.newScope(func() { 481 t := w.declare("t") 482 fn := w.declare("fn") 483 484 w.code(`func (`, t, ` *`, trace.Name, `) `, unexported(hook.Name)) 485 486 w.code(`(`) 487 var args []string 488 for i := range hook.Func.Params { 489 if i > 0 { 490 w.code(`, `) 491 } 492 args = append(args, w.funcParam(&hook.Func.Params[i])) 493 } 494 w.code(`)`) 495 if hook.Func.HasResult() { 496 w.code(` `) 497 } 498 w.funcResultsFlags(hook.Func, docs) 499 w.line(` {`) 500 w.block(func() { 501 w.line(fn, ` := `, t, `.`, hook.Name) 502 w.line(`if `, fn, ` == nil {`) 503 w.block(func() { 504 w.zeroReturn(hook.Func) 505 }) 506 w.line(`}`) 507 508 w.hookFuncCall(hook.Func, fn, args) 509 }) 510 w.line(`}`) 511 }) 512 } 513 514 func (w *Writer) hookFuncCall(fn *Func, name string, args []string) { 515 var res string 516 if fn.HasResult() { 517 res = w.declare("res") 518 w.code(res, ` := `) 519 } 520 521 w.code(name) 522 w.call(args) 523 524 if !fn.HasResult() { 525 return 526 } 527 528 r, isFunc := fn.Result[0].(*Func) 529 if isFunc { 530 w.line(`if `, res, ` == nil {`) 531 w.block(func() { 532 w.zeroReturn(fn) 533 }) 534 w.line(`}`) 535 536 if r.HasResult() { 537 w.newScope(func() { 538 w.code(`return func`) 539 args := w.funcParams(r.Params) 540 w.code(` `) 541 w.funcResults(r) 542 w.line(` {`) 543 w.block(func() { 544 w.hookFuncCall(r, res, args) 545 }) 546 w.line(`}`) 547 }) 548 549 return 550 } 551 } 552 553 w.line(`return `, res) 554 } 555 556 func nameParam(p *Param) (s string) { 557 s = p.Name 558 if s == "" { 559 s = firstChar(ident(typeBasename(p.Type))) 560 } 561 562 return unexported(s) 563 } 564 565 func (w *Writer) declareParams(src []Param) (names []string) { 566 names = make([]string, len(src)) 567 for i := range src { 568 names[i] = w.declare(nameParam(&src[i])) 569 } 570 571 return names 572 } 573 574 func flattenParams(params []Param) (dst []Param) { 575 for i := range params { 576 _, s := unwrapStruct(params[i].Type) 577 if s != nil { 578 dst = flattenStruct(dst, s) 579 580 continue 581 } 582 dst = append(dst, params[i]) 583 } 584 585 return dst 586 } 587 588 func typeBasename(t types.Type) (name string) { 589 lo, name := rsplit(t.String(), '.') 590 if name == "" { 591 name = lo 592 } 593 594 return name 595 } 596 597 func flattenStruct(dst []Param, s *types.Struct) []Param { 598 forEachField(s, func(f *types.Var) { 599 if !f.Exported() { 600 return 601 } 602 var ( 603 name = f.Name() 604 typ = f.Type() 605 ) 606 if name == typeBasename(typ) { 607 // NOTE: field name essentially be empty for embedded structs or 608 // fields called exactly as type. 609 name = "" 610 } 611 dst = append(dst, Param{ 612 Name: name, 613 Type: typ, 614 }) 615 }) 616 617 return dst 618 } 619 620 func (w *Writer) constructParams(params []Param, names []string) (res []string) { 621 for i := range params { 622 n, s := unwrapStruct(params[i].Type) 623 if s != nil { 624 var v string 625 v, names = w.constructStruct(n, s, names) 626 res = append(res, v) 627 628 continue 629 } 630 name := names[0] 631 names = names[1:] 632 res = append(res, name) 633 } 634 635 return res 636 } 637 638 func (w *Writer) constructStruct(n types.Type, s *types.Struct, vars []string) (string, []string) { 639 p := w.declare("p") 640 // maybe skip pointers from flattening to not allocate anyhing during trace. 641 w.line(`var `, p, ` `, w.typeString(n)) 642 for i := 0; i < s.NumFields(); i++ { 643 v := s.Field(i) 644 if !v.Exported() { 645 continue 646 } 647 name := vars[0] 648 vars = vars[1:] 649 w.line(p, `.`, v.Name(), ` = `, name) 650 } 651 652 return p, vars 653 } 654 655 func (w *Writer) hookShortcut(trace *Trace, hook Hook) { 656 name := exported(tempName(trace.Name, hook.Name)) 657 658 w.mustDeclare(name) 659 660 w.newScope(func() { 661 t := w.declare("t") 662 w.line(`// Internals: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#internals`) 663 w.code(`func `, name) 664 w.code(`(`) 665 var ctx string 666 w.code(t, ` *`, trace.Name) 667 668 var ( 669 params = flattenParams(hook.Func.Params) 670 names = w.declareParams(params) 671 ) 672 for i := range params { 673 w.code(`, `) 674 w.code(names[i], ` `, w.typeString(params[i].Type)) 675 } 676 w.code(`)`) 677 if hook.Func.HasResult() { 678 w.code(` `) 679 } 680 w.shortcutFuncResultsFlags(hook.Func, docs) 681 w.line(` {`) 682 w.block(func() { 683 for _, name := range names { 684 w.capture(name) 685 } 686 vars := w.constructParams(hook.Func.Params, names) 687 var res string 688 if hook.Func.HasResult() { 689 res = w.declare("res") 690 w.code(res, ` := `) 691 } 692 w.code(t, `.`, unexported(hook.Name)) 693 if ctx != "" { 694 vars = append([]string{ctx}, vars...) 695 } 696 w.call(vars) 697 if hook.Func.HasResult() { 698 w.code(`return `) 699 r := hook.Func.Result[0] 700 switch x := r.(type) { 701 case *Func: 702 w.hookFuncShortcut(x, res) 703 case *Trace: 704 w.line(res) 705 default: 706 panic("unexpected result type") 707 } 708 } 709 }) 710 w.line(`}`) 711 }) 712 } 713 714 func (w *Writer) hookFuncShortcut(fn *Func, name string) { 715 w.newScope(func() { 716 w.code(`func(`) 717 var ( 718 params = flattenParams(fn.Params) 719 names = w.declareParams(params) 720 ) 721 for i := range params { 722 if i > 0 { 723 w.code(`, `) 724 } 725 w.code(names[i], ` `, w.typeString(params[i].Type)) 726 } 727 w.code(`)`) 728 if fn.HasResult() { 729 w.code(` `) 730 } 731 w.shortcutFuncResults(fn) 732 w.line(` {`) 733 w.block(func() { 734 for _, name := range names { 735 w.capture(name) 736 } 737 params := w.constructParams(fn.Params, names) 738 var res string 739 if fn.HasResult() { 740 res = w.declare("res") 741 w.code(res, ` := `) 742 } 743 w.code(name) 744 w.call(params) 745 if fn.HasResult() { 746 r := fn.Result[0] 747 w.code(`return `) 748 switch x := r.(type) { 749 case *Func: 750 w.hookFuncShortcut(x, res) 751 case *Trace: 752 w.line(res) 753 default: 754 panic("unexpected result type") 755 } 756 } 757 }) 758 w.line(`}`) 759 }) 760 } 761 762 func (w *Writer) zeroReturn(fn *Func) { 763 if !fn.HasResult() { 764 w.line(`return`) 765 766 return 767 } 768 w.code(`return `) 769 switch x := fn.Result[0].(type) { 770 case *Func: 771 w.funcSignature(x) 772 w.line(` {`) 773 w.block(func() { 774 w.zeroReturn(x) 775 }) 776 w.line(`}`) 777 case *Trace: 778 w.line(x.Name, `{}`) 779 default: 780 panic("unexpected result type") 781 } 782 } 783 784 func (w *Writer) funcParams(params []Param) (vars []string) { 785 w.code(`(`) 786 for i := range params { 787 if i > 0 { 788 w.code(`, `) 789 } 790 vars = append(vars, w.funcParam(¶ms[i])) 791 } 792 w.code(`)`) 793 794 return 795 } 796 797 func (w *Writer) funcParam(p *Param) (name string) { 798 name = w.declare(nameParam(p)) 799 w.code(name, ` `) 800 w.code(w.typeString(p.Type)) 801 802 return name 803 } 804 805 func (w *Writer) funcParamSign(p *Param) { 806 name := nameParam(p) 807 if len(name) == 1 || isPredeclared(name) { 808 name = "_" 809 } 810 w.code(name, ` `) 811 w.code(w.typeString(p.Type)) 812 } 813 814 type flags uint8 815 816 func (f flags) has(x flags) bool { 817 return f&x != 0 818 } 819 820 const ( 821 _ flags = 1 << iota >> 1 822 docs 823 ) 824 825 func (w *Writer) funcResultsFlags(fn *Func, flags flags) { 826 for _, r := range fn.Result { 827 switch x := r.(type) { 828 case *Func: 829 w.funcSignatureFlags(x, flags) 830 case *Trace: 831 w.code(x.Name, ` `) 832 default: 833 panic("unexpected result type") 834 } 835 } 836 } 837 838 func (w *Writer) funcResults(fn *Func) { 839 w.funcResultsFlags(fn, 0) 840 } 841 842 func (w *Writer) funcSignatureFlags(fn *Func, flags flags) { 843 haveNames := haveNames(fn.Params) 844 w.code(`func(`) 845 for i := range fn.Params { 846 if i > 0 { 847 w.code(`, `) 848 } 849 if flags.has(docs) && haveNames { 850 w.funcParamSign(&fn.Params[i]) 851 } else { 852 w.code(w.typeString(fn.Params[i].Type)) 853 } 854 } 855 w.code(`)`) 856 if fn.HasResult() { 857 if fn.isFuncResult() { 858 w.code(` `) 859 } 860 w.funcResultsFlags(fn, flags) 861 } 862 } 863 864 func (w *Writer) funcSignature(fn *Func) { 865 w.funcSignatureFlags(fn, 0) 866 } 867 868 func (w *Writer) shortcutFuncSignFlags(fn *Func, flags flags) { 869 var ( 870 params = flattenParams(fn.Params) 871 haveNames = haveNames(params) 872 ) 873 w.code(`func(`) 874 for i := range params { 875 if i > 0 { 876 w.code(`, `) 877 } 878 if flags.has(docs) && haveNames { 879 w.funcParamSign(¶ms[i]) 880 } else { 881 w.code(w.typeString(params[i].Type)) 882 } 883 } 884 w.code(`)`) 885 if fn.HasResult() { 886 if fn.isFuncResult() { 887 w.code(` `) 888 } 889 w.shortcutFuncResultsFlags(fn, flags) 890 } 891 } 892 893 func (w *Writer) shortcutFuncResultsFlags(fn *Func, flags flags) { 894 for _, r := range fn.Result { 895 switch x := r.(type) { 896 case *Func: 897 w.shortcutFuncSignFlags(x, flags) 898 case *Trace: 899 w.code(x.Name, ` `) 900 default: 901 panic("unexpected result type") 902 } 903 } 904 } 905 906 func (w *Writer) shortcutFuncResults(fn *Func) { 907 w.shortcutFuncResultsFlags(fn, 0) 908 } 909 910 func haveNames(params []Param) bool { 911 for i := range params { 912 name := nameParam(¶ms[i]) 913 if len(name) > 1 && !isPredeclared(name) { 914 return true 915 } 916 } 917 918 return false 919 } 920 921 func (w *Writer) typeString(t types.Type) string { 922 return types.TypeString(t, func(pkg *types.Package) string { 923 if pkg.Path() == w.pkg.Path() { 924 return "" // same package; unqualified 925 } 926 927 return pkg.Name() 928 }) 929 } 930 931 func (w *Writer) block(fn func()) { 932 w.depth++ 933 w.newScope(fn) 934 w.depth-- 935 } 936 937 func (w *Writer) newScope(fn func()) { 938 w.scope.PushBack(new(scope)) 939 fn() 940 w.scope.Remove(w.scope.Back()) 941 } 942 943 func (w *Writer) line(args ...string) { 944 w.code(args...) 945 _ = w.bw.WriteByte('\n') 946 w.atEOL = true 947 } 948 949 func (w *Writer) code(args ...string) { 950 if w.atEOL { 951 for i := 0; i < w.depth; i++ { 952 _ = w.bw.WriteByte('\t') 953 } 954 w.atEOL = false 955 } 956 for _, arg := range args { 957 _, _ = w.bw.WriteString(arg) 958 } 959 } 960 961 func exported(s string) string { 962 r, size := utf8.DecodeRuneInString(s) 963 if r == utf8.RuneError { 964 panic("invalid string") 965 } 966 967 return string(unicode.ToUpper(r)) + s[size:] 968 } 969 970 func unexported(s string) string { 971 r, size := utf8.DecodeRuneInString(s) 972 if r == utf8.RuneError { 973 panic("invalid string") 974 } 975 976 return string(unicode.ToLower(r)) + s[size:] 977 } 978 979 func firstChar(s string) string { 980 r, _ := utf8.DecodeRuneInString(s) 981 if r == utf8.RuneError { 982 panic("invalid string") 983 } 984 985 return string(r) 986 } 987 988 func ident(s string) string { 989 // Identifier must not begin with number. 990 for len(s) > 0 { 991 r, size := utf8.DecodeRuneInString(s) 992 if r == utf8.RuneError { 993 panic("invalid string") 994 } 995 if !unicode.IsNumber(r) { 996 break 997 } 998 s = s[size:] 999 } 1000 1001 // Filter out non letter/number/underscore characters. 1002 s = strings.Map(func(r rune) rune { 1003 switch { 1004 case r == '_' || 1005 unicode.IsLetter(r) || 1006 unicode.IsNumber(r): 1007 1008 return r 1009 default: 1010 return -1 1011 } 1012 }, s) 1013 1014 if !token.IsIdentifier(s) { 1015 s = "_" + s 1016 } 1017 1018 return s 1019 } 1020 1021 func tempName(names ...string) string { 1022 var sb strings.Builder 1023 for i, name := range names { 1024 if i == 0 { 1025 name = unexported(name) 1026 } else { 1027 name = exported(name) 1028 } 1029 sb.WriteString(name) 1030 } 1031 1032 return sb.String() 1033 } 1034 1035 type decl struct { 1036 where string 1037 } 1038 1039 type scope struct { 1040 vars map[string]decl 1041 } 1042 1043 func (s *scope) set(v string) bool { 1044 if s.vars == nil { 1045 s.vars = make(map[string]decl) 1046 } 1047 if _, has := s.vars[v]; has { 1048 return false 1049 } 1050 _, file, line, _ := runtime.Caller(2) //nolint:gomnd 1051 s.vars[v] = decl{ 1052 where: fmt.Sprintf("%s:%d", file, line), 1053 } 1054 1055 return true 1056 } 1057 1058 func (s *scope) where(v string) string { 1059 d := s.vars[v] 1060 1061 return d.where 1062 }