diff options
Diffstat (limited to 'src/cmd/compile/internal/ir/fmt.go')
-rw-r--r-- | src/cmd/compile/internal/ir/fmt.go | 1916 |
1 files changed, 1916 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/ir/fmt.go b/src/cmd/compile/internal/ir/fmt.go new file mode 100644 index 0000000000..f394219c05 --- /dev/null +++ b/src/cmd/compile/internal/ir/fmt.go @@ -0,0 +1,1916 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ir + +import ( + "bytes" + "fmt" + "go/constant" + "io" + "strconv" + "strings" + "sync" + "unicode/utf8" + + "cmd/compile/internal/base" + "cmd/compile/internal/types" + "cmd/internal/src" +) + +// A FmtFlag value is a set of flags (or 0). +// They control how the Xconv functions format their values. +// See the respective function's documentation for details. +type FmtFlag int + +const ( // fmt.Format flag/prec or verb + FmtLeft FmtFlag = 1 << iota // '-' + FmtSharp // '#' + FmtSign // '+' + FmtUnsigned // internal use only (historic: u flag) + FmtShort // verb == 'S' (historic: h flag) + FmtLong // verb == 'L' (historic: l flag) + FmtComma // '.' (== hasPrec) (historic: , flag) + FmtByte // '0' (historic: hh flag) +) + +// fmtFlag computes the (internal) FmtFlag +// value given the fmt.State and format verb. +func fmtFlag(s fmt.State, verb rune) FmtFlag { + var flag FmtFlag + if s.Flag('-') { + flag |= FmtLeft + } + if s.Flag('#') { + flag |= FmtSharp + } + if s.Flag('+') { + flag |= FmtSign + } + if s.Flag(' ') { + base.Fatalf("FmtUnsigned in format string") + } + if _, ok := s.Precision(); ok { + flag |= FmtComma + } + if s.Flag('0') { + flag |= FmtByte + } + switch verb { + case 'S': + flag |= FmtShort + case 'L': + flag |= FmtLong + } + return flag +} + +// Format conversions: +// TODO(gri) verify these; eliminate those not used anymore +// +// %v Op Node opcodes +// Flags: #: print Go syntax (automatic unless mode == FDbg) +// +// %j *Node Node details +// Flags: 0: suppresses things not relevant until walk +// +// %v *Val Constant values +// +// %v *types.Sym Symbols +// %S unqualified identifier in any mode +// Flags: +,- #: mode (see below) +// 0: in export mode: unqualified identifier if exported, qualified if not +// +// %v *types.Type Types +// %S omit "func" and receiver in function types +// %L definition instead of name. +// Flags: +,- #: mode (see below) +// ' ' (only in -/Sym mode) print type identifiers wit package name instead of prefix. +// +// %v *Node Nodes +// %S (only in +/debug mode) suppress recursion +// %L (only in Error mode) print "foo (type Bar)" +// Flags: +,- #: mode (see below) +// +// %v Nodes Node lists +// Flags: those of *Node +// .: separate items with ',' instead of ';' + +// *types.Sym, *types.Type, and *Node types use the flags below to set the format mode +const ( + FErr FmtMode = iota + FDbg + FTypeId + FTypeIdName // same as FTypeId, but use package name instead of prefix +) + +// The mode flags '+', '-', and '#' are sticky; they persist through +// recursions of *Node, *types.Type, and *types.Sym values. The ' ' flag is +// sticky only on *types.Type recursions and only used in %-/*types.Sym mode. +// +// Example: given a *types.Sym: %+v %#v %-v print an identifier properly qualified for debug/export/internal mode + +// Useful format combinations: +// TODO(gri): verify these +// +// *Node, Nodes: +// %+v multiline recursive debug dump of *Node/Nodes +// %+S non-recursive debug dump +// +// *Node: +// %#v Go format +// %L "foo (type Bar)" for error messages +// +// *types.Type: +// %#v Go format +// %#L type definition instead of name +// %#S omit "func" and receiver in function signature +// +// %-v type identifiers +// %-S type identifiers without "func" and arg names in type signatures (methodsym) +// %- v type identifiers with package name instead of prefix (typesym, dcommontype, typehash) + +// update returns the results of applying f to mode. +func (f FmtFlag) update(mode FmtMode) (FmtFlag, FmtMode) { + switch { + case f&FmtSign != 0: + mode = FDbg + case f&FmtSharp != 0: + // ignore (textual export format no longer supported) + case f&FmtUnsigned != 0: + mode = FTypeIdName + case f&FmtLeft != 0: + mode = FTypeId + } + + f &^= FmtSharp | FmtLeft | FmtSign + return f, mode +} + +var OpNames = []string{ + OADDR: "&", + OADD: "+", + OADDSTR: "+", + OALIGNOF: "unsafe.Alignof", + OANDAND: "&&", + OANDNOT: "&^", + OAND: "&", + OAPPEND: "append", + OAS: "=", + OAS2: "=", + OBREAK: "break", + OCALL: "function call", // not actual syntax + OCAP: "cap", + OCASE: "case", + OCLOSE: "close", + OCOMPLEX: "complex", + OBITNOT: "^", + OCONTINUE: "continue", + OCOPY: "copy", + ODELETE: "delete", + ODEFER: "defer", + ODIV: "/", + OEQ: "==", + OFALL: "fallthrough", + OFOR: "for", + OFORUNTIL: "foruntil", // not actual syntax; used to avoid off-end pointer live on backedge.892 + OGE: ">=", + OGOTO: "goto", + OGT: ">", + OIF: "if", + OIMAG: "imag", + OINLMARK: "inlmark", + ODEREF: "*", + OLEN: "len", + OLE: "<=", + OLSH: "<<", + OLT: "<", + OMAKE: "make", + ONEG: "-", + OMOD: "%", + OMUL: "*", + ONEW: "new", + ONE: "!=", + ONOT: "!", + OOFFSETOF: "unsafe.Offsetof", + OOROR: "||", + OOR: "|", + OPANIC: "panic", + OPLUS: "+", + OPRINTN: "println", + OPRINT: "print", + ORANGE: "range", + OREAL: "real", + ORECV: "<-", + ORECOVER: "recover", + ORETURN: "return", + ORSH: ">>", + OSELECT: "select", + OSEND: "<-", + OSIZEOF: "unsafe.Sizeof", + OSUB: "-", + OSWITCH: "switch", + OXOR: "^", +} + +func (o Op) GoString() string { + return fmt.Sprintf("%#v", o) +} + +func (o Op) format(s fmt.State, verb rune, mode FmtMode) { + switch verb { + case 'v': + o.oconv(s, fmtFlag(s, verb), mode) + + default: + fmt.Fprintf(s, "%%!%c(Op=%d)", verb, int(o)) + } +} + +func (o Op) oconv(s fmt.State, flag FmtFlag, mode FmtMode) { + if flag&FmtSharp != 0 || mode != FDbg { + if int(o) < len(OpNames) && OpNames[o] != "" { + fmt.Fprint(s, OpNames[o]) + return + } + } + + // 'o.String()' instead of just 'o' to avoid infinite recursion + fmt.Fprint(s, o.String()) +} + +type FmtMode int + +type fmtNode struct { + x Node + m FmtMode +} + +func (f *fmtNode) Format(s fmt.State, verb rune) { nodeFormat(f.x, s, verb, f.m) } + +type fmtOp struct { + x Op + m FmtMode +} + +func (f *fmtOp) Format(s fmt.State, verb rune) { f.x.format(s, verb, f.m) } + +type fmtType struct { + x *types.Type + m FmtMode +} + +func (f *fmtType) Format(s fmt.State, verb rune) { typeFormat(f.x, s, verb, f.m) } + +type fmtSym struct { + x *types.Sym + m FmtMode +} + +func (f *fmtSym) Format(s fmt.State, verb rune) { symFormat(f.x, s, verb, f.m) } + +type fmtNodes struct { + x Nodes + m FmtMode +} + +func (f *fmtNodes) Format(s fmt.State, verb rune) { f.x.format(s, verb, f.m) } + +func (n *node) Format(s fmt.State, verb rune) { + FmtNode(n, s, verb) +} + +func FmtNode(n Node, s fmt.State, verb rune) { + nodeFormat(n, s, verb, FErr) +} + +func (o Op) Format(s fmt.State, verb rune) { o.format(s, verb, FErr) } + +// func (t *types.Type) Format(s fmt.State, verb rune) // in package types +// func (y *types.Sym) Format(s fmt.State, verb rune) // in package types { y.format(s, verb, FErr) } +func (n Nodes) Format(s fmt.State, verb rune) { n.format(s, verb, FErr) } + +func (m FmtMode) Fprintf(s fmt.State, format string, args ...interface{}) { + m.prepareArgs(args) + fmt.Fprintf(s, format, args...) +} + +func (m FmtMode) Sprintf(format string, args ...interface{}) string { + m.prepareArgs(args) + return fmt.Sprintf(format, args...) +} + +func (m FmtMode) Sprint(args ...interface{}) string { + m.prepareArgs(args) + return fmt.Sprint(args...) +} + +func (m FmtMode) prepareArgs(args []interface{}) { + for i, arg := range args { + switch arg := arg.(type) { + case Op: + args[i] = &fmtOp{arg, m} + case Node: + args[i] = &fmtNode{arg, m} + case nil: + args[i] = &fmtNode{nil, m} // assume this was a node interface + case *types.Type: + args[i] = &fmtType{arg, m} + case *types.Sym: + args[i] = &fmtSym{arg, m} + case Nodes: + args[i] = &fmtNodes{arg, m} + case int32, int64, string, types.EType, constant.Value: + // OK: printing these types doesn't depend on mode + default: + base.Fatalf("mode.prepareArgs type %T", arg) + } + } +} + +func nodeFormat(n Node, s fmt.State, verb rune, mode FmtMode) { + switch verb { + case 'v', 'S', 'L': + nconvFmt(n, s, fmtFlag(s, verb), mode) + + case 'j': + jconvFmt(n, s, fmtFlag(s, verb)) + + default: + fmt.Fprintf(s, "%%!%c(*Node=%p)", verb, n) + } +} + +// EscFmt is set by the escape analysis code to add escape analysis details to the node print. +var EscFmt func(n Node, short bool) string + +// *Node details +func jconvFmt(n Node, s fmt.State, flag FmtFlag) { + short := flag&FmtShort != 0 + + // Useful to see which nodes in an AST printout are actually identical + if base.Debug.DumpPtrs != 0 { + fmt.Fprintf(s, " p(%p)", n) + } + if !short && n.Name() != nil && n.Name().Vargen != 0 { + fmt.Fprintf(s, " g(%d)", n.Name().Vargen) + } + + if base.Debug.DumpPtrs != 0 && !short && n.Name() != nil && n.Name().Defn != nil { + // Useful to see where Defn is set and what node it points to + fmt.Fprintf(s, " defn(%p)", n.Name().Defn) + } + + if n.Pos().IsKnown() { + pfx := "" + switch n.Pos().IsStmt() { + case src.PosNotStmt: + pfx = "_" // "-" would be confusing + case src.PosIsStmt: + pfx = "+" + } + fmt.Fprintf(s, " l(%s%d)", pfx, n.Pos().Line()) + } + + if !short && n.Offset() != types.BADWIDTH { + fmt.Fprintf(s, " x(%d)", n.Offset()) + } + + if n.Class() != 0 { + fmt.Fprintf(s, " class(%v)", n.Class()) + } + + if n.Colas() { + fmt.Fprintf(s, " colas(%v)", n.Colas()) + } + + if EscFmt != nil { + if esc := EscFmt(n, short); esc != "" { + fmt.Fprintf(s, " %s", esc) + } + } + + if !short && n.Typecheck() != 0 { + fmt.Fprintf(s, " tc(%d)", n.Typecheck()) + } + + if n.IsDDD() { + fmt.Fprintf(s, " isddd(%v)", n.IsDDD()) + } + + if n.Implicit() { + fmt.Fprintf(s, " implicit(%v)", n.Implicit()) + } + + if n.Embedded() { + fmt.Fprintf(s, " embedded") + } + + if n.Op() == ONAME { + if n.Name().Addrtaken() { + fmt.Fprint(s, " addrtaken") + } + if n.Name().Assigned() { + fmt.Fprint(s, " assigned") + } + if n.Name().IsClosureVar() { + fmt.Fprint(s, " closurevar") + } + if n.Name().Captured() { + fmt.Fprint(s, " captured") + } + if n.Name().IsOutputParamHeapAddr() { + fmt.Fprint(s, " outputparamheapaddr") + } + } + if n.Bounded() { + fmt.Fprint(s, " bounded") + } + if n.NonNil() { + fmt.Fprint(s, " nonnil") + } + + if !short && n.HasCall() { + fmt.Fprint(s, " hascall") + } + + if !short && n.Name() != nil && n.Name().Used() { + fmt.Fprint(s, " used") + } +} + +func FmtConst(v constant.Value, flag FmtFlag) string { + if flag&FmtSharp == 0 && v.Kind() == constant.Complex { + real, imag := constant.Real(v), constant.Imag(v) + + var re string + sre := constant.Sign(real) + if sre != 0 { + re = real.String() + } + + var im string + sim := constant.Sign(imag) + if sim != 0 { + im = imag.String() + } + + switch { + case sre == 0 && sim == 0: + return "0" + case sre == 0: + return im + "i" + case sim == 0: + return re + case sim < 0: + return fmt.Sprintf("(%s%si)", re, im) + default: + return fmt.Sprintf("(%s+%si)", re, im) + } + } + + return v.String() +} + +/* +s%,%,\n%g +s%\n+%\n%g +s%^[ ]*T%%g +s%,.*%%g +s%.+% [T&] = "&",%g +s%^ ........*\]%&~%g +s%~ %%g +*/ + +func symfmt(b *bytes.Buffer, s *types.Sym, flag FmtFlag, mode FmtMode) { + if flag&FmtShort == 0 { + switch mode { + case FErr: // This is for the user + if s.Pkg == BuiltinPkg || s.Pkg == LocalPkg { + b.WriteString(s.Name) + return + } + + // If the name was used by multiple packages, display the full path, + if s.Pkg.Name != "" && NumImport[s.Pkg.Name] > 1 { + fmt.Fprintf(b, "%q.%s", s.Pkg.Path, s.Name) + return + } + b.WriteString(s.Pkg.Name) + b.WriteByte('.') + b.WriteString(s.Name) + return + + case FDbg: + b.WriteString(s.Pkg.Name) + b.WriteByte('.') + b.WriteString(s.Name) + return + + case FTypeIdName: + // dcommontype, typehash + b.WriteString(s.Pkg.Name) + b.WriteByte('.') + b.WriteString(s.Name) + return + + case FTypeId: + // (methodsym), typesym, weaksym + b.WriteString(s.Pkg.Prefix) + b.WriteByte('.') + b.WriteString(s.Name) + return + } + } + + if flag&FmtByte != 0 { + // FmtByte (hh) implies FmtShort (h) + // skip leading "type." in method name + name := s.Name + if i := strings.LastIndex(name, "."); i >= 0 { + name = name[i+1:] + } + + if mode == FDbg { + fmt.Fprintf(b, "@%q.%s", s.Pkg.Path, name) + return + } + + b.WriteString(name) + return + } + + b.WriteString(s.Name) +} + +var BasicTypeNames = []string{ + types.TINT: "int", + types.TUINT: "uint", + types.TINT8: "int8", + types.TUINT8: "uint8", + types.TINT16: "int16", + types.TUINT16: "uint16", + types.TINT32: "int32", + types.TUINT32: "uint32", + types.TINT64: "int64", + types.TUINT64: "uint64", + types.TUINTPTR: "uintptr", + types.TFLOAT32: "float32", + types.TFLOAT64: "float64", + types.TCOMPLEX64: "complex64", + types.TCOMPLEX128: "complex128", + types.TBOOL: "bool", + types.TANY: "any", + types.TSTRING: "string", + types.TNIL: "nil", + types.TIDEAL: "untyped number", + types.TBLANK: "blank", +} + +var fmtBufferPool = sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, +} + +func tconv(t *types.Type, flag FmtFlag, mode FmtMode) string { + buf := fmtBufferPool.Get().(*bytes.Buffer) + buf.Reset() + defer fmtBufferPool.Put(buf) + + tconv2(buf, t, flag, mode, nil) + return types.InternString(buf.Bytes()) +} + +// tconv2 writes a string representation of t to b. +// flag and mode control exactly what is printed. +// Any types x that are already in the visited map get printed as @%d where %d=visited[x]. +// See #16897 before changing the implementation of tconv. +func tconv2(b *bytes.Buffer, t *types.Type, flag FmtFlag, mode FmtMode, visited map[*types.Type]int) { + if off, ok := visited[t]; ok { + // We've seen this type before, so we're trying to print it recursively. + // Print a reference to it instead. + fmt.Fprintf(b, "@%d", off) + return + } + if t == nil { + b.WriteString("<T>") + return + } + if t.Etype == types.TSSA { + b.WriteString(t.Extra.(string)) + return + } + if t.Etype == types.TTUPLE { + b.WriteString(t.FieldType(0).String()) + b.WriteByte(',') + b.WriteString(t.FieldType(1).String()) + return + } + + if t.Etype == types.TRESULTS { + tys := t.Extra.(*types.Results).Types + for i, et := range tys { + if i > 0 { + b.WriteByte(',') + } + b.WriteString(et.String()) + } + return + } + + flag, mode = flag.update(mode) + if mode == FTypeIdName { + flag |= FmtUnsigned + } + if t == types.Bytetype || t == types.Runetype { + // in %-T mode collapse rune and byte with their originals. + switch mode { + case FTypeIdName, FTypeId: + t = types.Types[t.Etype] + default: + sconv2(b, t.Sym, FmtShort, mode) + return + } + } + if t == types.Errortype { + b.WriteString("error") + return + } + + // Unless the 'L' flag was specified, if the type has a name, just print that name. + if flag&FmtLong == 0 && t.Sym != nil && t != types.Types[t.Etype] { + switch mode { + case FTypeId, FTypeIdName: + if flag&FmtShort != 0 { + if t.Vargen != 0 { + sconv2(b, t.Sym, FmtShort, mode) + fmt.Fprintf(b, "·%d", t.Vargen) + return + } + sconv2(b, t.Sym, FmtShort, mode) + return + } + + if mode == FTypeIdName { + sconv2(b, t.Sym, FmtUnsigned, mode) + return + } + + if t.Sym.Pkg == LocalPkg && t.Vargen != 0 { + b.WriteString(mode.Sprintf("%v·%d", t.Sym, t.Vargen)) + return + } + } + + sconv2(b, t.Sym, 0, mode) + return + } + + if int(t.Etype) < len(BasicTypeNames) && BasicTypeNames[t.Etype] != "" { + var name string + switch t { + case types.UntypedBool: + name = "untyped bool" + case types.UntypedString: + name = "untyped string" + case types.UntypedInt: + name = "untyped int" + case types.UntypedRune: + name = "untyped rune" + case types.UntypedFloat: + name = "untyped float" + case types.UntypedComplex: + name = "untyped complex" + default: + name = BasicTypeNames[t.Etype] + } + b.WriteString(name) + return + } + + if mode == FDbg { + b.WriteString(t.Etype.String()) + b.WriteByte('-') + tconv2(b, t, flag, FErr, visited) + return + } + + // At this point, we might call tconv2 recursively. Add the current type to the visited list so we don't + // try to print it recursively. + // We record the offset in the result buffer where the type's text starts. This offset serves as a reference + // point for any later references to the same type. + // Note that we remove the type from the visited map as soon as the recursive call is done. + // This prevents encoding types like map[*int]*int as map[*int]@4. (That encoding would work, + // but I'd like to use the @ notation only when strictly necessary.) + if visited == nil { + visited = map[*types.Type]int{} + } + visited[t] = b.Len() + defer delete(visited, t) + + switch t.Etype { + case types.TPTR: + b.WriteByte('*') + switch mode { + case FTypeId, FTypeIdName: + if flag&FmtShort != 0 { + tconv2(b, t.Elem(), FmtShort, mode, visited) + return + } + } + tconv2(b, t.Elem(), 0, mode, visited) + + case types.TARRAY: + b.WriteByte('[') + b.WriteString(strconv.FormatInt(t.NumElem(), 10)) + b.WriteByte(']') + tconv2(b, t.Elem(), 0, mode, visited) + + case types.TSLICE: + b.WriteString("[]") + tconv2(b, t.Elem(), 0, mode, visited) + + case types.TCHAN: + switch t.ChanDir() { + case types.Crecv: + b.WriteString("<-chan ") + tconv2(b, t.Elem(), 0, mode, visited) + case types.Csend: + b.WriteString("chan<- ") + tconv2(b, t.Elem(), 0, mode, visited) + default: + b.WriteString("chan ") + if t.Elem() != nil && t.Elem().IsChan() && t.Elem().Sym == nil && t.Elem().ChanDir() == types.Crecv { + b.WriteByte('(') + tconv2(b, t.Elem(), 0, mode, visited) + b.WriteByte(')') + } else { + tconv2(b, t.Elem(), 0, mode, visited) + } + } + + case types.TMAP: + b.WriteString("map[") + tconv2(b, t.Key(), 0, mode, visited) + b.WriteByte(']') + tconv2(b, t.Elem(), 0, mode, visited) + + case types.TINTER: + if t.IsEmptyInterface() { + b.WriteString("interface {}") + break + } + b.WriteString("interface {") + for i, f := range t.Fields().Slice() { + if i != 0 { + b.WriteByte(';') + } + b.WriteByte(' ') + switch { + case f.Sym == nil: + // Check first that a symbol is defined for this type. + // Wrong interface definitions may have types lacking a symbol. + break + case types.IsExported(f.Sym.Name): + sconv2(b, f.Sym, FmtShort, mode) + default: + flag1 := FmtLeft + if flag&FmtUnsigned != 0 { + flag1 = FmtUnsigned + } + sconv2(b, f.Sym, flag1, mode) + } + tconv2(b, f.Type, FmtShort, mode, visited) + } + if t.NumFields() != 0 { + b.WriteByte(' ') + } + b.WriteByte('}') + + case types.TFUNC: + if flag&FmtShort != 0 { + // no leading func + } else { + if t.Recv() != nil { + b.WriteString("method") + tconv2(b, t.Recvs(), 0, mode, visited) + b.WriteByte(' ') + } + b.WriteString("func") + } + tconv2(b, t.Params(), 0, mode, visited) + + switch t.NumResults() { + case 0: + // nothing to do + + case 1: + b.WriteByte(' ') + tconv2(b, t.Results().Field(0).Type, 0, mode, visited) // struct->field->field's type + + default: + b.WriteByte(' ') + tconv2(b, t.Results(), 0, mode, visited) + } + + case types.TSTRUCT: + if m := t.StructType().Map; m != nil { + mt := m.MapType() + // Format the bucket struct for map[x]y as map.bucket[x]y. + // This avoids a recursive print that generates very long names. + switch t { + case mt.Bucket: + b.WriteString("map.bucket[") + case mt.Hmap: + b.WriteString("map.hdr[") + case mt.Hiter: + b.WriteString("map.iter[") + default: + base.Fatalf("unknown internal map type") + } + tconv2(b, m.Key(), 0, mode, visited) + b.WriteByte(']') + tconv2(b, m.Elem(), 0, mode, visited) + break + } + + if funarg := t.StructType().Funarg; funarg != types.FunargNone { + b.WriteByte('(') + var flag1 FmtFlag + switch mode { + case FTypeId, FTypeIdName, FErr: + // no argument names on function signature, and no "noescape"/"nosplit" tags + flag1 = FmtShort + } + for i, f := range t.Fields().Slice() { + if i != 0 { + b.WriteString(", ") + } + fldconv(b, f, flag1, mode, visited, funarg) + } + b.WriteByte(')') + } else { + b.WriteString("struct {") + for i, f := range t.Fields().Slice() { + if i != 0 { + b.WriteByte(';') + } + b.WriteByte(' ') + fldconv(b, f, FmtLong, mode, visited, funarg) + } + if t.NumFields() != 0 { + b.WriteByte(' ') + } + b.WriteByte('}') + } + + case types.TFORW: + b.WriteString("undefined") + if t.Sym != nil { + b.WriteByte(' ') + sconv2(b, t.Sym, 0, mode) + } + + case types.TUNSAFEPTR: + b.WriteString("unsafe.Pointer") + + case types.Txxx: + b.WriteString("Txxx") + default: + // Don't know how to handle - fall back to detailed prints. + b.WriteString(mode.Sprintf("%v <%v>", t.Etype, t.Sym)) + } +} + +// Statements which may be rendered with a simplestmt as init. +func StmtWithInit(op Op) bool { + switch op { + case OIF, OFOR, OFORUNTIL, OSWITCH: + return true + } + + return false +} + +func stmtFmt(n Node, s fmt.State, mode FmtMode) { + // some statements allow for an init, but at most one, + // but we may have an arbitrary number added, eg by typecheck + // and inlining. If it doesn't fit the syntax, emit an enclosing + // block starting with the init statements. + + // if we can just say "for" n->ninit; ... then do so + simpleinit := n.Init().Len() == 1 && n.Init().First().Init().Len() == 0 && StmtWithInit(n.Op()) + + // otherwise, print the inits as separate statements + complexinit := n.Init().Len() != 0 && !simpleinit && (mode != FErr) + + // but if it was for if/for/switch, put in an extra surrounding block to limit the scope + extrablock := complexinit && StmtWithInit(n.Op()) + + if extrablock { + fmt.Fprint(s, "{") + } + + if complexinit { + mode.Fprintf(s, " %v; ", n.Init()) + } + + switch n.Op() { + case ODCL: + mode.Fprintf(s, "var %v %v", n.Left().Sym(), n.Left().Type()) + + case ODCLFIELD: + if n.Sym() != nil { + mode.Fprintf(s, "%v %v", n.Sym(), n.Left()) + } else { + mode.Fprintf(s, "%v", n.Left()) + } + + // Don't export "v = <N>" initializing statements, hope they're always + // preceded by the DCL which will be re-parsed and typechecked to reproduce + // the "v = <N>" again. + case OAS: + if n.Colas() && !complexinit { + mode.Fprintf(s, "%v := %v", n.Left(), n.Right()) + } else { + mode.Fprintf(s, "%v = %v", n.Left(), n.Right()) + } + + case OASOP: + if n.Implicit() { + if n.SubOp() == OADD { + mode.Fprintf(s, "%v++", n.Left()) + } else { + mode.Fprintf(s, "%v--", n.Left()) + } + break + } + + mode.Fprintf(s, "%v %#v= %v", n.Left(), n.SubOp(), n.Right()) + + case OAS2: + if n.Colas() && !complexinit { + mode.Fprintf(s, "%.v := %.v", n.List(), n.Rlist()) + break + } + fallthrough + + case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: + mode.Fprintf(s, "%.v = %v", n.List(), n.Right()) + + case ORETURN: + mode.Fprintf(s, "return %.v", n.List()) + + case ORETJMP: + mode.Fprintf(s, "retjmp %v", n.Sym()) + + case OINLMARK: + mode.Fprintf(s, "inlmark %d", n.Offset()) + + case OGO: + mode.Fprintf(s, "go %v", n.Left()) + + case ODEFER: + mode.Fprintf(s, "defer %v", n.Left()) + + case OIF: + if simpleinit { + mode.Fprintf(s, "if %v; %v { %v }", n.Init().First(), n.Left(), n.Body()) + } else { + mode.Fprintf(s, "if %v { %v }", n.Left(), n.Body()) + } + if n.Rlist().Len() != 0 { + mode.Fprintf(s, " else { %v }", n.Rlist()) + } + + case OFOR, OFORUNTIL: + opname := "for" + if n.Op() == OFORUNTIL { + opname = "foruntil" + } + if mode == FErr { // TODO maybe only if FmtShort, same below + fmt.Fprintf(s, "%s loop", opname) + break + } + + fmt.Fprint(s, opname) + if simpleinit { + mode.Fprintf(s, " %v;", n.Init().First()) + } else if n.Right() != nil { + fmt.Fprint(s, " ;") + } + + if n.Left() != nil { + mode.Fprintf(s, " %v", n.Left()) + } + + if n.Right() != nil { + mode.Fprintf(s, "; %v", n.Right()) + } else if simpleinit { + fmt.Fprint(s, ";") + } + + if n.Op() == OFORUNTIL && n.List().Len() != 0 { + mode.Fprintf(s, "; %v", n.List()) + } + + mode.Fprintf(s, " { %v }", n.Body()) + + case ORANGE: + if mode == FErr { + fmt.Fprint(s, "for loop") + break + } + + if n.List().Len() == 0 { + mode.Fprintf(s, "for range %v { %v }", n.Right(), n.Body()) + break + } + + mode.Fprintf(s, "for %.v = range %v { %v }", n.List(), n.Right(), n.Body()) + + case OSELECT, OSWITCH: + if mode == FErr { + mode.Fprintf(s, "%v statement", n.Op()) + break + } + + mode.Fprintf(s, "%#v", n.Op()) + if simpleinit { + mode.Fprintf(s, " %v;", n.Init().First()) + } + if n.Left() != nil { + mode.Fprintf(s, " %v ", n.Left()) + } + + mode.Fprintf(s, " { %v }", n.List()) + + case OCASE: + if n.List().Len() != 0 { + mode.Fprintf(s, "case %.v", n.List()) + } else { + fmt.Fprint(s, "default") + } + mode.Fprintf(s, ": %v", n.Body()) + + case OBREAK, OCONTINUE, OGOTO, OFALL: + if n.Sym() != nil { + mode.Fprintf(s, "%#v %v", n.Op(), n.Sym()) + } else { + mode.Fprintf(s, "%#v", n.Op()) + } + + case OEMPTY: + break + + case OLABEL: + mode.Fprintf(s, "%v: ", n.Sym()) + } + + if extrablock { + fmt.Fprint(s, "}") + } +} + +var OpPrec = []int{ + OALIGNOF: 8, + OAPPEND: 8, + OBYTES2STR: 8, + OARRAYLIT: 8, + OSLICELIT: 8, + ORUNES2STR: 8, + OCALLFUNC: 8, + OCALLINTER: 8, + OCALLMETH: 8, + OCALL: 8, + OCAP: 8, + OCLOSE: 8, + OCONVIFACE: 8, + OCONVNOP: 8, + OCONV: 8, + OCOPY: 8, + ODELETE: 8, + OGETG: 8, + OLEN: 8, + OLITERAL: 8, + OMAKESLICE: 8, + OMAKESLICECOPY: 8, + OMAKE: 8, + OMAPLIT: 8, + ONAME: 8, + ONEW: 8, + ONIL: 8, + ONONAME: 8, + OOFFSETOF: 8, + OPACK: 8, + OPANIC: 8, + OPAREN: 8, + OPRINTN: 8, + OPRINT: 8, + ORUNESTR: 8, + OSIZEOF: 8, + OSTR2BYTES: 8, + OSTR2RUNES: 8, + OSTRUCTLIT: 8, + OTARRAY: 8, + OTCHAN: 8, + OTFUNC: 8, + OTINTER: 8, + OTMAP: 8, + OTSTRUCT: 8, + OINDEXMAP: 8, + OINDEX: 8, + OSLICE: 8, + OSLICESTR: 8, + OSLICEARR: 8, + OSLICE3: 8, + OSLICE3ARR: 8, + OSLICEHEADER: 8, + ODOTINTER: 8, + ODOTMETH: 8, + ODOTPTR: 8, + ODOTTYPE2: 8, + ODOTTYPE: 8, + ODOT: 8, + OXDOT: 8, + OCALLPART: 8, + OPLUS: 7, + ONOT: 7, + OBITNOT: 7, + ONEG: 7, + OADDR: 7, + ODEREF: 7, + ORECV: 7, + OMUL: 6, + ODIV: 6, + OMOD: 6, + OLSH: 6, + ORSH: 6, + OAND: 6, + OANDNOT: 6, + OADD: 5, + OSUB: 5, + OOR: 5, + OXOR: 5, + OEQ: 4, + OLT: 4, + OLE: 4, + OGE: 4, + OGT: 4, + ONE: 4, + OSEND: 3, + OANDAND: 2, + OOROR: 1, + + // Statements handled by stmtfmt + OAS: -1, + OAS2: -1, + OAS2DOTTYPE: -1, + OAS2FUNC: -1, + OAS2MAPR: -1, + OAS2RECV: -1, + OASOP: -1, + OBREAK: -1, + OCASE: -1, + OCONTINUE: -1, + ODCL: -1, + ODCLFIELD: -1, + ODEFER: -1, + OEMPTY: -1, + OFALL: -1, + OFOR: -1, + OFORUNTIL: -1, + OGOTO: -1, + OIF: -1, + OLABEL: -1, + OGO: -1, + ORANGE: -1, + ORETURN: -1, + OSELECT: -1, + OSWITCH: -1, + + OEND: 0, +} + +func exprFmt(n Node, s fmt.State, prec int, mode FmtMode) { + for n != nil && n.Implicit() && (n.Op() == ODEREF || n.Op() == OADDR) { + n = n.Left() + } + + if n == nil { + fmt.Fprint(s, "<N>") + return + } + + nprec := OpPrec[n.Op()] + if n.Op() == OTYPE && n.Sym() != nil { + nprec = 8 + } + + if prec > nprec { + mode.Fprintf(s, "(%v)", n) + return + } + + switch n.Op() { + case OPAREN: + mode.Fprintf(s, "(%v)", n.Left()) + + case ONIL: + fmt.Fprint(s, "nil") + + case OLITERAL: // this is a bit of a mess + if mode == FErr { + if n.Orig() != nil && n.Orig() != n { + exprFmt(n.Orig(), s, prec, mode) + return + } + if n.Sym() != nil { + fmt.Fprint(s, smodeString(n.Sym(), mode)) + return + } + } + + needUnparen := false + if n.Type() != nil && !n.Type().IsUntyped() { + // Need parens when type begins with what might + // be misinterpreted as a unary operator: * or <-. + if n.Type().IsPtr() || (n.Type().IsChan() && n.Type().ChanDir() == types.Crecv) { + mode.Fprintf(s, "(%v)(", n.Type()) + } else { + mode.Fprintf(s, "%v(", n.Type()) + } + needUnparen = true + } + + if n.Type() == types.UntypedRune { + switch x, ok := constant.Int64Val(n.Val()); { + case !ok: + fallthrough + default: + fmt.Fprintf(s, "('\\x00' + %v)", n.Val()) + + case ' ' <= x && x < utf8.RuneSelf && x != '\\' && x != '\'': + fmt.Fprintf(s, "'%c'", int(x)) + + case 0 <= x && x < 1<<16: + fmt.Fprintf(s, "'\\u%04x'", uint(int(x))) + + case 0 <= x && x <= utf8.MaxRune: + fmt.Fprintf(s, "'\\U%08x'", uint64(x)) + } + } else { + fmt.Fprint(s, FmtConst(n.Val(), fmtFlag(s, 'v'))) + } + + if needUnparen { + mode.Fprintf(s, ")") + } + + case ONAME: + // Special case: name used as local variable in export. + // _ becomes ~b%d internally; print as _ for export + if mode == FErr && n.Sym() != nil && n.Sym().Name[0] == '~' && n.Sym().Name[1] == 'b' { + fmt.Fprint(s, "_") + return + } + fallthrough + case OPACK, ONONAME, OMETHEXPR: + fmt.Fprint(s, smodeString(n.Sym(), mode)) + + case OTYPE: + if n.Type() == nil && n.Sym() != nil { + fmt.Fprint(s, smodeString(n.Sym(), mode)) + return + } + mode.Fprintf(s, "%v", n.Type()) + + case OTARRAY: + if n.Left() != nil { + mode.Fprintf(s, "[%v]%v", n.Left(), n.Right()) + return + } + mode.Fprintf(s, "[]%v", n.Right()) // happens before typecheck + + case OTMAP: + mode.Fprintf(s, "map[%v]%v", n.Left(), n.Right()) + + case OTCHAN: + switch n.TChanDir() { + case types.Crecv: + mode.Fprintf(s, "<-chan %v", n.Left()) + + case types.Csend: + mode.Fprintf(s, "chan<- %v", n.Left()) + + default: + if n.Left() != nil && n.Left().Op() == OTCHAN && n.Left().Sym() == nil && n.Left().TChanDir() == types.Crecv { + mode.Fprintf(s, "chan (%v)", n.Left()) + } else { + mode.Fprintf(s, "chan %v", n.Left()) + } + } + + case OTSTRUCT: + fmt.Fprint(s, "<struct>") + + case OTINTER: + fmt.Fprint(s, "<inter>") + + case OTFUNC: + fmt.Fprint(s, "<func>") + + case OCLOSURE: + if mode == FErr { + fmt.Fprint(s, "func literal") + return + } + if n.Body().Len() != 0 { + mode.Fprintf(s, "%v { %v }", n.Type(), n.Body()) + return + } + mode.Fprintf(s, "%v { %v }", n.Type(), n.Func().Decl.Body()) + + case OCOMPLIT: + if mode == FErr { + if n.Implicit() { + mode.Fprintf(s, "... argument") + return + } + if n.Right() != nil { + mode.Fprintf(s, "%v{%s}", n.Right(), ellipsisIf(n.List().Len() != 0)) + return + } + + fmt.Fprint(s, "composite literal") + return + } + mode.Fprintf(s, "(%v{ %.v })", n.Right(), n.List()) + + case OPTRLIT: + mode.Fprintf(s, "&%v", n.Left()) + + case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT: + if mode == FErr { + mode.Fprintf(s, "%v{%s}", n.Type(), ellipsisIf(n.List().Len() != 0)) + return + } + mode.Fprintf(s, "(%v{ %.v })", n.Type(), n.List()) + + case OKEY: + if n.Left() != nil && n.Right() != nil { + mode.Fprintf(s, "%v:%v", n.Left(), n.Right()) + return + } + + if n.Left() == nil && n.Right() != nil { + mode.Fprintf(s, ":%v", n.Right()) + return + } + if n.Left() != nil && n.Right() == nil { + mode.Fprintf(s, "%v:", n.Left()) + return + } + fmt.Fprint(s, ":") + + case OSTRUCTKEY: + mode.Fprintf(s, "%v:%v", n.Sym(), n.Left()) + + case OCALLPART: + exprFmt(n.Left(), s, nprec, mode) + if n.Right() == nil || n.Right().Sym() == nil { + fmt.Fprint(s, ".<nil>") + return + } + mode.Fprintf(s, ".%0S", n.Right().Sym()) + + case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH: + exprFmt(n.Left(), s, nprec, mode) + if n.Sym() == nil { + fmt.Fprint(s, ".<nil>") + return + } + mode.Fprintf(s, ".%0S", n.Sym()) + + case ODOTTYPE, ODOTTYPE2: + exprFmt(n.Left(), s, nprec, mode) + if n.Right() != nil { + mode.Fprintf(s, ".(%v)", n.Right()) + return + } + mode.Fprintf(s, ".(%v)", n.Type()) + + case OINDEX, OINDEXMAP: + exprFmt(n.Left(), s, nprec, mode) + mode.Fprintf(s, "[%v]", n.Right()) + + case OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR: + exprFmt(n.Left(), s, nprec, mode) + fmt.Fprint(s, "[") + low, high, max := n.SliceBounds() + if low != nil { + fmt.Fprint(s, modeString(low, mode)) + } + fmt.Fprint(s, ":") + if high != nil { + fmt.Fprint(s, modeString(high, mode)) + } + if n.Op().IsSlice3() { + fmt.Fprint(s, ":") + if max != nil { + fmt.Fprint(s, modeString(max, mode)) + } + } + fmt.Fprint(s, "]") + + case OSLICEHEADER: + if n.List().Len() != 2 { + base.Fatalf("bad OSLICEHEADER list length %d", n.List().Len()) + } + mode.Fprintf(s, "sliceheader{%v,%v,%v}", n.Left(), n.List().First(), n.List().Second()) + + case OCOMPLEX, OCOPY: + if n.Left() != nil { + mode.Fprintf(s, "%#v(%v, %v)", n.Op(), n.Left(), n.Right()) + } else { + mode.Fprintf(s, "%#v(%.v)", n.Op(), n.List()) + } + + case OCONV, + OCONVIFACE, + OCONVNOP, + OBYTES2STR, + ORUNES2STR, + OSTR2BYTES, + OSTR2RUNES, + ORUNESTR: + if n.Type() == nil || n.Type().Sym == nil { + mode.Fprintf(s, "(%v)", n.Type()) + } else { + mode.Fprintf(s, "%v", n.Type()) + } + if n.Left() != nil { + mode.Fprintf(s, "(%v)", n.Left()) + } else { + mode.Fprintf(s, "(%.v)", n.List()) + } + + case OREAL, + OIMAG, + OAPPEND, + OCAP, + OCLOSE, + ODELETE, + OLEN, + OMAKE, + ONEW, + OPANIC, + ORECOVER, + OALIGNOF, + OOFFSETOF, + OSIZEOF, + OPRINT, + OPRINTN: + if n.Left() != nil { + mode.Fprintf(s, "%#v(%v)", n.Op(), n.Left()) + return + } + if n.IsDDD() { + mode.Fprintf(s, "%#v(%.v...)", n.Op(), n.List()) + return + } + mode.Fprintf(s, "%#v(%.v)", n.Op(), n.List()) + + case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH, OGETG: + exprFmt(n.Left(), s, nprec, mode) + if n.IsDDD() { + mode.Fprintf(s, "(%.v...)", n.List()) + return + } + mode.Fprintf(s, "(%.v)", n.List()) + + case OMAKEMAP, OMAKECHAN, OMAKESLICE: + if n.List().Len() != 0 { // pre-typecheck + mode.Fprintf(s, "make(%v, %.v)", n.Type(), n.List()) + return + } + if n.Right() != nil { + mode.Fprintf(s, "make(%v, %v, %v)", n.Type(), n.Left(), n.Right()) + return + } + if n.Left() != nil && (n.Op() == OMAKESLICE || !n.Left().Type().IsUntyped()) { + mode.Fprintf(s, "make(%v, %v)", n.Type(), n.Left()) + return + } + mode.Fprintf(s, "make(%v)", n.Type()) + + case OMAKESLICECOPY: + mode.Fprintf(s, "makeslicecopy(%v, %v, %v)", n.Type(), n.Left(), n.Right()) + + case OPLUS, ONEG, OADDR, OBITNOT, ODEREF, ONOT, ORECV: + // Unary + mode.Fprintf(s, "%#v", n.Op()) + if n.Left() != nil && n.Left().Op() == n.Op() { + fmt.Fprint(s, " ") + } + exprFmt(n.Left(), s, nprec+1, mode) + + // Binary + case OADD, + OAND, + OANDAND, + OANDNOT, + ODIV, + OEQ, + OGE, + OGT, + OLE, + OLT, + OLSH, + OMOD, + OMUL, + ONE, + OOR, + OOROR, + ORSH, + OSEND, + OSUB, + OXOR: + exprFmt(n.Left(), s, nprec, mode) + mode.Fprintf(s, " %#v ", n.Op()) + exprFmt(n.Right(), s, nprec+1, mode) + + case OADDSTR: + for i, n1 := range n.List().Slice() { + if i != 0 { + fmt.Fprint(s, " + ") + } + exprFmt(n1, s, nprec, mode) + } + case ODDD: + mode.Fprintf(s, "...") + default: + mode.Fprintf(s, "<node %v>", n.Op()) + } +} + +func nodeFmt(n Node, s fmt.State, flag FmtFlag, mode FmtMode) { + t := n.Type() + + // We almost always want the original. + // TODO(gri) Why the special case for OLITERAL? + if n.Op() != OLITERAL && n.Orig() != nil { + n = n.Orig() + } + + if flag&FmtLong != 0 && t != nil { + if t.Etype == types.TNIL { + fmt.Fprint(s, "nil") + } else if n.Op() == ONAME && n.Name().AutoTemp() { + mode.Fprintf(s, "%v value", t) + } else { + mode.Fprintf(s, "%v (type %v)", n, t) + } + return + } + + // TODO inlining produces expressions with ninits. we can't print these yet. + + if OpPrec[n.Op()] < 0 { + stmtFmt(n, s, mode) + return + } + + exprFmt(n, s, 0, mode) +} + +func nodeDumpFmt(n Node, s fmt.State, flag FmtFlag, mode FmtMode) { + recur := flag&FmtShort == 0 + + if recur { + indent(s) + if dumpdepth > 40 { + fmt.Fprint(s, "...") + return + } + + if n.Init().Len() != 0 { + mode.Fprintf(s, "%v-init%v", n.Op(), n.Init()) + indent(s) + } + } + + switch n.Op() { + default: + mode.Fprintf(s, "%v%j", n.Op(), n) + + case OLITERAL: + mode.Fprintf(s, "%v-%v%j", n.Op(), n.Val(), n) + + case ONAME, ONONAME, OMETHEXPR: + if n.Sym() != nil { + mode.Fprintf(s, "%v-%v%j", n.Op(), n.Sym(), n) + } else { + mode.Fprintf(s, "%v%j", n.Op(), n) + } + if recur && n.Type() == nil && n.Name() != nil && n.Name().Param != nil && n.Name().Param.Ntype != nil { + indent(s) + mode.Fprintf(s, "%v-ntype%v", n.Op(), n.Name().Param.Ntype) + } + + case OASOP: + mode.Fprintf(s, "%v-%v%j", n.Op(), n.SubOp(), n) + + case OTYPE: + mode.Fprintf(s, "%v %v%j type=%v", n.Op(), n.Sym(), n, n.Type()) + if recur && n.Type() == nil && n.Name() != nil && n.Name().Param != nil && n.Name().Param.Ntype != nil { + indent(s) + mode.Fprintf(s, "%v-ntype%v", n.Op(), n.Name().Param.Ntype) + } + } + + if n.Op() == OCLOSURE && n.Func().Decl != nil && n.Func().Nname.Sym() != nil { + mode.Fprintf(s, " fnName %v", n.Func().Nname.Sym()) + } + if n.Sym() != nil && n.Op() != ONAME { + mode.Fprintf(s, " %v", n.Sym()) + } + + if n.Type() != nil { + mode.Fprintf(s, " %v", n.Type()) + } + + if recur { + if n.Left() != nil { + mode.Fprintf(s, "%v", n.Left()) + } + if n.Right() != nil { + mode.Fprintf(s, "%v", n.Right()) + } + if n.Op() == OCLOSURE && n.Func() != nil && n.Func().Decl != nil && n.Func().Decl.Body().Len() != 0 { + indent(s) + // The function associated with a closure + mode.Fprintf(s, "%v-clofunc%v", n.Op(), n.Func().Decl) + } + if n.Op() == ODCLFUNC && n.Func() != nil && n.Func().Dcl != nil && len(n.Func().Dcl) != 0 { + indent(s) + // The dcls for a func or closure + mode.Fprintf(s, "%v-dcl%v", n.Op(), AsNodes(n.Func().Dcl)) + } + if n.List().Len() != 0 { + indent(s) + mode.Fprintf(s, "%v-list%v", n.Op(), n.List()) + } + + if n.Rlist().Len() != 0 { + indent(s) + mode.Fprintf(s, "%v-rlist%v", n.Op(), n.Rlist()) + } + + if n.Body().Len() != 0 { + indent(s) + mode.Fprintf(s, "%v-body%v", n.Op(), n.Body()) + } + } +} + +// "%S" suppresses qualifying with package +func symFormat(s *types.Sym, f fmt.State, verb rune, mode FmtMode) { + switch verb { + case 'v', 'S': + fmt.Fprint(f, sconv(s, fmtFlag(f, verb), mode)) + + default: + fmt.Fprintf(f, "%%!%c(*types.Sym=%p)", verb, s) + } +} + +func smodeString(s *types.Sym, mode FmtMode) string { return sconv(s, 0, mode) } + +// See #16897 before changing the implementation of sconv. +func sconv(s *types.Sym, flag FmtFlag, mode FmtMode) string { + if flag&FmtLong != 0 { + panic("linksymfmt") + } + + if s == nil { + return "<S>" + } + + if s.Name == "_" { + return "_" + } + buf := fmtBufferPool.Get().(*bytes.Buffer) + buf.Reset() + defer fmtBufferPool.Put(buf) + + flag, mode = flag.update(mode) + symfmt(buf, s, flag, mode) + return types.InternString(buf.Bytes()) +} + +func sconv2(b *bytes.Buffer, s *types.Sym, flag FmtFlag, mode FmtMode) { + if flag&FmtLong != 0 { + panic("linksymfmt") + } + if s == nil { + b.WriteString("<S>") + return + } + if s.Name == "_" { + b.WriteString("_") + return + } + + flag, mode = flag.update(mode) + symfmt(b, s, flag, mode) +} + +func fldconv(b *bytes.Buffer, f *types.Field, flag FmtFlag, mode FmtMode, visited map[*types.Type]int, funarg types.Funarg) { + if f == nil { + b.WriteString("<T>") + return + } + flag, mode = flag.update(mode) + if mode == FTypeIdName { + flag |= FmtUnsigned + } + + var name string + if flag&FmtShort == 0 { + s := f.Sym + + // Take the name from the original. + if mode == FErr { + s = OrigSym(s) + } + + if s != nil && f.Embedded == 0 { + if funarg != types.FunargNone { + name = modeString(AsNode(f.Nname), mode) + } else if flag&FmtLong != 0 { + name = mode.Sprintf("%0S", s) + if !types.IsExported(name) && flag&FmtUnsigned == 0 { + name = smodeString(s, mode) // qualify non-exported names (used on structs, not on funarg) + } + } else { + name = smodeString(s, mode) + } + } + } + + if name != "" { + b.WriteString(name) + b.WriteString(" ") + } + + if f.IsDDD() { + var et *types.Type + if f.Type != nil { + et = f.Type.Elem() + } + b.WriteString("...") + tconv2(b, et, 0, mode, visited) + } else { + tconv2(b, f.Type, 0, mode, visited) + } + + if flag&FmtShort == 0 && funarg == types.FunargNone && f.Note != "" { + b.WriteString(" ") + b.WriteString(strconv.Quote(f.Note)) + } +} + +// "%L" print definition, not name +// "%S" omit 'func' and receiver from function types, short type names +func typeFormat(t *types.Type, s fmt.State, verb rune, mode FmtMode) { + switch verb { + case 'v', 'S', 'L': + fmt.Fprint(s, tconv(t, fmtFlag(s, verb), mode)) + default: + fmt.Fprintf(s, "%%!%c(*Type=%p)", verb, t) + } +} + +func (n *node) String() string { return fmt.Sprint(n) } +func modeString(n Node, mode FmtMode) string { return mode.Sprint(n) } + +// "%L" suffix with "(type %T)" where possible +// "%+S" in debug mode, don't recurse, no multiline output +func nconvFmt(n Node, s fmt.State, flag FmtFlag, mode FmtMode) { + if n == nil { + fmt.Fprint(s, "<N>") + return + } + + flag, mode = flag.update(mode) + + switch mode { + case FErr: + nodeFmt(n, s, flag, mode) + + case FDbg: + dumpdepth++ + nodeDumpFmt(n, s, flag, mode) + dumpdepth-- + + default: + base.Fatalf("unhandled %%N mode: %d", mode) + } +} + +func (l Nodes) format(s fmt.State, verb rune, mode FmtMode) { + switch verb { + case 'v': + l.hconv(s, fmtFlag(s, verb), mode) + + default: + fmt.Fprintf(s, "%%!%c(Nodes)", verb) + } +} + +func (n Nodes) String() string { + return fmt.Sprint(n) +} + +// Flags: all those of %N plus '.': separate with comma's instead of semicolons. +func (l Nodes) hconv(s fmt.State, flag FmtFlag, mode FmtMode) { + if l.Len() == 0 && mode == FDbg { + fmt.Fprint(s, "<nil>") + return + } + + flag, mode = flag.update(mode) + sep := "; " + if mode == FDbg { + sep = "\n" + } else if flag&FmtComma != 0 { + sep = ", " + } + + for i, n := range l.Slice() { + fmt.Fprint(s, modeString(n, mode)) + if i+1 < l.Len() { + fmt.Fprint(s, sep) + } + } +} + +func DumpList(s string, l Nodes) { + fmt.Printf("%s%+v\n", s, l) +} + +func FDumpList(w io.Writer, s string, l Nodes) { + fmt.Fprintf(w, "%s%+v\n", s, l) +} + +func Dump(s string, n Node) { + fmt.Printf("%s [%p]%+v\n", s, n, n) +} + +// TODO(gri) make variable local somehow +var dumpdepth int + +// indent prints indentation to s. +func indent(s fmt.State) { + fmt.Fprint(s, "\n") + for i := 0; i < dumpdepth; i++ { + fmt.Fprint(s, ". ") + } +} + +func ellipsisIf(b bool) string { + if b { + return "..." + } + return "" +} + +// numImport tracks how often a package with a given name is imported. +// It is used to provide a better error message (by using the package +// path to disambiguate) if a package that appears multiple times with +// the same name appears in an error message. +var NumImport = make(map[string]int) + +func InstallTypeFormats() { + types.Sconv = func(s *types.Sym, flag, mode int) string { + return sconv(s, FmtFlag(flag), FmtMode(mode)) + } + types.Tconv = func(t *types.Type, flag, mode int) string { + return tconv(t, FmtFlag(flag), FmtMode(mode)) + } + types.FormatSym = func(sym *types.Sym, s fmt.State, verb rune, mode int) { + symFormat(sym, s, verb, FmtMode(mode)) + } + types.FormatType = func(t *types.Type, s fmt.State, verb rune, mode int) { + typeFormat(t, s, verb, FmtMode(mode)) + } +} + +// Line returns n's position as a string. If n has been inlined, +// it uses the outermost position where n has been inlined. +func Line(n Node) string { + return base.FmtPos(n.Pos()) +} |