github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/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 "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 func describe(q *Query) error { 30 lconf := loader.Config{Build: q.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 lprog, err := lconf.Load() 39 if err != nil { 40 return err 41 } 42 43 qpos, err := parseQueryPos(lprog, q.Pos, true) // (need exact pos) 44 if err != nil { 45 return err 46 } 47 48 if false { // debugging 49 fprintf(os.Stderr, lprog.Fset, qpos.path[0], "you selected: %s %s", 50 astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path)) 51 } 52 53 var qr QueryResult 54 path, action := findInterestingNode(qpos.info, qpos.path) 55 switch action { 56 case actionExpr: 57 qr, err = describeValue(qpos, path) 58 59 case actionType: 60 qr, err = describeType(qpos, path) 61 62 case actionPackage: 63 qr, err = describePackage(qpos, path) 64 65 case actionStmt: 66 qr, err = describeStmt(qpos, path) 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.Fset, qr) 78 return nil 79 } 80 81 type describeUnknownResult struct { 82 node ast.Node 83 } 84 85 func (r *describeUnknownResult) PrintPlain(printf printfFunc) { 86 // Nothing much to say about misc syntax. 87 printf(r.node, "%s", astutil.NodeDescription(r.node)) 88 } 89 90 func (r *describeUnknownResult) JSON(fset *token.FileSet) []byte { 91 return toJSON(&serial.Describe{ 92 Desc: astutil.NodeDescription(r.node), 93 Pos: fset.Position(r.node.Pos()).String(), 94 }) 95 } 96 97 type action int 98 99 const ( 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. 117 func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) { 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 path, actionUnknown // 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 path, actionPackage 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 path, actionUnknown // 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 path, actionUnknown // uninteresting 166 167 case ast.Stmt: 168 return path, actionStmt 169 170 case *ast.ArrayType, 171 *ast.StructType, 172 *ast.FuncType, 173 *ast.InterfaceType, 174 *ast.MapType, 175 *ast.ChanType: 176 return path, actionType 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 path, actionExpr 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 path, actionUnknown 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 path, actionPackage 220 221 case *types.Const: 222 return path, actionExpr 223 224 case *types.Label: 225 return path, actionStmt 226 227 case *types.TypeName: 228 return path, actionType 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 path, actionExpr 239 240 case *types.Func: 241 return path, actionExpr 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 path, actionExpr 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 path, actionPackage 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 path, actionUnknown 291 } 292 293 case *ast.StarExpr: 294 if pkginfo.Types[n].IsType() { 295 return path, actionType 296 } 297 return path, actionExpr 298 299 case ast.Expr: 300 // All Expr but {BasicLit,Ident,StarExpr} are 301 // "true" expressions that evaluate to a value. 302 return path, actionExpr 303 } 304 305 // Ascend to parent. 306 path = path[1:] 307 } 308 309 return nil, actionUnknown // unreachable 310 } 311 312 func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error) { 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 nil, fmt.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 nil, fmt.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 c, ok := 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(nil, 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 // 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. 353 func appendNames(names []*types.Named, typ 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(names, t) 363 case *types.Map: 364 names = appendNames(names, t.Key()) 365 names = appendNames(names, t.Elem()) 366 case elemType: 367 names = appendNames(names, t.Elem()) 368 } 369 370 return names 371 } 372 373 type 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 384 func (r *describeValueResult) PrintPlain(printf printfFunc) { 385 var prefix, suffix 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", prefix, r.qpos.objectString(r.obj), suffix) 405 } else { 406 // referring ident 407 printf(r.expr, "reference to %s%s%s", prefix, r.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", desc, suffix) 417 } else { 418 // non-constant expression 419 printf(r.expr, "%s of type %s", desc, r.qpos.typeString(r.typ)) 420 } 421 } 422 423 printMethods(printf, r.expr, r.methods) 424 printFields(printf, r.expr, r.fields) 425 printNamedTypes(printf, r.expr, r.names) 426 } 427 428 func (r *describeValueResult) JSON(fset *token.FileSet) []byte { 429 var value, objpos 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.Definition, len(r.names)) 438 for i, t := range r.names { 439 typesPos[i] = serial.Definition{ 440 ObjPos: fset.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 TypesPos: typesPos, 452 Value: value, 453 ObjPos: objpos, 454 }, 455 }) 456 } 457 458 // ---- TYPE ------------------------------------------------------------ 459 460 func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) { 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 nil, fmt.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{WordSize: 8, MaxAlign: 8} // 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 description: description, 499 typ: typ, 500 methods: accessibleMethods(typ, qpos.info.Pkg), 501 fields: accessibleFields(typ, qpos.info.Pkg), 502 }, nil 503 } 504 505 type 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 514 type describeField struct { 515 implicits []*types.Named 516 field *types.Var 517 } 518 519 func printMethods(printf printfFunc, node ast.Node, methods []*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(meth, types.RelativeTo(meth.Obj().Pkg()))) 528 } 529 } 530 531 func printFields(printf printfFunc, node ast.Node, fields []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(names, name) 551 } 552 553 for i, f := 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", -width, names[i], 557 types.TypeString(f.field.Type(), types.RelativeTo(f.field.Pkg()))) 558 } 559 } 560 561 func printNamedTypes(printf printfFunc, node ast.Node, names []*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 574 func (r *describeTypeResult) PrintPlain(printf printfFunc) { 575 printf(r.node, "%s", r.description) 576 577 // Show the underlying type for a reference to a named type. 578 if nt, ok := 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(printf, r.node, r.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(printf, r.node, r.fields) 594 } 595 596 func (r *describeTypeResult) JSON(fset *token.FileSet) []byte { 597 var namePos, nameDef string 598 if nt, ok := 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 NamePos: namePos, 609 NameDef: nameDef, 610 Methods: methodsToSerial(r.qpos.info.Pkg, r.methods, fset), 611 }, 612 }) 613 } 614 615 // ---- PACKAGE ------------------------------------------------------------ 616 617 func describePackage(qpos *queryPos, path []ast.Node) (*describePackageResult, error) { 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 nil, fmt.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 nil, fmt.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 mem, ok := 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.fset, path[0], description, pkg, members}, nil 674 } 675 676 type 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 684 type describeMember struct { 685 obj types.Object 686 methods []*types.Selection // in types.MethodSet order 687 } 688 689 func (r *describePackageResult) PrintPlain(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.obj, maxname)) 702 for _, meth := range mem.methods { 703 printf(meth.Obj(), "\t\t%s", types.SelectionString(meth, types.RelativeTo(r.pkg))) 704 } 705 } 706 } 707 708 func formatMember(obj types.Object, maxname int) string { 709 qualifier := types.RelativeTo(obj.Pkg()) 710 var buf bytes.Buffer 711 fmt.Fprintf(&buf, "%-5s %-*s", tokenOf(obj), maxname, obj.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(typ, qualifier) 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 766 func (r *describePackageResult) JSON(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 Methods: methodsToSerial(r.pkg, mem.methods, fset), 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 Members: members, 799 }, 800 }) 801 } 802 803 func tokenOf(o types.Object) string { 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 827 func describeStmt(qpos *queryPos, path []ast.Node) (*describeStmtResult, error) { 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.fset, path[0], description}, nil 842 } 843 844 type describeStmtResult struct { 845 fset *token.FileSet 846 node ast.Node 847 description string 848 } 849 850 func (r *describeStmtResult) PrintPlain(printf printfFunc) { 851 printf(r.node, "%s", r.description) 852 } 853 854 func (r *describeStmtResult) JSON(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. 866 func pathToString(path []ast.Node) string { 867 var buf bytes.Buffer 868 fmt.Fprint(&buf, "[") 869 for i, n := range path { 870 if i > 0 { 871 fmt.Fprint(&buf, " ") 872 } 873 fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast.")) 874 } 875 fmt.Fprint(&buf, "]") 876 return buf.String() 877 } 878 879 func accessibleMethods(t types.Type, from *types.Package) []*types.Selection { 880 var methods []*types.Selection 881 for _, meth := range typeutil.IntuitiveMethodSet(t, nil) { 882 if isAccessibleFrom(meth.Obj(), from) { 883 methods = append(methods, meth) 884 } 885 } 886 return methods 887 } 888 889 // accessibleFields returns the set of accessible 890 // field selections on a value of type recv. 891 func accessibleFields(recv types.Type, from *types.Package) []describeField { 892 wantField := func(f *types.Var) bool { 893 if !isAccessibleFrom(f, from) { 894 return false 895 } 896 // Check that the field is not shadowed. 897 obj, _, _ := types.LookupFieldOrMethod(recv, true, f.Pkg(), f.Name()) 898 return obj == f 899 } 900 901 var fields []describeField 902 var visit func(t types.Type, stack []*types.Named) 903 visit = func(t types.Type, stack []*types.Named) { 904 tStruct, ok := deref(t).Underlying().(*types.Struct) 905 if !ok { 906 return 907 } 908 fieldloop: 909 for i := 0; i < 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 ptr, ok := tf.(*types.Pointer); ok { 916 tf = ptr.Elem() 917 } 918 if named, ok := 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(stack, named)) 927 } 928 } 929 930 // Save accessible fields. 931 if wantField(f) { 932 fields = append(fields, describeField{ 933 implicits: append([]*types.Named(nil), stack...), 934 field: f, 935 }) 936 } 937 } 938 } 939 visit(recv, nil) 940 941 return fields 942 } 943 944 func isAccessibleFrom(obj types.Object, pkg *types.Package) bool { 945 return ast.IsExported(obj.Name()) || obj.Pkg() == pkg 946 } 947 948 func methodsToSerial(this *types.Package, methods []*types.Selection, fset *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 Name: types.SelectionString(meth, qualifier), 956 Pos: fset.Position(meth.Obj().Pos()).String(), 957 } 958 } 959 jmethods = append(jmethods, ser) 960 } 961 return jmethods 962 }