src/cmd/compile/internal/ir/fmt.go GO 1,218 lines View on github.com → Search inside
1// Copyright 2011 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.45package ir67import (8	"bytes"9	"fmt"10	"go/constant"11	"io"12	"os"13	"path/filepath"14	"reflect"15	"strings"1617	"unicode/utf8"1819	"cmd/compile/internal/base"20	"cmd/compile/internal/types"21	"cmd/internal/src"22)2324// Op2526var OpNames = []string{27	OADDR:             "&",28	OADD:              "+",29	OADDSTR:           "+",30	OANDAND:           "&&",31	OANDNOT:           "&^",32	OAND:              "&",33	OAPPEND:           "append",34	OAS:               "=",35	OAS2:              "=",36	OBREAK:            "break",37	OCALL:             "function call", // not actual syntax38	OCAP:              "cap",39	OCASE:             "case",40	OCLEAR:            "clear",41	OCLOSE:            "close",42	OCOMPLEX:          "complex",43	OBITNOT:           "^",44	OCONTINUE:         "continue",45	OCOPY:             "copy",46	ODELETE:           "delete",47	ODEFER:            "defer",48	ODIV:              "/",49	OEQ:               "==",50	OFALL:             "fallthrough",51	OFOR:              "for",52	OGE:               ">=",53	OGOTO:             "goto",54	OGT:               ">",55	OIF:               "if",56	OIMAG:             "imag",57	OINLMARK:          "inlmark",58	ODEREF:            "*",59	OLEN:              "len",60	OLE:               "<=",61	OLSH:              "<<",62	OLT:               "<",63	OMAKE:             "make",64	ONEG:              "-",65	OMAX:              "max",66	OMIN:              "min",67	OMOD:              "%",68	OMUL:              "*",69	ONEW:              "new",70	ONE:               "!=",71	ONOT:              "!",72	OOROR:             "||",73	OOR:               "|",74	OPANIC:            "panic",75	OPLUS:             "+",76	OPRINTLN:          "println",77	OPRINT:            "print",78	ORANGE:            "range",79	OREAL:             "real",80	ORECV:             "<-",81	ORECOVER:          "recover",82	ORETURN:           "return",83	ORSH:              ">>",84	OSELECT:           "select",85	OSEND:             "<-",86	OSUB:              "-",87	OSWITCH:           "switch",88	OUNSAFEADD:        "unsafe.Add",89	OUNSAFESLICE:      "unsafe.Slice",90	OUNSAFESLICEDATA:  "unsafe.SliceData",91	OUNSAFESTRING:     "unsafe.String",92	OUNSAFESTRINGDATA: "unsafe.StringData",93	OXOR:              "^",94}9596// GoString returns the Go syntax for the Op, or else its name.97func (o Op) GoString() string {98	if int(o) < len(OpNames) && OpNames[o] != "" {99		return OpNames[o]100	}101	return o.String()102}103104// Format implements formatting for an Op.105// The valid formats are:106//107//	%v	Go syntax ("+", "<-", "print")108//	%+v	Debug syntax ("ADD", "RECV", "PRINT")109func (o Op) Format(s fmt.State, verb rune) {110	switch verb {111	default:112		fmt.Fprintf(s, "%%!%c(Op=%d)", verb, int(o))113	case 'v':114		if s.Flag('+') {115			// %+v is OMUL instead of "*"116			io.WriteString(s, o.String())117			return118		}119		io.WriteString(s, o.GoString())120	}121}122123// Node124125// fmtNode implements formatting for a Node n.126// Every Node implementation must define a Format method that calls fmtNode.127// The valid formats are:128//129//	%v	Go syntax130//	%L	Go syntax followed by " (type T)" if type is known.131//	%+v	Debug syntax, as in Dump.132func fmtNode(n Node, s fmt.State, verb rune) {133	// %+v prints Dump.134	// Otherwise we print Go syntax.135	if s.Flag('+') && verb == 'v' {136		dumpNode(s, n, 1)137		return138	}139140	if verb != 'v' && verb != 'S' && verb != 'L' {141		fmt.Fprintf(s, "%%!%c(*Node=%p)", verb, n)142		return143	}144145	if n == nil {146		fmt.Fprint(s, "<nil>")147		return148	}149150	t := n.Type()151	if verb == 'L' && t != nil {152		if t.Kind() == types.TNIL {153			fmt.Fprint(s, "nil")154		} else if n.Op() == ONAME && n.Name().AutoTemp() {155			fmt.Fprintf(s, "%v value", t)156		} else {157			fmt.Fprintf(s, "%v (type %v)", n, t)158		}159		return160	}161162	// TODO inlining produces expressions with ninits. we can't print these yet.163164	if OpPrec[n.Op()] < 0 {165		stmtFmt(n, s)166		return167	}168169	exprFmt(n, s, 0)170}171172var OpPrec = []int{173	OAPPEND:           8,174	OBYTES2STR:        8,175	OARRAYLIT:         8,176	OSLICELIT:         8,177	ORUNES2STR:        8,178	OCALLFUNC:         8,179	OCALLINTER:        8,180	OCALLMETH:         8,181	OCALL:             8,182	OCAP:              8,183	OCLEAR:            8,184	OCLOSE:            8,185	OCOMPLIT:          8,186	OCONVIFACE:        8,187	OCONVNOP:          8,188	OCONV:             8,189	OCOPY:             8,190	ODELETE:           8,191	OGETG:             8,192	OLEN:              8,193	OLITERAL:          8,194	OMAKESLICE:        8,195	OMAKESLICECOPY:    8,196	OMAKE:             8,197	OMAPLIT:           8,198	OMAX:              8,199	OMIN:              8,200	ONAME:             8,201	ONEW:              8,202	ONIL:              8,203	ONONAME:           8,204	OPANIC:            8,205	OPAREN:            8,206	OPRINTLN:          8,207	OPRINT:            8,208	ORUNESTR:          8,209	OSLICE2ARR:        8,210	OSLICE2ARRPTR:     8,211	OSTR2BYTES:        8,212	OSTR2RUNES:        8,213	OSTRUCTLIT:        8,214	OTYPE:             8,215	OUNSAFEADD:        8,216	OUNSAFESLICE:      8,217	OUNSAFESLICEDATA:  8,218	OUNSAFESTRING:     8,219	OUNSAFESTRINGDATA: 8,220	OINDEXMAP:         8,221	OINDEX:            8,222	OSLICE:            8,223	OSLICESTR:         8,224	OSLICEARR:         8,225	OSLICE3:           8,226	OSLICE3ARR:        8,227	OSLICEHEADER:      8,228	OSTRINGHEADER:     8,229	ODOTINTER:         8,230	ODOTMETH:          8,231	ODOTPTR:           8,232	ODOTTYPE2:         8,233	ODOTTYPE:          8,234	ODOT:              8,235	OXDOT:             8,236	OMETHVALUE:        8,237	OMETHEXPR:         8,238	OPLUS:             7,239	ONOT:              7,240	OBITNOT:           7,241	ONEG:              7,242	OADDR:             7,243	ODEREF:            7,244	ORECV:             7,245	OMUL:              6,246	ODIV:              6,247	OMOD:              6,248	OLSH:              6,249	ORSH:              6,250	OAND:              6,251	OANDNOT:           6,252	OADD:              5,253	OSUB:              5,254	OOR:               5,255	OXOR:              5,256	OEQ:               4,257	OLT:               4,258	OLE:               4,259	OGE:               4,260	OGT:               4,261	ONE:               4,262	OSEND:             3,263	OANDAND:           2,264	OOROR:             1,265266	// Statements handled by stmtfmt267	OAS:         -1,268	OAS2:        -1,269	OAS2DOTTYPE: -1,270	OAS2FUNC:    -1,271	OAS2MAPR:    -1,272	OAS2RECV:    -1,273	OASOP:       -1,274	OBLOCK:      -1,275	OBREAK:      -1,276	OCASE:       -1,277	OCONTINUE:   -1,278	ODCL:        -1,279	ODEFER:      -1,280	OFALL:       -1,281	OFOR:        -1,282	OGOTO:       -1,283	OIF:         -1,284	OLABEL:      -1,285	OGO:         -1,286	ORANGE:      -1,287	ORETURN:     -1,288	OSELECT:     -1,289	OSWITCH:     -1,290291	OEND: 0,292}293294// StmtWithInit reports whether op is a statement with an explicit init list.295func StmtWithInit(op Op) bool {296	switch op {297	case OIF, OFOR, OSWITCH:298		return true299	}300	return false301}302303func stmtFmt(n Node, s fmt.State) {304	// NOTE(rsc): This code used to support the text-based305	// which was more aggressive about printing full Go syntax306	// (for example, an actual loop instead of "for loop").307	// The code is preserved for now in case we want to expand308	// any of those shortenings later. Or maybe we will delete309	// the code. But for now, keep it.310	const exportFormat = false311312	// some statements allow for an init, but at most one,313	// but we may have an arbitrary number added, eg by typecheck314	// and inlining. If it doesn't fit the syntax, emit an enclosing315	// block starting with the init statements.316317	// if we can just say "for" n->ninit; ... then do so318	simpleinit := len(n.Init()) == 1 && len(n.Init()[0].Init()) == 0 && StmtWithInit(n.Op())319320	// otherwise, print the inits as separate statements321	complexinit := len(n.Init()) != 0 && !simpleinit && exportFormat322323	// but if it was for if/for/switch, put in an extra surrounding block to limit the scope324	extrablock := complexinit && StmtWithInit(n.Op())325326	if extrablock {327		fmt.Fprint(s, "{")328	}329330	if complexinit {331		fmt.Fprintf(s, " %v; ", n.Init())332	}333334	switch n.Op() {335	case ODCL:336		n := n.(*Decl)337		fmt.Fprintf(s, "var %v %v", n.X.Sym(), n.X.Type())338339	// Don't export "v = <N>" initializing statements, hope they're always340	// preceded by the DCL which will be re-parsed and typechecked to reproduce341	// the "v = <N>" again.342	case OAS:343		n := n.(*AssignStmt)344		if n.Def && !complexinit {345			fmt.Fprintf(s, "%v := %v", n.X, n.Y)346		} else {347			fmt.Fprintf(s, "%v = %v", n.X, n.Y)348		}349350	case OASOP:351		n := n.(*AssignOpStmt)352		if n.IncDec {353			if n.AsOp == OADD {354				fmt.Fprintf(s, "%v++", n.X)355			} else {356				fmt.Fprintf(s, "%v--", n.X)357			}358			break359		}360361		fmt.Fprintf(s, "%v %v= %v", n.X, n.AsOp, n.Y)362363	case OAS2, OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV:364		n := n.(*AssignListStmt)365		if n.Def && !complexinit {366			fmt.Fprintf(s, "%.v := %.v", n.Lhs, n.Rhs)367		} else {368			fmt.Fprintf(s, "%.v = %.v", n.Lhs, n.Rhs)369		}370371	case OBLOCK:372		n := n.(*BlockStmt)373		if len(n.List) != 0 {374			fmt.Fprintf(s, "%v", n.List)375		}376377	case ORETURN:378		n := n.(*ReturnStmt)379		fmt.Fprintf(s, "return %.v", n.Results)380381	case OTAILCALL:382		n := n.(*TailCallStmt)383		fmt.Fprintf(s, "tailcall %v", n.Call)384385	case OINLMARK:386		n := n.(*InlineMarkStmt)387		fmt.Fprintf(s, "inlmark %d", n.Index)388389	case OGO:390		n := n.(*GoDeferStmt)391		fmt.Fprintf(s, "go %v", n.Call)392393	case ODEFER:394		n := n.(*GoDeferStmt)395		fmt.Fprintf(s, "defer %v", n.Call)396397	case OIF:398		n := n.(*IfStmt)399		if simpleinit {400			fmt.Fprintf(s, "if %v; %v { %v }", n.Init()[0], n.Cond, n.Body)401		} else {402			fmt.Fprintf(s, "if %v { %v }", n.Cond, n.Body)403		}404		if len(n.Else) != 0 {405			fmt.Fprintf(s, " else { %v }", n.Else)406		}407408	case OFOR:409		n := n.(*ForStmt)410		if !exportFormat { // TODO maybe only if FmtShort, same below411			fmt.Fprintf(s, "for loop")412			break413		}414415		fmt.Fprint(s, "for")416		if n.DistinctVars {417			fmt.Fprint(s, " /* distinct */")418		}419		if simpleinit {420			fmt.Fprintf(s, " %v;", n.Init()[0])421		} else if n.Post != nil {422			fmt.Fprint(s, " ;")423		}424425		if n.Cond != nil {426			fmt.Fprintf(s, " %v", n.Cond)427		}428429		if n.Post != nil {430			fmt.Fprintf(s, "; %v", n.Post)431		} else if simpleinit {432			fmt.Fprint(s, ";")433		}434435		fmt.Fprintf(s, " { %v }", n.Body)436437	case ORANGE:438		n := n.(*RangeStmt)439		if !exportFormat {440			fmt.Fprint(s, "for loop")441			break442		}443444		fmt.Fprint(s, "for")445		if n.Key != nil {446			fmt.Fprintf(s, " %v", n.Key)447			if n.Value != nil {448				fmt.Fprintf(s, ", %v", n.Value)449			}450			fmt.Fprint(s, " =")451		}452		fmt.Fprintf(s, " range %v { %v }", n.X, n.Body)453		if n.DistinctVars {454			fmt.Fprint(s, " /* distinct vars */")455		}456457	case OSELECT:458		n := n.(*SelectStmt)459		if !exportFormat {460			fmt.Fprintf(s, "%v statement", n.Op())461			break462		}463		fmt.Fprintf(s, "select { %v }", n.Cases)464465	case OSWITCH:466		n := n.(*SwitchStmt)467		if !exportFormat {468			fmt.Fprintf(s, "%v statement", n.Op())469			break470		}471		fmt.Fprintf(s, "switch")472		if simpleinit {473			fmt.Fprintf(s, " %v;", n.Init()[0])474		}475		if n.Tag != nil {476			fmt.Fprintf(s, " %v ", n.Tag)477		}478		fmt.Fprintf(s, " { %v }", n.Cases)479480	case OCASE:481		n := n.(*CaseClause)482		if len(n.List) != 0 {483			fmt.Fprintf(s, "case %.v", n.List)484		} else {485			fmt.Fprint(s, "default")486		}487		fmt.Fprintf(s, ": %v", n.Body)488489	case OBREAK, OCONTINUE, OGOTO, OFALL:490		n := n.(*BranchStmt)491		if n.Label != nil {492			fmt.Fprintf(s, "%v %v", n.Op(), n.Label)493		} else {494			fmt.Fprintf(s, "%v", n.Op())495		}496497	case OLABEL:498		n := n.(*LabelStmt)499		fmt.Fprintf(s, "%v: ", n.Label)500	}501502	if extrablock {503		fmt.Fprint(s, "}")504	}505}506507func exprFmt(n Node, s fmt.State, prec int) {508	// NOTE(rsc): This code used to support the text-based509	// which was more aggressive about printing full Go syntax510	// (for example, an actual loop instead of "for loop").511	// The code is preserved for now in case we want to expand512	// any of those shortenings later. Or maybe we will delete513	// the code. But for now, keep it.514	const exportFormat = false515516	for {517		if n == nil {518			fmt.Fprint(s, "<nil>")519			return520		}521522		// Skip implicit operations introduced during typechecking.523		switch nn := n; nn.Op() {524		case OADDR:525			nn := nn.(*AddrExpr)526			if nn.Implicit() {527				n = nn.X528				continue529			}530		case ODEREF:531			nn := nn.(*StarExpr)532			if nn.Implicit() {533				n = nn.X534				continue535			}536		case OCONV, OCONVNOP, OCONVIFACE:537			nn := nn.(*ConvExpr)538			if nn.Implicit() {539				n = nn.X540				continue541			}542		}543544		break545	}546547	nprec := OpPrec[n.Op()]548	if n.Op() == OTYPE && n.Type() != nil && n.Type().IsPtr() {549		nprec = OpPrec[ODEREF]550	}551552	if prec > nprec {553		fmt.Fprintf(s, "(%v)", n)554		return555	}556557	switch n.Op() {558	case OPAREN:559		n := n.(*ParenExpr)560		fmt.Fprintf(s, "(%v)", n.X)561562	case ONIL:563		fmt.Fprint(s, "nil")564565	case OLITERAL:566		if n.Sym() != nil {567			fmt.Fprint(s, n.Sym())568			return569		}570571		typ := n.Type()572		val := n.Val()573574		// Special case for rune constants.575		if typ == types.RuneType || typ == types.UntypedRune {576			if x, ok := constant.Uint64Val(val); ok && x <= utf8.MaxRune {577				fmt.Fprintf(s, "%q", rune(x))578				return579			}580		}581582		// Only include typ if it's neither the default nor untyped type583		// for the constant value.584		if k := val.Kind(); typ == types.Types[types.DefaultKinds[k]] || typ == types.UntypedTypes[k] {585			fmt.Fprint(s, val)586		} else {587			fmt.Fprintf(s, "%v(%v)", typ, val)588		}589590	case ODCLFUNC:591		n := n.(*Func)592		if sym := n.Sym(); sym != nil {593			fmt.Fprint(s, sym)594			return595		}596		fmt.Fprintf(s, "<unnamed Func>")597598	case ONAME:599		n := n.(*Name)600		// Special case: name used as local variable in export.601		// _ becomes ~b%d internally; print as _ for export602		if !exportFormat && n.Sym() != nil && n.Sym().Name[0] == '~' && n.Sym().Name[1] == 'b' {603			fmt.Fprint(s, "_")604			return605		}606		fallthrough607	case ONONAME:608		fmt.Fprint(s, n.Sym())609610	case OLINKSYMOFFSET:611		n := n.(*LinksymOffsetExpr)612		fmt.Fprintf(s, "(%v)(%s@%d)", n.Type(), n.Linksym.Name, n.Offset_)613614	case OTYPE:615		if n.Type() == nil && n.Sym() != nil {616			fmt.Fprint(s, n.Sym())617			return618		}619		fmt.Fprintf(s, "%v", n.Type())620621	case OCLOSURE:622		n := n.(*ClosureExpr)623		if !exportFormat {624			fmt.Fprint(s, "func literal")625			return626		}627		fmt.Fprintf(s, "%v { %v }", n.Type(), n.Func.Body)628629	case OPTRLIT:630		n := n.(*AddrExpr)631		fmt.Fprintf(s, "&%v", n.X)632633	case OCOMPLIT, OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:634		n := n.(*CompLitExpr)635		if n.Implicit() {636			fmt.Fprintf(s, "... argument")637			return638		}639		fmt.Fprintf(s, "%v{%s}", n.Type(), ellipsisIf(len(n.List) != 0))640641	case OKEY:642		n := n.(*KeyExpr)643		if n.Key != nil && n.Value != nil {644			fmt.Fprintf(s, "%v:%v", n.Key, n.Value)645			return646		}647648		if n.Key == nil && n.Value != nil {649			fmt.Fprintf(s, ":%v", n.Value)650			return651		}652		if n.Key != nil && n.Value == nil {653			fmt.Fprintf(s, "%v:", n.Key)654			return655		}656		fmt.Fprint(s, ":")657658	case OSTRUCTKEY:659		n := n.(*StructKeyExpr)660		fmt.Fprintf(s, "%v:%v", n.Field, n.Value)661662	case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH, OMETHVALUE, OMETHEXPR:663		n := n.(*SelectorExpr)664		exprFmt(n.X, s, nprec)665		if n.Sel == nil {666			fmt.Fprint(s, ".<nil>")667			return668		}669		fmt.Fprintf(s, ".%s", n.Sel.Name)670671	case ODOTTYPE, ODOTTYPE2:672		n := n.(*TypeAssertExpr)673		exprFmt(n.X, s, nprec)674		fmt.Fprintf(s, ".(%v)", n.Type())675676	case OINDEX, OINDEXMAP:677		n := n.(*IndexExpr)678		exprFmt(n.X, s, nprec)679		fmt.Fprintf(s, "[%v]", n.Index)680681	case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR:682		n := n.(*SliceExpr)683		exprFmt(n.X, s, nprec)684		fmt.Fprint(s, "[")685		if n.Low != nil {686			fmt.Fprint(s, n.Low)687		}688		fmt.Fprint(s, ":")689		if n.High != nil {690			fmt.Fprint(s, n.High)691		}692		if n.Op().IsSlice3() {693			fmt.Fprint(s, ":")694			if n.Max != nil {695				fmt.Fprint(s, n.Max)696			}697		}698		fmt.Fprint(s, "]")699700	case OSLICEHEADER:701		n := n.(*SliceHeaderExpr)702		fmt.Fprintf(s, "sliceheader{%v,%v,%v}", n.Ptr, n.Len, n.Cap)703704	case OCOMPLEX, OCOPY, OUNSAFEADD, OUNSAFESLICE:705		n := n.(*BinaryExpr)706		fmt.Fprintf(s, "%v(%v, %v)", n.Op(), n.X, n.Y)707708	case OCONV,709		OCONVIFACE,710		OCONVNOP,711		OBYTES2STR,712		ORUNES2STR,713		OSTR2BYTES,714		OSTR2RUNES,715		ORUNESTR,716		OSLICE2ARR,717		OSLICE2ARRPTR:718		n := n.(*ConvExpr)719		if n.Type() == nil || n.Type().Sym() == nil {720			fmt.Fprintf(s, "(%v)", n.Type())721		} else {722			fmt.Fprintf(s, "%v", n.Type())723		}724		fmt.Fprintf(s, "(%v)", n.X)725726	case OREAL,727		OIMAG,728		OCAP,729		OCLEAR,730		OCLOSE,731		OLEN,732		ONEW,733		OPANIC:734		n := n.(*UnaryExpr)735		fmt.Fprintf(s, "%v(%v)", n.Op(), n.X)736737	case OAPPEND,738		ODELETE,739		OMAKE,740		OMAX,741		OMIN,742		ORECOVER,743		OPRINT,744		OPRINTLN:745		n := n.(*CallExpr)746		if n.IsDDD {747			fmt.Fprintf(s, "%v(%.v...)", n.Op(), n.Args)748			return749		}750		fmt.Fprintf(s, "%v(%.v)", n.Op(), n.Args)751752	case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG:753		n := n.(*CallExpr)754		exprFmt(n.Fun, s, nprec)755		if n.IsDDD {756			fmt.Fprintf(s, "(%.v...)", n.Args)757			return758		}759		fmt.Fprintf(s, "(%.v)", n.Args)760761	case OINLCALL:762		n := n.(*InlinedCallExpr)763		// TODO(mdempsky): Print Init and/or Body?764		if len(n.ReturnVars) == 1 {765			fmt.Fprintf(s, "%v", n.ReturnVars[0])766			return767		}768		fmt.Fprintf(s, "(.%v)", n.ReturnVars)769770	case OMAKEMAP, OMAKECHAN, OMAKESLICE:771		n := n.(*MakeExpr)772		if n.Cap != nil {773			fmt.Fprintf(s, "make(%v, %v, %v)", n.Type(), n.Len, n.Cap)774			return775		}776		if n.Len != nil && (n.Op() == OMAKESLICE || !n.Len.Type().IsUntyped()) {777			fmt.Fprintf(s, "make(%v, %v)", n.Type(), n.Len)778			return779		}780		fmt.Fprintf(s, "make(%v)", n.Type())781782	case OMAKESLICECOPY:783		n := n.(*MakeExpr)784		fmt.Fprintf(s, "makeslicecopy(%v, %v, %v)", n.Type(), n.Len, n.Cap)785786	case OPLUS, ONEG, OBITNOT, ONOT, ORECV:787		// Unary788		n := n.(*UnaryExpr)789		fmt.Fprintf(s, "%v", n.Op())790		if n.X != nil && n.X.Op() == n.Op() {791			fmt.Fprint(s, " ")792		}793		exprFmt(n.X, s, nprec+1)794795	case OADDR:796		n := n.(*AddrExpr)797		fmt.Fprintf(s, "%v", n.Op())798		if n.X != nil && n.X.Op() == n.Op() {799			fmt.Fprint(s, " ")800		}801		exprFmt(n.X, s, nprec+1)802803	case ODEREF:804		n := n.(*StarExpr)805		fmt.Fprintf(s, "%v", n.Op())806		exprFmt(n.X, s, nprec+1)807808		// Binary809	case OADD,810		OAND,811		OANDNOT,812		ODIV,813		OEQ,814		OGE,815		OGT,816		OLE,817		OLT,818		OLSH,819		OMOD,820		OMUL,821		ONE,822		OOR,823		ORSH,824		OSUB,825		OXOR:826		n := n.(*BinaryExpr)827		exprFmt(n.X, s, nprec)828		fmt.Fprintf(s, " %v ", n.Op())829		exprFmt(n.Y, s, nprec+1)830831	case OANDAND,832		OOROR:833		n := n.(*LogicalExpr)834		exprFmt(n.X, s, nprec)835		fmt.Fprintf(s, " %v ", n.Op())836		exprFmt(n.Y, s, nprec+1)837838	case OSEND:839		n := n.(*SendStmt)840		exprFmt(n.Chan, s, nprec)841		fmt.Fprintf(s, " <- ")842		exprFmt(n.Value, s, nprec+1)843844	case OADDSTR:845		n := n.(*AddStringExpr)846		for i, n1 := range n.List {847			if i != 0 {848				fmt.Fprint(s, " + ")849			}850			exprFmt(n1, s, nprec)851		}852	default:853		fmt.Fprintf(s, "<node %v>", n.Op())854	}855}856857func ellipsisIf(b bool) string {858	if b {859		return "..."860	}861	return ""862}863864// Nodes865866// Format implements formatting for a Nodes.867// The valid formats are:868//869//	%v	Go syntax, semicolon-separated870//	%.v	Go syntax, comma-separated871//	%+v	Debug syntax, as in DumpList.872func (l Nodes) Format(s fmt.State, verb rune) {873	if s.Flag('+') && verb == 'v' {874		// %+v is DumpList output875		dumpNodes(s, l, 1)876		return877	}878879	if verb != 'v' {880		fmt.Fprintf(s, "%%!%c(Nodes)", verb)881		return882	}883884	sep := "; "885	if _, ok := s.Precision(); ok { // %.v is expr list886		sep = ", "887	}888889	for i, n := range l {890		fmt.Fprint(s, n)891		if i+1 < len(l) {892			fmt.Fprint(s, sep)893		}894	}895}896897// Dump898899// Dump prints the message s followed by a debug dump of n.900// This includes all the recursive structure under n.901func Dump(s string, n Node) {902	fmt.Printf("%s%+v\n", s, n)903}904905// FDump prints to w the message s followed by a debug dump of n.906// This includes all the recursive structure under n.907func FDump(w io.Writer, s string, n Node) {908	fmt.Fprintf(w, "%s%+v\n", s, n)909}910911// DumpList prints the message s followed by a debug dump of each node in the list.912// This includes all the recursive structure under each node in the list.913func DumpList(s string, list Nodes) {914	var buf bytes.Buffer915	FDumpList(&buf, s, list)916	os.Stdout.Write(buf.Bytes())917}918919// FDumpList prints to w the message s followed by a debug dump of each node in the list.920// This includes all the recursive structure under each node in the list.921func FDumpList(w io.Writer, s string, list Nodes) {922	io.WriteString(w, s)923	dumpNodes(w, list, 1)924	io.WriteString(w, "\n")925}926927// indent prints indentation to w.928func indent(w io.Writer, depth int) {929	fmt.Fprint(w, "\n")930	for i := 0; i < depth; i++ {931		fmt.Fprint(w, ".   ")932	}933}934935// EscFmt is set by the escape analysis code to add escape analysis details to the node print.936var EscFmt func(n Node) string937938// dumpNodeHeader prints the debug-format node header line to w.939func dumpNodeHeader(w io.Writer, n Node) {940	// Useful to see which nodes in an AST printout are actually identical941	if base.Debug.DumpPtrs != 0 {942		fmt.Fprintf(w, " p(%p)", n)943	}944945	if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Defn != nil {946		// Useful to see where Defn is set and what node it points to947		fmt.Fprintf(w, " defn(%p)", n.Name().Defn)948	}949950	if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Curfn != nil {951		// Useful to see where Defn is set and what node it points to952		fmt.Fprintf(w, " curfn(%p)", n.Name().Curfn)953	}954	if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Outer != nil {955		// Useful to see where Defn is set and what node it points to956		fmt.Fprintf(w, " outer(%p)", n.Name().Outer)957	}958959	if EscFmt != nil {960		if esc := EscFmt(n); esc != "" {961			fmt.Fprintf(w, " %s", esc)962		}963	}964965	if n.Sym() != nil && n.Op() != ONAME && n.Op() != ONONAME && n.Op() != OTYPE {966		fmt.Fprintf(w, " %+v", n.Sym())967	}968969	// Print Node-specific fields of basic type in header line.970	v := reflect.ValueOf(n).Elem()971	t := v.Type()972	nf := t.NumField()973	for i := 0; i < nf; i++ {974		tf := t.Field(i)975		if tf.PkgPath != "" {976			// skip unexported field - Interface will fail977			continue978		}979		k := tf.Type.Kind()980		if reflect.Bool <= k && k <= reflect.Complex128 {981			name := strings.TrimSuffix(tf.Name, "_")982			vf := v.Field(i)983			vfi := vf.Interface()984			if name == "Offset" && vfi == types.BADWIDTH || name != "Offset" && vf.IsZero() {985				continue986			}987			if vfi == true {988				fmt.Fprintf(w, " %s", name)989			} else {990				fmt.Fprintf(w, " %s:%+v", name, vf.Interface())991			}992		}993	}994995	// Print Node-specific booleans by looking for methods.996	// Different v, t from above - want *Struct not Struct, for methods.997	v = reflect.ValueOf(n)998	t = v.Type()999	nm := t.NumMethod()1000	for i := 0; i < nm; i++ {1001		tm := t.Method(i)1002		if tm.PkgPath != "" {1003			// skip unexported method - call will fail1004			continue1005		}1006		m := v.Method(i)1007		mt := m.Type()1008		if mt.NumIn() == 0 && mt.NumOut() == 1 && mt.Out(0).Kind() == reflect.Bool {1009			// TODO(rsc): Remove the func/defer/recover wrapping,1010			// which is guarding against panics in miniExpr,1011			// once we get down to the simpler state in which1012			// nodes have no getter methods that aren't allowed to be called.1013			func() {1014				defer func() { recover() }()1015				if m.Call(nil)[0].Bool() {1016					name := strings.TrimSuffix(tm.Name, "_")1017					fmt.Fprintf(w, " %s", name)1018				}1019			}()1020		}1021	}10221023	if n.Op() == OCLOSURE {1024		n := n.(*ClosureExpr)1025		if fn := n.Func; fn != nil && fn.Nname.Sym() != nil {1026			fmt.Fprintf(w, " fnName(%+v)", fn.Nname.Sym())1027		}1028	}10291030	if n.Type() != nil {1031		if n.Op() == OTYPE {1032			fmt.Fprintf(w, " type")1033		}1034		fmt.Fprintf(w, " %+v", n.Type())1035	}1036	if n.Typecheck() != 0 {1037		fmt.Fprintf(w, " tc(%d)", n.Typecheck())1038	}10391040	if n.Pos().IsKnown() {1041		fmt.Fprint(w, " # ")1042		switch n.Pos().IsStmt() {1043		case src.PosNotStmt:1044			fmt.Fprint(w, "_") // "-" would be confusing1045		case src.PosIsStmt:1046			fmt.Fprint(w, "+")1047		}1048		sep := ""1049		base.Ctxt.AllPos(n.Pos(), func(pos src.Pos) {1050			fmt.Fprint(w, sep)1051			sep = " "1052			// TODO(mdempsky): Print line pragma details too.1053			file := filepath.Base(pos.Filename())1054			// Note: this output will be parsed by ssa/html.go:(*HTMLWriter).WriteAST. Keep in sync.1055			fmt.Fprintf(w, "%s:%d:%d", file, pos.Line(), pos.Col())1056		})1057	}1058}10591060func dumpNode(w io.Writer, n Node, depth int) {1061	indent(w, depth)1062	if depth > 40 {1063		fmt.Fprint(w, "...")1064		return1065	}10661067	if n == nil {1068		fmt.Fprint(w, "NilIrNode")1069		return1070	}10711072	if len(n.Init()) != 0 {1073		fmt.Fprintf(w, "%+v-init", n.Op())1074		dumpNodes(w, n.Init(), depth+1)1075		indent(w, depth)1076	}10771078	switch n.Op() {1079	default:1080		fmt.Fprintf(w, "%+v", n.Op())1081		dumpNodeHeader(w, n)10821083	case OLITERAL:1084		fmt.Fprintf(w, "%+v-%v", n.Op(), n.Val())1085		dumpNodeHeader(w, n)1086		return10871088	case ONAME, ONONAME:1089		if n.Sym() != nil {1090			fmt.Fprintf(w, "%+v-%+v", n.Op(), n.Sym())1091		} else {1092			fmt.Fprintf(w, "%+v", n.Op())1093		}1094		dumpNodeHeader(w, n)1095		return10961097	case OLINKSYMOFFSET:1098		n := n.(*LinksymOffsetExpr)1099		fmt.Fprintf(w, "%+v-%v", n.Op(), n.Linksym)1100		// Offset is almost always 0, so only print when it's interesting.1101		if n.Offset_ != 0 {1102			fmt.Fprintf(w, "%+v", n.Offset_)1103		}1104		dumpNodeHeader(w, n)11051106	case OASOP:1107		n := n.(*AssignOpStmt)1108		fmt.Fprintf(w, "%+v-%+v", n.Op(), n.AsOp)1109		dumpNodeHeader(w, n)11101111	case OTYPE:1112		fmt.Fprintf(w, "%+v %+v", n.Op(), n.Sym())1113		dumpNodeHeader(w, n)1114		return11151116	case OCLOSURE:1117		fmt.Fprintf(w, "%+v", n.Op())1118		dumpNodeHeader(w, n)11191120	case ODCLFUNC:1121		// Func has many fields we don't want to print.1122		// Bypass reflection and just print what we want.1123		n := n.(*Func)1124		fmt.Fprintf(w, "%+v", n.Op())1125		dumpNodeHeader(w, n)1126		fn := n1127		if len(fn.Dcl) > 0 {1128			indent(w, depth)1129			fmt.Fprintf(w, "%+v-Dcl", n.Op())1130			for _, dcl := range n.Dcl {1131				dumpNode(w, dcl, depth+1)1132			}1133		}1134		if len(fn.ClosureVars) > 0 {1135			indent(w, depth)1136			fmt.Fprintf(w, "%+v-ClosureVars", n.Op())1137			for _, cv := range fn.ClosureVars {1138				dumpNode(w, cv, depth+1)1139			}1140		}1141		if len(fn.Body) > 0 {1142			indent(w, depth)1143			fmt.Fprintf(w, "%+v-body", n.Op())1144			dumpNodes(w, fn.Body, depth+1)1145		}1146		return1147	}11481149	v := reflect.ValueOf(n).Elem()1150	t := reflect.TypeOf(n).Elem()1151	nf := t.NumField()1152	for i := 0; i < nf; i++ {1153		tf := t.Field(i)1154		vf := v.Field(i)1155		if tf.PkgPath != "" {1156			// skip unexported field - Interface will fail1157			continue1158		}1159		switch tf.Type.Kind() {1160		case reflect.Interface, reflect.Ptr, reflect.Slice:1161			if vf.IsNil() {1162				continue1163			}1164		}1165		name := strings.TrimSuffix(tf.Name, "_")1166		// Do not bother with field name header lines for the1167		// most common positional arguments: unary, binary expr,1168		// index expr, send stmt, go and defer call expression.1169		switch name {1170		case "X", "Y", "Index", "Chan", "Value", "Call":1171			name = ""1172		}1173		switch val := vf.Interface().(type) {1174		case Node:1175			if name != "" {1176				indent(w, depth)1177				fmt.Fprintf(w, "%+v-%s", n.Op(), name)1178			}1179			dumpNode(w, val, depth+1)1180		case Nodes:1181			if len(val) == 0 {1182				continue1183			}1184			if name != "" {1185				indent(w, depth)1186				fmt.Fprintf(w, "%+v-%s", n.Op(), name)1187			}1188			dumpNodes(w, val, depth+1)1189		default:1190			if vf.Kind() == reflect.Slice && vf.Type().Elem().Implements(nodeType) {1191				if vf.Len() == 0 {1192					continue1193				}1194				if name != "" {1195					indent(w, depth)1196					fmt.Fprintf(w, "%+v-%s", n.Op(), name)1197				}1198				for i, n := 0, vf.Len(); i < n; i++ {1199					dumpNode(w, vf.Index(i).Interface().(Node), depth+1)1200				}1201			}1202		}1203	}1204}12051206var nodeType = reflect.TypeFor[Node]()12071208func dumpNodes(w io.Writer, list Nodes, depth int) {1209	if len(list) == 0 {1210		fmt.Fprintf(w, " <nil>")1211		return1212	}12131214	for _, n := range list {1215		dumpNode(w, n, depth)1216	}1217}

