github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/internal/lsp/cache/symbols.go (about) 1 // Copyright 2021 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 cache 6 7 import ( 8 "context" 9 "go/ast" 10 "go/token" 11 "go/types" 12 "strings" 13 14 "github.com/powerman/golang-tools/internal/lsp/protocol" 15 "github.com/powerman/golang-tools/internal/lsp/source" 16 "github.com/powerman/golang-tools/internal/memoize" 17 "github.com/powerman/golang-tools/internal/span" 18 ) 19 20 type symbolHandle struct { 21 handle *memoize.Handle 22 23 fh source.FileHandle 24 25 // key is the hashed key for the package. 26 key symbolHandleKey 27 } 28 29 // symbolData contains the data produced by extracting symbols from a file. 30 type symbolData struct { 31 symbols []source.Symbol 32 err error 33 } 34 35 type symbolHandleKey string 36 37 func (s *snapshot) buildSymbolHandle(ctx context.Context, fh source.FileHandle) *symbolHandle { 38 if h := s.getSymbolHandle(fh.URI()); h != nil { 39 return h 40 } 41 key := symbolHandleKey(fh.FileIdentity().Hash) 42 h := s.generation.Bind(key, func(ctx context.Context, arg memoize.Arg) interface{} { 43 snapshot := arg.(*snapshot) 44 data := &symbolData{} 45 data.symbols, data.err = symbolize(ctx, snapshot, fh) 46 return data 47 }, nil) 48 49 sh := &symbolHandle{ 50 handle: h, 51 fh: fh, 52 key: key, 53 } 54 return s.addSymbolHandle(sh) 55 } 56 57 // symbolize extracts symbols from a file. It does not parse the file through the cache. 58 func symbolize(ctx context.Context, snapshot *snapshot, fh source.FileHandle) ([]source.Symbol, error) { 59 var w symbolWalker 60 fset := token.NewFileSet() // don't use snapshot.FileSet, as that would needlessly leak memory. 61 data := parseGo(ctx, fset, fh, source.ParseFull) 62 if data.parsed != nil && data.parsed.File != nil { 63 w.curFile = data.parsed 64 w.curURI = protocol.URIFromSpanURI(data.parsed.URI) 65 w.fileDecls(data.parsed.File.Decls) 66 } 67 return w.symbols, w.firstError 68 } 69 70 type symbolWalker struct { 71 curFile *source.ParsedGoFile 72 curURI protocol.DocumentURI 73 symbols []source.Symbol 74 firstError error 75 } 76 77 func (w *symbolWalker) atNode(node ast.Node, name string, kind protocol.SymbolKind, path ...*ast.Ident) { 78 var b strings.Builder 79 for _, ident := range path { 80 if ident != nil { 81 b.WriteString(ident.Name) 82 b.WriteString(".") 83 } 84 } 85 b.WriteString(name) 86 87 rng, err := fileRange(w.curFile, node.Pos(), node.End()) 88 if err != nil { 89 w.error(err) 90 return 91 } 92 sym := source.Symbol{ 93 Name: b.String(), 94 Kind: kind, 95 Range: rng, 96 } 97 w.symbols = append(w.symbols, sym) 98 } 99 100 func (w *symbolWalker) error(err error) { 101 if err != nil && w.firstError == nil { 102 w.firstError = err 103 } 104 } 105 106 func fileRange(pgf *source.ParsedGoFile, start, end token.Pos) (protocol.Range, error) { 107 s, err := span.FileSpan(pgf.Tok, pgf.Mapper.Converter, start, end) 108 if err != nil { 109 return protocol.Range{}, nil 110 } 111 return pgf.Mapper.Range(s) 112 } 113 114 func (w *symbolWalker) fileDecls(decls []ast.Decl) { 115 for _, decl := range decls { 116 switch decl := decl.(type) { 117 case *ast.FuncDecl: 118 kind := protocol.Function 119 var recv *ast.Ident 120 if decl.Recv.NumFields() > 0 { 121 kind = protocol.Method 122 recv = unpackRecv(decl.Recv.List[0].Type) 123 } 124 w.atNode(decl.Name, decl.Name.Name, kind, recv) 125 case *ast.GenDecl: 126 for _, spec := range decl.Specs { 127 switch spec := spec.(type) { 128 case *ast.TypeSpec: 129 kind := guessKind(spec) 130 w.atNode(spec.Name, spec.Name.Name, kind) 131 w.walkType(spec.Type, spec.Name) 132 case *ast.ValueSpec: 133 for _, name := range spec.Names { 134 kind := protocol.Variable 135 if decl.Tok == token.CONST { 136 kind = protocol.Constant 137 } 138 w.atNode(name, name.Name, kind) 139 } 140 } 141 } 142 } 143 } 144 } 145 146 func guessKind(spec *ast.TypeSpec) protocol.SymbolKind { 147 switch spec.Type.(type) { 148 case *ast.InterfaceType: 149 return protocol.Interface 150 case *ast.StructType: 151 return protocol.Struct 152 case *ast.FuncType: 153 return protocol.Function 154 } 155 return protocol.Class 156 } 157 158 func unpackRecv(rtyp ast.Expr) *ast.Ident { 159 // Extract the receiver identifier. Lifted from go/types/resolver.go 160 L: 161 for { 162 switch t := rtyp.(type) { 163 case *ast.ParenExpr: 164 rtyp = t.X 165 case *ast.StarExpr: 166 rtyp = t.X 167 default: 168 break L 169 } 170 } 171 if name, _ := rtyp.(*ast.Ident); name != nil { 172 return name 173 } 174 return nil 175 } 176 177 // walkType processes symbols related to a type expression. path is path of 178 // nested type identifiers to the type expression. 179 func (w *symbolWalker) walkType(typ ast.Expr, path ...*ast.Ident) { 180 switch st := typ.(type) { 181 case *ast.StructType: 182 for _, field := range st.Fields.List { 183 w.walkField(field, protocol.Field, protocol.Field, path...) 184 } 185 case *ast.InterfaceType: 186 for _, field := range st.Methods.List { 187 w.walkField(field, protocol.Interface, protocol.Method, path...) 188 } 189 } 190 } 191 192 // walkField processes symbols related to the struct field or interface method. 193 // 194 // unnamedKind and namedKind are the symbol kinds if the field is resp. unnamed 195 // or named. path is the path of nested identifiers containing the field. 196 func (w *symbolWalker) walkField(field *ast.Field, unnamedKind, namedKind protocol.SymbolKind, path ...*ast.Ident) { 197 if len(field.Names) == 0 { 198 switch typ := field.Type.(type) { 199 case *ast.SelectorExpr: 200 // embedded qualified type 201 w.atNode(field, typ.Sel.Name, unnamedKind, path...) 202 default: 203 w.atNode(field, types.ExprString(field.Type), unnamedKind, path...) 204 } 205 } 206 for _, name := range field.Names { 207 w.atNode(name, name.Name, namedKind, path...) 208 w.walkType(field.Type, append(path, name)...) 209 } 210 }