// Copyright 2018 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 ( "context" "fmt" "go/ast" "go/constant" "go/token" "go/types" "strconv" "strings" "time" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/internal/imports" "golang.org/x/tools/internal/lsp/fuzzy" "golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/snippet" "golang.org/x/tools/internal/telemetry/trace" errors "golang.org/x/xerrors" ) type CompletionItem struct { // Label is the primary text the user sees for this completion item. Label string // Detail is supplemental information to present to the user. // This often contains the type or return type of the completion item. Detail string // InsertText is the text to insert if this item is selected. // Any of the prefix that has already been typed is not trimmed. // The insert text does not contain snippets. InsertText string Kind protocol.CompletionItemKind // An optional array of additional TextEdits that are applied when // selecting this completion. // // Additional text edits should be used to change text unrelated to the current cursor position // (for example adding an import statement at the top of the file if the completion item will // insert an unqualified type). AdditionalTextEdits []protocol.TextEdit // Depth is how many levels were searched to find this completion. // For example when completing "foo<>", "fooBar" is depth 0, and // "fooBar.Baz" is depth 1. Depth int // Score is the internal relevance score. // A higher score indicates that this completion item is more relevant. Score float64 // snippet is the LSP snippet for the completion item. The LSP // specification contains details about LSP snippets. For example, a // snippet for a function with the following signature: // // func foo(a, b, c int) // // would be: // // foo(${1:a int}, ${2: b int}, ${3: c int}) // // If Placeholders is false in the CompletionOptions, the above // snippet would instead be: // // foo(${1:}) snippet *snippet.Builder // Documentation is the documentation for the completion item. Documentation string } // Snippet is a convenience returns the snippet if available, otherwise // the InsertText. // used for an item, depending on if the callee wants placeholders or not. func (i *CompletionItem) Snippet() string { if i.snippet != nil { return i.snippet.String() } return i.InsertText } // Scoring constants are used for weighting the relevance of different candidates. const ( // stdScore is the base score for all completion items. stdScore float64 = 1.0 // highScore indicates a very relevant completion item. highScore float64 = 10.0 // lowScore indicates an irrelevant or not useful completion item. lowScore float64 = 0.01 ) // matcher matches a candidate's label against the user input. The // returned score reflects the quality of the match. A score of zero // indicates no match, and a score of one means a perfect match. type matcher interface { Score(candidateLabel string) (score float32) } // prefixMatcher implements case sensitive prefix matching. type prefixMatcher string func (pm prefixMatcher) Score(candidateLabel string) float32 { if strings.HasPrefix(candidateLabel, string(pm)) { return 1 } return -1 } // insensitivePrefixMatcher implements case insensitive prefix matching. type insensitivePrefixMatcher string func (ipm insensitivePrefixMatcher) Score(candidateLabel string) float32 { if strings.HasPrefix(strings.ToLower(candidateLabel), string(ipm)) { return 1 } return -1 } // completer contains the necessary information for a single completion request. type completer struct { snapshot Snapshot pkg Package qf types.Qualifier opts CompletionOptions // ctx is the context associated with this completion request. ctx context.Context // filename is the name of the file associated with this completion request. filename string // file is the AST of the file associated with this completion request. file *ast.File // pos is the position at which the request was triggered. pos token.Pos // path is the path of AST nodes enclosing the position. path []ast.Node // seen is the map that ensures we do not return duplicate results. seen map[types.Object]bool // items is the list of completion items returned. items []CompletionItem // surrounding describes the identifier surrounding the position. surrounding *Selection // expectedType contains information about the type we expect the completion // candidate to be. It will be the zero value if no information is available. expectedType typeInference // enclosingFunc contains information about the function enclosing // the position. enclosingFunc *funcInfo // enclosingCompositeLiteral contains information about the composite literal // enclosing the position. enclosingCompositeLiteral *compLitInfo // deepState contains the current state of our deep completion search. deepState deepCompletionState // matcher matches the candidates against the surrounding prefix. matcher matcher // methodSetCache caches the types.NewMethodSet call, which is relatively // expensive and can be called many times for the same type while searching // for deep completions. methodSetCache map[methodSetKey]*types.MethodSet // mapper converts the positions in the file from which the completion originated. mapper *protocol.ColumnMapper // startTime is when we started processing this completion request. It does // not include any time the request spent in the queue. startTime time.Time } // funcInfo holds info about a function object. type funcInfo struct { // sig is the function declaration enclosing the position. sig *types.Signature // body is the function's body. body *ast.BlockStmt } type compLitInfo struct { // cl is the *ast.CompositeLit enclosing the position. cl *ast.CompositeLit // clType is the type of cl. clType types.Type // kv is the *ast.KeyValueExpr enclosing the position, if any. kv *ast.KeyValueExpr // inKey is true if we are certain the position is in the key side // of a key-value pair. inKey bool // maybeInFieldName is true if inKey is false and it is possible // we are completing a struct field name. For example, // "SomeStruct{<>}" will be inKey=false, but maybeInFieldName=true // because we _could_ be completing a field name. maybeInFieldName bool } type importInfo struct { importPath string name string pkg Package } type methodSetKey struct { typ types.Type addressable bool } // A Selection represents the cursor position and surrounding identifier. type Selection struct { content string cursor token.Pos mappedRange } func (p Selection) Prefix() string { return p.content[:p.cursor-p.spanRange.Start] } func (p Selection) Suffix() string { return p.content[p.cursor-p.spanRange.Start:] } func (c *completer) setSurrounding(ident *ast.Ident) { if c.surrounding != nil { return } if !(ident.Pos() <= c.pos && c.pos <= ident.End()) { return } c.surrounding = &Selection{ content: ident.Name, cursor: c.pos, // Overwrite the prefix only. mappedRange: newMappedRange(c.snapshot.View().Session().Cache().FileSet(), c.mapper, ident.Pos(), ident.End()), } if c.opts.FuzzyMatching { c.matcher = fuzzy.NewMatcher(c.surrounding.Prefix()) } else if c.opts.CaseSensitive { c.matcher = prefixMatcher(c.surrounding.Prefix()) } else { c.matcher = insensitivePrefixMatcher(strings.ToLower(c.surrounding.Prefix())) } } func (c *completer) getSurrounding() *Selection { if c.surrounding == nil { c.surrounding = &Selection{ content: "", cursor: c.pos, mappedRange: newMappedRange(c.snapshot.View().Session().Cache().FileSet(), c.mapper, c.pos, c.pos), } } return c.surrounding } // found adds a candidate completion. We will also search through the object's // members for more candidates. func (c *completer) found(obj types.Object, score float64, imp *importInfo) { if obj.Pkg() != nil && obj.Pkg() != c.pkg.GetTypes() && !obj.Exported() { // obj is not accessible because it lives in another package and is not // exported. Don't treat it as a completion candidate. return } if c.inDeepCompletion() { // When searching deep, just make sure we don't have a cycle in our chain. // We don't dedupe by object because we want to allow both "foo.Baz" and // "bar.Baz" even though "Baz" is represented the same types.Object in both. for _, seenObj := range c.deepState.chain { if seenObj == obj { return } } } else { // At the top level, dedupe by object. if c.seen[obj] { return } c.seen[obj] = true } // If we are running out of budgeted time we must limit our search for deep // completion candidates. if c.shouldPrune() { return } cand := candidate{ obj: obj, score: score, imp: imp, } if c.matchingCandidate(&cand) { cand.score *= highScore } else if isTypeName(obj) { // If obj is a *types.TypeName that didn't otherwise match, check // if a literal object of this type makes a good candidate. c.literal(obj.Type(), imp) } // Favor shallow matches by lowering weight according to depth. cand.score -= cand.score * float64(len(c.deepState.chain)) / 10 if cand.score < 0 { cand.score = 0 } cand.name = c.deepState.chainString(obj.Name()) matchScore := c.matcher.Score(cand.name) if matchScore > 0 { cand.score *= float64(matchScore) // Avoid calling c.item() for deep candidates that wouldn't be in the top // MaxDeepCompletions anyway. if !c.inDeepCompletion() || c.deepState.isHighScore(cand.score) { if item, err := c.item(cand); err == nil { c.items = append(c.items, item) } } } c.deepSearch(obj, imp) } // candidate represents a completion candidate. type candidate struct { // obj is the types.Object to complete to. obj types.Object // score is used to rank candidates. score float64 // name is the deep object name path, e.g. "foo.bar" name string // expandFuncCall is true if obj should be invoked in the completion. // For example, expandFuncCall=true yields "foo()", expandFuncCall=false yields "foo". expandFuncCall bool // imp is the import that needs to be added to this package in order // for this candidate to be valid. nil if no import needed. imp *importInfo } // ErrIsDefinition is an error that informs the user they got no // completions because they tried to complete the name of a new object // being defined. type ErrIsDefinition struct { objStr string } func (e ErrIsDefinition) Error() string { msg := "this is a definition" if e.objStr != "" { msg += " of " + e.objStr } return msg } // Completion returns a list of possible candidates for completion, given a // a file and a position. // // The selection is computed based on the preceding identifier and can be used by // the client to score the quality of the completion. For instance, some clients // may tolerate imperfect matches as valid completion results, since users may make typos. func Completion(ctx context.Context, snapshot Snapshot, f File, pos protocol.Position, opts CompletionOptions) ([]CompletionItem, *Selection, error) { ctx, done := trace.StartSpan(ctx, "source.Completion") defer done() startTime := time.Now() pkg, pgh, err := getParsedFile(ctx, snapshot, f, NarrowestCheckPackageHandle) if err != nil { return nil, nil, fmt.Errorf("getting file for Completion: %v", err) } file, m, _, err := pgh.Cached() if err != nil { return nil, nil, err } spn, err := m.PointSpan(pos) if err != nil { return nil, nil, err } rng, err := spn.Range(m.Converter) if err != nil { return nil, nil, err } // Completion is based on what precedes the cursor. // Find the path to the position before pos. path, _ := astutil.PathEnclosingInterval(file, rng.Start-1, rng.Start-1) if path == nil { return nil, nil, errors.Errorf("cannot find node enclosing position") } // Skip completion inside any kind of literal. if _, ok := path[0].(*ast.BasicLit); ok { return nil, nil, nil } c := &completer{ pkg: pkg, snapshot: snapshot, qf: qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()), ctx: ctx, filename: f.URI().Filename(), file: file, path: path, pos: rng.Start, seen: make(map[types.Object]bool), enclosingFunc: enclosingFunction(path, rng.Start, pkg.GetTypesInfo()), enclosingCompositeLiteral: enclosingCompositeLiteral(path, rng.Start, pkg.GetTypesInfo()), opts: opts, // default to a matcher that always matches matcher: prefixMatcher(""), methodSetCache: make(map[methodSetKey]*types.MethodSet), mapper: m, startTime: startTime, } if opts.Deep { // Initialize max search depth to unlimited. c.deepState.maxDepth = -1 } // Set the filter surrounding. if ident, ok := path[0].(*ast.Ident); ok { c.setSurrounding(ident) } c.expectedType = expectedType(c) // If we're inside a comment return comment completions for _, comment := range file.Comments { if comment.Pos() <= rng.Start && rng.Start <= comment.End() { c.populateCommentCompletions(comment) return c.items, c.getSurrounding(), nil } } // Struct literals are handled entirely separately. if c.wantStructFieldCompletions() { if err := c.structLiteralFieldName(); err != nil { return nil, nil, err } return c.items, c.getSurrounding(), nil } if lt := c.wantLabelCompletion(); lt != labelNone { c.labels(lt) return c.items, c.getSurrounding(), nil } switch n := path[0].(type) { case *ast.Ident: // Is this the Sel part of a selector? if sel, ok := path[1].(*ast.SelectorExpr); ok && sel.Sel == n { if err := c.selector(sel); err != nil { return nil, nil, err } return c.items, c.getSurrounding(), nil } // reject defining identifiers if obj, ok := pkg.GetTypesInfo().Defs[n]; ok { if v, ok := obj.(*types.Var); ok && v.IsField() && v.Embedded() { // An anonymous field is also a reference to a type. } else { objStr := "" if obj != nil { qual := types.RelativeTo(pkg.GetTypes()) objStr = types.ObjectString(obj, qual) } return nil, nil, ErrIsDefinition{objStr: objStr} } } if err := c.lexical(); err != nil { return nil, nil, err } if err := c.keyword(); err != nil { return nil, nil, err } // The function name hasn't been typed yet, but the parens are there: // recv.‸(arg) case *ast.TypeAssertExpr: // Create a fake selector expression. if err := c.selector(&ast.SelectorExpr{X: n.X}); err != nil { return nil, nil, err } case *ast.SelectorExpr: // The go parser inserts a phantom "_" Sel node when the selector is // not followed by an identifier or a "(". The "_" isn't actually in // the text, so don't think it is our surrounding. // TODO: Find a way to differentiate between phantom "_" and real "_", // perhaps by checking if "_" is present in file contents. if n.Sel.Name != "_" || c.pos != n.Sel.Pos() { c.setSurrounding(n.Sel) } if err := c.selector(n); err != nil { return nil, nil, err } default: // fallback to lexical completions if err := c.lexical(); err != nil { return nil, nil, err } } return c.items, c.getSurrounding(), nil } // populateCommentCompletions returns completions for an exported variable immediately preceeding comment func (c *completer) populateCommentCompletions(comment *ast.CommentGroup) { // Using the comment position find the line after fset := c.snapshot.View().Session().Cache().FileSet() file := fset.File(comment.Pos()) if file == nil { return } line := file.Line(comment.Pos()) nextLinePos := file.LineStart(line + 1) if !nextLinePos.IsValid() { return } // Using the next line pos, grab and parse the exported variable on that line for _, n := range c.file.Decls { if n.Pos() != nextLinePos { continue } switch node := n.(type) { case *ast.GenDecl: if node.Tok != token.VAR { return } for _, spec := range node.Specs { if value, ok := spec.(*ast.ValueSpec); ok { for _, name := range value.Names { if name.Name == "_" || !name.IsExported() { continue } exportedVar := c.pkg.GetTypesInfo().ObjectOf(name) c.found(exportedVar, stdScore, nil) } } } } } } func (c *completer) wantStructFieldCompletions() bool { clInfo := c.enclosingCompositeLiteral if clInfo == nil { return false } return clInfo.isStruct() && (clInfo.inKey || clInfo.maybeInFieldName) } func (c *completer) wantTypeName() bool { return c.expectedType.typeName.wantTypeName } // See https://golang.org/issue/36001. Unimported completions are expensive. const maxUnimported = 20 // selector finds completions for the specified selector expression. func (c *completer) selector(sel *ast.SelectorExpr) error { // Is sel a qualified identifier? if id, ok := sel.X.(*ast.Ident); ok { if pkgname, ok := c.pkg.GetTypesInfo().Uses[id].(*types.PkgName); ok { c.packageMembers(pkgname.Imported(), nil) return nil } } // Invariant: sel is a true selector. tv, ok := c.pkg.GetTypesInfo().Types[sel.X] if ok { return c.methodsAndFields(tv.Type, tv.Addressable(), nil) } // Try unimported packages. if id, ok := sel.X.(*ast.Ident); ok { pkgExports, err := PackageExports(c.ctx, c.snapshot.View(), id.Name, c.filename) if err != nil { return err } known := c.snapshot.KnownImportPaths() startingItems := len(c.items) for _, pkgExport := range pkgExports { if len(c.items)-startingItems >= maxUnimported { break } // If we've seen this import path, use the fully-typed version. if knownPkg, ok := known[pkgExport.Fix.StmtInfo.ImportPath]; ok { c.packageMembers(knownPkg.GetTypes(), &importInfo{ importPath: pkgExport.Fix.StmtInfo.ImportPath, name: pkgExport.Fix.StmtInfo.Name, pkg: knownPkg, }) continue } // Otherwise, continue with untyped proposals. pkg := types.NewPackage(pkgExport.Fix.StmtInfo.ImportPath, pkgExport.Fix.IdentName) for _, export := range pkgExport.Exports { c.found(types.NewVar(0, pkg, export, nil), 0.07, &importInfo{ importPath: pkgExport.Fix.StmtInfo.ImportPath, name: pkgExport.Fix.StmtInfo.Name, }) } } } return nil } func (c *completer) packageMembers(pkg *types.Package, imp *importInfo) { scope := pkg.Scope() for _, name := range scope.Names() { c.found(scope.Lookup(name), stdScore, imp) } } func (c *completer) methodsAndFields(typ types.Type, addressable bool, imp *importInfo) error { mset := c.methodSetCache[methodSetKey{typ, addressable}] if mset == nil { if addressable && !types.IsInterface(typ) && !isPointer(typ) { // Add methods of *T, which includes methods with receiver T. mset = types.NewMethodSet(types.NewPointer(typ)) } else { // Add methods of T. mset = types.NewMethodSet(typ) } c.methodSetCache[methodSetKey{typ, addressable}] = mset } for i := 0; i < mset.Len(); i++ { c.found(mset.At(i).Obj(), stdScore, imp) } // Add fields of T. for _, f := range fieldSelections(typ) { c.found(f, stdScore, imp) } return nil } // lexical finds completions in the lexical environment. func (c *completer) lexical() error { var scopes []*types.Scope // scopes[i], where i= maxUnimported { break } if _, ok := seen[pkg.IdentName]; !ok { // Do not add the unimported packages to seen, since we can have // multiple packages of the same name as completion suggestions, since // only one will be chosen. obj := types.NewPkgName(0, nil, pkg.IdentName, types.NewPackage(pkg.StmtInfo.ImportPath, pkg.IdentName)) c.found(obj, score, &importInfo{ importPath: pkg.StmtInfo.ImportPath, name: pkg.StmtInfo.Name, }) } } } if c.expectedType.objType != nil { // If we have an expected type and it is _not_ a named type, see // if an object literal makes a good candidate. For example, if // our expected type is "[]int", this will add a candidate of // "[]int{}". if _, named := deref(c.expectedType.objType).(*types.Named); !named { c.literal(c.expectedType.objType, nil) } } return nil } // alreadyImports reports whether f has an import with the specified path. func alreadyImports(f *ast.File, path string) bool { for _, s := range f.Imports { if importPath(s) == path { return true } } return false } // importPath returns the unquoted import path of s, // or "" if the path is not properly quoted. func importPath(s *ast.ImportSpec) string { t, err := strconv.Unquote(s.Path.Value) if err != nil { return "" } return t } func nodeContains(n ast.Node, pos token.Pos) bool { return n != nil && n.Pos() <= pos && pos <= n.End() } func (c *completer) inConstDecl() bool { for _, n := range c.path { if decl, ok := n.(*ast.GenDecl); ok && decl.Tok == token.CONST { return true } } return false } // structLiteralFieldName finds completions for struct field names inside a struct literal. func (c *completer) structLiteralFieldName() error { clInfo := c.enclosingCompositeLiteral // Mark fields of the composite literal that have already been set, // except for the current field. addedFields := make(map[*types.Var]bool) for _, el := range clInfo.cl.Elts { if kvExpr, ok := el.(*ast.KeyValueExpr); ok { if clInfo.kv == kvExpr { continue } if key, ok := kvExpr.Key.(*ast.Ident); ok { if used, ok := c.pkg.GetTypesInfo().Uses[key]; ok { if usedVar, ok := used.(*types.Var); ok { addedFields[usedVar] = true } } } } } switch t := clInfo.clType.(type) { case *types.Struct: for i := 0; i < t.NumFields(); i++ { field := t.Field(i) if !addedFields[field] { c.found(field, highScore, nil) } } // Add lexical completions if we aren't certain we are in the key part of a // key-value pair. if clInfo.maybeInFieldName { return c.lexical() } default: return c.lexical() } return nil } func (cl *compLitInfo) isStruct() bool { _, ok := cl.clType.(*types.Struct) return ok } // enclosingCompositeLiteral returns information about the composite literal enclosing the // position. func enclosingCompositeLiteral(path []ast.Node, pos token.Pos, info *types.Info) *compLitInfo { for _, n := range path { switch n := n.(type) { case *ast.CompositeLit: // The enclosing node will be a composite literal if the user has just // opened the curly brace (e.g. &x{<>) or the completion request is triggered // from an already completed composite literal expression (e.g. &x{foo: 1, <>}) // // The position is not part of the composite literal unless it falls within the // curly braces (e.g. "foo.Foo<>Struct{}"). if !(n.Lbrace < pos && pos <= n.Rbrace) { // Keep searching since we may yet be inside a composite literal. // For example "Foo{B: Ba<>{}}". break } tv, ok := info.Types[n] if !ok { return nil } clInfo := compLitInfo{ cl: n, clType: deref(tv.Type).Underlying(), } var ( expr ast.Expr hasKeys bool ) for _, el := range n.Elts { // Remember the expression that the position falls in, if any. if el.Pos() <= pos && pos <= el.End() { expr = el } if kv, ok := el.(*ast.KeyValueExpr); ok { hasKeys = true // If expr == el then we know the position falls in this expression, // so also record kv as the enclosing *ast.KeyValueExpr. if expr == el { clInfo.kv = kv break } } } if clInfo.kv != nil { // If in a *ast.KeyValueExpr, we know we are in the key if the position // is to the left of the colon (e.g. "Foo{F<>: V}". clInfo.inKey = pos <= clInfo.kv.Colon } else if hasKeys { // If we aren't in a *ast.KeyValueExpr but the composite literal has // other *ast.KeyValueExprs, we must be on the key side of a new // *ast.KeyValueExpr (e.g. "Foo{F: V, <>}"). clInfo.inKey = true } else { switch clInfo.clType.(type) { case *types.Struct: if len(n.Elts) == 0 { // If the struct literal is empty, next could be a struct field // name or an expression (e.g. "Foo{<>}" could become "Foo{F:}" // or "Foo{someVar}"). clInfo.maybeInFieldName = true } else if len(n.Elts) == 1 { // If there is one expression and the position is in that expression // and the expression is an identifier, we may be writing a field // name or an expression (e.g. "Foo{F<>}"). _, clInfo.maybeInFieldName = expr.(*ast.Ident) } case *types.Map: // If we aren't in a *ast.KeyValueExpr we must be adding a new key // to the map. clInfo.inKey = true } } return &clInfo default: if breaksExpectedTypeInference(n) { return nil } } } return nil } // enclosingFunction returns the signature and body of the function // enclosing the given position. func enclosingFunction(path []ast.Node, pos token.Pos, info *types.Info) *funcInfo { for _, node := range path { switch t := node.(type) { case *ast.FuncDecl: if obj, ok := info.Defs[t.Name]; ok { return &funcInfo{ sig: obj.Type().(*types.Signature), body: t.Body, } } case *ast.FuncLit: if typ, ok := info.Types[t]; ok { return &funcInfo{ sig: typ.Type.(*types.Signature), body: t.Body, } } } } return nil } func (c *completer) expectedCompositeLiteralType() types.Type { clInfo := c.enclosingCompositeLiteral switch t := clInfo.clType.(type) { case *types.Slice: if clInfo.inKey { return types.Typ[types.Int] } return t.Elem() case *types.Array: if clInfo.inKey { return types.Typ[types.Int] } return t.Elem() case *types.Map: if clInfo.inKey { return t.Key() } return t.Elem() case *types.Struct: // If we are completing a key (i.e. field name), there is no expected type. if clInfo.inKey { return nil } // If we are in a key-value pair, but not in the key, then we must be on the // value side. The expected type of the value will be determined from the key. if clInfo.kv != nil { if key, ok := clInfo.kv.Key.(*ast.Ident); ok { for i := 0; i < t.NumFields(); i++ { if field := t.Field(i); field.Name() == key.Name { return field.Type() } } } } else { // If we aren't in a key-value pair and aren't in the key, we must be using // implicit field names. // The order of the literal fields must match the order in the struct definition. // Find the element that the position belongs to and suggest that field's type. if i := indexExprAtPos(c.pos, clInfo.cl.Elts); i < t.NumFields() { return t.Field(i).Type() } } } return nil } // typeModifier represents an operator that changes the expected type. type typeModifier struct { mod typeMod arrayLen int64 } type typeMod int const ( star typeMod = iota // dereference operator for expressions, pointer indicator for types reference // reference ("&") operator chanRead // channel read ("<-") operator slice // make a slice type ("[]" in "[]int") array // make an array type ("[2]" in "[2]int") ) // typeInference holds information we have inferred about a type that can be // used at the current position. type typeInference struct { // objType is the desired type of an object used at the query position. objType types.Type // variadic is true if objType is a slice type from an initial // variadic param. variadic bool // modifiers are prefixes such as "*", "&" or "<-" that influence how // a candidate type relates to the expected type. modifiers []typeModifier // convertibleTo is a type our candidate type must be convertible to. convertibleTo types.Type // typeName holds information about the expected type name at // position, if any. typeName typeNameInference } // typeNameInference holds information about the expected type name at // position. type typeNameInference struct { // wantTypeName is true if we expect the name of a type. wantTypeName bool // modifiers are prefixes such as "*", "&" or "<-" that influence how // a candidate type relates to the expected type. modifiers []typeModifier // assertableFrom is a type that must be assertable to our candidate type. assertableFrom types.Type // wantComparable is true if we want a comparable type. wantComparable bool } // expectedType returns information about the expected type for an expression at // the query position. func expectedType(c *completer) (inf typeInference) { inf.typeName = expectTypeName(c) if c.enclosingCompositeLiteral != nil { inf.objType = c.expectedCompositeLiteralType() return inf } Nodes: for i, node := range c.path { switch node := node.(type) { case *ast.BinaryExpr: // Determine if query position comes from left or right of op. e := node.X if c.pos < node.OpPos { e = node.Y } if tv, ok := c.pkg.GetTypesInfo().Types[e]; ok { inf.objType = tv.Type break Nodes } case *ast.AssignStmt: // Only rank completions if you are on the right side of the token. if c.pos > node.TokPos { i := indexExprAtPos(c.pos, node.Rhs) if i >= len(node.Lhs) { i = len(node.Lhs) - 1 } if tv, ok := c.pkg.GetTypesInfo().Types[node.Lhs[i]]; ok { inf.objType = tv.Type break Nodes } } return inf case *ast.CallExpr: // Only consider CallExpr args if position falls between parens. if node.Lparen <= c.pos && c.pos <= node.Rparen { // For type conversions like "int64(foo)" we can only infer our // desired type is convertible to int64. if typ := typeConversion(node, c.pkg.GetTypesInfo()); typ != nil { inf.convertibleTo = typ break Nodes } if tv, ok := c.pkg.GetTypesInfo().Types[node.Fun]; ok { if sig, ok := tv.Type.(*types.Signature); ok { numParams := sig.Params().Len() if numParams == 0 { return inf } var ( exprIdx = indexExprAtPos(c.pos, node.Args) isLastParam = exprIdx == numParams-1 beyondLastParam = exprIdx >= numParams ) if sig.Variadic() { // If we are beyond the last param or we are the last // param w/ further expressions, we expect a single // variadic item. if beyondLastParam || isLastParam && len(node.Args) > numParams { inf.objType = sig.Params().At(numParams - 1).Type().(*types.Slice).Elem() break Nodes } // Otherwise if we are at the last param then we are // completing the variadic positition (i.e. we expect a // slice type []T or an individual item T). if isLastParam { inf.variadic = true } } // Make sure not to run past the end of expected parameters. if beyondLastParam { inf.objType = sig.Params().At(numParams - 1).Type() } else { inf.objType = sig.Params().At(exprIdx).Type() } break Nodes } } if funIdent, ok := node.Fun.(*ast.Ident); ok { switch c.pkg.GetTypesInfo().ObjectOf(funIdent) { case types.Universe.Lookup("append"): defer func() { exprIdx := indexExprAtPos(c.pos, node.Args) // Check if we are completing the variadic append() // param. We defer this since we don't want to inherit // variadicity from the next node. inf.variadic = exprIdx == 1 && len(node.Args) <= 2 // If we are completing an individual element of the // variadic param, "deslice" the expected type. if !inf.variadic && exprIdx > 0 { if slice, ok := inf.objType.(*types.Slice); ok { inf.objType = slice.Elem() } } }() // The expected type of append() arguments is the expected // type of the append() call itself. For example: // // var foo []int // foo = append(<>) // // To find the expected type at <> we "skip" the append() // node and get the expected type one level up, which is // []int. continue Nodes } } } return inf case *ast.ReturnStmt: if c.enclosingFunc != nil { sig := c.enclosingFunc.sig // Find signature result that corresponds to our return statement. if resultIdx := indexExprAtPos(c.pos, node.Results); resultIdx < len(node.Results) { if resultIdx < sig.Results().Len() { inf.objType = sig.Results().At(resultIdx).Type() break Nodes } } } return inf case *ast.CaseClause: if swtch, ok := findSwitchStmt(c.path[i+1:], c.pos, node).(*ast.SwitchStmt); ok { if tv, ok := c.pkg.GetTypesInfo().Types[swtch.Tag]; ok { inf.objType = tv.Type break Nodes } } return inf case *ast.SliceExpr: // Make sure position falls within the brackets (e.g. "foo[a:<>]"). if node.Lbrack < c.pos && c.pos <= node.Rbrack { inf.objType = types.Typ[types.Int] break Nodes } return inf case *ast.IndexExpr: // Make sure position falls within the brackets (e.g. "foo[<>]"). if node.Lbrack < c.pos && c.pos <= node.Rbrack { if tv, ok := c.pkg.GetTypesInfo().Types[node.X]; ok { switch t := tv.Type.Underlying().(type) { case *types.Map: inf.objType = t.Key() case *types.Slice, *types.Array: inf.objType = types.Typ[types.Int] default: return inf } break Nodes } } return inf case *ast.SendStmt: // Make sure we are on right side of arrow (e.g. "foo <- <>"). if c.pos > node.Arrow+1 { if tv, ok := c.pkg.GetTypesInfo().Types[node.Chan]; ok { if ch, ok := tv.Type.Underlying().(*types.Chan); ok { inf.objType = ch.Elem() break Nodes } } } return inf case *ast.StarExpr: inf.modifiers = append(inf.modifiers, typeModifier{mod: star}) case *ast.UnaryExpr: switch node.Op { case token.AND: inf.modifiers = append(inf.modifiers, typeModifier{mod: reference}) case token.ARROW: inf.modifiers = append(inf.modifiers, typeModifier{mod: chanRead}) } default: if breaksExpectedTypeInference(node) { return inf } } } return inf } // applyTypeModifiers applies the list of type modifiers to a type. func (ti typeInference) applyTypeModifiers(typ types.Type) types.Type { for _, mod := range ti.modifiers { switch mod.mod { case star: // For every "*" deref operator, remove a pointer layer from candidate type. typ = deref(typ) case reference: // For every "&" ref operator, add another pointer layer to candidate type. typ = types.NewPointer(typ) case chanRead: // For every "<-" operator, remove a layer of channelness. if ch, ok := typ.(*types.Chan); ok { typ = ch.Elem() } } } return typ } // applyTypeNameModifiers applies the list of type modifiers to a type name. func (ti typeInference) applyTypeNameModifiers(typ types.Type) types.Type { for _, mod := range ti.typeName.modifiers { switch mod.mod { case star: // For every "*" indicator, add a pointer layer to type name. typ = types.NewPointer(typ) case array: typ = types.NewArray(typ, mod.arrayLen) case slice: typ = types.NewSlice(typ) } } return typ } // matchesVariadic returns true if we are completing a variadic // parameter and candType is a compatible slice type. func (ti typeInference) matchesVariadic(candType types.Type) bool { return ti.variadic && types.AssignableTo(ti.objType, candType) } // findSwitchStmt returns an *ast.CaseClause's corresponding *ast.SwitchStmt or // *ast.TypeSwitchStmt. path should start from the case clause's first ancestor. func findSwitchStmt(path []ast.Node, pos token.Pos, c *ast.CaseClause) ast.Stmt { // Make sure position falls within a "case <>:" clause. if exprAtPos(pos, c.List) == nil { return nil } // A case clause is always nested within a block statement in a switch statement. if len(path) < 2 { return nil } if _, ok := path[0].(*ast.BlockStmt); !ok { return nil } switch s := path[1].(type) { case *ast.SwitchStmt: return s case *ast.TypeSwitchStmt: return s default: return nil } } // breaksExpectedTypeInference reports if an expression node's type is unrelated // to its child expression node types. For example, "Foo{Bar: x.Baz(<>)}" should // expect a function argument, not a composite literal value. func breaksExpectedTypeInference(n ast.Node) bool { switch n.(type) { case *ast.FuncLit, *ast.CallExpr, *ast.TypeAssertExpr, *ast.IndexExpr, *ast.SliceExpr, *ast.CompositeLit: return true default: return false } } // expectTypeName returns information about the expected type name at position. func expectTypeName(c *completer) typeNameInference { var ( wantTypeName bool wantComparable bool modifiers []typeModifier assertableFrom types.Type ) Nodes: for i, p := range c.path { switch n := p.(type) { case *ast.FieldList: // Expect a type name if pos is in a FieldList. This applies to // FuncType params/results, FuncDecl receiver, StructType, and // InterfaceType. We don't need to worry about the field name // because completion bails out early if pos is in an *ast.Ident // that defines an object. wantTypeName = true break Nodes case *ast.CaseClause: // Expect type names in type switch case clauses. if swtch, ok := findSwitchStmt(c.path[i+1:], c.pos, n).(*ast.TypeSwitchStmt); ok { // The case clause types must be assertable from the type switch parameter. ast.Inspect(swtch.Assign, func(n ast.Node) bool { if ta, ok := n.(*ast.TypeAssertExpr); ok { assertableFrom = c.pkg.GetTypesInfo().TypeOf(ta.X) return false } return true }) wantTypeName = true break Nodes } return typeNameInference{} case *ast.TypeAssertExpr: // Expect type names in type assert expressions. if n.Lparen < c.pos && c.pos <= n.Rparen { // The type in parens must be assertable from the expression type. assertableFrom = c.pkg.GetTypesInfo().TypeOf(n.X) wantTypeName = true break Nodes } return typeNameInference{} case *ast.StarExpr: modifiers = append(modifiers, typeModifier{mod: star}) case *ast.CompositeLit: // We want a type name if position is in the "Type" part of a // composite literal (e.g. "Foo<>{}"). if n.Type != nil && n.Type.Pos() <= c.pos && c.pos <= n.Type.End() { wantTypeName = true } break Nodes case *ast.ArrayType: // If we are inside the "Elt" part of an array type, we want a type name. if n.Elt.Pos() <= c.pos && c.pos <= n.Elt.End() { wantTypeName = true if n.Len == nil { // No "Len" expression means a slice type. modifiers = append(modifiers, typeModifier{mod: slice}) } else { // Try to get the array type using the constant value of "Len". tv, ok := c.pkg.GetTypesInfo().Types[n.Len] if ok && tv.Value != nil && tv.Value.Kind() == constant.Int { if arrayLen, ok := constant.Int64Val(tv.Value); ok { modifiers = append(modifiers, typeModifier{mod: array, arrayLen: arrayLen}) } } } // ArrayTypes can be nested, so keep going if our parent is an // ArrayType. if i < len(c.path)-1 { if _, ok := c.path[i+1].(*ast.ArrayType); ok { continue Nodes } } break Nodes } case *ast.MapType: wantTypeName = true if n.Key != nil { wantComparable = n.Key.Pos() <= c.pos && c.pos <= n.Key.End() } else { // If the key is empty, assume we are completing the key if // pos is directly after the "map[". wantComparable = c.pos == n.Pos()+token.Pos(len("map[")) } break Nodes default: if breaksExpectedTypeInference(p) { return typeNameInference{} } } } return typeNameInference{ wantTypeName: wantTypeName, wantComparable: wantComparable, modifiers: modifiers, assertableFrom: assertableFrom, } } // matchingType reports whether a type matches the expected type. func (c *completer) matchingType(T types.Type) bool { fakeObj := types.NewVar(token.NoPos, c.pkg.GetTypes(), "", T) return c.matchingCandidate(&candidate{obj: fakeObj}) } // matchingCandidate reports whether a candidate matches our type // inferences. func (c *completer) matchingCandidate(cand *candidate) bool { if isTypeName(cand.obj) { return c.matchingTypeName(cand) } else if c.wantTypeName() { // If we want a type, a non-type object never matches. return false } candType := cand.obj.Type() if candType == nil { return true } // Default to invoking *types.Func candidates. This is so function // completions in an empty statement (or other cases with no expected type) // are invoked by default. cand.expandFuncCall = isFunc(cand.obj) typeMatches := func(expType, candType types.Type) bool { if expType == nil { return false } // Take into account any type modifiers on the expected type. candType = c.expectedType.applyTypeModifiers(candType) // Handle untyped values specially since AssignableTo gives false negatives // for them (see https://golang.org/issue/32146). if candBasic, ok := candType.Underlying().(*types.Basic); ok { if wantBasic, ok := expType.Underlying().(*types.Basic); ok { // Make sure at least one of them is untyped. if isUntyped(candType) || isUntyped(expType) { // Check that their constant kind (bool|int|float|complex|string) matches. // This doesn't take into account the constant value, so there will be some // false positives due to integer sign and overflow. if candBasic.Info()&types.IsConstType == wantBasic.Info()&types.IsConstType { // Lower candidate score if the types are not identical. // This avoids ranking untyped integer constants above // candidates with an exact type match. if !types.Identical(candType, expType) { cand.score /= 2 } return true } } } } // AssignableTo covers the case where the types are equal, but also handles // cases like assigning a concrete type to an interface type. return types.AssignableTo(candType, expType) } if typeMatches(c.expectedType.objType, candType) { // If obj's type matches, we don't want to expand to an invocation of obj. cand.expandFuncCall = false return true } // Try using a function's return type as its type. if sig, ok := candType.Underlying().(*types.Signature); ok && sig.Results().Len() == 1 { if typeMatches(c.expectedType.objType, sig.Results().At(0).Type()) { // If obj's return value matches the expected type, we need to invoke obj // in the completion. cand.expandFuncCall = true return true } } // When completing the variadic parameter, if the expected type is // []T then check candType against T. if c.expectedType.variadic { if slice, ok := c.expectedType.objType.(*types.Slice); ok && typeMatches(slice.Elem(), candType) { return true } } if c.expectedType.convertibleTo != nil { return types.ConvertibleTo(candType, c.expectedType.convertibleTo) } return false } func (c *completer) matchingTypeName(cand *candidate) bool { if !c.wantTypeName() { return false } // Take into account any type name modifier prefixes. actual := c.expectedType.applyTypeNameModifiers(cand.obj.Type()) if c.expectedType.typeName.assertableFrom != nil { // Don't suggest the starting type in type assertions. For example, // if "foo" is an io.Writer, don't suggest "foo.(io.Writer)". if types.Identical(c.expectedType.typeName.assertableFrom, actual) { return false } if intf, ok := c.expectedType.typeName.assertableFrom.Underlying().(*types.Interface); ok { if !types.AssertableTo(intf, actual) { return false } } } if c.expectedType.typeName.wantComparable && !types.Comparable(actual) { return false } // We can expect a type name and have an expected type in cases like: // // var foo []int // foo = []i<> // // Where our expected type is "[]int", and we expect a type name. if c.expectedType.objType != nil { return types.AssignableTo(actual, c.expectedType.objType) } // Default to saying any type name is a match. return true }