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