golang.org/x/tools/gopls@v0.15.3/internal/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 "golang.org/x/tools/gopls/internal/file" 15 "golang.org/x/tools/gopls/internal/protocol" 16 "golang.org/x/tools/gopls/internal/util/astutil" 17 ) 18 19 // Symbol holds a precomputed symbol value. Note: we avoid using the 20 // protocol.SymbolInformation struct here in order to reduce the size of each 21 // symbol. 22 type Symbol struct { 23 Name string 24 Kind protocol.SymbolKind 25 Range protocol.Range 26 } 27 28 // symbolize returns the result of symbolizing the file identified by uri, using a cache. 29 func (s *Snapshot) symbolize(ctx context.Context, uri protocol.DocumentURI) ([]Symbol, error) { 30 31 s.mu.Lock() 32 entry, hit := s.symbolizeHandles.Get(uri) 33 s.mu.Unlock() 34 35 type symbolizeResult struct { 36 symbols []Symbol 37 err error 38 } 39 40 // Cache miss? 41 if !hit { 42 fh, err := s.ReadFile(ctx, uri) 43 if err != nil { 44 return nil, err 45 } 46 type symbolHandleKey file.Hash 47 key := symbolHandleKey(fh.Identity().Hash) 48 promise, release := s.store.Promise(key, func(ctx context.Context, arg interface{}) interface{} { 49 symbols, err := symbolizeImpl(ctx, arg.(*Snapshot), fh) 50 return symbolizeResult{symbols, err} 51 }) 52 53 entry = promise 54 55 s.mu.Lock() 56 s.symbolizeHandles.Set(uri, entry, func(_, _ interface{}) { release() }) 57 s.mu.Unlock() 58 } 59 60 // Await result. 61 v, err := s.awaitPromise(ctx, entry) 62 if err != nil { 63 return nil, err 64 } 65 res := v.(symbolizeResult) 66 return res.symbols, res.err 67 } 68 69 // symbolizeImpl reads and parses a file and extracts symbols from it. 70 func symbolizeImpl(ctx context.Context, snapshot *Snapshot, fh file.Handle) ([]Symbol, error) { 71 pgfs, err := snapshot.view.parseCache.parseFiles(ctx, token.NewFileSet(), ParseFull, false, fh) 72 if err != nil { 73 return nil, err 74 } 75 76 w := &symbolWalker{ 77 tokFile: pgfs[0].Tok, 78 mapper: pgfs[0].Mapper, 79 } 80 w.fileDecls(pgfs[0].File.Decls) 81 82 return w.symbols, w.firstError 83 } 84 85 type symbolWalker struct { 86 // for computing positions 87 tokFile *token.File 88 mapper *protocol.Mapper 89 90 symbols []Symbol 91 firstError error 92 } 93 94 func (w *symbolWalker) atNode(node ast.Node, name string, kind protocol.SymbolKind, path ...*ast.Ident) { 95 var b strings.Builder 96 for _, ident := range path { 97 if ident != nil { 98 b.WriteString(ident.Name) 99 b.WriteString(".") 100 } 101 } 102 b.WriteString(name) 103 104 rng, err := w.mapper.NodeRange(w.tokFile, node) 105 if err != nil { 106 w.error(err) 107 return 108 } 109 sym := Symbol{ 110 Name: b.String(), 111 Kind: kind, 112 Range: rng, 113 } 114 w.symbols = append(w.symbols, sym) 115 } 116 117 func (w *symbolWalker) error(err error) { 118 if err != nil && w.firstError == nil { 119 w.firstError = err 120 } 121 } 122 123 func (w *symbolWalker) fileDecls(decls []ast.Decl) { 124 for _, decl := range decls { 125 switch decl := decl.(type) { 126 case *ast.FuncDecl: 127 kind := protocol.Function 128 var recv *ast.Ident 129 if decl.Recv.NumFields() > 0 { 130 kind = protocol.Method 131 _, recv, _ = astutil.UnpackRecv(decl.Recv.List[0].Type) 132 } 133 w.atNode(decl.Name, decl.Name.Name, kind, recv) 134 case *ast.GenDecl: 135 for _, spec := range decl.Specs { 136 switch spec := spec.(type) { 137 case *ast.TypeSpec: 138 kind := guessKind(spec) 139 w.atNode(spec.Name, spec.Name.Name, kind) 140 w.walkType(spec.Type, spec.Name) 141 case *ast.ValueSpec: 142 for _, name := range spec.Names { 143 kind := protocol.Variable 144 if decl.Tok == token.CONST { 145 kind = protocol.Constant 146 } 147 w.atNode(name, name.Name, kind) 148 } 149 } 150 } 151 } 152 } 153 } 154 155 func guessKind(spec *ast.TypeSpec) protocol.SymbolKind { 156 switch spec.Type.(type) { 157 case *ast.InterfaceType: 158 return protocol.Interface 159 case *ast.StructType: 160 return protocol.Struct 161 case *ast.FuncType: 162 return protocol.Function 163 } 164 return protocol.Class 165 } 166 167 // walkType processes symbols related to a type expression. path is path of 168 // nested type identifiers to the type expression. 169 func (w *symbolWalker) walkType(typ ast.Expr, path ...*ast.Ident) { 170 switch st := typ.(type) { 171 case *ast.StructType: 172 for _, field := range st.Fields.List { 173 w.walkField(field, protocol.Field, protocol.Field, path...) 174 } 175 case *ast.InterfaceType: 176 for _, field := range st.Methods.List { 177 w.walkField(field, protocol.Interface, protocol.Method, path...) 178 } 179 } 180 } 181 182 // walkField processes symbols related to the struct field or interface method. 183 // 184 // unnamedKind and namedKind are the symbol kinds if the field is resp. unnamed 185 // or named. path is the path of nested identifiers containing the field. 186 func (w *symbolWalker) walkField(field *ast.Field, unnamedKind, namedKind protocol.SymbolKind, path ...*ast.Ident) { 187 if len(field.Names) == 0 { 188 switch typ := field.Type.(type) { 189 case *ast.SelectorExpr: 190 // embedded qualified type 191 w.atNode(field, typ.Sel.Name, unnamedKind, path...) 192 default: 193 w.atNode(field, types.ExprString(field.Type), unnamedKind, path...) 194 } 195 } 196 for _, name := range field.Names { 197 w.atNode(name, name.Name, namedKind, path...) 198 w.walkType(field.Type, append(path, name)...) 199 } 200 }