1 // Copyright 2020 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
18 "golang.org/x/tools/internal/event"
19 "golang.org/x/tools/internal/lsp/debug/tag"
20 "golang.org/x/tools/internal/lsp/protocol"
23 // FormatType returns the detail and kind for a types.Type.
24 func FormatType(typ types.Type, qf types.Qualifier) (detail string, kind protocol.CompletionItemKind) {
25 if types.IsInterface(typ) {
26 detail = "interface{...}"
27 kind = protocol.InterfaceCompletion
28 } else if _, ok := typ.(*types.Struct); ok {
29 detail = "struct{...}"
30 kind = protocol.StructCompletion
31 } else if typ != typ.Underlying() {
32 detail, kind = FormatType(typ.Underlying(), qf)
34 detail = types.TypeString(typ, qf)
35 kind = protocol.ClassCompletion
40 type signature struct {
42 params, results []string
47 func (s *signature) Format() string {
50 for i, p := range s.params {
58 // Add space between parameters and results.
59 if len(s.results) > 0 {
62 if s.needResultParens {
65 for i, r := range s.results {
71 if s.needResultParens {
77 func (s *signature) Params() []string {
81 // NewBuiltinSignature returns signature for the builtin object with a given
82 // name, if a builtin object with the name exists.
83 func NewBuiltinSignature(ctx context.Context, s Snapshot, name string) (*signature, error) {
84 builtin, err := s.BuiltinPackage(ctx)
88 obj := builtin.Package.Scope.Lookup(name)
90 return nil, fmt.Errorf("no builtin object for %s", name)
92 decl, ok := obj.Decl.(*ast.FuncDecl)
94 return nil, fmt.Errorf("no function declaration for builtin: %s", name)
97 return nil, fmt.Errorf("no type for builtin decl %s", decl.Name)
100 if decl.Type.Params.List != nil {
101 numParams := len(decl.Type.Params.List)
102 lastParam := decl.Type.Params.List[numParams-1]
103 if _, ok := lastParam.Type.(*ast.Ellipsis); ok {
107 params, _ := formatFieldList(ctx, s, decl.Type.Params, variadic)
108 results, needResultParens := formatFieldList(ctx, s, decl.Type.Results, false)
110 switch s.View().Options().HoverKind {
111 case SynopsisDocumentation:
113 case NoDocumentation:
119 needResultParens: needResultParens,
126 var replacer = strings.NewReplacer(
127 `ComplexType`, `complex128`,
128 `FloatType`, `float64`,
129 `IntegerType`, `int`,
132 func formatFieldList(ctx context.Context, snapshot Snapshot, list *ast.FieldList, variadic bool) ([]string, bool) {
136 var writeResultParens bool
138 for i := 0; i < len(list.List); i++ {
140 writeResultParens = true
143 cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 4}
145 if err := cfg.Fprint(b, snapshot.FileSet(), p.Type); err != nil {
146 event.Error(ctx, "unable to print type", nil, tag.Type.Of(p.Type))
149 typ := replacer.Replace(b.String())
150 if len(p.Names) == 0 {
151 result = append(result, typ)
153 for _, name := range p.Names {
156 writeResultParens = true
158 result = append(result, fmt.Sprintf("%s %s", name.Name, typ))
160 result = append(result, typ)
165 result[len(result)-1] = strings.Replace(result[len(result)-1], "[]", "...", 1)
167 return result, writeResultParens
170 // NewSignature returns formatted signature for a types.Signature struct.
171 func NewSignature(ctx context.Context, s Snapshot, pkg Package, sig *types.Signature, comment *ast.CommentGroup, qf types.Qualifier) *signature {
172 params := make([]string, 0, sig.Params().Len())
173 for i := 0; i < sig.Params().Len(); i++ {
174 el := sig.Params().At(i)
175 typ := FormatVarType(ctx, s, pkg, el, qf)
178 p = el.Name() + " " + typ
180 params = append(params, p)
182 var needResultParens bool
183 results := make([]string, 0, sig.Results().Len())
184 for i := 0; i < sig.Results().Len(); i++ {
186 needResultParens = true
188 el := sig.Results().At(i)
189 typ := FormatVarType(ctx, s, pkg, el, qf)
191 results = append(results, typ)
194 needResultParens = true
196 results = append(results, el.Name()+" "+typ)
203 switch s.View().Options().HoverKind {
204 case SynopsisDocumentation:
206 case NoDocumentation:
213 variadic: sig.Variadic(),
214 needResultParens: needResultParens,
218 // FormatVarType formats a *types.Var, accounting for type aliases.
219 // To do this, it looks in the AST of the file in which the object is declared.
220 // On any errors, it always fallbacks back to types.TypeString.
221 func FormatVarType(ctx context.Context, snapshot Snapshot, srcpkg Package, obj *types.Var, qf types.Qualifier) string {
222 pgf, pkg, err := FindPosInPackage(snapshot, srcpkg, obj.Pos())
224 return types.TypeString(obj.Type(), qf)
227 expr, err := varType(ctx, snapshot, pgf, obj)
229 return types.TypeString(obj.Type(), qf)
232 // The type names in the AST may not be correctly qualified.
233 // Determine the package name to use based on the package that originated
234 // the query and the package in which the type is declared.
235 // We then qualify the value by cloning the AST node and editing it.
236 clonedInfo := make(map[token.Pos]*types.PkgName)
237 qualified := cloneExpr(expr, pkg.GetTypesInfo(), clonedInfo)
239 // If the request came from a different package than the one in which the
240 // types are defined, we may need to modify the qualifiers.
241 qualified = qualifyExpr(qualified, srcpkg, pkg, clonedInfo, qf)
242 fmted := FormatNode(snapshot.FileSet(), qualified)
246 // varType returns the type expression for a *types.Var.
247 func varType(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile, obj *types.Var) (ast.Expr, error) {
248 posToField, err := snapshot.PosToField(ctx, pgf)
252 field := posToField[obj.Pos()]
254 return nil, fmt.Errorf("no declaration for object %s", obj.Name())
256 typ, ok := field.Type.(ast.Expr)
258 return nil, fmt.Errorf("unexpected type for node (%T)", field.Type)
263 // qualifyExpr applies the "pkgName." prefix to any *ast.Ident in the expr.
264 func qualifyExpr(expr ast.Expr, srcpkg, pkg Package, clonedInfo map[token.Pos]*types.PkgName, qf types.Qualifier) ast.Expr {
265 ast.Inspect(expr, func(n ast.Node) bool {
266 switch n := n.(type) {
267 case *ast.ArrayType, *ast.ChanType, *ast.Ellipsis,
268 *ast.FuncType, *ast.MapType, *ast.ParenExpr,
269 *ast.StarExpr, *ast.StructType:
270 // These are the only types that are cloned by cloneExpr below,
271 // so these are the only types that we can traverse and potentially
272 // modify. This is not an ideal approach, but it works for now.
274 case *ast.SelectorExpr:
275 // We may need to change any selectors in which the X is a package
276 // name and the Sel is exported.
277 x, ok := n.X.(*ast.Ident)
281 obj, ok := clonedInfo[x.Pos()]
285 x.Name = qf(obj.Imported())
291 // Only add the qualifier if the identifier is exported.
292 if ast.IsExported(n.Name) {
293 pkgName := qf(pkg.GetTypes())
294 n.Name = pkgName + "." + n.Name
302 // cloneExpr only clones expressions that appear in the parameters or return
303 // values of a function declaration. The original expression may be returned
304 // to the caller in 2 cases:
305 // (1) The expression has no pointer fields.
306 // (2) The expression cannot appear in an *ast.FuncType, making it
307 // unnecessary to clone.
308 // This function also keeps track of selector expressions in which the X is a
309 // package name and marks them in a map along with their type information, so
310 // that this information can be used when rewriting the expression.
312 // NOTE: This function is tailored to the use case of qualifyExpr, and should
313 // be used with caution.
314 func cloneExpr(expr ast.Expr, info *types.Info, clonedInfo map[token.Pos]*types.PkgName) ast.Expr {
315 switch expr := expr.(type) {
317 return &ast.ArrayType{
319 Elt: cloneExpr(expr.Elt, info, clonedInfo),
323 return &ast.ChanType{
327 Value: cloneExpr(expr.Value, info, clonedInfo),
330 return &ast.Ellipsis{
331 Ellipsis: expr.Ellipsis,
332 Elt: cloneExpr(expr.Elt, info, clonedInfo),
335 return &ast.FuncType{
337 Params: cloneFieldList(expr.Params, info, clonedInfo),
338 Results: cloneFieldList(expr.Results, info, clonedInfo),
341 return cloneIdent(expr)
345 Key: cloneExpr(expr.Key, info, clonedInfo),
346 Value: cloneExpr(expr.Value, info, clonedInfo),
349 return &ast.ParenExpr{
352 X: cloneExpr(expr.X, info, clonedInfo),
354 case *ast.SelectorExpr:
355 s := &ast.SelectorExpr{
356 Sel: cloneIdent(expr.Sel),
357 X: cloneExpr(expr.X, info, clonedInfo),
359 if x, ok := expr.X.(*ast.Ident); ok && ast.IsExported(expr.Sel.Name) {
360 if obj, ok := info.ObjectOf(x).(*types.PkgName); ok {
361 clonedInfo[s.X.Pos()] = obj
366 return &ast.StarExpr{
368 X: cloneExpr(expr.X, info, clonedInfo),
370 case *ast.StructType:
371 return &ast.StructType{
373 Fields: cloneFieldList(expr.Fields, info, clonedInfo),
374 Incomplete: expr.Incomplete,
381 func cloneFieldList(fl *ast.FieldList, info *types.Info, clonedInfo map[token.Pos]*types.PkgName) *ast.FieldList {
386 return &ast.FieldList{
391 list := make([]*ast.Field, 0, len(fl.List))
392 for _, f := range fl.List {
393 var names []*ast.Ident
394 for _, n := range f.Names {
395 names = append(names, cloneIdent(n))
397 list = append(list, &ast.Field{
402 Type: cloneExpr(f.Type, info, clonedInfo),
405 return &ast.FieldList{
412 func cloneIdent(ident *ast.Ident) *ast.Ident {
414 NamePos: ident.NamePos,