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