Source file src/cmd/compile/internal/types/fmt.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package types
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/md5"
    10  	"encoding/binary"
    11  	"fmt"
    12  	"go/constant"
    13  	"strconv"
    14  	"strings"
    15  	"sync"
    16  
    17  	"cmd/compile/internal/base"
    18  )
    19  
    20  // BuiltinPkg is a fake package that declares the universe block.
    21  var BuiltinPkg *Pkg
    22  
    23  // LocalPkg is the package being compiled.
    24  var LocalPkg *Pkg
    25  
    26  // UnsafePkg is package unsafe.
    27  var UnsafePkg *Pkg
    28  
    29  // BlankSym is the blank (_) symbol.
    30  var BlankSym *Sym
    31  
    32  // OrigSym returns the original symbol written by the user.
    33  func OrigSym(s *Sym) *Sym {
    34  	if s == nil {
    35  		return nil
    36  	}
    37  
    38  	if len(s.Name) > 1 && s.Name[0] == '~' {
    39  		switch s.Name[1] {
    40  		case 'r': // originally an unnamed result
    41  			return nil
    42  		case 'b': // originally the blank identifier _
    43  			// TODO(mdempsky): Does s.Pkg matter here?
    44  			return BlankSym
    45  		}
    46  		return s
    47  	}
    48  
    49  	if strings.HasPrefix(s.Name, ".anon") {
    50  		// originally an unnamed or _ name (see subr.go: NewFuncParams)
    51  		return nil
    52  	}
    53  
    54  	return s
    55  }
    56  
    57  // numImport tracks how often a package with a given name is imported.
    58  // It is used to provide a better error message (by using the package
    59  // path to disambiguate) if a package that appears multiple times with
    60  // the same name appears in an error message.
    61  var NumImport = make(map[string]int)
    62  
    63  // fmtMode represents the kind of printing being done.
    64  // The default is regular Go syntax (fmtGo).
    65  // fmtDebug is like fmtGo but for debugging dumps and prints the type kind too.
    66  // fmtTypeID and fmtTypeIDName are for generating various unique representations
    67  // of types used in hashes, the linker, and function/method instantiations.
    68  type fmtMode int
    69  
    70  const (
    71  	fmtGo fmtMode = iota
    72  	fmtDebug
    73  	fmtTypeID
    74  	fmtTypeIDName
    75  	fmtTypeIDHash
    76  )
    77  
    78  // Sym
    79  
    80  // Format implements formatting for a Sym.
    81  // The valid formats are:
    82  //
    83  //	%v	Go syntax: Name for symbols in the local package, PkgName.Name for imported symbols.
    84  //	%+v	Debug syntax: always include PkgName. prefix even for local names.
    85  //	%S	Short syntax: Name only, no matter what.
    86  //
    87  func (s *Sym) Format(f fmt.State, verb rune) {
    88  	mode := fmtGo
    89  	switch verb {
    90  	case 'v', 'S':
    91  		if verb == 'v' && f.Flag('+') {
    92  			mode = fmtDebug
    93  		}
    94  		fmt.Fprint(f, sconv(s, verb, mode))
    95  
    96  	default:
    97  		fmt.Fprintf(f, "%%!%c(*types.Sym=%p)", verb, s)
    98  	}
    99  }
   100  
   101  func (s *Sym) String() string {
   102  	return sconv(s, 0, fmtGo)
   103  }
   104  
   105  // See #16897 for details about performance implications
   106  // before changing the implementation of sconv.
   107  func sconv(s *Sym, verb rune, mode fmtMode) string {
   108  	if verb == 'L' {
   109  		panic("linksymfmt")
   110  	}
   111  
   112  	if s == nil {
   113  		return "<S>"
   114  	}
   115  
   116  	q := pkgqual(s.Pkg, verb, mode)
   117  	if q == "" {
   118  		return s.Name
   119  	}
   120  
   121  	buf := fmtBufferPool.Get().(*bytes.Buffer)
   122  	buf.Reset()
   123  	defer fmtBufferPool.Put(buf)
   124  
   125  	buf.WriteString(q)
   126  	buf.WriteByte('.')
   127  	buf.WriteString(s.Name)
   128  	return InternString(buf.Bytes())
   129  }
   130  
   131  func sconv2(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
   132  	if verb == 'L' {
   133  		panic("linksymfmt")
   134  	}
   135  	if s == nil {
   136  		b.WriteString("<S>")
   137  		return
   138  	}
   139  
   140  	symfmt(b, s, verb, mode)
   141  }
   142  
   143  func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
   144  	name := s.Name
   145  	if q := pkgqual(s.Pkg, verb, mode); q != "" {
   146  		b.WriteString(q)
   147  		b.WriteByte('.')
   148  		switch mode {
   149  		case fmtTypeIDName:
   150  			// If name is a generic instantiation, it might have local package placeholders
   151  			// in it. Replace those placeholders with the package name. See issue 49547.
   152  			name = strings.Replace(name, LocalPkg.Prefix, q, -1)
   153  		case fmtTypeIDHash:
   154  			// If name is a generic instantiation, don't hash the instantiating types.
   155  			// This isn't great, but it is safe. If we hash the instantiating types, then
   156  			// we need to make sure they have just the package name. At this point, they
   157  			// either have "", or the whole package path, and it is hard to reconcile
   158  			// the two without depending on -p (which we might do someday).
   159  			// See issue 51250.
   160  			if i := strings.Index(name, "["); i >= 0 {
   161  				name = name[:i]
   162  			}
   163  		}
   164  	}
   165  	b.WriteString(name)
   166  }
   167  
   168  // pkgqual returns the qualifier that should be used for printing
   169  // symbols from the given package in the given mode.
   170  // If it returns the empty string, no qualification is needed.
   171  func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string {
   172  	if verb != 'S' {
   173  		switch mode {
   174  		case fmtGo: // This is for the user
   175  			if pkg == BuiltinPkg || pkg == LocalPkg {
   176  				return ""
   177  			}
   178  
   179  			// If the name was used by multiple packages, display the full path,
   180  			if pkg.Name != "" && NumImport[pkg.Name] > 1 {
   181  				return strconv.Quote(pkg.Path)
   182  			}
   183  			return pkg.Name
   184  
   185  		case fmtDebug:
   186  			return pkg.Name
   187  
   188  		case fmtTypeIDName, fmtTypeIDHash:
   189  			// dcommontype, typehash
   190  			return pkg.Name
   191  
   192  		case fmtTypeID:
   193  			// (methodsym), typesym, weaksym
   194  			return pkg.Prefix
   195  		}
   196  	}
   197  
   198  	return ""
   199  }
   200  
   201  // Type
   202  
   203  var BasicTypeNames = []string{
   204  	TINT:        "int",
   205  	TUINT:       "uint",
   206  	TINT8:       "int8",
   207  	TUINT8:      "uint8",
   208  	TINT16:      "int16",
   209  	TUINT16:     "uint16",
   210  	TINT32:      "int32",
   211  	TUINT32:     "uint32",
   212  	TINT64:      "int64",
   213  	TUINT64:     "uint64",
   214  	TUINTPTR:    "uintptr",
   215  	TFLOAT32:    "float32",
   216  	TFLOAT64:    "float64",
   217  	TCOMPLEX64:  "complex64",
   218  	TCOMPLEX128: "complex128",
   219  	TBOOL:       "bool",
   220  	TANY:        "any",
   221  	TSTRING:     "string",
   222  	TNIL:        "nil",
   223  	TIDEAL:      "untyped number",
   224  	TBLANK:      "blank",
   225  }
   226  
   227  var fmtBufferPool = sync.Pool{
   228  	New: func() interface{} {
   229  		return new(bytes.Buffer)
   230  	},
   231  }
   232  
   233  // Format implements formatting for a Type.
   234  // The valid formats are:
   235  //
   236  //	%v	Go syntax
   237  //	%+v	Debug syntax: Go syntax with a KIND- prefix for all but builtins.
   238  //	%L	Go syntax for underlying type if t is named
   239  //	%S	short Go syntax: drop leading "func" in function type
   240  //	%-S	special case for method receiver symbol
   241  //
   242  func (t *Type) Format(s fmt.State, verb rune) {
   243  	mode := fmtGo
   244  	switch verb {
   245  	case 'v', 'S', 'L':
   246  		if verb == 'v' && s.Flag('+') { // %+v is debug format
   247  			mode = fmtDebug
   248  		}
   249  		if verb == 'S' && s.Flag('-') { // %-S is special case for receiver - short typeid format
   250  			mode = fmtTypeID
   251  		}
   252  		fmt.Fprint(s, tconv(t, verb, mode))
   253  	default:
   254  		fmt.Fprintf(s, "%%!%c(*Type=%p)", verb, t)
   255  	}
   256  }
   257  
   258  // String returns the Go syntax for the type t.
   259  func (t *Type) String() string {
   260  	return tconv(t, 0, fmtGo)
   261  }
   262  
   263  // LinkString returns an unexpanded string description of t, suitable
   264  // for use in link symbols. "Unexpanded" here means that the
   265  // description uses `"".` to qualify identifiers from the current
   266  // package, and "expansion" refers to the renaming step performed by
   267  // the linker to replace these qualifiers with proper `path/to/pkg.`
   268  // qualifiers.
   269  //
   270  // After expansion, the description corresponds to type identity. That
   271  // is, for any pair of types t1 and t2, Identical(t1, t2) and
   272  // expand(t1.LinkString()) == expand(t2.LinkString()) report the same
   273  // value.
   274  //
   275  // Within a single compilation unit, LinkString always returns the
   276  // same unexpanded description for identical types. Thus it's safe to
   277  // use as a map key to implement a type-identity-keyed map. However,
   278  // make sure all LinkString calls used for this purpose happen within
   279  // the same compile process; the string keys are not stable across
   280  // multiple processes.
   281  func (t *Type) LinkString() string {
   282  	return tconv(t, 0, fmtTypeID)
   283  }
   284  
   285  // NameString generates a user-readable, mostly unique string
   286  // description of t. NameString always returns the same description
   287  // for identical types, even across compilation units.
   288  //
   289  // NameString qualifies identifiers by package name, so it has
   290  // collisions when different packages share the same names and
   291  // identifiers. It also does not distinguish function-scope defined
   292  // types from package-scoped defined types or from each other.
   293  func (t *Type) NameString() string {
   294  	return tconv(t, 0, fmtTypeIDName)
   295  }
   296  
   297  func tconv(t *Type, verb rune, mode fmtMode) string {
   298  	buf := fmtBufferPool.Get().(*bytes.Buffer)
   299  	buf.Reset()
   300  	defer fmtBufferPool.Put(buf)
   301  
   302  	tconv2(buf, t, verb, mode, nil)
   303  	return InternString(buf.Bytes())
   304  }
   305  
   306  // tconv2 writes a string representation of t to b.
   307  // flag and mode control exactly what is printed.
   308  // Any types x that are already in the visited map get printed as @%d where %d=visited[x].
   309  // See #16897 before changing the implementation of tconv.
   310  func tconv2(b *bytes.Buffer, t *Type, verb rune, mode fmtMode, visited map[*Type]int) {
   311  	if off, ok := visited[t]; ok {
   312  		// We've seen this type before, so we're trying to print it recursively.
   313  		// Print a reference to it instead.
   314  		fmt.Fprintf(b, "@%d", off)
   315  		return
   316  	}
   317  	if t == nil {
   318  		b.WriteString("<T>")
   319  		return
   320  	}
   321  	if t.Kind() == TSSA {
   322  		b.WriteString(t.extra.(string))
   323  		return
   324  	}
   325  	if t.Kind() == TTUPLE {
   326  		b.WriteString(t.FieldType(0).String())
   327  		b.WriteByte(',')
   328  		b.WriteString(t.FieldType(1).String())
   329  		return
   330  	}
   331  
   332  	if t.Kind() == TRESULTS {
   333  		tys := t.extra.(*Results).Types
   334  		for i, et := range tys {
   335  			if i > 0 {
   336  				b.WriteByte(',')
   337  			}
   338  			b.WriteString(et.String())
   339  		}
   340  		return
   341  	}
   342  
   343  	if t == AnyType || t == ByteType || t == RuneType {
   344  		// in %-T mode collapse predeclared aliases with their originals.
   345  		switch mode {
   346  		case fmtTypeIDName, fmtTypeIDHash, fmtTypeID:
   347  			t = Types[t.Kind()]
   348  		default:
   349  			sconv2(b, t.Sym(), 'S', mode)
   350  			return
   351  		}
   352  	}
   353  	if t == ErrorType {
   354  		b.WriteString("error")
   355  		return
   356  	}
   357  
   358  	// Unless the 'L' flag was specified, if the type has a name, just print that name.
   359  	if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] {
   360  		// Default to 'v' if verb is invalid.
   361  		if verb != 'S' {
   362  			verb = 'v'
   363  		}
   364  
   365  		// In unified IR, function-scope defined types will have a ·N
   366  		// suffix embedded directly in their Name. Trim this off for
   367  		// non-fmtTypeID modes.
   368  		sym := t.Sym()
   369  		if mode != fmtTypeID {
   370  			i := len(sym.Name)
   371  			for i > 0 && sym.Name[i-1] >= '0' && sym.Name[i-1] <= '9' {
   372  				i--
   373  			}
   374  			const dot = "·"
   375  			if i >= len(dot) && sym.Name[i-len(dot):i] == dot {
   376  				sym = &Sym{Pkg: sym.Pkg, Name: sym.Name[:i-len(dot)]}
   377  			}
   378  		}
   379  		sconv2(b, sym, verb, mode)
   380  
   381  		// TODO(mdempsky): Investigate including Vargen in fmtTypeIDName
   382  		// output too. It seems like it should, but that mode is currently
   383  		// used in string representation used by reflection, which is
   384  		// user-visible and doesn't expect this.
   385  		if mode == fmtTypeID && t.vargen != 0 {
   386  			fmt.Fprintf(b, "·%d", t.vargen)
   387  		}
   388  		return
   389  	}
   390  
   391  	if int(t.Kind()) < len(BasicTypeNames) && BasicTypeNames[t.Kind()] != "" {
   392  		var name string
   393  		switch t {
   394  		case UntypedBool:
   395  			name = "untyped bool"
   396  		case UntypedString:
   397  			name = "untyped string"
   398  		case UntypedInt:
   399  			name = "untyped int"
   400  		case UntypedRune:
   401  			name = "untyped rune"
   402  		case UntypedFloat:
   403  			name = "untyped float"
   404  		case UntypedComplex:
   405  			name = "untyped complex"
   406  		default:
   407  			name = BasicTypeNames[t.Kind()]
   408  		}
   409  		b.WriteString(name)
   410  		return
   411  	}
   412  
   413  	if mode == fmtDebug {
   414  		b.WriteString(t.Kind().String())
   415  		b.WriteByte('-')
   416  		tconv2(b, t, 'v', fmtGo, visited)
   417  		return
   418  	}
   419  
   420  	// At this point, we might call tconv2 recursively. Add the current type to the visited list so we don't
   421  	// try to print it recursively.
   422  	// We record the offset in the result buffer where the type's text starts. This offset serves as a reference
   423  	// point for any later references to the same type.
   424  	// Note that we remove the type from the visited map as soon as the recursive call is done.
   425  	// This prevents encoding types like map[*int]*int as map[*int]@4. (That encoding would work,
   426  	// but I'd like to use the @ notation only when strictly necessary.)
   427  	if visited == nil {
   428  		visited = map[*Type]int{}
   429  	}
   430  	visited[t] = b.Len()
   431  	defer delete(visited, t)
   432  
   433  	switch t.Kind() {
   434  	case TPTR:
   435  		b.WriteByte('*')
   436  		switch mode {
   437  		case fmtTypeID, fmtTypeIDName, fmtTypeIDHash:
   438  			if verb == 'S' {
   439  				tconv2(b, t.Elem(), 'S', mode, visited)
   440  				return
   441  			}
   442  		}
   443  		tconv2(b, t.Elem(), 'v', mode, visited)
   444  
   445  	case TARRAY:
   446  		b.WriteByte('[')
   447  		b.WriteString(strconv.FormatInt(t.NumElem(), 10))
   448  		b.WriteByte(']')
   449  		tconv2(b, t.Elem(), 0, mode, visited)
   450  
   451  	case TSLICE:
   452  		b.WriteString("[]")
   453  		tconv2(b, t.Elem(), 0, mode, visited)
   454  
   455  	case TCHAN:
   456  		switch t.ChanDir() {
   457  		case Crecv:
   458  			b.WriteString("<-chan ")
   459  			tconv2(b, t.Elem(), 0, mode, visited)
   460  		case Csend:
   461  			b.WriteString("chan<- ")
   462  			tconv2(b, t.Elem(), 0, mode, visited)
   463  		default:
   464  			b.WriteString("chan ")
   465  			if t.Elem() != nil && t.Elem().IsChan() && t.Elem().Sym() == nil && t.Elem().ChanDir() == Crecv {
   466  				b.WriteByte('(')
   467  				tconv2(b, t.Elem(), 0, mode, visited)
   468  				b.WriteByte(')')
   469  			} else {
   470  				tconv2(b, t.Elem(), 0, mode, visited)
   471  			}
   472  		}
   473  
   474  	case TMAP:
   475  		b.WriteString("map[")
   476  		tconv2(b, t.Key(), 0, mode, visited)
   477  		b.WriteByte(']')
   478  		tconv2(b, t.Elem(), 0, mode, visited)
   479  
   480  	case TINTER:
   481  		if t.IsEmptyInterface() {
   482  			b.WriteString("interface {}")
   483  			break
   484  		}
   485  		b.WriteString("interface {")
   486  		for i, f := range t.AllMethods().Slice() {
   487  			if i != 0 {
   488  				b.WriteByte(';')
   489  			}
   490  			b.WriteByte(' ')
   491  			switch {
   492  			case f.Sym == nil:
   493  				// Check first that a symbol is defined for this type.
   494  				// Wrong interface definitions may have types lacking a symbol.
   495  				break
   496  			case IsExported(f.Sym.Name):
   497  				sconv2(b, f.Sym, 'S', mode)
   498  			default:
   499  				if mode != fmtTypeIDName && mode != fmtTypeIDHash {
   500  					mode = fmtTypeID
   501  				}
   502  				sconv2(b, f.Sym, 'v', mode)
   503  			}
   504  			tconv2(b, f.Type, 'S', mode, visited)
   505  		}
   506  		if t.AllMethods().Len() != 0 {
   507  			b.WriteByte(' ')
   508  		}
   509  		b.WriteByte('}')
   510  
   511  	case TFUNC:
   512  		if verb == 'S' {
   513  			// no leading func
   514  		} else {
   515  			if t.Recv() != nil {
   516  				b.WriteString("method")
   517  				tconv2(b, t.Recvs(), 0, mode, visited)
   518  				b.WriteByte(' ')
   519  			}
   520  			b.WriteString("func")
   521  		}
   522  		if t.NumTParams() > 0 {
   523  			tconv2(b, t.TParams(), 0, mode, visited)
   524  		}
   525  		tconv2(b, t.Params(), 0, mode, visited)
   526  
   527  		switch t.NumResults() {
   528  		case 0:
   529  			// nothing to do
   530  
   531  		case 1:
   532  			b.WriteByte(' ')
   533  			tconv2(b, t.Results().Field(0).Type, 0, mode, visited) // struct->field->field's type
   534  
   535  		default:
   536  			b.WriteByte(' ')
   537  			tconv2(b, t.Results(), 0, mode, visited)
   538  		}
   539  
   540  	case TSTRUCT:
   541  		if m := t.StructType().Map; m != nil {
   542  			mt := m.MapType()
   543  			// Format the bucket struct for map[x]y as map.bucket[x]y.
   544  			// This avoids a recursive print that generates very long names.
   545  			switch t {
   546  			case mt.Bucket:
   547  				b.WriteString("map.bucket[")
   548  			case mt.Hmap:
   549  				b.WriteString("map.hdr[")
   550  			case mt.Hiter:
   551  				b.WriteString("map.iter[")
   552  			default:
   553  				base.Fatalf("unknown internal map type")
   554  			}
   555  			tconv2(b, m.Key(), 0, mode, visited)
   556  			b.WriteByte(']')
   557  			tconv2(b, m.Elem(), 0, mode, visited)
   558  			break
   559  		}
   560  
   561  		if funarg := t.StructType().Funarg; funarg != FunargNone {
   562  			open, close := '(', ')'
   563  			if funarg == FunargTparams {
   564  				open, close = '[', ']'
   565  			}
   566  			b.WriteByte(byte(open))
   567  			fieldVerb := 'v'
   568  			switch mode {
   569  			case fmtTypeID, fmtTypeIDName, fmtTypeIDHash, fmtGo:
   570  				// no argument names on function signature, and no "noescape"/"nosplit" tags
   571  				fieldVerb = 'S'
   572  			}
   573  			for i, f := range t.Fields().Slice() {
   574  				if i != 0 {
   575  					b.WriteString(", ")
   576  				}
   577  				fldconv(b, f, fieldVerb, mode, visited, funarg)
   578  			}
   579  			b.WriteByte(byte(close))
   580  		} else {
   581  			b.WriteString("struct {")
   582  			for i, f := range t.Fields().Slice() {
   583  				if i != 0 {
   584  					b.WriteByte(';')
   585  				}
   586  				b.WriteByte(' ')
   587  				fldconv(b, f, 'L', mode, visited, funarg)
   588  			}
   589  			if t.NumFields() != 0 {
   590  				b.WriteByte(' ')
   591  			}
   592  			b.WriteByte('}')
   593  		}
   594  
   595  	case TFORW:
   596  		b.WriteString("undefined")
   597  		if t.Sym() != nil {
   598  			b.WriteByte(' ')
   599  			sconv2(b, t.Sym(), 'v', mode)
   600  		}
   601  
   602  	case TUNSAFEPTR:
   603  		b.WriteString("unsafe.Pointer")
   604  
   605  	case TTYPEPARAM:
   606  		if t.Sym() != nil {
   607  			sconv2(b, t.Sym(), 'v', mode)
   608  		} else {
   609  			b.WriteString("tp")
   610  			// Print out the pointer value for now to disambiguate type params
   611  			b.WriteString(fmt.Sprintf("%p", t))
   612  		}
   613  
   614  	case TUNION:
   615  		for i := 0; i < t.NumTerms(); i++ {
   616  			if i > 0 {
   617  				b.WriteString("|")
   618  			}
   619  			elem, tilde := t.Term(i)
   620  			if tilde {
   621  				b.WriteString("~")
   622  			}
   623  			tconv2(b, elem, 0, mode, visited)
   624  		}
   625  
   626  	case Txxx:
   627  		b.WriteString("Txxx")
   628  
   629  	default:
   630  		// Don't know how to handle - fall back to detailed prints
   631  		b.WriteString(t.Kind().String())
   632  		b.WriteString(" <")
   633  		sconv2(b, t.Sym(), 'v', mode)
   634  		b.WriteString(">")
   635  
   636  	}
   637  }
   638  
   639  func fldconv(b *bytes.Buffer, f *Field, verb rune, mode fmtMode, visited map[*Type]int, funarg Funarg) {
   640  	if f == nil {
   641  		b.WriteString("<T>")
   642  		return
   643  	}
   644  
   645  	var name string
   646  	nameSep := " "
   647  	if verb != 'S' {
   648  		s := f.Sym
   649  
   650  		// Take the name from the original.
   651  		if mode == fmtGo {
   652  			s = OrigSym(s)
   653  		}
   654  
   655  		// Using type aliases and embedded fields, it's possible to
   656  		// construct types that can't be directly represented as a
   657  		// type literal. For example, given "type Int = int" (#50190),
   658  		// it would be incorrect to format "struct{ Int }" as either
   659  		// "struct{ int }" or "struct{ Int int }", because those each
   660  		// represent other, distinct types.
   661  		//
   662  		// So for the purpose of LinkString (i.e., fmtTypeID), we use
   663  		// the non-standard syntax "struct{ Int = int }" to represent
   664  		// embedded fields that have been renamed through the use of
   665  		// type aliases.
   666  		if f.Embedded != 0 {
   667  			if mode == fmtTypeID {
   668  				nameSep = " = "
   669  
   670  				// Compute tsym, the symbol that would normally be used as
   671  				// the field name when embedding f.Type.
   672  				// TODO(mdempsky): Check for other occurrences of this logic
   673  				// and deduplicate.
   674  				typ := f.Type
   675  				if typ.IsPtr() {
   676  					base.Assertf(typ.Sym() == nil, "embedded pointer type has name: %L", typ)
   677  					typ = typ.Elem()
   678  				}
   679  				tsym := typ.Sym()
   680  
   681  				// If the field name matches the embedded type's name, then
   682  				// suppress printing of the field name. For example, format
   683  				// "struct{ T }" as simply that instead of "struct{ T = T }".
   684  				if tsym != nil && (s == tsym || IsExported(tsym.Name) && s.Name == tsym.Name) {
   685  					s = nil
   686  				}
   687  			} else {
   688  				// Suppress the field name for embedded fields for
   689  				// non-LinkString formats, to match historical behavior.
   690  				// TODO(mdempsky): Re-evaluate this.
   691  				s = nil
   692  			}
   693  		}
   694  
   695  		if s != nil {
   696  			if funarg != FunargNone {
   697  				name = fmt.Sprint(f.Nname)
   698  			} else if verb == 'L' {
   699  				name = s.Name
   700  				if name == ".F" {
   701  					name = "F" // Hack for toolstash -cmp.
   702  				}
   703  				if !IsExported(name) && mode != fmtTypeIDName && mode != fmtTypeIDHash {
   704  					name = sconv(s, 0, mode) // qualify non-exported names (used on structs, not on funarg)
   705  				}
   706  			} else {
   707  				name = sconv(s, 0, mode)
   708  			}
   709  		}
   710  	}
   711  
   712  	if name != "" {
   713  		b.WriteString(name)
   714  		b.WriteString(nameSep)
   715  	}
   716  
   717  	if f.IsDDD() {
   718  		var et *Type
   719  		if f.Type != nil {
   720  			et = f.Type.Elem()
   721  		}
   722  		b.WriteString("...")
   723  		tconv2(b, et, 0, mode, visited)
   724  	} else {
   725  		tconv2(b, f.Type, 0, mode, visited)
   726  	}
   727  
   728  	if verb != 'S' && funarg == FunargNone && f.Note != "" {
   729  		b.WriteString(" ")
   730  		b.WriteString(strconv.Quote(f.Note))
   731  	}
   732  }
   733  
   734  // Val
   735  
   736  func FmtConst(v constant.Value, sharp bool) string {
   737  	if !sharp && v.Kind() == constant.Complex {
   738  		real, imag := constant.Real(v), constant.Imag(v)
   739  
   740  		var re string
   741  		sre := constant.Sign(real)
   742  		if sre != 0 {
   743  			re = real.String()
   744  		}
   745  
   746  		var im string
   747  		sim := constant.Sign(imag)
   748  		if sim != 0 {
   749  			im = imag.String()
   750  		}
   751  
   752  		switch {
   753  		case sre == 0 && sim == 0:
   754  			return "0"
   755  		case sre == 0:
   756  			return im + "i"
   757  		case sim == 0:
   758  			return re
   759  		case sim < 0:
   760  			return fmt.Sprintf("(%s%si)", re, im)
   761  		default:
   762  			return fmt.Sprintf("(%s+%si)", re, im)
   763  		}
   764  	}
   765  
   766  	return v.String()
   767  }
   768  
   769  // TypeHash computes a hash value for type t to use in type switch statements.
   770  func TypeHash(t *Type) uint32 {
   771  	p := tconv(t, 0, fmtTypeIDHash)
   772  
   773  	// Using MD5 is overkill, but reduces accidental collisions.
   774  	h := md5.Sum([]byte(p))
   775  	return binary.LittleEndian.Uint32(h[:4])
   776  }
   777  

View as plain text