golang.org/x/tools/gopls@v0.15.3/internal/golang/completion/format.go (about) 1 // Copyright 2019 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 "errors" 10 "fmt" 11 "go/ast" 12 "go/doc" 13 "go/types" 14 "strings" 15 16 "golang.org/x/tools/gopls/internal/golang" 17 "golang.org/x/tools/gopls/internal/golang/completion/snippet" 18 "golang.org/x/tools/gopls/internal/protocol" 19 "golang.org/x/tools/gopls/internal/util/safetoken" 20 "golang.org/x/tools/internal/event" 21 "golang.org/x/tools/internal/imports" 22 ) 23 24 var ( 25 errNoMatch = errors.New("not a surrounding match") 26 errLowScore = errors.New("not a high scoring candidate") 27 ) 28 29 // item formats a candidate to a CompletionItem. 30 func (c *completer) item(ctx context.Context, cand candidate) (CompletionItem, error) { 31 obj := cand.obj 32 33 // if the object isn't a valid match against the surrounding, return early. 34 matchScore := c.matcher.Score(cand.name) 35 if matchScore <= 0 { 36 return CompletionItem{}, errNoMatch 37 } 38 cand.score *= float64(matchScore) 39 40 // Ignore deep candidates that won't be in the MaxDeepCompletions anyway. 41 if len(cand.path) != 0 && !c.deepState.isHighScore(cand.score) { 42 return CompletionItem{}, errLowScore 43 } 44 45 // Handle builtin types separately. 46 if obj.Parent() == types.Universe { 47 return c.formatBuiltin(ctx, cand) 48 } 49 50 var ( 51 label = cand.name 52 detail = types.TypeString(obj.Type(), c.qf) 53 insert = label 54 kind = protocol.TextCompletion 55 snip snippet.Builder 56 protocolEdits []protocol.TextEdit 57 ) 58 if obj.Type() == nil { 59 detail = "" 60 } 61 if isTypeName(obj) && c.wantTypeParams() { 62 x := cand.obj.(*types.TypeName) 63 if named, ok := x.Type().(*types.Named); ok { 64 tp := named.TypeParams() 65 label += golang.FormatTypeParams(tp) 66 insert = label // maintain invariant above (label == insert) 67 } 68 } 69 70 snip.WriteText(insert) 71 72 switch obj := obj.(type) { 73 case *types.TypeName: 74 detail, kind = golang.FormatType(obj.Type(), c.qf) 75 case *types.Const: 76 kind = protocol.ConstantCompletion 77 case *types.Var: 78 if _, ok := obj.Type().(*types.Struct); ok { 79 detail = "struct{...}" // for anonymous structs 80 } else if obj.IsField() { 81 var err error 82 detail, err = golang.FormatVarType(ctx, c.snapshot, c.pkg, obj, c.qf, c.mq) 83 if err != nil { 84 return CompletionItem{}, err 85 } 86 } 87 if obj.IsField() { 88 kind = protocol.FieldCompletion 89 c.structFieldSnippet(cand, detail, &snip) 90 } else { 91 kind = protocol.VariableCompletion 92 } 93 if obj.Type() == nil { 94 break 95 } 96 case *types.Func: 97 sig, ok := obj.Type().Underlying().(*types.Signature) 98 if !ok { 99 break 100 } 101 kind = protocol.FunctionCompletion 102 if sig != nil && sig.Recv() != nil { 103 kind = protocol.MethodCompletion 104 } 105 case *types.PkgName: 106 kind = protocol.ModuleCompletion 107 detail = fmt.Sprintf("%q", obj.Imported().Path()) 108 case *types.Label: 109 kind = protocol.ConstantCompletion 110 detail = "label" 111 } 112 113 var prefix string 114 for _, mod := range cand.mods { 115 switch mod { 116 case reference: 117 prefix = "&" + prefix 118 case dereference: 119 prefix = "*" + prefix 120 case chanRead: 121 prefix = "<-" + prefix 122 } 123 } 124 125 var ( 126 suffix string 127 funcType = obj.Type() 128 ) 129 Suffixes: 130 for _, mod := range cand.mods { 131 switch mod { 132 case invoke: 133 if sig, ok := funcType.Underlying().(*types.Signature); ok { 134 s, err := golang.NewSignature(ctx, c.snapshot, c.pkg, sig, nil, c.qf, c.mq) 135 if err != nil { 136 return CompletionItem{}, err 137 } 138 139 tparams := s.TypeParams() 140 if len(tparams) > 0 { 141 // Eliminate the suffix of type parameters that are 142 // likely redundant because they can probably be 143 // inferred from the argument types (#51783). 144 // 145 // We don't bother doing the reverse inference from 146 // result types as result-only type parameters are 147 // quite unusual. 148 free := inferableTypeParams(sig) 149 for i := sig.TypeParams().Len() - 1; i >= 0; i-- { 150 tparam := sig.TypeParams().At(i) 151 if !free[tparam] { 152 break 153 } 154 tparams = tparams[:i] // eliminate 155 } 156 } 157 158 c.functionCallSnippet("", tparams, s.Params(), &snip) 159 if sig.Results().Len() == 1 { 160 funcType = sig.Results().At(0).Type() 161 } 162 detail = "func" + s.Format() 163 } 164 165 if !c.opts.snippets { 166 // Without snippets the candidate will not include "()". Don't 167 // add further suffixes since they will be invalid. For 168 // example, with snippets "foo()..." would become "foo..." 169 // without snippets if we added the dotDotDot. 170 break Suffixes 171 } 172 case takeSlice: 173 suffix += "[:]" 174 case takeDotDotDot: 175 suffix += "..." 176 case index: 177 snip.WriteText("[") 178 snip.WritePlaceholder(nil) 179 snip.WriteText("]") 180 } 181 } 182 183 // If this candidate needs an additional import statement, 184 // add the additional text edits needed. 185 if cand.imp != nil { 186 addlEdits, err := c.importEdits(cand.imp) 187 188 if err != nil { 189 return CompletionItem{}, err 190 } 191 192 protocolEdits = append(protocolEdits, addlEdits...) 193 if kind != protocol.ModuleCompletion { 194 if detail != "" { 195 detail += " " 196 } 197 detail += fmt.Sprintf("(from %q)", cand.imp.importPath) 198 } 199 } 200 201 if cand.convertTo != nil { 202 typeName := types.TypeString(cand.convertTo, c.qf) 203 204 switch t := cand.convertTo.(type) { 205 // We need extra parens when casting to these types. For example, 206 // we need "(*int)(foo)", not "*int(foo)". 207 case *types.Pointer, *types.Signature: 208 typeName = "(" + typeName + ")" 209 case *types.Basic: 210 // If the types are incompatible (as determined by typeMatches), then we 211 // must need a conversion here. However, if the target type is untyped, 212 // don't suggest converting to e.g. "untyped float" (golang/go#62141). 213 if t.Info()&types.IsUntyped != 0 { 214 typeName = types.TypeString(types.Default(cand.convertTo), c.qf) 215 } 216 } 217 218 prefix = typeName + "(" + prefix 219 suffix = ")" 220 } 221 222 if prefix != "" { 223 // If we are in a selector, add an edit to place prefix before selector. 224 if sel := enclosingSelector(c.path, c.pos); sel != nil { 225 edits, err := c.editText(sel.Pos(), sel.Pos(), prefix) 226 if err != nil { 227 return CompletionItem{}, err 228 } 229 protocolEdits = append(protocolEdits, edits...) 230 } else { 231 // If there is no selector, just stick the prefix at the start. 232 insert = prefix + insert 233 snip.PrependText(prefix) 234 } 235 } 236 237 if suffix != "" { 238 insert += suffix 239 snip.WriteText(suffix) 240 } 241 242 detail = strings.TrimPrefix(detail, "untyped ") 243 // override computed detail with provided detail, if something is provided. 244 if cand.detail != "" { 245 detail = cand.detail 246 } 247 item := CompletionItem{ 248 Label: label, 249 InsertText: insert, 250 AdditionalTextEdits: protocolEdits, 251 Detail: detail, 252 Kind: kind, 253 Score: cand.score, 254 Depth: len(cand.path), 255 snippet: &snip, 256 isSlice: isSlice(obj), 257 } 258 // If the user doesn't want documentation for completion items. 259 if !c.opts.documentation { 260 return item, nil 261 } 262 pos := safetoken.StartPosition(c.pkg.FileSet(), obj.Pos()) 263 264 // We ignore errors here, because some types, like "unsafe" or "error", 265 // may not have valid positions that we can use to get documentation. 266 if !pos.IsValid() { 267 return item, nil 268 } 269 270 comment, err := golang.HoverDocForObject(ctx, c.snapshot, c.pkg.FileSet(), obj) 271 if err != nil { 272 event.Error(ctx, fmt.Sprintf("failed to find Hover for %q", obj.Name()), err) 273 return item, nil 274 } 275 if c.opts.fullDocumentation { 276 item.Documentation = comment.Text() 277 } else { 278 item.Documentation = doc.Synopsis(comment.Text()) 279 } 280 // The desired pattern is `^// Deprecated`, but the prefix has been removed 281 // TODO(rfindley): It doesn't look like this does the right thing for 282 // multi-line comments. 283 if strings.HasPrefix(comment.Text(), "Deprecated") { 284 if c.snapshot.Options().CompletionTags { 285 item.Tags = []protocol.CompletionItemTag{protocol.ComplDeprecated} 286 } else if c.snapshot.Options().CompletionDeprecated { 287 item.Deprecated = true 288 } 289 } 290 291 return item, nil 292 } 293 294 // importEdits produces the text edits necessary to add the given import to the current file. 295 func (c *completer) importEdits(imp *importInfo) ([]protocol.TextEdit, error) { 296 if imp == nil { 297 return nil, nil 298 } 299 300 pgf, err := c.pkg.File(protocol.URIFromPath(c.filename)) 301 if err != nil { 302 return nil, err 303 } 304 305 return golang.ComputeOneImportFixEdits(c.snapshot, pgf, &imports.ImportFix{ 306 StmtInfo: imports.ImportInfo{ 307 ImportPath: imp.importPath, 308 Name: imp.name, 309 }, 310 // IdentName is unused on this path and is difficult to get. 311 FixType: imports.AddImport, 312 }) 313 } 314 315 func (c *completer) formatBuiltin(ctx context.Context, cand candidate) (CompletionItem, error) { 316 obj := cand.obj 317 item := CompletionItem{ 318 Label: obj.Name(), 319 InsertText: obj.Name(), 320 Score: cand.score, 321 } 322 switch obj.(type) { 323 case *types.Const: 324 item.Kind = protocol.ConstantCompletion 325 case *types.Builtin: 326 item.Kind = protocol.FunctionCompletion 327 sig, err := golang.NewBuiltinSignature(ctx, c.snapshot, obj.Name()) 328 if err != nil { 329 return CompletionItem{}, err 330 } 331 item.Detail = "func" + sig.Format() 332 item.snippet = &snippet.Builder{} 333 // The signature inferred for a built-in is instantiated, so TypeParams=∅. 334 c.functionCallSnippet(obj.Name(), sig.TypeParams(), sig.Params(), item.snippet) 335 case *types.TypeName: 336 if types.IsInterface(obj.Type()) { 337 item.Kind = protocol.InterfaceCompletion 338 } else { 339 item.Kind = protocol.ClassCompletion 340 } 341 case *types.Nil: 342 item.Kind = protocol.VariableCompletion 343 } 344 return item, nil 345 } 346 347 // decide if the type params (if any) should be part of the completion 348 // which only possible for types.Named and types.Signature 349 // (so far, only in receivers, e.g.; func (s *GENERIC[K, V])..., which is a types.Named) 350 func (c *completer) wantTypeParams() bool { 351 // Need to be lexically in a receiver, and a child of an IndexListExpr 352 // (but IndexListExpr only exists with go1.18) 353 start := c.path[0].Pos() 354 for i, nd := range c.path { 355 if fd, ok := nd.(*ast.FuncDecl); ok { 356 if i > 0 && fd.Recv != nil && start < fd.Recv.End() { 357 return true 358 } else { 359 return false 360 } 361 } 362 } 363 return false 364 } 365 366 // inferableTypeParams returns the set of type parameters 367 // of sig that are constrained by (inferred from) the argument types. 368 func inferableTypeParams(sig *types.Signature) map[*types.TypeParam]bool { 369 free := make(map[*types.TypeParam]bool) 370 371 // visit adds to free all the free type parameters of t. 372 var visit func(t types.Type) 373 visit = func(t types.Type) { 374 switch t := t.(type) { 375 case *types.Array: 376 visit(t.Elem()) 377 case *types.Chan: 378 visit(t.Elem()) 379 case *types.Map: 380 visit(t.Key()) 381 visit(t.Elem()) 382 case *types.Pointer: 383 visit(t.Elem()) 384 case *types.Slice: 385 visit(t.Elem()) 386 case *types.Interface: 387 for i := 0; i < t.NumExplicitMethods(); i++ { 388 visit(t.ExplicitMethod(i).Type()) 389 } 390 for i := 0; i < t.NumEmbeddeds(); i++ { 391 visit(t.EmbeddedType(i)) 392 } 393 case *types.Union: 394 for i := 0; i < t.Len(); i++ { 395 visit(t.Term(i).Type()) 396 } 397 case *types.Signature: 398 if tp := t.TypeParams(); tp != nil { 399 // Generic signatures only appear as the type of generic 400 // function declarations, so this isn't really reachable. 401 for i := 0; i < tp.Len(); i++ { 402 visit(tp.At(i).Constraint()) 403 } 404 } 405 visit(t.Params()) 406 visit(t.Results()) 407 case *types.Tuple: 408 for i := 0; i < t.Len(); i++ { 409 visit(t.At(i).Type()) 410 } 411 case *types.Struct: 412 for i := 0; i < t.NumFields(); i++ { 413 visit(t.Field(i).Type()) 414 } 415 case *types.TypeParam: 416 free[t] = true 417 case *types.Basic, *types.Named: 418 // nop 419 default: 420 panic(t) 421 } 422 } 423 424 visit(sig.Params()) 425 426 // Perform induction through constraints. 427 restart: 428 for i := 0; i < sig.TypeParams().Len(); i++ { 429 tp := sig.TypeParams().At(i) 430 if free[tp] { 431 n := len(free) 432 visit(tp.Constraint()) 433 if len(free) > n { 434 goto restart // iterate until fixed point 435 } 436 } 437 } 438 return free 439 }