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  }