github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/syntax/printer.go (about) 1 // Copyright 2016 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 // This file implements printing of syntax trees in source format. 6 7 package syntax 8 9 import ( 10 "fmt" 11 "io" 12 "strings" 13 ) 14 15 // Form controls print formatting. 16 type Form uint 17 18 const ( 19 _ Form = iota // default 20 LineForm // use spaces instead of linebreaks where possible 21 ShortForm // like LineForm but print "…" for non-empty function or composite literal bodies 22 ) 23 24 // Fprint prints node x to w in the specified form. 25 // It returns the number of bytes written, and whether there was an error. 26 func Fprint(w io.Writer, x Node, form Form) (n int, err error) { 27 p := printer{ 28 output: w, 29 form: form, 30 linebreaks: form == 0, 31 } 32 33 defer func() { 34 n = p.written 35 if e := recover(); e != nil { 36 err = e.(writeError).err // re-panics if it's not a writeError 37 } 38 }() 39 40 p.print(x) 41 p.flush(_EOF) 42 43 return 44 } 45 46 // String is a convenience function that prints n in ShortForm 47 // and returns the printed string. 48 func String(n Node) string { 49 var buf strings.Builder 50 _, err := Fprint(&buf, n, ShortForm) 51 if err != nil { 52 fmt.Fprintf(&buf, "<<< ERROR: %s", err) 53 } 54 return buf.String() 55 } 56 57 type ctrlSymbol int 58 59 const ( 60 none ctrlSymbol = iota 61 semi 62 blank 63 newline 64 indent 65 outdent 66 // comment 67 // eolComment 68 ) 69 70 type whitespace struct { 71 last token 72 kind ctrlSymbol 73 //text string // comment text (possibly ""); valid if kind == comment 74 } 75 76 type printer struct { 77 output io.Writer 78 written int // number of bytes written 79 form Form 80 linebreaks bool // print linebreaks instead of semis 81 82 indent int // current indentation level 83 nlcount int // number of consecutive newlines 84 85 pending []whitespace // pending whitespace 86 lastTok token // last token (after any pending semi) processed by print 87 } 88 89 // write is a thin wrapper around p.output.Write 90 // that takes care of accounting and error handling. 91 func (p *printer) write(data []byte) { 92 n, err := p.output.Write(data) 93 p.written += n 94 if err != nil { 95 panic(writeError{err}) 96 } 97 } 98 99 var ( 100 tabBytes = []byte("\t\t\t\t\t\t\t\t") 101 newlineByte = []byte("\n") 102 blankByte = []byte(" ") 103 ) 104 105 func (p *printer) writeBytes(data []byte) { 106 if len(data) == 0 { 107 panic("expected non-empty []byte") 108 } 109 if p.nlcount > 0 && p.indent > 0 { 110 // write indentation 111 n := p.indent 112 for n > len(tabBytes) { 113 p.write(tabBytes) 114 n -= len(tabBytes) 115 } 116 p.write(tabBytes[:n]) 117 } 118 p.write(data) 119 p.nlcount = 0 120 } 121 122 func (p *printer) writeString(s string) { 123 p.writeBytes([]byte(s)) 124 } 125 126 // If impliesSemi returns true for a non-blank line's final token tok, 127 // a semicolon is automatically inserted. Vice versa, a semicolon may 128 // be omitted in those cases. 129 func impliesSemi(tok token) bool { 130 switch tok { 131 case _Name, 132 _Break, _Continue, _Fallthrough, _Return, 133 /*_Inc, _Dec,*/ _Rparen, _Rbrack, _Rbrace: // TODO(gri) fix this 134 return true 135 } 136 return false 137 } 138 139 // TODO(gri) provide table of []byte values for all tokens to avoid repeated string conversion 140 141 func lineComment(text string) bool { 142 return strings.HasPrefix(text, "//") 143 } 144 145 func (p *printer) addWhitespace(kind ctrlSymbol, text string) { 146 p.pending = append(p.pending, whitespace{p.lastTok, kind /*text*/}) 147 switch kind { 148 case semi: 149 p.lastTok = _Semi 150 case newline: 151 p.lastTok = 0 152 // TODO(gri) do we need to handle /*-style comments containing newlines here? 153 } 154 } 155 156 func (p *printer) flush(next token) { 157 // eliminate semis and redundant whitespace 158 sawNewline := next == _EOF 159 sawParen := next == _Rparen || next == _Rbrace 160 for i := len(p.pending) - 1; i >= 0; i-- { 161 switch p.pending[i].kind { 162 case semi: 163 k := semi 164 if sawParen { 165 sawParen = false 166 k = none // eliminate semi 167 } else if sawNewline && impliesSemi(p.pending[i].last) { 168 sawNewline = false 169 k = none // eliminate semi 170 } 171 p.pending[i].kind = k 172 case newline: 173 sawNewline = true 174 case blank, indent, outdent: 175 // nothing to do 176 // case comment: 177 // // A multi-line comment acts like a newline; and a "" 178 // // comment implies by definition at least one newline. 179 // if text := p.pending[i].text; strings.HasPrefix(text, "/*") && strings.ContainsRune(text, '\n') { 180 // sawNewline = true 181 // } 182 // case eolComment: 183 // // TODO(gri) act depending on sawNewline 184 default: 185 panic("unreachable") 186 } 187 } 188 189 // print pending 190 prev := none 191 for i := range p.pending { 192 switch p.pending[i].kind { 193 case none: 194 // nothing to do 195 case semi: 196 p.writeString(";") 197 p.nlcount = 0 198 prev = semi 199 case blank: 200 if prev != blank { 201 // at most one blank 202 p.writeBytes(blankByte) 203 p.nlcount = 0 204 prev = blank 205 } 206 case newline: 207 const maxEmptyLines = 1 208 if p.nlcount <= maxEmptyLines { 209 p.write(newlineByte) 210 p.nlcount++ 211 prev = newline 212 } 213 case indent: 214 p.indent++ 215 case outdent: 216 p.indent-- 217 if p.indent < 0 { 218 panic("negative indentation") 219 } 220 // case comment: 221 // if text := p.pending[i].text; text != "" { 222 // p.writeString(text) 223 // p.nlcount = 0 224 // prev = comment 225 // } 226 // // TODO(gri) should check that line comments are always followed by newline 227 default: 228 panic("unreachable") 229 } 230 } 231 232 p.pending = p.pending[:0] // re-use underlying array 233 } 234 235 func mayCombine(prev token, next byte) (b bool) { 236 return // for now 237 // switch prev { 238 // case lexical.Int: 239 // b = next == '.' // 1. 240 // case lexical.Add: 241 // b = next == '+' // ++ 242 // case lexical.Sub: 243 // b = next == '-' // -- 244 // case lexical.Quo: 245 // b = next == '*' // /* 246 // case lexical.Lss: 247 // b = next == '-' || next == '<' // <- or << 248 // case lexical.And: 249 // b = next == '&' || next == '^' // && or &^ 250 // } 251 // return 252 } 253 254 func (p *printer) print(args ...interface{}) { 255 for i := 0; i < len(args); i++ { 256 switch x := args[i].(type) { 257 case nil: 258 // we should not reach here but don't crash 259 260 case Node: 261 p.printNode(x) 262 263 case token: 264 // _Name implies an immediately following string 265 // argument which is the actual value to print. 266 var s string 267 if x == _Name { 268 i++ 269 if i >= len(args) { 270 panic("missing string argument after _Name") 271 } 272 s = args[i].(string) 273 } else { 274 s = x.String() 275 } 276 277 // TODO(gri) This check seems at the wrong place since it doesn't 278 // take into account pending white space. 279 if mayCombine(p.lastTok, s[0]) { 280 panic("adjacent tokens combine without whitespace") 281 } 282 283 if x == _Semi { 284 // delay printing of semi 285 p.addWhitespace(semi, "") 286 } else { 287 p.flush(x) 288 p.writeString(s) 289 p.nlcount = 0 290 p.lastTok = x 291 } 292 293 case Operator: 294 if x != 0 { 295 p.flush(_Operator) 296 p.writeString(x.String()) 297 } 298 299 case ctrlSymbol: 300 switch x { 301 case none, semi /*, comment*/ : 302 panic("unreachable") 303 case newline: 304 // TODO(gri) need to handle mandatory newlines after a //-style comment 305 if !p.linebreaks { 306 x = blank 307 } 308 } 309 p.addWhitespace(x, "") 310 311 // case *Comment: // comments are not Nodes 312 // p.addWhitespace(comment, x.Text) 313 314 default: 315 panic(fmt.Sprintf("unexpected argument %v (%T)", x, x)) 316 } 317 } 318 } 319 320 func (p *printer) printNode(n Node) { 321 // ncom := *n.Comments() 322 // if ncom != nil { 323 // // TODO(gri) in general we cannot make assumptions about whether 324 // // a comment is a /*- or a //-style comment since the syntax 325 // // tree may have been manipulated. Need to make sure the correct 326 // // whitespace is emitted. 327 // for _, c := range ncom.Alone { 328 // p.print(c, newline) 329 // } 330 // for _, c := range ncom.Before { 331 // if c.Text == "" || lineComment(c.Text) { 332 // panic("unexpected empty line or //-style 'before' comment") 333 // } 334 // p.print(c, blank) 335 // } 336 // } 337 338 p.printRawNode(n) 339 340 // if ncom != nil && len(ncom.After) > 0 { 341 // for i, c := range ncom.After { 342 // if i+1 < len(ncom.After) { 343 // if c.Text == "" || lineComment(c.Text) { 344 // panic("unexpected empty line or //-style non-final 'after' comment") 345 // } 346 // } 347 // p.print(blank, c) 348 // } 349 // //p.print(newline) 350 // } 351 } 352 353 func (p *printer) printRawNode(n Node) { 354 switch n := n.(type) { 355 case nil: 356 // we should not reach here but don't crash 357 358 // expressions and types 359 case *BadExpr: 360 p.print(_Name, "<bad expr>") 361 362 case *Name: 363 p.print(_Name, n.Value) // _Name requires actual value following immediately 364 365 case *BasicLit: 366 p.print(_Name, n.Value) // _Name requires actual value following immediately 367 368 case *FuncLit: 369 p.print(n.Type, blank) 370 if n.Body != nil { 371 if p.form == ShortForm { 372 p.print(_Lbrace) 373 if len(n.Body.List) > 0 { 374 p.print(_Name, "…") 375 } 376 p.print(_Rbrace) 377 } else { 378 p.print(n.Body) 379 } 380 } 381 382 case *CompositeLit: 383 if n.Type != nil { 384 p.print(n.Type) 385 } 386 p.print(_Lbrace) 387 if p.form == ShortForm { 388 if len(n.ElemList) > 0 { 389 p.print(_Name, "…") 390 } 391 } else { 392 if n.NKeys > 0 && n.NKeys == len(n.ElemList) { 393 p.printExprLines(n.ElemList) 394 } else { 395 p.printExprList(n.ElemList) 396 } 397 } 398 p.print(_Rbrace) 399 400 case *ParenExpr: 401 p.print(_Lparen, n.X, _Rparen) 402 403 case *SelectorExpr: 404 p.print(n.X, _Dot, n.Sel) 405 406 case *IndexExpr: 407 p.print(n.X, _Lbrack, n.Index, _Rbrack) 408 409 case *SliceExpr: 410 p.print(n.X, _Lbrack) 411 if i := n.Index[0]; i != nil { 412 p.printNode(i) 413 } 414 p.print(_Colon) 415 if j := n.Index[1]; j != nil { 416 p.printNode(j) 417 } 418 if k := n.Index[2]; k != nil { 419 p.print(_Colon, k) 420 } 421 p.print(_Rbrack) 422 423 case *AssertExpr: 424 p.print(n.X, _Dot, _Lparen, n.Type, _Rparen) 425 426 case *TypeSwitchGuard: 427 if n.Lhs != nil { 428 p.print(n.Lhs, blank, _Define, blank) 429 } 430 p.print(n.X, _Dot, _Lparen, _Type, _Rparen) 431 432 case *CallExpr: 433 p.print(n.Fun, _Lparen) 434 p.printExprList(n.ArgList) 435 if n.HasDots { 436 p.print(_DotDotDot) 437 } 438 p.print(_Rparen) 439 440 case *Operation: 441 if n.Y == nil { 442 // unary expr 443 p.print(n.Op) 444 // if n.Op == lexical.Range { 445 // p.print(blank) 446 // } 447 p.print(n.X) 448 } else { 449 // binary expr 450 // TODO(gri) eventually take precedence into account 451 // to control possibly missing parentheses 452 p.print(n.X, blank, n.Op, blank, n.Y) 453 } 454 455 case *KeyValueExpr: 456 p.print(n.Key, _Colon, blank, n.Value) 457 458 case *ListExpr: 459 p.printExprList(n.ElemList) 460 461 case *ArrayType: 462 var len interface{} = _DotDotDot 463 if n.Len != nil { 464 len = n.Len 465 } 466 p.print(_Lbrack, len, _Rbrack, n.Elem) 467 468 case *SliceType: 469 p.print(_Lbrack, _Rbrack, n.Elem) 470 471 case *DotsType: 472 p.print(_DotDotDot, n.Elem) 473 474 case *StructType: 475 p.print(_Struct) 476 if len(n.FieldList) > 0 && p.linebreaks { 477 p.print(blank) 478 } 479 p.print(_Lbrace) 480 if len(n.FieldList) > 0 { 481 if p.linebreaks { 482 p.print(newline, indent) 483 p.printFieldList(n.FieldList, n.TagList, _Semi) 484 p.print(outdent, newline) 485 } else { 486 p.printFieldList(n.FieldList, n.TagList, _Semi) 487 } 488 } 489 p.print(_Rbrace) 490 491 case *FuncType: 492 p.print(_Func) 493 p.printSignature(n) 494 495 case *InterfaceType: 496 p.print(_Interface) 497 if p.linebreaks && len(n.MethodList) > 1 { 498 p.print(blank) 499 p.print(_Lbrace) 500 p.print(newline, indent) 501 p.printMethodList(n.MethodList) 502 p.print(outdent, newline) 503 } else { 504 p.print(_Lbrace) 505 p.printMethodList(n.MethodList) 506 } 507 p.print(_Rbrace) 508 509 case *MapType: 510 p.print(_Map, _Lbrack, n.Key, _Rbrack, n.Value) 511 512 case *ChanType: 513 if n.Dir == RecvOnly { 514 p.print(_Arrow) 515 } 516 p.print(_Chan) 517 if n.Dir == SendOnly { 518 p.print(_Arrow) 519 } 520 p.print(blank) 521 if e, _ := n.Elem.(*ChanType); n.Dir == 0 && e != nil && e.Dir == RecvOnly { 522 // don't print chan (<-chan T) as chan <-chan T 523 p.print(_Lparen) 524 p.print(n.Elem) 525 p.print(_Rparen) 526 } else { 527 p.print(n.Elem) 528 } 529 530 // statements 531 case *DeclStmt: 532 p.printDecl(n.DeclList) 533 534 case *EmptyStmt: 535 // nothing to print 536 537 case *LabeledStmt: 538 p.print(outdent, n.Label, _Colon, indent, newline, n.Stmt) 539 540 case *ExprStmt: 541 p.print(n.X) 542 543 case *SendStmt: 544 p.print(n.Chan, blank, _Arrow, blank, n.Value) 545 546 case *AssignStmt: 547 p.print(n.Lhs) 548 if n.Rhs == nil { 549 // TODO(gri) This is going to break the mayCombine 550 // check once we enable that again. 551 p.print(n.Op, n.Op) // ++ or -- 552 } else { 553 p.print(blank, n.Op, _Assign, blank) 554 p.print(n.Rhs) 555 } 556 557 case *CallStmt: 558 p.print(n.Tok, blank, n.Call) 559 560 case *ReturnStmt: 561 p.print(_Return) 562 if n.Results != nil { 563 p.print(blank, n.Results) 564 } 565 566 case *BranchStmt: 567 p.print(n.Tok) 568 if n.Label != nil { 569 p.print(blank, n.Label) 570 } 571 572 case *BlockStmt: 573 p.print(_Lbrace) 574 if len(n.List) > 0 { 575 p.print(newline, indent) 576 p.printStmtList(n.List, true) 577 p.print(outdent, newline) 578 } 579 p.print(_Rbrace) 580 581 case *IfStmt: 582 p.print(_If, blank) 583 if n.Init != nil { 584 p.print(n.Init, _Semi, blank) 585 } 586 p.print(n.Cond, blank, n.Then) 587 if n.Else != nil { 588 p.print(blank, _Else, blank, n.Else) 589 } 590 591 case *SwitchStmt: 592 p.print(_Switch, blank) 593 if n.Init != nil { 594 p.print(n.Init, _Semi, blank) 595 } 596 if n.Tag != nil { 597 p.print(n.Tag, blank) 598 } 599 p.printSwitchBody(n.Body) 600 601 case *SelectStmt: 602 p.print(_Select, blank) // for now 603 p.printSelectBody(n.Body) 604 605 case *RangeClause: 606 if n.Lhs != nil { 607 tok := _Assign 608 if n.Def { 609 tok = _Define 610 } 611 p.print(n.Lhs, blank, tok, blank) 612 } 613 p.print(_Range, blank, n.X) 614 615 case *ForStmt: 616 p.print(_For, blank) 617 if n.Init == nil && n.Post == nil { 618 if n.Cond != nil { 619 p.print(n.Cond, blank) 620 } 621 } else { 622 if n.Init != nil { 623 p.print(n.Init) 624 // TODO(gri) clean this up 625 if _, ok := n.Init.(*RangeClause); ok { 626 p.print(blank, n.Body) 627 break 628 } 629 } 630 p.print(_Semi, blank) 631 if n.Cond != nil { 632 p.print(n.Cond) 633 } 634 p.print(_Semi, blank) 635 if n.Post != nil { 636 p.print(n.Post, blank) 637 } 638 } 639 p.print(n.Body) 640 641 case *ImportDecl: 642 if n.Group == nil { 643 p.print(_Import, blank) 644 } 645 if n.LocalPkgName != nil { 646 p.print(n.LocalPkgName, blank) 647 } 648 p.print(n.Path) 649 650 case *ConstDecl: 651 if n.Group == nil { 652 p.print(_Const, blank) 653 } 654 p.printNameList(n.NameList) 655 if n.Type != nil { 656 p.print(blank, n.Type) 657 } 658 if n.Values != nil { 659 p.print(blank, _Assign, blank, n.Values) 660 } 661 662 case *TypeDecl: 663 if n.Group == nil { 664 p.print(_Type, blank) 665 } 666 p.print(n.Name) 667 if n.TParamList != nil { 668 p.printParameterList(n.TParamList, _Type) 669 } 670 p.print(blank) 671 if n.Alias { 672 p.print(_Assign, blank) 673 } 674 p.print(n.Type) 675 676 case *VarDecl: 677 if n.Group == nil { 678 p.print(_Var, blank) 679 } 680 p.printNameList(n.NameList) 681 if n.Type != nil { 682 p.print(blank, n.Type) 683 } 684 if n.Values != nil { 685 p.print(blank, _Assign, blank, n.Values) 686 } 687 688 case *FuncDecl: 689 p.print(_Func, blank) 690 if r := n.Recv; r != nil { 691 p.print(_Lparen) 692 if r.Name != nil { 693 p.print(r.Name, blank) 694 } 695 p.printNode(r.Type) 696 p.print(_Rparen, blank) 697 } 698 p.print(n.Name) 699 if n.TParamList != nil { 700 p.printParameterList(n.TParamList, _Func) 701 } 702 p.printSignature(n.Type) 703 if n.Body != nil { 704 p.print(blank, n.Body) 705 } 706 707 case *printGroup: 708 p.print(n.Tok, blank, _Lparen) 709 if len(n.Decls) > 0 { 710 p.print(newline, indent) 711 for _, d := range n.Decls { 712 p.printNode(d) 713 p.print(_Semi, newline) 714 } 715 p.print(outdent) 716 } 717 p.print(_Rparen) 718 719 // files 720 case *File: 721 p.print(_Package, blank, n.PkgName) 722 if len(n.DeclList) > 0 { 723 p.print(_Semi, newline, newline) 724 p.printDeclList(n.DeclList) 725 } 726 727 default: 728 panic(fmt.Sprintf("syntax.Iterate: unexpected node type %T", n)) 729 } 730 } 731 732 func (p *printer) printFields(fields []*Field, tags []*BasicLit, i, j int) { 733 if i+1 == j && fields[i].Name == nil { 734 // anonymous field 735 p.printNode(fields[i].Type) 736 } else { 737 for k, f := range fields[i:j] { 738 if k > 0 { 739 p.print(_Comma, blank) 740 } 741 p.printNode(f.Name) 742 } 743 p.print(blank) 744 p.printNode(fields[i].Type) 745 } 746 if i < len(tags) && tags[i] != nil { 747 p.print(blank) 748 p.printNode(tags[i]) 749 } 750 } 751 752 func (p *printer) printFieldList(fields []*Field, tags []*BasicLit, sep token) { 753 i0 := 0 754 var typ Expr 755 for i, f := range fields { 756 if f.Name == nil || f.Type != typ { 757 if i0 < i { 758 p.printFields(fields, tags, i0, i) 759 p.print(sep, newline) 760 i0 = i 761 } 762 typ = f.Type 763 } 764 } 765 p.printFields(fields, tags, i0, len(fields)) 766 } 767 768 func (p *printer) printMethodList(methods []*Field) { 769 for i, m := range methods { 770 if i > 0 { 771 p.print(_Semi, newline) 772 } 773 if m.Name != nil { 774 p.printNode(m.Name) 775 p.printSignature(m.Type.(*FuncType)) 776 } else { 777 p.printNode(m.Type) 778 } 779 } 780 } 781 782 func (p *printer) printNameList(list []*Name) { 783 for i, x := range list { 784 if i > 0 { 785 p.print(_Comma, blank) 786 } 787 p.printNode(x) 788 } 789 } 790 791 func (p *printer) printExprList(list []Expr) { 792 for i, x := range list { 793 if i > 0 { 794 p.print(_Comma, blank) 795 } 796 p.printNode(x) 797 } 798 } 799 800 func (p *printer) printExprLines(list []Expr) { 801 if len(list) > 0 { 802 p.print(newline, indent) 803 for _, x := range list { 804 p.print(x, _Comma, newline) 805 } 806 p.print(outdent) 807 } 808 } 809 810 func groupFor(d Decl) (token, *Group) { 811 switch d := d.(type) { 812 case *ImportDecl: 813 return _Import, d.Group 814 case *ConstDecl: 815 return _Const, d.Group 816 case *TypeDecl: 817 return _Type, d.Group 818 case *VarDecl: 819 return _Var, d.Group 820 case *FuncDecl: 821 return _Func, nil 822 default: 823 panic("unreachable") 824 } 825 } 826 827 type printGroup struct { 828 node 829 Tok token 830 Decls []Decl 831 } 832 833 func (p *printer) printDecl(list []Decl) { 834 tok, group := groupFor(list[0]) 835 836 if group == nil { 837 if len(list) != 1 { 838 panic("unreachable") 839 } 840 p.printNode(list[0]) 841 return 842 } 843 844 // if _, ok := list[0].(*EmptyDecl); ok { 845 // if len(list) != 1 { 846 // panic("unreachable") 847 // } 848 // // TODO(gri) if there are comments inside the empty 849 // // group, we may need to keep the list non-nil 850 // list = nil 851 // } 852 853 // printGroup is here for consistent comment handling 854 // (this is not yet used) 855 var pg printGroup 856 // *pg.Comments() = *group.Comments() 857 pg.Tok = tok 858 pg.Decls = list 859 p.printNode(&pg) 860 } 861 862 func (p *printer) printDeclList(list []Decl) { 863 i0 := 0 864 var tok token 865 var group *Group 866 for i, x := range list { 867 if s, g := groupFor(x); g == nil || g != group { 868 if i0 < i { 869 p.printDecl(list[i0:i]) 870 p.print(_Semi, newline) 871 // print empty line between different declaration groups, 872 // different kinds of declarations, or between functions 873 if g != group || s != tok || s == _Func { 874 p.print(newline) 875 } 876 i0 = i 877 } 878 tok, group = s, g 879 } 880 } 881 p.printDecl(list[i0:]) 882 } 883 884 func (p *printer) printSignature(sig *FuncType) { 885 p.printParameterList(sig.ParamList, 0) 886 if list := sig.ResultList; list != nil { 887 p.print(blank) 888 if len(list) == 1 && list[0].Name == nil { 889 p.printNode(list[0].Type) 890 } else { 891 p.printParameterList(list, 0) 892 } 893 } 894 } 895 896 // If tok != 0 print a type parameter list: tok == _Type means 897 // a type parameter list for a type, tok == _Func means a type 898 // parameter list for a func. 899 func (p *printer) printParameterList(list []*Field, tok token) { 900 open, close := _Lparen, _Rparen 901 if tok != 0 { 902 open, close = _Lbrack, _Rbrack 903 } 904 p.print(open) 905 for i, f := range list { 906 if i > 0 { 907 p.print(_Comma, blank) 908 } 909 if f.Name != nil { 910 p.printNode(f.Name) 911 if i+1 < len(list) { 912 f1 := list[i+1] 913 if f1.Name != nil && f1.Type == f.Type { 914 continue // no need to print type 915 } 916 } 917 p.print(blank) 918 } 919 p.printNode(Unparen(f.Type)) // no need for (extra) parentheses around parameter types 920 } 921 // A type parameter list [P T] where the name P and the type expression T syntactically 922 // combine to another valid (value) expression requires a trailing comma, as in [P *T,] 923 // (or an enclosing interface as in [P interface(*T)]), so that the type parameter list 924 // is not parsed as an array length [P*T]. 925 if tok == _Type && len(list) == 1 && combinesWithName(list[0].Type) { 926 p.print(_Comma) 927 } 928 p.print(close) 929 } 930 931 // combinesWithName reports whether a name followed by the expression x 932 // syntactically combines to another valid (value) expression. For instance 933 // using *T for x, "name *T" syntactically appears as the expression x*T. 934 // On the other hand, using P|Q or *P|~Q for x, "name P|Q" or name *P|~Q" 935 // cannot be combined into a valid (value) expression. 936 func combinesWithName(x Expr) bool { 937 switch x := x.(type) { 938 case *Operation: 939 if x.Y == nil { 940 // name *x.X combines to name*x.X if x.X is not a type element 941 return x.Op == Mul && !isTypeElem(x.X) 942 } 943 // binary expressions 944 return combinesWithName(x.X) && !isTypeElem(x.Y) 945 case *ParenExpr: 946 // name(x) combines but we are making sure at 947 // the call site that x is never parenthesized. 948 panic("unexpected parenthesized expression") 949 } 950 return false 951 } 952 953 func (p *printer) printStmtList(list []Stmt, braces bool) { 954 for i, x := range list { 955 p.print(x, _Semi) 956 if i+1 < len(list) { 957 p.print(newline) 958 } else if braces { 959 // Print an extra semicolon if the last statement is 960 // an empty statement and we are in a braced block 961 // because one semicolon is automatically removed. 962 if _, ok := x.(*EmptyStmt); ok { 963 p.print(x, _Semi) 964 } 965 } 966 } 967 } 968 969 func (p *printer) printSwitchBody(list []*CaseClause) { 970 p.print(_Lbrace) 971 if len(list) > 0 { 972 p.print(newline) 973 for i, c := range list { 974 p.printCaseClause(c, i+1 == len(list)) 975 p.print(newline) 976 } 977 } 978 p.print(_Rbrace) 979 } 980 981 func (p *printer) printSelectBody(list []*CommClause) { 982 p.print(_Lbrace) 983 if len(list) > 0 { 984 p.print(newline) 985 for i, c := range list { 986 p.printCommClause(c, i+1 == len(list)) 987 p.print(newline) 988 } 989 } 990 p.print(_Rbrace) 991 } 992 993 func (p *printer) printCaseClause(c *CaseClause, braces bool) { 994 if c.Cases != nil { 995 p.print(_Case, blank, c.Cases) 996 } else { 997 p.print(_Default) 998 } 999 p.print(_Colon) 1000 if len(c.Body) > 0 { 1001 p.print(newline, indent) 1002 p.printStmtList(c.Body, braces) 1003 p.print(outdent) 1004 } 1005 } 1006 1007 func (p *printer) printCommClause(c *CommClause, braces bool) { 1008 if c.Comm != nil { 1009 p.print(_Case, blank) 1010 p.print(c.Comm) 1011 } else { 1012 p.print(_Default) 1013 } 1014 p.print(_Colon) 1015 if len(c.Body) > 0 { 1016 p.print(newline, indent) 1017 p.printStmtList(c.Body, braces) 1018 p.print(outdent) 1019 } 1020 }