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