| 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 |
| } |