golang.org/x/tools/gopls@v0.15.3/internal/golang/symbols.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 golang 6 7 import ( 8 "context" 9 "fmt" 10 "go/ast" 11 "go/token" 12 "go/types" 13 14 "golang.org/x/tools/gopls/internal/cache" 15 "golang.org/x/tools/gopls/internal/file" 16 "golang.org/x/tools/gopls/internal/protocol" 17 "golang.org/x/tools/internal/event" 18 ) 19 20 func DocumentSymbols(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]protocol.DocumentSymbol, error) { 21 ctx, done := event.Start(ctx, "golang.DocumentSymbols") 22 defer done() 23 24 pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) 25 if err != nil { 26 return nil, fmt.Errorf("getting file for DocumentSymbols: %w", err) 27 } 28 29 // Build symbols for file declarations. When encountering a declaration with 30 // errors (typically because positions are invalid), we skip the declaration 31 // entirely. VS Code fails to show any symbols if one of the top-level 32 // symbols is missing position information. 33 var symbols []protocol.DocumentSymbol 34 for _, decl := range pgf.File.Decls { 35 switch decl := decl.(type) { 36 case *ast.FuncDecl: 37 if decl.Name.Name == "_" { 38 continue 39 } 40 fs, err := funcSymbol(pgf.Mapper, pgf.Tok, decl) 41 if err == nil { 42 // If function is a method, prepend the type of the method. 43 if decl.Recv != nil && len(decl.Recv.List) > 0 { 44 fs.Name = fmt.Sprintf("(%s).%s", types.ExprString(decl.Recv.List[0].Type), fs.Name) 45 } 46 symbols = append(symbols, fs) 47 } 48 case *ast.GenDecl: 49 for _, spec := range decl.Specs { 50 switch spec := spec.(type) { 51 case *ast.TypeSpec: 52 if spec.Name.Name == "_" { 53 continue 54 } 55 ts, err := typeSymbol(pgf.Mapper, pgf.Tok, spec) 56 if err == nil { 57 symbols = append(symbols, ts) 58 } 59 case *ast.ValueSpec: 60 for _, name := range spec.Names { 61 if name.Name == "_" { 62 continue 63 } 64 vs, err := varSymbol(pgf.Mapper, pgf.Tok, spec, name, decl.Tok == token.CONST) 65 if err == nil { 66 symbols = append(symbols, vs) 67 } 68 } 69 } 70 } 71 } 72 } 73 return symbols, nil 74 } 75 76 func funcSymbol(m *protocol.Mapper, tf *token.File, decl *ast.FuncDecl) (protocol.DocumentSymbol, error) { 77 s := protocol.DocumentSymbol{ 78 Name: decl.Name.Name, 79 Kind: protocol.Function, 80 } 81 if decl.Recv != nil { 82 s.Kind = protocol.Method 83 } 84 var err error 85 s.Range, err = m.NodeRange(tf, decl) 86 if err != nil { 87 return protocol.DocumentSymbol{}, err 88 } 89 s.SelectionRange, err = m.NodeRange(tf, decl.Name) 90 if err != nil { 91 return protocol.DocumentSymbol{}, err 92 } 93 s.Detail = types.ExprString(decl.Type) 94 return s, nil 95 } 96 97 func typeSymbol(m *protocol.Mapper, tf *token.File, spec *ast.TypeSpec) (protocol.DocumentSymbol, error) { 98 s := protocol.DocumentSymbol{ 99 Name: spec.Name.Name, 100 } 101 var err error 102 s.Range, err = m.NodeRange(tf, spec) 103 if err != nil { 104 return protocol.DocumentSymbol{}, err 105 } 106 s.SelectionRange, err = m.NodeRange(tf, spec.Name) 107 if err != nil { 108 return protocol.DocumentSymbol{}, err 109 } 110 s.Kind, s.Detail, s.Children = typeDetails(m, tf, spec.Type) 111 return s, nil 112 } 113 114 func typeDetails(m *protocol.Mapper, tf *token.File, typExpr ast.Expr) (kind protocol.SymbolKind, detail string, children []protocol.DocumentSymbol) { 115 switch typExpr := typExpr.(type) { 116 case *ast.StructType: 117 kind = protocol.Struct 118 children = fieldListSymbols(m, tf, typExpr.Fields, protocol.Field) 119 if len(children) > 0 { 120 detail = "struct{...}" 121 } else { 122 detail = "struct{}" 123 } 124 125 // Find interface methods and embedded types. 126 case *ast.InterfaceType: 127 kind = protocol.Interface 128 children = fieldListSymbols(m, tf, typExpr.Methods, protocol.Method) 129 if len(children) > 0 { 130 detail = "interface{...}" 131 } else { 132 detail = "interface{}" 133 } 134 135 case *ast.FuncType: 136 kind = protocol.Function 137 detail = types.ExprString(typExpr) 138 139 default: 140 kind = protocol.Class // catch-all, for cases where we don't know the kind syntactically 141 detail = types.ExprString(typExpr) 142 } 143 return 144 } 145 146 func fieldListSymbols(m *protocol.Mapper, tf *token.File, fields *ast.FieldList, fieldKind protocol.SymbolKind) []protocol.DocumentSymbol { 147 if fields == nil { 148 return nil 149 } 150 151 var symbols []protocol.DocumentSymbol 152 for _, field := range fields.List { 153 detail, children := "", []protocol.DocumentSymbol(nil) 154 if field.Type != nil { 155 _, detail, children = typeDetails(m, tf, field.Type) 156 } 157 if len(field.Names) == 0 { // embedded interface or struct field 158 // By default, use the formatted type details as the name of this field. 159 // This handles potentially invalid syntax, as well as type embeddings in 160 // interfaces. 161 child := protocol.DocumentSymbol{ 162 Name: detail, 163 Kind: protocol.Field, // consider all embeddings to be fields 164 Children: children, 165 } 166 167 // If the field is a valid embedding, promote the type name to field 168 // name. 169 selection := field.Type 170 if id := embeddedIdent(field.Type); id != nil { 171 child.Name = id.Name 172 child.Detail = detail 173 selection = id 174 } 175 176 if rng, err := m.NodeRange(tf, field.Type); err == nil { 177 child.Range = rng 178 } 179 if rng, err := m.NodeRange(tf, selection); err == nil { 180 child.SelectionRange = rng 181 } 182 183 symbols = append(symbols, child) 184 } else { 185 for _, name := range field.Names { 186 child := protocol.DocumentSymbol{ 187 Name: name.Name, 188 Kind: fieldKind, 189 Detail: detail, 190 Children: children, 191 } 192 193 if rng, err := m.NodeRange(tf, field); err == nil { 194 child.Range = rng 195 } 196 if rng, err := m.NodeRange(tf, name); err == nil { 197 child.SelectionRange = rng 198 } 199 200 symbols = append(symbols, child) 201 } 202 } 203 204 } 205 return symbols 206 } 207 208 func varSymbol(m *protocol.Mapper, tf *token.File, spec *ast.ValueSpec, name *ast.Ident, isConst bool) (protocol.DocumentSymbol, error) { 209 s := protocol.DocumentSymbol{ 210 Name: name.Name, 211 Kind: protocol.Variable, 212 } 213 if isConst { 214 s.Kind = protocol.Constant 215 } 216 var err error 217 s.Range, err = m.NodeRange(tf, spec) 218 if err != nil { 219 return protocol.DocumentSymbol{}, err 220 } 221 s.SelectionRange, err = m.NodeRange(tf, name) 222 if err != nil { 223 return protocol.DocumentSymbol{}, err 224 } 225 if spec.Type != nil { // type may be missing from the syntax 226 _, s.Detail, s.Children = typeDetails(m, tf, spec.Type) 227 } 228 return s, nil 229 }