diff options
Diffstat (limited to 'internal/lsp/source/types_format.go')
-rw-r--r-- | internal/lsp/source/types_format.go | 459 |
1 files changed, 0 insertions, 459 deletions
diff --git a/internal/lsp/source/types_format.go b/internal/lsp/source/types_format.go deleted file mode 100644 index fcbf228ec..000000000 --- a/internal/lsp/source/types_format.go +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright 2020 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 ( - "bytes" - "context" - "fmt" - "go/ast" - "go/doc" - "go/printer" - "go/token" - "go/types" - "strings" - - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/lsp/debug/tag" - "golang.org/x/tools/internal/lsp/protocol" - "golang.org/x/tools/internal/typeparams" -) - -// FormatType returns the detail and kind for a types.Type. -func FormatType(typ types.Type, qf types.Qualifier) (detail string, kind protocol.CompletionItemKind) { - if types.IsInterface(typ) { - detail = "interface{...}" - kind = protocol.InterfaceCompletion - } else if _, ok := typ.(*types.Struct); ok { - detail = "struct{...}" - kind = protocol.StructCompletion - } else if typ != typ.Underlying() { - detail, kind = FormatType(typ.Underlying(), qf) - } else { - detail = types.TypeString(typ, qf) - kind = protocol.ClassCompletion - } - return detail, kind -} - -type signature struct { - name, doc string - typeParams, params, results []string - variadic bool - needResultParens bool -} - -func (s *signature) Format() string { - var b strings.Builder - b.WriteByte('(') - for i, p := range s.params { - if i > 0 { - b.WriteString(", ") - } - b.WriteString(p) - } - b.WriteByte(')') - - // Add space between parameters and results. - if len(s.results) > 0 { - b.WriteByte(' ') - } - if s.needResultParens { - b.WriteByte('(') - } - for i, r := range s.results { - if i > 0 { - b.WriteString(", ") - } - b.WriteString(r) - } - if s.needResultParens { - b.WriteByte(')') - } - return b.String() -} - -func (s *signature) TypeParams() []string { - return s.typeParams -} - -func (s *signature) Params() []string { - return s.params -} - -// NewBuiltinSignature returns signature for the builtin object with a given -// name, if a builtin object with the name exists. -func NewBuiltinSignature(ctx context.Context, s Snapshot, name string) (*signature, error) { - builtin, err := s.BuiltinFile(ctx) - if err != nil { - return nil, err - } - obj := builtin.File.Scope.Lookup(name) - if obj == nil { - return nil, fmt.Errorf("no builtin object for %s", name) - } - decl, ok := obj.Decl.(*ast.FuncDecl) - if !ok { - return nil, fmt.Errorf("no function declaration for builtin: %s", name) - } - if decl.Type == nil { - return nil, fmt.Errorf("no type for builtin decl %s", decl.Name) - } - var variadic bool - if decl.Type.Params.List != nil { - numParams := len(decl.Type.Params.List) - lastParam := decl.Type.Params.List[numParams-1] - if _, ok := lastParam.Type.(*ast.Ellipsis); ok { - variadic = true - } - } - params, _ := formatFieldList(ctx, s, decl.Type.Params, variadic) - results, needResultParens := formatFieldList(ctx, s, decl.Type.Results, false) - d := decl.Doc.Text() - switch s.View().Options().HoverKind { - case SynopsisDocumentation: - d = doc.Synopsis(d) - case NoDocumentation: - d = "" - } - return &signature{ - doc: d, - name: name, - needResultParens: needResultParens, - params: params, - results: results, - variadic: variadic, - }, nil -} - -var replacer = strings.NewReplacer( - `ComplexType`, `complex128`, - `FloatType`, `float64`, - `IntegerType`, `int`, -) - -func formatFieldList(ctx context.Context, snapshot Snapshot, list *ast.FieldList, variadic bool) ([]string, bool) { - if list == nil { - return nil, false - } - var writeResultParens bool - var result []string - for i := 0; i < len(list.List); i++ { - if i >= 1 { - writeResultParens = true - } - p := list.List[i] - cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 4} - b := &bytes.Buffer{} - if err := cfg.Fprint(b, snapshot.FileSet(), p.Type); err != nil { - event.Error(ctx, "unable to print type", nil, tag.Type.Of(p.Type)) - continue - } - typ := replacer.Replace(b.String()) - if len(p.Names) == 0 { - result = append(result, typ) - } - for _, name := range p.Names { - if name.Name != "" { - if i == 0 { - writeResultParens = true - } - result = append(result, fmt.Sprintf("%s %s", name.Name, typ)) - } else { - result = append(result, typ) - } - } - } - if variadic { - result[len(result)-1] = strings.Replace(result[len(result)-1], "[]", "...", 1) - } - return result, writeResultParens -} - -// FormatTypeParams turns TypeParamList into its Go representation, such as: -// [T, Y]. Note that it does not print constraints as this is mainly used for -// formatting type params in method receivers. -func FormatTypeParams(tparams *typeparams.TypeParamList) string { - if tparams == nil || tparams.Len() == 0 { - return "" - } - var buf bytes.Buffer - buf.WriteByte('[') - for i := 0; i < tparams.Len(); i++ { - if i > 0 { - buf.WriteString(", ") - } - buf.WriteString(tparams.At(i).Obj().Name()) - } - buf.WriteByte(']') - return buf.String() -} - -// NewSignature returns formatted signature for a types.Signature struct. -func NewSignature(ctx context.Context, s Snapshot, pkg Package, sig *types.Signature, comment *ast.CommentGroup, qf types.Qualifier) *signature { - var tparams []string - tpList := typeparams.ForSignature(sig) - for i := 0; i < tpList.Len(); i++ { - tparam := tpList.At(i) - // TODO: is it possible to reuse the logic from FormatVarType here? - s := tparam.Obj().Name() + " " + tparam.Constraint().String() - tparams = append(tparams, s) - } - - params := make([]string, 0, sig.Params().Len()) - for i := 0; i < sig.Params().Len(); i++ { - el := sig.Params().At(i) - typ := FormatVarType(ctx, s, pkg, el, qf) - p := typ - if el.Name() != "" { - p = el.Name() + " " + typ - } - params = append(params, p) - } - - var needResultParens bool - results := make([]string, 0, sig.Results().Len()) - for i := 0; i < sig.Results().Len(); i++ { - if i >= 1 { - needResultParens = true - } - el := sig.Results().At(i) - typ := FormatVarType(ctx, s, pkg, el, qf) - if el.Name() == "" { - results = append(results, typ) - } else { - if i == 0 { - needResultParens = true - } - results = append(results, el.Name()+" "+typ) - } - } - var d string - if comment != nil { - d = comment.Text() - } - switch s.View().Options().HoverKind { - case SynopsisDocumentation: - d = doc.Synopsis(d) - case NoDocumentation: - d = "" - } - return &signature{ - doc: d, - typeParams: tparams, - params: params, - results: results, - variadic: sig.Variadic(), - needResultParens: needResultParens, - } -} - -// FormatVarType formats a *types.Var, accounting for type aliases. -// To do this, it looks in the AST of the file in which the object is declared. -// On any errors, it always falls back to types.TypeString. -func FormatVarType(ctx context.Context, snapshot Snapshot, srcpkg Package, obj *types.Var, qf types.Qualifier) string { - pkg, err := FindPackageFromPos(ctx, snapshot, obj.Pos()) - if err != nil { - return types.TypeString(obj.Type(), qf) - } - - expr, err := varType(ctx, snapshot, pkg, obj) - if err != nil { - return types.TypeString(obj.Type(), qf) - } - - // If the given expr refers to a type parameter, then use the - // object's Type instead of the type parameter declaration. This helps - // format the instantiated type as opposed to the original undeclared - // generic type. - if typeparams.IsTypeParam(pkg.GetTypesInfo().Types[expr].Type) { - return types.TypeString(obj.Type(), qf) - } - - // The type names in the AST may not be correctly qualified. - // Determine the package name to use based on the package that originated - // the query and the package in which the type is declared. - // We then qualify the value by cloning the AST node and editing it. - clonedInfo := make(map[token.Pos]*types.PkgName) - qualified := cloneExpr(expr, pkg.GetTypesInfo(), clonedInfo) - - // If the request came from a different package than the one in which the - // types are defined, we may need to modify the qualifiers. - qualified = qualifyExpr(qualified, srcpkg, pkg, clonedInfo, qf) - fmted := FormatNode(snapshot.FileSet(), qualified) - return fmted -} - -// varType returns the type expression for a *types.Var. -func varType(ctx context.Context, snapshot Snapshot, pkg Package, obj *types.Var) (ast.Expr, error) { - field, err := snapshot.PosToField(ctx, pkg, obj.Pos()) - if err != nil { - return nil, err - } - if field == nil { - return nil, fmt.Errorf("no declaration for object %s", obj.Name()) - } - return field.Type, nil -} - -// qualifyExpr applies the "pkgName." prefix to any *ast.Ident in the expr. -func qualifyExpr(expr ast.Expr, srcpkg, pkg Package, clonedInfo map[token.Pos]*types.PkgName, qf types.Qualifier) ast.Expr { - ast.Inspect(expr, func(n ast.Node) bool { - switch n := n.(type) { - case *ast.ArrayType, *ast.ChanType, *ast.Ellipsis, - *ast.FuncType, *ast.MapType, *ast.ParenExpr, - *ast.StarExpr, *ast.StructType, *ast.FieldList, *ast.Field: - // These are the only types that are cloned by cloneExpr below, - // so these are the only types that we can traverse and potentially - // modify. This is not an ideal approach, but it works for now. - - // TODO(rFindley): can we eliminate this filtering entirely? This caused - // bugs in the past (golang/go#50539) - return true - case *ast.SelectorExpr: - // We may need to change any selectors in which the X is a package - // name and the Sel is exported. - x, ok := n.X.(*ast.Ident) - if !ok { - return false - } - obj, ok := clonedInfo[x.Pos()] - if !ok { - return false - } - x.Name = qf(obj.Imported()) - return false - case *ast.Ident: - if srcpkg == pkg { - return false - } - // Only add the qualifier if the identifier is exported. - if ast.IsExported(n.Name) { - pkgName := qf(pkg.GetTypes()) - n.Name = pkgName + "." + n.Name - } - } - return false - }) - return expr -} - -// cloneExpr only clones expressions that appear in the parameters or return -// values of a function declaration. The original expression may be returned -// to the caller in 2 cases: -// (1) The expression has no pointer fields. -// (2) The expression cannot appear in an *ast.FuncType, making it -// unnecessary to clone. -// This function also keeps track of selector expressions in which the X is a -// package name and marks them in a map along with their type information, so -// that this information can be used when rewriting the expression. -// -// NOTE: This function is tailored to the use case of qualifyExpr, and should -// be used with caution. -func cloneExpr(expr ast.Expr, info *types.Info, clonedInfo map[token.Pos]*types.PkgName) ast.Expr { - switch expr := expr.(type) { - case *ast.ArrayType: - return &ast.ArrayType{ - Lbrack: expr.Lbrack, - Elt: cloneExpr(expr.Elt, info, clonedInfo), - Len: expr.Len, - } - case *ast.ChanType: - return &ast.ChanType{ - Arrow: expr.Arrow, - Begin: expr.Begin, - Dir: expr.Dir, - Value: cloneExpr(expr.Value, info, clonedInfo), - } - case *ast.Ellipsis: - return &ast.Ellipsis{ - Ellipsis: expr.Ellipsis, - Elt: cloneExpr(expr.Elt, info, clonedInfo), - } - case *ast.FuncType: - return &ast.FuncType{ - Func: expr.Func, - Params: cloneFieldList(expr.Params, info, clonedInfo), - Results: cloneFieldList(expr.Results, info, clonedInfo), - } - case *ast.Ident: - return cloneIdent(expr) - case *ast.MapType: - return &ast.MapType{ - Map: expr.Map, - Key: cloneExpr(expr.Key, info, clonedInfo), - Value: cloneExpr(expr.Value, info, clonedInfo), - } - case *ast.ParenExpr: - return &ast.ParenExpr{ - Lparen: expr.Lparen, - Rparen: expr.Rparen, - X: cloneExpr(expr.X, info, clonedInfo), - } - case *ast.SelectorExpr: - s := &ast.SelectorExpr{ - Sel: cloneIdent(expr.Sel), - X: cloneExpr(expr.X, info, clonedInfo), - } - if x, ok := expr.X.(*ast.Ident); ok && ast.IsExported(expr.Sel.Name) { - if obj, ok := info.ObjectOf(x).(*types.PkgName); ok { - clonedInfo[s.X.Pos()] = obj - } - } - return s - case *ast.StarExpr: - return &ast.StarExpr{ - Star: expr.Star, - X: cloneExpr(expr.X, info, clonedInfo), - } - case *ast.StructType: - return &ast.StructType{ - Struct: expr.Struct, - Fields: cloneFieldList(expr.Fields, info, clonedInfo), - Incomplete: expr.Incomplete, - } - default: - return expr - } -} - -func cloneFieldList(fl *ast.FieldList, info *types.Info, clonedInfo map[token.Pos]*types.PkgName) *ast.FieldList { - if fl == nil { - return nil - } - if fl.List == nil { - return &ast.FieldList{ - Closing: fl.Closing, - Opening: fl.Opening, - } - } - list := make([]*ast.Field, 0, len(fl.List)) - for _, f := range fl.List { - var names []*ast.Ident - for _, n := range f.Names { - names = append(names, cloneIdent(n)) - } - list = append(list, &ast.Field{ - Comment: f.Comment, - Doc: f.Doc, - Names: names, - Tag: f.Tag, - Type: cloneExpr(f.Type, info, clonedInfo), - }) - } - return &ast.FieldList{ - Closing: fl.Closing, - Opening: fl.Opening, - List: list, - } -} - -func cloneIdent(ident *ast.Ident) *ast.Ident { - return &ast.Ident{ - NamePos: ident.NamePos, - Name: ident.Name, - Obj: ident.Obj, - } -} |