| // Copyright 2020 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package source |
| |
| import ( |
| "go/ast" |
| "go/types" |
| ) |
| |
| // builtinArgKind determines the expected object kind for a builtin |
| // argument. It attempts to use the AST hints from builtin.go where |
| // possible. |
| func (c *completer) builtinArgKind(obj types.Object, call *ast.CallExpr) objKind { |
| astObj, err := c.snapshot.View().LookupBuiltin(c.ctx, obj.Name()) |
| if err != nil { |
| return 0 |
| } |
| exprIdx := exprAtPos(c.pos, call.Args) |
| |
| decl, ok := astObj.Decl.(*ast.FuncDecl) |
| if !ok || exprIdx >= len(decl.Type.Params.List) { |
| return 0 |
| } |
| |
| switch ptyp := decl.Type.Params.List[exprIdx].Type.(type) { |
| case *ast.ChanType: |
| return kindChan |
| case *ast.ArrayType: |
| return kindSlice |
| case *ast.MapType: |
| return kindMap |
| case *ast.Ident: |
| switch ptyp.Name { |
| case "Type": |
| switch obj.Name() { |
| case "make": |
| return kindChan | kindSlice | kindMap |
| case "len": |
| return kindSlice | kindMap | kindArray | kindString | kindChan |
| case "cap": |
| return kindSlice | kindArray | kindChan |
| } |
| } |
| } |
| |
| return 0 |
| } |
| |
| // builtinArgType infers the type of an argument to a builtin |
| // function. "parentType" is the inferred type for the builtin call's |
| // parent node. |
| func (c *completer) builtinArgType(obj types.Object, call *ast.CallExpr, parentType types.Type) (infType types.Type, variadic bool) { |
| exprIdx := exprAtPos(c.pos, call.Args) |
| |
| switch obj.Name() { |
| case "append": |
| // Check if we are completing the variadic append() param. |
| variadic = exprIdx == 1 && len(call.Args) <= 2 |
| infType = parentType |
| |
| // If we are completing an individual element of the variadic |
| // param, "deslice" the expected type. |
| if !variadic && exprIdx > 0 { |
| if slice, ok := parentType.(*types.Slice); ok { |
| infType = slice.Elem() |
| } |
| } |
| case "delete": |
| if exprIdx > 0 && len(call.Args) > 0 { |
| // Try to fill in expected type of map key. |
| firstArgType := c.pkg.GetTypesInfo().TypeOf(call.Args[0]) |
| if firstArgType != nil { |
| if mt, ok := firstArgType.Underlying().(*types.Map); ok { |
| infType = mt.Key() |
| } |
| } |
| } |
| case "copy": |
| var t1, t2 types.Type |
| if len(call.Args) > 0 { |
| t1 = c.pkg.GetTypesInfo().TypeOf(call.Args[0]) |
| if len(call.Args) > 1 { |
| t2 = c.pkg.GetTypesInfo().TypeOf(call.Args[1]) |
| } |
| } |
| |
| // Fill in expected type of either arg if the other is already present. |
| if exprIdx == 1 && t1 != nil { |
| infType = t1 |
| } else if exprIdx == 0 && t2 != nil { |
| infType = t2 |
| } |
| case "new": |
| if parentType != nil { |
| // Expected type for "new" is the de-pointered parent type. |
| if ptr, ok := parentType.Underlying().(*types.Pointer); ok { |
| infType = ptr.Elem() |
| } |
| } |
| case "make": |
| if exprIdx == 0 { |
| infType = parentType |
| } |
| } |
| |
| return infType, variadic |
| } |