From d783a7a02aca257c3d427409e6a3d3976253529b Mon Sep 17 00:00:00 2001 From: "prath.shenoy" Date: Sun, 19 Apr 2026 19:19:19 +0000 Subject: [PATCH 1/3] perf(loader): Resolve dependency symbols on demand --- internal/implementations/extractor.go | 154 +++++++++++ internal/implementations/implementations.go | 163 ++---------- internal/index/scip.go | 48 ++-- internal/loader/loader.go | 5 +- internal/lookup/lookup.go | 15 +- internal/symbols/composer.go | 247 ++++++++++++++++++ internal/symbols/composer_test.go | 175 +++++++++++++ .../input/dep-symbols/dep_symbols.go | 42 +++ .../snapshots/input/dep-symbols/deplib/box.go | 26 ++ .../snapshots/input/dep-symbols/deplib/go.mod | 3 + .../snapshots/input/dep-symbols/go.mod | 7 + .../output/dep-symbols/dep_symbols.go | 177 +++++++++++++ .../snapshots/output/embedded/embedded.go | 2 - .../snapshots/output/embedded/nested.go | 4 +- .../snapshots/output/embedded/something.go | 6 +- .../snapshots/output/impls/remote_impls.go | 12 +- .../output/testdata/implementations_remote.go | 14 +- 17 files changed, 899 insertions(+), 201 deletions(-) create mode 100644 internal/implementations/extractor.go create mode 100644 internal/symbols/composer.go create mode 100644 internal/symbols/composer_test.go create mode 100644 internal/testdata/snapshots/input/dep-symbols/dep_symbols.go create mode 100644 internal/testdata/snapshots/input/dep-symbols/deplib/box.go create mode 100644 internal/testdata/snapshots/input/dep-symbols/deplib/go.mod create mode 100644 internal/testdata/snapshots/input/dep-symbols/go.mod create mode 100755 internal/testdata/snapshots/output/dep-symbols/dep_symbols.go diff --git a/internal/implementations/extractor.go b/internal/implementations/extractor.go new file mode 100644 index 0000000..2650fa3 --- /dev/null +++ b/internal/implementations/extractor.go @@ -0,0 +1,154 @@ +package impls + +import ( + "fmt" + "go/types" + "log/slog" + + "github.com/scip-code/scip-go/internal/loader" + "github.com/scip-code/scip-go/internal/lookup" + "github.com/scip-code/scip/bindings/go/scip" + "golang.org/x/tools/go/packages" + "golang.org/x/tools/go/types/typeutil" +) + +type Extractor interface { + Extract(pkgLookup loader.PackageLookup) (interfaces, concretes map[string]ImplDef) +} + +type extractor struct { + global *lookup.Global + methodSetCache typeutil.MethodSetCache +} + +func NewExtractor(global *lookup.Global) Extractor { + return &extractor{ + global: global, + } +} + +func (e *extractor) Extract(pkgLookup loader.PackageLookup) (map[string]ImplDef, map[string]ImplDef) { + interfaces := map[string]ImplDef{} + concretes := map[string]ImplDef{} + + for _, pkg := range pkgLookup { + if pkg.Name == "builtin" { + continue + } + + if pkg.TypesInfo != nil { + e.extractLocal(pkg, interfaces, concretes) + } else if pkg.Types != nil { + e.extractRemote(pkg, interfaces, concretes) + } else { + slog.Warn("No types for package", "path", pkg.PkgPath) + } + } + + return interfaces, concretes +} + +func (e *extractor) extractLocal(pkg *packages.Package, interfaces, concretes map[string]ImplDef) { + pkgSymbols := e.global.GetPackage(pkg) + if pkgSymbols == nil { + slog.Warn("No symbols for package", "path", pkg.PkgPath) + return + } + + for ident, obj := range pkg.TypesInfo.Defs { + if obj == nil { + continue + } + + typeName, ok := obj.(*types.TypeName) + if !ok { + continue + } + + if pkg.Types != nil && typeName.Parent() != pkg.Types.Scope() { + continue + } + + named, ok := obj.Type().(*types.Named) + if !ok { + continue + } + + sym, ok := pkgSymbols.Get(typeName.Pos()) + if !ok { + slog.Debug( + "No symbol for package-level named type", + "identifier", ident.Name, + "package", pkg.PkgPath, + "id", obj.Id(), + ) + continue + } + + e.classify(named, sym, pkg.PkgPath, interfaces, concretes) + } +} + +func (e *extractor) extractRemote(pkg *packages.Package, interfaces, concretes map[string]ImplDef) { + scope := pkg.Types.Scope() + + for _, name := range scope.Names() { + typeName, ok := scope.Lookup(name).(*types.TypeName) + if !ok || !typeName.Exported() { + continue + } + + named, ok := typeName.Type().(*types.Named) + if !ok { + continue + } + + sym := e.global.Composer().Compose(pkg, typeName) + if sym == "" { + continue + } + + e.classify(named, &scip.SymbolInformation{Symbol: sym}, pkg.PkgPath, interfaces, concretes) + } +} + +func (e *extractor) classify( + named *types.Named, + sym *scip.SymbolInformation, + pkgPath string, + interfaces, concretes map[string]ImplDef, +) { + methods := typeutil.IntuitiveMethodSet(named, &e.methodSetCache) + if len(methods) == 0 { + return + } + + methodSymbols := map[methodID]*scip.SymbolInformation{} + for _, method := range methods { + sym, ok, err := e.global.GetSymbolOfObject(method.Obj()) + if err != nil { + slog.Debug(fmt.Sprintf("Error while looking for symbol %s | %s", err, method.Obj())) + continue + } + if !ok { + continue + } + + methodSymbols[methodID(method.Obj().Id())] = sym + } + + impl := ImplDef{ + Symbol: sym, + Named: named, + Methods: methodSymbols, + Mask: methodMask(methods), + MethodCount: len(methods), + HasUnexported: hasUnexportedMethods(methods), + PkgPath: pkgPath, + } + if types.IsInterface(named) { + interfaces[impl.Symbol.Symbol] = impl + } else { + concretes[impl.Symbol.Symbol] = impl + } +} diff --git a/internal/implementations/implementations.go b/internal/implementations/implementations.go index ef3d586..dc5c34e 100644 --- a/internal/implementations/implementations.go +++ b/internal/implementations/implementations.go @@ -1,21 +1,15 @@ package impls import ( - "fmt" - "go/ast" "go/types" "hash/crc32" - "log/slog" "sync" "sync/atomic" "github.com/scip-code/scip-go/internal/implementations/fingerprint" "github.com/scip-code/scip-go/internal/loader" - "github.com/scip-code/scip-go/internal/lookup" "github.com/scip-code/scip-go/internal/output" "github.com/scip-code/scip/bindings/go/scip" - "golang.org/x/tools/go/packages" - "golang.org/x/tools/go/types/typeutil" ) // methodID is a unique identifier for a method, using types.Id semantics @@ -23,11 +17,7 @@ import ( type methodID string type ImplDef struct { - // The corresponding scip symbol, generated via previous iteration over the AST - Symbol *scip.SymbolInformation - - Pkg *packages.Package - Ident *ast.Ident + Symbol *scip.SymbolInformation Named *types.Named Methods map[methodID]*scip.SymbolInformation @@ -69,19 +59,9 @@ func hasUnexportedMethods(methods []*types.Selection) bool { return false } -func findImplementations(concreteTypes map[string]ImplDef, interfaces map[string]ImplDef, symbols *lookup.Global, count *uint64) { +func findImplementations(concreteTypes map[string]ImplDef, interfaces map[string]ImplDef, count *uint64) { for _, ty := range concreteTypes { - pos := ty.Ident.Pos() - sym, ok := symbols.GetSymbolInformation(ty.Pkg, pos) - if !ok { - panic(fmt.Sprintf("Could not find symbol for %s", ty.Symbol)) - } - for _, iface := range interfaces { - if iface.Ident == nil { - continue - } - ifaceType, ok := iface.Named.Underlying().(*types.Interface) if !ok { continue @@ -114,7 +94,7 @@ func findImplementations(concreteTypes map[string]ImplDef, interfaces map[string } // Add implementation details for the struct & interface relationship - sym.Relationships = append(sym.Relationships, &scip.Relationship{ + ty.Symbol.Relationships = append(ty.Symbol.Relationships, &scip.Relationship{ Symbol: iface.Symbol.Symbol, IsImplementation: true, }) @@ -133,6 +113,11 @@ func findImplementations(concreteTypes map[string]ImplDef, interfaces map[string } } + ty.Symbol.Relationships = scip.CanonicalizeRelationships(ty.Symbol.Relationships) + for _, method := range ty.Methods { + method.Relationships = scip.CanonicalizeRelationships(method.Relationships) + } + atomic.AddUint64(count, 1) } } @@ -140,16 +125,11 @@ func findImplementations(concreteTypes map[string]ImplDef, interfaces map[string func AddImplementationRelationships( pkgs loader.PackageLookup, allPackages loader.PackageLookup, - symbols *lookup.Global, + extractor Extractor, ) ([]*scip.SymbolInformation, error) { var externalSymbols []*scip.SymbolInformation - var msCache typeutil.MethodSetCache - localInterfaces, localTypes, err := extractInterfacesAndConcreteTypes( - pkgs, symbols, &msCache) - if err != nil { - return nil, err - } + localInterfaces, localTypes := extractor.Extract(pkgs) remotePackages := make(loader.PackageLookup) for pkgID, pkg := range allPackages { @@ -159,11 +139,7 @@ func AddImplementationRelationships( remotePackages[pkgID] = pkg } - remoteInterfaces, remoteTypes, err := extractInterfacesAndConcreteTypes( - remotePackages, symbols, &msCache) - if err != nil { - return nil, err - } + remoteInterfaces, remoteTypes := extractor.Extract(remotePackages) // Total concrete types to check across the three passes. total := uint64(len(localTypes)*2 + len(remoteTypes)) @@ -175,131 +151,24 @@ func AddImplementationRelationships( defer wg.Done() // local type -> local interface - findImplementations(localTypes, localInterfaces, symbols, &count) + findImplementations(localTypes, localInterfaces, &count) // local type -> remote interface - findImplementations(localTypes, remoteInterfaces, symbols, &count) + findImplementations(localTypes, remoteInterfaces, &count) // remote type -> local interface // We emit these as external symbols so index consumer can merge them. - findImplementations(remoteTypes, localInterfaces, symbols, &count) + findImplementations(remoteTypes, localInterfaces, &count) }() output.WithProgressParallel(&wg, "Indexing Implementations", &count, total) // Collect remote type symbols that gained relationships for _, typ := range remoteTypes { - if sym, ok := symbols.GetSymbolInformation(typ.Pkg, typ.Ident.Pos()); ok { - if len(sym.Relationships) > 0 { - externalSymbols = append(externalSymbols, sym) - } + if len(typ.Symbol.Relationships) > 0 { + externalSymbols = append(externalSymbols, typ.Symbol) } } return externalSymbols, nil } - -func extractInterfacesAndConcreteTypes( - pkgs loader.PackageLookup, - symbols *lookup.Global, - msCache *typeutil.MethodSetCache, -) (interfaces map[string]ImplDef, concreteTypes map[string]ImplDef, err error) { - interfaces = map[string]ImplDef{} - concreteTypes = map[string]ImplDef{} - - for _, pkg := range pkgs { - // Builtin isn't the same as standard library, that is for builtin types - // We don't need to check those for implemenations. - if pkg.Name == "builtin" { - continue - } - - if pkg.TypesInfo == nil { - slog.Warn("No types for package", "path", pkg.PkgPath) - continue - } - - pkgSymbols := symbols.GetPackage(pkg) - if pkgSymbols == nil { - slog.Warn("No symbols for package", "path", pkg.PkgPath) - continue - } - - for ident, obj := range pkg.TypesInfo.Defs { - if obj == nil { - continue - } - - // We ignore aliases 'type M = N' to avoid duplicate reporting - // of the Named type N. - obj, ok := obj.(*types.TypeName) - if !ok { - continue - } - - // Skip types declared inside function bodies — the type visitor - // only indexes package-level declarations, so local types will - // never have a symbol entry. - if pkg.Types != nil && obj.Parent() != pkg.Types.Scope() { - continue - } - - objType, ok := obj.Type().(*types.Named) - if !ok { - continue - } - - symbol, ok := pkgSymbols.Get(obj.Pos()) - if !ok { - slog.Debug( - "No symbol for package-level named type", - "identifier", ident.Name, "package", pkg.PkgPath, "id", obj.Id()) - continue - } - - methods := typeutil.IntuitiveMethodSet(objType, msCache) - - // ignore interfaces that are empty. they are too - // plentiful and don't provide useful intelligence. - if len(methods) == 0 { - continue - } - - methodIds := map[methodID]*scip.SymbolInformation{} - for _, m := range methods { - sym, ok, err := symbols.GetSymbolOfObject(m.Obj()) - if err != nil { - slog.Debug(fmt.Sprintf("Error while looking for symbol %s | %s", err, m.Obj())) - continue - } - - if !ok { - continue - } - - methodIds[methodID(m.Obj().Id())] = sym - } - - d := ImplDef{ - Symbol: symbol, - Pkg: pkg, - Ident: ident, - Named: objType, - Methods: methodIds, - Mask: methodMask(methods), - MethodCount: len(methods), - HasUnexported: hasUnexportedMethods(methods), - PkgPath: pkg.PkgPath, - } - - if types.IsInterface(objType) { - interfaces[d.Symbol.Symbol] = d - } else { - concreteTypes[d.Symbol.Symbol] = d - } - - } - } - - return -} diff --git a/internal/index/scip.go b/internal/index/scip.go index 3154dda..feabf83 100644 --- a/internal/index/scip.go +++ b/internal/index/scip.go @@ -55,15 +55,21 @@ func GetPackages(opts config.IndexOpts) (current []newtypes.PackageID, deps []ne } func ListMissing(opts config.IndexOpts) (missing []string, err error) { - projectPackages, allPackages, err := loader.LoadPackages(opts, opts.ModuleRoot) + projectPackages, _, err := loader.LoadPackages(opts, opts.ModuleRoot) if err != nil { return nil, err } + composer := symbols.NewComposer(symbols.ComposerConfig{ + DefaultModulePath: opts.ModuleRoot, + DefaultModuleVersion: opts.ModuleVersion, + }) + globalSymbols := lookup.NewGlobalSymbols(composer) + pathToDocuments := map[string]*document.Document{} - for _, pkg := range allPackages { + for _, pkg := range projectPackages { visitors.VisitPackageSyntax( - opts.ModuleRoot, pkg, pathToDocuments, lookup.NewGlobalSymbols()) + opts.ModuleRoot, pkg, pathToDocuments, globalSymbols) } for _, pkg := range projectPackages { @@ -104,7 +110,7 @@ func Index(writer func(proto.Message) error, opts config.IndexOpts) error { pathToDocument, globalSymbols := indexVisitPackages(opts, projectPackages, allPackages) if !opts.SkipImplementations { implSymbols, err := impls.AddImplementationRelationships( - projectPackages, allPackages, globalSymbols, + projectPackages, allPackages, impls.NewExtractor(globalSymbols), ) if err != nil { return err @@ -180,39 +186,33 @@ func indexVisitPackages( allPackages loader.PackageLookup, ) (map[string]*document.Document, *lookup.Global) { pathToDocuments := map[string]*document.Document{} - globalSymbols := lookup.NewGlobalSymbols() + + composer := symbols.NewComposer(symbols.ComposerConfig{ + DefaultModulePath: opts.ModuleRoot, + DefaultModuleVersion: opts.ModuleVersion, + }) + globalSymbols := lookup.NewGlobalSymbols(composer) + for _, pkg := range allPackages { + globalSymbols.SetPkgSymbol(pkg) + } var count uint64 var wg sync.WaitGroup wg.Add(1) - lookupIDs := slices.Sorted(maps.Keys(allPackages)) + lookupIDs := slices.Sorted(maps.Keys(projectPackages)) - // We have to visit all the packages to get the definition sites - // for all the symbols. - // - // We don't want to visit in the same depth as file visitors though, - // so we do ONLY do this + // Visit project packages to collect definition sites. Dependency packages + // skip syntax visiting; their symbols are composed on demand. go func() { defer wg.Done() for _, pkgID := range lookupIDs { - pkg := allPackages[pkgID] + pkg := projectPackages[pkgID] slog.Debug("Visiting package", "path", pkg.PkgPath) visitors.VisitPackageSyntax(opts.ModuleRoot, pkg, pathToDocuments, globalSymbols) - pkgSymbol := globalSymbols.SetPkgSymbol(pkg) - - // If we don't have this package anywhere, don't try to create a new symbol - if _, ok := projectPackages[newtypes.GetID(pkg)]; !ok { - atomic.AddUint64(&count, 1) - continue - } - - if len(pkg.Syntax) == 0 { - atomic.AddUint64(&count, 1) - continue - } + pkgSymbol, _ := globalSymbols.GetPkgSymbol(pkg) symInfo := &scip.SymbolInformation{ Symbol: pkgSymbol, diff --git a/internal/loader/loader.go b/internal/loader/loader.go index 46f5e36..d488da9 100644 --- a/internal/loader/loader.go +++ b/internal/loader/loader.go @@ -19,8 +19,7 @@ import ( type PackageLookup map[newtypes.PackageID]*packages.Package -var loadMode = packages.NeedDeps | - packages.NeedImports | +var loadMode = packages.NeedImports | packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo | @@ -119,7 +118,7 @@ func LoadPackages( for _, pkg := range pkgs { addImportsToPkgs(allPackages, &opts, pkg) - projectPackages[newtypes.GetID(pkg)] = pkg + projectPackages[newtypes.GetID(pkg)] = allPackages[newtypes.GetID(pkg)] } return nil diff --git a/internal/lookup/lookup.go b/internal/lookup/lookup.go index 4408778..5c5c591 100644 --- a/internal/lookup/lookup.go +++ b/internal/lookup/lookup.go @@ -21,10 +21,11 @@ func NewPackageSymbols(pkg *packages.Package) *Package { } } -func NewGlobalSymbols() *Global { +func NewGlobalSymbols(composer symbols.Composer) *Global { return &Global{ symbols: map[newtypes.PackageID]*Package{}, pkgSymbols: map[newtypes.PackageID]string{}, + composer: composer, } } @@ -75,6 +76,11 @@ type Global struct { m sync.Mutex symbols map[newtypes.PackageID]*Package pkgSymbols map[newtypes.PackageID]string + composer symbols.Composer +} + +func (p *Global) Composer() symbols.Composer { + return p.composer } func (p *Global) Add(pkgSymbols *Package) { @@ -90,6 +96,7 @@ func (p *Global) SetPkgSymbol(pkg *packages.Package) string { }) p.m.Lock() p.pkgSymbols[newtypes.GetID(pkg)] = sym + p.symbols[newtypes.GetID(pkg)] = NewPackageSymbols(pkg) p.m.Unlock() return sym } @@ -171,6 +178,12 @@ func (p *Global) GetSymbolOfObject(obj types.Object) (*scip.SymbolInformation, b } } + if pkgSymbols, ok := p.symbols[newtypes.PackageID(pkgPath)]; ok { + if sym := p.composer.Compose(pkgSymbols.pkg, obj); sym != "" { + return &scip.SymbolInformation{Symbol: sym}, true, nil + } + } + switch obj := obj.(type) { case *types.Var: // , "| position", pkg.Fset.Position(obj.Pos()))) diff --git a/internal/symbols/composer.go b/internal/symbols/composer.go new file mode 100644 index 0000000..3266545 --- /dev/null +++ b/internal/symbols/composer.go @@ -0,0 +1,247 @@ +package symbols + +import ( + "go/types" + + "github.com/scip-code/scip/bindings/go/scip" + "golang.org/x/tools/go/packages" +) + +const ( + _scheme = "scip-go" + _manager = "gomod" +) + +type Composer interface { + Compose(pkg *packages.Package, obj types.Object) string +} + +type ComposerConfig struct { + DefaultModulePath string + DefaultModuleVersion string +} + +type composer struct { + defaultModulePath string + defaultModuleVersion string + + compositions map[types.Object]string + fieldOwners map[*types.Var]*types.TypeName +} + +func NewComposer(cfg ComposerConfig) Composer { + return &composer{ + defaultModulePath: cfg.DefaultModulePath, + defaultModuleVersion: cfg.DefaultModuleVersion, + + compositions: make(map[types.Object]string), + fieldOwners: make(map[*types.Var]*types.TypeName), + } +} + +func (c *composer) Compose(pkg *packages.Package, obj types.Object) string { + if obj == nil || obj.Pkg() == nil { + return "" + } + + if composition, ok := c.compositions[obj]; ok { + return composition + } + + var descriptors []*scip.Descriptor + switch obj := obj.(type) { + case *types.PkgName: + descriptors = c.describePkgName(obj) + case *types.TypeName: + descriptors = c.describeTypeName(obj) + case *types.Const: + descriptors = c.describeConst(obj) + case *types.Func: + descriptors = c.describeFunc(obj) + case *types.Var: + descriptors = c.describeVar(obj) + } + if len(descriptors) == 0 { + return "" + } + + c.compositions[obj] = scip.VerboseSymbolFormatter.FormatSymbol(&scip.Symbol{ + Scheme: _scheme, + Package: c.pack(pkg), + Descriptors: descriptors, + }) + return c.compositions[obj] +} + +func (c *composer) pack(pkg *packages.Package) *scip.Package { + if pkg == nil || pkg.Module == nil { + return &scip.Package{ + Manager: _manager, + Name: c.defaultModulePath, + Version: c.defaultModuleVersion, + } + } + + return &scip.Package{ + Manager: _manager, + Name: pkg.Module.Path, + Version: pkg.Module.Version, + } +} + +func (c *composer) describePkgName(pkgName *types.PkgName) []*scip.Descriptor { + return []*scip.Descriptor{ + { + Name: pkgName.Imported().Path(), + Suffix: scip.Descriptor_Namespace, + }, + } +} + +func (c *composer) describeTypeName(typeName *types.TypeName) []*scip.Descriptor { + return []*scip.Descriptor{ + { + Name: typeName.Pkg().Path(), + Suffix: scip.Descriptor_Namespace, + }, + { + Name: typeName.Name(), + Suffix: scip.Descriptor_Type, + }, + } +} + +func (c *composer) describeConst(constant *types.Const) []*scip.Descriptor { + return []*scip.Descriptor{ + { + Name: constant.Pkg().Path(), + Suffix: scip.Descriptor_Namespace, + }, + { + Name: constant.Name(), + Suffix: scip.Descriptor_Term, + }, + } +} + +func (c *composer) describeFunc(fn *types.Func) []*scip.Descriptor { + sig, ok := fn.Type().(*types.Signature) + if !ok { + return nil + } + + if recv := sig.Recv(); recv != nil { + recvTypeName := c.nameType(recv.Type()) + if recvTypeName == "" { + return nil + } + + return []*scip.Descriptor{ + { + Name: fn.Pkg().Path(), + Suffix: scip.Descriptor_Namespace, + }, + { + Name: recvTypeName, + Suffix: scip.Descriptor_Type, + }, + { + Name: fn.Name(), + Suffix: scip.Descriptor_Method, + }, + } + } + + return []*scip.Descriptor{ + { + Name: fn.Pkg().Path(), + Suffix: scip.Descriptor_Namespace, + }, + { + Name: fn.Name(), + Suffix: scip.Descriptor_Method, + }, + } +} + +func (c *composer) nameType(t types.Type) string { + for { + switch u := types.Unalias(t).(type) { + case *types.Pointer: + t = u.Elem() + case *types.Named: + return u.Obj().Name() + default: + return "" + } + } +} + +func (c *composer) describeVar(variable *types.Var) []*scip.Descriptor { + if !variable.IsField() { + return []*scip.Descriptor{ + { + Name: variable.Pkg().Path(), + Suffix: scip.Descriptor_Namespace, + }, + { + Name: variable.Name(), + Suffix: scip.Descriptor_Term, + }, + } + } + + owner := c.locateOwner(variable) + if owner == nil { + return nil + } + + return []*scip.Descriptor{ + { + Name: variable.Pkg().Path(), + Suffix: scip.Descriptor_Namespace, + }, + { + Name: owner.Name(), + Suffix: scip.Descriptor_Type, + }, + { + Name: variable.Name(), + Suffix: scip.Descriptor_Term, + }, + } +} + +func (c *composer) locateOwner(field *types.Var) *types.TypeName { + origin := field.Origin() + + if owner, ok := c.fieldOwners[origin]; ok { + return owner + } + + scope := field.Pkg().Scope() + for _, name := range scope.Names() { + obj := scope.Lookup(name) + + typeName, ok := obj.(*types.TypeName) + if !ok { + continue + } + + named, ok := typeName.Type().(*types.Named) + if !ok { + continue + } + + structType, ok := named.Underlying().(*types.Struct) + if !ok { + continue + } + + for field := range structType.Fields() { + c.fieldOwners[field.Origin()] = typeName + } + } + + return c.fieldOwners[origin] +} diff --git a/internal/symbols/composer_test.go b/internal/symbols/composer_test.go new file mode 100644 index 0000000..8f02f85 --- /dev/null +++ b/internal/symbols/composer_test.go @@ -0,0 +1,175 @@ +package symbols_test + +import ( + "go/token" + "go/types" + "testing" + + "github.com/scip-code/scip-go/internal/symbols" + "golang.org/x/tools/go/packages" +) + +func TestComposeNil(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{}) + pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} + + if got := c.Compose(pkg, nil); got != "" { + t.Errorf("Compose(nil) = %q, want empty", got) + } +} + +func TestComposeNilPkg(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{}) + pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} + + obj := types.Universe.Lookup("len") + if got := c.Compose(pkg, obj); got != "" { + t.Errorf("Compose(builtin) = %q, want empty", got) + } +} + +func TestComposeTypeName(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{}) + pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} + + tpkg := types.NewPackage("example.com/lib", "lib") + obj := types.NewTypeName(token.NoPos, tpkg, "MyStruct", nil) + types.NewNamed(obj, types.NewStruct(nil, nil), nil) + + got := c.Compose(pkg, obj) + want := "scip-go gomod example.com/lib v1.0.0 `example.com/lib`/MyStruct#" + if got != want { + t.Errorf("Compose(TypeName) =\n %q\nwant\n %q", got, want) + } +} + +func TestComposeConst(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{}) + pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} + + tpkg := types.NewPackage("example.com/lib", "lib") + obj := types.NewConst(token.NoPos, tpkg, "MaxRetries", types.Typ[types.Int], nil) + + got := c.Compose(pkg, obj) + want := "scip-go gomod example.com/lib v1.0.0 `example.com/lib`/MaxRetries." + if got != want { + t.Errorf("Compose(Const) =\n %q\nwant\n %q", got, want) + } +} + +func TestComposePackageVar(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{}) + pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} + + tpkg := types.NewPackage("example.com/lib", "lib") + obj := types.NewVar(token.NoPos, tpkg, "GlobalCount", types.Typ[types.Int]) + + got := c.Compose(pkg, obj) + want := "scip-go gomod example.com/lib v1.0.0 `example.com/lib`/GlobalCount." + if got != want { + t.Errorf("Compose(Var) =\n %q\nwant\n %q", got, want) + } +} + +func TestComposeFunc(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{}) + pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} + + tpkg := types.NewPackage("example.com/lib", "lib") + sig := types.NewSignatureType(nil, nil, nil, nil, nil, false) + obj := types.NewFunc(token.NoPos, tpkg, "DoWork", sig) + + got := c.Compose(pkg, obj) + want := "scip-go gomod example.com/lib v1.0.0 `example.com/lib`/DoWork()." + if got != want { + t.Errorf("Compose(Func) =\n %q\nwant\n %q", got, want) + } +} + +func TestComposeMethod(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{}) + pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} + + tpkg := types.NewPackage("example.com/lib", "lib") + typeName := types.NewTypeName(token.NoPos, tpkg, "Server", nil) + named := types.NewNamed(typeName, types.NewStruct(nil, nil), nil) + + recv := types.NewVar(token.NoPos, tpkg, "s", named) + sig := types.NewSignatureType(recv, nil, nil, nil, nil, false) + obj := types.NewFunc(token.NoPos, tpkg, "Start", sig) + + got := c.Compose(pkg, obj) + want := "scip-go gomod example.com/lib v1.0.0 `example.com/lib`/Server#Start()." + if got != want { + t.Errorf("Compose(Method) =\n %q\nwant\n %q", got, want) + } +} + +func TestComposeMethodPointerReceiver(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{}) + pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} + + tpkg := types.NewPackage("example.com/lib", "lib") + typeName := types.NewTypeName(token.NoPos, tpkg, "Server", nil) + named := types.NewNamed(typeName, types.NewStruct(nil, nil), nil) + + recv := types.NewVar(token.NoPos, tpkg, "s", types.NewPointer(named)) + sig := types.NewSignatureType(recv, nil, nil, nil, nil, false) + obj := types.NewFunc(token.NoPos, tpkg, "Stop", sig) + + got := c.Compose(pkg, obj) + want := "scip-go gomod example.com/lib v1.0.0 `example.com/lib`/Server#Stop()." + if got != want { + t.Errorf("Compose(Method ptr recv) =\n %q\nwant\n %q", got, want) + } +} + +func TestComposeStructField(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{}) + pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} + + tpkg := types.NewPackage("example.com/lib", "lib") + field := types.NewField(token.NoPos, tpkg, "Name", types.Typ[types.String], false) + structType := types.NewStruct([]*types.Var{field}, nil) + + typeName := types.NewTypeName(token.NoPos, tpkg, "Config", nil) + types.NewNamed(typeName, structType, nil) + tpkg.Scope().Insert(typeName) + + got := c.Compose(pkg, field) + want := "scip-go gomod example.com/lib v1.0.0 `example.com/lib`/Config#Name." + if got != want { + t.Errorf("Compose(Field) =\n %q\nwant\n %q", got, want) + } +} + +func TestComposeDefaultModule(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{ + DefaultModulePath: "example.com/project", + DefaultModuleVersion: "1.0.0", + }) + pkg := &packages.Package{PkgPath: "example.com/lib"} + + tpkg := types.NewPackage("example.com/lib", "lib") + obj := types.NewConst(token.NoPos, tpkg, "Version", types.Typ[types.String], nil) + + got := c.Compose(pkg, obj) + want := "scip-go gomod example.com/project 1.0.0 `example.com/lib`/Version." + if got != want { + t.Errorf("Compose(default module) =\n %q\nwant\n %q", got, want) + } +} + +func TestComposeMemoization(t *testing.T) { + c := symbols.NewComposer(symbols.ComposerConfig{}) + pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} + + tpkg := types.NewPackage("example.com/lib", "lib") + obj := types.NewConst(token.NoPos, tpkg, "X", types.Typ[types.Int], nil) + + first := c.Compose(pkg, obj) + second := c.Compose(pkg, obj) + if first != second { + t.Errorf("memoization broken: %q != %q", first, second) + } +} diff --git a/internal/testdata/snapshots/input/dep-symbols/dep_symbols.go b/internal/testdata/snapshots/input/dep-symbols/dep_symbols.go new file mode 100644 index 0000000..a9a15be --- /dev/null +++ b/internal/testdata/snapshots/input/dep-symbols/dep_symbols.go @@ -0,0 +1,42 @@ +package depsymbols + +import "github.com/example/deplib" + +func UseGenericField() int { + b := deplib.Box[int]{Value: 42} + return b.Value +} + +func UseGenericMethod() string { + b := deplib.Box[string]{Value: "hello"} + return b.Get() +} + +func UseNonGenericField() string { + c := deplib.Config{Name: "test", Verbose: true} + return c.Name +} + +func UseConst() string { + return deplib.DefaultName +} + +func UseVar() int { + return deplib.GlobalCounter +} + +type LocalType struct{} + +func (l LocalType) String() string { return "local" } + +type EmbeddedStringer struct { + LocalType +} + +type LocalInterface interface { + Get() int +} + +func UseDepWriter(w deplib.Writer) { + w.Write(nil) +} diff --git a/internal/testdata/snapshots/input/dep-symbols/deplib/box.go b/internal/testdata/snapshots/input/dep-symbols/deplib/box.go new file mode 100644 index 0000000..dfd92cf --- /dev/null +++ b/internal/testdata/snapshots/input/dep-symbols/deplib/box.go @@ -0,0 +1,26 @@ +package deplib + +type Box[T any] struct { + Value T +} + +func (b Box[T]) Get() T { + return b.Value +} + +type Config struct { + Name string + Verbose bool +} + +const DefaultName = "default" + +var GlobalCounter int + +type Stringer interface { + String() string +} + +type Writer interface { + Write(p []byte) (n int, err error) +} diff --git a/internal/testdata/snapshots/input/dep-symbols/deplib/go.mod b/internal/testdata/snapshots/input/dep-symbols/deplib/go.mod new file mode 100644 index 0000000..ceea55a --- /dev/null +++ b/internal/testdata/snapshots/input/dep-symbols/deplib/go.mod @@ -0,0 +1,3 @@ +module github.com/example/deplib + +go 1.22 diff --git a/internal/testdata/snapshots/input/dep-symbols/go.mod b/internal/testdata/snapshots/input/dep-symbols/go.mod new file mode 100644 index 0000000..19966a0 --- /dev/null +++ b/internal/testdata/snapshots/input/dep-symbols/go.mod @@ -0,0 +1,7 @@ +module sg/dep-symbols + +go 1.22 + +require github.com/example/deplib v0.0.0 + +replace github.com/example/deplib => ./deplib diff --git a/internal/testdata/snapshots/output/dep-symbols/dep_symbols.go b/internal/testdata/snapshots/output/dep-symbols/dep_symbols.go new file mode 100755 index 0000000..a40c845 --- /dev/null +++ b/internal/testdata/snapshots/output/dep-symbols/dep_symbols.go @@ -0,0 +1,177 @@ + package depsymbols +// ^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/ +// kind Package +// display_name depsymbols +// signature_documentation +// > package depsymbols + + import "github.com/example/deplib" +// ^^^^^^^^^^^^^^^^^^^^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/ + +//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseGenericField(). + func UseGenericField() int { +// ^^^^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/UseGenericField(). +// kind Function +// display_name UseGenericField +// signature_documentation +// > func UseGenericField() int + b := deplib.Box[int]{Value: 42} +// ^ definition local 0 +// kind Variable +// display_name b +// signature_documentation +// > var b Box[int] +// ^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/ +// ^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Box# +// ^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Box#Value. + return b.Value +// ^ reference local 0 +// ^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Box#Value. + } +//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseGenericField(). + +//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseGenericMethod(). + func UseGenericMethod() string { +// ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/UseGenericMethod(). +// kind Function +// display_name UseGenericMethod +// signature_documentation +// > func UseGenericMethod() string + b := deplib.Box[string]{Value: "hello"} +// ^ definition local 1 +// kind Variable +// display_name b +// signature_documentation +// > var b Box[string] +// ^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/ +// ^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Box# +// ^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Box#Value. + return b.Get() +// ^ reference local 1 +// ^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Box#Get(). + } +//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseGenericMethod(). + +//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseNonGenericField(). + func UseNonGenericField() string { +// ^^^^^^^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/UseNonGenericField(). +// kind Function +// display_name UseNonGenericField +// signature_documentation +// > func UseNonGenericField() string + c := deplib.Config{Name: "test", Verbose: true} +// ^ definition local 2 +// kind Variable +// display_name c +// signature_documentation +// > var c Config +// ^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/ +// ^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Config# +// ^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Config#Name. +// ^^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Config#Verbose. + return c.Name +// ^ reference local 2 +// ^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Config#Name. + } +//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseNonGenericField(). + +//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseConst(). + func UseConst() string { +// ^^^^^^^^ definition 0.1.test `sg/dep-symbols`/UseConst(). +// kind Function +// display_name UseConst +// signature_documentation +// > func UseConst() string + return deplib.DefaultName +// ^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/ +// ^^^^^^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/DefaultName. + } +//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseConst(). + +//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseVar(). + func UseVar() int { +// ^^^^^^ definition 0.1.test `sg/dep-symbols`/UseVar(). +// kind Function +// display_name UseVar +// signature_documentation +// > func UseVar() int + return deplib.GlobalCounter +// ^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/ +// ^^^^^^^^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/GlobalCounter. + } +//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseVar(). + + type LocalType struct{} +// ^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/LocalType# +// kind Struct +// display_name LocalType +// signature_documentation +// > type LocalType struct{} +// relationship github.com/example/deplib 0.1.test `github.com/example/deplib`/Stringer# implementation + +//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/LocalType#String(). + func (l LocalType) String() string { return "local" } +// ^ definition local 3 +// kind Variable +// display_name l +// signature_documentation +// > var l LocalType +// ^^^^^^^^^ reference 0.1.test `sg/dep-symbols`/LocalType# +// ^^^^^^ definition 0.1.test `sg/dep-symbols`/LocalType#String(). +// kind Method +// display_name String +// signature_documentation +// > func (LocalType).String() string +// relationship github.com/example/deplib 0.1.test `github.com/example/deplib`/Stringer#String(). implementation +// ⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/LocalType#String(). + + type EmbeddedStringer struct { +// ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/EmbeddedStringer# +// kind Struct +// display_name EmbeddedStringer +// signature_documentation +// > type EmbeddedStringer struct{ LocalType } +// relationship github.com/example/deplib 0.1.test `github.com/example/deplib`/Stringer# implementation + LocalType +// ^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/EmbeddedStringer#LocalType. +// kind Field +// display_name LocalType +// signature_documentation +// > struct field LocalType LocalType +// ^^^^^^^^^ reference 0.1.test `sg/dep-symbols`/LocalType# + } + + type LocalInterface interface { +// ^^^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/LocalInterface# +// kind Interface +// display_name LocalInterface +// signature_documentation +// > type LocalInterface interface{ Get() int } + Get() int +// ^^^ definition 0.1.test `sg/dep-symbols`/LocalInterface#Get. +// kind MethodSpecification +// display_name Get +// signature_documentation +// > func (LocalInterface).Get() int + } + +//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseDepWriter(). + func UseDepWriter(w deplib.Writer) { +// ^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/UseDepWriter(). +// kind Function +// display_name UseDepWriter +// signature_documentation +// > func UseDepWriter(w deplib.Writer) +// ^ definition local 4 +// kind Variable +// display_name w +// signature_documentation +// > var w Writer +// ^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/ +// ^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Writer# + w.Write(nil) +// ^ reference local 4 +// ^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Writer#Write(). + } +//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseDepWriter(). + diff --git a/internal/testdata/snapshots/output/embedded/embedded.go b/internal/testdata/snapshots/output/embedded/embedded.go index ec28e85..6fa1991 100755 --- a/internal/testdata/snapshots/output/embedded/embedded.go +++ b/internal/testdata/snapshots/output/embedded/embedded.go @@ -18,9 +18,7 @@ // display_name osExecCommand // signature_documentation // > type osExecCommand struct{ *exec.Cmd } -// relationship github.com/golang/go/src go1.22 context/stringer# implementation // relationship github.com/golang/go/src go1.22 fmt/Stringer# implementation -// relationship github.com/golang/go/src go1.22 runtime/stringer# implementation *exec.Cmd // ^^^^ reference github.com/golang/go/src go1.22 `os/exec`/ // ^^^ definition 0.1.test `sg/embedded`/osExecCommand#Cmd. diff --git a/internal/testdata/snapshots/output/embedded/nested.go b/internal/testdata/snapshots/output/embedded/nested.go index c869d73..bafcb47 100755 --- a/internal/testdata/snapshots/output/embedded/nested.go +++ b/internal/testdata/snapshots/output/embedded/nested.go @@ -50,10 +50,10 @@ _ = n.Handler.ServeHTTP // ^ reference local 0 // ^^^^^^^ reference 0.1.test `sg/embedded`/NestedHandler#Handler. -// ^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Handler#ServeHTTP. +// ^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Handler#ServeHTTP(). _ = n.ServeHTTP // ^ reference local 0 -// ^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Handler#ServeHTTP. +// ^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Handler#ServeHTTP(). _ = n.Other // ^ reference local 0 // ^^^^^ reference 0.1.test `sg/embedded`/NestedHandler#Other. diff --git a/internal/testdata/snapshots/output/embedded/something.go b/internal/testdata/snapshots/output/embedded/something.go index e78dedb..cdceb7b 100755 --- a/internal/testdata/snapshots/output/embedded/something.go +++ b/internal/testdata/snapshots/output/embedded/something.go @@ -17,9 +17,7 @@ // display_name String // signature_documentation // > func (*RecentCommittersResults).String() string -// relationship github.com/golang/go/src go1.22 context/stringer#String. implementation -// relationship github.com/golang/go/src go1.22 fmt/Stringer#String. implementation -// relationship github.com/golang/go/src go1.22 runtime/stringer#String. implementation +// relationship github.com/golang/go/src go1.22 fmt/Stringer#String(). implementation return fmt.Sprintf("RecentCommittersResults{Nodes: %d}", len(r.Nodes)) // ^^^ reference github.com/golang/go/src go1.22 fmt/ // ^^^^^^^ reference github.com/golang/go/src go1.22 fmt/Sprintf(). @@ -47,9 +45,7 @@ // > } // > PageInfo struct{ HasNextPage bool } // > } -// relationship github.com/golang/go/src go1.22 context/stringer# implementation // relationship github.com/golang/go/src go1.22 fmt/Stringer# implementation -// relationship github.com/golang/go/src go1.22 runtime/stringer# implementation Nodes []struct { // ^^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#Nodes. // kind Field diff --git a/internal/testdata/snapshots/output/impls/remote_impls.go b/internal/testdata/snapshots/output/impls/remote_impls.go index 6447e46..e7d26e5 100755 --- a/internal/testdata/snapshots/output/impls/remote_impls.go +++ b/internal/testdata/snapshots/output/impls/remote_impls.go @@ -26,8 +26,6 @@ // display_name MyWriter // signature_documentation // > type MyWriter struct{} -// relationship github.com/golang/go/src go1.22 `crypto/tls`/transcriptHash# implementation -// relationship github.com/golang/go/src go1.22 `internal/bisect`/Writer# implementation // relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter# implementation // relationship github.com/golang/go/src go1.22 io/Writer# implementation @@ -44,7 +42,7 @@ // display_name Header // signature_documentation // > func (MyWriter).Header() http.Header -// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Header. implementation +// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Header(). implementation // ^^^^ reference github.com/golang/go/src go1.22 `net/http`/ // ^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Header# // ⌃ enclosing_range_end 0.1.test `sg/impls`/MyWriter#Header(). @@ -61,10 +59,8 @@ // display_name Write // signature_documentation // > func (MyWriter).Write([]byte) (int, error) -// relationship github.com/golang/go/src go1.22 `crypto/tls`/transcriptHash#Write. implementation -// relationship github.com/golang/go/src go1.22 `internal/bisect`/Writer#Write. implementation -// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Write. implementation -// relationship github.com/golang/go/src go1.22 io/Writer#Write. implementation +// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Write(). implementation +// relationship github.com/golang/go/src go1.22 io/Writer#Write(). implementation // ⌃ enclosing_range_end 0.1.test `sg/impls`/MyWriter#Write(). //⌄ enclosing_range_start 0.1.test `sg/impls`/MyWriter#WriteHeader(). func (w MyWriter) WriteHeader(statusCode int) { panic("") } @@ -79,7 +75,7 @@ // display_name WriteHeader // signature_documentation // > func (MyWriter).WriteHeader(statusCode int) -// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#WriteHeader. implementation +// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#WriteHeader(). implementation // ^^^^^^^^^^ definition local 4 // kind Variable // display_name statusCode diff --git a/internal/testdata/snapshots/output/testdata/implementations_remote.go b/internal/testdata/snapshots/output/testdata/implementations_remote.go index fd583d3..911d269 100755 --- a/internal/testdata/snapshots/output/testdata/implementations_remote.go +++ b/internal/testdata/snapshots/output/testdata/implementations_remote.go @@ -10,8 +10,6 @@ // display_name implementsWriter // signature_documentation // > type implementsWriter struct{} -// relationship github.com/golang/go/src go1.22 `crypto/tls`/transcriptHash# implementation -// relationship github.com/golang/go/src go1.22 `internal/bisect`/Writer# implementation // relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter# implementation // relationship github.com/golang/go/src go1.22 io/Writer# implementation @@ -23,7 +21,7 @@ // display_name Header // signature_documentation // > func (implementsWriter).Header() http.Header -// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Header. implementation +// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Header(). implementation // ^^^^ reference github.com/golang/go/src go1.22 `net/http`/ // ^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Header# // ⌃ enclosing_range_end 0.1.test `sg/testdata`/implementsWriter#Header(). @@ -35,10 +33,8 @@ // display_name Write // signature_documentation // > func (implementsWriter).Write([]byte) (int, error) -// relationship github.com/golang/go/src go1.22 `crypto/tls`/transcriptHash#Write. implementation -// relationship github.com/golang/go/src go1.22 `internal/bisect`/Writer#Write. implementation -// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Write. implementation -// relationship github.com/golang/go/src go1.22 io/Writer#Write. implementation +// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Write(). implementation +// relationship github.com/golang/go/src go1.22 io/Writer#Write(). implementation // ⌃ enclosing_range_end 0.1.test `sg/testdata`/implementsWriter#Write(). //⌄ enclosing_range_start 0.1.test `sg/testdata`/implementsWriter#WriteHeader(). func (implementsWriter) WriteHeader(statusCode int) {} @@ -48,7 +44,7 @@ // display_name WriteHeader // signature_documentation // > func (implementsWriter).WriteHeader(statusCode int) -// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#WriteHeader. implementation +// relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#WriteHeader(). implementation // ^^^^^^^^^^ definition local 0 // kind Variable // display_name statusCode @@ -72,7 +68,7 @@ // ^^^^^^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ResponseWriter# respWriter.WriteHeader(1) // ^^^^^^^^^^ reference local 1 -// ^^^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ResponseWriter#WriteHeader. +// ^^^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ResponseWriter#WriteHeader(). } //⌃ enclosing_range_end 0.1.test `sg/testdata`/ShowsInSignature(). From 3d09385c3690032841658e5b0f66420d8f13cfdd Mon Sep 17 00:00:00 2001 From: pj <113072185+prathshenoy@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:23:42 -0400 Subject: [PATCH 2/3] fix(loader): Load export file --- internal/loader/loader.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/loader/loader.go b/internal/loader/loader.go index d488da9..087f2e1 100644 --- a/internal/loader/loader.go +++ b/internal/loader/loader.go @@ -19,7 +19,8 @@ import ( type PackageLookup map[newtypes.PackageID]*packages.Package -var loadMode = packages.NeedImports | +var loadMode = packages.NeedExportFile | + packages.NeedImports | packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo | From aa66fe7b988e7d05775eeb97804bcec6f51d680e Mon Sep 17 00:00:00 2001 From: "prath.shenoy" Date: Tue, 28 Apr 2026 23:32:30 +0000 Subject: [PATCH 3/3] refactor(symbols): Address feedback --- internal/implementations/extractor.go | 18 ++--- internal/implementations/implementations.go | 2 +- internal/index/scip.go | 10 +-- internal/lookup/lookup.go | 6 +- internal/symbols/composer.go | 80 ++++++++----------- internal/symbols/composer_test.go | 25 +++--- .../testdata/snapshots/input/pr222/README.md | 13 +++ .../{dep-symbols => pr222}/deplib/box.go | 0 .../{dep-symbols => pr222}/deplib/go.mod | 0 .../input/{dep-symbols => pr222}/go.mod | 2 +- .../dep_symbols.go => pr222/pr222.go} | 2 +- .../dep_symbols.go => pr222/pr222.go} | 68 ++++++++-------- 12 files changed, 108 insertions(+), 118 deletions(-) create mode 100644 internal/testdata/snapshots/input/pr222/README.md rename internal/testdata/snapshots/input/{dep-symbols => pr222}/deplib/box.go (100%) rename internal/testdata/snapshots/input/{dep-symbols => pr222}/deplib/go.mod (100%) rename internal/testdata/snapshots/input/{dep-symbols => pr222}/go.mod (81%) rename internal/testdata/snapshots/input/{dep-symbols/dep_symbols.go => pr222/pr222.go} (97%) rename internal/testdata/snapshots/output/{dep-symbols/dep_symbols.go => pr222/pr222.go} (74%) diff --git a/internal/implementations/extractor.go b/internal/implementations/extractor.go index 2650fa3..a8b0a40 100644 --- a/internal/implementations/extractor.go +++ b/internal/implementations/extractor.go @@ -12,22 +12,18 @@ import ( "golang.org/x/tools/go/types/typeutil" ) -type Extractor interface { - Extract(pkgLookup loader.PackageLookup) (interfaces, concretes map[string]ImplDef) -} - -type extractor struct { +type Extractor struct { global *lookup.Global methodSetCache typeutil.MethodSetCache } -func NewExtractor(global *lookup.Global) Extractor { - return &extractor{ +func NewExtractor(global *lookup.Global) *Extractor { + return &Extractor{ global: global, } } -func (e *extractor) Extract(pkgLookup loader.PackageLookup) (map[string]ImplDef, map[string]ImplDef) { +func (e *Extractor) Extract(pkgLookup loader.PackageLookup) (map[string]ImplDef, map[string]ImplDef) { interfaces := map[string]ImplDef{} concretes := map[string]ImplDef{} @@ -48,7 +44,7 @@ func (e *extractor) Extract(pkgLookup loader.PackageLookup) (map[string]ImplDef, return interfaces, concretes } -func (e *extractor) extractLocal(pkg *packages.Package, interfaces, concretes map[string]ImplDef) { +func (e *Extractor) extractLocal(pkg *packages.Package, interfaces, concretes map[string]ImplDef) { pkgSymbols := e.global.GetPackage(pkg) if pkgSymbols == nil { slog.Warn("No symbols for package", "path", pkg.PkgPath) @@ -89,7 +85,7 @@ func (e *extractor) extractLocal(pkg *packages.Package, interfaces, concretes ma } } -func (e *extractor) extractRemote(pkg *packages.Package, interfaces, concretes map[string]ImplDef) { +func (e *Extractor) extractRemote(pkg *packages.Package, interfaces, concretes map[string]ImplDef) { scope := pkg.Types.Scope() for _, name := range scope.Names() { @@ -112,7 +108,7 @@ func (e *extractor) extractRemote(pkg *packages.Package, interfaces, concretes m } } -func (e *extractor) classify( +func (e *Extractor) classify( named *types.Named, sym *scip.SymbolInformation, pkgPath string, diff --git a/internal/implementations/implementations.go b/internal/implementations/implementations.go index dc5c34e..107a82b 100644 --- a/internal/implementations/implementations.go +++ b/internal/implementations/implementations.go @@ -125,7 +125,7 @@ func findImplementations(concreteTypes map[string]ImplDef, interfaces map[string func AddImplementationRelationships( pkgs loader.PackageLookup, allPackages loader.PackageLookup, - extractor Extractor, + extractor *Extractor, ) ([]*scip.SymbolInformation, error) { var externalSymbols []*scip.SymbolInformation diff --git a/internal/index/scip.go b/internal/index/scip.go index feabf83..5c7b27c 100644 --- a/internal/index/scip.go +++ b/internal/index/scip.go @@ -60,10 +60,7 @@ func ListMissing(opts config.IndexOpts) (missing []string, err error) { return nil, err } - composer := symbols.NewComposer(symbols.ComposerConfig{ - DefaultModulePath: opts.ModuleRoot, - DefaultModuleVersion: opts.ModuleVersion, - }) + composer := symbols.NewComposer(opts.ModuleRoot, opts.ModuleVersion) globalSymbols := lookup.NewGlobalSymbols(composer) pathToDocuments := map[string]*document.Document{} @@ -187,10 +184,7 @@ func indexVisitPackages( ) (map[string]*document.Document, *lookup.Global) { pathToDocuments := map[string]*document.Document{} - composer := symbols.NewComposer(symbols.ComposerConfig{ - DefaultModulePath: opts.ModuleRoot, - DefaultModuleVersion: opts.ModuleVersion, - }) + composer := symbols.NewComposer(opts.ModuleRoot, opts.ModuleVersion) globalSymbols := lookup.NewGlobalSymbols(composer) for _, pkg := range allPackages { globalSymbols.SetPkgSymbol(pkg) diff --git a/internal/lookup/lookup.go b/internal/lookup/lookup.go index 5c5c591..51c9b68 100644 --- a/internal/lookup/lookup.go +++ b/internal/lookup/lookup.go @@ -21,7 +21,7 @@ func NewPackageSymbols(pkg *packages.Package) *Package { } } -func NewGlobalSymbols(composer symbols.Composer) *Global { +func NewGlobalSymbols(composer *symbols.Composer) *Global { return &Global{ symbols: map[newtypes.PackageID]*Package{}, pkgSymbols: map[newtypes.PackageID]string{}, @@ -76,10 +76,10 @@ type Global struct { m sync.Mutex symbols map[newtypes.PackageID]*Package pkgSymbols map[newtypes.PackageID]string - composer symbols.Composer + composer *symbols.Composer } -func (p *Global) Composer() symbols.Composer { +func (p *Global) Composer() *symbols.Composer { return p.composer } diff --git a/internal/symbols/composer.go b/internal/symbols/composer.go index 3266545..6bb373d 100644 --- a/internal/symbols/composer.go +++ b/internal/symbols/composer.go @@ -2,48 +2,44 @@ package symbols import ( "go/types" + "sync" "github.com/scip-code/scip/bindings/go/scip" "golang.org/x/tools/go/packages" ) -const ( - _scheme = "scip-go" - _manager = "gomod" -) - -type Composer interface { - Compose(pkg *packages.Package, obj types.Object) string -} - -type ComposerConfig struct { - DefaultModulePath string - DefaultModuleVersion string -} - -type composer struct { +type Composer struct { defaultModulePath string defaultModuleVersion string + mu sync.Mutex compositions map[types.Object]string - fieldOwners map[*types.Var]*types.TypeName } -func NewComposer(cfg ComposerConfig) Composer { - return &composer{ - defaultModulePath: cfg.DefaultModulePath, - defaultModuleVersion: cfg.DefaultModuleVersion, +func NewComposer(defaultModulePath, defaultModuleVersion string) *Composer { + return &Composer{ + defaultModulePath: defaultModulePath, + defaultModuleVersion: defaultModuleVersion, compositions: make(map[types.Object]string), - fieldOwners: make(map[*types.Var]*types.TypeName), } } -func (c *composer) Compose(pkg *packages.Package, obj types.Object) string { +func (c *Composer) Compose(pkg *packages.Package, obj types.Object) string { if obj == nil || obj.Pkg() == nil { return "" } + switch o := obj.(type) { + case *types.Var: + obj = o.Origin() + case *types.Func: + obj = o.Origin() + } + + c.mu.Lock() + defer c.mu.Unlock() + if composition, ok := c.compositions[obj]; ok { return composition } @@ -66,30 +62,30 @@ func (c *composer) Compose(pkg *packages.Package, obj types.Object) string { } c.compositions[obj] = scip.VerboseSymbolFormatter.FormatSymbol(&scip.Symbol{ - Scheme: _scheme, + Scheme: "scip-go", Package: c.pack(pkg), Descriptors: descriptors, }) return c.compositions[obj] } -func (c *composer) pack(pkg *packages.Package) *scip.Package { +func (c *Composer) pack(pkg *packages.Package) *scip.Package { if pkg == nil || pkg.Module == nil { return &scip.Package{ - Manager: _manager, + Manager: "gomod", Name: c.defaultModulePath, Version: c.defaultModuleVersion, } } return &scip.Package{ - Manager: _manager, + Manager: "gomod", Name: pkg.Module.Path, Version: pkg.Module.Version, } } -func (c *composer) describePkgName(pkgName *types.PkgName) []*scip.Descriptor { +func (c *Composer) describePkgName(pkgName *types.PkgName) []*scip.Descriptor { return []*scip.Descriptor{ { Name: pkgName.Imported().Path(), @@ -98,7 +94,7 @@ func (c *composer) describePkgName(pkgName *types.PkgName) []*scip.Descriptor { } } -func (c *composer) describeTypeName(typeName *types.TypeName) []*scip.Descriptor { +func (c *Composer) describeTypeName(typeName *types.TypeName) []*scip.Descriptor { return []*scip.Descriptor{ { Name: typeName.Pkg().Path(), @@ -111,7 +107,7 @@ func (c *composer) describeTypeName(typeName *types.TypeName) []*scip.Descriptor } } -func (c *composer) describeConst(constant *types.Const) []*scip.Descriptor { +func (c *Composer) describeConst(constant *types.Const) []*scip.Descriptor { return []*scip.Descriptor{ { Name: constant.Pkg().Path(), @@ -124,7 +120,7 @@ func (c *composer) describeConst(constant *types.Const) []*scip.Descriptor { } } -func (c *composer) describeFunc(fn *types.Func) []*scip.Descriptor { +func (c *Composer) describeFunc(fn *types.Func) []*scip.Descriptor { sig, ok := fn.Type().(*types.Signature) if !ok { return nil @@ -164,7 +160,7 @@ func (c *composer) describeFunc(fn *types.Func) []*scip.Descriptor { } } -func (c *composer) nameType(t types.Type) string { +func (c *Composer) nameType(t types.Type) string { for { switch u := types.Unalias(t).(type) { case *types.Pointer: @@ -177,7 +173,7 @@ func (c *composer) nameType(t types.Type) string { } } -func (c *composer) describeVar(variable *types.Var) []*scip.Descriptor { +func (c *Composer) describeVar(variable *types.Var) []*scip.Descriptor { if !variable.IsField() { return []*scip.Descriptor{ { @@ -212,18 +208,10 @@ func (c *composer) describeVar(variable *types.Var) []*scip.Descriptor { } } -func (c *composer) locateOwner(field *types.Var) *types.TypeName { - origin := field.Origin() - - if owner, ok := c.fieldOwners[origin]; ok { - return owner - } - +func (c *Composer) locateOwner(field *types.Var) *types.TypeName { scope := field.Pkg().Scope() for _, name := range scope.Names() { - obj := scope.Lookup(name) - - typeName, ok := obj.(*types.TypeName) + typeName, ok := scope.Lookup(name).(*types.TypeName) if !ok { continue } @@ -238,10 +226,12 @@ func (c *composer) locateOwner(field *types.Var) *types.TypeName { continue } - for field := range structType.Fields() { - c.fieldOwners[field.Origin()] = typeName + for f := range structType.Fields() { + if f == field { + return typeName + } } } - return c.fieldOwners[origin] + return nil } diff --git a/internal/symbols/composer_test.go b/internal/symbols/composer_test.go index 8f02f85..8e6d5f0 100644 --- a/internal/symbols/composer_test.go +++ b/internal/symbols/composer_test.go @@ -10,7 +10,7 @@ import ( ) func TestComposeNil(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{}) + c := symbols.NewComposer("", "") pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} if got := c.Compose(pkg, nil); got != "" { @@ -19,7 +19,7 @@ func TestComposeNil(t *testing.T) { } func TestComposeNilPkg(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{}) + c := symbols.NewComposer("", "") pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} obj := types.Universe.Lookup("len") @@ -29,7 +29,7 @@ func TestComposeNilPkg(t *testing.T) { } func TestComposeTypeName(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{}) + c := symbols.NewComposer("", "") pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} tpkg := types.NewPackage("example.com/lib", "lib") @@ -44,7 +44,7 @@ func TestComposeTypeName(t *testing.T) { } func TestComposeConst(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{}) + c := symbols.NewComposer("", "") pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} tpkg := types.NewPackage("example.com/lib", "lib") @@ -58,7 +58,7 @@ func TestComposeConst(t *testing.T) { } func TestComposePackageVar(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{}) + c := symbols.NewComposer("", "") pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} tpkg := types.NewPackage("example.com/lib", "lib") @@ -72,7 +72,7 @@ func TestComposePackageVar(t *testing.T) { } func TestComposeFunc(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{}) + c := symbols.NewComposer("", "") pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} tpkg := types.NewPackage("example.com/lib", "lib") @@ -87,7 +87,7 @@ func TestComposeFunc(t *testing.T) { } func TestComposeMethod(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{}) + c := symbols.NewComposer("", "") pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} tpkg := types.NewPackage("example.com/lib", "lib") @@ -106,7 +106,7 @@ func TestComposeMethod(t *testing.T) { } func TestComposeMethodPointerReceiver(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{}) + c := symbols.NewComposer("", "") pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} tpkg := types.NewPackage("example.com/lib", "lib") @@ -125,7 +125,7 @@ func TestComposeMethodPointerReceiver(t *testing.T) { } func TestComposeStructField(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{}) + c := symbols.NewComposer("", "") pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} tpkg := types.NewPackage("example.com/lib", "lib") @@ -144,10 +144,7 @@ func TestComposeStructField(t *testing.T) { } func TestComposeDefaultModule(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{ - DefaultModulePath: "example.com/project", - DefaultModuleVersion: "1.0.0", - }) + c := symbols.NewComposer("example.com/project", "1.0.0") pkg := &packages.Package{PkgPath: "example.com/lib"} tpkg := types.NewPackage("example.com/lib", "lib") @@ -161,7 +158,7 @@ func TestComposeDefaultModule(t *testing.T) { } func TestComposeMemoization(t *testing.T) { - c := symbols.NewComposer(symbols.ComposerConfig{}) + c := symbols.NewComposer("", "") pkg := &packages.Package{PkgPath: "example.com/lib", Module: &packages.Module{Path: "example.com/lib", Version: "v1.0.0"}} tpkg := types.NewPackage("example.com/lib", "lib") diff --git a/internal/testdata/snapshots/input/pr222/README.md b/internal/testdata/snapshots/input/pr222/README.md new file mode 100644 index 0000000..739a1d1 --- /dev/null +++ b/internal/testdata/snapshots/input/pr222/README.md @@ -0,0 +1,13 @@ +# On-demand dependency symbol resolution + +Tests that references into a dependency module produce correct symbols when the +dependency's symbols are resolved on demand by the `Composer` rather than +eagerly indexed. + +Exercises the major symbol shapes a dependency can expose: generic and +non-generic struct fields, methods on generic types, package-level consts and +vars, embedded fields, and interface methods used through a dependency-defined +interface. + +The `deplib/` subdirectory is a separate module wired in via a `replace` +directive in `go.mod`. diff --git a/internal/testdata/snapshots/input/dep-symbols/deplib/box.go b/internal/testdata/snapshots/input/pr222/deplib/box.go similarity index 100% rename from internal/testdata/snapshots/input/dep-symbols/deplib/box.go rename to internal/testdata/snapshots/input/pr222/deplib/box.go diff --git a/internal/testdata/snapshots/input/dep-symbols/deplib/go.mod b/internal/testdata/snapshots/input/pr222/deplib/go.mod similarity index 100% rename from internal/testdata/snapshots/input/dep-symbols/deplib/go.mod rename to internal/testdata/snapshots/input/pr222/deplib/go.mod diff --git a/internal/testdata/snapshots/input/dep-symbols/go.mod b/internal/testdata/snapshots/input/pr222/go.mod similarity index 81% rename from internal/testdata/snapshots/input/dep-symbols/go.mod rename to internal/testdata/snapshots/input/pr222/go.mod index 19966a0..1f173cd 100644 --- a/internal/testdata/snapshots/input/dep-symbols/go.mod +++ b/internal/testdata/snapshots/input/pr222/go.mod @@ -1,4 +1,4 @@ -module sg/dep-symbols +module sg/pr222 go 1.22 diff --git a/internal/testdata/snapshots/input/dep-symbols/dep_symbols.go b/internal/testdata/snapshots/input/pr222/pr222.go similarity index 97% rename from internal/testdata/snapshots/input/dep-symbols/dep_symbols.go rename to internal/testdata/snapshots/input/pr222/pr222.go index a9a15be..e430843 100644 --- a/internal/testdata/snapshots/input/dep-symbols/dep_symbols.go +++ b/internal/testdata/snapshots/input/pr222/pr222.go @@ -1,4 +1,4 @@ -package depsymbols +package pr222 import "github.com/example/deplib" diff --git a/internal/testdata/snapshots/output/dep-symbols/dep_symbols.go b/internal/testdata/snapshots/output/pr222/pr222.go similarity index 74% rename from internal/testdata/snapshots/output/dep-symbols/dep_symbols.go rename to internal/testdata/snapshots/output/pr222/pr222.go index a40c845..932b614 100755 --- a/internal/testdata/snapshots/output/dep-symbols/dep_symbols.go +++ b/internal/testdata/snapshots/output/pr222/pr222.go @@ -1,16 +1,16 @@ - package depsymbols -// ^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/ -// kind Package -// display_name depsymbols -// signature_documentation -// > package depsymbols + package pr222 +// ^^^^^ definition 0.1.test `sg/pr222`/ +// kind Package +// display_name pr222 +// signature_documentation +// > package pr222 import "github.com/example/deplib" // ^^^^^^^^^^^^^^^^^^^^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/ -//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseGenericField(). +//⌄ enclosing_range_start 0.1.test `sg/pr222`/UseGenericField(). func UseGenericField() int { -// ^^^^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/UseGenericField(). +// ^^^^^^^^^^^^^^^ definition 0.1.test `sg/pr222`/UseGenericField(). // kind Function // display_name UseGenericField // signature_documentation @@ -28,11 +28,11 @@ // ^ reference local 0 // ^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Box#Value. } -//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseGenericField(). +//⌃ enclosing_range_end 0.1.test `sg/pr222`/UseGenericField(). -//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseGenericMethod(). +//⌄ enclosing_range_start 0.1.test `sg/pr222`/UseGenericMethod(). func UseGenericMethod() string { -// ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/UseGenericMethod(). +// ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/pr222`/UseGenericMethod(). // kind Function // display_name UseGenericMethod // signature_documentation @@ -50,11 +50,11 @@ // ^ reference local 1 // ^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Box#Get(). } -//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseGenericMethod(). +//⌃ enclosing_range_end 0.1.test `sg/pr222`/UseGenericMethod(). -//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseNonGenericField(). +//⌄ enclosing_range_start 0.1.test `sg/pr222`/UseNonGenericField(). func UseNonGenericField() string { -// ^^^^^^^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/UseNonGenericField(). +// ^^^^^^^^^^^^^^^^^^ definition 0.1.test `sg/pr222`/UseNonGenericField(). // kind Function // display_name UseNonGenericField // signature_documentation @@ -73,11 +73,11 @@ // ^ reference local 2 // ^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Config#Name. } -//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseNonGenericField(). +//⌃ enclosing_range_end 0.1.test `sg/pr222`/UseNonGenericField(). -//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseConst(). +//⌄ enclosing_range_start 0.1.test `sg/pr222`/UseConst(). func UseConst() string { -// ^^^^^^^^ definition 0.1.test `sg/dep-symbols`/UseConst(). +// ^^^^^^^^ definition 0.1.test `sg/pr222`/UseConst(). // kind Function // display_name UseConst // signature_documentation @@ -86,11 +86,11 @@ // ^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/ // ^^^^^^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/DefaultName. } -//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseConst(). +//⌃ enclosing_range_end 0.1.test `sg/pr222`/UseConst(). -//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseVar(). +//⌄ enclosing_range_start 0.1.test `sg/pr222`/UseVar(). func UseVar() int { -// ^^^^^^ definition 0.1.test `sg/dep-symbols`/UseVar(). +// ^^^^^^ definition 0.1.test `sg/pr222`/UseVar(). // kind Function // display_name UseVar // signature_documentation @@ -99,65 +99,65 @@ // ^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/ // ^^^^^^^^^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/GlobalCounter. } -//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseVar(). +//⌃ enclosing_range_end 0.1.test `sg/pr222`/UseVar(). type LocalType struct{} -// ^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/LocalType# +// ^^^^^^^^^ definition 0.1.test `sg/pr222`/LocalType# // kind Struct // display_name LocalType // signature_documentation // > type LocalType struct{} // relationship github.com/example/deplib 0.1.test `github.com/example/deplib`/Stringer# implementation -//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/LocalType#String(). +//⌄ enclosing_range_start 0.1.test `sg/pr222`/LocalType#String(). func (l LocalType) String() string { return "local" } // ^ definition local 3 // kind Variable // display_name l // signature_documentation // > var l LocalType -// ^^^^^^^^^ reference 0.1.test `sg/dep-symbols`/LocalType# -// ^^^^^^ definition 0.1.test `sg/dep-symbols`/LocalType#String(). +// ^^^^^^^^^ reference 0.1.test `sg/pr222`/LocalType# +// ^^^^^^ definition 0.1.test `sg/pr222`/LocalType#String(). // kind Method // display_name String // signature_documentation // > func (LocalType).String() string // relationship github.com/example/deplib 0.1.test `github.com/example/deplib`/Stringer#String(). implementation -// ⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/LocalType#String(). +// ⌃ enclosing_range_end 0.1.test `sg/pr222`/LocalType#String(). type EmbeddedStringer struct { -// ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/EmbeddedStringer# +// ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/pr222`/EmbeddedStringer# // kind Struct // display_name EmbeddedStringer // signature_documentation // > type EmbeddedStringer struct{ LocalType } // relationship github.com/example/deplib 0.1.test `github.com/example/deplib`/Stringer# implementation LocalType -// ^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/EmbeddedStringer#LocalType. +// ^^^^^^^^^ definition 0.1.test `sg/pr222`/EmbeddedStringer#LocalType. // kind Field // display_name LocalType // signature_documentation // > struct field LocalType LocalType -// ^^^^^^^^^ reference 0.1.test `sg/dep-symbols`/LocalType# +// ^^^^^^^^^ reference 0.1.test `sg/pr222`/LocalType# } type LocalInterface interface { -// ^^^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/LocalInterface# +// ^^^^^^^^^^^^^^ definition 0.1.test `sg/pr222`/LocalInterface# // kind Interface // display_name LocalInterface // signature_documentation // > type LocalInterface interface{ Get() int } Get() int -// ^^^ definition 0.1.test `sg/dep-symbols`/LocalInterface#Get. +// ^^^ definition 0.1.test `sg/pr222`/LocalInterface#Get. // kind MethodSpecification // display_name Get // signature_documentation // > func (LocalInterface).Get() int } -//⌄ enclosing_range_start 0.1.test `sg/dep-symbols`/UseDepWriter(). +//⌄ enclosing_range_start 0.1.test `sg/pr222`/UseDepWriter(). func UseDepWriter(w deplib.Writer) { -// ^^^^^^^^^^^^ definition 0.1.test `sg/dep-symbols`/UseDepWriter(). +// ^^^^^^^^^^^^ definition 0.1.test `sg/pr222`/UseDepWriter(). // kind Function // display_name UseDepWriter // signature_documentation @@ -173,5 +173,5 @@ // ^ reference local 4 // ^^^^^ reference github.com/example/deplib 0.1.test `github.com/example/deplib`/Writer#Write(). } -//⌃ enclosing_range_end 0.1.test `sg/dep-symbols`/UseDepWriter(). +//⌃ enclosing_range_end 0.1.test `sg/pr222`/UseDepWriter().