github.com/v2fly/tools@v0.100.0/internal/lsp/source/completion/builtin.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package completion 6 7 import ( 8 "context" 9 "go/ast" 10 "go/types" 11 ) 12 13 // builtinArgKind determines the expected object kind for a builtin 14 // argument. It attempts to use the AST hints from builtin.go where 15 // possible. 16 func (c *completer) builtinArgKind(ctx context.Context, obj types.Object, call *ast.CallExpr) objKind { 17 builtin, err := c.snapshot.BuiltinFile(ctx) 18 if err != nil { 19 return 0 20 } 21 exprIdx := exprAtPos(c.pos, call.Args) 22 23 builtinObj := builtin.File.Scope.Lookup(obj.Name()) 24 if builtinObj == nil { 25 return 0 26 } 27 decl, ok := builtinObj.Decl.(*ast.FuncDecl) 28 if !ok || exprIdx >= len(decl.Type.Params.List) { 29 return 0 30 } 31 32 switch ptyp := decl.Type.Params.List[exprIdx].Type.(type) { 33 case *ast.ChanType: 34 return kindChan 35 case *ast.ArrayType: 36 return kindSlice 37 case *ast.MapType: 38 return kindMap 39 case *ast.Ident: 40 switch ptyp.Name { 41 case "Type": 42 switch obj.Name() { 43 case "make": 44 return kindChan | kindSlice | kindMap 45 case "len": 46 return kindSlice | kindMap | kindArray | kindString | kindChan 47 case "cap": 48 return kindSlice | kindArray | kindChan 49 } 50 } 51 } 52 53 return 0 54 } 55 56 // builtinArgType infers the type of an argument to a builtin 57 // function. parentInf is the inferred type info for the builtin 58 // call's parent node. 59 func (c *completer) builtinArgType(obj types.Object, call *ast.CallExpr, parentInf candidateInference) candidateInference { 60 var ( 61 exprIdx = exprAtPos(c.pos, call.Args) 62 63 // Propagate certain properties from our parent's inference. 64 inf = candidateInference{ 65 typeName: parentInf.typeName, 66 modifiers: parentInf.modifiers, 67 } 68 ) 69 70 switch obj.Name() { 71 case "append": 72 if parentInf.objType == nil { 73 break 74 } 75 76 inf.objType = parentInf.objType 77 78 if exprIdx <= 0 { 79 break 80 } 81 82 inf.objType = deslice(inf.objType) 83 84 // Check if we are completing the variadic append() param. 85 inf.variadic = exprIdx == 1 && len(call.Args) <= 2 86 87 // Penalize the first append() argument as a candidate. You 88 // don't normally append a slice to itself. 89 if sliceChain := objChain(c.pkg.GetTypesInfo(), call.Args[0]); len(sliceChain) > 0 { 90 inf.penalized = append(inf.penalized, penalizedObj{objChain: sliceChain, penalty: 0.9}) 91 } 92 case "delete": 93 if exprIdx > 0 && len(call.Args) > 0 { 94 // Try to fill in expected type of map key. 95 firstArgType := c.pkg.GetTypesInfo().TypeOf(call.Args[0]) 96 if firstArgType != nil { 97 if mt, ok := firstArgType.Underlying().(*types.Map); ok { 98 inf.objType = mt.Key() 99 } 100 } 101 } 102 case "copy": 103 var t1, t2 types.Type 104 if len(call.Args) > 0 { 105 t1 = c.pkg.GetTypesInfo().TypeOf(call.Args[0]) 106 if len(call.Args) > 1 { 107 t2 = c.pkg.GetTypesInfo().TypeOf(call.Args[1]) 108 } 109 } 110 111 // Fill in expected type of either arg if the other is already present. 112 if exprIdx == 1 && t1 != nil { 113 inf.objType = t1 114 } else if exprIdx == 0 && t2 != nil { 115 inf.objType = t2 116 } 117 case "new": 118 inf.typeName.wantTypeName = true 119 if parentInf.objType != nil { 120 // Expected type for "new" is the de-pointered parent type. 121 if ptr, ok := parentInf.objType.Underlying().(*types.Pointer); ok { 122 inf.objType = ptr.Elem() 123 } 124 } 125 case "make": 126 if exprIdx == 0 { 127 inf.typeName.wantTypeName = true 128 inf.objType = parentInf.objType 129 } else { 130 inf.objType = types.Typ[types.UntypedInt] 131 } 132 } 133 134 return inf 135 }