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
5package main
6
7import (
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//
30func 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
82type describeUnknownResult struct {
83	node ast.Node
84}
85
86func (r *describeUnknownResult) PrintPlain(printf printfFunc) {
87	// Nothing much to say about misc syntax.
88	printf(r.node, "%s", astutil.NodeDescription(r.node))
89}
90
91func (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
98type action int
99
100const (
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//
118func 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
313func 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.
354func 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
374type 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
385func (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
429func (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
461func 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
506type 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
515type describeField struct {
516	implicits []*types.Named
517	field     *types.Var
518}
519
520func 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
532func 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
562func 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
575func (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
597func (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
618func 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
677type 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
685type describeMember struct {
686	obj     types.Object
687	methods []*types.Selection // in types.MethodSet order
688}
689
690func (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
709func 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
767func (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
804func 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
828func 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
845type describeStmtResult struct {
846	fset        *token.FileSet
847	node        ast.Node
848	description string
849}
850
851func (r *describeStmtResult) PrintPlain(printf printfFunc) {
852	printf(r.node, "%s", r.description)
853}
854
855func (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.
867func 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
880func 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.
892func 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
945func isAccessibleFrom(obj types.Object, pkg *types.Package) bool {
946	return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
947}
948
949func 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}
964