mirror of
https://github.com/golang/go
synced 2024-11-19 02:04:42 -07:00
112 lines
2.8 KiB
Go
112 lines
2.8 KiB
Go
|
package source
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"go/ast"
|
||
|
"go/token"
|
||
|
"go/types"
|
||
|
)
|
||
|
|
||
|
// indexExprAtPos returns the index of the expression containing pos.
|
||
|
func indexExprAtPos(pos token.Pos, args []ast.Expr) int {
|
||
|
for i, expr := range args {
|
||
|
if expr.Pos() <= pos && pos <= expr.End() {
|
||
|
return i
|
||
|
}
|
||
|
}
|
||
|
return len(args)
|
||
|
}
|
||
|
|
||
|
func exprAtPos(pos token.Pos, args []ast.Expr) ast.Expr {
|
||
|
for _, expr := range args {
|
||
|
if expr.Pos() <= pos && pos <= expr.End() {
|
||
|
return expr
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// fieldSelections returns the set of fields that can
|
||
|
// be selected from a value of type T.
|
||
|
func fieldSelections(T types.Type) (fields []*types.Var) {
|
||
|
// TODO(adonovan): this algorithm doesn't exclude ambiguous
|
||
|
// selections that match more than one field/method.
|
||
|
// types.NewSelectionSet should do that for us.
|
||
|
|
||
|
seen := make(map[types.Type]bool) // for termination on recursive types
|
||
|
var visit func(T types.Type)
|
||
|
visit = func(T types.Type) {
|
||
|
if !seen[T] {
|
||
|
seen[T] = true
|
||
|
if T, ok := deref(T).Underlying().(*types.Struct); ok {
|
||
|
for i := 0; i < T.NumFields(); i++ {
|
||
|
f := T.Field(i)
|
||
|
fields = append(fields, f)
|
||
|
if f.Anonymous() {
|
||
|
visit(f.Type())
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
visit(T)
|
||
|
|
||
|
return fields
|
||
|
}
|
||
|
|
||
|
// resolveInvalid traverses the node of the AST that defines the scope
|
||
|
// containing the declaration of obj, and attempts to find a user-friendly
|
||
|
// name for its invalid type. The resulting Object and its Type are fake.
|
||
|
func resolveInvalid(obj types.Object, node ast.Node, info *types.Info) types.Object {
|
||
|
// Construct a fake type for the object and return a fake object with this type.
|
||
|
formatResult := func(expr ast.Expr) types.Object {
|
||
|
var typename string
|
||
|
switch t := expr.(type) {
|
||
|
case *ast.SelectorExpr:
|
||
|
typename = fmt.Sprintf("%s.%s", t.X, t.Sel)
|
||
|
case *ast.Ident:
|
||
|
typename = t.String()
|
||
|
default:
|
||
|
return nil
|
||
|
}
|
||
|
typ := types.NewNamed(types.NewTypeName(token.NoPos, obj.Pkg(), typename, nil), nil, nil)
|
||
|
return types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), typ)
|
||
|
}
|
||
|
var resultExpr ast.Expr
|
||
|
ast.Inspect(node, func(node ast.Node) bool {
|
||
|
switch n := node.(type) {
|
||
|
case *ast.ValueSpec:
|
||
|
for _, name := range n.Names {
|
||
|
if info.Defs[name] == obj {
|
||
|
resultExpr = n.Type
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
case *ast.Field: // This case handles parameters and results of a FuncDecl or FuncLit.
|
||
|
for _, name := range n.Names {
|
||
|
if info.Defs[name] == obj {
|
||
|
resultExpr = n.Type
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
// TODO(rstambler): Handle range statements.
|
||
|
default:
|
||
|
return true
|
||
|
}
|
||
|
})
|
||
|
return formatResult(resultExpr)
|
||
|
}
|
||
|
|
||
|
func isPointer(T types.Type) bool {
|
||
|
_, ok := T.(*types.Pointer)
|
||
|
return ok
|
||
|
}
|
||
|
|
||
|
// deref returns a pointer's element type; otherwise it returns typ.
|
||
|
func deref(typ types.Type) types.Type {
|
||
|
if p, ok := typ.Underlying().(*types.Pointer); ok {
|
||
|
return p.Elem()
|
||
|
}
|
||
|
return typ
|
||
|
}
|