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  }