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  }