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  }