GoPLS Viewer

Home|gopls/cmd/guru/describe.go
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)
29func describe(q *Queryerror {
30    lconf := loader.Config{Buildq.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    lprogerr := lconf.Load()
39    if err != nil {
40        return err
41    }
42
43    qposerr := parseQueryPos(lprogq.Postrue// (need exact pos)
44    if err != nil {
45        return err
46    }
47
48    if false { // debugging
49        fprintf(os.Stderrlprog.Fsetqpos.path[0], "you selected: %s %s",
50            astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path))
51    }
52
53    var qr QueryResult
54    pathaction := findInterestingNode(qpos.infoqpos.path)
55    switch action {
56    case actionExpr:
57        qrerr = describeValue(qpospath)
58
59    case actionType:
60        qrerr = describeType(qpospath)
61
62    case actionPackage:
63        qrerr = describePackage(qpospath)
64
65    case actionStmt:
66        qrerr = describeStmt(qpospath)
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.Fsetqr)
78    return nil
79}
80
81type describeUnknownResult struct {
82    node ast.Node
83}
84
85func (r *describeUnknownResultPrintPlain(printf printfFunc) {
86    // Nothing much to say about misc syntax.
87    printf(r.node"%s"astutil.NodeDescription(r.node))
88}
89
90func (r *describeUnknownResultJSON(fset *token.FileSet) []byte {
91    return toJSON(&serial.Describe{
92        Descastutil.NodeDescription(r.node),
93        Pos:  fset.Position(r.node.Pos()).String(),
94    })
95}
96
97type action int
98
99const (
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.
117func findInterestingNode(pkginfo *loader.PackageInfopath []ast.Node) ([]ast.Nodeaction) {
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 pathactionUnknown // 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 pathactionPackage
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 pathactionUnknown // 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 pathactionUnknown // uninteresting
166
167        case ast.Stmt:
168            return pathactionStmt
169
170        case *ast.ArrayType,
171            *ast.StructType,
172            *ast.FuncType,
173            *ast.InterfaceType,
174            *ast.MapType,
175            *ast.ChanType:
176            return pathactionType
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 pathactionExpr
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 pathactionUnknown
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 pathactionPackage
220
221            case *types.Const:
222                return pathactionExpr
223
224            case *types.Label:
225                return pathactionStmt
226
227            case *types.TypeName:
228                return pathactionType
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 pathactionExpr
239
240            case *types.Func:
241                return pathactionExpr
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 pathactionExpr
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 pathactionPackage
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 pathactionUnknown
291            }
292
293        case *ast.StarExpr:
294            if pkginfo.Types[n].IsType() {
295                return pathactionType
296            }
297            return pathactionExpr
298
299        case ast.Expr:
300            // All Expr but {BasicLit,Ident,StarExpr} are
301            // "true" expressions that evaluate to a value.
302            return pathactionExpr
303        }
304
305        // Ascend to parent.
306        path = path[1:]
307    }
308
309    return nilactionUnknown // unreachable
310}
311
312func describeValue(qpos *queryPospath []ast.Node) (*describeValueResulterror) {
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 nilfmt.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 nilfmt.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 cok := 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(niltyp),
343        constValconstVal,
344        obj:      obj,
345        methods:  accessibleMethods(typqpos.info.Pkg),
346        fields:   accessibleFields(typqpos.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.
353func appendNames(names []*types.Namedtyp 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(namest)
363    case *types.Map:
364        names = appendNames(namest.Key())
365        names = appendNames(namest.Elem())
366    case elemType:
367        names = appendNames(namest.Elem())
368    }
369
370    return names
371}
372
373type 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
384func (r *describeValueResultPrintPlain(printf printfFunc) {
385    var prefixsuffix 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"prefixr.qpos.objectString(r.obj), suffix)
405        } else {
406            // referring ident
407            printf(r.expr"reference to %s%s%s"prefixr.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"descsuffix)
417        } else {
418            // non-constant expression
419            printf(r.expr"%s of type %s"descr.qpos.typeString(r.typ))
420        }
421    }
422
423    printMethods(printfr.exprr.methods)
424    printFields(printfr.exprr.fields)
425    printNamedTypes(printfr.exprr.names)
426}
427
428func (r *describeValueResultJSON(fset *token.FileSet) []byte {
429    var valueobjpos 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.Definitionlen(r.names))
438    for it := range r.names {
439        typesPos[i] = serial.Definition{
440            ObjPosfset.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            TypesPostypesPos,
452            Value:    value,
453            ObjPos:   objpos,
454        },
455    })
456}
457
458// ---- TYPE ------------------------------------------------------------
459
460func describeType(qpos *queryPospath []ast.Node) (*describeTypeResulterror) {
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 nilfmt.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{WordSize8MaxAlign8// 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        descriptiondescription,
499        typ:         typ,
500        methods:     accessibleMethods(typqpos.info.Pkg),
501        fields:      accessibleFields(typqpos.info.Pkg),
502    }, nil
503}
504
505type 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
514type describeField struct {
515    implicits []*types.Named
516    field     *types.Var
517}
518
519func printMethods(printf printfFuncnode ast.Nodemethods []*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(methtypes.RelativeTo(meth.Obj().Pkg())))
528    }
529}
530
531func printFields(printf printfFuncnode ast.Nodefields []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(namesname)
551    }
552
553    for if := 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", -widthnames[i],
557            types.TypeString(f.field.Type(), types.RelativeTo(f.field.Pkg())))
558    }
559}
560
561func printNamedTypes(printf printfFuncnode ast.Nodenames []*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
574func (r *describeTypeResultPrintPlain(printf printfFunc) {
575    printf(r.node"%s"r.description)
576
577    // Show the underlying type for a reference to a named type.
578    if ntok := 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(printfr.noder.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(printfr.noder.fields)
594}
595
596func (r *describeTypeResultJSON(fset *token.FileSet) []byte {
597    var namePosnameDef string
598    if ntok := 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            NamePosnamePos,
609            NameDefnameDef,
610            MethodsmethodsToSerial(r.qpos.info.Pkgr.methodsfset),
611        },
612    })
613}
614
615// ---- PACKAGE ------------------------------------------------------------
616
617func describePackage(qpos *queryPospath []ast.Node) (*describePackageResulterror) {
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 nilfmt.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 nilfmt.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 memok := 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.fsetpath[0], descriptionpkgmembers}, nil
674}
675
676type 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
684type describeMember struct {
685    obj     types.Object
686    methods []*types.Selection // in types.MethodSet order
687}
688
689func (r *describePackageResultPrintPlain(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.objmaxname))
702        for _meth := range mem.methods {
703            printf(meth.Obj(), "\t\t%s"types.SelectionString(methtypes.RelativeTo(r.pkg)))
704        }
705    }
706}
707
708func formatMember(obj types.Objectmaxname intstring {
709    qualifier := types.RelativeTo(obj.Pkg())
710    var buf bytes.Buffer
711    fmt.Fprintf(&buf"%-5s %-*s"tokenOf(obj), maxnameobj.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(typqualifier)
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
766func (r *describePackageResultJSON(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            MethodsmethodsToSerial(r.pkgmem.methodsfset),
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            Membersmembers,
799        },
800    })
801}
802
803func tokenOf(o types.Objectstring {
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
827func describeStmt(qpos *queryPospath []ast.Node) (*describeStmtResulterror) {
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.fsetpath[0], description}, nil
842}
843
844type describeStmtResult struct {
845    fset        *token.FileSet
846    node        ast.Node
847    description string
848}
849
850func (r *describeStmtResultPrintPlain(printf printfFunc) {
851    printf(r.node"%s"r.description)
852}
853
854func (r *describeStmtResultJSON(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.
866func pathToString(path []ast.Nodestring {
867    var buf bytes.Buffer
868    fmt.Fprint(&buf"[")
869    for in := range path {
870        if i > 0 {
871            fmt.Fprint(&buf" ")
872        }
873        fmt.Fprint(&bufstrings.TrimPrefix(fmt.Sprintf("%T"n), "*ast."))
874    }
875    fmt.Fprint(&buf"]")
876    return buf.String()
877}
878
879func accessibleMethods(t types.Typefrom *types.Package) []*types.Selection {
880    var methods []*types.Selection
881    for _meth := range typeutil.IntuitiveMethodSet(tnil) {
882        if isAccessibleFrom(meth.Obj(), from) {
883            methods = append(methodsmeth)
884        }
885    }
886    return methods
887}
888
889// accessibleFields returns the set of accessible
890// field selections on a value of type recv.
891func accessibleFields(recv types.Typefrom *types.Package) []describeField {
892    wantField := func(f *types.Varbool {
893        if !isAccessibleFrom(ffrom) {
894            return false
895        }
896        // Check that the field is not shadowed.
897        obj__ := types.LookupFieldOrMethod(recvtruef.Pkg(), f.Name())
898        return obj == f
899    }
900
901    var fields []describeField
902    var visit func(t types.Typestack []*types.Named)
903    visit = func(t types.Typestack []*types.Named) {
904        tStructok := deref(t).Underlying().(*types.Struct)
905        if !ok {
906            return
907        }
908    fieldloop:
909        for i := 0i < 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 ptrok := tf.(*types.Pointer); ok {
916                    tf = ptr.Elem()
917                }
918                if namedok := 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(stacknamed))
927                }
928            }
929
930            // Save accessible fields.
931            if wantField(f) {
932                fields = append(fieldsdescribeField{
933                    implicitsappend([]*types.Named(nil), stack...),
934                    field:     f,
935                })
936            }
937        }
938    }
939    visit(recvnil)
940
941    return fields
942}
943
944func isAccessibleFrom(obj types.Objectpkg *types.Packagebool {
945    return ast.IsExported(obj.Name()) || obj.Pkg() == pkg
946}
947
948func methodsToSerial(this *types.Packagemethods []*types.Selectionfset *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                Nametypes.SelectionString(methqualifier),
956                Pos:  fset.Position(meth.Obj().Pos()).String(),
957            }
958        }
959        jmethods = append(jmethodsser)
960    }
961    return jmethods
962}
963
MembersX
describeField.implicits
describePackage.qpos
describePackageResult.JSON.RangeStmt_20704.mem
describePackageResult.JSON.RangeStmt_20704.BlockStmt.val
describe.q
appendNames.elemType
describePackageResult.description
describeStmtResult.PrintPlain
describePackageResult.PrintPlain.RangeStmt_18451.mem
formatMember.obj
findInterestingNode
describeValueResult.PrintPlain.prefix
describeValueResult.PrintPlain.suffix
describeTypeResult.typ
printFields.names
describePackageResult.node
describeStmtResult.JSON
pathToString.RangeStmt_23078.i
os
describeValueResult.obj
describeValueResult.JSON.RangeStmt_11473.i
describeType
printMethods.node
describeTypeResult.JSON.nameDef
methodsToSerial.fset
describePackage.pkg
accessibleMethods.from
describeUnknownResult
describeValueResult.expr
describeValueResult.names
describeValueResult.methods
describeValueResult.PrintPlain.printf
describeType.path
printNamedTypes.node
describePackageResult
constant
describeValueResult.JSON.RangeStmt_11473.t
describeType.description
printMethods.printf
printMethods.RangeStmt_13667.meth
printFields.printf
describeMember.methods
describePackageResult.PrintPlain.maxname
describe.path
appendNames
describe.qpos
describeValueResult.PrintPlain.r
printFields.RangeStmt_14127.BlockStmt.RangeStmt_14179.fld
describePackageResult.members
describeMember.obj
describeStmtResult.node
accessibleFields
describeUnknownResult.node
describeValue.path
describeValue.expr
describeValueResult.JSON.fset
describeTypeResult.fields
describeTypeResult.JSON
accessibleFields.BlockStmt.BlockStmt.f
methodsToSerial.RangeStmt_25240.meth
describeUnknownResult.JSON
describeValueResult.constVal
printFields.RangeStmt_14127.f
printFields.RangeStmt_14127.BlockStmt.buf
describePackage.BlockStmt.RangeStmt_17528.BlockStmt.BlockStmt.methods
describeStmtResult.PrintPlain.r
describe.action
describePackage.BlockStmt.RangeStmt_17528.name
describeStmtResult.description
methodsToSerial.qualifier
printNamedTypes.RangeStmt_14827.t
describeTypeResult.JSON.fset
describe.err
action
describeValueResult.typ
describeValueResult.PrintPlain.BlockStmt.recv
describeValueResult.JSON.value
describeTypeResult
describePackage.BlockStmt.obj
describeStmtResult.fset
describeStmtResult.JSON.fset
accessibleFields.BlockStmt.obj
isAccessibleFrom
methodsToSerial
describeValueResult.PrintPlain.BlockStmt.desc
describePackageResult.PrintPlain.RangeStmt_18451.BlockStmt.l
formatMember
describeStmt
describeStmt.path
methodsToSerial.jmethods
astutil
describeValue.qpos
describeValueResult.fields
printFields.RangeStmt_14127.BlockStmt.n
describePackageResult.fset
describePackageResult.JSON.r
accessibleMethods.methods
accessibleMethods.RangeStmt_23377.meth
bytes
findInterestingNode.pkginfo
printFields.width
printFields.RangeStmt_14437.i
describePackage
describeStmt.qpos
describeUnknownResult.JSON.fset
describeValueResult.qpos
actionUnknown
appendNames.typ
printNamedTypes.names
pathToString.path
printFields.node
describePackageResult.pkg
describePackageResult.JSON
accessibleFields.recv
accessibleFields.fields
describeUnknownResult.PrintPlain
describeTypeResult.qpos
printFields.RangeStmt_14127.BlockStmt.name
describePackage.description
isAccessibleFrom.obj
methodsToSerial.this
describeUnknownResult.PrintPlain.r
describeValueResult.PrintPlain
describeTypeResult.node
describeTypeResult.description
describePackageResult.PrintPlain.printf
accessibleFields.BlockStmt.BlockStmt.BlockStmt.tf
formatMember.buf
formatMember.BlockStmt.typ
strings
describe.lprog
describeUnknownResult.PrintPlain.printf
describeValueResult.JSON.r
printMethods.methods
describePackage.BlockStmt.RangeStmt_17528.BlockStmt.BlockStmt.mem
pathToString
accessibleFields.BlockStmt._
accessibleFields.from
describe
describe.lconf
printFields.fields
describePackageResult.JSON.RangeStmt_20704.BlockStmt.obj
describePackageResult.JSON.RangeStmt_20704.BlockStmt.alias
tokenOf
findInterestingNode.path
describeTypeResult.PrintPlain.r
describeMember
describeStmt.description
pathToString.buf
utf8
describeValueResult.PrintPlain.BlockStmt.BlockStmt.def
describeValueResult.JSON.objpos
printNamedTypes.printf
describeTypeResult.JSON.namePos
describePackageResult.PrintPlain.RangeStmt_18552.mem
describe.qr
describeValue.obj
describeType.BlockStmt.szs
printFields.RangeStmt_14437.f
printNamedTypes
formatMember.BlockStmt.typestr
methodsToSerial.RangeStmt_25240.BlockStmt.ser
describeUnknownResult.JSON.r
describeValue.typ
describeType.typ
describeStmtResult.JSON.r
accessibleFields.BlockStmt.i
methodsToSerial.methods
describeStmtResult
describeValueResult
describeType.qpos
describeField.field
printMethods
describePackage.path
formatMember.maxname
describe._
describeTypeResult.methods
describeTypeResult.PrintPlain.printf
formatMember.qualifier
describePackageResult.JSON.fset
describeStmtResult.PrintPlain.printf
typeutil
describeValue
appendNames.names
describePackageResult.JSON.RangeStmt_20704.BlockStmt.typ
accessibleMethods
isAccessibleFrom.pkg
describePackageResult.JSON.members
tokenOf.o
describeValueResult.JSON
describeValueResult.JSON.typesPos
printFields
describeTypeResult.PrintPlain
describeTypeResult.JSON.r
describePackage.members
describeValue.constVal
describePackageResult.PrintPlain.r
describePackageResult.PrintPlain
formatMember.BlockStmt.BlockStmt.qualifier
pathToString.RangeStmt_23078.n
accessibleFields.BlockStmt.BlockStmt.BlockStmt.BlockStmt.RangeStmt_24554.x
describeField
describePackageResult.PrintPlain.RangeStmt_18552.BlockStmt.RangeStmt_18644.meth
accessibleMethods.t
Members
X