golang.org/x/tools@v0.21.0/cmd/godex/writetype.go (about)

     1  // Copyright 2014 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  // This file implements writing of types. The functionality is lifted
     6  // directly from go/types, but now contains various modifications for
     7  // nicer output.
     8  //
     9  // TODO(gri) back-port once we have a fixed interface and once the
    10  // go/types API is not frozen anymore for the 1.3 release; and remove
    11  // this implementation if possible.
    12  
    13  package main
    14  
    15  import (
    16  	"go/types"
    17  
    18  	"golang.org/x/tools/internal/aliases"
    19  )
    20  
    21  func (p *printer) writeType(this *types.Package, typ types.Type) {
    22  	p.writeTypeInternal(this, typ, make([]types.Type, 8))
    23  }
    24  
    25  // From go/types - leave for now to ease back-porting this code.
    26  const GcCompatibilityMode = false
    27  
    28  func (p *printer) writeTypeInternal(this *types.Package, typ types.Type, visited []types.Type) {
    29  	// Theoretically, this is a quadratic lookup algorithm, but in
    30  	// practice deeply nested composite types with unnamed component
    31  	// types are uncommon. This code is likely more efficient than
    32  	// using a map.
    33  	for _, t := range visited {
    34  		if t == typ {
    35  			p.printf("○%T", typ) // cycle to typ
    36  			return
    37  		}
    38  	}
    39  	visited = append(visited, typ)
    40  
    41  	switch t := typ.(type) {
    42  	case nil:
    43  		p.print("<nil>")
    44  
    45  	case *types.Basic:
    46  		if t.Kind() == types.UnsafePointer {
    47  			p.print("unsafe.")
    48  		}
    49  		if GcCompatibilityMode {
    50  			// forget the alias names
    51  			switch t.Kind() {
    52  			case types.Byte:
    53  				t = types.Typ[types.Uint8]
    54  			case types.Rune:
    55  				t = types.Typ[types.Int32]
    56  			}
    57  		}
    58  		p.print(t.Name())
    59  
    60  	case *types.Array:
    61  		p.printf("[%d]", t.Len())
    62  		p.writeTypeInternal(this, t.Elem(), visited)
    63  
    64  	case *types.Slice:
    65  		p.print("[]")
    66  		p.writeTypeInternal(this, t.Elem(), visited)
    67  
    68  	case *types.Struct:
    69  		n := t.NumFields()
    70  		if n == 0 {
    71  			p.print("struct{}")
    72  			return
    73  		}
    74  
    75  		p.print("struct {\n")
    76  		p.indent++
    77  		for i := 0; i < n; i++ {
    78  			f := t.Field(i)
    79  			if !f.Anonymous() {
    80  				p.printf("%s ", f.Name())
    81  			}
    82  			p.writeTypeInternal(this, f.Type(), visited)
    83  			if tag := t.Tag(i); tag != "" {
    84  				p.printf(" %q", tag)
    85  			}
    86  			p.print("\n")
    87  		}
    88  		p.indent--
    89  		p.print("}")
    90  
    91  	case *types.Pointer:
    92  		p.print("*")
    93  		p.writeTypeInternal(this, t.Elem(), visited)
    94  
    95  	case *types.Tuple:
    96  		p.writeTuple(this, t, false, visited)
    97  
    98  	case *types.Signature:
    99  		p.print("func")
   100  		p.writeSignatureInternal(this, t, visited)
   101  
   102  	case *types.Interface:
   103  		// We write the source-level methods and embedded types rather
   104  		// than the actual method set since resolved method signatures
   105  		// may have non-printable cycles if parameters have anonymous
   106  		// interface types that (directly or indirectly) embed the
   107  		// current interface. For instance, consider the result type
   108  		// of m:
   109  		//
   110  		//     type T interface{
   111  		//         m() interface{ T }
   112  		//     }
   113  		//
   114  		n := t.NumMethods()
   115  		if n == 0 {
   116  			p.print("interface{}")
   117  			return
   118  		}
   119  
   120  		p.print("interface {\n")
   121  		p.indent++
   122  		if GcCompatibilityMode {
   123  			// print flattened interface
   124  			// (useful to compare against gc-generated interfaces)
   125  			for i := 0; i < n; i++ {
   126  				m := t.Method(i)
   127  				p.print(m.Name())
   128  				p.writeSignatureInternal(this, m.Type().(*types.Signature), visited)
   129  				p.print("\n")
   130  			}
   131  		} else {
   132  			// print explicit interface methods and embedded types
   133  			for i, n := 0, t.NumExplicitMethods(); i < n; i++ {
   134  				m := t.ExplicitMethod(i)
   135  				p.print(m.Name())
   136  				p.writeSignatureInternal(this, m.Type().(*types.Signature), visited)
   137  				p.print("\n")
   138  			}
   139  			for i, n := 0, t.NumEmbeddeds(); i < n; i++ {
   140  				typ := t.EmbeddedType(i)
   141  				p.writeTypeInternal(this, typ, visited)
   142  				p.print("\n")
   143  			}
   144  		}
   145  		p.indent--
   146  		p.print("}")
   147  
   148  	case *types.Map:
   149  		p.print("map[")
   150  		p.writeTypeInternal(this, t.Key(), visited)
   151  		p.print("]")
   152  		p.writeTypeInternal(this, t.Elem(), visited)
   153  
   154  	case *types.Chan:
   155  		var s string
   156  		var parens bool
   157  		switch t.Dir() {
   158  		case types.SendRecv:
   159  			s = "chan "
   160  			// chan (<-chan T) requires parentheses
   161  			if c, _ := t.Elem().(*types.Chan); c != nil && c.Dir() == types.RecvOnly {
   162  				parens = true
   163  			}
   164  		case types.SendOnly:
   165  			s = "chan<- "
   166  		case types.RecvOnly:
   167  			s = "<-chan "
   168  		default:
   169  			panic("unreachable")
   170  		}
   171  		p.print(s)
   172  		if parens {
   173  			p.print("(")
   174  		}
   175  		p.writeTypeInternal(this, t.Elem(), visited)
   176  		if parens {
   177  			p.print(")")
   178  		}
   179  
   180  	case *aliases.Alias:
   181  		// TODO(adonovan): display something aliasy.
   182  		p.writeTypeInternal(this, aliases.Unalias(t), visited)
   183  
   184  	case *types.Named:
   185  		s := "<Named w/o object>"
   186  		if obj := t.Obj(); obj != nil {
   187  			if pkg := obj.Pkg(); pkg != nil {
   188  				if pkg != this {
   189  					p.print(pkg.Path())
   190  					p.print(".")
   191  				}
   192  				// TODO(gri): function-local named types should be displayed
   193  				// differently from named types at package level to avoid
   194  				// ambiguity.
   195  			}
   196  			s = obj.Name()
   197  		}
   198  		p.print(s)
   199  
   200  	default:
   201  		// For externally defined implementations of Type.
   202  		p.print(t.String())
   203  	}
   204  }
   205  
   206  func (p *printer) writeTuple(this *types.Package, tup *types.Tuple, variadic bool, visited []types.Type) {
   207  	p.print("(")
   208  	for i, n := 0, tup.Len(); i < n; i++ {
   209  		if i > 0 {
   210  			p.print(", ")
   211  		}
   212  		v := tup.At(i)
   213  		if name := v.Name(); name != "" {
   214  			p.print(name)
   215  			p.print(" ")
   216  		}
   217  		typ := v.Type()
   218  		if variadic && i == n-1 {
   219  			p.print("...")
   220  			typ = typ.(*types.Slice).Elem()
   221  		}
   222  		p.writeTypeInternal(this, typ, visited)
   223  	}
   224  	p.print(")")
   225  }
   226  
   227  func (p *printer) writeSignature(this *types.Package, sig *types.Signature) {
   228  	p.writeSignatureInternal(this, sig, make([]types.Type, 8))
   229  }
   230  
   231  func (p *printer) writeSignatureInternal(this *types.Package, sig *types.Signature, visited []types.Type) {
   232  	p.writeTuple(this, sig.Params(), sig.Variadic(), visited)
   233  
   234  	res := sig.Results()
   235  	n := res.Len()
   236  	if n == 0 {
   237  		// no result
   238  		return
   239  	}
   240  
   241  	p.print(" ")
   242  	if n == 1 && res.At(0).Name() == "" {
   243  		// single unnamed result
   244  		p.writeTypeInternal(this, res.At(0).Type(), visited)
   245  		return
   246  	}
   247  
   248  	// multiple or named result(s)
   249  	p.writeTuple(this, res, false, visited)
   250  }