golang.org/x/tools/gopls@v0.15.3/internal/golang/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 exprIdx <= 0 {
    73  			// Infer first append() arg type as apparent return type of
    74  			// append().
    75  			inf.objType = parentInf.objType
    76  			if parentInf.variadic {
    77  				inf.objType = types.NewSlice(inf.objType)
    78  			}
    79  			break
    80  		}
    81  
    82  		// For non-initial append() args, infer slice type from the first
    83  		// append() arg, or from parent context.
    84  		if len(call.Args) > 0 {
    85  			inf.objType = c.pkg.GetTypesInfo().TypeOf(call.Args[0])
    86  		}
    87  		if inf.objType == nil {
    88  			inf.objType = parentInf.objType
    89  		}
    90  		if inf.objType == nil {
    91  			break
    92  		}
    93  
    94  		inf.objType = deslice(inf.objType)
    95  
    96  		// Check if we are completing the variadic append() param.
    97  		inf.variadic = exprIdx == 1 && len(call.Args) <= 2
    98  
    99  		// Penalize the first append() argument as a candidate. You
   100  		// don't normally append a slice to itself.
   101  		if sliceChain := objChain(c.pkg.GetTypesInfo(), call.Args[0]); len(sliceChain) > 0 {
   102  			inf.penalized = append(inf.penalized, penalizedObj{objChain: sliceChain, penalty: 0.9})
   103  		}
   104  	case "delete":
   105  		if exprIdx > 0 && len(call.Args) > 0 {
   106  			// Try to fill in expected type of map key.
   107  			firstArgType := c.pkg.GetTypesInfo().TypeOf(call.Args[0])
   108  			if firstArgType != nil {
   109  				if mt, ok := firstArgType.Underlying().(*types.Map); ok {
   110  					inf.objType = mt.Key()
   111  				}
   112  			}
   113  		}
   114  	case "copy":
   115  		var t1, t2 types.Type
   116  		if len(call.Args) > 0 {
   117  			t1 = c.pkg.GetTypesInfo().TypeOf(call.Args[0])
   118  			if len(call.Args) > 1 {
   119  				t2 = c.pkg.GetTypesInfo().TypeOf(call.Args[1])
   120  			}
   121  		}
   122  
   123  		// Fill in expected type of either arg if the other is already present.
   124  		if exprIdx == 1 && t1 != nil {
   125  			inf.objType = t1
   126  		} else if exprIdx == 0 && t2 != nil {
   127  			inf.objType = t2
   128  		}
   129  	case "new":
   130  		inf.typeName.wantTypeName = true
   131  		if parentInf.objType != nil {
   132  			// Expected type for "new" is the de-pointered parent type.
   133  			if ptr, ok := parentInf.objType.Underlying().(*types.Pointer); ok {
   134  				inf.objType = ptr.Elem()
   135  			}
   136  		}
   137  	case "make":
   138  		if exprIdx == 0 {
   139  			inf.typeName.wantTypeName = true
   140  			inf.objType = parentInf.objType
   141  		} else {
   142  			inf.objType = types.Typ[types.UntypedInt]
   143  		}
   144  	}
   145  
   146  	return inf
   147  }