1// Copyright 2016 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45// This file implements printing of syntax trees in source format.67package syntax89import (10 "fmt"11 "io"12 "strings"13)1415// Form controls print formatting.16type Form uint1718const (19 _ Form = iota // default20 LineForm // use spaces instead of linebreaks where possible21 ShortForm // like LineForm but print "…" for non-empty function or composite literal bodies22)2324// Fprint prints node x to w in the specified form.25// It returns the number of bytes written, and whether there was an error.26func 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 }3233 defer func() {34 n = p.written35 if e := recover(); e != nil {36 err = e.(writeError).err // re-panics if it's not a writeError37 }38 }()3940 p.print(x)41 p.flush(_EOF)4243 return44}4546// String is a convenience function that prints n in ShortForm47// and returns the printed string.48func String(n Node) string {49 var buf strings.Builder50 _, err := Fprint(&buf, n, ShortForm)51 if err != nil {52 fmt.Fprintf(&buf, "<<< ERROR: %s", err)53 }54 return buf.String()55}5657type ctrlSymbol int5859const (60 none ctrlSymbol = iota61 semi62 blank63 newline64 indent65 outdent66 // comment67 // eolComment68)6970type whitespace struct {71 last token72 kind ctrlSymbol73 //text string // comment text (possibly ""); valid if kind == comment74}7576type printer struct {77 output io.Writer78 written int // number of bytes written79 form Form80 linebreaks bool // print linebreaks instead of semis8182 indent int // current indentation level83 nlcount int // number of consecutive newlines8485 pending []whitespace // pending whitespace86 lastTok token // last token (after any pending semi) processed by print87}8889// write is a thin wrapper around p.output.Write90// that takes care of accounting and error handling.91func (p *printer) write(data []byte) {92 n, err := p.output.Write(data)93 p.written += n94 if err != nil {95 panic(writeError{err})96 }97}9899var (100 tabBytes = []byte("\t\t\t\t\t\t\t\t")101 newlineByte = []byte("\n")102 blankByte = []byte(" ")103)104105func (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 indentation111 n := p.indent112 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 = 0120}121122func (p *printer) writeString(s string) {123 p.writeBytes([]byte(s))124}125126// If impliesSemi returns true for a non-blank line's final token tok,127// a semicolon is automatically inserted. Vice versa, a semicolon may128// be omitted in those cases.129func impliesSemi(tok token) bool {130 switch tok {131 case _Name,132 _Break, _Continue, _Fallthrough, _Return,133 /*_Inc, _Dec,*/ _Rparen, _Rbrack, _Rbrace: // TODO(gri) fix this134 return true135 }136 return false137}138139// TODO(gri) provide table of []byte values for all tokens to avoid repeated string conversion140141func (p *printer) addWhitespace(kind ctrlSymbol, text string) {142 p.pending = append(p.pending, whitespace{p.lastTok, kind /*text*/})143 switch kind {144 case semi:145 p.lastTok = _Semi146 case newline:147 p.lastTok = 0148 // TODO(gri) do we need to handle /*-style comments containing newlines here?149 }150}151152func (p *printer) flush(next token) {153 // eliminate semis and redundant whitespace154 sawNewline := next == _EOF155 sawParen := next == _Rparen || next == _Rbrace156 for i := len(p.pending) - 1; i >= 0; i-- {157 switch p.pending[i].kind {158 case semi:159 k := semi160 if sawParen {161 sawParen = false162 k = none // eliminate semi163 } else if sawNewline && impliesSemi(p.pending[i].last) {164 sawNewline = false165 k = none // eliminate semi166 }167 p.pending[i].kind = k168 case newline:169 sawNewline = true170 case blank, indent, outdent:171 // nothing to do172 // case comment:173 // // A multi-line comment acts like a newline; and a ""174 // // comment implies by definition at least one newline.175 // if text := p.pending[i].text; strings.HasPrefix(text, "/*") && strings.ContainsRune(text, '\n') {176 // sawNewline = true177 // }178 // case eolComment:179 // // TODO(gri) act depending on sawNewline180 default:181 panic("unreachable")182 }183 }184185 // print pending186 prev := none187 for i := range p.pending {188 switch p.pending[i].kind {189 case none:190 // nothing to do191 case semi:192 p.writeString(";")193 p.nlcount = 0194 prev = semi195 case blank:196 if prev != blank {197 // at most one blank198 p.writeBytes(blankByte)199 p.nlcount = 0200 prev = blank201 }202 case newline:203 const maxEmptyLines = 1204 if p.nlcount <= maxEmptyLines {205 p.write(newlineByte)206 p.nlcount++207 prev = newline208 }209 case indent:210 p.indent++211 case outdent:212 p.indent--213 if p.indent < 0 {214 panic("negative indentation")215 }216 // case comment:217 // if text := p.pending[i].text; text != "" {218 // p.writeString(text)219 // p.nlcount = 0220 // prev = comment221 // }222 // // TODO(gri) should check that line comments are always followed by newline223 default:224 panic("unreachable")225 }226 }227228 p.pending = p.pending[:0] // re-use underlying array229}230231func mayCombine(prev token, next byte) (b bool) {232 return // for now233 // switch prev {234 // case lexical.Int:235 // b = next == '.' // 1.236 // case lexical.Add:237 // b = next == '+' // ++238 // case lexical.Sub:239 // b = next == '-' // --240 // case lexical.Quo:241 // b = next == '*' // /*242 // case lexical.Lss:243 // b = next == '-' || next == '<' // <- or <<244 // case lexical.And:245 // b = next == '&' || next == '^' // && or &^246 // }247 // return248}249250func (p *printer) print(args ...any) {251 for i := 0; i < len(args); i++ {252 switch x := args[i].(type) {253 case nil:254 // we should not reach here but don't crash255256 case Node:257 p.printNode(x)258259 case token:260 // _Name implies an immediately following string261 // argument which is the actual value to print.262 var s string263 if x == _Name {264 i++265 if i >= len(args) {266 panic("missing string argument after _Name")267 }268 s = args[i].(string)269 } else {270 s = x.String()271 }272273 // TODO(gri) This check seems at the wrong place since it doesn't274 // take into account pending white space.275 if mayCombine(p.lastTok, s[0]) {276 panic("adjacent tokens combine without whitespace")277 }278279 if x == _Semi {280 // delay printing of semi281 p.addWhitespace(semi, "")282 } else {283 p.flush(x)284 p.writeString(s)285 p.nlcount = 0286 p.lastTok = x287 }288289 case Operator:290 if x != 0 {291 p.flush(_Operator)292 p.writeString(x.String())293 }294295 case ctrlSymbol:296 switch x {297 case none, semi /*, comment*/ :298 panic("unreachable")299 case newline:300 // TODO(gri) need to handle mandatory newlines after a //-style comment301 if !p.linebreaks {302 x = blank303 }304 }305 p.addWhitespace(x, "")306307 // case *Comment: // comments are not Nodes308 // p.addWhitespace(comment, x.Text)309310 default:311 panic(fmt.Sprintf("unexpected argument %v (%T)", x, x))312 }313 }314}315316func (p *printer) printNode(n Node) {317 // ncom := *n.Comments()318 // if ncom != nil {319 // // TODO(gri) in general we cannot make assumptions about whether320 // // a comment is a /*- or a //-style comment since the syntax321 // // tree may have been manipulated. Need to make sure the correct322 // // whitespace is emitted.323 // for _, c := range ncom.Alone {324 // p.print(c, newline)325 // }326 // for _, c := range ncom.Before {327 // if c.Text == "" || lineComment(c.Text) {328 // panic("unexpected empty line or //-style 'before' comment")329 // }330 // p.print(c, blank)331 // }332 // }333334 p.printRawNode(n)335336 // if ncom != nil && len(ncom.After) > 0 {337 // for i, c := range ncom.After {338 // if i+1 < len(ncom.After) {339 // if c.Text == "" || lineComment(c.Text) {340 // panic("unexpected empty line or //-style non-final 'after' comment")341 // }342 // }343 // p.print(blank, c)344 // }345 // //p.print(newline)346 // }347}348349func (p *printer) printRawNode(n Node) {350 switch n := n.(type) {351 case nil:352 // we should not reach here but don't crash353354 // expressions and types355 case *BadExpr:356 p.print(_Name, "<bad expr>")357358 case *Name:359 p.print(_Name, n.Value) // _Name requires actual value following immediately360361 case *BasicLit:362 p.print(_Name, n.Value) // _Name requires actual value following immediately363364 case *FuncLit:365 p.print(n.Type, blank)366 if n.Body != nil {367 if p.form == ShortForm {368 p.print(_Lbrace)369 if len(n.Body.List) > 0 {370 p.print(_Name, "…")371 }372 p.print(_Rbrace)373 } else {374 p.print(n.Body)375 }376 }377378 case *CompositeLit:379 if n.Type != nil {380 p.print(n.Type)381 }382 p.print(_Lbrace)383 if p.form == ShortForm {384 if len(n.ElemList) > 0 {385 p.print(_Name, "…")386 }387 } else {388 if n.NKeys > 0 && n.NKeys == len(n.ElemList) {389 p.printExprLines(n.ElemList)390 } else {391 p.printExprList(n.ElemList)392 }393 }394 p.print(_Rbrace)395396 case *ParenExpr:397 p.print(_Lparen, n.X, _Rparen)398399 case *SelectorExpr:400 p.print(n.X, _Dot, n.Sel)401402 case *IndexExpr:403 p.print(n.X, _Lbrack, n.Index, _Rbrack)404405 case *SliceExpr:406 p.print(n.X, _Lbrack)407 if i := n.Index[0]; i != nil {408 p.printNode(i)409 }410 p.print(_Colon)411 if j := n.Index[1]; j != nil {412 p.printNode(j)413 }414 if k := n.Index[2]; k != nil {415 p.print(_Colon, k)416 }417 p.print(_Rbrack)418419 case *AssertExpr:420 p.print(n.X, _Dot, _Lparen, n.Type, _Rparen)421422 case *TypeSwitchGuard:423 if n.Lhs != nil {424 p.print(n.Lhs, blank, _Define, blank)425 }426 p.print(n.X, _Dot, _Lparen, _Type, _Rparen)427428 case *CallExpr:429 p.print(n.Fun, _Lparen)430 p.printExprList(n.ArgList)431 if n.HasDots {432 p.print(_DotDotDot)433 }434 p.print(_Rparen)435436 case *Operation:437 if n.Y == nil {438 // unary expr439 p.print(n.Op)440 // if n.Op == lexical.Range {441 // p.print(blank)442 // }443 p.print(n.X)444 } else {445 // binary expr446 // TODO(gri) eventually take precedence into account447 // to control possibly missing parentheses448 p.print(n.X, blank, n.Op, blank, n.Y)449 }450451 case *KeyValueExpr:452 p.print(n.Key, _Colon, blank, n.Value)453454 case *ListExpr:455 p.printExprList(n.ElemList)456457 case *ArrayType:458 var len any = _DotDotDot459 if n.Len != nil {460 len = n.Len461 }462 p.print(_Lbrack, len, _Rbrack, n.Elem)463464 case *SliceType:465 p.print(_Lbrack, _Rbrack, n.Elem)466467 case *DotsType:468 p.print(_DotDotDot, n.Elem)469470 case *StructType:471 p.print(_Struct)472 if len(n.FieldList) > 0 && p.linebreaks {473 p.print(blank)474 }475 p.print(_Lbrace)476 if len(n.FieldList) > 0 {477 if p.linebreaks {478 p.print(newline, indent)479 p.printFieldList(n.FieldList, n.TagList, _Semi)480 p.print(outdent, newline)481 } else {482 p.printFieldList(n.FieldList, n.TagList, _Semi)483 }484 }485 p.print(_Rbrace)486487 case *FuncType:488 p.print(_Func)489 p.printSignature(n)490491 case *InterfaceType:492 p.print(_Interface)493 if p.linebreaks && len(n.MethodList) > 1 {494 p.print(blank)495 p.print(_Lbrace)496 p.print(newline, indent)497 p.printMethodList(n.MethodList)498 p.print(outdent, newline)499 } else {500 p.print(_Lbrace)501 p.printMethodList(n.MethodList)502 }503 p.print(_Rbrace)504505 case *MapType:506 p.print(_Map, _Lbrack, n.Key, _Rbrack, n.Value)507508 case *ChanType:509 if n.Dir == RecvOnly {510 p.print(_Arrow)511 }512 p.print(_Chan)513 if n.Dir == SendOnly {514 p.print(_Arrow)515 }516 p.print(blank)517 if e, _ := n.Elem.(*ChanType); n.Dir == 0 && e != nil && e.Dir == RecvOnly {518 // don't print chan (<-chan T) as chan <-chan T519 p.print(_Lparen)520 p.print(n.Elem)521 p.print(_Rparen)522 } else {523 p.print(n.Elem)524 }525526 // statements527 case *DeclStmt:528 p.printDecl(n.DeclList)529530 case *EmptyStmt:531 // nothing to print532533 case *LabeledStmt:534 p.print(outdent, n.Label, _Colon, indent, newline, n.Stmt)535536 case *ExprStmt:537 p.print(n.X)538539 case *SendStmt:540 p.print(n.Chan, blank, _Arrow, blank, n.Value)541542 case *AssignStmt:543 p.print(n.Lhs)544 if n.Rhs == nil {545 // TODO(gri) This is going to break the mayCombine546 // check once we enable that again.547 p.print(n.Op, n.Op) // ++ or --548 } else {549 p.print(blank, n.Op, _Assign, blank)550 p.print(n.Rhs)551 }552553 case *CallStmt:554 p.print(n.Tok, blank, n.Call)555556 case *ReturnStmt:557 p.print(_Return)558 if n.Results != nil {559 p.print(blank, n.Results)560 }561562 case *BranchStmt:563 p.print(n.Tok)564 if n.Label != nil {565 p.print(blank, n.Label)566 }567568 case *BlockStmt:569 p.print(_Lbrace)570 if len(n.List) > 0 {571 p.print(newline, indent)572 p.printStmtList(n.List, true)573 p.print(outdent, newline)574 }575 p.print(_Rbrace)576577 case *IfStmt:578 p.print(_If, blank)579 if n.Init != nil {580 p.print(n.Init, _Semi, blank)581 }582 p.print(n.Cond, blank, n.Then)583 if n.Else != nil {584 p.print(blank, _Else, blank, n.Else)585 }586587 case *SwitchStmt:588 p.print(_Switch, blank)589 if n.Init != nil {590 p.print(n.Init, _Semi, blank)591 }592 if n.Tag != nil {593 p.print(n.Tag, blank)594 }595 p.printSwitchBody(n.Body)596597 case *SelectStmt:598 p.print(_Select, blank) // for now599 p.printSelectBody(n.Body)600601 case *RangeClause:602 if n.Lhs != nil {603 tok := _Assign604 if n.Def {605 tok = _Define606 }607 p.print(n.Lhs, blank, tok, blank)608 }609 p.print(_Range, blank, n.X)610611 case *ForStmt:612 p.print(_For, blank)613 if n.Init == nil && n.Post == nil {614 if n.Cond != nil {615 p.print(n.Cond, blank)616 }617 } else {618 if n.Init != nil {619 p.print(n.Init)620 // TODO(gri) clean this up621 if _, ok := n.Init.(*RangeClause); ok {622 p.print(blank, n.Body)623 break624 }625 }626 p.print(_Semi, blank)627 if n.Cond != nil {628 p.print(n.Cond)629 }630 p.print(_Semi, blank)631 if n.Post != nil {632 p.print(n.Post, blank)633 }634 }635 p.print(n.Body)636637 case *ImportDecl:638 if n.Group == nil {639 p.print(_Import, blank)640 }641 if n.LocalPkgName != nil {642 p.print(n.LocalPkgName, blank)643 }644 p.print(n.Path)645646 case *ConstDecl:647 if n.Group == nil {648 p.print(_Const, blank)649 }650 p.printNameList(n.NameList)651 if n.Type != nil {652 p.print(blank, n.Type)653 }654 if n.Values != nil {655 p.print(blank, _Assign, blank, n.Values)656 }657658 case *TypeDecl:659 if n.Group == nil {660 p.print(_Type, blank)661 }662 p.print(n.Name)663 if n.TParamList != nil {664 p.printParameterList(n.TParamList, _Type)665 }666 p.print(blank)667 if n.Alias {668 p.print(_Assign, blank)669 }670 p.print(n.Type)671672 case *VarDecl:673 if n.Group == nil {674 p.print(_Var, blank)675 }676 p.printNameList(n.NameList)677 if n.Type != nil {678 p.print(blank, n.Type)679 }680 if n.Values != nil {681 p.print(blank, _Assign, blank, n.Values)682 }683684 case *FuncDecl:685 p.print(_Func, blank)686 if r := n.Recv; r != nil {687 p.print(_Lparen)688 if r.Name != nil {689 p.print(r.Name, blank)690 }691 p.printNode(r.Type)692 p.print(_Rparen, blank)693 }694 p.print(n.Name)695 if n.TParamList != nil {696 p.printParameterList(n.TParamList, _Func)697 }698 p.printSignature(n.Type)699 if n.Body != nil {700 p.print(blank, n.Body)701 }702703 case *printGroup:704 p.print(n.Tok, blank, _Lparen)705 if len(n.Decls) > 0 {706 p.print(newline, indent)707 for _, d := range n.Decls {708 p.printNode(d)709 p.print(_Semi, newline)710 }711 p.print(outdent)712 }713 p.print(_Rparen)714715 // files716 case *File:717 p.print(_Package, blank, n.PkgName)718 if len(n.DeclList) > 0 {719 p.print(_Semi, newline, newline)720 p.printDeclList(n.DeclList)721 }722723 default:724 panic(fmt.Sprintf("syntax.Iterate: unexpected node type %T", n))725 }726}727728func (p *printer) printFields(fields []*Field, tags []*BasicLit, i, j int) {729 if i+1 == j && fields[i].Name == nil {730 // anonymous field731 p.printNode(fields[i].Type)732 } else {733 for k, f := range fields[i:j] {734 if k > 0 {735 p.print(_Comma, blank)736 }737 p.printNode(f.Name)738 }739 p.print(blank)740 p.printNode(fields[i].Type)741 }742 if i < len(tags) && tags[i] != nil {743 p.print(blank)744 p.printNode(tags[i])745 }746}747748func (p *printer) printFieldList(fields []*Field, tags []*BasicLit, sep token) {749 i0 := 0750 var typ Expr751 for i, f := range fields {752 if f.Name == nil || f.Type != typ {753 if i0 < i {754 p.printFields(fields, tags, i0, i)755 p.print(sep, newline)756 i0 = i757 }758 typ = f.Type759 }760 }761 p.printFields(fields, tags, i0, len(fields))762}763764func (p *printer) printMethodList(methods []*Field) {765 for i, m := range methods {766 if i > 0 {767 p.print(_Semi, newline)768 }769 if m.Name != nil {770 p.printNode(m.Name)771 p.printSignature(m.Type.(*FuncType))772 } else {773 p.printNode(m.Type)774 }775 }776}777778func (p *printer) printNameList(list []*Name) {779 for i, x := range list {780 if i > 0 {781 p.print(_Comma, blank)782 }783 p.printNode(x)784 }785}786787func (p *printer) printExprList(list []Expr) {788 for i, x := range list {789 if i > 0 {790 p.print(_Comma, blank)791 }792 p.printNode(x)793 }794}795796func (p *printer) printExprLines(list []Expr) {797 if len(list) > 0 {798 p.print(newline, indent)799 for _, x := range list {800 p.print(x, _Comma, newline)801 }802 p.print(outdent)803 }804}805806func groupFor(d Decl) (token, *Group) {807 switch d := d.(type) {808 case *ImportDecl:809 return _Import, d.Group810 case *ConstDecl:811 return _Const, d.Group812 case *TypeDecl:813 return _Type, d.Group814 case *VarDecl:815 return _Var, d.Group816 case *FuncDecl:817 return _Func, nil818 default:819 panic("unreachable")820 }821}822823type printGroup struct {824 node825 Tok token826 Decls []Decl827}828829func (p *printer) printDecl(list []Decl) {830 tok, group := groupFor(list[0])831832 if group == nil {833 if len(list) != 1 {834 panic("unreachable")835 }836 p.printNode(list[0])837 return838 }839840 // if _, ok := list[0].(*EmptyDecl); ok {841 // if len(list) != 1 {842 // panic("unreachable")843 // }844 // // TODO(gri) if there are comments inside the empty845 // // group, we may need to keep the list non-nil846 // list = nil847 // }848849 // printGroup is here for consistent comment handling850 // (this is not yet used)851 var pg printGroup852 // *pg.Comments() = *group.Comments()853 pg.Tok = tok854 pg.Decls = list855 p.printNode(&pg)856}857858func (p *printer) printDeclList(list []Decl) {859 i0 := 0860 var tok token861 var group *Group862 for i, x := range list {863 if s, g := groupFor(x); g == nil || g != group {864 if i0 < i {865 p.printDecl(list[i0:i])866 p.print(_Semi, newline)867 // print empty line between different declaration groups,868 // different kinds of declarations, or between functions869 if g != group || s != tok || s == _Func {870 p.print(newline)871 }872 i0 = i873 }874 tok, group = s, g875 }876 }877 p.printDecl(list[i0:])878}879880func (p *printer) printSignature(sig *FuncType) {881 p.printParameterList(sig.ParamList, 0)882 if list := sig.ResultList; list != nil {883 p.print(blank)884 if len(list) == 1 && list[0].Name == nil {885 p.printNode(list[0].Type)886 } else {887 p.printParameterList(list, 0)888 }889 }890}891892// If tok != 0 print a type parameter list: tok == _Type means893// a type parameter list for a type, tok == _Func means a type894// parameter list for a func.895func (p *printer) printParameterList(list []*Field, tok token) {896 open, close := _Lparen, _Rparen897 if tok != 0 {898 open, close = _Lbrack, _Rbrack899 }900 p.print(open)901 for i, f := range list {902 if i > 0 {903 p.print(_Comma, blank)904 }905 if f.Name != nil {906 p.printNode(f.Name)907 if i+1 < len(list) {908 f1 := list[i+1]909 if f1.Name != nil && f1.Type == f.Type {910 continue // no need to print type911 }912 }913 p.print(blank)914 }915 p.printNode(f.Type)916 }917 // A type parameter list [P T] where the name P and the type expression T syntactically918 // combine to another valid (value) expression requires a trailing comma, as in [P *T,]919 // (or an enclosing interface as in [P interface(*T)]), so that the type parameter list920 // is not parsed as an array length [P*T].921 if tok == _Type && len(list) == 1 && combinesWithName(list[0].Type) {922 p.print(_Comma)923 }924 p.print(close)925}926927// combinesWithName reports whether a name followed by the expression x928// syntactically combines to another valid (value) expression. For instance929// using *T for x, "name *T" syntactically appears as the expression x*T.930// On the other hand, using P|Q or *P|~Q for x, "name P|Q" or "name *P|~Q"931// cannot be combined into a valid (value) expression.932func combinesWithName(x Expr) bool {933 switch x := x.(type) {934 case *Operation:935 if x.Y == nil {936 // name *x.X combines to name*x.X if x.X is not a type element937 return x.Op == Mul && !isTypeElem(x.X)938 }939 // binary expressions940 return combinesWithName(x.X) && !isTypeElem(x.Y)941 case *ParenExpr:942 // Note that the parser strips parentheses in these cases943 // (see extractName, parser.typeOrNil) unless keep_parens944 // is set, so we should never reach here.945 // Do the right thing (rather than panic) for testing and946 // in case we change parser behavior.947 // See also go.dev/issues/69206.948 return !isTypeElem(x.X)949 }950 return false951}952953func (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 is960 // an empty statement and we are in a braced block961 // because one semicolon is automatically removed.962 if _, ok := x.(*EmptyStmt); ok {963 p.print(x, _Semi)964 }965 }966 }967}968969func (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}980981func (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}992993func (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}10061007func (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}
Findings
✓ No findings reported for this file.