Code quality findings 10

Use of unsafe package detected; ensure it’s necessary, justified in comments, and bounds-checked to avoid memory corruption
warning safety unsafe-package
OUNSAFEADD: "unsafe.Add",
Use of unsafe package detected; ensure it’s necessary, justified in comments, and bounds-checked to avoid memory corruption
warning safety unsafe-package
OUNSAFESLICE: "unsafe.Slice",
Use of unsafe package detected; ensure it’s necessary, justified in comments, and bounds-checked to avoid memory corruption
warning safety unsafe-package
OUNSAFESLICEDATA: "unsafe.SliceData",
Use of unsafe package detected; ensure it’s necessary, justified in comments, and bounds-checked to avoid memory corruption
warning safety unsafe-package
OUNSAFESTRING: "unsafe.String",
Use of unsafe package detected; ensure it’s necessary, justified in comments, and bounds-checked to avoid memory corruption
warning safety unsafe-package
OUNSAFESTRINGDATA: "unsafe.StringData",
May hide panics instead of handling errors properly; use only with specific panic recovery logic
warning correctness recover-without-defer
defer func() { recover() }()
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, n1 := range n.List {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, n := range l {
Formatted output to console; prefer structured logging for consistency
info correctness fmt-printf
fmt.Printf("%s%+v\n", s, n)
Type switch without default case; unhandled types will silently do nothing. Add a default case for safety
info correctness unchecked-type-switch
switch val := vf.Interface().(type) {

Get this view in your editor

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