OLD | NEW |
1 // Copyright 2013 The Go Authors. All rights reserved. | 1 // Copyright 2013 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package oracle | 5 package oracle |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "fmt" | 9 "fmt" |
10 "go/ast" | 10 "go/ast" |
11 "go/token" | 11 "go/token" |
| 12 "os" |
12 "sort" | 13 "sort" |
13 "strconv" | 14 "strconv" |
14 "strings" | 15 "strings" |
15 | 16 |
| 17 "code.google.com/p/go.tools/go/exact" |
16 "code.google.com/p/go.tools/go/types" | 18 "code.google.com/p/go.tools/go/types" |
17 "code.google.com/p/go.tools/importer" | 19 "code.google.com/p/go.tools/importer" |
| 20 "code.google.com/p/go.tools/oracle/json" |
18 "code.google.com/p/go.tools/pointer" | 21 "code.google.com/p/go.tools/pointer" |
19 "code.google.com/p/go.tools/ssa" | 22 "code.google.com/p/go.tools/ssa" |
20 ) | 23 ) |
21 | 24 |
22 // TODO(adonovan): all printed sets must be sorted to ensure test determinism. | |
23 | |
24 // describe describes the syntax node denoted by the query position, | 25 // describe describes the syntax node denoted by the query position, |
25 // including: | 26 // including: |
26 // - its syntactic category | 27 // - its syntactic category |
27 // - the location of the definition of its referent (for identifiers) | 28 // - the location of the definition of its referent (for identifiers) |
28 // - its type and method set (for an expression or type expression) | 29 // - its type and method set (for an expression or type expression) |
29 // - its points-to set (for a pointer-like expression) | 30 // - its points-to set (for a pointer-like expression) |
30 // - its concrete types (for an interface expression) and their points-to sets. | 31 // - its concrete types (for an interface expression) and their points-to sets. |
31 // | 32 // |
| 33 // All printed sets are sorted to ensure determinism. |
| 34 // |
32 func describe(o *oracle) (queryResult, error) { | 35 func describe(o *oracle) (queryResult, error) { |
33 if false { // debugging | 36 if false { // debugging |
34 » » o.printf(o.queryPath[0], "you selected: %s %s", | 37 » » o.fprintf(os.Stderr, o.queryPath[0], "you selected: %s %s", |
35 importer.NodeDescription(o.queryPath[0]), pathToString2(
o.queryPath)) | 38 importer.NodeDescription(o.queryPath[0]), pathToString2(
o.queryPath)) |
36 } | 39 } |
37 | 40 |
38 path, action := findInterestingNode(o.queryPkgInfo, o.queryPath) | 41 path, action := findInterestingNode(o.queryPkgInfo, o.queryPath) |
39 switch action { | 42 switch action { |
40 case actionExpr: | 43 case actionExpr: |
41 return describeValue(o, path) | 44 return describeValue(o, path) |
42 | 45 |
43 case actionType: | 46 case actionType: |
44 return describeType(o, path) | 47 return describeType(o, path) |
45 | 48 |
46 case actionPackage: | 49 case actionPackage: |
47 return describePackage(o, path) | 50 return describePackage(o, path) |
48 | 51 |
49 case actionStmt: | 52 case actionStmt: |
50 return describeStmt(o, path) | 53 return describeStmt(o, path) |
51 | 54 |
52 case actionUnknown: | 55 case actionUnknown: |
53 return &describeUnknownResult{path[0]}, nil | 56 return &describeUnknownResult{path[0]}, nil |
54 | 57 |
55 default: | 58 default: |
56 panic(action) // unreachable | 59 panic(action) // unreachable |
57 } | 60 } |
58 } | 61 } |
59 | 62 |
60 type describeUnknownResult struct { | 63 type describeUnknownResult struct { |
61 node ast.Node | 64 node ast.Node |
62 } | 65 } |
63 | 66 |
64 func (r *describeUnknownResult) display(o *oracle) { | 67 func (r *describeUnknownResult) display(printf printfFunc) { |
65 // Nothing much to say about misc syntax. | 68 // Nothing much to say about misc syntax. |
66 » o.printf(r.node, "%s", importer.NodeDescription(r.node)) | 69 » printf(r.node, "%s", importer.NodeDescription(r.node)) |
| 70 } |
| 71 |
| 72 func (r *describeUnknownResult) toJSON(res *json.Result, fset *token.FileSet) { |
| 73 » res.Describe = &json.Describe{ |
| 74 » » Desc: importer.NodeDescription(r.node), |
| 75 » » Pos: fset.Position(r.node.Pos()).String(), |
| 76 » } |
67 } | 77 } |
68 | 78 |
69 type action int | 79 type action int |
70 | 80 |
71 const ( | 81 const ( |
72 actionUnknown action = iota // None of the below | 82 actionUnknown action = iota // None of the below |
73 actionExpr // FuncDecl, true Expr or Ident(types.{Const
,Var}) | 83 actionExpr // FuncDecl, true Expr or Ident(types.{Const
,Var}) |
74 actionType // type Expr or Ident(types.TypeName). | 84 actionType // type Expr or Ident(types.TypeName). |
75 actionStmt // Stmt or Ident(types.Label) | 85 actionStmt // Stmt or Ident(types.Label) |
76 actionPackage // Ident(types.Package) or ImportSpec | 86 actionPackage // Ident(types.Package) or ImportSpec |
(...skipping 15 matching lines...) Expand all Loading... |
92 // doesn't crash. | 102 // doesn't crash. |
93 | 103 |
94 // TODO(adonovan): audit for ParenExpr safety, esp. since we | 104 // TODO(adonovan): audit for ParenExpr safety, esp. since we |
95 // traverse up and down. | 105 // traverse up and down. |
96 | 106 |
97 // TODO(adonovan): if the users selects the "." in | 107 // TODO(adonovan): if the users selects the "." in |
98 // "fmt.Fprintf()", they'll get an ambiguous selection error; | 108 // "fmt.Fprintf()", they'll get an ambiguous selection error; |
99 // we won't even reach here. Can we do better? | 109 // we won't even reach here. Can we do better? |
100 | 110 |
101 // TODO(adonovan): describing a field within 'type T struct {...}' | 111 // TODO(adonovan): describing a field within 'type T struct {...}' |
102 » // describes the (anonymous) struct type and concludes "no methods". Fi
x. | 112 » // describes the (anonymous) struct type and concludes "no methods". |
| 113 » // We should ascend to the enclosing type decl, if any. |
103 | 114 |
104 for len(path) > 0 { | 115 for len(path) > 0 { |
105 switch n := path[0].(type) { | 116 switch n := path[0].(type) { |
106 case *ast.GenDecl: | 117 case *ast.GenDecl: |
107 if len(n.Specs) == 1 { | 118 if len(n.Specs) == 1 { |
108 // Descend to sole {Import,Type,Value}Spec child
. | 119 // Descend to sole {Import,Type,Value}Spec child
. |
109 path = append([]ast.Node{n.Specs[0]}, path...) | 120 path = append([]ast.Node{n.Specs[0]}, path...) |
110 continue | 121 continue |
111 } | 122 } |
112 return path, actionUnknown // uninteresting | 123 return path, actionUnknown // uninteresting |
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 return nil, o.errorf(n, "multiple value specification") | 348 return nil, o.errorf(n, "multiple value specification") |
338 case ast.Expr: | 349 case ast.Expr: |
339 expr = n | 350 expr = n |
340 default: | 351 default: |
341 // Is this reachable? | 352 // Is this reachable? |
342 return nil, o.errorf(n, "unexpected AST for expr: %T", n) | 353 return nil, o.errorf(n, "unexpected AST for expr: %T", n) |
343 } | 354 } |
344 | 355 |
345 // From this point on, we cannot fail with an error. | 356 // From this point on, we cannot fail with an error. |
346 // Failure to run the pointer analysis will be reported later. | 357 // Failure to run the pointer analysis will be reported later. |
| 358 // |
| 359 // Our disposition to pointer analysis may be one of the following: |
| 360 // - ok: ssa.Value was const or func. |
| 361 // - error: no ssa.Value for expr (e.g. trivially dead code) |
| 362 // - ok: ssa.Value is non-pointerlike |
| 363 // - error: no Pointer for ssa.Value (e.g. analytically unreachable) |
| 364 // - ok: Pointer has empty points-to set |
| 365 // - ok: Pointer has non-empty points-to set |
| 366 // ptaErr is non-nil only in the "error:" cases. |
347 | 367 |
348 var value ssa.Value | 368 var value ssa.Value |
349 var ptaErr error | 369 var ptaErr error |
350 var obj types.Object | 370 var obj types.Object |
351 | 371 |
352 // Determine the ssa.Value for the expression. | 372 // Determine the ssa.Value for the expression. |
353 if id, ok := expr.(*ast.Ident); ok { | 373 if id, ok := expr.(*ast.Ident); ok { |
354 // def/ref of func/var/const object | 374 // def/ref of func/var/const object |
355 obj = o.queryPkgInfo.ObjectOf(id) | 375 obj = o.queryPkgInfo.ObjectOf(id) |
356 value, ptaErr = ssaValueForIdent(o, obj, path) | 376 value, ptaErr = ssaValueForIdent(o, obj, path) |
357 } else { | 377 } else { |
358 // any other expression | 378 // any other expression |
359 if o.queryPkgInfo.ValueOf(expr) == nil { // non-constant? | 379 if o.queryPkgInfo.ValueOf(expr) == nil { // non-constant? |
360 value, ptaErr = ssaValueForExpr(o, path) | 380 value, ptaErr = ssaValueForExpr(o, path) |
361 } | 381 } |
362 } | 382 } |
363 | 383 |
364 // Don't run pointer analysis on non-pointerlike types. | 384 // Don't run pointer analysis on non-pointerlike types. |
365 if value != nil && !pointer.CanPoint(value.Type()) { | 385 if value != nil && !pointer.CanPoint(value.Type()) { |
366 value = nil | 386 value = nil |
367 } | 387 } |
368 | 388 |
369 // Run pointer analysis of the selected SSA value. | 389 // Run pointer analysis of the selected SSA value. |
370 » var ptrs []pointer.Pointer | 390 » var ptrs []pointerResult |
371 if value != nil { | 391 if value != nil { |
372 buildSSA(o) | 392 buildSSA(o) |
373 | 393 |
374 o.config.QueryValues = map[ssa.Value][]pointer.Pointer{value: ni
l} | 394 o.config.QueryValues = map[ssa.Value][]pointer.Pointer{value: ni
l} |
375 ptrAnalysis(o) | 395 ptrAnalysis(o) |
376 » » ptrs = o.config.QueryValues[value] | 396 |
| 397 » » // Combine the PT sets from all contexts. |
| 398 » » pointers := o.config.QueryValues[value] |
| 399 » » if pointers == nil { |
| 400 » » » ptaErr = fmt.Errorf("PTA did not encounter this expressi
on (dead code?)") |
| 401 » » } |
| 402 » » pts := pointer.PointsToCombined(pointers) |
| 403 |
| 404 » » if _, ok := value.Type().Underlying().(*types.Interface); ok { |
| 405 » » » // Show concrete types for interface expression. |
| 406 » » » if concs := pts.ConcreteTypes(); concs.Len() > 0 { |
| 407 » » » » concs.Iterate(func(conc types.Type, pta interfac
e{}) { |
| 408 » » » » » combined := pointer.PointsToCombined(pta
.([]pointer.Pointer)) |
| 409 » » » » » labels := combined.Labels() |
| 410 » » » » » sort.Sort(byPosAndString(labels)) // to
ensure determinism |
| 411 » » » » » ptrs = append(ptrs, pointerResult{conc,
labels}) |
| 412 » » » » }) |
| 413 » » » } |
| 414 » » } else { |
| 415 » » » // Show labels for other expressions. |
| 416 » » » labels := pts.Labels() |
| 417 » » » sort.Sort(byPosAndString(labels)) // to ensure determini
sm |
| 418 » » » ptrs = append(ptrs, pointerResult{value.Type(), labels}) |
| 419 » » } |
377 } | 420 } |
| 421 sort.Sort(byTypeString(ptrs)) // to ensure determinism |
| 422 |
| 423 typ := o.queryPkgInfo.TypeOf(expr) |
| 424 constVal := o.queryPkgInfo.ValueOf(expr) |
378 | 425 |
379 return &describeValueResult{ | 426 return &describeValueResult{ |
380 » » expr: expr, | 427 » » expr: expr, |
381 » » obj: obj, | 428 » » typ: typ, |
382 » » value: value, | 429 » » constVal: constVal, |
383 » » ptaErr: ptaErr, | 430 » » obj: obj, |
384 » » ptrs: ptrs, | 431 » » ptaErr: ptaErr, |
| 432 » » ptrs: ptrs, |
385 }, nil | 433 }, nil |
386 } | 434 } |
387 | 435 |
388 type describeValueResult struct { | 436 type pointerResult struct { |
389 » expr ast.Expr // query node | 437 » typ types.Type // type of the pointer (always concrete) |
390 » obj types.Object // var/func/const object, if expr was Ident | 438 » labels []*pointer.Label |
391 » value ssa.Value // ssa.Value for pointer analysis query | |
392 » ptaErr error // explanation of why we couldn't run pointer a
nalysis | |
393 » ptrs []pointer.Pointer // result of pointer analysis query | |
394 } | 439 } |
395 | 440 |
396 func (r *describeValueResult) display(o *oracle) { | 441 type describeValueResult struct { |
| 442 » expr ast.Expr // query node |
| 443 » typ types.Type // type of expression |
| 444 » constVal exact.Value // value of expression, if constant |
| 445 » obj types.Object // var/func/const object, if expr was Ident |
| 446 » ptaErr error // reason why pointer analysis couldn't be run,
or failed |
| 447 » ptrs []pointerResult // pointer info (typ is concrete => len==1) |
| 448 } |
| 449 |
| 450 func (r *describeValueResult) display(printf printfFunc) { |
397 suffix := "" | 451 suffix := "" |
398 » if val := o.queryPkgInfo.ValueOf(r.expr); val != nil { | 452 » if r.constVal != nil { |
399 » » suffix = fmt.Sprintf(" of constant value %s", val) | 453 » » suffix = fmt.Sprintf(" of constant value %s", r.constVal) |
400 } | 454 } |
401 | 455 |
402 // Describe the expression. | 456 // Describe the expression. |
403 if r.obj != nil { | 457 if r.obj != nil { |
404 if r.obj.Pos() == r.expr.Pos() { | 458 if r.obj.Pos() == r.expr.Pos() { |
405 // defining ident | 459 // defining ident |
406 » » » o.printf(r.expr, "definition of %s%s", r.obj, suffix) | 460 » » » printf(r.expr, "definition of %s%s", r.obj, suffix) |
407 } else { | 461 } else { |
408 // referring ident | 462 // referring ident |
409 » » » o.printf(r.expr, "reference to %s%s", r.obj, suffix) | 463 » » » printf(r.expr, "reference to %s%s", r.obj, suffix) |
410 if def := r.obj.Pos(); def != token.NoPos { | 464 if def := r.obj.Pos(); def != token.NoPos { |
411 » » » » o.printf(def, "defined here") | 465 » » » » printf(def, "defined here") |
412 } | 466 } |
413 } | 467 } |
414 } else { | 468 } else { |
415 desc := importer.NodeDescription(r.expr) | 469 desc := importer.NodeDescription(r.expr) |
416 if suffix != "" { | 470 if suffix != "" { |
417 // constant expression | 471 // constant expression |
418 » » » o.printf(r.expr, "%s%s", desc, suffix) | 472 » » » printf(r.expr, "%s%s", desc, suffix) |
419 } else { | 473 } else { |
420 // non-constant expression | 474 // non-constant expression |
421 » » » o.printf(r.expr, "%s of type %s", desc, o.queryPkgInfo.T
ypeOf(r.expr)) | 475 » » » printf(r.expr, "%s of type %s", desc, r.typ) |
422 } | 476 } |
423 } | 477 } |
424 | 478 |
425 » if r.value == nil { | 479 » // pointer analysis could not be run |
426 » » // pointer analysis was not run | 480 » if r.ptaErr != nil { |
427 » » if r.ptaErr != nil { | 481 » » printf(r.expr, "no points-to information: %s", r.ptaErr) |
428 » » » o.printf(r.expr, "no pointer analysis: %s", r.ptaErr) | |
429 » » } | |
430 return | 482 return |
431 } | 483 } |
432 | 484 |
433 if r.ptrs == nil { | 485 if r.ptrs == nil { |
434 » » o.printf(r.expr, "pointer analysis did not analyze this expressi
on (dead code?)") | 486 » » return // PTA was not invoked (not an error) |
435 » » return | |
436 } | 487 } |
437 | 488 |
438 // Display the results of pointer analysis. | 489 // Display the results of pointer analysis. |
439 | 490 » if _, ok := r.typ.Underlying().(*types.Interface); ok { |
440 » // Combine the PT sets from all contexts. | |
441 » pts := pointer.PointsToCombined(r.ptrs) | |
442 | |
443 » // Report which make(chan) labels the query's channel can alias. | |
444 » if _, ok := r.value.Type().Underlying().(*types.Interface); ok { | |
445 // Show concrete types for interface expression. | 491 // Show concrete types for interface expression. |
446 » » if concs := pts.ConcreteTypes(); concs.Len() > 0 { | 492 » » if len(r.ptrs) > 0 { |
447 » » » o.printf(o, "interface may contain these concrete types:
") | 493 » » » printf(false, "interface may contain these concrete type
s:") |
448 » » » // TODO(adonovan): must sort to ensure deterministic tes
t behaviour. | 494 » » » for _, ptr := range r.ptrs { |
449 » » » concs.Iterate(func(conc types.Type, ptrs interface{}) { | |
450 var obj types.Object | 495 var obj types.Object |
451 » » » » if nt, ok := deref(conc).(*types.Named); ok { | 496 » » » » if nt, ok := deref(ptr.typ).(*types.Named); ok { |
452 obj = nt.Obj() | 497 obj = nt.Obj() |
453 } | 498 } |
454 | 499 » » » » if len(ptr.labels) > 0 { |
455 » » » » pts := pointer.PointsToCombined(ptrs.([]pointer.
Pointer)) | 500 » » » » » printf(obj, "\t%s, may point to:", ptr.t
yp) |
456 » » » » if labels := pts.Labels(); len(labels) > 0 { | 501 » » » » » printLabels(printf, ptr.labels, "\t\t") |
457 » » » » » o.printf(obj, "\t%s, may point to:", con
c) | |
458 » » » » » printLabels(o, labels, "\t\t") | |
459 } else { | 502 } else { |
460 » » » » » o.printf(obj, "\t%s", conc) | 503 » » » » » printf(obj, "\t%s", ptr.typ) |
461 } | 504 } |
462 » » » }) | 505 » » » } |
463 } else { | 506 } else { |
464 » » » o.printf(o, "interface cannot contain any concrete value
s.") | 507 » » » printf(false, "interface cannot contain any concrete val
ues.") |
465 } | 508 } |
466 } else { | 509 } else { |
467 // Show labels for other expressions. | 510 // Show labels for other expressions. |
468 » » if labels := pts.Labels(); len(labels) > 0 { | 511 » » if ptr := r.ptrs[0]; len(ptr.labels) > 0 { |
469 » » » o.printf(o, "value may point to these labels:") | 512 » » » printf(false, "value may point to these labels:") |
470 » » » printLabels(o, labels, "\t") | 513 » » » printLabels(printf, ptr.labels, "\t") |
471 } else { | 514 } else { |
472 » » » o.printf(o, "value cannot point to anything.") | 515 » » » printf(false, "value cannot point to anything.") |
473 } | 516 } |
474 } | 517 } |
475 } | 518 } |
476 | 519 |
| 520 func (r *describeValueResult) toJSON(res *json.Result, fset *token.FileSet) { |
| 521 var value, objpos, ptaerr string |
| 522 if r.constVal != nil { |
| 523 value = r.constVal.String() |
| 524 } |
| 525 if r.obj != nil { |
| 526 objpos = fset.Position(r.obj.Pos()).String() |
| 527 } |
| 528 if r.ptaErr != nil { |
| 529 ptaerr = r.ptaErr.Error() |
| 530 } |
| 531 |
| 532 var pts []*json.DescribePointer |
| 533 for _, ptr := range r.ptrs { |
| 534 var namePos string |
| 535 if nt, ok := deref(ptr.typ).(*types.Named); ok { |
| 536 namePos = fset.Position(nt.Obj().Pos()).String() |
| 537 } |
| 538 var labels []json.DescribePTALabel |
| 539 for _, l := range ptr.labels { |
| 540 labels = append(labels, json.DescribePTALabel{ |
| 541 Pos: fset.Position(l.Pos()).String(), |
| 542 Desc: l.String(), |
| 543 }) |
| 544 } |
| 545 pts = append(pts, &json.DescribePointer{ |
| 546 Type: ptr.typ.String(), |
| 547 NamePos: namePos, |
| 548 Labels: labels, |
| 549 }) |
| 550 } |
| 551 |
| 552 res.Describe = &json.Describe{ |
| 553 Desc: importer.NodeDescription(r.expr), |
| 554 Pos: fset.Position(r.expr.Pos()).String(), |
| 555 Detail: "value", |
| 556 Value: &json.DescribeValue{ |
| 557 Type: r.typ.String(), |
| 558 Value: value, |
| 559 ObjPos: objpos, |
| 560 PTAErr: ptaerr, |
| 561 PTS: pts, |
| 562 }, |
| 563 } |
| 564 } |
| 565 |
| 566 type byTypeString []pointerResult |
| 567 |
| 568 func (a byTypeString) Len() int { return len(a) } |
| 569 func (a byTypeString) Less(i, j int) bool { return a[i].typ.String() < a[j].typ.
String() } |
| 570 func (a byTypeString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } |
| 571 |
477 type byPosAndString []*pointer.Label | 572 type byPosAndString []*pointer.Label |
478 | 573 |
479 func (a byPosAndString) Len() int { return len(a) } | 574 func (a byPosAndString) Len() int { return len(a) } |
480 func (a byPosAndString) Less(i, j int) bool { | 575 func (a byPosAndString) Less(i, j int) bool { |
481 cmp := a[i].Pos() - a[j].Pos() | 576 cmp := a[i].Pos() - a[j].Pos() |
482 return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String()) | 577 return cmp < 0 || (cmp == 0 && a[i].String() < a[j].String()) |
483 } | 578 } |
484 func (a byPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } | 579 func (a byPosAndString) Swap(i, j int) { a[i], a[j] = a[j], a[i] } |
485 | 580 |
486 func printLabels(o *oracle, labels []*pointer.Label, prefix string) { | 581 func printLabels(printf printfFunc, labels []*pointer.Label, prefix string) { |
487 » // Sort, to ensure deterministic test behaviour. | |
488 » sort.Sort(byPosAndString(labels)) | |
489 // TODO(adonovan): due to context-sensitivity, many of these | 582 // TODO(adonovan): due to context-sensitivity, many of these |
490 // labels may differ only by context, which isn't apparent. | 583 // labels may differ only by context, which isn't apparent. |
491 for _, label := range labels { | 584 for _, label := range labels { |
492 » » o.printf(label, "%s%s", prefix, label) | 585 » » printf(label, "%s%s", prefix, label) |
493 } | 586 } |
494 } | 587 } |
495 | 588 |
496 // ---- TYPE ------------------------------------------------------------ | 589 // ---- TYPE ------------------------------------------------------------ |
497 | 590 |
498 func describeType(o *oracle, path []ast.Node) (*describeTypeResult, error) { | 591 func describeType(o *oracle, path []ast.Node) (*describeTypeResult, error) { |
499 var description string | 592 var description string |
500 var t types.Type | 593 var t types.Type |
501 switch n := path[0].(type) { | 594 switch n := path[0].(type) { |
502 case *ast.Ident: | 595 case *ast.Ident: |
(...skipping 13 matching lines...) Expand all Loading... |
516 | 609 |
517 case ast.Expr: | 610 case ast.Expr: |
518 t = o.queryPkgInfo.TypeOf(n) | 611 t = o.queryPkgInfo.TypeOf(n) |
519 description = "type " + t.String() | 612 description = "type " + t.String() |
520 | 613 |
521 default: | 614 default: |
522 // Unreachable? | 615 // Unreachable? |
523 return nil, o.errorf(n, "unexpected AST for type: %T", n) | 616 return nil, o.errorf(n, "unexpected AST for type: %T", n) |
524 } | 617 } |
525 | 618 |
526 » return &describeTypeResult{path[0], description, t}, nil | 619 » return &describeTypeResult{ |
| 620 » » node: path[0], |
| 621 » » description: description, |
| 622 » » typ: t, |
| 623 » » methods: accessibleMethods(t, o.queryPkgInfo.Pkg), |
| 624 » }, nil |
527 } | 625 } |
528 | 626 |
529 type describeTypeResult struct { | 627 type describeTypeResult struct { |
530 node ast.Node | 628 node ast.Node |
531 description string | 629 description string |
532 typ types.Type | 630 typ types.Type |
| 631 methods []*types.Selection |
533 } | 632 } |
534 | 633 |
535 func (r *describeTypeResult) display(o *oracle) { | 634 func (r *describeTypeResult) display(printf printfFunc) { |
536 » o.printf(r.node, "%s", r.description) | 635 » printf(r.node, "%s", r.description) |
537 | 636 |
538 // Show the underlying type for a reference to a named type. | 637 // Show the underlying type for a reference to a named type. |
539 if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos()
{ | 638 if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos()
{ |
540 » » o.printf(nt.Obj(), "defined as %s", nt.Underlying()) | 639 » » printf(nt.Obj(), "defined as %s", nt.Underlying()) |
541 } | 640 } |
542 | 641 |
543 // Print the method set, if the type kind is capable of bearing methods. | 642 // Print the method set, if the type kind is capable of bearing methods. |
544 switch r.typ.(type) { | 643 switch r.typ.(type) { |
545 case *types.Interface, *types.Struct, *types.Named: | 644 case *types.Interface, *types.Struct, *types.Named: |
546 » » // TODO(adonovan): don't show unexported methods if | 645 » » if len(r.methods) > 0 { |
547 » » // r.typ belongs to a package other than the query | 646 » » » printf(r.node, "Method set:") |
548 » » // package. | 647 » » » for _, meth := range r.methods { |
549 » » if m := ssa.IntuitiveMethodSet(r.typ); m != nil { | 648 » » » » printf(meth.Obj(), "\t%s", meth) |
550 » » » o.printf(r.node, "Method set:") | |
551 » » » for _, meth := range m { | |
552 » » » » o.printf(meth.Obj(), "\t%s", meth) | |
553 } | 649 } |
554 } else { | 650 } else { |
555 » » » o.printf(r.node, "No methods.") | 651 » » » printf(r.node, "No methods.") |
556 } | 652 } |
557 } | 653 } |
558 } | 654 } |
559 | 655 |
| 656 func (r *describeTypeResult) toJSON(res *json.Result, fset *token.FileSet) { |
| 657 var namePos, nameDef string |
| 658 if nt, ok := r.typ.(*types.Named); ok { |
| 659 namePos = fset.Position(nt.Obj().Pos()).String() |
| 660 nameDef = nt.Underlying().String() |
| 661 } |
| 662 res.Describe = &json.Describe{ |
| 663 Desc: r.description, |
| 664 Pos: fset.Position(r.node.Pos()).String(), |
| 665 Detail: "type", |
| 666 Type: &json.DescribeType{ |
| 667 Type: r.typ.String(), |
| 668 NamePos: namePos, |
| 669 NameDef: nameDef, |
| 670 Methods: methodsToJSON(r.methods, fset), |
| 671 }, |
| 672 } |
| 673 } |
| 674 |
560 // ---- PACKAGE ------------------------------------------------------------ | 675 // ---- PACKAGE ------------------------------------------------------------ |
561 | 676 |
562 func describePackage(o *oracle, path []ast.Node) (*describePackageResult, error)
{ | 677 func describePackage(o *oracle, path []ast.Node) (*describePackageResult, error)
{ |
563 var description string | 678 var description string |
564 var importPath string | 679 var importPath string |
565 switch n := path[0].(type) { | 680 switch n := path[0].(type) { |
566 case *ast.ImportSpec: | 681 case *ast.ImportSpec: |
567 // importPath = o.queryPkgInfo.ObjectOf(n.Name).(*types.Package)
.Path() | 682 // importPath = o.queryPkgInfo.ObjectOf(n.Name).(*types.Package)
.Path() |
568 // description = "import of package " + importPath | 683 // description = "import of package " + importPath |
569 // TODO(gri): o.queryPkgInfo.ObjectOf(n.Name) may be nil. | 684 // TODO(gri): o.queryPkgInfo.ObjectOf(n.Name) may be nil. |
(...skipping 12 matching lines...) Expand all Loading... |
582 if importPath == "" { | 697 if importPath == "" { |
583 // TODO(gri): fix. | 698 // TODO(gri): fix. |
584 return nil, o.errorf(n, "types.Package.Path() returned \
"\"\n") | 699 return nil, o.errorf(n, "types.Package.Path() returned \
"\"\n") |
585 } | 700 } |
586 | 701 |
587 default: | 702 default: |
588 // Unreachable? | 703 // Unreachable? |
589 return nil, o.errorf(n, "unexpected AST for package: %T", n) | 704 return nil, o.errorf(n, "unexpected AST for package: %T", n) |
590 } | 705 } |
591 | 706 |
592 » pkg := o.prog.PackagesByPath[importPath] | 707 » var members []*describeMember |
| 708 » // NB: package "unsafe" has no object. |
| 709 » if pkg := o.prog.PackagesByPath[importPath]; pkg != nil { |
| 710 » » // Compute set of exported package members in lexicographic orde
r. |
| 711 » » var names []string |
| 712 » » for name := range pkg.Members { |
| 713 » » » if pkg.Object == o.queryPkgInfo.Pkg || ast.IsExported(na
me) { |
| 714 » » » » names = append(names, name) |
| 715 » » » } |
| 716 » » } |
| 717 » » sort.Strings(names) |
593 | 718 |
594 » return &describePackageResult{path[0], description, pkg}, nil | 719 » » // Enumerate the package members. |
| 720 » » for _, name := range names { |
| 721 » » » mem := pkg.Members[name] |
| 722 » » » var methods []*types.Selection |
| 723 » » » if mem, ok := mem.(*ssa.Type); ok { |
| 724 » » » » methods = accessibleMethods(mem.Type(), o.queryP
kgInfo.Pkg) |
| 725 » » » } |
| 726 » » » members = append(members, &describeMember{ |
| 727 » » » » mem, |
| 728 » » » » methods, |
| 729 » » » }) |
| 730 » » } |
| 731 » } |
| 732 |
| 733 » return &describePackageResult{o.prog.Fset, path[0], description, importP
ath, members}, nil |
595 } | 734 } |
596 | 735 |
597 type describePackageResult struct { | 736 type describePackageResult struct { |
| 737 fset *token.FileSet |
598 node ast.Node | 738 node ast.Node |
599 description string | 739 description string |
600 » pkg *ssa.Package | 740 » path string |
| 741 » members []*describeMember // in lexicographic name order |
601 } | 742 } |
602 | 743 |
603 func (r *describePackageResult) display(o *oracle) { | 744 type describeMember struct { |
604 » o.printf(r.node, "%s", r.description) | 745 » mem ssa.Member |
605 » // TODO(adonovan): factor this into a testable utility function. | 746 » methods []*types.Selection // in types.MethodSet order |
606 » if p := r.pkg; p != nil { | 747 } |
607 » » samePkg := p.Object == o.queryPkgInfo.Pkg | |
608 | 748 |
609 » » // Describe exported package members, in lexicographic order. | 749 func (r *describePackageResult) display(printf printfFunc) { |
| 750 » printf(r.node, "%s", r.description) |
610 | 751 |
611 » » // Compute max width of name "column". | 752 » // Compute max width of name "column". |
612 » » var names []string | 753 » maxname := 0 |
613 » » maxname := 0 | 754 » for _, mem := range r.members { |
614 » » for name := range p.Members { | 755 » » if l := len(mem.mem.Name()); l > maxname { |
615 » » » if samePkg || ast.IsExported(name) { | 756 » » » maxname = l |
616 » » » » if l := len(name); l > maxname { | |
617 » » » » » maxname = l | |
618 » » » » } | |
619 » » » » names = append(names, name) | |
620 » » » } | |
621 } | 757 } |
| 758 } |
622 | 759 |
623 » » sort.Strings(names) | 760 » for _, mem := range r.members { |
624 | 761 » » printf(mem.mem, "\t%s", formatMember(mem.mem, maxname)) |
625 » » // Print the members. | 762 » » for _, meth := range mem.methods { |
626 » » for _, name := range names { | 763 » » » printf(meth.Obj(), "\t\t%s", meth) |
627 » » » mem := p.Members[name] | |
628 » » » o.printf(mem, "%s", formatMember(mem, maxname)) | |
629 » » » // Print method set. | |
630 » » » if mem, ok := mem.(*ssa.Type); ok { | |
631 » » » » for _, meth := range ssa.IntuitiveMethodSet(mem.
Type()) { | |
632 » » » » » if samePkg || ast.IsExported(meth.Obj().
Name()) { | |
633 » » » » » » o.printf(meth.Obj(), "\t\t%s", m
eth) | |
634 » » » » » } | |
635 » » » » } | |
636 » » » } | |
637 } | 764 } |
638 } | 765 } |
639 } | 766 } |
640 | 767 |
641 func formatMember(mem ssa.Member, maxname int) string { | 768 func formatMember(mem ssa.Member, maxname int) string { |
642 var buf bytes.Buffer | 769 var buf bytes.Buffer |
643 » fmt.Fprintf(&buf, "\t%-5s %-*s", mem.Token(), maxname, mem.Name()) | 770 » fmt.Fprintf(&buf, "%-5s %-*s", mem.Token(), maxname, mem.Name()) |
644 switch mem := mem.(type) { | 771 switch mem := mem.(type) { |
645 case *ssa.NamedConst: | 772 case *ssa.NamedConst: |
646 fmt.Fprintf(&buf, " %s = %s", mem.Type(), mem.Value.Name()) | 773 fmt.Fprintf(&buf, " %s = %s", mem.Type(), mem.Value.Name()) |
647 | 774 |
648 case *ssa.Function: | 775 case *ssa.Function: |
649 fmt.Fprintf(&buf, " %s", mem.Type()) | 776 fmt.Fprintf(&buf, " %s", mem.Type()) |
650 | 777 |
651 case *ssa.Type: | 778 case *ssa.Type: |
652 // Abbreviate long aggregate type names. | 779 // Abbreviate long aggregate type names. |
653 var abbrev string | 780 var abbrev string |
(...skipping 12 matching lines...) Expand all Loading... |
666 } else { | 793 } else { |
667 fmt.Fprintf(&buf, " %s", abbrev) | 794 fmt.Fprintf(&buf, " %s", abbrev) |
668 } | 795 } |
669 | 796 |
670 case *ssa.Global: | 797 case *ssa.Global: |
671 fmt.Fprintf(&buf, " %s", deref(mem.Type())) | 798 fmt.Fprintf(&buf, " %s", deref(mem.Type())) |
672 } | 799 } |
673 return buf.String() | 800 return buf.String() |
674 } | 801 } |
675 | 802 |
| 803 func (r *describePackageResult) toJSON(res *json.Result, fset *token.FileSet) { |
| 804 var members []*json.DescribeMember |
| 805 for _, mem := range r.members { |
| 806 typ := mem.mem.Type() |
| 807 var val string |
| 808 switch mem := mem.mem.(type) { |
| 809 case *ssa.NamedConst: |
| 810 val = mem.Value.Value.String() |
| 811 case *ssa.Type: |
| 812 typ = typ.Underlying() |
| 813 case *ssa.Global: |
| 814 typ = deref(typ) |
| 815 } |
| 816 members = append(members, &json.DescribeMember{ |
| 817 Name: mem.mem.Name(), |
| 818 Type: typ.String(), |
| 819 Value: val, |
| 820 Pos: fset.Position(mem.mem.Pos()).String(), |
| 821 Kind: mem.mem.Token().String(), |
| 822 Methods: methodsToJSON(mem.methods, fset), |
| 823 }) |
| 824 } |
| 825 res.Describe = &json.Describe{ |
| 826 Desc: r.description, |
| 827 Pos: fset.Position(r.node.Pos()).String(), |
| 828 Detail: "package", |
| 829 Package: &json.DescribePackage{ |
| 830 Path: r.path, |
| 831 Members: members, |
| 832 }, |
| 833 } |
| 834 } |
| 835 |
676 // ---- STATEMENT ------------------------------------------------------------ | 836 // ---- STATEMENT ------------------------------------------------------------ |
677 | 837 |
678 func describeStmt(o *oracle, path []ast.Node) (*describeStmtResult, error) { | 838 func describeStmt(o *oracle, path []ast.Node) (*describeStmtResult, error) { |
679 var description string | 839 var description string |
680 switch n := path[0].(type) { | 840 switch n := path[0].(type) { |
681 case *ast.Ident: | 841 case *ast.Ident: |
682 if o.queryPkgInfo.ObjectOf(n).Pos() == n.Pos() { | 842 if o.queryPkgInfo.ObjectOf(n).Pos() == n.Pos() { |
683 description = "labelled statement" | 843 description = "labelled statement" |
684 } else { | 844 } else { |
685 description = "reference to labelled statement" | 845 description = "reference to labelled statement" |
686 } | 846 } |
687 | 847 |
688 default: | 848 default: |
689 // Nothing much to say about statements. | 849 // Nothing much to say about statements. |
690 description = importer.NodeDescription(n) | 850 description = importer.NodeDescription(n) |
691 } | 851 } |
692 » return &describeStmtResult{path[0], description}, nil | 852 » return &describeStmtResult{o.prog.Fset, path[0], description}, nil |
693 } | 853 } |
694 | 854 |
695 type describeStmtResult struct { | 855 type describeStmtResult struct { |
| 856 fset *token.FileSet |
696 node ast.Node | 857 node ast.Node |
697 description string | 858 description string |
698 } | 859 } |
699 | 860 |
700 func (r *describeStmtResult) display(o *oracle) { | 861 func (r *describeStmtResult) display(printf printfFunc) { |
701 » o.printf(r.node, "%s", r.description) | 862 » printf(r.node, "%s", r.description) |
| 863 } |
| 864 |
| 865 func (r *describeStmtResult) toJSON(res *json.Result, fset *token.FileSet) { |
| 866 » res.Describe = &json.Describe{ |
| 867 » » Desc: r.description, |
| 868 » » Pos: fset.Position(r.node.Pos()).String(), |
| 869 » » Detail: "unknown", |
| 870 » } |
702 } | 871 } |
703 | 872 |
704 // ------------------- Utilities ------------------- | 873 // ------------------- Utilities ------------------- |
705 | 874 |
706 // pathToString returns a string containing the concrete types of the | 875 // pathToString returns a string containing the concrete types of the |
707 // nodes in path. | 876 // nodes in path. |
708 func pathToString2(path []ast.Node) string { | 877 func pathToString2(path []ast.Node) string { |
709 var buf bytes.Buffer | 878 var buf bytes.Buffer |
710 fmt.Fprint(&buf, "[") | 879 fmt.Fprint(&buf, "[") |
711 for i, n := range path { | 880 for i, n := range path { |
712 if i > 0 { | 881 if i > 0 { |
713 fmt.Fprint(&buf, " ") | 882 fmt.Fprint(&buf, " ") |
714 } | 883 } |
715 fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast.
")) | 884 fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast.
")) |
716 } | 885 } |
717 fmt.Fprint(&buf, "]") | 886 fmt.Fprint(&buf, "]") |
718 return buf.String() | 887 return buf.String() |
719 } | 888 } |
| 889 |
| 890 func accessibleMethods(t types.Type, from *types.Package) []*types.Selection { |
| 891 var methods []*types.Selection |
| 892 for _, meth := range ssa.IntuitiveMethodSet(t) { |
| 893 if isAccessibleFrom(meth.Obj(), from) { |
| 894 methods = append(methods, meth) |
| 895 } |
| 896 } |
| 897 return methods |
| 898 } |
| 899 |
| 900 func isAccessibleFrom(obj types.Object, pkg *types.Package) bool { |
| 901 return ast.IsExported(obj.Name()) || obj.Pkg() == pkg |
| 902 } |
| 903 |
| 904 func methodsToJSON(methods []*types.Selection, fset *token.FileSet) []json.Descr
ibeMethod { |
| 905 var jmethods []json.DescribeMethod |
| 906 for _, meth := range methods { |
| 907 jmethods = append(jmethods, json.DescribeMethod{ |
| 908 Name: meth.String(), |
| 909 Pos: fset.Position(meth.Obj().Pos()).String(), |
| 910 }) |
| 911 } |
| 912 return jmethods |
| 913 } |
OLD | NEW |