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
7package syntax
8
9import (
10	"fmt"
11	"io"
12	"strings"
13)
14
15// Form controls print formatting.
16type Form uint
17
18const (
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.
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	}
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.
48func 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
57type ctrlSymbol int
58
59const (
60	none ctrlSymbol = iota
61	semi
62	blank
63	newline
64	indent
65	outdent
66	// comment
67	// eolComment
68)
69
70type whitespace struct {
71	last token
72	kind ctrlSymbol
73	//text string // comment text (possibly ""); valid if kind == comment
74}
75
76type 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.
91func (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
99var (
100	tabBytes    = []byte("\t\t\t\t\t\t\t\t")
101	newlineByte = []byte("\n")
102	blankByte   = []byte(" ")
103)
104
105func (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
122func (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.
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 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
141func lineComment(text string) bool {
142	return strings.HasPrefix(text, "//")
143}
144
145func (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
156func (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
235func 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
254func (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
320func (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
353func (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
732func (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
752func (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
768func (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
782func (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
791func (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
800func (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
810func 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
827type printGroup struct {
828	node
829	Tok   token
830	Decls []Decl
831}
832
833func (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
862func (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
884func (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.
899func (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.
936func 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
953func (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
969func (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
981func (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
993func (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
1007func (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}
1021