// 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/token" "go/types" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/internal/lsp/fuzzy" "golang.org/x/tools/internal/lsp/snippet" "golang.org/x/tools/internal/lsp/telemetry/trace" "golang.org/x/tools/internal/span" ) 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 CompletionItemKind // 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, without placeholders. // 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:}) // plainSnippet *snippet.Builder // PlaceholderSnippet is the LSP snippet for the completion ite, containing // placeholders. The LSP specification contains details about LSP snippets. // For example, a placeholder 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}) // placeholderSnippet *snippet.Builder // Documentation is the documentation for the completion item. Documentation string } // Snippet is a convenience function that determines the snippet that should be // used for an item, depending on if the callee wants placeholders or not. func (i *CompletionItem) Snippet(usePlaceholders bool) string { if usePlaceholders { if i.placeholderSnippet != nil { return i.placeholderSnippet.String() } } if i.plainSnippet != nil { return i.plainSnippet.String() } return i.InsertText } type CompletionItemKind int const ( Unknown CompletionItemKind = iota InterfaceCompletionItem StructCompletionItem TypeCompletionItem ConstantCompletionItem FieldCompletionItem ParameterCompletionItem VariableCompletionItem FunctionCompletionItem MethodCompletionItem PackageCompletionItem ) // 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 ) // completer contains the necessary information for a single completion request. type completer struct { // Package-specific fields. types *types.Package info *types.Info qf types.Qualifier opts CompletionOptions // view is the View associated with this completion request. view View // ctx is the context associated with this completion request. ctx context.Context // 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 conains information about the type we expect the completion // candidate to be. It will be the zero value if no information is available. expectedType typeInference // enclosingFunction is the function declaration enclosing the position. enclosingFunction *types.Signature // 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 does fuzzy matching of the candidates for the surrounding prefix. matcher *fuzzy.Matcher } 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 } // A Selection represents the cursor position and surrounding identifier. type Selection struct { Content string Range span.Range Cursor token.Pos } func (p Selection) Prefix() string { return p.Content[:p.Cursor-p.Range.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, Range: span.NewRange(c.view.Session().Cache().FileSet(), ident.Pos(), ident.End()), Cursor: c.pos, } if c.surrounding.Prefix() != "" { c.matcher = fuzzy.NewMatcher(c.surrounding.Prefix(), fuzzy.Symbol) } } // 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) error { if obj.Pkg() != nil && obj.Pkg() != c.types && !obj.Exported() { return fmt.Errorf("%s is inaccessible from %s", obj.Name(), c.types.Path()) } 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 nil } } } else { // At the top level, dedupe by object. if c.seen[obj] { return nil } c.seen[obj] = true } cand := candidate{ obj: obj, score: score, } if c.matchingType(&cand) { cand.score *= highScore } // Favor shallow matches by lowering weight according to depth. cand.score -= stdScore * float64(len(c.deepState.chain)) item, err := c.item(cand) if err != nil { return err } c.items = append(c.items, item) c.deepSearch(obj) return nil } // 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 // expandFuncCall is true if obj should be invoked in the completion. // For example, expandFuncCall=true yields "foo()", expandFuncCall=false yields "foo". expandFuncCall bool } type CompletionOptions struct { DeepComplete bool WantDocumentaton bool } // 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, view View, f GoFile, pos token.Pos, opts CompletionOptions) ([]CompletionItem, *Selection, error) { ctx, done := trace.StartSpan(ctx, "source.Completion") defer done() file, err := f.GetAST(ctx, ParseFull) if file == nil { return nil, nil, err } pkg := f.GetPackage(ctx) if pkg == nil || pkg.IsIllTyped() { return nil, nil, fmt.Errorf("package for %s is ill typed", f.URI()) } // Completion is based on what precedes the cursor. // Find the path to the position before pos. path, _ := astutil.PathEnclosingInterval(file, pos-1, pos-1) if path == nil { return nil, nil, fmt.Errorf("cannot find node enclosing position") } // Skip completion inside comments. for _, g := range file.Comments { if g.Pos() <= pos && pos <= g.End() { return nil, nil, nil } } // Skip completion inside any kind of literal. if _, ok := path[0].(*ast.BasicLit); ok { return nil, nil, nil } clInfo := enclosingCompositeLiteral(path, pos, pkg.GetTypesInfo()) c := &completer{ types: pkg.GetTypes(), info: pkg.GetTypesInfo(), qf: qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()), view: view, ctx: ctx, path: path, pos: pos, seen: make(map[types.Object]bool), enclosingFunction: enclosingFunction(path, pos, pkg.GetTypesInfo()), enclosingCompositeLiteral: clInfo, opts: opts, } c.deepState.enabled = opts.DeepComplete // Set the filter surrounding. if ident, ok := path[0].(*ast.Ident); ok { c.setSurrounding(ident) } c.expectedType = expectedType(c) // Struct literals are handled entirely separately. if c.wantStructFieldCompletions() { if err := c.structLiteralFieldName(); err != nil { return nil, nil, err } return c.items, c.surrounding, 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.surrounding, nil } // reject defining identifiers if obj, ok := pkg.GetTypesInfo().Defs[n]; ok { if v, ok := obj.(*types.Var); ok && v.IsField() { // An anonymous field is also a reference to a type. } else { of := "" if obj != nil { qual := types.RelativeTo(pkg.GetTypes()) of += ", of " + types.ObjectString(obj, qual) } return nil, nil, fmt.Errorf("this is a definition%s", of) } } if err := c.lexical(); 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.surrounding, 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.wantTypeName } // 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.info.Uses[id].(*types.PkgName); ok { c.packageMembers(pkgname) return nil } } // Invariant: sel is a true selector. tv, ok := c.info.Types[sel.X] if !ok { return fmt.Errorf("cannot resolve %s", sel.X) } return c.methodsAndFields(tv.Type, tv.Addressable()) } func (c *completer) packageMembers(pkg *types.PkgName) { scope := pkg.Imported().Scope() for _, name := range scope.Names() { c.found(scope.Lookup(name), stdScore) } } func (c *completer) methodsAndFields(typ types.Type, addressable bool) error { var mset *types.MethodSet 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) } for i := 0; i < mset.Len(); i++ { c.found(mset.At(i).Obj(), stdScore) } // Add fields of T. for _, f := range fieldSelections(typ) { c.found(f, stdScore) } return nil } // lexical finds completions in the lexical environment. func (c *completer) lexical() error { var scopes []*types.Scope // scopes[i], where i) 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) { return nil } tv, ok := info.Types[n] if !ok { return nil } clInfo := compLitInfo{ cl: n, clType: 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 of the function enclosing the given position. func enclosingFunction(path []ast.Node, pos token.Pos, info *types.Info) *types.Signature { for _, node := range path { switch t := node.(type) { case *ast.FuncDecl: if obj, ok := info.Defs[t.Name]; ok { return obj.Type().(*types.Signature) } case *ast.FuncLit: if typ, ok := info.Types[t]; ok { return typ.Type.(*types.Signature) } } } 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 int const ( star typeModifier = iota // dereference operator for expressions, pointer indicator for types reference // reference ("&") operator chanRead // channel read ("<-") operator ) // 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 // 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 // convertibleTo is a type our candidate type must be convertible to. convertibleTo types.Type } // expectedType returns information about the expected type for an expression at // the query position. func expectedType(c *completer) typeInference { if ti := expectTypeName(c); ti.wantTypeName { return ti } if c.enclosingCompositeLiteral != nil { return typeInference{objType: c.expectedCompositeLiteralType()} } var ( modifiers []typeModifier typ types.Type convertibleTo types.Type ) 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.info.Types[e]; ok { typ = 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.info.Types[node.Lhs[i]]; ok { typ = tv.Type break Nodes } } return typeInference{} 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.info); typ != nil { convertibleTo = typ break Nodes } if tv, ok := c.info.Types[node.Fun]; ok { if sig, ok := tv.Type.(*types.Signature); ok { if sig.Params().Len() == 0 { return typeInference{} } i := indexExprAtPos(c.pos, node.Args) // Make sure not to run past the end of expected parameters. if i >= sig.Params().Len() { i = sig.Params().Len() - 1 } typ = sig.Params().At(i).Type() break Nodes } } } return typeInference{} case *ast.ReturnStmt: if sig := c.enclosingFunction; sig != nil { // 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() { typ = sig.Results().At(resultIdx).Type() break Nodes } } } return typeInference{} case *ast.CaseClause: if swtch, ok := findSwitchStmt(c.path[i+1:], c.pos, node).(*ast.SwitchStmt); ok { if tv, ok := c.info.Types[swtch.Tag]; ok { typ = tv.Type break Nodes } } return typeInference{} case *ast.SliceExpr: // Make sure position falls within the brackets (e.g. "foo[a:<>]"). if node.Lbrack < c.pos && c.pos <= node.Rbrack { typ = types.Typ[types.Int] break Nodes } return typeInference{} 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.info.Types[node.X]; ok { switch t := tv.Type.Underlying().(type) { case *types.Map: typ = t.Key() case *types.Slice, *types.Array: typ = types.Typ[types.Int] default: return typeInference{} } break Nodes } } return typeInference{} 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.info.Types[node.Chan]; ok { if ch, ok := tv.Type.Underlying().(*types.Chan); ok { typ = ch.Elem() break Nodes } } } return typeInference{} case *ast.StarExpr: modifiers = append(modifiers, star) case *ast.UnaryExpr: switch node.Op { case token.AND: modifiers = append(modifiers, reference) case token.ARROW: modifiers = append(modifiers, chanRead) } default: if breaksExpectedTypeInference(node) { return typeInference{} } } } return typeInference{ objType: typ, modifiers: modifiers, convertibleTo: convertibleTo, } } // 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 { 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.modifiers { switch mod { case star: // For every "*" indicator, add a pointer layer to type name. typ = types.NewPointer(typ) } } return typ } // 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) typeInference { var ( wantTypeName bool modifiers []typeModifier assertableFrom types.Type ) Nodes: for i, p := range c.path { switch n := p.(type) { case *ast.FuncDecl: // Expect type names in a function declaration receiver, params and results. if r := n.Recv; r != nil && r.Pos() <= c.pos && c.pos <= r.End() { wantTypeName = true break Nodes } if t := n.Type; t != nil { if p := t.Params; p != nil && p.Pos() <= c.pos && c.pos <= p.End() { wantTypeName = true break Nodes } if r := t.Results; r != nil && r.Pos() <= c.pos && c.pos <= r.End() { wantTypeName = true break Nodes } } return typeInference{} 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.info.TypeOf(ta.X) return false } return true }) wantTypeName = true break Nodes } return typeInference{} 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.info.TypeOf(n.X) wantTypeName = true break Nodes } return typeInference{} case *ast.StarExpr: modifiers = append(modifiers, star) default: if breaksExpectedTypeInference(p) { return typeInference{} } } } return typeInference{ wantTypeName: wantTypeName, modifiers: modifiers, assertableFrom: assertableFrom, } } // matchingType reports whether an object is a good completion candidate // in the context of the expected type. func (c *completer) matchingType(cand *candidate) bool { if isTypeName(cand.obj) { return c.matchingTypeName(cand) } objType := cand.obj.Type() // 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(candType types.Type) bool { // Take into account any type modifiers on the expected type. candType = c.expectedType.applyTypeModifiers(candType) if c.expectedType.objType != nil { wantType := types.Default(c.expectedType.objType) // Handle untyped values specially since AssignableTo gives false negatives // for them (see https://golang.org/issue/32146). if candBasic, ok := candType.(*types.Basic); ok && candBasic.Info()&types.IsUntyped > 0 { if wantBasic, ok := wantType.Underlying().(*types.Basic); ok { // 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. return candBasic.Info()&types.IsConstType == wantBasic.Info()&types.IsConstType } return false } // 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, wantType) } return false } if typeMatches(objType) { // 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 := objType.Underlying().(*types.Signature); ok && sig.Results().Len() == 1 { if typeMatches(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 } } if c.expectedType.convertibleTo != nil { return types.ConvertibleTo(objType, 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.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.assertableFrom, actual) { return false } if intf, ok := c.expectedType.assertableFrom.Underlying().(*types.Interface); ok { if !types.AssertableTo(intf, actual) { return false } } } // Default to saying any type name is a match. return true }