golang.org/x/tools@v0.21.0/cmd/godex/print.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  package main
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"go/constant"
    11  	"go/token"
    12  	"go/types"
    13  	"io"
    14  	"math/big"
    15  
    16  	"golang.org/x/tools/internal/aliases"
    17  )
    18  
    19  // TODO(gri) use tabwriter for alignment?
    20  
    21  func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) {
    22  	var p printer
    23  	p.pkg = pkg
    24  	p.printPackage(pkg, filter)
    25  	p.printGccgoExtra(pkg)
    26  	io.Copy(w, &p.buf)
    27  }
    28  
    29  type printer struct {
    30  	pkg    *types.Package
    31  	buf    bytes.Buffer
    32  	indent int  // current indentation level
    33  	last   byte // last byte written
    34  }
    35  
    36  func (p *printer) print(s string) {
    37  	// Write the string one byte at a time. We care about the presence of
    38  	// newlines for indentation which we will see even in the presence of
    39  	// (non-corrupted) Unicode; no need to read one rune at a time.
    40  	for i := 0; i < len(s); i++ {
    41  		ch := s[i]
    42  		if ch != '\n' && p.last == '\n' {
    43  			// Note: This could lead to a range overflow for very large
    44  			// indentations, but it's extremely unlikely to happen for
    45  			// non-pathological code.
    46  			p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent])
    47  		}
    48  		p.buf.WriteByte(ch)
    49  		p.last = ch
    50  	}
    51  }
    52  
    53  func (p *printer) printf(format string, args ...interface{}) {
    54  	p.print(fmt.Sprintf(format, args...))
    55  }
    56  
    57  // methodsFor returns the named type and corresponding methods if the type
    58  // denoted by obj is not an interface and has methods. Otherwise it returns
    59  // the zero value.
    60  func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) {
    61  	named, _ := aliases.Unalias(obj.Type()).(*types.Named)
    62  	if named == nil {
    63  		// A type name's type can also be the
    64  		// exported basic type unsafe.Pointer.
    65  		return nil, nil
    66  	}
    67  	if _, ok := named.Underlying().(*types.Interface); ok {
    68  		// ignore interfaces
    69  		return nil, nil
    70  	}
    71  	methods := combinedMethodSet(named)
    72  	if len(methods) == 0 {
    73  		return nil, nil
    74  	}
    75  	return named, methods
    76  }
    77  
    78  func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) {
    79  	// collect objects by kind
    80  	var (
    81  		consts   []*types.Const
    82  		typem    []*types.Named    // non-interface types with methods
    83  		typez    []*types.TypeName // interfaces or types without methods
    84  		vars     []*types.Var
    85  		funcs    []*types.Func
    86  		builtins []*types.Builtin
    87  		methods  = make(map[*types.Named][]*types.Selection) // method sets for named types
    88  	)
    89  	scope := pkg.Scope()
    90  	for _, name := range scope.Names() {
    91  		obj := scope.Lookup(name)
    92  		if obj.Exported() {
    93  			// collect top-level exported and possibly filtered objects
    94  			if filter == nil || filter(obj) {
    95  				switch obj := obj.(type) {
    96  				case *types.Const:
    97  					consts = append(consts, obj)
    98  				case *types.TypeName:
    99  					// group into types with methods and types without
   100  					if named, m := methodsFor(obj); named != nil {
   101  						typem = append(typem, named)
   102  						methods[named] = m
   103  					} else {
   104  						typez = append(typez, obj)
   105  					}
   106  				case *types.Var:
   107  					vars = append(vars, obj)
   108  				case *types.Func:
   109  					funcs = append(funcs, obj)
   110  				case *types.Builtin:
   111  					// for unsafe.Sizeof, etc.
   112  					builtins = append(builtins, obj)
   113  				}
   114  			}
   115  		} else if filter == nil {
   116  			// no filtering: collect top-level unexported types with methods
   117  			if obj, _ := obj.(*types.TypeName); obj != nil {
   118  				// see case *types.TypeName above
   119  				if named, m := methodsFor(obj); named != nil {
   120  					typem = append(typem, named)
   121  					methods[named] = m
   122  				}
   123  			}
   124  		}
   125  	}
   126  
   127  	p.printf("package %s  // %q\n", pkg.Name(), pkg.Path())
   128  
   129  	p.printDecl("const", len(consts), func() {
   130  		for _, obj := range consts {
   131  			p.printObj(obj)
   132  			p.print("\n")
   133  		}
   134  	})
   135  
   136  	p.printDecl("var", len(vars), func() {
   137  		for _, obj := range vars {
   138  			p.printObj(obj)
   139  			p.print("\n")
   140  		}
   141  	})
   142  
   143  	p.printDecl("type", len(typez), func() {
   144  		for _, obj := range typez {
   145  			p.printf("%s ", obj.Name())
   146  			typ := obj.Type()
   147  			if isAlias(obj) {
   148  				p.print("= ")
   149  				p.writeType(p.pkg, typ)
   150  			} else {
   151  				p.writeType(p.pkg, typ.Underlying())
   152  			}
   153  			p.print("\n")
   154  		}
   155  	})
   156  
   157  	// non-interface types with methods
   158  	for _, named := range typem {
   159  		first := true
   160  		if obj := named.Obj(); obj.Exported() {
   161  			if first {
   162  				p.print("\n")
   163  				first = false
   164  			}
   165  			p.printf("type %s ", obj.Name())
   166  			p.writeType(p.pkg, named.Underlying())
   167  			p.print("\n")
   168  		}
   169  		for _, m := range methods[named] {
   170  			if obj := m.Obj(); obj.Exported() {
   171  				if first {
   172  					p.print("\n")
   173  					first = false
   174  				}
   175  				p.printFunc(m.Recv(), obj.(*types.Func))
   176  				p.print("\n")
   177  			}
   178  		}
   179  	}
   180  
   181  	if len(funcs) > 0 {
   182  		p.print("\n")
   183  		for _, obj := range funcs {
   184  			p.printFunc(nil, obj)
   185  			p.print("\n")
   186  		}
   187  	}
   188  
   189  	// TODO(gri) better handling of builtins (package unsafe only)
   190  	if len(builtins) > 0 {
   191  		p.print("\n")
   192  		for _, obj := range builtins {
   193  			p.printf("func %s() // builtin\n", obj.Name())
   194  		}
   195  	}
   196  
   197  	p.print("\n")
   198  }
   199  
   200  func (p *printer) printDecl(keyword string, n int, printGroup func()) {
   201  	switch n {
   202  	case 0:
   203  		// nothing to do
   204  	case 1:
   205  		p.printf("\n%s ", keyword)
   206  		printGroup()
   207  	default:
   208  		p.printf("\n%s (\n", keyword)
   209  		p.indent++
   210  		printGroup()
   211  		p.indent--
   212  		p.print(")\n")
   213  	}
   214  }
   215  
   216  // absInt returns the absolute value of v as a *big.Int.
   217  // v must be a numeric value.
   218  func absInt(v constant.Value) *big.Int {
   219  	// compute big-endian representation of v
   220  	b := constant.Bytes(v) // little-endian
   221  	for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
   222  		b[i], b[j] = b[j], b[i]
   223  	}
   224  	return new(big.Int).SetBytes(b)
   225  }
   226  
   227  var (
   228  	one = big.NewRat(1, 1)
   229  	ten = big.NewRat(10, 1)
   230  )
   231  
   232  // floatString returns the string representation for a
   233  // numeric value v in normalized floating-point format.
   234  func floatString(v constant.Value) string {
   235  	if constant.Sign(v) == 0 {
   236  		return "0.0"
   237  	}
   238  	// x != 0
   239  
   240  	// convert |v| into a big.Rat x
   241  	x := new(big.Rat).SetFrac(absInt(constant.Num(v)), absInt(constant.Denom(v)))
   242  
   243  	// normalize x and determine exponent e
   244  	// (This is not very efficient, but also not speed-critical.)
   245  	var e int
   246  	for x.Cmp(ten) >= 0 {
   247  		x.Quo(x, ten)
   248  		e++
   249  	}
   250  	for x.Cmp(one) < 0 {
   251  		x.Mul(x, ten)
   252  		e--
   253  	}
   254  
   255  	// TODO(gri) Values such as 1/2 are easier to read in form 0.5
   256  	// rather than 5.0e-1. Similarly, 1.0e1 is easier to read as
   257  	// 10.0. Fine-tune best exponent range for readability.
   258  
   259  	s := x.FloatString(100) // good-enough precision
   260  
   261  	// trim trailing 0's
   262  	i := len(s)
   263  	for i > 0 && s[i-1] == '0' {
   264  		i--
   265  	}
   266  	s = s[:i]
   267  
   268  	// add a 0 if the number ends in decimal point
   269  	if len(s) > 0 && s[len(s)-1] == '.' {
   270  		s += "0"
   271  	}
   272  
   273  	// add exponent and sign
   274  	if e != 0 {
   275  		s += fmt.Sprintf("e%+d", e)
   276  	}
   277  	if constant.Sign(v) < 0 {
   278  		s = "-" + s
   279  	}
   280  
   281  	// TODO(gri) If v is a "small" fraction (i.e., numerator and denominator
   282  	// are just a small number of decimal digits), add the exact fraction as
   283  	// a comment. For instance: 3.3333...e-1 /* = 1/3 */
   284  
   285  	return s
   286  }
   287  
   288  // valString returns the string representation for the value v.
   289  // Setting floatFmt forces an integer value to be formatted in
   290  // normalized floating-point format.
   291  // TODO(gri) Move this code into package constant.
   292  func valString(v constant.Value, floatFmt bool) string {
   293  	switch v.Kind() {
   294  	case constant.Int:
   295  		if floatFmt {
   296  			return floatString(v)
   297  		}
   298  	case constant.Float:
   299  		return floatString(v)
   300  	case constant.Complex:
   301  		re := constant.Real(v)
   302  		im := constant.Imag(v)
   303  		var s string
   304  		if constant.Sign(re) != 0 {
   305  			s = floatString(re)
   306  			if constant.Sign(im) >= 0 {
   307  				s += " + "
   308  			} else {
   309  				s += " - "
   310  				im = constant.UnaryOp(token.SUB, im, 0) // negate im
   311  			}
   312  		}
   313  		// im != 0, otherwise v would be constant.Int or constant.Float
   314  		return s + floatString(im) + "i"
   315  	}
   316  	return v.String()
   317  }
   318  
   319  func (p *printer) printObj(obj types.Object) {
   320  	p.print(obj.Name())
   321  
   322  	typ, basic := obj.Type().Underlying().(*types.Basic)
   323  	if basic && typ.Info()&types.IsUntyped != 0 {
   324  		// don't write untyped types
   325  	} else {
   326  		p.print(" ")
   327  		p.writeType(p.pkg, obj.Type())
   328  	}
   329  
   330  	if obj, ok := obj.(*types.Const); ok {
   331  		floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0
   332  		p.print(" = ")
   333  		p.print(valString(obj.Val(), floatFmt))
   334  	}
   335  }
   336  
   337  func (p *printer) printFunc(recvType types.Type, obj *types.Func) {
   338  	p.print("func ")
   339  	sig := obj.Type().(*types.Signature)
   340  	if recvType != nil {
   341  		p.print("(")
   342  		p.writeType(p.pkg, recvType)
   343  		p.print(") ")
   344  	}
   345  	p.print(obj.Name())
   346  	p.writeSignature(p.pkg, sig)
   347  }
   348  
   349  // combinedMethodSet returns the method set for a named type T
   350  // merged with all the methods of *T that have different names than
   351  // the methods of T.
   352  //
   353  // combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet
   354  // but doesn't require a MethodSetCache.
   355  // TODO(gri) If this functionality doesn't change over time, consider
   356  // just calling IntuitiveMethodSet eventually.
   357  func combinedMethodSet(T *types.Named) []*types.Selection {
   358  	// method set for T
   359  	mset := types.NewMethodSet(T)
   360  	var res []*types.Selection
   361  	for i, n := 0, mset.Len(); i < n; i++ {
   362  		res = append(res, mset.At(i))
   363  	}
   364  
   365  	// add all *T methods with names different from T methods
   366  	pmset := types.NewMethodSet(types.NewPointer(T))
   367  	for i, n := 0, pmset.Len(); i < n; i++ {
   368  		pm := pmset.At(i)
   369  		if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil {
   370  			res = append(res, pm)
   371  		}
   372  	}
   373  
   374  	return res
   375  }