github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/cmd/guru/describe.go (about)

     1  // Copyright 2013 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/ast"
    11  	"go/constant"
    12  	"go/token"
    13  	"go/types"
    14  	"os"
    15  	"strings"
    16  	"unicode/utf8"
    17  
    18  	"golang.org/x/tools/cmd/guru/serial"
    19  	"golang.org/x/tools/go/ast/astutil"
    20  	"golang.org/x/tools/go/loader"
    21  	"golang.org/x/tools/go/types/typeutil"
    22  )
    23  
    24  // describe describes the syntax node denoted by the query position,
    25  // including:
    26  // - its syntactic category
    27  // - the definition of its referent (for identifiers) [now redundant]
    28  // - its type, fields, and methods (for an expression or type expression)
    29  func describe(q *Query) error {
    30  	lconf := loader.Config{Build: q.Build}
    31  	allowErrors(&lconf)
    32  
    33  	if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
    34  		return err
    35  	}
    36  
    37  	// Load/parse/type-check the program.
    38  	lprog, err := lconf.Load()
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	qpos, err := parseQueryPos(lprog, q.Pos, true) // (need exact pos)
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	if false { // debugging
    49  		fprintf(os.Stderr, lprog.Fset, qpos.path[0], "you selected: %s %s",
    50  			astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path))
    51  	}
    52  
    53  	var qr QueryResult
    54  	path, action := findInterestingNode(qpos.info, qpos.path)
    55  	switch action {
    56  	case actionExpr:
    57  		qr, err = describeValue(qpos, path)
    58  
    59  	case actionType:
    60  		qr, err = describeType(qpos, path)
    61  
    62  	case actionPackage:
    63  		qr, err = describePackage(qpos, path)
    64  
    65  	case actionStmt:
    66  		qr, err = describeStmt(qpos, path)
    67  
    68  	case actionUnknown:
    69  		qr = &describeUnknownResult{path[0]}
    70  
    71  	default:
    72  		panic(action) // unreachable
    73  	}
    74  	if err != nil {
    75  		return err
    76  	}
    77  	q.Output(lprog.Fset, qr)
    78  	return nil
    79  }
    80  
    81  type describeUnknownResult struct {
    82  	node ast.Node
    83  }
    84  
    85  func (r *describeUnknownResult) PrintPlain(printf printfFunc) {
    86  	// Nothing much to say about misc syntax.
    87  	printf(r.node, "%s", astutil.NodeDescription(r.node))
    88  }
    89  
    90  func (r *describeUnknownResult) JSON(fset *token.FileSet) []byte {
    91  	return toJSON(&serial.Describe{
    92  		Desc: astutil.NodeDescription(r.node),
    93  		Pos:  fset.Position(r.node.Pos()).String(),
    94  	})
    95  }
    96  
    97  type action int
    98  
    99  const (
   100  	actionUnknown action = iota // None of the below
   101  	actionExpr                  // FuncDecl, true Expr or Ident(types.{Const,Var})
   102  	actionType                  // type Expr or Ident(types.TypeName).
   103  	actionStmt                  // Stmt or Ident(types.Label)
   104  	actionPackage               // Ident(types.Package) or ImportSpec
   105  )
   106  
   107  // findInterestingNode classifies the syntax node denoted by path as one of:
   108  //   - an expression, part of an expression or a reference to a constant
   109  //     or variable;
   110  //   - a type, part of a type, or a reference to a named type;
   111  //   - a statement, part of a statement, or a label referring to a statement;
   112  //   - part of a package declaration or import spec.
   113  //   - none of the above.
   114  //
   115  // and returns the most "interesting" associated node, which may be
   116  // the same node, an ancestor or a descendent.
   117  func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) {
   118  	// TODO(adonovan): integrate with go/types/stdlib_test.go and
   119  	// apply this to every AST node we can find to make sure it
   120  	// doesn't crash.
   121  
   122  	// TODO(adonovan): audit for ParenExpr safety, esp. since we
   123  	// traverse up and down.
   124  
   125  	// TODO(adonovan): if the users selects the "." in
   126  	// "fmt.Fprintf()", they'll get an ambiguous selection error;
   127  	// we won't even reach here.  Can we do better?
   128  
   129  	// TODO(adonovan): describing a field within 'type T struct {...}'
   130  	// describes the (anonymous) struct type and concludes "no methods".
   131  	// We should ascend to the enclosing type decl, if any.
   132  
   133  	for len(path) > 0 {
   134  		switch n := path[0].(type) {
   135  		case *ast.GenDecl:
   136  			if len(n.Specs) == 1 {
   137  				// Descend to sole {Import,Type,Value}Spec child.
   138  				path = append([]ast.Node{n.Specs[0]}, path...)
   139  				continue
   140  			}
   141  			return path, actionUnknown // uninteresting
   142  
   143  		case *ast.FuncDecl:
   144  			// Descend to function name.
   145  			path = append([]ast.Node{n.Name}, path...)
   146  			continue
   147  
   148  		case *ast.ImportSpec:
   149  			return path, actionPackage
   150  
   151  		case *ast.ValueSpec:
   152  			if len(n.Names) == 1 {
   153  				// Descend to sole Ident child.
   154  				path = append([]ast.Node{n.Names[0]}, path...)
   155  				continue
   156  			}
   157  			return path, actionUnknown // uninteresting
   158  
   159  		case *ast.TypeSpec:
   160  			// Descend to type name.
   161  			path = append([]ast.Node{n.Name}, path...)
   162  			continue
   163  
   164  		case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause:
   165  			return path, actionUnknown // uninteresting
   166  
   167  		case ast.Stmt:
   168  			return path, actionStmt
   169  
   170  		case *ast.ArrayType,
   171  			*ast.StructType,
   172  			*ast.FuncType,
   173  			*ast.InterfaceType,
   174  			*ast.MapType,
   175  			*ast.ChanType:
   176  			return path, actionType
   177  
   178  		case *ast.Ellipsis:
   179  			// Continue to enclosing node.
   180  			// e.g. [...]T in ArrayType
   181  			//      f(x...) in CallExpr
   182  			//      f(x...T) in FuncType
   183  
   184  		case *ast.Field:
   185  			// TODO(adonovan): this needs more thought,
   186  			// since fields can be so many things.
   187  			if len(n.Names) == 1 {
   188  				// Descend to sole Ident child.
   189  				path = append([]ast.Node{n.Names[0]}, path...)
   190  				continue
   191  			}
   192  			// Zero names (e.g. anon field in struct)
   193  			// or multiple field or param names:
   194  			// continue to enclosing field list.
   195  
   196  		case *ast.FieldList:
   197  			// Continue to enclosing node:
   198  			// {Struct,Func,Interface}Type or FuncDecl.
   199  
   200  		case *ast.BasicLit:
   201  			if _, ok := path[1].(*ast.ImportSpec); ok {
   202  				return path[1:], actionPackage
   203  			}
   204  			return path, actionExpr
   205  
   206  		case *ast.SelectorExpr:
   207  			// TODO(adonovan): use Selections info directly.
   208  			if pkginfo.Uses[n.Sel] == nil {
   209  				// TODO(adonovan): is this reachable?
   210  				return path, actionUnknown
   211  			}
   212  			// Descend to .Sel child.
   213  			path = append([]ast.Node{n.Sel}, path...)
   214  			continue
   215  
   216  		case *ast.Ident:
   217  			switch pkginfo.ObjectOf(n).(type) {
   218  			case *types.PkgName:
   219  				return path, actionPackage
   220  
   221  			case *types.Const:
   222  				return path, actionExpr
   223  
   224  			case *types.Label:
   225  				return path, actionStmt
   226  
   227  			case *types.TypeName:
   228  				return path, actionType
   229  
   230  			case *types.Var:
   231  				// For x in 'struct {x T}', return struct type, for now.
   232  				if _, ok := path[1].(*ast.Field); ok {
   233  					_ = path[2].(*ast.FieldList) // assertion
   234  					if _, ok := path[3].(*ast.StructType); ok {
   235  						return path[3:], actionType
   236  					}
   237  				}
   238  				return path, actionExpr
   239  
   240  			case *types.Func:
   241  				return path, actionExpr
   242  
   243  			case *types.Builtin:
   244  				// For reference to built-in function, return enclosing call.
   245  				path = path[1:] // ascend to enclosing function call
   246  				continue
   247  
   248  			case *types.Nil:
   249  				return path, actionExpr
   250  			}
   251  
   252  			// No object.
   253  			switch path[1].(type) {
   254  			case *ast.SelectorExpr:
   255  				// Return enclosing selector expression.
   256  				return path[1:], actionExpr
   257  
   258  			case *ast.Field:
   259  				// TODO(adonovan): test this.
   260  				// e.g. all f in:
   261  				//  struct { f, g int }
   262  				//  interface { f() }
   263  				//  func (f T) method(f, g int) (f, g bool)
   264  				//
   265  				// switch path[3].(type) {
   266  				// case *ast.FuncDecl:
   267  				// case *ast.StructType:
   268  				// case *ast.InterfaceType:
   269  				// }
   270  				//
   271  				// return path[1:], actionExpr
   272  				//
   273  				// Unclear what to do with these.
   274  				// Struct.Fields             -- field
   275  				// Interface.Methods         -- field
   276  				// FuncType.{Params.Results} -- actionExpr
   277  				// FuncDecl.Recv             -- actionExpr
   278  
   279  			case *ast.File:
   280  				// 'package foo'
   281  				return path, actionPackage
   282  
   283  			case *ast.ImportSpec:
   284  				return path[1:], actionPackage
   285  
   286  			default:
   287  				// e.g. blank identifier
   288  				// or y in "switch y := x.(type)"
   289  				// or code in a _test.go file that's not part of the package.
   290  				return path, actionUnknown
   291  			}
   292  
   293  		case *ast.StarExpr:
   294  			if pkginfo.Types[n].IsType() {
   295  				return path, actionType
   296  			}
   297  			return path, actionExpr
   298  
   299  		case ast.Expr:
   300  			// All Expr but {BasicLit,Ident,StarExpr} are
   301  			// "true" expressions that evaluate to a value.
   302  			return path, actionExpr
   303  		}
   304  
   305  		// Ascend to parent.
   306  		path = path[1:]
   307  	}
   308  
   309  	return nil, actionUnknown // unreachable
   310  }
   311  
   312  func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error) {
   313  	var expr ast.Expr
   314  	var obj types.Object
   315  	switch n := path[0].(type) {
   316  	case *ast.ValueSpec:
   317  		// ambiguous ValueSpec containing multiple names
   318  		return nil, fmt.Errorf("multiple value specification")
   319  	case *ast.Ident:
   320  		obj = qpos.info.ObjectOf(n)
   321  		expr = n
   322  	case ast.Expr:
   323  		expr = n
   324  	default:
   325  		// TODO(adonovan): is this reachable?
   326  		return nil, fmt.Errorf("unexpected AST for expr: %T", n)
   327  	}
   328  
   329  	typ := qpos.info.TypeOf(expr)
   330  	if typ == nil {
   331  		typ = types.Typ[types.Invalid]
   332  	}
   333  	constVal := qpos.info.Types[expr].Value
   334  	if c, ok := obj.(*types.Const); ok {
   335  		constVal = c.Val()
   336  	}
   337  
   338  	return &describeValueResult{
   339  		qpos:     qpos,
   340  		expr:     expr,
   341  		typ:      typ,
   342  		names:    appendNames(nil, typ),
   343  		constVal: constVal,
   344  		obj:      obj,
   345  		methods:  accessibleMethods(typ, qpos.info.Pkg),
   346  		fields:   accessibleFields(typ, qpos.info.Pkg),
   347  	}, nil
   348  }
   349  
   350  // appendNames returns named types found within the Type by
   351  // removing map, pointer, channel, slice, and array constructors.
   352  // It does not descend into structs or interfaces.
   353  func appendNames(names []*types.Named, typ types.Type) []*types.Named {
   354  	// elemType specifies type that has some element in it
   355  	// such as array, slice, chan, pointer
   356  	type elemType interface {
   357  		Elem() types.Type
   358  	}
   359  
   360  	switch t := typ.(type) {
   361  	case *types.Named:
   362  		names = append(names, t)
   363  	case *types.Map:
   364  		names = appendNames(names, t.Key())
   365  		names = appendNames(names, t.Elem())
   366  	case elemType:
   367  		names = appendNames(names, t.Elem())
   368  	}
   369  
   370  	return names
   371  }
   372  
   373  type describeValueResult struct {
   374  	qpos     *queryPos
   375  	expr     ast.Expr       // query node
   376  	typ      types.Type     // type of expression
   377  	names    []*types.Named // named types within typ
   378  	constVal constant.Value // value of expression, if constant
   379  	obj      types.Object   // var/func/const object, if expr was Ident
   380  	methods  []*types.Selection
   381  	fields   []describeField
   382  }
   383  
   384  func (r *describeValueResult) PrintPlain(printf printfFunc) {
   385  	var prefix, suffix string
   386  	if r.constVal != nil {
   387  		suffix = fmt.Sprintf(" of value %s", r.constVal)
   388  	}
   389  	switch obj := r.obj.(type) {
   390  	case *types.Func:
   391  		if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
   392  			if _, ok := recv.Type().Underlying().(*types.Interface); ok {
   393  				prefix = "interface method "
   394  			} else {
   395  				prefix = "method "
   396  			}
   397  		}
   398  	}
   399  
   400  	// Describe the expression.
   401  	if r.obj != nil {
   402  		if r.obj.Pos() == r.expr.Pos() {
   403  			// defining ident
   404  			printf(r.expr, "definition of %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
   405  		} else {
   406  			// referring ident
   407  			printf(r.expr, "reference to %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
   408  			if def := r.obj.Pos(); def != token.NoPos {
   409  				printf(def, "defined here")
   410  			}
   411  		}
   412  	} else {
   413  		desc := astutil.NodeDescription(r.expr)
   414  		if suffix != "" {
   415  			// constant expression
   416  			printf(r.expr, "%s%s", desc, suffix)
   417  		} else {
   418  			// non-constant expression
   419  			printf(r.expr, "%s of type %s", desc, r.qpos.typeString(r.typ))
   420  		}
   421  	}
   422  
   423  	printMethods(printf, r.expr, r.methods)
   424  	printFields(printf, r.expr, r.fields)
   425  	printNamedTypes(printf, r.expr, r.names)
   426  }
   427  
   428  func (r *describeValueResult) JSON(fset *token.FileSet) []byte {
   429  	var value, objpos string
   430  	if r.constVal != nil {
   431  		value = r.constVal.String()
   432  	}
   433  	if r.obj != nil {
   434  		objpos = fset.Position(r.obj.Pos()).String()
   435  	}
   436  
   437  	typesPos := make([]serial.Definition, len(r.names))
   438  	for i, t := range r.names {
   439  		typesPos[i] = serial.Definition{
   440  			ObjPos: fset.Position(t.Obj().Pos()).String(),
   441  			Desc:   r.qpos.typeString(t),
   442  		}
   443  	}
   444  
   445  	return toJSON(&serial.Describe{
   446  		Desc:   astutil.NodeDescription(r.expr),
   447  		Pos:    fset.Position(r.expr.Pos()).String(),
   448  		Detail: "value",
   449  		Value: &serial.DescribeValue{
   450  			Type:     r.qpos.typeString(r.typ),
   451  			TypesPos: typesPos,
   452  			Value:    value,
   453  			ObjPos:   objpos,
   454  		},
   455  	})
   456  }
   457  
   458  // ---- TYPE ------------------------------------------------------------
   459  
   460  func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) {
   461  	var description string
   462  	var typ types.Type
   463  	switch n := path[0].(type) {
   464  	case *ast.Ident:
   465  		obj := qpos.info.ObjectOf(n).(*types.TypeName)
   466  		typ = obj.Type()
   467  		if isAlias(obj) {
   468  			description = "alias of "
   469  		} else if obj.Pos() == n.Pos() {
   470  			description = "definition of " // (Named type)
   471  		} else if _, ok := typ.(*types.Basic); ok {
   472  			description = "reference to built-in "
   473  		} else {
   474  			description = "reference to " // (Named type)
   475  		}
   476  
   477  	case ast.Expr:
   478  		typ = qpos.info.TypeOf(n)
   479  
   480  	default:
   481  		// Unreachable?
   482  		return nil, fmt.Errorf("unexpected AST for type: %T", n)
   483  	}
   484  
   485  	description = description + "type " + qpos.typeString(typ)
   486  
   487  	// Show sizes for structs and named types (it's fairly obvious for others).
   488  	switch typ.(type) {
   489  	case *types.Named, *types.Struct:
   490  		szs := types.StdSizes{WordSize: 8, MaxAlign: 8} // assume amd64
   491  		description = fmt.Sprintf("%s (size %d, align %d)", description,
   492  			szs.Sizeof(typ), szs.Alignof(typ))
   493  	}
   494  
   495  	return &describeTypeResult{
   496  		qpos:        qpos,
   497  		node:        path[0],
   498  		description: description,
   499  		typ:         typ,
   500  		methods:     accessibleMethods(typ, qpos.info.Pkg),
   501  		fields:      accessibleFields(typ, qpos.info.Pkg),
   502  	}, nil
   503  }
   504  
   505  type describeTypeResult struct {
   506  	qpos        *queryPos
   507  	node        ast.Node
   508  	description string
   509  	typ         types.Type
   510  	methods     []*types.Selection
   511  	fields      []describeField
   512  }
   513  
   514  type describeField struct {
   515  	implicits []*types.Named
   516  	field     *types.Var
   517  }
   518  
   519  func printMethods(printf printfFunc, node ast.Node, methods []*types.Selection) {
   520  	if len(methods) > 0 {
   521  		printf(node, "Methods:")
   522  	}
   523  	for _, meth := range methods {
   524  		// Print the method type relative to the package
   525  		// in which it was defined, not the query package,
   526  		printf(meth.Obj(), "\t%s",
   527  			types.SelectionString(meth, types.RelativeTo(meth.Obj().Pkg())))
   528  	}
   529  }
   530  
   531  func printFields(printf printfFunc, node ast.Node, fields []describeField) {
   532  	if len(fields) > 0 {
   533  		printf(node, "Fields:")
   534  	}
   535  
   536  	// Align the names and the types (requires two passes).
   537  	var width int
   538  	var names []string
   539  	for _, f := range fields {
   540  		var buf bytes.Buffer
   541  		for _, fld := range f.implicits {
   542  			buf.WriteString(fld.Obj().Name())
   543  			buf.WriteByte('.')
   544  		}
   545  		buf.WriteString(f.field.Name())
   546  		name := buf.String()
   547  		if n := utf8.RuneCountInString(name); n > width {
   548  			width = n
   549  		}
   550  		names = append(names, name)
   551  	}
   552  
   553  	for i, f := range fields {
   554  		// Print the field type relative to the package
   555  		// in which it was defined, not the query package,
   556  		printf(f.field, "\t%*s %s", -width, names[i],
   557  			types.TypeString(f.field.Type(), types.RelativeTo(f.field.Pkg())))
   558  	}
   559  }
   560  
   561  func printNamedTypes(printf printfFunc, node ast.Node, names []*types.Named) {
   562  	if len(names) > 0 {
   563  		printf(node, "Named types:")
   564  	}
   565  
   566  	for _, t := range names {
   567  		// Print the type relative to the package
   568  		// in which it was defined, not the query package,
   569  		printf(t.Obj(), "\ttype %s defined here",
   570  			types.TypeString(t.Obj().Type(), types.RelativeTo(t.Obj().Pkg())))
   571  	}
   572  }
   573  
   574  func (r *describeTypeResult) PrintPlain(printf printfFunc) {
   575  	printf(r.node, "%s", r.description)
   576  
   577  	// Show the underlying type for a reference to a named type.
   578  	if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() {
   579  		// TODO(adonovan): improve display of complex struct/interface types.
   580  		printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying()))
   581  	}
   582  
   583  	printMethods(printf, r.node, r.methods)
   584  	if len(r.methods) == 0 {
   585  		// Only report null result for type kinds
   586  		// capable of bearing methods.
   587  		switch r.typ.(type) {
   588  		case *types.Interface, *types.Struct, *types.Named:
   589  			printf(r.node, "No methods.")
   590  		}
   591  	}
   592  
   593  	printFields(printf, r.node, r.fields)
   594  }
   595  
   596  func (r *describeTypeResult) JSON(fset *token.FileSet) []byte {
   597  	var namePos, nameDef string
   598  	if nt, ok := r.typ.(*types.Named); ok {
   599  		namePos = fset.Position(nt.Obj().Pos()).String()
   600  		nameDef = nt.Underlying().String()
   601  	}
   602  	return toJSON(&serial.Describe{
   603  		Desc:   r.description,
   604  		Pos:    fset.Position(r.node.Pos()).String(),
   605  		Detail: "type",
   606  		Type: &serial.DescribeType{
   607  			Type:    r.qpos.typeString(r.typ),
   608  			NamePos: namePos,
   609  			NameDef: nameDef,
   610  			Methods: methodsToSerial(r.qpos.info.Pkg, r.methods, fset),
   611  		},
   612  	})
   613  }
   614  
   615  // ---- PACKAGE ------------------------------------------------------------
   616  
   617  func describePackage(qpos *queryPos, path []ast.Node) (*describePackageResult, error) {
   618  	var description string
   619  	var pkg *types.Package
   620  	switch n := path[0].(type) {
   621  	case *ast.ImportSpec:
   622  		var obj types.Object
   623  		if n.Name != nil {
   624  			obj = qpos.info.Defs[n.Name]
   625  		} else {
   626  			obj = qpos.info.Implicits[n]
   627  		}
   628  		pkgname, _ := obj.(*types.PkgName)
   629  		if pkgname == nil {
   630  			return nil, fmt.Errorf("can't import package %s", n.Path.Value)
   631  		}
   632  		pkg = pkgname.Imported()
   633  		description = fmt.Sprintf("import of package %q", pkg.Path())
   634  
   635  	case *ast.Ident:
   636  		if _, isDef := path[1].(*ast.File); isDef {
   637  			// e.g. package id
   638  			pkg = qpos.info.Pkg
   639  			description = fmt.Sprintf("definition of package %q", pkg.Path())
   640  		} else {
   641  			// e.g. import id "..."
   642  			//  or  id.F()
   643  			pkg = qpos.info.ObjectOf(n).(*types.PkgName).Imported()
   644  			description = fmt.Sprintf("reference to package %q", pkg.Path())
   645  		}
   646  
   647  	default:
   648  		// Unreachable?
   649  		return nil, fmt.Errorf("unexpected AST for package: %T", n)
   650  	}
   651  
   652  	var members []*describeMember
   653  	// NB: "unsafe" has no types.Package
   654  	if pkg != nil {
   655  		// Enumerate the accessible package members
   656  		// in lexicographic order.
   657  		for _, name := range pkg.Scope().Names() {
   658  			if pkg == qpos.info.Pkg || ast.IsExported(name) {
   659  				mem := pkg.Scope().Lookup(name)
   660  				var methods []*types.Selection
   661  				if mem, ok := mem.(*types.TypeName); ok {
   662  					methods = accessibleMethods(mem.Type(), qpos.info.Pkg)
   663  				}
   664  				members = append(members, &describeMember{
   665  					mem,
   666  					methods,
   667  				})
   668  
   669  			}
   670  		}
   671  	}
   672  
   673  	return &describePackageResult{qpos.fset, path[0], description, pkg, members}, nil
   674  }
   675  
   676  type describePackageResult struct {
   677  	fset        *token.FileSet
   678  	node        ast.Node
   679  	description string
   680  	pkg         *types.Package
   681  	members     []*describeMember // in lexicographic name order
   682  }
   683  
   684  type describeMember struct {
   685  	obj     types.Object
   686  	methods []*types.Selection // in types.MethodSet order
   687  }
   688  
   689  func (r *describePackageResult) PrintPlain(printf printfFunc) {
   690  	printf(r.node, "%s", r.description)
   691  
   692  	// Compute max width of name "column".
   693  	maxname := 0
   694  	for _, mem := range r.members {
   695  		if l := len(mem.obj.Name()); l > maxname {
   696  			maxname = l
   697  		}
   698  	}
   699  
   700  	for _, mem := range r.members {
   701  		printf(mem.obj, "\t%s", formatMember(mem.obj, maxname))
   702  		for _, meth := range mem.methods {
   703  			printf(meth.Obj(), "\t\t%s", types.SelectionString(meth, types.RelativeTo(r.pkg)))
   704  		}
   705  	}
   706  }
   707  
   708  func formatMember(obj types.Object, maxname int) string {
   709  	qualifier := types.RelativeTo(obj.Pkg())
   710  	var buf bytes.Buffer
   711  	fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.Name())
   712  	switch obj := obj.(type) {
   713  	case *types.Const:
   714  		fmt.Fprintf(&buf, " %s = %s", types.TypeString(obj.Type(), qualifier), obj.Val())
   715  
   716  	case *types.Func:
   717  		fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
   718  
   719  	case *types.TypeName:
   720  		typ := obj.Type()
   721  		if isAlias(obj) {
   722  			buf.WriteString(" = ")
   723  		} else {
   724  			buf.WriteByte(' ')
   725  			typ = typ.Underlying()
   726  		}
   727  		var typestr string
   728  		// Abbreviate long aggregate type names.
   729  		switch typ := typ.(type) {
   730  		case *types.Interface:
   731  			if typ.NumMethods() > 1 {
   732  				typestr = "interface{...}"
   733  			}
   734  		case *types.Struct:
   735  			if typ.NumFields() > 1 {
   736  				typestr = "struct{...}"
   737  			}
   738  		}
   739  		if typestr == "" {
   740  			// The fix for #44515 changed the printing of unsafe.Pointer
   741  			// such that it uses a qualifier if one is provided. Using
   742  			// the types.RelativeTo qualifier provided here, the output
   743  			// is just "Pointer" rather than "unsafe.Pointer". This is
   744  			// consistent with the printing of non-type objects but it
   745  			// breaks an existing test which needs to work with older
   746  			// versions of Go. Re-establish the original output by not
   747  			// using a qualifier at all if we're printing a type from
   748  			// package unsafe - there's only unsafe.Pointer (#44596).
   749  			// NOTE: This correction can be removed (and the test's
   750  			// golden file adjusted) once we only run against go1.17
   751  			// or bigger.
   752  			qualifier := qualifier
   753  			if obj.Pkg() == types.Unsafe {
   754  				qualifier = nil
   755  			}
   756  			typestr = types.TypeString(typ, qualifier)
   757  		}
   758  		buf.WriteString(typestr)
   759  
   760  	case *types.Var:
   761  		fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
   762  	}
   763  	return buf.String()
   764  }
   765  
   766  func (r *describePackageResult) JSON(fset *token.FileSet) []byte {
   767  	var members []*serial.DescribeMember
   768  	for _, mem := range r.members {
   769  		obj := mem.obj
   770  		typ := obj.Type()
   771  		var val string
   772  		var alias string
   773  		switch obj := obj.(type) {
   774  		case *types.Const:
   775  			val = obj.Val().String()
   776  		case *types.TypeName:
   777  			if isAlias(obj) {
   778  				alias = "= " // kludgy
   779  			} else {
   780  				typ = typ.Underlying()
   781  			}
   782  		}
   783  		members = append(members, &serial.DescribeMember{
   784  			Name:    obj.Name(),
   785  			Type:    alias + typ.String(),
   786  			Value:   val,
   787  			Pos:     fset.Position(obj.Pos()).String(),
   788  			Kind:    tokenOf(obj),
   789  			Methods: methodsToSerial(r.pkg, mem.methods, fset),
   790  		})
   791  	}
   792  	return toJSON(&serial.Describe{
   793  		Desc:   r.description,
   794  		Pos:    fset.Position(r.node.Pos()).String(),
   795  		Detail: "package",
   796  		Package: &serial.DescribePackage{
   797  			Path:    r.pkg.Path(),
   798  			Members: members,
   799  		},
   800  	})
   801  }
   802  
   803  func tokenOf(o types.Object) string {
   804  	switch o.(type) {
   805  	case *types.Func:
   806  		return "func"
   807  	case *types.Var:
   808  		return "var"
   809  	case *types.TypeName:
   810  		return "type"
   811  	case *types.Const:
   812  		return "const"
   813  	case *types.PkgName:
   814  		return "package"
   815  	case *types.Builtin:
   816  		return "builtin" // e.g. when describing package "unsafe"
   817  	case *types.Nil:
   818  		return "nil"
   819  	case *types.Label:
   820  		return "label"
   821  	}
   822  	panic(o)
   823  }
   824  
   825  // ---- STATEMENT ------------------------------------------------------------
   826  
   827  func describeStmt(qpos *queryPos, path []ast.Node) (*describeStmtResult, error) {
   828  	var description string
   829  	switch n := path[0].(type) {
   830  	case *ast.Ident:
   831  		if qpos.info.Defs[n] != nil {
   832  			description = "labelled statement"
   833  		} else {
   834  			description = "reference to labelled statement"
   835  		}
   836  
   837  	default:
   838  		// Nothing much to say about statements.
   839  		description = astutil.NodeDescription(n)
   840  	}
   841  	return &describeStmtResult{qpos.fset, path[0], description}, nil
   842  }
   843  
   844  type describeStmtResult struct {
   845  	fset        *token.FileSet
   846  	node        ast.Node
   847  	description string
   848  }
   849  
   850  func (r *describeStmtResult) PrintPlain(printf printfFunc) {
   851  	printf(r.node, "%s", r.description)
   852  }
   853  
   854  func (r *describeStmtResult) JSON(fset *token.FileSet) []byte {
   855  	return toJSON(&serial.Describe{
   856  		Desc:   r.description,
   857  		Pos:    fset.Position(r.node.Pos()).String(),
   858  		Detail: "unknown",
   859  	})
   860  }
   861  
   862  // ------------------- Utilities -------------------
   863  
   864  // pathToString returns a string containing the concrete types of the
   865  // nodes in path.
   866  func pathToString(path []ast.Node) string {
   867  	var buf bytes.Buffer
   868  	fmt.Fprint(&buf, "[")
   869  	for i, n := range path {
   870  		if i > 0 {
   871  			fmt.Fprint(&buf, " ")
   872  		}
   873  		fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast."))
   874  	}
   875  	fmt.Fprint(&buf, "]")
   876  	return buf.String()
   877  }
   878  
   879  func accessibleMethods(t types.Type, from *types.Package) []*types.Selection {
   880  	var methods []*types.Selection
   881  	for _, meth := range typeutil.IntuitiveMethodSet(t, nil) {
   882  		if isAccessibleFrom(meth.Obj(), from) {
   883  			methods = append(methods, meth)
   884  		}
   885  	}
   886  	return methods
   887  }
   888  
   889  // accessibleFields returns the set of accessible
   890  // field selections on a value of type recv.
   891  func accessibleFields(recv types.Type, from *types.Package) []describeField {
   892  	wantField := func(f *types.Var) bool {
   893  		if !isAccessibleFrom(f, from) {
   894  			return false
   895  		}
   896  		// Check that the field is not shadowed.
   897  		obj, _, _ := types.LookupFieldOrMethod(recv, true, f.Pkg(), f.Name())
   898  		return obj == f
   899  	}
   900  
   901  	var fields []describeField
   902  	var visit func(t types.Type, stack []*types.Named)
   903  	visit = func(t types.Type, stack []*types.Named) {
   904  		tStruct, ok := deref(t).Underlying().(*types.Struct)
   905  		if !ok {
   906  			return
   907  		}
   908  	fieldloop:
   909  		for i := 0; i < tStruct.NumFields(); i++ {
   910  			f := tStruct.Field(i)
   911  
   912  			// Handle recursion through anonymous fields.
   913  			if f.Anonymous() {
   914  				tf := f.Type()
   915  				if ptr, ok := tf.(*types.Pointer); ok {
   916  					tf = ptr.Elem()
   917  				}
   918  				if named, ok := tf.(*types.Named); ok { // (be defensive)
   919  					// If we've already visited this named type
   920  					// on this path, break the cycle.
   921  					for _, x := range stack {
   922  						if x == named {
   923  							continue fieldloop
   924  						}
   925  					}
   926  					visit(f.Type(), append(stack, named))
   927  				}
   928  			}
   929  
   930  			// Save accessible fields.
   931  			if wantField(f) {
   932  				fields = append(fields, describeField{
   933  					implicits: append([]*types.Named(nil), stack...),
   934  					field:     f,
   935  				})
   936  			}
   937  		}
   938  	}
   939  	visit(recv, nil)
   940  
   941  	return fields
   942  }
   943  
   944  func isAccessibleFrom(obj types.Object, pkg *types.Package) bool {
   945  	return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
   946  }
   947  
   948  func methodsToSerial(this *types.Package, methods []*types.Selection, fset *token.FileSet) []serial.DescribeMethod {
   949  	qualifier := types.RelativeTo(this)
   950  	var jmethods []serial.DescribeMethod
   951  	for _, meth := range methods {
   952  		var ser serial.DescribeMethod
   953  		if meth != nil { // may contain nils when called by implements (on a method)
   954  			ser = serial.DescribeMethod{
   955  				Name: types.SelectionString(meth, qualifier),
   956  				Pos:  fset.Position(meth.Obj().Pos()).String(),
   957  			}
   958  		}
   959  		jmethods = append(jmethods, ser)
   960  	}
   961  	return jmethods
   962  }