// 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 gc import ( "cmd/internal/obj" "fmt" "strings" "unicode/utf8" ) // // Format conversions // %L int Line numbers // // %E int etype values (aka 'Kind') // // %O int Node Opcodes // Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg) // // %J Node* Node details // Flags: "%hJ" suppresses things not relevant until walk. // // %V Val* Constant values // // %S Sym* Symbols // Flags: +,- #: mode (see below) // "%hS" unqualified identifier in any mode // "%hhS" in export mode: unqualified identifier if exported, qualified if not // // %T Type* Types // Flags: +,- #: mode (see below) // 'l' definition instead of name. // 'h' omit "func" and receiver in function types // 'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix. // // %N Node* Nodes // Flags: +,- #: mode (see below) // 'h' (only in +/debug mode) suppress recursion // 'l' (only in Error mode) print "foo (type Bar)" // // %H NodeList* NodeLists // Flags: those of %N // ',' separate items with ',' instead of ';' // // %Z Strlit* String literals // // In mparith1.c: // %B Mpint* Big integers // %F Mpflt* Big floats // // %S, %T and %N obey use the following flags to set the format mode: const ( FErr = iota FDbg FExp FTypeId ) var fmtmode int = FErr var fmtpkgpfx int // %uT stickyness // // E.g. for %S: %+S %#S %-S print an identifier properly qualified for debug/export/internal mode. // // The mode flags +, - and # are sticky, meaning they persist through // recursions of %N, %T and %S, but not the h and l flags. The u flag is // sticky only on %T recursions and only used in %-/Sym mode. // // Useful format combinations: // // %+N %+H multiline recursive debug dump of node/nodelist // %+hN %+hH non recursive debug dump // // %#N %#T export format // %#lT type definition instead of name // %#hT omit"func" and receiver in function signature // // %lN "foo (type Bar)" for error messages // // %-T type identifiers // %-hT type identifiers without "func" and arg names in type signatures (methodsym) // %-uT type identifiers with package name instead of prefix (typesym, dcommontype, typehash) // func setfmode(flags *int) int { var fm int fm = fmtmode if *flags&obj.FmtSign != 0 { fmtmode = FDbg } else if *flags&obj.FmtSharp != 0 { fmtmode = FExp } else if *flags&obj.FmtLeft != 0 { fmtmode = FTypeId } *flags &^= (obj.FmtSharp | obj.FmtLeft | obj.FmtSign) return fm } // Fmt "%L": Linenumbers var goopnames = []string{ OADDR: "&", OADD: "+", OADDSTR: "+", OANDAND: "&&", OANDNOT: "&^", OAND: "&", OAPPEND: "append", OAS: "=", OAS2: "=", OBREAK: "break", OCALL: "function call", // not actual syntax OCAP: "cap", OCASE: "case", OCLOSE: "close", OCOMPLEX: "complex", OCOM: "^", OCONTINUE: "continue", OCOPY: "copy", ODEC: "--", ODELETE: "delete", ODEFER: "defer", ODIV: "/", OEQ: "==", OFALL: "fallthrough", OFOR: "for", OGE: ">=", OGOTO: "goto", OGT: ">", OIF: "if", OIMAG: "imag", OINC: "++", OIND: "*", OLEN: "len", OLE: "<=", OLSH: "<<", OLT: "<", OMAKE: "make", OMINUS: "-", OMOD: "%", OMUL: "*", ONEW: "new", ONE: "!=", ONOT: "!", OOROR: "||", OOR: "|", OPANIC: "panic", OPLUS: "+", OPRINTN: "println", OPRINT: "print", ORANGE: "range", OREAL: "real", ORECV: "<-", ORECOVER: "recover", ORETURN: "return", ORSH: ">>", OSELECT: "select", OSEND: "<-", OSUB: "-", OSWITCH: "switch", OXOR: "^", } // Fmt "%O": Node opcodes func Oconv(o int, flag int) string { var fp string if (flag&obj.FmtSharp != 0 /*untyped*/) || fmtmode != FDbg { if o >= 0 && o < len(goopnames) && goopnames[o] != "" { fp += goopnames[o] return fp } } if o >= 0 && o < len(opnames) && opnames[o] != "" { fp += opnames[o] return fp } fp += fmt.Sprintf("O-%d", o) return fp } var classnames = []string{ "Pxxx", "PEXTERN", "PAUTO", "PPARAM", "PPARAMOUT", "PPARAMREF", "PFUNC", } // Fmt "%J": Node details. func Jconv(n *Node, flag int) string { var fp string var s string var c int c = flag & obj.FmtShort if c == 0 && n.Ullman != 0 { fp += fmt.Sprintf(" u(%d)", n.Ullman) } if c == 0 && n.Addable != 0 { fp += fmt.Sprintf(" a(%d)", n.Addable) } if c == 0 && n.Vargen != 0 { fp += fmt.Sprintf(" g(%d)", n.Vargen) } if n.Lineno != 0 { fp += fmt.Sprintf(" l(%d)", n.Lineno) } if c == 0 && n.Xoffset != BADWIDTH { fp += fmt.Sprintf(" x(%d%+d)", n.Xoffset, n.Stkdelta) } if n.Class != 0 { s = "" if n.Class&PHEAP != 0 { s = ",heap" } if int(n.Class&^PHEAP) < len(classnames) { fp += fmt.Sprintf(" class(%s%s)", classnames[n.Class&^PHEAP], s) } else { fp += fmt.Sprintf(" class(%d?%s)", n.Class&^PHEAP, s) } } if n.Colas != 0 { fp += fmt.Sprintf(" colas(%d)", n.Colas) } if n.Funcdepth != 0 { fp += fmt.Sprintf(" f(%d)", n.Funcdepth) } switch n.Esc { case EscUnknown: break case EscHeap: fp += fmt.Sprintf(" esc(h)") case EscScope: fp += fmt.Sprintf(" esc(s)") case EscNone: fp += fmt.Sprintf(" esc(no)") case EscNever: if c == 0 { fp += fmt.Sprintf(" esc(N)") } default: fp += fmt.Sprintf(" esc(%d)", n.Esc) } if n.Escloopdepth != 0 { fp += fmt.Sprintf(" ld(%d)", n.Escloopdepth) } if c == 0 && n.Typecheck != 0 { fp += fmt.Sprintf(" tc(%d)", n.Typecheck) } if c == 0 && n.Dodata != 0 { fp += fmt.Sprintf(" dd(%d)", n.Dodata) } if n.Isddd != 0 { fp += fmt.Sprintf(" isddd(%d)", n.Isddd) } if n.Implicit != 0 { fp += fmt.Sprintf(" implicit(%d)", n.Implicit) } if n.Embedded != 0 { fp += fmt.Sprintf(" embedded(%d)", n.Embedded) } if n.Addrtaken != 0 { fp += fmt.Sprintf(" addrtaken") } if n.Assigned != 0 { fp += fmt.Sprintf(" assigned") } if c == 0 && n.Used != 0 { fp += fmt.Sprintf(" used(%d)", n.Used) } return fp } // Fmt "%V": Values func Vconv(v *Val, flag int) string { var fp string var x int64 switch v.Ctype { case CTINT: if (flag&obj.FmtSharp != 0 /*untyped*/) || fmtmode == FExp { fp += fmt.Sprintf("%v", Bconv(v.U.Xval, obj.FmtSharp)) return fp } fp += fmt.Sprintf("%v", Bconv(v.U.Xval, 0)) return fp case CTRUNE: x = Mpgetfix(v.U.Xval) if ' ' <= x && x < 0x80 && x != '\\' && x != '\'' { fp += fmt.Sprintf("'%c'", int(x)) return fp } if 0 <= x && x < 1<<16 { fp += fmt.Sprintf("'\\u%04x'", uint(int(x))) return fp } if 0 <= x && x <= utf8.MaxRune { fp += fmt.Sprintf("'\\U%08x'", uint64(x)) return fp } fp += fmt.Sprintf("('\\x00' + %v)", Bconv(v.U.Xval, 0)) return fp case CTFLT: if (flag&obj.FmtSharp != 0 /*untyped*/) || fmtmode == FExp { fp += fmt.Sprintf("%v", Fconv(v.U.Fval, 0)) return fp } fp += fmt.Sprintf("%v", Fconv(v.U.Fval, obj.FmtSharp)) return fp case CTCPLX: if (flag&obj.FmtSharp != 0 /*untyped*/) || fmtmode == FExp { fp += fmt.Sprintf("(%v+%vi)", Fconv(&v.U.Cval.Real, 0), Fconv(&v.U.Cval.Imag, 0)) return fp } if mpcmpfltc(&v.U.Cval.Real, 0) == 0 { fp += fmt.Sprintf("%vi", Fconv(&v.U.Cval.Imag, obj.FmtSharp)) return fp } if mpcmpfltc(&v.U.Cval.Imag, 0) == 0 { fp += fmt.Sprintf("%v", Fconv(&v.U.Cval.Real, obj.FmtSharp)) return fp } if mpcmpfltc(&v.U.Cval.Imag, 0) < 0 { fp += fmt.Sprintf("(%v%vi)", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp)) return fp } fp += fmt.Sprintf("(%v+%vi)", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp)) return fp case CTSTR: fp += fmt.Sprintf("\"%v\"", Zconv(v.U.Sval, 0)) return fp case CTBOOL: if v.U.Bval != 0 { fp += "true" return fp } fp += "false" return fp case CTNIL: fp += "nil" return fp } fp += fmt.Sprintf("<ctype=%d>", v.Ctype) return fp } // Fmt "%Z": escaped string literals func Zconv(sp *Strlit, flag int) string { var fp string var s string var n int if sp == nil { fp += "<nil>" return fp } // NOTE: Keep in sync with ../ld/go.c:/^Zconv. s = sp.S for i := 0; i < len(s); i += n { var r rune r, n = utf8.DecodeRuneInString(s[i:]) switch r { case utf8.RuneError: if n == 1 { fp += fmt.Sprintf("\\x%02x", s[i]) break } fallthrough // fall through default: if r < ' ' { fp += fmt.Sprintf("\\x%02x", r) break } fp += string(r) case '\t': fp += "\\t" case '\n': fp += "\\n" case '"', '\\': fp += `\` + string(r) case 0xFEFF: // BOM, basically disallowed in source code fp += "\\uFEFF" } } return fp } /* s%,%,\n%g s%\n+%\n%g s%^[ ]*T%%g s%,.*%%g s%.+% [T&] = "&",%g s%^ ........*\]%&~%g s%~ %%g */ var etnames = []string{ TINT: "INT", TUINT: "UINT", TINT8: "INT8", TUINT8: "UINT8", TINT16: "INT16", TUINT16: "UINT16", TINT32: "INT32", TUINT32: "UINT32", TINT64: "INT64", TUINT64: "UINT64", TUINTPTR: "UINTPTR", TFLOAT32: "FLOAT32", TFLOAT64: "FLOAT64", TCOMPLEX64: "COMPLEX64", TCOMPLEX128: "COMPLEX128", TBOOL: "BOOL", TPTR32: "PTR32", TPTR64: "PTR64", TFUNC: "FUNC", TARRAY: "ARRAY", TSTRUCT: "STRUCT", TCHAN: "CHAN", TMAP: "MAP", TINTER: "INTER", TFORW: "FORW", TFIELD: "FIELD", TSTRING: "STRING", TANY: "ANY", } // Fmt "%E": etype func Econv(et int, flag int) string { var fp string if et >= 0 && et < len(etnames) && etnames[et] != "" { fp += etnames[et] return fp } fp += fmt.Sprintf("E-%d", et) return fp } // Fmt "%S": syms func symfmt(s *Sym, flag int) string { var fp string var p string if s.Pkg != nil && flag&obj.FmtShort == 0 /*untyped*/ { switch fmtmode { case FErr: // This is for the user if s.Pkg == localpkg { fp += s.Name return fp } // If the name was used by multiple packages, display the full path, if s.Pkg.Name != "" && Pkglookup(s.Pkg.Name, nil).Npkg > 1 { fp += fmt.Sprintf("\"%v\".%s", Zconv(s.Pkg.Path, 0), s.Name) return fp } fp += fmt.Sprintf("%s.%s", s.Pkg.Name, s.Name) return fp case FDbg: fp += fmt.Sprintf("%s.%s", s.Pkg.Name, s.Name) return fp case FTypeId: if flag&obj.FmtUnsigned != 0 /*untyped*/ { fp += fmt.Sprintf("%s.%s", s.Pkg.Name, s.Name) return fp // dcommontype, typehash } fp += fmt.Sprintf("%s.%s", s.Pkg.Prefix, s.Name) return fp // (methodsym), typesym, weaksym case FExp: if s.Name != "" && s.Name[0] == '.' { Fatal("exporting synthetic symbol %s", s.Name) } if s.Pkg != builtinpkg { fp += fmt.Sprintf("@\"%v\".%s", Zconv(s.Pkg.Path, 0), s.Name) return fp } } } if flag&obj.FmtByte != 0 /*untyped*/ { // FmtByte (hh) implies FmtShort (h) // skip leading "type." in method name p = s.Name if i := strings.LastIndex(s.Name, "."); i >= 0 { p = s.Name[i+1:] } // exportname needs to see the name without the prefix too. if (fmtmode == FExp && !exportname(p)) || fmtmode == FDbg { fp += fmt.Sprintf("@\"%v\".%s", Zconv(s.Pkg.Path, 0), p) return fp } fp += p return fp } fp += s.Name return fp } var basicnames = []string{ TINT: "int", TUINT: "uint", TINT8: "int8", TUINT8: "uint8", TINT16: "int16", TUINT16: "uint16", TINT32: "int32", TUINT32: "uint32", TINT64: "int64", TUINT64: "uint64", TUINTPTR: "uintptr", TFLOAT32: "float32", TFLOAT64: "float64", TCOMPLEX64: "complex64", TCOMPLEX128: "complex128", TBOOL: "bool", TANY: "any", TSTRING: "string", TNIL: "nil", TIDEAL: "untyped number", TBLANK: "blank", } func typefmt(t *Type, flag int) string { var fp string var t1 *Type var s *Sym if t == nil { fp += "<T>" return fp } if t == bytetype || t == runetype { // in %-T mode collapse rune and byte with their originals. if fmtmode != FTypeId { fp += fmt.Sprintf("%v", Sconv(t.Sym, obj.FmtShort)) return fp } t = Types[t.Etype] } if t == errortype { fp += "error" return fp } // Unless the 'l' flag was specified, if the type has a name, just print that name. if flag&obj.FmtLong == 0 /*untyped*/ && t.Sym != nil && t.Etype != TFIELD && t != Types[t.Etype] { switch fmtmode { case FTypeId: if flag&obj.FmtShort != 0 /*untyped*/ { if t.Vargen != 0 { fp += fmt.Sprintf("%v·%d", Sconv(t.Sym, obj.FmtShort), t.Vargen) return fp } fp += fmt.Sprintf("%v", Sconv(t.Sym, obj.FmtShort)) return fp } if flag&obj.FmtUnsigned != 0 /*untyped*/ { fp += fmt.Sprintf("%v", Sconv(t.Sym, obj.FmtUnsigned)) return fp } fallthrough // fallthrough case FExp: if t.Sym.Pkg == localpkg && t.Vargen != 0 { fp += fmt.Sprintf("%v·%d", Sconv(t.Sym, 0), t.Vargen) return fp } } fp += fmt.Sprintf("%v", Sconv(t.Sym, 0)) return fp } if int(t.Etype) < len(basicnames) && basicnames[t.Etype] != "" { if fmtmode == FErr && (t == idealbool || t == idealstring) { fp += "untyped " } fp += basicnames[t.Etype] return fp } if fmtmode == FDbg { fp += fmt.Sprintf("%v-", Econv(int(t.Etype), 0)) } switch t.Etype { case TPTR32, TPTR64: if fmtmode == FTypeId && (flag&obj.FmtShort != 0 /*untyped*/) { fp += fmt.Sprintf("*%v", Tconv(t.Type, obj.FmtShort)) return fp } fp += fmt.Sprintf("*%v", Tconv(t.Type, 0)) return fp case TARRAY: if t.Bound >= 0 { fp += fmt.Sprintf("[%d]%v", t.Bound, Tconv(t.Type, 0)) return fp } if t.Bound == -100 { fp += fmt.Sprintf("[...]%v", Tconv(t.Type, 0)) return fp } fp += fmt.Sprintf("[]%v", Tconv(t.Type, 0)) return fp case TCHAN: switch t.Chan { case Crecv: fp += fmt.Sprintf("<-chan %v", Tconv(t.Type, 0)) return fp case Csend: fp += fmt.Sprintf("chan<- %v", Tconv(t.Type, 0)) return fp } if t.Type != nil && t.Type.Etype == TCHAN && t.Type.Sym == nil && t.Type.Chan == Crecv { fp += fmt.Sprintf("chan (%v)", Tconv(t.Type, 0)) return fp } fp += fmt.Sprintf("chan %v", Tconv(t.Type, 0)) return fp case TMAP: fp += fmt.Sprintf("map[%v]%v", Tconv(t.Down, 0), Tconv(t.Type, 0)) return fp case TINTER: fp += "interface {" for t1 = t.Type; t1 != nil; t1 = t1.Down { if exportname(t1.Sym.Name) { if t1.Down != nil { fp += fmt.Sprintf(" %v%v;", Sconv(t1.Sym, obj.FmtShort), Tconv(t1.Type, obj.FmtShort)) } else { fp += fmt.Sprintf(" %v%v ", Sconv(t1.Sym, obj.FmtShort), Tconv(t1.Type, obj.FmtShort)) } } else { // non-exported method names must be qualified if t1.Down != nil { fp += fmt.Sprintf(" %v%v;", Sconv(t1.Sym, obj.FmtUnsigned), Tconv(t1.Type, obj.FmtShort)) } else { fp += fmt.Sprintf(" %v%v ", Sconv(t1.Sym, obj.FmtUnsigned), Tconv(t1.Type, obj.FmtShort)) } } } fp += "}" return fp case TFUNC: if flag&obj.FmtShort != 0 /*untyped*/ { fp += fmt.Sprintf("%v", Tconv(getinargx(t), 0)) } else { if t.Thistuple != 0 { fp += fmt.Sprintf("method%v func%v", Tconv(getthisx(t), 0), Tconv(getinargx(t), 0)) } else { fp += fmt.Sprintf("func%v", Tconv(getinargx(t), 0)) } } switch t.Outtuple { case 0: break case 1: if fmtmode != FExp { fp += fmt.Sprintf(" %v", Tconv(getoutargx(t).Type.Type, 0)) // struct->field->field's type break } fallthrough default: fp += fmt.Sprintf(" %v", Tconv(getoutargx(t), 0)) } return fp // Format the bucket struct for map[x]y as map.bucket[x]y. // This avoids a recursive print that generates very long names. case TSTRUCT: if t.Map != nil { if t.Map.Bucket == t { fp += fmt.Sprintf("map.bucket[%v]%v", Tconv(t.Map.Down, 0), Tconv(t.Map.Type, 0)) return fp } if t.Map.Hmap == t { fp += fmt.Sprintf("map.hdr[%v]%v", Tconv(t.Map.Down, 0), Tconv(t.Map.Type, 0)) return fp } if t.Map.Hiter == t { fp += fmt.Sprintf("map.iter[%v]%v", Tconv(t.Map.Down, 0), Tconv(t.Map.Type, 0)) return fp } Yyerror("unknown internal map type") } if t.Funarg != 0 { fp += "(" if fmtmode == FTypeId || fmtmode == FErr { // no argument names on function signature, and no "noescape"/"nosplit" tags for t1 = t.Type; t1 != nil; t1 = t1.Down { if t1.Down != nil { fp += fmt.Sprintf("%v, ", Tconv(t1, obj.FmtShort)) } else { fp += fmt.Sprintf("%v", Tconv(t1, obj.FmtShort)) } } } else { for t1 = t.Type; t1 != nil; t1 = t1.Down { if t1.Down != nil { fp += fmt.Sprintf("%v, ", Tconv(t1, 0)) } else { fp += fmt.Sprintf("%v", Tconv(t1, 0)) } } } fp += ")" } else { fp += "struct {" for t1 = t.Type; t1 != nil; t1 = t1.Down { if t1.Down != nil { fp += fmt.Sprintf(" %v;", Tconv(t1, obj.FmtLong)) } else { fp += fmt.Sprintf(" %v ", Tconv(t1, obj.FmtLong)) } } fp += "}" } return fp case TFIELD: if flag&obj.FmtShort == 0 /*untyped*/ { s = t.Sym // Take the name from the original, lest we substituted it with ~r%d or ~b%d. // ~r%d is a (formerly) unnamed result. if (fmtmode == FErr || fmtmode == FExp) && t.Nname != nil { if t.Nname.Orig != nil { s = t.Nname.Orig.Sym if s != nil && s.Name[0] == '~' { if s.Name[1] == 'r' { // originally an unnamed result s = nil } else if s.Name[1] == 'b' { // originally the blank identifier _ s = Lookup("_") } } } else { s = nil } } if s != nil && t.Embedded == 0 { if t.Funarg != 0 { fp += fmt.Sprintf("%v ", Nconv(t.Nname, 0)) } else if flag&obj.FmtLong != 0 /*untyped*/ { fp += fmt.Sprintf("%v ", Sconv(s, obj.FmtShort|obj.FmtByte)) // qualify non-exported names (used on structs, not on funarg) } else { fp += fmt.Sprintf("%v ", Sconv(s, 0)) } } else if fmtmode == FExp { // TODO(rsc) this breaks on the eliding of unused arguments in the backend // when this is fixed, the special case in dcl.c checkarglist can go. //if(t->funarg) // fmtstrcpy(fp, "_ "); //else if t.Embedded != 0 && s.Pkg != nil && len(s.Pkg.Path.S) > 0 { fp += fmt.Sprintf("@\"%v\".? ", Zconv(s.Pkg.Path, 0)) } else { fp += "? " } } } if t.Isddd != 0 { fp += fmt.Sprintf("...%v", Tconv(t.Type.Type, 0)) } else { fp += fmt.Sprintf("%v", Tconv(t.Type, 0)) } if flag&obj.FmtShort == 0 /*untyped*/ && t.Note != nil { fp += fmt.Sprintf(" \"%v\"", Zconv(t.Note, 0)) } return fp case TFORW: if t.Sym != nil { fp += fmt.Sprintf("undefined %v", Sconv(t.Sym, 0)) return fp } fp += "undefined" return fp case TUNSAFEPTR: if fmtmode == FExp { fp += fmt.Sprintf("@\"unsafe\".Pointer") return fp } fp += fmt.Sprintf("unsafe.Pointer") return fp } if fmtmode == FExp { Fatal("missing %v case during export", Econv(int(t.Etype), 0)) } // Don't know how to handle - fall back to detailed prints. fp += fmt.Sprintf("%v <%v> %v", Econv(int(t.Etype), 0), Sconv(t.Sym, 0), Tconv(t.Type, 0)) return fp } // Statements which may be rendered with a simplestmt as init. func stmtwithinit(op int) bool { switch op { case OIF, OFOR, OSWITCH: return true } return false } func stmtfmt(n *Node) string { var f string var complexinit bool var simpleinit bool var extrablock bool // 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.Ninit != nil && n.Ninit.Next == nil && n.Ninit.N.Ninit == nil && stmtwithinit(int(n.Op)) // otherwise, print the inits as separate statements complexinit = n.Ninit != nil && !simpleinit && (fmtmode != FErr) // but if it was for if/for/switch, put in an extra surrounding block to limit the scope extrablock = complexinit && stmtwithinit(int(n.Op)) if extrablock { f += "{" } if complexinit { f += fmt.Sprintf(" %v; ", Hconv(n.Ninit, 0)) } switch n.Op { case ODCL: if fmtmode == FExp { switch n.Left.Class &^ PHEAP { case PPARAM, PPARAMOUT, PAUTO: f += fmt.Sprintf("var %v %v", Nconv(n.Left, 0), Tconv(n.Left.Type, 0)) goto ret } } f += fmt.Sprintf("var %v %v", Sconv(n.Left.Sym, 0), Tconv(n.Left.Type, 0)) case ODCLFIELD: if n.Left != nil { f += fmt.Sprintf("%v %v", Nconv(n.Left, 0), Nconv(n.Right, 0)) } else { f += fmt.Sprintf("%v", Nconv(n.Right, 0)) } // Don't export "v = <N>" initializing statements, hope they're always // preceded by the DCL which will be re-parsed and typecheck to reproduce // the "v = <N>" again. case OAS: if fmtmode == FExp && n.Right == nil { break } if n.Colas != 0 && !complexinit { f += fmt.Sprintf("%v := %v", Nconv(n.Left, 0), Nconv(n.Right, 0)) } else { f += fmt.Sprintf("%v = %v", Nconv(n.Left, 0), Nconv(n.Right, 0)) } case OASOP: if n.Implicit != 0 { if n.Etype == OADD { f += fmt.Sprintf("%v++", Nconv(n.Left, 0)) } else { f += fmt.Sprintf("%v--", Nconv(n.Left, 0)) } break } f += fmt.Sprintf("%v %v= %v", Nconv(n.Left, 0), Oconv(int(n.Etype), obj.FmtSharp), Nconv(n.Right, 0)) case OAS2: if n.Colas != 0 && !complexinit { f += fmt.Sprintf("%v := %v", Hconv(n.List, obj.FmtComma), Hconv(n.Rlist, obj.FmtComma)) break } fallthrough // fallthrough case OAS2DOTTYPE, OAS2FUNC, OAS2MAPR, OAS2RECV: f += fmt.Sprintf("%v = %v", Hconv(n.List, obj.FmtComma), Hconv(n.Rlist, obj.FmtComma)) case ORETURN: f += fmt.Sprintf("return %v", Hconv(n.List, obj.FmtComma)) case ORETJMP: f += fmt.Sprintf("retjmp %v", Sconv(n.Sym, 0)) case OPROC: f += fmt.Sprintf("go %v", Nconv(n.Left, 0)) case ODEFER: f += fmt.Sprintf("defer %v", Nconv(n.Left, 0)) case OIF: if simpleinit { f += fmt.Sprintf("if %v; %v { %v }", Nconv(n.Ninit.N, 0), Nconv(n.Ntest, 0), Hconv(n.Nbody, 0)) } else { f += fmt.Sprintf("if %v { %v }", Nconv(n.Ntest, 0), Hconv(n.Nbody, 0)) } if n.Nelse != nil { f += fmt.Sprintf(" else { %v }", Hconv(n.Nelse, 0)) } case OFOR: if fmtmode == FErr { // TODO maybe only if FmtShort, same below f += "for loop" break } f += "for" if simpleinit { f += fmt.Sprintf(" %v;", Nconv(n.Ninit.N, 0)) } else if n.Nincr != nil { f += " ;" } if n.Ntest != nil { f += fmt.Sprintf(" %v", Nconv(n.Ntest, 0)) } if n.Nincr != nil { f += fmt.Sprintf("; %v", Nconv(n.Nincr, 0)) } else if simpleinit { f += ";" } f += fmt.Sprintf(" { %v }", Hconv(n.Nbody, 0)) case ORANGE: if fmtmode == FErr { f += "for loop" break } if n.List == nil { f += fmt.Sprintf("for range %v { %v }", Nconv(n.Right, 0), Hconv(n.Nbody, 0)) break } f += fmt.Sprintf("for %v = range %v { %v }", Hconv(n.List, obj.FmtComma), Nconv(n.Right, 0), Hconv(n.Nbody, 0)) case OSELECT, OSWITCH: if fmtmode == FErr { f += fmt.Sprintf("%v statement", Oconv(int(n.Op), 0)) break } f += fmt.Sprintf("%v", Oconv(int(n.Op), obj.FmtSharp)) if simpleinit { f += fmt.Sprintf(" %v;", Nconv(n.Ninit.N, 0)) } if n.Ntest != nil { f += fmt.Sprintf("%v", Nconv(n.Ntest, 0)) } f += fmt.Sprintf(" { %v }", Hconv(n.List, 0)) case OCASE, OXCASE: if n.List != nil { f += fmt.Sprintf("case %v: %v", Hconv(n.List, obj.FmtComma), Hconv(n.Nbody, 0)) } else { f += fmt.Sprintf("default: %v", Hconv(n.Nbody, 0)) } case OBREAK, OCONTINUE, OGOTO, OFALL, OXFALL: if n.Left != nil { f += fmt.Sprintf("%v %v", Oconv(int(n.Op), obj.FmtSharp), Nconv(n.Left, 0)) } else { f += fmt.Sprintf("%v", Oconv(int(n.Op), obj.FmtSharp)) } case OEMPTY: break case OLABEL: f += fmt.Sprintf("%v: ", Nconv(n.Left, 0)) } ret: if extrablock { f += "}" } return f } var opprec = []int{ OAPPEND: 8, OARRAYBYTESTR: 8, OARRAYLIT: 8, OARRAYRUNESTR: 8, OCALLFUNC: 8, OCALLINTER: 8, OCALLMETH: 8, OCALL: 8, OCAP: 8, OCLOSE: 8, OCONVIFACE: 8, OCONVNOP: 8, OCONV: 8, OCOPY: 8, ODELETE: 8, OLEN: 8, OLITERAL: 8, OMAKESLICE: 8, OMAKE: 8, OMAPLIT: 8, ONAME: 8, ONEW: 8, ONONAME: 8, OPACK: 8, OPANIC: 8, OPAREN: 8, OPRINTN: 8, OPRINT: 8, ORUNESTR: 8, OSTRARRAYBYTE: 8, OSTRARRAYRUNE: 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, ODOTINTER: 8, ODOTMETH: 8, ODOTPTR: 8, ODOTTYPE2: 8, ODOTTYPE: 8, ODOT: 8, OXDOT: 8, OCALLPART: 8, OPLUS: 7, ONOT: 7, OCOM: 7, OMINUS: 7, OADDR: 7, OIND: 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, OCMPSTR: 4, OCMPIFACE: 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, OGOTO: -1, OIF: -1, OLABEL: -1, OPROC: -1, ORANGE: -1, ORETURN: -1, OSELECT: -1, OSWITCH: -1, OXCASE: -1, OXFALL: -1, OEND: 0, } func exprfmt(n *Node, prec int) string { var f string var nprec int var ptrlit bool var l *NodeList for n != nil && n.Implicit != 0 && (n.Op == OIND || n.Op == OADDR) { n = n.Left } if n == nil { f += "<N>" return f } nprec = opprec[n.Op] if n.Op == OTYPE && n.Sym != nil { nprec = 8 } if prec > nprec { f += fmt.Sprintf("(%v)", Nconv(n, 0)) return f } switch n.Op { case OPAREN: f += fmt.Sprintf("(%v)", Nconv(n.Left, 0)) return f case ODDDARG: f += fmt.Sprintf("... argument") return f case OREGISTER: f += fmt.Sprintf("%v", Ctxt.Rconv(int(n.Val.U.Reg))) return f case OLITERAL: // this is a bit of a mess if fmtmode == FErr && n.Sym != nil { f += fmt.Sprintf("%v", Sconv(n.Sym, 0)) return f } if n.Val.Ctype == CTNIL && n.Orig != nil && n.Orig != n { f += exprfmt(n.Orig, prec) return f } if n.Type != nil && n.Type != Types[n.Type.Etype] && n.Type != idealbool && n.Type != idealstring { // Need parens when type begins with what might // be misinterpreted as a unary operator: * or <-. if Isptr[n.Type.Etype] != 0 || (n.Type.Etype == TCHAN && n.Type.Chan == Crecv) { f += fmt.Sprintf("(%v)(%v)", Tconv(n.Type, 0), Vconv(&n.Val, 0)) return f } else { f += fmt.Sprintf("%v(%v)", Tconv(n.Type, 0), Vconv(&n.Val, 0)) return f } } f += fmt.Sprintf("%v", Vconv(&n.Val, 0)) return f // Special case: name used as local variable in export. // _ becomes ~b%d internally; print as _ for export case ONAME: if fmtmode == FExp && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' { f += fmt.Sprintf("_") return f } if fmtmode == FExp && n.Sym != nil && !isblank(n) && n.Vargen > 0 { f += fmt.Sprintf("%v·%d", Sconv(n.Sym, 0), n.Vargen) return f } // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method, // but for export, this should be rendered as (*pkg.T).meth. // These nodes have the special property that they are names with a left OTYPE and a right ONAME. if fmtmode == FExp && n.Left != nil && n.Left.Op == OTYPE && n.Right != nil && n.Right.Op == ONAME { if Isptr[n.Left.Type.Etype] != 0 { f += fmt.Sprintf("(%v).%v", Tconv(n.Left.Type, 0), Sconv(n.Right.Sym, obj.FmtShort|obj.FmtByte)) return f } else { f += fmt.Sprintf("%v.%v", Tconv(n.Left.Type, 0), Sconv(n.Right.Sym, obj.FmtShort|obj.FmtByte)) return f } } fallthrough //fallthrough case OPACK, ONONAME: f += fmt.Sprintf("%v", Sconv(n.Sym, 0)) return f case OTYPE: if n.Type == nil && n.Sym != nil { f += fmt.Sprintf("%v", Sconv(n.Sym, 0)) return f } f += fmt.Sprintf("%v", Tconv(n.Type, 0)) return f case OTARRAY: if n.Left != nil { f += fmt.Sprintf("[]%v", Nconv(n.Left, 0)) return f } f += fmt.Sprintf("[]%v", Nconv(n.Right, 0)) return f // happens before typecheck case OTMAP: f += fmt.Sprintf("map[%v]%v", Nconv(n.Left, 0), Nconv(n.Right, 0)) return f case OTCHAN: switch n.Etype { case Crecv: f += fmt.Sprintf("<-chan %v", Nconv(n.Left, 0)) return f case Csend: f += fmt.Sprintf("chan<- %v", Nconv(n.Left, 0)) return f default: if n.Left != nil && n.Left.Op == OTCHAN && n.Left.Sym == nil && n.Left.Etype == Crecv { f += fmt.Sprintf("chan (%v)", Nconv(n.Left, 0)) return f } else { f += fmt.Sprintf("chan %v", Nconv(n.Left, 0)) return f } } fallthrough case OTSTRUCT: f += fmt.Sprintf("<struct>") return f case OTINTER: f += fmt.Sprintf("<inter>") return f case OTFUNC: f += fmt.Sprintf("<func>") return f case OCLOSURE: if fmtmode == FErr { f += "func literal" return f } if n.Nbody != nil { f += fmt.Sprintf("%v { %v }", Tconv(n.Type, 0), Hconv(n.Nbody, 0)) return f } f += fmt.Sprintf("%v { %v }", Tconv(n.Type, 0), Hconv(n.Closure.Nbody, 0)) return f case OCOMPLIT: ptrlit = n.Right != nil && n.Right.Implicit != 0 && n.Right.Type != nil && Isptr[n.Right.Type.Etype] != 0 if fmtmode == FErr { if n.Right != nil && n.Right.Type != nil && n.Implicit == 0 { if ptrlit { f += fmt.Sprintf("&%v literal", Tconv(n.Right.Type.Type, 0)) return f } else { f += fmt.Sprintf("%v literal", Tconv(n.Right.Type, 0)) return f } } f += "composite literal" return f } if fmtmode == FExp && ptrlit { // typecheck has overwritten OIND by OTYPE with pointer type. f += fmt.Sprintf("(&%v{ %v })", Tconv(n.Right.Type.Type, 0), Hconv(n.List, obj.FmtComma)) return f } f += fmt.Sprintf("(%v{ %v })", Nconv(n.Right, 0), Hconv(n.List, obj.FmtComma)) return f case OPTRLIT: if fmtmode == FExp && n.Left.Implicit != 0 { f += fmt.Sprintf("%v", Nconv(n.Left, 0)) return f } f += fmt.Sprintf("&%v", Nconv(n.Left, 0)) return f case OSTRUCTLIT: if fmtmode == FExp { // requires special handling of field names if n.Implicit != 0 { f += "{" } else { f += fmt.Sprintf("(%v{", Tconv(n.Type, 0)) } for l = n.List; l != nil; l = l.Next { f += fmt.Sprintf(" %v:%v", Sconv(l.N.Left.Sym, obj.FmtShort|obj.FmtByte), Nconv(l.N.Right, 0)) if l.Next != nil { f += "," } else { f += " " } } if n.Implicit == 0 { f += "})" return f } f += "}" return f } fallthrough // fallthrough case OARRAYLIT, OMAPLIT: if fmtmode == FErr { f += fmt.Sprintf("%v literal", Tconv(n.Type, 0)) return f } if fmtmode == FExp && n.Implicit != 0 { f += fmt.Sprintf("{ %v }", Hconv(n.List, obj.FmtComma)) return f } f += fmt.Sprintf("(%v{ %v })", Tconv(n.Type, 0), Hconv(n.List, obj.FmtComma)) return f case OKEY: if n.Left != nil && n.Right != nil { if fmtmode == FExp && n.Left.Type != nil && n.Left.Type.Etype == TFIELD { // requires special handling of field names f += fmt.Sprintf("%v:%v", Sconv(n.Left.Sym, obj.FmtShort|obj.FmtByte), Nconv(n.Right, 0)) return f } else { f += fmt.Sprintf("%v:%v", Nconv(n.Left, 0), Nconv(n.Right, 0)) return f } } if n.Left == nil && n.Right != nil { f += fmt.Sprintf(":%v", Nconv(n.Right, 0)) return f } if n.Left != nil && n.Right == nil { f += fmt.Sprintf("%v:", Nconv(n.Left, 0)) return f } f += ":" return f case OXDOT, ODOT, ODOTPTR, ODOTINTER, ODOTMETH, OCALLPART: f += exprfmt(n.Left, nprec) if n.Right == nil || n.Right.Sym == nil { f += ".<nil>" return f } f += fmt.Sprintf(".%v", Sconv(n.Right.Sym, obj.FmtShort|obj.FmtByte)) return f case ODOTTYPE, ODOTTYPE2: f += exprfmt(n.Left, nprec) if n.Right != nil { f += fmt.Sprintf(".(%v)", Nconv(n.Right, 0)) return f } f += fmt.Sprintf(".(%v)", Tconv(n.Type, 0)) return f case OINDEX, OINDEXMAP, OSLICE, OSLICESTR, OSLICEARR, OSLICE3, OSLICE3ARR: f += exprfmt(n.Left, nprec) f += fmt.Sprintf("[%v]", Nconv(n.Right, 0)) return f case OCOPY, OCOMPLEX: f += fmt.Sprintf("%v(%v, %v)", Oconv(int(n.Op), obj.FmtSharp), Nconv(n.Left, 0), Nconv(n.Right, 0)) return f case OCONV, OCONVIFACE, OCONVNOP, OARRAYBYTESTR, OARRAYRUNESTR, OSTRARRAYBYTE, OSTRARRAYRUNE, ORUNESTR: if n.Type == nil || n.Type.Sym == nil { f += fmt.Sprintf("(%v)(%v)", Tconv(n.Type, 0), Nconv(n.Left, 0)) return f } if n.Left != nil { f += fmt.Sprintf("%v(%v)", Tconv(n.Type, 0), Nconv(n.Left, 0)) return f } f += fmt.Sprintf("%v(%v)", Tconv(n.Type, 0), Hconv(n.List, obj.FmtComma)) return f case OREAL, OIMAG, OAPPEND, OCAP, OCLOSE, ODELETE, OLEN, OMAKE, ONEW, OPANIC, ORECOVER, OPRINT, OPRINTN: if n.Left != nil { f += fmt.Sprintf("%v(%v)", Oconv(int(n.Op), obj.FmtSharp), Nconv(n.Left, 0)) return f } if n.Isddd != 0 { f += fmt.Sprintf("%v(%v...)", Oconv(int(n.Op), obj.FmtSharp), Hconv(n.List, obj.FmtComma)) return f } f += fmt.Sprintf("%v(%v)", Oconv(int(n.Op), obj.FmtSharp), Hconv(n.List, obj.FmtComma)) return f case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH: f += exprfmt(n.Left, nprec) if n.Isddd != 0 { f += fmt.Sprintf("(%v...)", Hconv(n.List, obj.FmtComma)) return f } f += fmt.Sprintf("(%v)", Hconv(n.List, obj.FmtComma)) return f case OMAKEMAP, OMAKECHAN, OMAKESLICE: if n.List != nil { // pre-typecheck f += fmt.Sprintf("make(%v, %v)", Tconv(n.Type, 0), Hconv(n.List, obj.FmtComma)) return f } if n.Right != nil { f += fmt.Sprintf("make(%v, %v, %v)", Tconv(n.Type, 0), Nconv(n.Left, 0), Nconv(n.Right, 0)) return f } if n.Left != nil { f += fmt.Sprintf("make(%v, %v)", Tconv(n.Type, 0), Nconv(n.Left, 0)) return f } f += fmt.Sprintf("make(%v)", Tconv(n.Type, 0)) return f // Unary case OPLUS, OMINUS, OADDR, OCOM, OIND, ONOT, ORECV: if n.Left.Op == n.Op { f += fmt.Sprintf("%v ", Oconv(int(n.Op), obj.FmtSharp)) } else { f += fmt.Sprintf("%v", Oconv(int(n.Op), obj.FmtSharp)) } f += exprfmt(n.Left, nprec+1) return f // Binary case OADD, OAND, OANDAND, OANDNOT, ODIV, OEQ, OGE, OGT, OLE, OLT, OLSH, OMOD, OMUL, ONE, OOR, OOROR, ORSH, OSEND, OSUB, OXOR: f += exprfmt(n.Left, nprec) f += fmt.Sprintf(" %v ", Oconv(int(n.Op), obj.FmtSharp)) f += exprfmt(n.Right, nprec+1) return f case OADDSTR: for l = n.List; l != nil; l = l.Next { if l != n.List { f += fmt.Sprintf(" + ") } f += exprfmt(l.N, nprec) } return f case OCMPSTR, OCMPIFACE: f += exprfmt(n.Left, nprec) f += fmt.Sprintf(" %v ", Oconv(int(n.Etype), obj.FmtSharp)) f += exprfmt(n.Right, nprec+1) return f } f += fmt.Sprintf("<node %v>", Oconv(int(n.Op), 0)) return f } func nodefmt(n *Node, flag int) string { var f string var t *Type t = n.Type // we almost always want the original, except in export mode for literals // this saves the importer some work, and avoids us having to redo some // special casing for package unsafe if (fmtmode != FExp || n.Op != OLITERAL) && n.Orig != nil { n = n.Orig } if flag&obj.FmtLong != 0 /*untyped*/ && t != nil { if t.Etype == TNIL { f += fmt.Sprintf("nil") return f } else { f += fmt.Sprintf("%v (type %v)", Nconv(n, 0), Tconv(t, 0)) return f } } // TODO inlining produces expressions with ninits. we can't print these yet. if opprec[n.Op] < 0 { return stmtfmt(n) } f += exprfmt(n, 0) return f } var dumpdepth int func indent(s string) string { return s + "\n" + strings.Repeat(". ", dumpdepth) } func nodedump(n *Node, flag int) string { var fp string var recur bool if n == nil { return fp } recur = flag&obj.FmtShort == 0 /*untyped*/ if recur { fp = indent(fp) if dumpdepth > 10 { fp += "..." return fp } if n.Ninit != nil { fp += fmt.Sprintf("%v-init%v", Oconv(int(n.Op), 0), Hconv(n.Ninit, 0)) fp = indent(fp) } } // fmtprint(fp, "[%p]", n); switch n.Op { default: fp += fmt.Sprintf("%v%v", Oconv(int(n.Op), 0), Jconv(n, 0)) case OREGISTER, OINDREG: fp += fmt.Sprintf("%v-%v%v", Oconv(int(n.Op), 0), Ctxt.Rconv(int(n.Val.U.Reg)), Jconv(n, 0)) case OLITERAL: fp += fmt.Sprintf("%v-%v%v", Oconv(int(n.Op), 0), Vconv(&n.Val, 0), Jconv(n, 0)) case ONAME, ONONAME: if n.Sym != nil { fp += fmt.Sprintf("%v-%v%v", Oconv(int(n.Op), 0), Sconv(n.Sym, 0), Jconv(n, 0)) } else { fp += fmt.Sprintf("%v%v", Oconv(int(n.Op), 0), Jconv(n, 0)) } if recur && n.Type == nil && n.Ntype != nil { fp = indent(fp) fp += fmt.Sprintf("%v-ntype%v", Oconv(int(n.Op), 0), Nconv(n.Ntype, 0)) } case OASOP: fp += fmt.Sprintf("%v-%v%v", Oconv(int(n.Op), 0), Oconv(int(n.Etype), 0), Jconv(n, 0)) case OTYPE: fp += fmt.Sprintf("%v %v%v type=%v", Oconv(int(n.Op), 0), Sconv(n.Sym, 0), Jconv(n, 0), Tconv(n.Type, 0)) if recur && n.Type == nil && n.Ntype != nil { fp = indent(fp) fp += fmt.Sprintf("%v-ntype%v", Oconv(int(n.Op), 0), Nconv(n.Ntype, 0)) } } if n.Sym != nil && n.Op != ONAME { fp += fmt.Sprintf(" %v G%d", Sconv(n.Sym, 0), n.Vargen) } if n.Type != nil { fp += fmt.Sprintf(" %v", Tconv(n.Type, 0)) } if recur { if n.Left != nil { fp += fmt.Sprintf("%v", Nconv(n.Left, 0)) } if n.Right != nil { fp += fmt.Sprintf("%v", Nconv(n.Right, 0)) } if n.List != nil { fp = indent(fp) fp += fmt.Sprintf("%v-list%v", Oconv(int(n.Op), 0), Hconv(n.List, 0)) } if n.Rlist != nil { fp = indent(fp) fp += fmt.Sprintf("%v-rlist%v", Oconv(int(n.Op), 0), Hconv(n.Rlist, 0)) } if n.Ntest != nil { fp = indent(fp) fp += fmt.Sprintf("%v-test%v", Oconv(int(n.Op), 0), Nconv(n.Ntest, 0)) } if n.Nbody != nil { fp = indent(fp) fp += fmt.Sprintf("%v-body%v", Oconv(int(n.Op), 0), Hconv(n.Nbody, 0)) } if n.Nelse != nil { fp = indent(fp) fp += fmt.Sprintf("%v-else%v", Oconv(int(n.Op), 0), Hconv(n.Nelse, 0)) } if n.Nincr != nil { fp = indent(fp) fp += fmt.Sprintf("%v-incr%v", Oconv(int(n.Op), 0), Nconv(n.Nincr, 0)) } } return fp } // Fmt "%S": syms // Flags: "%hS" suppresses qualifying with package func Sconv(s *Sym, flag int) string { var fp string var r int var sm int var sf int if flag&obj.FmtLong != 0 /*untyped*/ { panic("linksymfmt") } if s == nil { fp += "<S>" return fp } if s.Name == "_" { fp += "_" return fp } sf = flag sm = setfmode(&flag) _ = r str := symfmt(s, flag) flag = sf fmtmode = sm return str } // Fmt "%T": types. // Flags: 'l' print definition, not name // 'h' omit 'func' and receiver from function types, short type names // 'u' package name, not prefix (FTypeId mode, sticky) func Tconv(t *Type, flag int) string { var fp string var r int var sm int var sf int if t == nil { fp += "<T>" return fp } if t.Trecur > 4 { fp += "<...>" return fp } t.Trecur++ sf = flag sm = setfmode(&flag) if fmtmode == FTypeId && (sf&obj.FmtUnsigned != 0) { fmtpkgpfx++ } if fmtpkgpfx != 0 { flag |= obj.FmtUnsigned } _ = r str := typefmt(t, flag) if fmtmode == FTypeId && (sf&obj.FmtUnsigned != 0) { fmtpkgpfx-- } flag = sf fmtmode = sm t.Trecur-- return str } // Fmt '%N': Nodes. // Flags: 'l' suffix with "(type %T)" where possible // '+h' in debug mode, don't recurse, no multiline output func Nconv(n *Node, flag int) string { var fp string var r int var sm int var sf int if n == nil { fp += "<N>" return fp } sf = flag sm = setfmode(&flag) _ = r var str string switch fmtmode { case FErr, FExp: str = nodefmt(n, flag) case FDbg: dumpdepth++ str = nodedump(n, flag) dumpdepth-- default: Fatal("unhandled %N mode") } flag = sf fmtmode = sm return str } // Fmt '%H': NodeList. // Flags: all those of %N plus ',': separate with comma's instead of semicolons. func Hconv(l *NodeList, flag int) string { var fp string var r int var sm int var sf int var sep string if l == nil && fmtmode == FDbg { fp += "<nil>" return fp } sf = flag sm = setfmode(&flag) _ = r sep = "; " if fmtmode == FDbg { sep = "\n" } else if flag&obj.FmtComma != 0 /*untyped*/ { sep = ", " } for ; l != nil; l = l.Next { fp += fmt.Sprintf("%v", Nconv(l.N, 0)) if l.Next != nil { fp += sep } } flag = sf fmtmode = sm return fp } func dumplist(s string, l *NodeList) { fmt.Printf("%s%v\n", s, Hconv(l, obj.FmtSign)) } func Dump(s string, n *Node) { fmt.Printf("%s [%p]%v\n", s, n, Nconv(n, obj.FmtSign)) }