| // 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 completion |
| |
| import ( |
| "context" |
| "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(ctx context.Context, obj types.Object, call *ast.CallExpr) objKind { |
| builtin, err := c.snapshot.BuiltinFile(ctx) |
| if err != nil { |
| return 0 |
| } |
| exprIdx := exprAtPos(c.pos, call.Args) |
| |
| builtinObj := builtin.File.Scope.Lookup(obj.Name()) |
| if builtinObj == nil { |
| return 0 |
| } |
| decl, ok := builtinObj.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. parentInf is the inferred type info for the builtin |
| // call's parent node. |
| func (c *completer) builtinArgType(obj types.Object, call *ast.CallExpr, parentInf candidateInference) candidateInference { |
| var ( |
| exprIdx = exprAtPos(c.pos, call.Args) |
| |
| // Propagate certain properties from our parent's inference. |
| inf = candidateInference{ |
| typeName: parentInf.typeName, |
| modifiers: parentInf.modifiers, |
| } |
| ) |
| |
| switch obj.Name() { |
| case "append": |
| if exprIdx <= 0 { |
| // Infer first append() arg type as apparent return type of |
| // append(). |
| inf.objType = parentInf.objType |
| if parentInf.variadic { |
| inf.objType = types.NewSlice(inf.objType) |
| } |
| break |
| } |
| |
| // For non-initial append() args, infer slice type from the first |
| // append() arg, or from parent context. |
| if len(call.Args) > 0 { |
| inf.objType = c.pkg.GetTypesInfo().TypeOf(call.Args[0]) |
| } |
| if inf.objType == nil { |
| inf.objType = parentInf.objType |
| } |
| if inf.objType == nil { |
| break |
| } |
| |
| inf.objType = deslice(inf.objType) |
| |
| // Check if we are completing the variadic append() param. |
| inf.variadic = exprIdx == 1 && len(call.Args) <= 2 |
| |
| // Penalize the first append() argument as a candidate. You |
| // don't normally append a slice to itself. |
| if sliceChain := objChain(c.pkg.GetTypesInfo(), call.Args[0]); len(sliceChain) > 0 { |
| inf.penalized = append(inf.penalized, penalizedObj{objChain: sliceChain, penalty: 0.9}) |
| } |
| 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 { |
| inf.objType = 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 { |
| inf.objType = t1 |
| } else if exprIdx == 0 && t2 != nil { |
| inf.objType = t2 |
| } |
| case "new": |
| inf.typeName.wantTypeName = true |
| if parentInf.objType != nil { |
| // Expected type for "new" is the de-pointered parent type. |
| if ptr, ok := parentInf.objType.Underlying().(*types.Pointer); ok { |
| inf.objType = ptr.Elem() |
| } |
| } |
| case "make": |
| if exprIdx == 0 { |
| inf.typeName.wantTypeName = true |
| inf.objType = parentInf.objType |
| } else { |
| inf.objType = types.Typ[types.UntypedInt] |
| } |
| } |
| |
| return inf |
| } |