Skip to content

Commit a565a5c

Browse files
authored
fix(parser): stability issues (#45)
* fix(collect): do not corrupt functionInfo * fix(collect): fix duplicates in filterEntitySymbols When trying to `getSymbolByLocation(loc)`, multiple entity symbols could contain `loc`. For example: `trait MyTrait { fn my_trait<<loc> }`, both `MyTrait` and `my_trait` could be selected by `filterEntitySymbols`, but the expected result is `my_trait` since it is more specific. * doc(collect): add docs
1 parent ee3ccbc commit a565a5c

File tree

1 file changed

+69
-52
lines changed

1 file changed

+69
-52
lines changed

lang/collect/collect.go

Lines changed: 69 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ package collect
1717
import (
1818
"context"
1919
"fmt"
20+
"maps"
2021
"os"
2122
"path/filepath"
23+
"slices"
2224
"strings"
2325
"unicode"
2426

@@ -306,13 +308,44 @@ func (c *Collector) getSymbolByTokenWithLimit(ctx context.Context, tok Token, de
306308
return c.getSymbolByLocation(ctx, defs[0], depth, tok)
307309
}
308310

309-
func (c *Collector) filterEntitySymbols(syms []*DocumentSymbol) *DocumentSymbol {
311+
// Find the symbol (from the symbol list) that matches the location.
312+
// It is the smallest (most specific) entity symbol that contains the location.
313+
//
314+
// Parameters:
315+
//
316+
// @syms: the list of symbols to search in
317+
// @loc: the location to find the symbol for
318+
//
319+
// Returns:
320+
//
321+
// *DocumentSymbol: the most specific entity symbol that contains the location.
322+
// If no such symbol is found, it returns nil.
323+
func (c *Collector) findMatchingSymbolIn(loc Location, syms []*DocumentSymbol) *DocumentSymbol {
324+
var most_specific *DocumentSymbol
310325
for _, sym := range syms {
311-
if c.spec.IsEntitySymbol(*sym) {
312-
return sym
326+
if !sym.Location.Include(loc) || !c.spec.IsEntitySymbol(*sym) {
327+
continue
328+
}
329+
// now we have a candidate (containing loc && entity), check if it is the most specific
330+
if most_specific == nil {
331+
most_specific = sym
332+
continue
313333
}
334+
if most_specific.Location.Include(sym.Location) {
335+
// use sym, which is more specific than most_specific
336+
most_specific = sym
337+
continue
338+
}
339+
if sym.Location.Include(most_specific.Location) {
340+
// remain current choice
341+
continue
342+
}
343+
// Indicates a bad usage, sym contains unstructured symbols.
344+
log.Error("getMostSpecificEntitySymbol: cannot decide between symbols %s (at %+v) and %s (at %+v)\n",
345+
most_specific.Name, most_specific.Location,
346+
sym.Name, sym.Location)
314347
}
315-
return nil
348+
return most_specific
316349
}
317350

318351
// return a language entity symbol
@@ -324,22 +357,19 @@ func (c *Collector) getSymbolByLocation(ctx context.Context, loc Location, depth
324357
// if sym, ok := c.syms[loc]; ok {
325358
// return sym, nil
326359
// }
327-
var ret []*DocumentSymbol
328-
for _, sym := range c.syms {
329-
if sym.Location.Include(loc) {
330-
ret = append(ret, sym)
331-
}
332-
}
333-
if len(ret) > 0 {
334-
return c.filterEntitySymbols(ret), nil
360+
361+
// 1. already loaded
362+
if sym := c.findMatchingSymbolIn(loc, slices.Collect(maps.Values(c.syms))); sym != nil {
363+
return sym, nil
335364
}
336365

337366
if c.LoadExternalSymbol && !c.internal(loc) && (c.NeedStdSymbol || !c.spec.IsStdToken(from)) {
338-
// external symbol, locate and process it
367+
// 2. load external symbol from its file
339368
syms, err := c.cli.DocumentSymbols(ctx, loc.URI)
340369
if err != nil {
341370
return nil, err
342371
}
372+
// load the other external symbols in that file
343373
for _, sym := range syms {
344374
// save symbol first
345375
if _, ok := c.syms[sym.Location]; !ok {
@@ -350,10 +380,8 @@ func (c *Collector) getSymbolByLocation(ctx context.Context, loc Location, depth
350380
sym.Text = content
351381
c.syms[sym.Location] = sym
352382
}
353-
if sym.Location.Include(loc) {
354-
ret = append(ret, sym)
355-
}
356383
}
384+
// load more external symbols if depth permits
357385
if depth >= 0 {
358386
// process target symbol
359387
for _, sym := range syms {
@@ -369,13 +397,10 @@ func (c *Collector) getSymbolByLocation(ctx context.Context, loc Location, depth
369397
}
370398
}
371399
}
372-
373-
// filter entity symbol
374-
rsym := c.filterEntitySymbols(ret)
400+
rsym := c.findMatchingSymbolIn(loc, slices.Collect(maps.Values(syms)))
375401
return rsym, nil
376-
377402
} else {
378-
// external symbol, just locate the content
403+
// external symbol, just locate the content
379404
var text string
380405
if c.internal(loc) {
381406
// maybe internal symbol not loaded, like `lazy_static!` in Rust
@@ -535,13 +560,16 @@ func (c *Collector) collectImpl(ctx context.Context, sym *DocumentSymbol, depth
535560
for _, method := range c.syms {
536561
// NOTICE: some class method (ex: XXType::new) are SKFunction, but still collect its receiver
537562
if (method.Kind == SKMethod || method.Kind == SKFunction) && sym.Location.Include(method.Location) {
538-
c.funcs[method] = functionInfo{
539-
Method: &methodInfo{
540-
Receiver: *rd,
541-
Interface: ind,
542-
ImplHead: impl,
543-
},
563+
if _, ok := c.funcs[method]; !ok {
564+
c.funcs[method] = functionInfo{}
565+
}
566+
f := c.funcs[method]
567+
f.Method = &methodInfo{
568+
Receiver: *rd,
569+
Interface: ind,
570+
ImplHead: impl,
544571
}
572+
c.funcs[method] = f
545573
}
546574
}
547575
}
@@ -601,32 +629,21 @@ func (c *Collector) processSymbol(ctx context.Context, sym *DocumentSymbol, dept
601629
}
602630

603631
func (c *Collector) updateFunctionInfo(sym *DocumentSymbol, tsyms, ipsyms, opsyms map[int]dependency, ts, is, os []dependency, rsym *dependency) {
604-
f, ok := c.funcs[sym]
605-
if ok {
606-
f.TypeParams = tsyms
607-
f.TypeParamsSorted = ts
608-
f.Inputs = ipsyms
609-
f.InputsSorted = is
610-
f.Outputs = opsyms
611-
f.OutputsSorted = os
612-
if rsym != nil {
613-
if f.Method == nil {
614-
f.Method = &methodInfo{}
615-
}
616-
f.Method.Receiver = *rsym
617-
}
618-
} else {
619-
f = functionInfo{
620-
TypeParams: tsyms,
621-
Inputs: ipsyms,
622-
Outputs: opsyms,
623-
}
624-
if rsym != nil {
625-
if f.Method == nil {
626-
f.Method = &methodInfo{}
627-
}
628-
f.Method.Receiver = *rsym
632+
if _, ok := c.funcs[sym]; !ok {
633+
c.funcs[sym] = functionInfo{}
634+
}
635+
f := c.funcs[sym]
636+
f.TypeParams = tsyms
637+
f.TypeParamsSorted = ts
638+
f.Inputs = ipsyms
639+
f.InputsSorted = is
640+
f.Outputs = opsyms
641+
f.OutputsSorted = os
642+
if rsym != nil {
643+
if f.Method == nil {
644+
f.Method = &methodInfo{}
629645
}
646+
f.Method.Receiver = *rsym
630647
}
631648
c.funcs[sym] = f
632649
}

0 commit comments

Comments
 (0)