You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

899 lines
23 KiB

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