src/cmd/compile/internal/syntax/printer.go GO 1,021 lines View on github.com → Search inside
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.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.