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  }