├── .tool-versions ├── internal ├── index │ ├── version.txt │ ├── package_ident_test.go │ ├── package_ident.go │ └── scip_test.go ├── testdata │ └── snapshots │ │ ├── input │ │ ├── impls │ │ │ ├── go.mod │ │ │ ├── impls.go │ │ │ └── remote_impls.go │ │ ├── initial │ │ │ ├── go.mod │ │ │ ├── builtin_types.go │ │ │ ├── func_declarations.go │ │ │ ├── package_definition.go │ │ │ ├── type_hovers.go │ │ │ ├── rangefunc.go │ │ │ ├── toplevel_decls.go │ │ │ ├── methods_and_receivers.go │ │ │ ├── type_declarations.go │ │ │ └── child_symbols.go │ │ ├── embedded │ │ │ ├── go.mod │ │ │ ├── nested.go │ │ │ ├── internal │ │ │ │ └── nested.go │ │ │ ├── something.go │ │ │ └── embedded.go │ │ ├── switches │ │ │ ├── go.mod │ │ │ └── switches.go │ │ ├── testdata │ │ │ ├── go.mod │ │ │ ├── duplicate_path_id │ │ │ │ ├── two.go │ │ │ │ └── main.go │ │ │ ├── internal │ │ │ │ └── secret │ │ │ │ │ ├── doc.go │ │ │ │ │ └── secret.go │ │ │ ├── external_composite.go │ │ │ ├── cmd │ │ │ │ └── minimal_main │ │ │ │ │ └── minimal_main.go │ │ │ ├── implementations_embedded.go │ │ │ ├── named_import.go │ │ │ ├── typealias.go │ │ │ ├── typeswitch.go │ │ │ ├── conflicting_test_symbols │ │ │ │ ├── sandbox_unsupported_test.go │ │ │ │ └── sandbox_unsupported.go │ │ │ ├── implementations_remote.go │ │ │ ├── implementations_methods.go │ │ │ ├── anonymous_structs.go │ │ │ ├── parallel.go │ │ │ ├── implementations.go │ │ │ └── data.go │ │ ├── inlinestruct │ │ │ ├── go.mod │ │ │ ├── inlinestruct_map.go │ │ │ ├── inlinestruct_func.go │ │ │ ├── inlinestruct.go │ │ │ ├── inlinestruct_interface.go │ │ │ ├── inlinestruct_genericindex.go │ │ │ └── inlinestruct_multivar.go │ │ ├── testspecial │ │ │ ├── go.mod │ │ │ ├── foo.go │ │ │ ├── foo_test.go │ │ │ └── foo_other_test.go │ │ ├── sharedtestmodule │ │ │ ├── go.mod │ │ │ └── cmd │ │ │ │ └── gitserver │ │ │ │ └── server │ │ │ │ ├── server.go │ │ │ │ ├── cleanup.go │ │ │ │ ├── cleanup_test.go │ │ │ │ └── server_test.go │ │ ├── package-documentation │ │ │ ├── secondary.go │ │ │ └── primary.go │ │ ├── generallyeric │ │ │ ├── go.mod │ │ │ ├── generallyeric.go │ │ │ ├── go.sum │ │ │ ├── new_operators.go │ │ │ └── person.go │ │ ├── replace-directives │ │ │ ├── replace_directives.go │ │ │ ├── go.sum │ │ │ └── go.mod │ │ └── alias │ │ │ └── main.go │ │ └── output │ │ ├── testdata │ │ ├── internal │ │ │ └── secret │ │ │ │ ├── doc.go │ │ │ │ └── secret.go │ │ ├── duplicate_path_id │ │ │ ├── two.go │ │ │ └── main.go │ │ ├── named_import.go │ │ ├── typeswitch.go │ │ ├── external_composite.go │ │ ├── typealias.go │ │ ├── implementations_embedded.go │ │ ├── cmd │ │ │ └── minimal_main │ │ │ │ └── minimal_main.go │ │ ├── conflicting_test_symbols │ │ │ └── sandbox_unsupported_test.go │ │ ├── parallel.go │ │ └── implementations_remote.go │ │ ├── initial │ │ ├── package_definition.go │ │ ├── builtin_types.go │ │ ├── type_hovers.go │ │ ├── func_declarations.go │ │ ├── rangefunc.go │ │ ├── toplevel_decls.go │ │ ├── methods_and_receivers.go │ │ └── type_declarations.go │ │ ├── inlinestruct │ │ ├── inlinestruct_map.go │ │ ├── inlinestruct_func.go │ │ ├── inlinestruct.go │ │ ├── inlinestruct_interface.go │ │ ├── inlinestruct_genericindex.go │ │ └── inlinestruct_multivar.go │ │ ├── testspecial │ │ ├── foo.go │ │ ├── foo_test.go │ │ └── foo_other_test.go │ │ ├── sharedtestmodule │ │ └── cmd │ │ │ └── gitserver │ │ │ └── server │ │ │ ├── cleanup.go │ │ │ ├── server.go │ │ │ ├── cleanup_test.go │ │ │ └── server_test.go │ │ ├── package-documentation │ │ ├── secondary.go │ │ └── primary.go │ │ ├── generallyeric │ │ ├── generallyeric.go │ │ ├── new_operators.go │ │ └── person.go │ │ ├── replace-directives │ │ └── replace_directives.go │ │ ├── embedded │ │ ├── internal │ │ │ └── nested.go │ │ ├── nested.go │ │ ├── something.go │ │ └── embedded.go │ │ ├── switches │ │ └── switches.go │ │ ├── alias │ │ └── main.go │ │ └── impls │ │ ├── impls.go │ │ └── remote_impls.go ├── handler │ └── handler.go ├── newtypes │ └── newtypes.go ├── funk │ └── funk.go ├── git │ ├── toplevel.go │ ├── infer_module_version.go │ ├── infer_repo.go │ └── infer_repo_test.go ├── command │ └── command.go ├── parallel │ ├── parallel.go │ └── parallel_test.go ├── symbols │ └── symbols.go ├── lookup │ └── local.go ├── visitors │ ├── scope.go │ ├── visitor_func.go │ ├── visitor_type.go │ ├── visitors.go │ └── visitor_var.go ├── output │ ├── duration.go │ ├── duration_test.go │ └── output.go ├── loader │ ├── stdlib_dynamic.go │ ├── stdlib_dynamic_test.go │ ├── stdlib.go │ └── loader_test.go ├── config │ └── config.go └── modules │ └── modules.go ├── .github ├── labeler.yml ├── dependabot.yml ├── renovate.json └── workflows │ ├── labeler.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .vscode └── launch.json ├── Development.md ├── scripts ├── gen_std_lib.sh ├── dockerfile_checks.sh └── sourcegraph_qa.sh ├── .goreleaser.yml ├── cmd └── scip-go │ └── paths.go ├── Dockerfile ├── dev └── publish-release.sh ├── AGENTS.md ├── README.md └── CHANGELOG.md /.tool-versions: -------------------------------------------------------------------------------- 1 | golang 1.25.0 2 | -------------------------------------------------------------------------------- /internal/index/version.txt: -------------------------------------------------------------------------------- 1 | 0.1.26 2 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/impls/go.mod: -------------------------------------------------------------------------------- 1 | module sg/impls 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | team/graph: 2 | - '/.*/' 3 | graph/scip-go: 4 | - '/.*/' 5 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/initial/go.mod: -------------------------------------------------------------------------------- 1 | module sg/initial 2 | 3 | go 1.23 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/embedded/go.mod: -------------------------------------------------------------------------------- 1 | module sg/embedded 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/switches/go.mod: -------------------------------------------------------------------------------- 1 | module sg/switches 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/go.mod: -------------------------------------------------------------------------------- 1 | module sg/testdata 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/inlinestruct/go.mod: -------------------------------------------------------------------------------- 1 | module sg/inlinestruct 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testspecial/go.mod: -------------------------------------------------------------------------------- 1 | module sg/testspecial 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testspecial/foo.go: -------------------------------------------------------------------------------- 1 | package testspecial 2 | 3 | func Foo() {} 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/sharedtestmodule/go.mod: -------------------------------------------------------------------------------- 1 | module sg/sharedtestmodule 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/duplicate_path_id/two.go: -------------------------------------------------------------------------------- 1 | package gosrc 2 | 3 | func init() {} 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testspecial/foo_test.go: -------------------------------------------------------------------------------- 1 | package testspecial 2 | 3 | func TestFoo_Whitebox() { Foo() } 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/package-documentation/secondary.go: -------------------------------------------------------------------------------- 1 | package packagedocumentation 2 | 3 | func AlsoExporter() {} 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/sharedtestmodule/cmd/gitserver/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | func AnythingAtAll() {} 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/internal/secret/doc.go: -------------------------------------------------------------------------------- 1 | // secret is a package that holds secrets. 2 | package secret 3 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/sharedtestmodule/cmd/gitserver/server/cleanup.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | func LiterallyAnything() {} 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/inlinestruct/inlinestruct_map.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | 3 | var testHook = func(map[string]string) {} 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/initial/builtin_types.go: -------------------------------------------------------------------------------- 1 | package initial 2 | 3 | func UsesBuiltin() int { 4 | var x int = 5 5 | return x 6 | } 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "docker" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/generallyeric/go.mod: -------------------------------------------------------------------------------- 1 | module sg/generallyeric 2 | 3 | go 1.19 4 | 5 | require golang.org/x/exp v0.0.0-20221205204356-47842c84f3db 6 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/initial/func_declarations.go: -------------------------------------------------------------------------------- 1 | package initial 2 | 3 | func UsesLater() { 4 | DefinedLater() 5 | } 6 | 7 | func DefinedLater() {} 8 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/package-documentation/primary.go: -------------------------------------------------------------------------------- 1 | // This is documentation for this package. 2 | package packagedocumentation 3 | 4 | func Exported() {} 5 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/initial/package_definition.go: -------------------------------------------------------------------------------- 1 | // This is a module for testing purposes. 2 | // This should now be the place that has a definition 3 | package initial 4 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/external_composite.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import "net/http" 4 | 5 | type NestedHandler struct { 6 | http.Handler 7 | Other int 8 | } 9 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/cmd/minimal_main/minimal_main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type User struct { 4 | Id, Name string 5 | } 6 | 7 | type UserResource struct{} 8 | 9 | func main() {} 10 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/inlinestruct/inlinestruct_func.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | 3 | type InFuncSig struct { 4 | value bool 5 | } 6 | 7 | var rowsCloseHook = func() func(InFuncSig, *error) { return nil } 8 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/implementations_embedded.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import "io" 4 | 5 | type I3 interface { 6 | Close() error 7 | } 8 | 9 | type TClose struct { 10 | io.Closer 11 | } 12 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/named_import.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | . "fmt" 5 | h "net/http" 6 | ) 7 | 8 | func Example() { 9 | Println(h.CanonicalHeaderKey("accept-encoding")) 10 | } 11 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testspecial/foo_other_test.go: -------------------------------------------------------------------------------- 1 | package testspecial_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "sg/testspecial" 7 | ) 8 | 9 | func TestFoo_Blackbox(*testing.T) { testspecial.Foo() } 10 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/initial/type_hovers.go: -------------------------------------------------------------------------------- 1 | package initial 2 | 3 | type ( 4 | // HoverTypeList is a cool struct 5 | HoverTypeList struct{} 6 | ) 7 | 8 | // This should show up as well 9 | type HoverType struct{} 10 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/initial/rangefunc.go: -------------------------------------------------------------------------------- 1 | package initial 2 | 3 | import ( 4 | "slices" 5 | ) 6 | 7 | func f(xs []int) int { 8 | for _, x := range slices.All(xs) { 9 | return x 10 | } 11 | return -1 12 | } 13 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/initial/toplevel_decls.go: -------------------------------------------------------------------------------- 1 | package initial 2 | 3 | const MY_THING = 10 4 | const OTHER_THING = MY_THING 5 | 6 | func usesMyThing() { 7 | _ = MY_THING 8 | } 9 | 10 | var initFunctions = map[string]int{} 11 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "ignorePaths": [ "**/internal/testdata/snapshots/**", ".tool-versions" ], 4 | "extends": [ 5 | "local>sourcegraph/renovate-config" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/internal/secret/secret.go: -------------------------------------------------------------------------------- 1 | package secret 2 | 3 | // SecretScore is like score but _secret_. 4 | const SecretScore = uint64(43) 5 | 6 | // Original doc 7 | type Burger struct { 8 | Field int 9 | } 10 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/generallyeric/generallyeric.go: -------------------------------------------------------------------------------- 1 | // generallyeric -> generic for short 2 | package generallyeric 3 | 4 | import "fmt" 5 | 6 | func Print[T any](s []T) { 7 | for _, v := range s { 8 | fmt.Print(v) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/replace-directives/replace_directives.go: -------------------------------------------------------------------------------- 1 | package replacers 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/dghubble/gologin" 7 | ) 8 | 9 | func Something() { 10 | fmt.Println(gologin.DefaultCookieConfig) 11 | } 12 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/generallyeric/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= 2 | golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= 3 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/sharedtestmodule/cmd/gitserver/server/cleanup_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "testing" 4 | 5 | func TestStuff(t *testing.T) { 6 | wd := "hello" 7 | repo := "world" 8 | 9 | runCmd(t, wd, "git", "init", repo) 10 | } 11 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/typealias.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "sg/testdata/internal/secret" 5 | ) 6 | 7 | // Type aliased doc 8 | type SecretBurger = secret.Burger 9 | 10 | type BadBurger = struct { 11 | Field string 12 | } 13 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/sharedtestmodule/cmd/gitserver/server/server_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "testing" 4 | 5 | func TestExecRequest(t *testing.T) { 6 | t.Log("hello world") 7 | } 8 | 9 | func runCmd(t *testing.T, dir string, cmd string, arg ...string) {} 10 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/alias/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Check that we don't panic 4 | // Copied from https://github.com/golang/go/issues/68877#issuecomment-2290000187 5 | type ( 6 | T struct{} 7 | U = T 8 | V = U 9 | S U 10 | Z int32 11 | ) 12 | 13 | func f(u U) {} 14 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/internal/secret/doc.go: -------------------------------------------------------------------------------- 1 | // secret is a package that holds secrets. 2 | package secret 3 | // ^^^^^^ definition 0.1.test `sg/testdata/internal/secret`/ 4 | // documentation 5 | // > secret is a package that holds secrets. 6 | 7 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/replace-directives/go.sum: -------------------------------------------------------------------------------- 1 | github.com/sourcegraph/gologin v1.0.2-0.20181110030308-c6f1b62954d8 h1:K7hzuWsJGoU8ILHJzrXxsuvXvLHpP/g4iUk7VFj2lY8= 2 | github.com/sourcegraph/gologin v1.0.2-0.20181110030308-c6f1b62954d8/go.mod h1:0VfoEApmSPgPhnePllwhrB4vwCUkI0K0w8aueOgoJQI= 3 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/initial/methods_and_receivers.go: -------------------------------------------------------------------------------- 1 | package initial 2 | 3 | import "fmt" 4 | 5 | type MyStruct struct{ f, y int } 6 | 7 | func (m MyStruct) RecvFunction(b int) int { return m.f + b } 8 | 9 | func SomethingElse() { 10 | s := MyStruct{f: 0} 11 | fmt.Println(s) 12 | } 13 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/duplicate_path_id/main.go: -------------------------------------------------------------------------------- 1 | package gosrc 2 | 3 | type importMeta struct{} 4 | 5 | type sourceMeta struct{} 6 | 7 | func fetchMeta() (string, *importMeta, *sourceMeta) { 8 | panic("hmm") 9 | } 10 | 11 | func init() {} 12 | func init() {} 13 | func init() {} 14 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/replace-directives/go.mod: -------------------------------------------------------------------------------- 1 | module sg/replace-directives 2 | 3 | go 1.19 4 | 5 | 6 | require ( 7 | github.com/dghubble/gologin v2.2.0+incompatible 8 | ) 9 | 10 | replace ( 11 | github.com/dghubble/gologin => github.com/sourcegraph/gologin v1.0.2-0.20181110030308-c6f1b62954d8 12 | ) 13 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/typeswitch.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | func Switch(interfaceValue interface{}) bool { 4 | switch concreteValue := interfaceValue.(type) { 5 | case int: 6 | return concreteValue*3 > 10 7 | case bool: 8 | return !concreteValue 9 | default: 10 | return false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/handler/handler.go: -------------------------------------------------------------------------------- 1 | package handler 2 | 3 | import "fmt" 4 | 5 | var devMode bool = false 6 | 7 | func SetDev(dev bool) { 8 | devMode = dev 9 | } 10 | 11 | func ErrOrPanic(format string, a ...any) error { 12 | if devMode { 13 | panic(fmt.Sprintf(format, a...)) 14 | } 15 | 16 | return fmt.Errorf(format, a...) 17 | } 18 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/embedded/nested.go: -------------------------------------------------------------------------------- 1 | package embedded 2 | 3 | import "net/http" 4 | 5 | type NestedHandler struct { 6 | http.Handler 7 | 8 | // Wow, a great thing for integers 9 | Other int 10 | } 11 | 12 | func NestedExample(n NestedHandler) { 13 | _ = n.Handler.ServeHTTP 14 | _ = n.ServeHTTP 15 | _ = n.Other 16 | } 17 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/embedded/internal/nested.go: -------------------------------------------------------------------------------- 1 | package nested_internal 2 | 3 | import ( 4 | "fmt" 5 | "sg/embedded" 6 | ) 7 | 8 | func Something(recent embedded.RecentCommittersResults) { 9 | for _, commit := range recent.Nodes { 10 | for _, author := range commit.Authors.Nodes { 11 | fmt.Println(author.Name) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/inlinestruct/inlinestruct.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | 3 | type FieldInterface interface { 4 | SomeMethod() string 5 | } 6 | 7 | var MyInline = struct { 8 | privateField FieldInterface 9 | PublicField FieldInterface 10 | }{} 11 | 12 | func MyFunc() { 13 | _ = MyInline.privateField 14 | _ = MyInline.PublicField 15 | } 16 | -------------------------------------------------------------------------------- /internal/newtypes/newtypes.go: -------------------------------------------------------------------------------- 1 | package newtypes 2 | 3 | import ( 4 | "go/types" 5 | 6 | "golang.org/x/tools/go/packages" 7 | ) 8 | 9 | type PackageID string 10 | 11 | func GetID(pkg *packages.Package) PackageID { 12 | return PackageID(pkg.PkgPath) 13 | } 14 | 15 | func GetFromTypesPackage(tPkg *types.Package) PackageID { 16 | return PackageID(tPkg.Path()) 17 | } 18 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/impls/impls.go: -------------------------------------------------------------------------------- 1 | package impls 2 | 3 | type I1 interface { 4 | F1() 5 | } 6 | 7 | type I1Clone interface { 8 | F1() 9 | } 10 | 11 | type IfaceOther interface { 12 | Something() 13 | Another() 14 | } 15 | 16 | type T1 int 17 | 18 | func (r T1) F1() {} 19 | 20 | type T2 int 21 | 22 | func (r T2) F1() {} 23 | func (r T2) F2() {} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | /scip-go 18 | 19 | scratch/ 20 | .idea/ 21 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/initial/package_definition.go: -------------------------------------------------------------------------------- 1 | // This is a module for testing purposes. 2 | // This should now be the place that has a definition 3 | package initial 4 | // ^^^^^^^ definition 0.1.test `sg/initial`/ 5 | // documentation 6 | // > This is a module for testing purposes. 7 | // > This should now be the place that has a definition 8 | 9 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/inlinestruct/inlinestruct_map.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | // ^^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/ 3 | 4 | var testHook = func(map[string]string) {} 5 | // ^^^^^^^^ definition 0.1.test `sg/inlinestruct`/testHook. 6 | // documentation 7 | // > ```go 8 | // > var testHook func(map[string]string) 9 | // > ``` 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Issue Labeler" 2 | on: 3 | issues: 4 | types: [opened, edited] 5 | 6 | permissions: 7 | issues: write 8 | contents: read 9 | 10 | jobs: 11 | triage: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: github/issue-labeler@v3.4 15 | with: 16 | configuration-path: .github/labeler.yml 17 | enable-versioned-regex: 0 18 | repo-token: ${{ github.token }} 19 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/impls/remote_impls.go: -------------------------------------------------------------------------------- 1 | package impls 2 | 3 | import "net/http" 4 | 5 | func Something(r http.ResponseWriter) {} 6 | 7 | type MyWriter struct{} 8 | 9 | func (w MyWriter) Header() http.Header { panic("") } 10 | func (w MyWriter) Write([]byte) (int, error) { panic("") } 11 | func (w MyWriter) WriteHeader(statusCode int) { panic("") } 12 | 13 | func Another() { 14 | Something(MyWriter{}) 15 | } 16 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/embedded/something.go: -------------------------------------------------------------------------------- 1 | package embedded 2 | 3 | import "fmt" 4 | 5 | type RecentCommittersResults struct { 6 | Nodes []struct { 7 | Authors struct { 8 | Nodes []struct { 9 | Date string 10 | Email string 11 | Name string 12 | User struct { 13 | Login string 14 | } 15 | AvatarURL string 16 | } 17 | } 18 | } 19 | PageInfo struct { 20 | HasNextPage bool 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/generallyeric/new_operators.go: -------------------------------------------------------------------------------- 1 | package generallyeric 2 | 3 | import "golang.org/x/exp/constraints" 4 | 5 | type Number interface { 6 | constraints.Float | constraints.Integer | constraints.Complex 7 | } 8 | 9 | func Double[T Number](value T) T { 10 | return value * 2 11 | } 12 | 13 | type Box[T any] struct { 14 | Something T 15 | } 16 | 17 | type handler[T any] struct { 18 | Box[T] 19 | Another string 20 | } 21 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/inlinestruct/inlinestruct_interface.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | 3 | import "context" 4 | 5 | func Target() interface { 6 | OID(context.Context) (int, error) 7 | AbbreviatedOID(context.Context) (string, error) 8 | Commit(context.Context) (string, error) 9 | Type(context.Context) (int, error) 10 | } { 11 | panic("not implemented") 12 | } 13 | 14 | func something() { 15 | x := Target() 16 | x.OID(context.Background()) 17 | } 18 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/inlinestruct/inlinestruct_genericindex.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | 3 | type Processor[T any] interface { 4 | Process(payload T) 5 | ProcessorType() string 6 | } 7 | 8 | type Limit int 9 | 10 | type ProcessImpl struct{} 11 | 12 | func (p *ProcessImpl) Process(payload Limit) { panic("not implemented") } 13 | func (p *ProcessImpl) ProcessorType() string { panic("not implemented") } 14 | 15 | var _ Processor[Limit] = &ProcessImpl{} 16 | -------------------------------------------------------------------------------- /internal/funk/funk.go: -------------------------------------------------------------------------------- 1 | package funk 2 | 3 | import ( 4 | "slices" 5 | 6 | "golang.org/x/exp/constraints" 7 | "golang.org/x/exp/maps" 8 | ) 9 | 10 | func SortedKeys[K constraints.Ordered, V any](m map[K]V) []K { 11 | keys := maps.Keys(m) 12 | slices.Sort(keys) 13 | return keys 14 | } 15 | 16 | func Map[T any, V any](l []T, f func(T) V) []V { 17 | vals := make([]V, 0, len(l)) 18 | for i, v := range l { 19 | vals[i] = f(v) 20 | } 21 | 22 | return vals 23 | } 24 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/conflicting_test_symbols/sandbox_unsupported_test.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !windows && !freebsd 2 | // +build !linux,!windows,!freebsd 3 | 4 | package osl 5 | 6 | import ( 7 | "errors" 8 | "testing" 9 | ) 10 | 11 | var ErrNotImplemented = errors.New("not implemented") 12 | 13 | func newKey(t *testing.T) (string, error) { 14 | return "", ErrNotImplemented 15 | } 16 | 17 | func verifySandbox(t *testing.T, s string) { 18 | return 19 | } 20 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/implementations_remote.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import "net/http" 4 | 5 | type implementsWriter struct{} 6 | 7 | func (implementsWriter) Header() http.Header { panic("Just for how") } 8 | func (implementsWriter) Write([]byte) (int, error) { panic("Just for show") } 9 | func (implementsWriter) WriteHeader(statusCode int) {} 10 | 11 | func ShowsInSignature(respWriter http.ResponseWriter) { 12 | respWriter.WriteHeader(1) 13 | } 14 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/inlinestruct/inlinestruct_multivar.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | 3 | type Params struct{} 4 | type HighlightedCode struct{} 5 | 6 | var Mocks, emptyMocks struct { 7 | Code func(p Params) (response *HighlightedCode, aborted bool, err error) 8 | } 9 | 10 | var MocksSingle struct { 11 | Code func(p Params) (response *HighlightedCode, aborted bool, err error) 12 | } 13 | 14 | var ( 15 | okReply interface{} = "OK" 16 | pongReply interface{} = "PONG" 17 | ) 18 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/generallyeric/person.go: -------------------------------------------------------------------------------- 1 | package generallyeric 2 | 3 | import "fmt" 4 | 5 | type Person interface { 6 | Work() 7 | } 8 | 9 | type worker string 10 | 11 | func (w worker) Work() { 12 | fmt.Printf("%s is working\n", w) 13 | } 14 | 15 | func DoWork[T Person](things []T) { 16 | for _, v := range things { 17 | v.Work() 18 | } 19 | } 20 | 21 | func main() { 22 | var a, b, c worker 23 | a = "A" 24 | b = "B" 25 | c = "C" 26 | DoWork([]worker{a, b, c}) 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run SCIP-GO", 6 | "type": "go", 7 | "request": "launch", 8 | "mode": "auto", 9 | "program": "cmd/scip-go", 10 | "cwd": "${input:path}", 11 | } 12 | ], 13 | "inputs": [ 14 | { 15 | "id": "path", 16 | "description": "Please enter the path to the project to index", 17 | "default": "", 18 | "type": "promptString" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testspecial/foo.go: -------------------------------------------------------------------------------- 1 | package testspecial 2 | // ^^^^^^^^^^^ definition 0.1.test `sg/testspecial`/ 3 | // documentation 4 | // > package testspecial 5 | 6 | //⌄ enclosing_range_start 0.1.test `sg/testspecial`/Foo(). 7 | func Foo() {} 8 | // ^^^ definition 0.1.test `sg/testspecial`/Foo(). 9 | // documentation 10 | // > ```go 11 | // > func Foo() 12 | // > ``` 13 | // ⌃ enclosing_range_end 0.1.test `sg/testspecial`/Foo(). 14 | 15 | -------------------------------------------------------------------------------- /internal/git/toplevel.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/sourcegraph/scip-go/internal/command" 8 | ) 9 | 10 | // TopLevel returns the root of the git project containing the given directory. 11 | func TopLevel(dir string) (string, error) { 12 | output, err := command.Run(dir, "git", "rev-parse", "--show-toplevel") 13 | if err != nil { 14 | return "", fmt.Errorf("failed to get toplevel: %v\n%s", err, output) 15 | } 16 | 17 | return strings.TrimSpace(string(output)), nil 18 | } 19 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/initial/type_declarations.go: -------------------------------------------------------------------------------- 1 | package initial 2 | 3 | type LiteralType int 4 | 5 | type FuncType func(LiteralType, int) bool 6 | 7 | type IfaceType interface { 8 | Method() LiteralType 9 | } 10 | 11 | type StructType struct { 12 | m IfaceType 13 | f LiteralType 14 | 15 | // anonymous struct 16 | anon struct { 17 | sub int 18 | } 19 | 20 | // interface within struct 21 | i interface { 22 | AnonMethod() bool 23 | } 24 | } 25 | 26 | type DeclaredBefore struct{ DeclaredAfter } 27 | type DeclaredAfter struct{} 28 | -------------------------------------------------------------------------------- /Development.md: -------------------------------------------------------------------------------- 1 | # Development docs 2 | 3 | ## Run tests 4 | 5 | Run all tests: 6 | 7 | ```bash 8 | go test ./... 9 | ``` 10 | 11 | Update snapshot tests: 12 | 13 | ```bash 14 | go test ./internal/index -update-snapshots 15 | ``` 16 | 17 | ## Cutting releases 18 | 19 | 1. Land a PR with the following changes: 20 | 21 | - A ChangeLog entry with `## vM.N.P` 22 | - Updated version in `internal/index/version.txt` 23 | 24 | 2. On the `main` branch, run the following: 25 | 26 | ```bash 27 | NEW_VERSION="M.N.P" ./dev/publish-release.sh 28 | ``` 29 | -------------------------------------------------------------------------------- /internal/command/command.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "os/exec" 5 | "strings" 6 | ) 7 | 8 | // Run runs the given command using the given working directory. If the command succeeds, 9 | // the value of stdout is returned with trailing whitespace removed. If the command fails, 10 | // the combined stdout/stderr text will also be returned. 11 | func Run(dir, command string, args ...string) (string, error) { 12 | cmd := exec.Command(command, args...) 13 | cmd.Dir = dir 14 | 15 | out, err := cmd.CombinedOutput() 16 | return strings.TrimSpace(string(out)), err 17 | } 18 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/duplicate_path_id/two.go: -------------------------------------------------------------------------------- 1 | package gosrc 2 | // ^^^^^ definition 0.1.test `sg/testdata/duplicate_path_id`/ 3 | // documentation 4 | // > package gosrc 5 | 6 | //⌄ enclosing_range_start 0.1.test `sg/testdata/duplicate_path_id`/init(). 7 | func init() {} 8 | // ^^^^ definition 0.1.test `sg/testdata/duplicate_path_id`/init(). 9 | // documentation 10 | // > ```go 11 | // > func init() 12 | // > ``` 13 | // ⌃ enclosing_range_end 0.1.test `sg/testdata/duplicate_path_id`/init(). 14 | 15 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/switches/switches.go: -------------------------------------------------------------------------------- 1 | package switches 2 | 3 | // CustomSwitch does the things in a switch 4 | type CustomSwitch struct{} 5 | 6 | // Something does some things... and stuff 7 | func (c *CustomSwitch) Something() bool { return false } 8 | 9 | func Switch(interfaceValue interface{}) bool { 10 | switch concreteValue := interfaceValue.(type) { 11 | case int: 12 | return concreteValue*3 > 10 13 | case bool: 14 | return !concreteValue 15 | case CustomSwitch: 16 | return concreteValue.Something() 17 | default: 18 | return false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/initial/builtin_types.go: -------------------------------------------------------------------------------- 1 | package initial 2 | // ^^^^^^^ reference 0.1.test `sg/initial`/ 3 | 4 | //⌄ enclosing_range_start 0.1.test `sg/initial`/UsesBuiltin(). 5 | func UsesBuiltin() int { 6 | // ^^^^^^^^^^^ definition 0.1.test `sg/initial`/UsesBuiltin(). 7 | // documentation 8 | // > ```go 9 | // > func UsesBuiltin() int 10 | // > ``` 11 | var x int = 5 12 | // ^ definition local 0 13 | return x 14 | // ^ reference local 0 15 | } 16 | //⌃ enclosing_range_end 0.1.test `sg/initial`/UsesBuiltin(). 17 | 18 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/embedded/embedded.go: -------------------------------------------------------------------------------- 1 | package embedded 2 | 3 | import ( 4 | "fmt" 5 | "os/exec" 6 | ) 7 | 8 | type osExecCommand struct { 9 | *exec.Cmd 10 | } 11 | 12 | func wrapExecCommand(c *exec.Cmd) { 13 | _ = &osExecCommand{Cmd: c} 14 | } 15 | 16 | type Inner struct { 17 | X int 18 | Y int 19 | Z int 20 | } 21 | 22 | type Outer struct { 23 | Inner 24 | W int 25 | } 26 | 27 | func useOfCompositeStructs() { 28 | o := Outer{ 29 | Inner: Inner{ 30 | X: 1, 31 | Y: 2, 32 | Z: 3, 33 | }, 34 | W: 4, 35 | } 36 | 37 | fmt.Printf("> %d\n", o.X) 38 | fmt.Println(o.Inner.Y) 39 | } 40 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testspecial/foo_test.go: -------------------------------------------------------------------------------- 1 | package testspecial 2 | // ^^^^^^^^^^^ reference 0.1.test `sg/testspecial`/ 3 | 4 | //⌄ enclosing_range_start 0.1.test `sg/testspecial`/TestFoo_Whitebox(). 5 | func TestFoo_Whitebox() { Foo() } 6 | // ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/testspecial`/TestFoo_Whitebox(). 7 | // documentation 8 | // > ```go 9 | // > func TestFoo_Whitebox() 10 | // > ``` 11 | // ^^^ reference 0.1.test `sg/testspecial`/Foo(). 12 | // ⌃ enclosing_range_end 0.1.test `sg/testspecial`/TestFoo_Whitebox(). 13 | 14 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/sharedtestmodule/cmd/gitserver/server/cleanup.go: -------------------------------------------------------------------------------- 1 | package server 2 | // ^^^^^^ reference 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/ 3 | 4 | //⌄ enclosing_range_start 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/LiterallyAnything(). 5 | func LiterallyAnything() {} 6 | // ^^^^^^^^^^^^^^^^^ definition 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/LiterallyAnything(). 7 | // documentation 8 | // > ```go 9 | // > func LiterallyAnything() 10 | // > ``` 11 | // ⌃ enclosing_range_end 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/LiterallyAnything(). 12 | 13 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/implementations_methods.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | type InterfaceWithSingleMethod interface { 4 | SingleMethod() float64 5 | } 6 | 7 | type StructWithMethods struct{} 8 | 9 | func (StructWithMethods) SingleMethod() float64 { return 5.0 } 10 | 11 | type InterfaceWithSingleMethodTwoImplementers interface { 12 | SingleMethodTwoImpl() float64 13 | } 14 | 15 | type TwoImplOne struct{} 16 | 17 | func (TwoImplOne) SingleMethodTwoImpl() float64 { return 5.0 } 18 | 19 | type TwoImplTwo struct{} 20 | 21 | func (TwoImplTwo) SingleMethodTwoImpl() float64 { return 5.0 } 22 | func (TwoImplTwo) RandomThingThatDoesntMatter() float64 { return 5.0 } 23 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/sharedtestmodule/cmd/gitserver/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | // ^^^^^^ definition 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/ 3 | // documentation 4 | // > package server 5 | 6 | //⌄ enclosing_range_start 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/AnythingAtAll(). 7 | func AnythingAtAll() {} 8 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/AnythingAtAll(). 9 | // documentation 10 | // > ```go 11 | // > func AnythingAtAll() 12 | // > ``` 13 | // ⌃ enclosing_range_end 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/AnythingAtAll(). 14 | 15 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/initial/type_hovers.go: -------------------------------------------------------------------------------- 1 | package initial 2 | // ^^^^^^^ reference 0.1.test `sg/initial`/ 3 | 4 | type ( 5 | // HoverTypeList is a cool struct 6 | HoverTypeList struct{} 7 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/initial`/HoverTypeList# 8 | // documentation 9 | // > ```go 10 | // > type HoverTypeList struct 11 | // > ``` 12 | // documentation 13 | // > ```go 14 | // > struct{} 15 | // > ``` 16 | ) 17 | 18 | // This should show up as well 19 | type HoverType struct{} 20 | // ^^^^^^^^^ definition 0.1.test `sg/initial`/HoverType# 21 | // documentation 22 | // > ```go 23 | // > type HoverType struct 24 | // > ``` 25 | // documentation 26 | // > This should show up as well 27 | // documentation 28 | // > ```go 29 | // > struct{} 30 | // > ``` 31 | 32 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/conflicting_test_symbols/sandbox_unsupported.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !darwin 2 | // +build !linux,!darwin 3 | 4 | // From https://github.com/moby/moby/blob/master/libnetwork/osl/sandbox_unsupported.go 5 | 6 | package osl 7 | 8 | import "errors" 9 | 10 | var ( 11 | // ErrNotImplemented is for platforms which don't implement sandbox 12 | ErrNotImplemented = errors.New("not implemented") 13 | ) 14 | 15 | // NewSandbox provides a new sandbox instance created in an os specific way 16 | // provided a key which uniquely identifies the sandbox 17 | func NewSandbox(key string, osCreate, isRestore bool) (*string, error) { 18 | return nil, ErrNotImplemented 19 | } 20 | 21 | // GenerateKey generates a sandbox key based on the passed 22 | // container id. 23 | func GenerateKey(containerID string) string { 24 | return "" 25 | } 26 | -------------------------------------------------------------------------------- /scripts/gen_std_lib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | cat > ./internal/loader/stdlib.go << EOT 5 | // THIS FILE IS GENERATED. SEE ./scripts/gen_std_lib.sh 6 | EOT 7 | 8 | echo "// Generated by: $(go version)" >> ./internal/loader/stdlib.go 9 | 10 | cat >> ./internal/loader/stdlib.go << EOT 11 | package loader 12 | 13 | var contained = struct{}{} 14 | 15 | // This list is calculated from "go list std". 16 | var stdPackages = map[string]struct{}{ 17 | EOT 18 | 19 | # Runs golist, replaces all the subsequent sub packages with nothing 20 | # and the sorts, and uniqs them, to then print them out into strings 21 | # in a map 22 | 23 | go list std \ 24 | | sed 's:/.*$::' \ 25 | | sort \ 26 | | uniq \ 27 | | awk '{ print "\""$0"\": contained,"}' >> ./internal/loader/stdlib.go 28 | 29 | echo "}" >> ./internal/loader/stdlib.go 30 | 31 | go fmt ./internal/loader/stdlib.go 32 | -------------------------------------------------------------------------------- /internal/git/infer_module_version.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/sourcegraph/scip-go/internal/command" 7 | ) 8 | 9 | // InferModuleVersion returns the version of the module declared in the given 10 | // directory. This will be either the work tree commit's tag, or it will be the 11 | // short revhash of the HEAD commit. 12 | func InferModuleVersion(dir string) (string, error) { 13 | version, err := command.Run(dir, "git", "tag", "-l", "--points-at", "HEAD") 14 | if err != nil { 15 | return "", fmt.Errorf("failed to tags for current commit: %v\n%s", err, version) 16 | } 17 | if version != "" { 18 | return version, nil 19 | } 20 | 21 | commit, err := command.Run(dir, "git", "rev-parse", "HEAD") 22 | if err != nil { 23 | return "", fmt.Errorf("failed to get current commit: %v\n%s", err, commit) 24 | } 25 | 26 | return commit[:12], nil 27 | } 28 | -------------------------------------------------------------------------------- /scripts/dockerfile_checks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | fromGolangLines="$(grep 'FROM golang' Dockerfile | wc -l)" 4 | if [[ "$fromGolangLines" -ne 2 ]]; then 5 | echo "Expected exactly 2 FROM lines in Dockerfile" 6 | exit 1 7 | fi 8 | 9 | uniqueGolangImages="$(grep 'FROM golang' Dockerfile | cut -d " " -f 2 | uniq | wc -l)" 10 | if [[ "$uniqueGolangImages" -ne 1 ]]; then 11 | echo "Expected same image to be repeated in Dockerfile" 12 | exit 1 13 | fi 14 | 15 | golangTag="$(grep 'FROM golang' Dockerfile | head -n 1 | cut -d " " -f 2 | cut -d "@" -f 1)" 16 | golangSha="$(grep 'FROM golang' Dockerfile | head -n 1 | cut -d " " -f 2 | cut -d "@" -f 2)" 17 | digestLine="$(docker buildx imagetools inspect "$golangTag" | grep -E "^Digest:")" 18 | if [[ "$digestLine" =~ *"golangSha"* ]]; then 19 | echo "SHA for $golangTag image ($golangSha) does not match $digestLine" 20 | exit 1 21 | fi 22 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/anonymous_structs.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import "fmt" 4 | 5 | type TypeContainingAnonymousStructs struct { 6 | a, b struct { 7 | x int 8 | y string 9 | } 10 | c struct { 11 | X int 12 | Y string 13 | } 14 | } 15 | 16 | func funcContainingAnonymousStructs() { 17 | d := struct { 18 | x int 19 | y string 20 | }{ 21 | x: 1, 22 | y: "one", 23 | } 24 | 25 | var e struct { 26 | x int 27 | y string 28 | } 29 | 30 | e.x = 2 31 | e.y = "two" 32 | 33 | var f TypeContainingAnonymousStructs 34 | f.a.x = 3 35 | f.a.y = "three" 36 | f.b.x = 4 37 | f.b.y = "four" 38 | f.c.X = 5 39 | f.c.Y = "five" 40 | 41 | fmt.Printf("> %s, %s\n", d.x, d.y) 42 | fmt.Printf("> %s, %s\n", e.x, e.y) 43 | 44 | fmt.Printf("> %s, %s\n", f.a.x, f.a.y) 45 | fmt.Printf("> %s, %s\n", f.b.x, f.b.y) 46 | fmt.Printf("> %s, %s\n", f.c.X, f.c.Y) 47 | } 48 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/initial/func_declarations.go: -------------------------------------------------------------------------------- 1 | package initial 2 | // ^^^^^^^ reference 0.1.test `sg/initial`/ 3 | 4 | //⌄ enclosing_range_start 0.1.test `sg/initial`/UsesLater(). 5 | func UsesLater() { 6 | // ^^^^^^^^^ definition 0.1.test `sg/initial`/UsesLater(). 7 | // documentation 8 | // > ```go 9 | // > func UsesLater() 10 | // > ``` 11 | DefinedLater() 12 | // ^^^^^^^^^^^^ reference 0.1.test `sg/initial`/DefinedLater(). 13 | } 14 | //⌃ enclosing_range_end 0.1.test `sg/initial`/UsesLater(). 15 | 16 | //⌄ enclosing_range_start 0.1.test `sg/initial`/DefinedLater(). 17 | func DefinedLater() {} 18 | // ^^^^^^^^^^^^ definition 0.1.test `sg/initial`/DefinedLater(). 19 | // documentation 20 | // > ```go 21 | // > func DefinedLater() 22 | // > ``` 23 | // ⌃ enclosing_range_end 0.1.test `sg/initial`/DefinedLater(). 24 | 25 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/package-documentation/secondary.go: -------------------------------------------------------------------------------- 1 | package packagedocumentation 2 | // ^^^^^^^^^^^^^^^^^^^^ reference github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/package-documentation`/ 3 | 4 | //⌄ enclosing_range_start github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/package-documentation`/AlsoExporter(). 5 | func AlsoExporter() {} 6 | // ^^^^^^^^^^^^ definition github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/package-documentation`/AlsoExporter(). 7 | // documentation 8 | // > ```go 9 | // > func AlsoExporter() 10 | // > ``` 11 | // ⌃ enclosing_range_end github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/package-documentation`/AlsoExporter(). 12 | 13 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/parallel.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | ) 7 | 8 | // ParallelizableFunc is a function that can be called concurrently with other instances 9 | // of this function type. 10 | type ParallelizableFunc func(ctx context.Context) error 11 | 12 | // Parallel invokes each of the given parallelizable functions in their own goroutines and 13 | // returns the first error to occur. This method will block until all goroutines have returned. 14 | func Parallel(ctx context.Context, fns ...ParallelizableFunc) error { 15 | var wg sync.WaitGroup 16 | errs := make(chan error, len(fns)) 17 | 18 | for _, fn := range fns { 19 | wg.Add(1) 20 | 21 | go func(fn ParallelizableFunc) { 22 | errs <- fn(ctx) 23 | wg.Done() 24 | }(fn) 25 | } 26 | 27 | wg.Wait() 28 | 29 | for err := range errs { 30 | if err != nil { 31 | return err 32 | } 33 | } 34 | 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/named_import.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | // ^^^^^^^^ reference 0.1.test `sg/testdata`/ 3 | 4 | import ( 5 | . "fmt" 6 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 7 | h "net/http" 8 | // ^ definition local 0 9 | // ^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 10 | ) 11 | 12 | //⌄ enclosing_range_start 0.1.test `sg/testdata`/Example(). 13 | func Example() { 14 | // ^^^^^^^ definition 0.1.test `sg/testdata`/Example(). 15 | // documentation 16 | // > ```go 17 | // > func Example() 18 | // > ``` 19 | Println(h.CanonicalHeaderKey("accept-encoding")) 20 | // ^^^^^^^ reference github.com/golang/go/src go1.22 fmt/Println(). 21 | // ^ reference local 0 22 | // ^^^^^^^^^^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/CanonicalHeaderKey(). 23 | } 24 | //⌃ enclosing_range_end 0.1.test `sg/testdata`/Example(). 25 | 26 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | project_name: scip-go 2 | version: 2 3 | 4 | dist: release 5 | 6 | env: 7 | - GO111MODULE=on 8 | - CGO_ENABLED=0 9 | 10 | before: 11 | hooks: 12 | - go mod download 13 | - go mod tidy 14 | 15 | builds: 16 | - 17 | main: ./cmd/scip-go/ 18 | binary: scip-go 19 | ldflags: 20 | - -X main.version={{.Version}} 21 | goos: 22 | - linux 23 | - windows 24 | - darwin 25 | goarch: 26 | - amd64 27 | - arm64 28 | 29 | archives: 30 | - id: tarball 31 | format: tar.gz 32 | 33 | dockers: 34 | - ids: 35 | - scip-go 36 | image_templates: 37 | - "sourcegraph/scip-go:{{ .Tag }}" 38 | - "sourcegraph/scip-go:v{{ .Major }}" 39 | - "sourcegraph/scip-go:v{{ .Major }}.{{ .Minor }}" 40 | - "sourcegraph/scip-go:latest" 41 | extra_files: 42 | - go.mod 43 | - go.sum 44 | - cmd 45 | - internal 46 | 47 | changelog: 48 | sort: asc 49 | filters: 50 | exclude: 51 | - '^docs:' 52 | - '^test:' 53 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/initial/rangefunc.go: -------------------------------------------------------------------------------- 1 | package initial 2 | // ^^^^^^^ reference 0.1.test `sg/initial`/ 3 | 4 | import ( 5 | "slices" 6 | // ^^^^^^ reference github.com/golang/go/src go1.22 slices/ 7 | ) 8 | 9 | //⌄ enclosing_range_start 0.1.test `sg/initial`/f(). 10 | func f(xs []int) int { 11 | // ^ definition 0.1.test `sg/initial`/f(). 12 | // documentation 13 | // > ```go 14 | // > func f(xs []int) int 15 | // > ``` 16 | // ^^ definition local 0 17 | for _, x := range slices.All(xs) { 18 | // ^ definition local 1 19 | // ^^^^^^ reference github.com/golang/go/src go1.22 slices/ 20 | // ^^^ reference github.com/golang/go/src go1.22 slices/All(). 21 | // ^^ reference local 0 22 | return x 23 | // ^ reference local 1 24 | } 25 | return -1 26 | } 27 | //⌃ enclosing_range_end 0.1.test `sg/initial`/f(). 28 | 29 | -------------------------------------------------------------------------------- /internal/parallel/parallel.go: -------------------------------------------------------------------------------- 1 | package parallel 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | // Run will run the functions read from the given channel concurrently. This function 10 | // returns a wait group synchronized on the invocation functions, a channel on which any error 11 | // values are written, and a pointer to the number of tasks that have completed, which is 12 | // updated atomically. 13 | func Run(ch <-chan func() error) (*sync.WaitGroup, chan error, *uint64) { 14 | var count uint64 15 | var wg sync.WaitGroup 16 | 17 | maxRoutines := runtime.GOMAXPROCS(0) 18 | errCh := make(chan error, maxRoutines) // each goroutine returns at most one error 19 | for i := 0; i < maxRoutines; i++ { 20 | wg.Add(1) 21 | 22 | go func() { 23 | defer wg.Done() 24 | 25 | for fn := range ch { 26 | err := fn() 27 | if err != nil { 28 | errCh <- err 29 | } 30 | atomic.AddUint64(&count, 1) 31 | } 32 | }() 33 | } 34 | 35 | return &wg, errCh, &count 36 | } 37 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/package-documentation/primary.go: -------------------------------------------------------------------------------- 1 | // This is documentation for this package. 2 | package packagedocumentation 3 | // ^^^^^^^^^^^^^^^^^^^^ definition github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/package-documentation`/ 4 | // documentation 5 | // > This is documentation for this package. 6 | 7 | //⌄ enclosing_range_start github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/package-documentation`/Exported(). 8 | func Exported() {} 9 | // ^^^^^^^^ definition github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/package-documentation`/Exported(). 10 | // documentation 11 | // > ```go 12 | // > func Exported() 13 | // > ``` 14 | // ⌃ enclosing_range_end github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/package-documentation`/Exported(). 15 | 16 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/inlinestruct/inlinestruct_func.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | // ^^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/ 3 | 4 | type InFuncSig struct { 5 | // ^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/InFuncSig# 6 | // documentation 7 | // > ```go 8 | // > type InFuncSig struct 9 | // > ``` 10 | // documentation 11 | // > ```go 12 | // > struct { 13 | // > value bool 14 | // > } 15 | // > ``` 16 | value bool 17 | // ^^^^^ definition 0.1.test `sg/inlinestruct`/InFuncSig#value. 18 | // documentation 19 | // > ```go 20 | // > struct field value bool 21 | // > ``` 22 | } 23 | 24 | var rowsCloseHook = func() func(InFuncSig, *error) { return nil } 25 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/rowsCloseHook. 26 | // documentation 27 | // > ```go 28 | // > var rowsCloseHook func() func(InFuncSig, *error) 29 | // > ``` 30 | // ^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/InFuncSig# 31 | 32 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/implementations.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | type I0 interface{} 4 | 5 | type I1 interface { 6 | F1() 7 | } 8 | 9 | type I2 interface { 10 | F2() 11 | } 12 | 13 | type T1 int 14 | 15 | func (r T1) F1() {} 16 | 17 | type T2 int 18 | 19 | func (r T2) F1() {} 20 | func (r T2) F2() {} 21 | 22 | type A1 = T1 23 | type A12 = A1 24 | 25 | type InterfaceWithNonExportedMethod interface { 26 | nonExportedMethod() 27 | } 28 | 29 | type InterfaceWithExportedMethod interface { 30 | ExportedMethod() 31 | } 32 | 33 | type Foo int 34 | 35 | func (r Foo) nonExportedMethod() {} 36 | func (r Foo) ExportedMethod() {} 37 | func (r Foo) Close() error { return nil } 38 | 39 | type SharedOne interface { 40 | Shared() 41 | Distinct() 42 | } 43 | 44 | type SharedTwo interface { 45 | Shared() 46 | Unique() 47 | } 48 | 49 | type Between struct{} 50 | 51 | func (Between) Shared() {} 52 | func (Between) Distinct() {} 53 | func (Between) Unique() {} 54 | 55 | func shouldShow(shared SharedOne) { 56 | shared.Shared() 57 | } 58 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/internal/secret/secret.go: -------------------------------------------------------------------------------- 1 | package secret 2 | // ^^^^^^ reference 0.1.test `sg/testdata/internal/secret`/ 3 | 4 | // SecretScore is like score but _secret_. 5 | const SecretScore = uint64(43) 6 | // ^^^^^^^^^^^ definition 0.1.test `sg/testdata/internal/secret`/SecretScore. 7 | // documentation 8 | // > ```go 9 | // > const SecretScore uint64 = 43 10 | // > ``` 11 | // documentation 12 | // > SecretScore is like score but _secret_. 13 | 14 | // Original doc 15 | type Burger struct { 16 | // ^^^^^^ definition 0.1.test `sg/testdata/internal/secret`/Burger# 17 | // documentation 18 | // > ```go 19 | // > type Burger struct 20 | // > ``` 21 | // documentation 22 | // > Original doc 23 | // documentation 24 | // > ```go 25 | // > struct { 26 | // > Field int 27 | // > } 28 | // > ``` 29 | Field int 30 | // ^^^^^ definition 0.1.test `sg/testdata/internal/secret`/Burger#Field. 31 | // documentation 32 | // > ```go 33 | // > struct field Field int 34 | // > ``` 35 | } 36 | 37 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/generallyeric/generallyeric.go: -------------------------------------------------------------------------------- 1 | // generallyeric -> generic for short 2 | package generallyeric 3 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/generallyeric`/ 4 | // documentation 5 | // > generallyeric -> generic for short 6 | 7 | import "fmt" 8 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 9 | 10 | //⌄ enclosing_range_start 0.1.test `sg/generallyeric`/Print(). 11 | func Print[T any](s []T) { 12 | // ^^^^^ definition 0.1.test `sg/generallyeric`/Print(). 13 | // documentation 14 | // > ```go 15 | // > func Print[T any](s []T) 16 | // > ``` 17 | // ^ definition local 0 18 | // ^ definition local 1 19 | // ^ reference local 0 20 | for _, v := range s { 21 | // ^ definition local 2 22 | // ^ reference local 1 23 | fmt.Print(v) 24 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 25 | // ^^^^^ reference github.com/golang/go/src go1.22 fmt/Print(). 26 | // ^ reference local 2 27 | } 28 | } 29 | //⌃ enclosing_range_end 0.1.test `sg/generallyeric`/Print(). 30 | 31 | -------------------------------------------------------------------------------- /cmd/scip-go/paths.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "strings" 7 | "sync" 8 | 9 | "github.com/sourcegraph/scip-go/internal/git" 10 | ) 11 | 12 | var wd = sync.OnceValue(func() string { 13 | if wd, err := os.Getwd(); err == nil { 14 | return wd 15 | } 16 | 17 | return "" 18 | }) 19 | 20 | var toplevel = sync.OnceValue(func() string { 21 | if toplevel, err := git.TopLevel("."); err == nil { 22 | return toplevel 23 | } 24 | 25 | return "" 26 | }) 27 | 28 | func searchForGoMod(path, repositoryRoot string) string { 29 | for ; !strings.HasPrefix(path, repositoryRoot); path = filepath.Dir(path) { 30 | _, err := os.Stat(filepath.Join(path, "go.mod")) 31 | if err == nil { 32 | return rel(path) 33 | } 34 | 35 | if !os.IsNotExist(err) { 36 | // Actual FS error, stop 37 | break 38 | } 39 | 40 | if filepath.Dir(path) == path { 41 | // We just checked the root, prevent infinite loop 42 | break 43 | } 44 | } 45 | 46 | return "." 47 | } 48 | 49 | func rel(path string) string { 50 | relative, err := filepath.Rel(wd(), path) 51 | if err != nil { 52 | return "." 53 | } 54 | 55 | return relative 56 | } 57 | -------------------------------------------------------------------------------- /internal/symbols/symbols.go: -------------------------------------------------------------------------------- 1 | package symbols 2 | 3 | import ( 4 | "fmt" 5 | "go/token" 6 | 7 | "github.com/sourcegraph/scip/bindings/go/scip" 8 | "golang.org/x/tools/go/packages" 9 | ) 10 | 11 | func FromDescriptors(pkg *packages.Package, descriptors ...*scip.Descriptor) string { 12 | if pkg.Module == nil { 13 | panic(fmt.Sprintf("Failed to find package for: %+v\n", pkg.PkgPath)) 14 | } 15 | 16 | return scip.VerboseSymbolFormatter.FormatSymbol(&scip.Symbol{ 17 | Scheme: "scip-go", 18 | Package: &scip.Package{ 19 | Manager: "gomod", 20 | Name: pkg.Module.Path, 21 | Version: pkg.Module.Version, 22 | }, 23 | Descriptors: descriptors, 24 | }) 25 | } 26 | 27 | func RangeFromName(position token.Position, name string, adjust bool) []int32 { 28 | var adjustment int32 = 0 29 | if adjust { 30 | adjustment = 1 31 | } 32 | 33 | line := int32(position.Line - 1) 34 | column := int32(position.Column - 1) 35 | n := int32(len(name)) 36 | 37 | return []int32{line, column + adjustment, column + n + adjustment} 38 | } 39 | 40 | func FormatCode(v string) string { 41 | if v == "" { 42 | return "" 43 | } 44 | 45 | return fmt.Sprintf("```go\n%s\n```", v) 46 | } 47 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/typeswitch.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | // ^^^^^^^^ reference 0.1.test `sg/testdata`/ 3 | 4 | //⌄ enclosing_range_start 0.1.test `sg/testdata`/Switch(). 5 | func Switch(interfaceValue interface{}) bool { 6 | // ^^^^^^ definition 0.1.test `sg/testdata`/Switch(). 7 | // documentation 8 | // > ```go 9 | // > func Switch(interfaceValue interface{}) bool 10 | // > ``` 11 | // ^^^^^^^^^^^^^^ definition local 0 12 | switch concreteValue := interfaceValue.(type) { 13 | // ^^^^^^^^^^^^^ definition local 1 14 | // ^^^^^^^^^^^^^^ reference local 0 15 | case int: 16 | return concreteValue*3 > 10 17 | // ^^^^^^^^^^^^^ reference local 1 18 | // override_documentation 19 | // > ```go 20 | // > int 21 | // > ``` 22 | case bool: 23 | return !concreteValue 24 | // ^^^^^^^^^^^^^ reference local 1 25 | // override_documentation 26 | // > ```go 27 | // > bool 28 | // > ``` 29 | default: 30 | return false 31 | } 32 | } 33 | //⌃ enclosing_range_end 0.1.test `sg/testdata`/Switch(). 34 | 35 | -------------------------------------------------------------------------------- /internal/lookup/local.go: -------------------------------------------------------------------------------- 1 | package lookup 2 | 3 | import ( 4 | "go/types" 5 | "strings" 6 | ) 7 | 8 | // Local contains information about a local symbol 9 | type Local struct { 10 | Symbol string 11 | Obj types.Object 12 | } 13 | 14 | // SignatureText builds a concise signature string for this local symbol. 15 | func (l *Local) SignatureText() string { 16 | if l.Obj == nil { 17 | return "" 18 | } 19 | 20 | var parts []string 21 | 22 | switch l.Obj.(type) { 23 | case *types.Const: 24 | parts = append(parts, "const") 25 | case *types.PkgName: 26 | parts = append(parts, "import") 27 | case *types.Var: 28 | parts = append(parts, "var") 29 | } 30 | 31 | if name := l.Obj.Name(); name != "" { 32 | parts = append(parts, name) 33 | } 34 | 35 | // For PkgName, append the package path instead of type 36 | if pkgName, isPkgName := l.Obj.(*types.PkgName); isPkgName { 37 | if imported := pkgName.Imported(); imported != nil { 38 | parts = append(parts, imported.Path()) 39 | } 40 | } else { 41 | if t := l.Obj.Type(); t != nil { 42 | if ts := t.String(); ts != "" { 43 | parts = append(parts, ts) 44 | } 45 | } 46 | } 47 | 48 | return strings.Join(parts, " ") 49 | } 50 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # When updating the version of the base container, please use the 2 | # SHA256 listed under 'Index digest' on Docker Hub, 3 | # not the 'Manifest digest'. 4 | # 5 | # This ensures that when pulling the container, Docker will detect 6 | # the platform and pull the correct image (if it exists) 7 | # 8 | # Alternate way of determining the Index digest using the docker CLI. 9 | # 10 | # $ docker buildx imagetools inspect golang:1.21.5-alpine 11 | # Name: docker.io/library/golang:1.21.5-alpine 12 | # MediaType: application/vnd.docker.distribution.manifest.list.v2+json 13 | # Digest: sha256:4db4aac30880b978cae5445dd4a706215249ad4f43d28bd7cdf7906e9be8dd6b 14 | # Manifests: 15 | # 16 | # And use this digest in FROM 17 | 18 | ARG base_sha=74908ad827a5849c557eeca81d46263acf788ead606102d83466f499f83e35b1 19 | 20 | FROM golang:1.25.0-bookworm@sha256:${base_sha} AS builder 21 | 22 | COPY . /sources 23 | WORKDIR /sources 24 | RUN go build -o scip-go ./cmd/scip-go 25 | 26 | # Keep in sync with builder image 27 | FROM golang:1.25.0-bookworm@sha256:${base_sha} AS final 28 | 29 | COPY --from=builder /sources/scip-go /usr/bin/ 30 | ENV GOTOOLCHAIN=auto 31 | CMD ["scip-go"] 32 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/external_composite.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | // ^^^^^^^^ reference 0.1.test `sg/testdata`/ 3 | 4 | import "net/http" 5 | // ^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 6 | 7 | type NestedHandler struct { 8 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/testdata`/NestedHandler# 9 | // documentation 10 | // > ```go 11 | // > type NestedHandler struct 12 | // > ``` 13 | // documentation 14 | // > ```go 15 | // > struct { 16 | // > Handler 17 | // > Other int 18 | // > } 19 | // > ``` 20 | // relationship github.com/golang/go/src go1.22 `net/http`/Handler# implementation 21 | http.Handler 22 | // ^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 23 | // ^^^^^^^ definition 0.1.test `sg/testdata`/NestedHandler#Handler. 24 | // documentation 25 | // > ```go 26 | // > struct field Handler net/http.Handler 27 | // > ``` 28 | // ^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Handler# 29 | Other int 30 | // ^^^^^ definition 0.1.test `sg/testdata`/NestedHandler#Other. 31 | // documentation 32 | // > ```go 33 | // > struct field Other int 34 | // > ``` 35 | } 36 | 37 | -------------------------------------------------------------------------------- /internal/visitors/scope.go: -------------------------------------------------------------------------------- 1 | package visitors 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/sourcegraph/scip-go/internal/funk" 7 | "github.com/sourcegraph/scip-go/internal/symbols" 8 | "github.com/sourcegraph/scip/bindings/go/scip" 9 | "golang.org/x/tools/go/packages" 10 | ) 11 | 12 | type Scope struct { 13 | descriptors []*scip.Descriptor 14 | } 15 | 16 | func NewScope(pkgPath string) *Scope { 17 | return &Scope{ 18 | descriptors: []*scip.Descriptor{ 19 | { 20 | Name: pkgPath, 21 | Suffix: scip.Descriptor_Namespace, 22 | }, 23 | }, 24 | } 25 | } 26 | 27 | func (s *Scope) push(name string, suffix scip.Descriptor_Suffix) { 28 | s.descriptors = append(s.descriptors, &scip.Descriptor{Name: name, Suffix: suffix}) 29 | } 30 | 31 | func (s *Scope) pop() { 32 | s.descriptors = s.descriptors[:len(s.descriptors)-1] 33 | } 34 | 35 | func (s *Scope) makeSymbol(pkg *packages.Package, name string, suffix scip.Descriptor_Suffix) string { 36 | return symbols.FromDescriptors(pkg, append(s.descriptors, &scip.Descriptor{Name: name, Suffix: suffix})...) 37 | } 38 | 39 | func (s *Scope) String() string { 40 | return strings.Join(funk.Map(s.descriptors, func(d *scip.Descriptor) string { 41 | return d.Name 42 | }), " > ") 43 | } 44 | -------------------------------------------------------------------------------- /internal/git/infer_repo.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "strings" 7 | 8 | "github.com/sourcegraph/scip-go/internal/command" 9 | ) 10 | 11 | // InferRepo gets a human-readable repository name from the git clone enclosing 12 | // the given directory. 13 | func InferRepo(dir string) (string, error) { 14 | remoteURL, err := command.Run(dir, "git", "remote", "get-url", "origin") 15 | if err != nil { 16 | return "", err 17 | } 18 | 19 | return parseRemote(remoteURL) 20 | } 21 | 22 | // parseRemote converts a git origin url into a Sourcegraph-friendly repo name. 23 | func parseRemote(remoteURL string) (string, error) { 24 | // e.g., git@github.com:sourcegraph/scip-go.git 25 | if strings.HasPrefix(remoteURL, "git@") { 26 | if parts := strings.Split(remoteURL, ":"); len(parts) == 2 { 27 | return strings.Join([]string{ 28 | strings.TrimPrefix(parts[0], "git@"), 29 | strings.TrimSuffix(parts[1], ".git"), 30 | }, "/"), nil 31 | } 32 | } 33 | 34 | // e.g., https://github.com/sourcegraph/scip-go.git 35 | if url, err := url.Parse(remoteURL); err == nil { 36 | return url.Hostname() + strings.TrimSuffix(url.Path, ".git"), nil 37 | } 38 | 39 | return "", fmt.Errorf("unrecognized remote URL: %s", remoteURL) 40 | } 41 | -------------------------------------------------------------------------------- /dev/publish-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | { 6 | if [ -z "${NEW_VERSION:-}" ]; then 7 | echo "error: Missing value for environment variable NEW_VERSION" 8 | echo "hint: Invoke this script as NEW_VERSION=M.N.P ./dev/publish-release.sh" 9 | exit 1 10 | fi 11 | 12 | if ! grep -q "## v$NEW_VERSION" CHANGELOG.md; then 13 | echo "error: Missing CHANGELOG entry for v$NEW_VERSION" 14 | echo "note: CHANGELOG entries are required for publishing releases" 15 | exit 1 16 | fi 17 | 18 | VERSION_FILE_PATH="internal/index/version.txt" 19 | if ! grep -q "$NEW_VERSION" "$VERSION_FILE_PATH"; then 20 | echo "error: scip-go version in $VERSION_FILE_PATH doesn't match NEW_VERSION=$NEW_VERSION" 21 | exit 1 22 | fi 23 | 24 | if ! git diff --quiet; then 25 | echo "error: Found unstaged changes; aborting." 26 | exit 1 27 | fi 28 | 29 | if ! git diff --quiet --cached; then 30 | echo "error: Found staged-but-uncommitted changes; aborting." 31 | exit 1 32 | fi 33 | 34 | if ! git rev-parse --abbrev-ref HEAD | grep -q "main"; then 35 | echo "error: Releases should be published from main but HEAD is on a different branch" >&2 36 | exit 1 37 | fi 38 | } >&2 39 | 40 | TAG="v$NEW_VERSION" 41 | git tag "$TAG" 42 | git push origin "$TAG" 43 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testspecial/foo_other_test.go: -------------------------------------------------------------------------------- 1 | package testspecial_test 2 | // ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/testspecial_test`/ 3 | // documentation 4 | // > package testspecial_test 5 | 6 | import ( 7 | "testing" 8 | // ^^^^^^^ reference github.com/golang/go/src go1.22 testing/ 9 | 10 | "sg/testspecial" 11 | // ^^^^^^^^^^^^^^ reference 0.1.test `sg/testspecial`/ 12 | ) 13 | 14 | //⌄ enclosing_range_start 0.1.test `sg/testspecial_test`/TestFoo_Blackbox(). 15 | func TestFoo_Blackbox(*testing.T) { testspecial.Foo() } 16 | // ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/testspecial_test`/TestFoo_Blackbox(). 17 | // documentation 18 | // > ```go 19 | // > func TestFoo_Blackbox(*T) 20 | // > ``` 21 | // ^^^^^^^ reference github.com/golang/go/src go1.22 testing/ 22 | // ^ reference github.com/golang/go/src go1.22 testing/T# 23 | // ^^^^^^^^^^^ reference 0.1.test `sg/testspecial`/ 24 | // ^^^ reference 0.1.test `sg/testspecial`/Foo(). 25 | // ⌃ enclosing_range_end 0.1.test `sg/testspecial_test`/TestFoo_Blackbox(). 26 | 27 | -------------------------------------------------------------------------------- /internal/git/infer_repo_test.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestInferRepo(t *testing.T) { 9 | repo, err := InferRepo("") 10 | if err != nil { 11 | t.Fatalf("unexpected error inferring repo: %s", err) 12 | } 13 | 14 | if repo != "github.com/sourcegraph/scip-go" { 15 | t.Errorf("unexpected remote repo. want=%q have=%q", "github.com/sourcegraph/scip-go", repo) 16 | } 17 | } 18 | 19 | func TestParseRemote(t *testing.T) { 20 | testCases := map[string]string{ 21 | "git@github.com:sourcegraph/scip-go.git": "github.com/sourcegraph/scip-go", 22 | "https://github.com/sourcegraph/scip-go": "github.com/sourcegraph/scip-go", 23 | "ssh://git@phabricator.company.com:2222/diffusion/COMPANY/companay.git": "phabricator.company.com/diffusion/COMPANY/companay", 24 | } 25 | 26 | for input, expectedOutput := range testCases { 27 | t.Run(fmt.Sprintf("input=%q", input), func(t *testing.T) { 28 | output, err := parseRemote(input) 29 | if err != nil { 30 | t.Fatalf("unexpected error parsing remote: %s", err) 31 | } 32 | 33 | if output != expectedOutput { 34 | t.Errorf("unexpected repo name. want=%q have=%q", expectedOutput, output) 35 | } 36 | }) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/initial/toplevel_decls.go: -------------------------------------------------------------------------------- 1 | package initial 2 | // ^^^^^^^ reference 0.1.test `sg/initial`/ 3 | 4 | const MY_THING = 10 5 | // ^^^^^^^^ definition 0.1.test `sg/initial`/MY_THING. 6 | // documentation 7 | // > ```go 8 | // > const MY_THING untyped int = 10 9 | // > ``` 10 | const OTHER_THING = MY_THING 11 | // ^^^^^^^^^^^ definition 0.1.test `sg/initial`/OTHER_THING. 12 | // documentation 13 | // > ```go 14 | // > const OTHER_THING untyped int = 10 15 | // > ``` 16 | // ^^^^^^^^ reference 0.1.test `sg/initial`/MY_THING. 17 | 18 | //⌄ enclosing_range_start 0.1.test `sg/initial`/usesMyThing(). 19 | func usesMyThing() { 20 | // ^^^^^^^^^^^ definition 0.1.test `sg/initial`/usesMyThing(). 21 | // documentation 22 | // > ```go 23 | // > func usesMyThing() 24 | // > ``` 25 | _ = MY_THING 26 | // ^^^^^^^^ reference 0.1.test `sg/initial`/MY_THING. 27 | } 28 | //⌃ enclosing_range_end 0.1.test `sg/initial`/usesMyThing(). 29 | 30 | var initFunctions = map[string]int{} 31 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/initial`/initFunctions. 32 | // documentation 33 | // > ```go 34 | // > var initFunctions map[string]int 35 | // > ``` 36 | 37 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/replace-directives/replace_directives.go: -------------------------------------------------------------------------------- 1 | package replacers 2 | // ^^^^^^^^^ definition 0.1.test `sg/replace-directives`/ 3 | // documentation 4 | // > package replacers 5 | 6 | import ( 7 | "fmt" 8 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 9 | 10 | "github.com/dghubble/gologin" 11 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference github.com/sourcegraph/gologin c6f1b62954d8 `github.com/dghubble/gologin`/ 12 | ) 13 | 14 | //⌄ enclosing_range_start 0.1.test `sg/replace-directives`/Something(). 15 | func Something() { 16 | // ^^^^^^^^^ definition 0.1.test `sg/replace-directives`/Something(). 17 | // documentation 18 | // > ```go 19 | // > func Something() 20 | // > ``` 21 | fmt.Println(gologin.DefaultCookieConfig) 22 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 23 | // ^^^^^^^ reference github.com/golang/go/src go1.22 fmt/Println(). 24 | // ^^^^^^^ reference github.com/sourcegraph/gologin c6f1b62954d8 `github.com/dghubble/gologin`/ 25 | // ^^^^^^^^^^^^^^^^^^^ reference github.com/sourcegraph/gologin c6f1b62954d8 `github.com/dghubble/gologin`/DefaultCookieConfig. 26 | } 27 | //⌃ enclosing_range_end 0.1.test `sg/replace-directives`/Something(). 28 | 29 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/sharedtestmodule/cmd/gitserver/server/cleanup_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | // ^^^^^^ reference 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/ 3 | 4 | import "testing" 5 | // ^^^^^^^ reference github.com/golang/go/src go1.22 testing/ 6 | 7 | //⌄ enclosing_range_start 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/TestStuff(). 8 | func TestStuff(t *testing.T) { 9 | // ^^^^^^^^^ definition 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/TestStuff(). 10 | // documentation 11 | // > ```go 12 | // > func TestStuff(t *T) 13 | // > ``` 14 | // ^ definition local 0 15 | // ^^^^^^^ reference github.com/golang/go/src go1.22 testing/ 16 | // ^ reference github.com/golang/go/src go1.22 testing/T# 17 | wd := "hello" 18 | // ^^ definition local 1 19 | repo := "world" 20 | // ^^^^ definition local 2 21 | 22 | runCmd(t, wd, "git", "init", repo) 23 | // ^^^^^^ reference 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/runCmd(). 24 | // ^ reference local 0 25 | // ^^ reference local 1 26 | // ^^^^ reference local 2 27 | } 28 | //⌃ enclosing_range_end 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/TestStuff(). 29 | 30 | -------------------------------------------------------------------------------- /internal/output/duration.go: -------------------------------------------------------------------------------- 1 | package output 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var durations = []time.Duration{ 8 | time.Nanosecond, 9 | time.Microsecond, 10 | time.Millisecond, 11 | time.Second, 12 | time.Minute, 13 | time.Hour, 14 | } 15 | 16 | // HumanElapsed returns the time elapsed since the given start time truncated 17 | // to 100x the highest non-zero duration unit (ns, us, ms, ...). This tends to 18 | // create very short duration strings when printed (e.g. 725.8ms) without having 19 | // to fiddle too much with 20 | func HumanElapsed(start time.Time) time.Duration { 21 | return humanElapsed(time.Since(start)) 22 | } 23 | 24 | func humanElapsed(elapsed time.Duration) time.Duration { 25 | i := 0 26 | for i < len(durations) && elapsed >= durations[i] { 27 | i++ 28 | } 29 | 30 | if i >= 2 { 31 | // Truncate to the next duration unit 32 | resolution := durations[i-2] 33 | 34 | if (durations[i-1] / durations[i-2]) > 100 { 35 | // If we're going from ns -> us, us -> ms, ms -> s, 36 | // then we want to have two decimal points of precision 37 | // here. Not doing this for s -> m or m -> h is fine as 38 | // there will already be this much precision. 39 | resolution *= 10 40 | } 41 | 42 | return elapsed.Truncate(resolution) 43 | } 44 | 45 | return elapsed 46 | } 47 | -------------------------------------------------------------------------------- /internal/loader/stdlib_dynamic.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "os" 5 | "os/exec" 6 | "strings" 7 | "sync" 8 | 9 | "github.com/charmbracelet/log" 10 | ) 11 | 12 | var ( 13 | stdlibOnce sync.Once 14 | stdlibMap map[string]struct{} 15 | ) 16 | 17 | func getStdlibPackages() map[string]struct{} { 18 | stdlibOnce.Do(func() { 19 | stdlibMap = stdPackages 20 | 21 | if os.Getenv("GOPACKAGESDRIVER") != "" { 22 | log.Debug("GOPACKAGESDRIVER is set, using hardcoded stdlib list") 23 | return 24 | } 25 | 26 | cmd, err := exec.LookPath("go") 27 | if err != nil { 28 | log.Warn("go command not found, using hardcoded stdlib list") 29 | return 30 | } 31 | 32 | output, err := exec.Command(cmd, "list", "std").Output() 33 | if err != nil { 34 | log.Warn( 35 | "Failed to run 'go list std', using hardcoded stdlib list", "error", err) 36 | return 37 | } 38 | 39 | stdlibMap = make(map[string]struct{}) 40 | packages := strings.SplitSeq(strings.TrimSpace(string(output)), "\n") 41 | for pkg := range packages { 42 | if pkg == "" { 43 | continue 44 | } 45 | base := strings.Split(pkg, "/")[0] 46 | stdlibMap[base] = struct{}{} 47 | } 48 | 49 | log.Debug("Successfully loaded stdlib packages dynamically", "count", len(stdlibMap)) 50 | }) 51 | return stdlibMap 52 | } 53 | -------------------------------------------------------------------------------- /internal/output/duration_test.go: -------------------------------------------------------------------------------- 1 | package output 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestHumanElapsed(t *testing.T) { 9 | testCases := []struct { 10 | input time.Duration 11 | expected time.Duration 12 | }{ 13 | { 14 | input: time.Nanosecond * 123, 15 | expected: time.Nanosecond * 123, 16 | }, 17 | 18 | { 19 | input: time.Microsecond*5 + time.Nanosecond*123, 20 | expected: time.Microsecond*5 + time.Nanosecond*120, 21 | }, 22 | { 23 | input: time.Millisecond*5 + time.Microsecond*123 + time.Nanosecond*123, 24 | expected: time.Millisecond*5 + time.Microsecond*120, 25 | }, 26 | { 27 | input: time.Second*5 + time.Millisecond*123 + time.Microsecond*123, 28 | expected: time.Second*5 + time.Millisecond*120, 29 | }, 30 | { 31 | input: time.Minute*5 + time.Second*12 + time.Millisecond*123, 32 | expected: time.Minute*5 + time.Second*12, 33 | }, 34 | { 35 | input: time.Hour*5 + time.Minute*12 + time.Second*12, 36 | expected: time.Hour*5 + time.Minute*12, 37 | }, 38 | } 39 | 40 | for _, testCase := range testCases { 41 | if actual := humanElapsed(testCase.input); actual != testCase.expected { 42 | t.Errorf("unexpected duration for %s. want=%q have=%q", testCase.input, testCase.expected, actual) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /internal/parallel/parallel_test.go: -------------------------------------------------------------------------------- 1 | package parallel 2 | 3 | import ( 4 | "sync/atomic" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestRun(t *testing.T) { 10 | ch := make(chan func() error, 3) 11 | ch <- func() error { return nil } 12 | ch <- func() error { return nil } 13 | ch <- func() error { return nil } 14 | close(ch) 15 | 16 | wg, _, n := Run(ch) 17 | wg.Wait() 18 | 19 | if *n != 3 { 20 | t.Errorf("unexpected count. want=%d want=%d", 3, *n) 21 | } 22 | } 23 | 24 | func TestRunProgress(t *testing.T) { 25 | sync1 := make(chan struct{}) 26 | sync2 := make(chan struct{}) 27 | sync3 := make(chan struct{}) 28 | 29 | ch := make(chan func() error, 3) 30 | ch <- func() error { <-sync1; return nil } 31 | ch <- func() error { <-sync2; return nil } 32 | ch <- func() error { <-sync3; return nil } 33 | close(ch) 34 | 35 | wg, _, n := Run(ch) 36 | 37 | checkValue := func(expected uint64) { 38 | var v uint64 39 | 40 | for i := 0; i < 10; i++ { 41 | if v = atomic.LoadUint64(n); v == expected { 42 | return 43 | } 44 | 45 | <-time.After(time.Millisecond) 46 | } 47 | 48 | t.Fatalf("unexpected progress value. want=%d have=%d", expected, v) 49 | } 50 | 51 | checkValue(0) 52 | close(sync1) 53 | checkValue(1) 54 | close(sync2) 55 | checkValue(2) 56 | close(sync3) 57 | checkValue(3) 58 | wg.Wait() 59 | } 60 | -------------------------------------------------------------------------------- /internal/loader/stdlib_dynamic_test.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "sync" 5 | "testing" 6 | 7 | "golang.org/x/tools/go/packages" 8 | ) 9 | 10 | func TestStdlibDetectionNoBazel(t *testing.T) { 11 | // Add a test-only package to the hardcoded list to verify different behavior 12 | // between dynamic loading and hardcoded list 13 | stdPackages["testonly"] = struct{}{} 14 | t.Cleanup(func() { 15 | delete(stdPackages, "testonly") 16 | }) 17 | 18 | testCases := []struct { 19 | pkgPath string 20 | isStdlib bool 21 | goPackagesDriver string 22 | }{ 23 | {"fmt", true, ""}, 24 | {"net/http", true, ""}, 25 | {"encoding/json", true, ""}, 26 | {"vendor/golang.org/x/crypto/chacha20", true, ""}, 27 | {"github.com/sourcegraph/scip-go", false, ""}, 28 | {"golang.org/x/tools", false, ""}, 29 | {"vendorapi.com/foo", false, ""}, 30 | {"testonly", false, ""}, 31 | {"testonly", true, "bazel"}, 32 | } 33 | 34 | t.Run("DynamicLoading", func(t *testing.T) { 35 | for _, tc := range testCases { 36 | stdlibOnce = sync.Once{} 37 | t.Setenv("GOPACKAGESDRIVER", tc.goPackagesDriver) 38 | 39 | pkg := &packages.Package{PkgPath: tc.pkgPath} 40 | if got := IsStandardLib(pkg); got != tc.isStdlib { 41 | t.Errorf("IsStandardLib(%q) = %v, want %v", 42 | tc.pkgPath, got, tc.isStdlib) 43 | } 44 | } 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | goreleaser: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 16 | - name: Check direct push 17 | run: | 18 | VERSION_FILE_PATH="internal/index/version.txt" 19 | if ! grep -q "${TAG/v/}" "$VERSION_FILE_PATH"; then 20 | echo "error: scip-go version in $VERSION_FILE_PATH doesn't match NEW_VERSION=$NEW_VERSION" 21 | exit 1 22 | fi 23 | env: 24 | TAG: ${{ github.ref_name }} 25 | 26 | - name: Install ASDF 27 | uses: asdf-vm/actions/setup@v3 28 | 29 | - name: Install matching Go toolchain 30 | run: | 31 | asdf plugin add golang 32 | asdf install golang 33 | 34 | - uses: azure/docker-login@v1 35 | with: 36 | username: ${{ secrets.DOCKER_USERNAME }} 37 | password: ${{ secrets.DOCKER_PASSWORD }} 38 | - 39 | name: Run GoReleaser 40 | uses: goreleaser/goreleaser-action@v4 41 | with: 42 | distribution: goreleaser 43 | version: v2.0.1 44 | args: release --clean 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/typealias.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | // ^^^^^^^^ reference 0.1.test `sg/testdata`/ 3 | 4 | import ( 5 | "sg/testdata/internal/secret" 6 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference 0.1.test `sg/testdata/internal/secret`/ 7 | ) 8 | 9 | // Type aliased doc 10 | type SecretBurger = secret.Burger 11 | // ^^^^^^^^^^^^ definition 0.1.test `sg/testdata`/SecretBurger# 12 | // documentation 13 | // > ```go 14 | // > type SecretBurger = secret.Burger 15 | // > ``` 16 | // documentation 17 | // > Type aliased doc 18 | // documentation 19 | // > ```go 20 | // > struct { 21 | // > Field int 22 | // > } 23 | // > ``` 24 | // ^^^^^^ reference 0.1.test `sg/testdata/internal/secret`/ 25 | // ^^^^^^ reference 0.1.test `sg/testdata/internal/secret`/Burger# 26 | 27 | type BadBurger = struct { 28 | // ^^^^^^^^^ definition 0.1.test `sg/testdata`/BadBurger# 29 | // documentation 30 | // > ```go 31 | // > type BadBurger = struct 32 | // > ``` 33 | // documentation 34 | // > ```go 35 | // > struct { 36 | // > Field string 37 | // > } 38 | // > ``` 39 | Field string 40 | // ^^^^^ definition 0.1.test `sg/testdata`/BadBurger#Field. 41 | // documentation 42 | // > ```go 43 | // > struct field Field string 44 | // > ``` 45 | } 46 | 47 | -------------------------------------------------------------------------------- /internal/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | ) 7 | 8 | type IndexOpts struct { 9 | ModuleRoot string 10 | ModuleVersion string 11 | 12 | // Path for the current module we are indexing. Same as packages.Package.Module.Path 13 | ModulePath string 14 | 15 | IsGoPackagesDriverSet bool 16 | 17 | // Go version. Used for linking to the Go standard library 18 | GoStdlibVersion string 19 | 20 | // Whether we should emit implementations 21 | SkipImplementations bool 22 | SkipTests bool 23 | 24 | IsIndexingStdlib bool 25 | 26 | // Package patterns to index 27 | PackagePatterns []string 28 | } 29 | 30 | func New(ModuleRoot, ModuleVersion, ModulePath, GoStdlibVersion string, IsIndexingStdlib bool, SkipImplementations bool, SkipTests bool, PackagePatterns []string) IndexOpts { 31 | ModuleRoot, err := filepath.Abs(ModuleRoot) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | driver := os.Getenv("GOPACKAGESDRIVER") 37 | isGoPackagesDriverSet := driver != "" && driver != "off" 38 | 39 | return IndexOpts{ 40 | ModuleRoot: ModuleRoot, 41 | ModuleVersion: ModuleVersion, 42 | ModulePath: ModulePath, 43 | GoStdlibVersion: GoStdlibVersion, 44 | SkipImplementations: SkipImplementations, 45 | SkipTests: SkipTests, 46 | IsIndexingStdlib: IsIndexingStdlib, 47 | PackagePatterns: PackagePatterns, 48 | IsGoPackagesDriverSet: isGoPackagesDriverSet, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/implementations_embedded.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | // ^^^^^^^^ reference 0.1.test `sg/testdata`/ 3 | 4 | import "io" 5 | // ^^ reference github.com/golang/go/src go1.22 io/ 6 | 7 | type I3 interface { 8 | // ^^ definition 0.1.test `sg/testdata`/I3# 9 | // documentation 10 | // > ```go 11 | // > type I3 interface 12 | // > ``` 13 | // documentation 14 | // > ```go 15 | // > interface { 16 | // > Close() error 17 | // > } 18 | // > ``` 19 | Close() error 20 | // ^^^^^ definition 0.1.test `sg/testdata`/I3#Close. 21 | // documentation 22 | // > ```go 23 | // > func (I3).Close() error 24 | // > ``` 25 | } 26 | 27 | type TClose struct { 28 | // ^^^^^^ definition 0.1.test `sg/testdata`/TClose# 29 | // documentation 30 | // > ```go 31 | // > type TClose struct 32 | // > ``` 33 | // documentation 34 | // > ```go 35 | // > struct { 36 | // > Closer 37 | // > } 38 | // > ``` 39 | // relationship github.com/golang/go/src go1.22 io/Closer# implementation 40 | // relationship 0.1.test `sg/testdata`/I3# implementation 41 | io.Closer 42 | // ^^ reference github.com/golang/go/src go1.22 io/ 43 | // ^^^^^^ definition 0.1.test `sg/testdata`/TClose#Closer. 44 | // documentation 45 | // > ```go 46 | // > struct field Closer io.Closer 47 | // > ``` 48 | // ^^^^^^ reference github.com/golang/go/src go1.22 io/Closer# 49 | } 50 | 51 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/cmd/minimal_main/minimal_main.go: -------------------------------------------------------------------------------- 1 | package main 2 | // ^^^^ definition 0.1.test `sg/testdata/cmd/minimal_main`/ 3 | // documentation 4 | // > package main 5 | 6 | type User struct { 7 | // ^^^^ definition 0.1.test `sg/testdata/cmd/minimal_main`/User# 8 | // documentation 9 | // > ```go 10 | // > type User struct 11 | // > ``` 12 | // documentation 13 | // > ```go 14 | // > struct { 15 | // > Id string 16 | // > Name string 17 | // > } 18 | // > ``` 19 | Id, Name string 20 | // ^^ definition 0.1.test `sg/testdata/cmd/minimal_main`/User#Id. 21 | // documentation 22 | // > ```go 23 | // > struct field Id string 24 | // > ``` 25 | // ^^^^ definition 0.1.test `sg/testdata/cmd/minimal_main`/User#Name. 26 | // documentation 27 | // > ```go 28 | // > struct field Name string 29 | // > ``` 30 | } 31 | 32 | type UserResource struct{} 33 | // ^^^^^^^^^^^^ definition 0.1.test `sg/testdata/cmd/minimal_main`/UserResource# 34 | // documentation 35 | // > ```go 36 | // > type UserResource struct 37 | // > ``` 38 | // documentation 39 | // > ```go 40 | // > struct{} 41 | // > ``` 42 | 43 | //⌄ enclosing_range_start 0.1.test `sg/testdata/cmd/minimal_main`/main(). 44 | func main() {} 45 | // ^^^^ definition 0.1.test `sg/testdata/cmd/minimal_main`/main(). 46 | // documentation 47 | // > ```go 48 | // > func main() 49 | // > ``` 50 | // ⌃ enclosing_range_end 0.1.test `sg/testdata/cmd/minimal_main`/main(). 51 | 52 | -------------------------------------------------------------------------------- /AGENTS.md: -------------------------------------------------------------------------------- 1 | # scip-go Development Guide 2 | 3 | ## Commands 4 | 5 | - **Build**: `go build ./cmd/scip-go` 6 | - **Test all**: `go test ./...` 7 | - **Test single package**: `go test ./internal/index` 8 | - **Update snapshots**: `go test ./internal/index -update-snapshots` 9 | - **Install**: `go install ./cmd/scip-go` 10 | - **Version check**: `go version` (uses Go 1.24.3) 11 | 12 | ## Testing 13 | 14 | - **Snapshot testing**: Primary testing method using 15 | `internal/testdata/snapshots` directory with `internal/index/scip_test.go` as 16 | the test runner 17 | - **Test data**: Located in `internal/testdata/` with test cases as 18 | subdirectories 19 | - **Update snapshots**: Run with `-update-snapshots` flag to regenerate expected 20 | outputs 21 | - **Filter tests**: Use `-filter ` to run specific test cases 22 | 23 | ## Architecture 24 | 25 | SCIP (Source Code Intelligence Protocol) indexer for Go. Main entry point is 26 | `cmd/scip-go/main.go`. Key packages: `internal/index` (core indexing), 27 | `internal/loader` (package loading), `internal/visitors` (AST traversal), 28 | `internal/document` (SCIP document generation). 29 | 30 | ## Code Style 31 | 32 | - Package naming: lowercase, single word preferred (e.g., `index`, `loader`, 33 | `symbols`) 34 | - Imports: grouped by std library, third-party, then internal packages with 35 | blank lines 36 | - Test files: use `package_test` convention for black-box testing 37 | - Embed files: use `//go:embed` for version.txt and similar resources 38 | - Error handling: return explicit errors, no panics in library code 39 | - Logging: use `charmbracelet/log` for structured logging 40 | -------------------------------------------------------------------------------- /internal/loader/stdlib.go: -------------------------------------------------------------------------------- 1 | // THIS FILE IS GENERATED. SEE ./scripts/gen_std_lib.sh 2 | // Generated by: go version go1.24.0 darwin/arm64 3 | package loader 4 | 5 | var contained = struct{}{} 6 | 7 | // This list is calculated from "go list std". 8 | var stdPackages = map[string]struct{}{ 9 | "archive": contained, 10 | "bufio": contained, 11 | "bytes": contained, 12 | "cmp": contained, 13 | "compress": contained, 14 | "container": contained, 15 | "context": contained, 16 | "crypto": contained, 17 | "database": contained, 18 | "debug": contained, 19 | "embed": contained, 20 | "encoding": contained, 21 | "errors": contained, 22 | "expvar": contained, 23 | "flag": contained, 24 | "fmt": contained, 25 | "go": contained, 26 | "hash": contained, 27 | "html": contained, 28 | "image": contained, 29 | "index": contained, 30 | "internal": contained, 31 | "io": contained, 32 | "iter": contained, 33 | "log": contained, 34 | "maps": contained, 35 | "math": contained, 36 | "mime": contained, 37 | "net": contained, 38 | "os": contained, 39 | "path": contained, 40 | "plugin": contained, 41 | "reflect": contained, 42 | "regexp": contained, 43 | "runtime": contained, 44 | "slices": contained, 45 | "sort": contained, 46 | "strconv": contained, 47 | "strings": contained, 48 | "structs": contained, 49 | "sync": contained, 50 | "syscall": contained, 51 | "testing": contained, 52 | "text": contained, 53 | "time": contained, 54 | "unicode": contained, 55 | "unique": contained, 56 | "unsafe": contained, 57 | "vendor": contained, 58 | "weak": contained, 59 | } 60 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/embedded/internal/nested.go: -------------------------------------------------------------------------------- 1 | package nested_internal 2 | // ^^^^^^^^^^^^^^^ definition 0.1.test `sg/embedded/internal`/ 3 | // documentation 4 | // > package nested_internal 5 | 6 | import ( 7 | "fmt" 8 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 9 | "sg/embedded" 10 | // ^^^^^^^^^^^ reference 0.1.test `sg/embedded`/ 11 | ) 12 | 13 | //⌄ enclosing_range_start 0.1.test `sg/embedded/internal`/Something(). 14 | func Something(recent embedded.RecentCommittersResults) { 15 | // ^^^^^^^^^ definition 0.1.test `sg/embedded/internal`/Something(). 16 | // documentation 17 | // > ```go 18 | // > func Something(recent RecentCommittersResults) 19 | // > ``` 20 | // ^^^^^^ definition local 0 21 | // ^^^^^^^^ reference 0.1.test `sg/embedded`/ 22 | // ^^^^^^^^^^^^^^^^^^^^^^^ reference 0.1.test `sg/embedded`/RecentCommittersResults# 23 | for _, commit := range recent.Nodes { 24 | // ^^^^^^ definition local 1 25 | // ^^^^^^ reference local 0 26 | // ^^^^^ reference 0.1.test `sg/embedded`/RecentCommittersResults#Nodes. 27 | for _, author := range commit.Authors.Nodes { 28 | // ^^^^^^ definition local 2 29 | // ^^^^^^ reference local 1 30 | // ^^^^^^^ reference 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors. 31 | // ^^^^^ reference 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors.Nodes. 32 | fmt.Println(author.Name) 33 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 34 | // ^^^^^^^ reference github.com/golang/go/src go1.22 fmt/Println(). 35 | // ^^^^^^ reference local 2 36 | // ^^^^ reference 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors.Nodes.Name. 37 | } 38 | } 39 | } 40 | //⌃ enclosing_range_end 0.1.test `sg/embedded/internal`/Something(). 41 | 42 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/sharedtestmodule/cmd/gitserver/server/server_test.go: -------------------------------------------------------------------------------- 1 | package server 2 | // ^^^^^^ reference 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/ 3 | 4 | import "testing" 5 | // ^^^^^^^ reference github.com/golang/go/src go1.22 testing/ 6 | 7 | //⌄ enclosing_range_start 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/TestExecRequest(). 8 | func TestExecRequest(t *testing.T) { 9 | // ^^^^^^^^^^^^^^^ definition 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/TestExecRequest(). 10 | // documentation 11 | // > ```go 12 | // > func TestExecRequest(t *T) 13 | // > ``` 14 | // ^ definition local 0 15 | // ^^^^^^^ reference github.com/golang/go/src go1.22 testing/ 16 | // ^ reference github.com/golang/go/src go1.22 testing/T# 17 | t.Log("hello world") 18 | // ^ reference local 0 19 | // ^^^ reference github.com/golang/go/src go1.22 testing/common#Log(). 20 | } 21 | //⌃ enclosing_range_end 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/TestExecRequest(). 22 | 23 | //⌄ enclosing_range_start 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/runCmd(). 24 | func runCmd(t *testing.T, dir string, cmd string, arg ...string) {} 25 | // ^^^^^^ definition 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/runCmd(). 26 | // documentation 27 | // > ```go 28 | // > func runCmd(t *T, dir string, cmd string, arg ...string) 29 | // > ``` 30 | // ^ definition local 1 31 | // ^^^^^^^ reference github.com/golang/go/src go1.22 testing/ 32 | // ^ reference github.com/golang/go/src go1.22 testing/T# 33 | // ^^^ definition local 2 34 | // ^^^ definition local 3 35 | // ^^^ definition local 4 36 | // ⌃ enclosing_range_end 0.1.test `sg/sharedtestmodule/cmd/gitserver/server`/runCmd(). 37 | 38 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run go tests 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | tags: ["v*"] 7 | pull_request: 8 | branches: ["*"] 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Install ASDF 17 | uses: asdf-vm/actions/setup@v3 18 | 19 | - name: Install matching Go toolchain 20 | run: | 21 | asdf plugin add golang 22 | asdf install golang 23 | 24 | - name: Go version 25 | run: go version 26 | 27 | - name: Install dependencies 28 | run: go get ./... 29 | 30 | - name: Build 31 | run: go build -v ./... 32 | 33 | - name: Test 34 | run: go test -v ./... 35 | 36 | - name: Goreleaser checks 37 | uses: goreleaser/goreleaser-action@v4 38 | with: 39 | distribution: goreleaser 40 | version: latest 41 | args: release --snapshot --clean 42 | 43 | test-gotip: 44 | runs-on: ubuntu-latest 45 | steps: 46 | - uses: actions/checkout@v3 47 | 48 | - name: Install ASDF 49 | uses: asdf-vm/actions/setup@v3 50 | 51 | - name: Install matching Go toolchain 52 | run: | 53 | asdf plugin add golang 54 | asdf install golang 55 | 56 | - name: Install gotip 57 | run: | 58 | go install golang.org/dl/gotip@latest 59 | asdf reshim 60 | gotip download 61 | 62 | - name: Go version 63 | run: gotip version 64 | 65 | - name: Install dependencies 66 | run: gotip get ./... 67 | 68 | - name: Build 69 | run: gotip build -v ./... 70 | 71 | - name: Test 72 | run: gotip test -v ./... 73 | 74 | docker-build: 75 | runs-on: ubuntu-latest 76 | steps: 77 | - uses: actions/checkout@v3 78 | - name: 'Check Dockerfile is well-formed' 79 | run: ./scripts/dockerfile_checks.sh 80 | - name: 'Build Docker image' 81 | run: | 82 | docker build -t local-scip-go . 83 | docker run local-scip-go scip-go --help 84 | docker run --volume .:/sources --workdir /sources local-scip-go scip-go 85 | file index.scip 86 | 87 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/embedded/nested.go: -------------------------------------------------------------------------------- 1 | package embedded 2 | // ^^^^^^^^ definition 0.1.test `sg/embedded`/ 3 | // documentation 4 | // > package embedded 5 | 6 | import "net/http" 7 | // ^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 8 | 9 | type NestedHandler struct { 10 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/embedded`/NestedHandler# 11 | // documentation 12 | // > ```go 13 | // > type NestedHandler struct 14 | // > ``` 15 | // documentation 16 | // > ```go 17 | // > struct { 18 | // > Handler 19 | // > Other int 20 | // > } 21 | // > ``` 22 | // relationship github.com/golang/go/src go1.22 `net/http`/Handler# implementation 23 | http.Handler 24 | // ^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 25 | // ^^^^^^^ definition 0.1.test `sg/embedded`/NestedHandler#Handler. 26 | // documentation 27 | // > ```go 28 | // > struct field Handler net/http.Handler 29 | // > ``` 30 | // ^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Handler# 31 | 32 | // Wow, a great thing for integers 33 | Other int 34 | // ^^^^^ definition 0.1.test `sg/embedded`/NestedHandler#Other. 35 | // documentation 36 | // > ```go 37 | // > struct field Other int 38 | // > ``` 39 | } 40 | 41 | //⌄ enclosing_range_start 0.1.test `sg/embedded`/NestedExample(). 42 | func NestedExample(n NestedHandler) { 43 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/embedded`/NestedExample(). 44 | // documentation 45 | // > ```go 46 | // > func NestedExample(n NestedHandler) 47 | // > ``` 48 | // ^ definition local 0 49 | // ^^^^^^^^^^^^^ reference 0.1.test `sg/embedded`/NestedHandler# 50 | _ = n.Handler.ServeHTTP 51 | // ^ reference local 0 52 | // ^^^^^^^ reference 0.1.test `sg/embedded`/NestedHandler#Handler. 53 | // ^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Handler#ServeHTTP. 54 | _ = n.ServeHTTP 55 | // ^ reference local 0 56 | // ^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Handler#ServeHTTP. 57 | _ = n.Other 58 | // ^ reference local 0 59 | // ^^^^^ reference 0.1.test `sg/embedded`/NestedHandler#Other. 60 | } 61 | //⌃ enclosing_range_end 0.1.test `sg/embedded`/NestedExample(). 62 | 63 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/inlinestruct/inlinestruct.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | // ^^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/ 3 | // documentation 4 | // > package inlinestruct 5 | 6 | type FieldInterface interface { 7 | // ^^^^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/FieldInterface# 8 | // documentation 9 | // > ```go 10 | // > type FieldInterface interface 11 | // > ``` 12 | // documentation 13 | // > ```go 14 | // > interface { 15 | // > SomeMethod() string 16 | // > } 17 | // > ``` 18 | SomeMethod() string 19 | // ^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/FieldInterface#SomeMethod. 20 | // documentation 21 | // > ```go 22 | // > func (FieldInterface).SomeMethod() string 23 | // > ``` 24 | } 25 | 26 | var MyInline = struct { 27 | // ^^^^^^^^ definition 0.1.test `sg/inlinestruct`/MyInline. 28 | // documentation 29 | // > ```go 30 | // > var MyInline struct{privateField FieldInterface; PublicField FieldInterface} 31 | // > ``` 32 | privateField FieldInterface 33 | // ^^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/MyInline:privateField. 34 | // documentation 35 | // > ```go 36 | // > struct field privateField sg/inlinestruct.FieldInterface 37 | // > ``` 38 | // ^^^^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/FieldInterface# 39 | PublicField FieldInterface 40 | // ^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/MyInline:PublicField. 41 | // documentation 42 | // > ```go 43 | // > struct field PublicField sg/inlinestruct.FieldInterface 44 | // > ``` 45 | // ^^^^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/FieldInterface# 46 | }{} 47 | 48 | //⌄ enclosing_range_start 0.1.test `sg/inlinestruct`/MyFunc(). 49 | func MyFunc() { 50 | // ^^^^^^ definition 0.1.test `sg/inlinestruct`/MyFunc(). 51 | // documentation 52 | // > ```go 53 | // > func MyFunc() 54 | // > ``` 55 | _ = MyInline.privateField 56 | // ^^^^^^^^ reference 0.1.test `sg/inlinestruct`/MyInline. 57 | // ^^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/MyInline:privateField. 58 | _ = MyInline.PublicField 59 | // ^^^^^^^^ reference 0.1.test `sg/inlinestruct`/MyInline. 60 | // ^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/MyInline:PublicField. 61 | } 62 | //⌃ enclosing_range_end 0.1.test `sg/inlinestruct`/MyFunc(). 63 | 64 | -------------------------------------------------------------------------------- /scripts/sourcegraph_qa.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define default output directory 4 | output_dir="scratch" 5 | dev_flag="" 6 | 7 | # Parse command line arguments 8 | while [[ $# -gt 0 ]]; do 9 | key="$1" 10 | case $key in 11 | -o|--output) 12 | output_dir="$2" 13 | shift 2 14 | ;; 15 | --dev) 16 | dev_flag="--dev" 17 | shift 18 | ;; 19 | *) 20 | echo "Unknown option: $1" 21 | exit 1 22 | ;; 23 | esac 24 | done 25 | 26 | # 27 | pids=() 28 | 29 | # Define a function to download and run command on each repository 30 | function download_and_run_command { 31 | repo=$1 32 | if [ ! -d "$output_dir/$(basename "$repo")" ]; then 33 | git clone "https://$repo.git" "$output_dir/$(basename "$repo")" & 34 | pid=$! 35 | wait $pid 36 | else 37 | echo "already cloned $repo" 38 | fi 39 | cd "$output_dir/$(basename "$repo")" 40 | echo "running : $repo" 41 | output=$(../../scip-go $dev_flag 2>&1 &) 42 | pid=$! 43 | pids+=($pid) 44 | 45 | if [ $? -eq 0 ]; then 46 | echo "completed : $repo" 47 | else 48 | echo "failed : $repo" 49 | echo "===========================================" 50 | echo "$output" 51 | echo "===========================================" 52 | fi 53 | cd ../.. 54 | } 55 | 56 | # Create output directory if it doesn't exist 57 | if [ ! -d "$output_dir" ]; then 58 | mkdir -p "$output_dir" 59 | fi 60 | 61 | # Define an array of repositories to download 62 | repos=( 63 | "github.com/sourcegraph/scip-go" 64 | "github.com/sourcegraph-testing/etcd" 65 | "github.com/sourcegraph-testing/tidb" 66 | "github.com/sourcegraph-testing/titan" 67 | "github.com/sourcegraph-testing/zap" 68 | "github.com/sourcegraph-testing/nacelle" 69 | "github.com/sourcegraph-testing/nacelle-config" 70 | "github.com/sourcegraph-testing/nacelle-service" 71 | "github.com/sourcegraph/code-intel-extensions" 72 | ) 73 | 74 | # Download and run command on each repository in parallel 75 | for repo in "${repos[@]}"; do 76 | download_and_run_command "$repo" & 77 | done 78 | 79 | # Wait for all commands to finish 80 | while true; do 81 | echo "checking jobs..." 82 | 83 | for pid in "${pids[@]}"; do 84 | if jobs -l | grep -q $pid; then 85 | echo "stil waiting for $pid" 86 | fi 87 | done 88 | 89 | sleep 1; 90 | done 91 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/duplicate_path_id/main.go: -------------------------------------------------------------------------------- 1 | package gosrc 2 | // ^^^^^ reference 0.1.test `sg/testdata/duplicate_path_id`/ 3 | 4 | type importMeta struct{} 5 | // ^^^^^^^^^^ definition 0.1.test `sg/testdata/duplicate_path_id`/importMeta# 6 | // documentation 7 | // > ```go 8 | // > type importMeta struct 9 | // > ``` 10 | // documentation 11 | // > ```go 12 | // > struct{} 13 | // > ``` 14 | 15 | type sourceMeta struct{} 16 | // ^^^^^^^^^^ definition 0.1.test `sg/testdata/duplicate_path_id`/sourceMeta# 17 | // documentation 18 | // > ```go 19 | // > type sourceMeta struct 20 | // > ``` 21 | // documentation 22 | // > ```go 23 | // > struct{} 24 | // > ``` 25 | 26 | //⌄ enclosing_range_start 0.1.test `sg/testdata/duplicate_path_id`/fetchMeta(). 27 | func fetchMeta() (string, *importMeta, *sourceMeta) { 28 | // ^^^^^^^^^ definition 0.1.test `sg/testdata/duplicate_path_id`/fetchMeta(). 29 | // documentation 30 | // > ```go 31 | // > func fetchMeta() (string, *importMeta, *sourceMeta) 32 | // > ``` 33 | // ^^^^^^^^^^ reference 0.1.test `sg/testdata/duplicate_path_id`/importMeta# 34 | // ^^^^^^^^^^ reference 0.1.test `sg/testdata/duplicate_path_id`/sourceMeta# 35 | panic("hmm") 36 | } 37 | //⌃ enclosing_range_end 0.1.test `sg/testdata/duplicate_path_id`/fetchMeta(). 38 | 39 | //⌄ enclosing_range_start 0.1.test `sg/testdata/duplicate_path_id`/init(). 40 | func init() {} 41 | // ^^^^ definition 0.1.test `sg/testdata/duplicate_path_id`/init(). 42 | // documentation 43 | // > ```go 44 | // > func init() 45 | // > ``` 46 | // ⌃ enclosing_range_end 0.1.test `sg/testdata/duplicate_path_id`/init(). 47 | //⌄ enclosing_range_start 0.1.test `sg/testdata/duplicate_path_id`/init(). 48 | func init() {} 49 | // ^^^^ definition 0.1.test `sg/testdata/duplicate_path_id`/init(). 50 | // documentation 51 | // > ```go 52 | // > func init() 53 | // > ``` 54 | // ⌃ enclosing_range_end 0.1.test `sg/testdata/duplicate_path_id`/init(). 55 | //⌄ enclosing_range_start 0.1.test `sg/testdata/duplicate_path_id`/init(). 56 | func init() {} 57 | // ^^^^ definition 0.1.test `sg/testdata/duplicate_path_id`/init(). 58 | // documentation 59 | // > ```go 60 | // > func init() 61 | // > ``` 62 | // ⌃ enclosing_range_end 0.1.test `sg/testdata/duplicate_path_id`/init(). 63 | 64 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/conflicting_test_symbols/sandbox_unsupported_test.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !windows && !freebsd 2 | // +build !linux,!windows,!freebsd 3 | 4 | package osl 5 | // ^^^ definition 0.1.test `sg/testdata/conflicting_test_symbols`/ 6 | // documentation 7 | // > package osl 8 | 9 | import ( 10 | "errors" 11 | // ^^^^^^ reference github.com/golang/go/src go1.22 errors/ 12 | "testing" 13 | // ^^^^^^^ reference github.com/golang/go/src go1.22 testing/ 14 | ) 15 | 16 | var ErrNotImplemented = errors.New("not implemented") 17 | // ^^^^^^^^^^^^^^^^^ definition 0.1.test `sg/testdata/conflicting_test_symbols`/ErrNotImplemented. 18 | // documentation 19 | // > ```go 20 | // > var ErrNotImplemented error 21 | // > ``` 22 | // ^^^^^^ reference github.com/golang/go/src go1.22 errors/ 23 | // ^^^ reference github.com/golang/go/src go1.22 errors/New(). 24 | 25 | //⌄ enclosing_range_start 0.1.test `sg/testdata/conflicting_test_symbols`/newKey(). 26 | func newKey(t *testing.T) (string, error) { 27 | // ^^^^^^ definition 0.1.test `sg/testdata/conflicting_test_symbols`/newKey(). 28 | // documentation 29 | // > ```go 30 | // > func newKey(t *T) (string, error) 31 | // > ``` 32 | // ^ definition local 0 33 | // ^^^^^^^ reference github.com/golang/go/src go1.22 testing/ 34 | // ^ reference github.com/golang/go/src go1.22 testing/T# 35 | return "", ErrNotImplemented 36 | // ^^^^^^^^^^^^^^^^^ reference 0.1.test `sg/testdata/conflicting_test_symbols`/ErrNotImplemented. 37 | } 38 | //⌃ enclosing_range_end 0.1.test `sg/testdata/conflicting_test_symbols`/newKey(). 39 | 40 | //⌄ enclosing_range_start 0.1.test `sg/testdata/conflicting_test_symbols`/verifySandbox(). 41 | func verifySandbox(t *testing.T, s string) { 42 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/testdata/conflicting_test_symbols`/verifySandbox(). 43 | // documentation 44 | // > ```go 45 | // > func verifySandbox(t *T, s string) 46 | // > ``` 47 | // ^ definition local 1 48 | // ^^^^^^^ reference github.com/golang/go/src go1.22 testing/ 49 | // ^ reference github.com/golang/go/src go1.22 testing/T# 50 | // ^ definition local 2 51 | return 52 | } 53 | //⌃ enclosing_range_end 0.1.test `sg/testdata/conflicting_test_symbols`/verifySandbox(). 54 | 55 | -------------------------------------------------------------------------------- /internal/modules/modules.go: -------------------------------------------------------------------------------- 1 | package modules 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/charmbracelet/log" 10 | "github.com/sourcegraph/scip-go/internal/command" 11 | "github.com/sourcegraph/scip-go/internal/output" 12 | "golang.org/x/tools/go/vcs" 13 | ) 14 | 15 | func ModuleName(dir, repo, inName string) (moduleName string, isStdLib bool, err error) { 16 | resolve := func() error { 17 | if inName != "" { 18 | moduleName = inName 19 | return nil 20 | } 21 | 22 | moduleName = repo 23 | 24 | if !isModule(dir) { 25 | log.Warn("No go.mod file found in current directory.") 26 | } else { 27 | if moduleName, err = command.Run(dir, "go", "list", "-mod=readonly", "-m"); err != nil { 28 | return fmt.Errorf("failed to list modules: %v\n%s", err, moduleName) 29 | } 30 | 31 | return nil 32 | } 33 | 34 | moduleName, isStdLib, err = resolveModuleName(repo, moduleName) 35 | 36 | return nil 37 | } 38 | 39 | err = output.WithProgress("Resolving module name", resolve) 40 | if err != nil { 41 | return "", false, err 42 | } 43 | 44 | if moduleName == "std" { 45 | isStdLib = true 46 | } 47 | 48 | return moduleName, isStdLib, err 49 | } 50 | 51 | // resolveModuleName converts the given repository and import path into a canonical 52 | // representation of a module name usable for moniker identifiers. The base of the 53 | // import path will be the resolved repository remote, and the given module name 54 | // is used only to determine the path suffix. 55 | func resolveModuleName(repo, name string) (string, bool, error) { 56 | // Determine path suffix relative to repository root 57 | var suffix string 58 | 59 | if nameRepoRoot, err := vcs.RepoRootForImportPath(name, false); err == nil { 60 | suffix = strings.TrimPrefix(name, nameRepoRoot.Root) 61 | } else { 62 | // A user-visible warning will occur on this path as the declared 63 | // module will be resolved as part of gomod.ListDependencies. 64 | } 65 | 66 | // Determine the canonical code host of the current repository 67 | repoRepoRoot, err := vcs.RepoRootForImportPath(repo, false) 68 | if err != nil { 69 | help := "Make sure your git repo has a remote (git remote add origin git@github.com:owner/repo)" 70 | return "", false, fmt.Errorf("%v\n\n%s", err, help) 71 | } 72 | 73 | name = repoRepoRoot.Root + suffix 74 | return name, name == "std", nil 75 | } 76 | 77 | // isModule returns true if there is a go.mod file in the given directory. 78 | func isModule(dir string) bool { 79 | _, err := os.Stat(filepath.Join(dir, "go.mod")) 80 | return err == nil 81 | } 82 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/initial/methods_and_receivers.go: -------------------------------------------------------------------------------- 1 | package initial 2 | // ^^^^^^^ reference 0.1.test `sg/initial`/ 3 | 4 | import "fmt" 5 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 6 | 7 | type MyStruct struct{ f, y int } 8 | // ^^^^^^^^ definition 0.1.test `sg/initial`/MyStruct# 9 | // documentation 10 | // > ```go 11 | // > type MyStruct struct 12 | // > ``` 13 | // documentation 14 | // > ```go 15 | // > struct { 16 | // > f int 17 | // > y int 18 | // > } 19 | // > ``` 20 | // ^ definition 0.1.test `sg/initial`/MyStruct#f. 21 | // documentation 22 | // > ```go 23 | // > struct field f int 24 | // > ``` 25 | // ^ definition 0.1.test `sg/initial`/MyStruct#y. 26 | // documentation 27 | // > ```go 28 | // > struct field y int 29 | // > ``` 30 | 31 | //⌄ enclosing_range_start 0.1.test `sg/initial`/MyStruct#RecvFunction(). 32 | func (m MyStruct) RecvFunction(b int) int { return m.f + b } 33 | // ^ definition local 0 34 | // ^^^^^^^^ reference 0.1.test `sg/initial`/MyStruct# 35 | // ^^^^^^^^^^^^ definition 0.1.test `sg/initial`/MyStruct#RecvFunction(). 36 | // documentation 37 | // > ```go 38 | // > func (MyStruct).RecvFunction(b int) int 39 | // > ``` 40 | // ^ definition local 1 41 | // ^ reference local 0 42 | // ^ reference 0.1.test `sg/initial`/MyStruct#f. 43 | // ^ reference local 1 44 | // ⌃ enclosing_range_end 0.1.test `sg/initial`/MyStruct#RecvFunction(). 45 | 46 | //⌄ enclosing_range_start 0.1.test `sg/initial`/SomethingElse(). 47 | func SomethingElse() { 48 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/initial`/SomethingElse(). 49 | // documentation 50 | // > ```go 51 | // > func SomethingElse() 52 | // > ``` 53 | s := MyStruct{f: 0} 54 | // ^ definition local 2 55 | // ^^^^^^^^ reference 0.1.test `sg/initial`/MyStruct# 56 | // ^ reference 0.1.test `sg/initial`/MyStruct#f. 57 | fmt.Println(s) 58 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 59 | // ^^^^^^^ reference github.com/golang/go/src go1.22 fmt/Println(). 60 | // ^ reference local 2 61 | } 62 | //⌃ enclosing_range_end 0.1.test `sg/initial`/SomethingElse(). 63 | 64 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/switches/switches.go: -------------------------------------------------------------------------------- 1 | package switches 2 | // ^^^^^^^^ definition 0.1.test `sg/switches`/ 3 | // documentation 4 | // > package switches 5 | 6 | // CustomSwitch does the things in a switch 7 | type CustomSwitch struct{} 8 | // ^^^^^^^^^^^^ definition 0.1.test `sg/switches`/CustomSwitch# 9 | // documentation 10 | // > ```go 11 | // > type CustomSwitch struct 12 | // > ``` 13 | // documentation 14 | // > CustomSwitch does the things in a switch 15 | // documentation 16 | // > ```go 17 | // > struct{} 18 | // > ``` 19 | 20 | // Something does some things... and stuff 21 | //⌄ enclosing_range_start 0.1.test `sg/switches`/CustomSwitch#Something(). 22 | func (c *CustomSwitch) Something() bool { return false } 23 | // ^ definition local 0 24 | // ^^^^^^^^^^^^ reference 0.1.test `sg/switches`/CustomSwitch# 25 | // ^^^^^^^^^ definition 0.1.test `sg/switches`/CustomSwitch#Something(). 26 | // documentation 27 | // > ```go 28 | // > func (*CustomSwitch).Something() bool 29 | // > ``` 30 | // documentation 31 | // > Something does some things... and stuff 32 | // ⌃ enclosing_range_end 0.1.test `sg/switches`/CustomSwitch#Something(). 33 | 34 | //⌄ enclosing_range_start 0.1.test `sg/switches`/Switch(). 35 | func Switch(interfaceValue interface{}) bool { 36 | // ^^^^^^ definition 0.1.test `sg/switches`/Switch(). 37 | // documentation 38 | // > ```go 39 | // > func Switch(interfaceValue interface{}) bool 40 | // > ``` 41 | // ^^^^^^^^^^^^^^ definition local 1 42 | switch concreteValue := interfaceValue.(type) { 43 | // ^^^^^^^^^^^^^ definition local 2 44 | // ^^^^^^^^^^^^^^ reference local 1 45 | case int: 46 | return concreteValue*3 > 10 47 | // ^^^^^^^^^^^^^ reference local 2 48 | // override_documentation 49 | // > ```go 50 | // > int 51 | // > ``` 52 | case bool: 53 | return !concreteValue 54 | // ^^^^^^^^^^^^^ reference local 2 55 | // override_documentation 56 | // > ```go 57 | // > bool 58 | // > ``` 59 | case CustomSwitch: 60 | // ^^^^^^^^^^^^ reference 0.1.test `sg/switches`/CustomSwitch# 61 | return concreteValue.Something() 62 | // ^^^^^^^^^^^^^ reference local 2 63 | // override_documentation 64 | // > ```go 65 | // > sg/switches.CustomSwitch 66 | // > ``` 67 | // ^^^^^^^^^ reference 0.1.test `sg/switches`/CustomSwitch#Something(). 68 | default: 69 | return false 70 | } 71 | } 72 | //⌃ enclosing_range_end 0.1.test `sg/switches`/Switch(). 73 | 74 | -------------------------------------------------------------------------------- /internal/index/package_ident_test.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | "testing" 7 | 8 | "golang.org/x/tools/go/packages" 9 | ) 10 | 11 | func TestIndexer_findBestPackageDefinitionPath(t *testing.T) { 12 | type FileInfo struct { 13 | Name string 14 | Docs bool 15 | } 16 | 17 | makePackage := func(pkgName string, fileInfo []FileInfo) (*packages.Package, map[*ast.File]string) { 18 | fset := token.NewFileSet() 19 | 20 | files := map[string]*ast.File{} 21 | syntax := []*ast.File{} 22 | posMap := map[*ast.File]string{} 23 | 24 | for idx, info := range fileInfo { 25 | var doc *ast.CommentGroup 26 | if info.Docs { 27 | doc = &ast.CommentGroup{} 28 | } 29 | 30 | f := &ast.File{ 31 | Doc: doc, 32 | Package: 0, 33 | Name: &ast.Ident{ 34 | NamePos: token.Pos(idx), 35 | Name: pkgName, 36 | Obj: &ast.Object{}, 37 | }, 38 | } 39 | 40 | files[info.Name] = f 41 | syntax = append(syntax, f) 42 | posMap[f] = info.Name 43 | 44 | fset.AddFile(info.Name, fset.Base(), 1) 45 | } 46 | 47 | return &packages.Package{ 48 | ID: "test-package", 49 | Name: pkgName, 50 | PkgPath: "test-package", 51 | Imports: map[string]*packages.Package{}, 52 | Fset: fset, 53 | Syntax: syntax, 54 | }, posMap 55 | } 56 | 57 | makeTest := func(name, pkgName, expected string, fileInfo []FileInfo) { 58 | t.Run(name, func(t *testing.T) { 59 | pkg, names := makePackage(pkgName, fileInfo) 60 | 61 | pkgToken, _ := findBestPackageDefinitionPath(pkg) 62 | if name := names[pkgToken]; name != expected { 63 | t.Errorf("incorrect hover text documentation. want=%s have=%s", name, expected) 64 | } 65 | }) 66 | } 67 | 68 | makeTest("Should find exact name match", 69 | "smol", 70 | "smol.go", 71 | []FileInfo{ 72 | {"smol.go", false}, 73 | {"other.go", false}, 74 | }, 75 | ) 76 | 77 | makeTest("Should return something even if nothing matches", 78 | "smol", 79 | "random.go", 80 | []FileInfo{ 81 | {"random.go", false}, 82 | }, 83 | ) 84 | 85 | makeTest("Should not pick _test files if package is not a test package", 86 | "unreleated", 87 | "smol.go", 88 | []FileInfo{ 89 | {"smol.go", false}, 90 | {"smol_test.go", false}, 91 | }, 92 | ) 93 | 94 | makeTest("Pick whatever has documentation", 95 | "mylib", 96 | "has_docs.go", 97 | []FileInfo{ 98 | {"mylib.go", false}, 99 | {"has_docs.go", true}, 100 | }, 101 | ) 102 | 103 | makeTest("should pick a name that is a closer edit distance than one far away", 104 | "http_router", 105 | "httprouter.go", 106 | []FileInfo{ 107 | {"httprouter.go", false}, 108 | {"httpother.go", false}, 109 | }, 110 | ) 111 | 112 | makeTest("should prefer test packages over other packages if the package name has test suffix", 113 | "mylib_test", 114 | "mylib_test.go", 115 | []FileInfo{ 116 | {"mylib_test.go", false}, 117 | {"mylib.go", false}, 118 | }, 119 | ) 120 | } 121 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/inlinestruct/inlinestruct_interface.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | // ^^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/ 3 | 4 | import "context" 5 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/ 6 | 7 | //⌄ enclosing_range_start 0.1.test `sg/inlinestruct`/Target(). 8 | func Target() interface { 9 | // ^^^^^^ definition 0.1.test `sg/inlinestruct`/Target(). 10 | // documentation 11 | // > ```go 12 | // > func Target() interface{AbbreviatedOID(Context) (string, error); Commit(Context) (string, error); OID(Context) (int, error); Type(Context) (int, error)} 13 | // > ``` 14 | OID(context.Context) (int, error) 15 | // ^^^ definition 0.1.test `sg/inlinestruct`/func:Target:OID(). 16 | // documentation 17 | // > ```go 18 | // > func (interface).OID(Context) (int, error) 19 | // > ``` 20 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/ 21 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/Context# 22 | AbbreviatedOID(context.Context) (string, error) 23 | // ^^^^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/func:Target:AbbreviatedOID(). 24 | // documentation 25 | // > ```go 26 | // > func (interface).AbbreviatedOID(Context) (string, error) 27 | // > ``` 28 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/ 29 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/Context# 30 | Commit(context.Context) (string, error) 31 | // ^^^^^^ definition 0.1.test `sg/inlinestruct`/func:Target:Commit(). 32 | // documentation 33 | // > ```go 34 | // > func (interface).Commit(Context) (string, error) 35 | // > ``` 36 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/ 37 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/Context# 38 | Type(context.Context) (int, error) 39 | // ^^^^ definition 0.1.test `sg/inlinestruct`/func:Target:Type(). 40 | // documentation 41 | // > ```go 42 | // > func (interface).Type(Context) (int, error) 43 | // > ``` 44 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/ 45 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/Context# 46 | } { 47 | panic("not implemented") 48 | } 49 | //⌃ enclosing_range_end 0.1.test `sg/inlinestruct`/Target(). 50 | 51 | //⌄ enclosing_range_start 0.1.test `sg/inlinestruct`/something(). 52 | func something() { 53 | // ^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/something(). 54 | // documentation 55 | // > ```go 56 | // > func something() 57 | // > ``` 58 | x := Target() 59 | // ^ definition local 0 60 | // ^^^^^^ reference 0.1.test `sg/inlinestruct`/Target(). 61 | x.OID(context.Background()) 62 | // ^ reference local 0 63 | // ^^^ reference 0.1.test `sg/inlinestruct`/func:Target:OID(). 64 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/ 65 | // ^^^^^^^^^^ reference github.com/golang/go/src go1.22 context/Background(). 66 | } 67 | //⌃ enclosing_range_end 0.1.test `sg/inlinestruct`/something(). 68 | 69 | -------------------------------------------------------------------------------- /internal/index/package_ident.go: -------------------------------------------------------------------------------- 1 | package index 2 | 3 | import ( 4 | "go/ast" 5 | "math" 6 | "path" 7 | "strings" 8 | 9 | "github.com/agnivade/levenshtein" 10 | "golang.org/x/tools/go/packages" 11 | ) 12 | 13 | func findBestPackageDefinitionPath(pkg *packages.Package) (*ast.File, error) { 14 | if pkg.PkgPath == "builtin" { 15 | return nil, nil 16 | } 17 | 18 | // Unsafe is special case for builtin 19 | if pkg.PkgPath == "unsafe" { 20 | return nil, nil 21 | } 22 | 23 | if len(pkg.Syntax) == 0 { 24 | // This case can be triggered when a package directory only contains `_test.go` files, 25 | // as those files will be compiled as part of a separate _test package. 26 | return nil, nil 27 | } 28 | 29 | files := []*ast.File{} 30 | filesWithDocs := []*ast.File{} 31 | for _, f := range pkg.Syntax { 32 | // pos := pkg.Fset.Position(f.Pos()) 33 | 34 | files = append(files, f) 35 | if f.Doc != nil { 36 | filesWithDocs = append(filesWithDocs, f) 37 | } 38 | } 39 | 40 | // The idiomatic way is to _only_ have one .go file per package that has a docstring 41 | // for the package. This should generally return here. 42 | if len(filesWithDocs) == 1 { 43 | return filesWithDocs[0], nil 44 | } 45 | 46 | // If we for some reason have more than one .go file per package that has a docstring, 47 | // only consider returning paths that contain the docstring (instead of any of the possible 48 | // paths). 49 | if len(filesWithDocs) > 1 { 50 | files = filesWithDocs 51 | } 52 | 53 | // Try to only pick non _test files for non _test packages and vice versa. 54 | files = filterBasedOnTestFiles(pkg, files) 55 | 56 | // Find the best remaining path. 57 | // Chooses: 58 | // 1. doc.go 59 | // 2. exact match 60 | // 3. computes levenshtein and picks best score 61 | var bestFile *ast.File 62 | 63 | minDistance := math.MaxInt32 64 | for _, f := range files { 65 | fPath := pkg.Fset.Position(f.Pos()).Filename 66 | fileName := fileNameWithoutExtension(fPath) 67 | 68 | if "doc.go" == path.Base(fPath) { 69 | return f, nil 70 | } 71 | 72 | if pkg.Name == fileName { 73 | return f, nil 74 | } 75 | 76 | distance := levenshtein.ComputeDistance(pkg.Name, fileName) 77 | if distance < minDistance { 78 | minDistance = distance 79 | bestFile = f 80 | } 81 | } 82 | 83 | return bestFile, nil 84 | } 85 | 86 | func fileNameWithoutExtension(fileName string) string { 87 | return strings.TrimSuffix(fileName, path.Ext(fileName)) 88 | } 89 | 90 | func filterBasedOnTestFiles(pkg *packages.Package, files []*ast.File) []*ast.File { 91 | packageNameEndsWithTest := strings.HasSuffix(pkg.Name, "_test") 92 | 93 | preferredFiles := []*ast.File{} 94 | for _, f := range files { 95 | fPath := pkg.Fset.Position(f.Pos()) 96 | if packageNameEndsWithTest == strings.HasSuffix(fPath.Filename, "_test.go") { 97 | preferredFiles = append(preferredFiles, f) 98 | } 99 | } 100 | 101 | if len(preferredFiles) > 0 { 102 | return preferredFiles 103 | } 104 | 105 | return files 106 | } 107 | -------------------------------------------------------------------------------- /internal/visitors/visitor_func.go: -------------------------------------------------------------------------------- 1 | package visitors 2 | 3 | import ( 4 | "go/ast" 5 | 6 | "github.com/sourcegraph/scip-go/internal/document" 7 | "github.com/sourcegraph/scip/bindings/go/scip" 8 | "golang.org/x/tools/go/packages" 9 | ) 10 | 11 | var _ ast.Visitor = &funcVisitor{} 12 | 13 | type funcVisitor struct { 14 | pkg *packages.Package 15 | doc *document.Document 16 | scope *Scope 17 | } 18 | 19 | func (v funcVisitor) Visit(n ast.Node) ast.Visitor { 20 | if n == nil { 21 | return nil 22 | } 23 | 24 | switch node := n.(type) { 25 | case *ast.FuncDecl: 26 | // Receiver, if applicable 27 | if recv, has := receiverTypeName(node); has { 28 | v.scope.push(recv, scip.Descriptor_Type) 29 | } 30 | 31 | symbol := v.scope.makeSymbol(v.pkg, node.Name.Name, scip.Descriptor_Method) 32 | v.doc.SetNewSymbol(symbol, node, node.Name) 33 | 34 | // Any associated declarations should be generated with the scope of this method 35 | v.scope.push("func", scip.Descriptor_Meta) 36 | v.scope.push(node.Name.Name, scip.Descriptor_Meta) 37 | ast.Walk(v, node.Type) 38 | v.scope.pop() 39 | 40 | return nil 41 | 42 | case *ast.FuncType: 43 | // Should not need to declare any non-local definitions in the type params 44 | // if node.TypeParams != nil { 45 | // Walk(v, node.TypeParams) 46 | // } 47 | 48 | // Should not need to declare any non-local definitions in the params 49 | // if node.Params != nil { 50 | // Walk(v, node.Params) 51 | // } 52 | 53 | // Types can create new interfaces and/or types, 54 | // so we need to visit them and potentially declare new non-local symbols 55 | if node.Results != nil { 56 | ast.Walk(v, node.Results) 57 | } 58 | 59 | return nil 60 | 61 | case *ast.BlockStmt: 62 | return nil 63 | 64 | case *ast.InterfaceType: 65 | // TODO: Should handle this more elegantly? 66 | for _, field := range node.Methods.List { 67 | for _, name := range field.Names { 68 | symbol := v.scope.makeSymbol(v.pkg, name.Name, scip.Descriptor_Method) 69 | v.doc.SetNewSymbol(symbol, name, name) 70 | } 71 | } 72 | 73 | return nil 74 | 75 | default: 76 | return v 77 | } 78 | 79 | } 80 | 81 | func visitFunctionDefinition(doc *document.Document, pkg *packages.Package, node *ast.FuncDecl) { 82 | visitor := funcVisitor{ 83 | pkg: pkg, 84 | doc: doc, 85 | scope: NewScope(pkg.PkgPath), 86 | } 87 | 88 | ast.Walk(visitor, node) 89 | } 90 | 91 | func receiverTypeName(f *ast.FuncDecl) (string, bool) { 92 | recv := f.Recv 93 | if recv == nil { 94 | return "", false 95 | } 96 | 97 | if len(recv.List) > 1 { 98 | panic("I don't understand what this would look like") 99 | } else if len(recv.List) == 0 { 100 | return "", false 101 | } 102 | 103 | field := recv.List[0] 104 | if field.Type == nil { 105 | return "", false 106 | } 107 | 108 | // Dereference pointer receiver types 109 | typ := field.Type 110 | if p, _ := typ.(*ast.StarExpr); p != nil { 111 | typ = p.X 112 | } 113 | 114 | // If we have an identifier, then we have a receiver 115 | if p, _ := typ.(*ast.Ident); p != nil { 116 | return p.Name, true 117 | } 118 | 119 | return "", false 120 | } 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scip-go 2 | 3 | SCIP indexer for Golang. 4 | 5 | # Quick Start 6 | 7 | ## Installation 8 | 9 | This will build and install the latest version of `scip-go` 10 | 11 | ``` 12 | go install github.com/sourcegraph/scip-go/cmd/scip-go@latest 13 | ``` 14 | 15 | You can confirm it's been installed by running: 16 | 17 | ``` 18 | scip-go --version 19 | ``` 20 | 21 | ## Indexing a Go project 22 | 23 | ### Standard `go.mod` project 24 | 25 | From the root of your project, you can run: 26 | 27 | ``` 28 | scip-go 29 | ``` 30 | 31 | If `scip-go` is unable to determine some project information, you may need to provide some command-line arguments. 32 | 33 | 34 | ``` 35 | scip-go --module-name=NAME --module-version=VERSION 36 | ``` 37 | 38 | If this doesn't solve the problem, check the rest of the available flags in: 39 | 40 | ``` 41 | scip-go --help 42 | ``` 43 | 44 | ### Other build systems 45 | 46 | The other build systems Buck/Bazel/Please/etc are supported via [Go Packages Driver Protocol](https://pkg.go.dev/golang.org/x/tools/go/packages#hdr-The_driver_protocol). 47 | 48 | 49 | Usage: 50 | ``` 51 | GOPACKAGESDRIVER=your_driver scip-go 52 | ``` 53 | 54 | Note: Due to the current protocol design cross-repo navigation will not work. 55 | 56 | ### Common Problems: 57 | 58 | - Unable to navigate to Go standard library. 59 | - To solve this, you may want to use the `--go-version=go1.X.Y` flag when indexing and then also index the go versions manually. 60 | - To index the Go standard library, you'll want to check out https://github.com/golang/go, checkout the tag you want, navigate to the `src/` directory, and then run: 61 | - `$ scip-go --go-version=go1.X.Y` and then upload that index to your local sourcegraph instance. 62 | - After you've done this, you should be able to navigate to the standard library. 63 | 64 | 65 | (NOTE: Projects without a `go.mod` may experience challenges indexing. See next section for details) 66 | 67 | ## Indexing without shelling to `go` binary 68 | 69 | `scip-go` by default uses a few different `go` commands from the command line to 70 | gain information about the project and module. To avoid running `go` directly 71 | (perhaps you have some other build system), you will need to supply the following args. 72 | 73 | ``` 74 | scip-go --module-name="" 75 | ``` 76 | 77 | NOTE: The rest of this isn't properly implemented yet. It's on the todo list for scip-go. 78 | 79 | ## Indexing in CI 80 | 81 | ``` 82 | # Install scip-go 83 | go install github.com/sourcegraph/scip-go/cmd/scip-go@latest 84 | 85 | # Run scip-go 86 | scip-go 87 | 88 | # Upload index with any necessary tokens (shown here using GitHub workflow syntax) 89 | src code-intel upload -github-token='${{ secrets.GITHUB_TOKEN }}' -no-progress 90 | ``` 91 | 92 | ## Docker 93 | 94 | To build a self-contained Docker container with the indexer, use 95 | the following command: 96 | 97 | ```bash 98 | docker build -t scip-go:latest . 99 | ``` 100 | 101 | # Contributing 102 | 103 | Contributors should follow the [Sourcegraph Community Code of Conduct](https://handbook.sourcegraph.com/company-info-and-process/community/code_of_conduct/). 104 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/inlinestruct/inlinestruct_genericindex.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | // ^^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/ 3 | 4 | type Processor[T any] interface { 5 | // ^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/Processor# 6 | // documentation 7 | // > ```go 8 | // > type Processor interface 9 | // > ``` 10 | // documentation 11 | // > ```go 12 | // > interface { 13 | // > Process(payload T) 14 | // > ProcessorType() string 15 | // > } 16 | // > ``` 17 | // ^ definition local 0 18 | Process(payload T) 19 | // ^^^^^^^ definition 0.1.test `sg/inlinestruct`/Processor#Process. 20 | // documentation 21 | // > ```go 22 | // > func (Processor[T any]).Process(payload T) 23 | // > ``` 24 | // ^^^^^^^ definition local 1 25 | // ^ reference local 0 26 | ProcessorType() string 27 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/Processor#ProcessorType. 28 | // documentation 29 | // > ```go 30 | // > func (Processor[T any]).ProcessorType() string 31 | // > ``` 32 | } 33 | 34 | type Limit int 35 | // ^^^^^ definition 0.1.test `sg/inlinestruct`/Limit# 36 | // documentation 37 | // > ```go 38 | // > int 39 | // > ``` 40 | 41 | type ProcessImpl struct{} 42 | // ^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/ProcessImpl# 43 | // documentation 44 | // > ```go 45 | // > type ProcessImpl struct 46 | // > ``` 47 | // documentation 48 | // > ```go 49 | // > struct{} 50 | // > ``` 51 | 52 | //⌄ enclosing_range_start 0.1.test `sg/inlinestruct`/ProcessImpl#Process(). 53 | func (p *ProcessImpl) Process(payload Limit) { panic("not implemented") } 54 | // ^ definition local 2 55 | // ^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/ProcessImpl# 56 | // ^^^^^^^ definition 0.1.test `sg/inlinestruct`/ProcessImpl#Process(). 57 | // documentation 58 | // > ```go 59 | // > func (*ProcessImpl).Process(payload Limit) 60 | // > ``` 61 | // ^^^^^^^ definition local 3 62 | // ^^^^^ reference 0.1.test `sg/inlinestruct`/Limit# 63 | // ⌃ enclosing_range_end 0.1.test `sg/inlinestruct`/ProcessImpl#Process(). 64 | //⌄ enclosing_range_start 0.1.test `sg/inlinestruct`/ProcessImpl#ProcessorType(). 65 | func (p *ProcessImpl) ProcessorType() string { panic("not implemented") } 66 | // ^ definition local 4 67 | // ^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/ProcessImpl# 68 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/ProcessImpl#ProcessorType(). 69 | // documentation 70 | // > ```go 71 | // > func (*ProcessImpl).ProcessorType() string 72 | // > ``` 73 | // ⌃ enclosing_range_end 0.1.test `sg/inlinestruct`/ProcessImpl#ProcessorType(). 74 | 75 | var _ Processor[Limit] = &ProcessImpl{} 76 | // ^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/Processor# 77 | // ^^^^^ reference 0.1.test `sg/inlinestruct`/Limit# 78 | // ^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/ProcessImpl# 79 | 80 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/inlinestruct/inlinestruct_multivar.go: -------------------------------------------------------------------------------- 1 | package inlinestruct 2 | // ^^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/ 3 | 4 | type Params struct{} 5 | // ^^^^^^ definition 0.1.test `sg/inlinestruct`/Params# 6 | // documentation 7 | // > ```go 8 | // > type Params struct 9 | // > ``` 10 | // documentation 11 | // > ```go 12 | // > struct{} 13 | // > ``` 14 | type HighlightedCode struct{} 15 | // ^^^^^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/HighlightedCode# 16 | // documentation 17 | // > ```go 18 | // > type HighlightedCode struct 19 | // > ``` 20 | // documentation 21 | // > ```go 22 | // > struct{} 23 | // > ``` 24 | 25 | var Mocks, emptyMocks struct { 26 | // ^^^^^ definition 0.1.test `sg/inlinestruct`/Mocks. 27 | // documentation 28 | // > ```go 29 | // > var Mocks struct{Code func(p Params) (response *HighlightedCode, aborted bool, err error)} 30 | // > ``` 31 | // ^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/emptyMocks. 32 | // documentation 33 | // > ```go 34 | // > var emptyMocks struct{Code func(p Params) (response *HighlightedCode, aborted bool, err error)} 35 | // > ``` 36 | Code func(p Params) (response *HighlightedCode, aborted bool, err error) 37 | // ^^^^ definition 0.1.test `sg/inlinestruct`/inline-6-5:Code. 38 | // documentation 39 | // > ```go 40 | // > struct field Code func(p sg/inlinestruct.Params) (response *sg/inlinestruct.HighlightedCode, aborted bool, err error) 41 | // > ``` 42 | // ^ definition local 0 43 | // ^^^^^^ reference 0.1.test `sg/inlinestruct`/Params# 44 | // ^^^^^^^^ definition local 1 45 | // ^^^^^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/HighlightedCode# 46 | // ^^^^^^^ definition local 2 47 | // ^^^ definition local 3 48 | } 49 | 50 | var MocksSingle struct { 51 | // ^^^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/MocksSingle. 52 | // documentation 53 | // > ```go 54 | // > var MocksSingle struct{Code func(p Params) (response *HighlightedCode, aborted bool, err error)} 55 | // > ``` 56 | Code func(p Params) (response *HighlightedCode, aborted bool, err error) 57 | // ^^^^ definition 0.1.test `sg/inlinestruct`/MocksSingle:Code. 58 | // documentation 59 | // > ```go 60 | // > struct field Code func(p sg/inlinestruct.Params) (response *sg/inlinestruct.HighlightedCode, aborted bool, err error) 61 | // > ``` 62 | // ^ definition local 4 63 | // ^^^^^^ reference 0.1.test `sg/inlinestruct`/Params# 64 | // ^^^^^^^^ definition local 5 65 | // ^^^^^^^^^^^^^^^ reference 0.1.test `sg/inlinestruct`/HighlightedCode# 66 | // ^^^^^^^ definition local 6 67 | // ^^^ definition local 7 68 | } 69 | 70 | var ( 71 | okReply interface{} = "OK" 72 | // ^^^^^^^ definition 0.1.test `sg/inlinestruct`/okReply. 73 | // documentation 74 | // > ```go 75 | // > var okReply interface{} 76 | // > ``` 77 | pongReply interface{} = "PONG" 78 | // ^^^^^^^^^ definition 0.1.test `sg/inlinestruct`/pongReply. 79 | // documentation 80 | // > ```go 81 | // > var pongReply interface{} 82 | // > ``` 83 | ) 84 | 85 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/alias/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | // ^^^^ definition github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/ 3 | // documentation 4 | // > package main 5 | 6 | // Check that we don't panic 7 | // Copied from https://github.com/golang/go/issues/68877#issuecomment-2290000187 8 | type ( 9 | T struct{} 10 | // ^ definition github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/T# 11 | // documentation 12 | // > ```go 13 | // > type T struct 14 | // > ``` 15 | // documentation 16 | // > Check that we don't panic 17 | // > Copied from https://github.com/golang/go/issues/68877#issuecomment-2290000187 18 | // documentation 19 | // > ```go 20 | // > struct{} 21 | // > ``` 22 | U = T 23 | // ^ definition github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/U# 24 | // documentation 25 | // > ```go 26 | // > type U = T 27 | // > ``` 28 | // documentation 29 | // > Check that we don't panic 30 | // > Copied from https://github.com/golang/go/issues/68877#issuecomment-2290000187 31 | // documentation 32 | // > ```go 33 | // > struct{} 34 | // > ``` 35 | // ^ reference github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/T# 36 | V = U 37 | // ^ definition github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/V# 38 | // documentation 39 | // > ```go 40 | // > type V = U 41 | // > ``` 42 | // documentation 43 | // > Check that we don't panic 44 | // > Copied from https://github.com/golang/go/issues/68877#issuecomment-2290000187 45 | // documentation 46 | // > ```go 47 | // > struct{} 48 | // > ``` 49 | // ^ reference github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/U# 50 | S U 51 | // ^ definition github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/S# 52 | // documentation 53 | // > ```go 54 | // > type S struct 55 | // > ``` 56 | // documentation 57 | // > Check that we don't panic 58 | // > Copied from https://github.com/golang/go/issues/68877#issuecomment-2290000187 59 | // documentation 60 | // > ```go 61 | // > struct{} 62 | // > ``` 63 | // ^ reference github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/U# 64 | Z int32 65 | // ^ definition github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/Z# 66 | // documentation 67 | // > Check that we don't panic 68 | // > Copied from https://github.com/golang/go/issues/68877#issuecomment-2290000187 69 | // documentation 70 | // > ```go 71 | // > int32 72 | // > ``` 73 | ) 74 | 75 | //⌄ enclosing_range_start github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/f(). 76 | func f(u U) {} 77 | // ^ definition github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/f(). 78 | // documentation 79 | // > ```go 80 | // > func f(u U) 81 | // > ``` 82 | // ^ definition local 0 83 | // ^ reference github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/U# 84 | // ⌃ enclosing_range_end github.com/sourcegraph/scip-go . `github.com/sourcegraph/scip-go/internal/testdata/snapshots/input/alias`/f(). 85 | 86 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/generallyeric/new_operators.go: -------------------------------------------------------------------------------- 1 | package generallyeric 2 | // ^^^^^^^^^^^^^ reference 0.1.test `sg/generallyeric`/ 3 | 4 | import "golang.org/x/exp/constraints" 5 | // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference golang.org/x/exp 47842c84f3db `golang.org/x/exp/constraints`/ 6 | 7 | type Number interface { 8 | // ^^^^^^ definition 0.1.test `sg/generallyeric`/Number# 9 | // documentation 10 | // > ```go 11 | // > type Number interface 12 | // > ``` 13 | // documentation 14 | // > ```go 15 | // > interface { 16 | // > Float | Integer | Complex 17 | // > } 18 | // > ``` 19 | constraints.Float | constraints.Integer | constraints.Complex 20 | // ^^^^^^^^^^^ reference golang.org/x/exp 47842c84f3db `golang.org/x/exp/constraints`/ 21 | // ^^^^^ reference golang.org/x/exp 47842c84f3db `golang.org/x/exp/constraints`/Float# 22 | // ^^^^^^^^^^^ reference golang.org/x/exp 47842c84f3db `golang.org/x/exp/constraints`/ 23 | // ^^^^^^^ reference golang.org/x/exp 47842c84f3db `golang.org/x/exp/constraints`/Integer# 24 | // ^^^^^^^^^^^ reference golang.org/x/exp 47842c84f3db `golang.org/x/exp/constraints`/ 25 | // ^^^^^^^ reference golang.org/x/exp 47842c84f3db `golang.org/x/exp/constraints`/Complex# 26 | } 27 | 28 | //⌄ enclosing_range_start 0.1.test `sg/generallyeric`/Double(). 29 | func Double[T Number](value T) T { 30 | // ^^^^^^ definition 0.1.test `sg/generallyeric`/Double(). 31 | // documentation 32 | // > ```go 33 | // > func Double[T Number](value T) T 34 | // > ``` 35 | // ^ definition local 0 36 | // ^^^^^^ reference 0.1.test `sg/generallyeric`/Number# 37 | // ^^^^^ definition local 1 38 | // ^ reference local 0 39 | // ^ reference local 0 40 | return value * 2 41 | // ^^^^^ reference local 1 42 | } 43 | //⌃ enclosing_range_end 0.1.test `sg/generallyeric`/Double(). 44 | 45 | type Box[T any] struct { 46 | // ^^^ definition 0.1.test `sg/generallyeric`/Box# 47 | // documentation 48 | // > ```go 49 | // > type Box struct 50 | // > ``` 51 | // documentation 52 | // > ```go 53 | // > struct { 54 | // > Something T 55 | // > } 56 | // > ``` 57 | // ^ definition local 2 58 | Something T 59 | // ^^^^^^^^^ definition 0.1.test `sg/generallyeric`/Box#Something. 60 | // documentation 61 | // > ```go 62 | // > struct field Something T 63 | // > ``` 64 | // ^ reference local 2 65 | } 66 | 67 | type handler[T any] struct { 68 | // ^^^^^^^ definition 0.1.test `sg/generallyeric`/handler# 69 | // documentation 70 | // > ```go 71 | // > type handler struct 72 | // > ``` 73 | // documentation 74 | // > ```go 75 | // > struct { 76 | // > Box[T] 77 | // > Another string 78 | // > } 79 | // > ``` 80 | // ^ definition local 3 81 | Box[T] 82 | // ^^^ definition 0.1.test `sg/generallyeric`/handler#Box. 83 | // documentation 84 | // > ```go 85 | // > struct field Box sg/generallyeric.Box[T] 86 | // > ``` 87 | // ^^^ reference 0.1.test `sg/generallyeric`/Box# 88 | // ^ reference local 3 89 | Another string 90 | // ^^^^^^^ definition 0.1.test `sg/generallyeric`/handler#Another. 91 | // documentation 92 | // > ```go 93 | // > struct field Another string 94 | // > ``` 95 | } 96 | 97 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/generallyeric/person.go: -------------------------------------------------------------------------------- 1 | package generallyeric 2 | // ^^^^^^^^^^^^^ reference 0.1.test `sg/generallyeric`/ 3 | 4 | import "fmt" 5 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 6 | 7 | type Person interface { 8 | // ^^^^^^ definition 0.1.test `sg/generallyeric`/Person# 9 | // documentation 10 | // > ```go 11 | // > type Person interface 12 | // > ``` 13 | // documentation 14 | // > ```go 15 | // > interface { 16 | // > Work() 17 | // > } 18 | // > ``` 19 | Work() 20 | // ^^^^ definition 0.1.test `sg/generallyeric`/Person#Work. 21 | // documentation 22 | // > ```go 23 | // > func (Person).Work() 24 | // > ``` 25 | } 26 | 27 | type worker string 28 | // ^^^^^^ definition 0.1.test `sg/generallyeric`/worker# 29 | // documentation 30 | // > ```go 31 | // > string 32 | // > ``` 33 | // relationship 0.1.test `sg/generallyeric`/Person# implementation 34 | 35 | //⌄ enclosing_range_start 0.1.test `sg/generallyeric`/worker#Work(). 36 | func (w worker) Work() { 37 | // ^ definition local 0 38 | // ^^^^^^ reference 0.1.test `sg/generallyeric`/worker# 39 | // ^^^^ definition 0.1.test `sg/generallyeric`/worker#Work(). 40 | // documentation 41 | // > ```go 42 | // > func (worker).Work() 43 | // > ``` 44 | // relationship 0.1.test `sg/generallyeric`/Person#Work. implementation 45 | fmt.Printf("%s is working\n", w) 46 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 47 | // ^^^^^^ reference github.com/golang/go/src go1.22 fmt/Printf(). 48 | // ^ reference local 0 49 | } 50 | //⌃ enclosing_range_end 0.1.test `sg/generallyeric`/worker#Work(). 51 | 52 | //⌄ enclosing_range_start 0.1.test `sg/generallyeric`/DoWork(). 53 | func DoWork[T Person](things []T) { 54 | // ^^^^^^ definition 0.1.test `sg/generallyeric`/DoWork(). 55 | // documentation 56 | // > ```go 57 | // > func DoWork[T Person](things []T) 58 | // > ``` 59 | // ^ definition local 1 60 | // ^^^^^^ reference 0.1.test `sg/generallyeric`/Person# 61 | // ^^^^^^ definition local 2 62 | // ^ reference local 1 63 | for _, v := range things { 64 | // ^ definition local 3 65 | // ^^^^^^ reference local 2 66 | v.Work() 67 | // ^ reference local 3 68 | // ^^^^ reference 0.1.test `sg/generallyeric`/Person#Work. 69 | } 70 | } 71 | //⌃ enclosing_range_end 0.1.test `sg/generallyeric`/DoWork(). 72 | 73 | //⌄ enclosing_range_start 0.1.test `sg/generallyeric`/main(). 74 | func main() { 75 | // ^^^^ definition 0.1.test `sg/generallyeric`/main(). 76 | // documentation 77 | // > ```go 78 | // > func main() 79 | // > ``` 80 | var a, b, c worker 81 | // ^ definition local 4 82 | // ^ definition local 5 83 | // ^ definition local 6 84 | // ^^^^^^ reference 0.1.test `sg/generallyeric`/worker# 85 | a = "A" 86 | // ^ reference local 4 87 | b = "B" 88 | // ^ reference local 5 89 | c = "C" 90 | // ^ reference local 6 91 | DoWork([]worker{a, b, c}) 92 | // ^^^^^^ reference 0.1.test `sg/generallyeric`/DoWork(). 93 | // ^^^^^^ reference 0.1.test `sg/generallyeric`/worker# 94 | // ^ reference local 4 95 | // ^ reference local 5 96 | // ^ reference local 6 97 | } 98 | //⌃ enclosing_range_end 0.1.test `sg/generallyeric`/main(). 99 | 100 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/parallel.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | // ^^^^^^^^ reference 0.1.test `sg/testdata`/ 3 | 4 | import ( 5 | "context" 6 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/ 7 | "sync" 8 | // ^^^^ reference github.com/golang/go/src go1.22 sync/ 9 | ) 10 | 11 | // ParallelizableFunc is a function that can be called concurrently with other instances 12 | // of this function type. 13 | type ParallelizableFunc func(ctx context.Context) error 14 | // ^^^^^^^^^^^^^^^^^^ definition 0.1.test `sg/testdata`/ParallelizableFunc# 15 | // documentation 16 | // > ParallelizableFunc is a function that can be called concurrently with other instances 17 | // > of this function type. 18 | // documentation 19 | // > ```go 20 | // > func(ctx Context) error 21 | // > ``` 22 | // ^^^ definition local 0 23 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/ 24 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/Context# 25 | 26 | // Parallel invokes each of the given parallelizable functions in their own goroutines and 27 | // returns the first error to occur. This method will block until all goroutines have returned. 28 | //⌄ enclosing_range_start 0.1.test `sg/testdata`/Parallel(). 29 | func Parallel(ctx context.Context, fns ...ParallelizableFunc) error { 30 | // ^^^^^^^^ definition 0.1.test `sg/testdata`/Parallel(). 31 | // documentation 32 | // > ```go 33 | // > func Parallel(ctx Context, fns ...ParallelizableFunc) error 34 | // > ``` 35 | // documentation 36 | // > Parallel invokes each of the given parallelizable functions in their own goroutines and 37 | // > returns the first error to occur. This method will block until all goroutines have returned. 38 | // ^^^ definition local 1 39 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/ 40 | // ^^^^^^^ reference github.com/golang/go/src go1.22 context/Context# 41 | // ^^^ definition local 2 42 | // ^^^^^^^^^^^^^^^^^^ reference 0.1.test `sg/testdata`/ParallelizableFunc# 43 | var wg sync.WaitGroup 44 | // ^^ definition local 3 45 | // ^^^^ reference github.com/golang/go/src go1.22 sync/ 46 | // ^^^^^^^^^ reference github.com/golang/go/src go1.22 sync/WaitGroup# 47 | errs := make(chan error, len(fns)) 48 | // ^^^^ definition local 4 49 | // ^^^ reference local 2 50 | 51 | for _, fn := range fns { 52 | // ^^ definition local 5 53 | // ^^^ reference local 2 54 | wg.Add(1) 55 | // ^^ reference local 3 56 | // ^^^ reference github.com/golang/go/src go1.22 sync/WaitGroup#Add(). 57 | 58 | go func(fn ParallelizableFunc) { 59 | // ^^ definition local 6 60 | // ^^^^^^^^^^^^^^^^^^ reference 0.1.test `sg/testdata`/ParallelizableFunc# 61 | errs <- fn(ctx) 62 | // ^^^^ reference local 4 63 | // ^^ reference local 6 64 | // ^^^ reference local 1 65 | wg.Done() 66 | // ^^ reference local 3 67 | // ^^^^ reference github.com/golang/go/src go1.22 sync/WaitGroup#Done(). 68 | }(fn) 69 | // ^^ reference local 5 70 | } 71 | 72 | wg.Wait() 73 | // ^^ reference local 3 74 | // ^^^^ reference github.com/golang/go/src go1.22 sync/WaitGroup#Wait(). 75 | 76 | for err := range errs { 77 | // ^^^ definition local 7 78 | // ^^^^ reference local 4 79 | if err != nil { 80 | // ^^^ reference local 7 81 | return err 82 | // ^^^ reference local 7 83 | } 84 | } 85 | 86 | return nil 87 | } 88 | //⌃ enclosing_range_end 0.1.test `sg/testdata`/Parallel(). 89 | 90 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | 4 | ## v0.1.26 5 | 6 | - Add information about local symbols to generated index 7 | 8 | ## v0.1.25 9 | 10 | - Upgrades Dockerfile to use Go 1.25.0 (released Aug 12 2025) 11 | 12 | ## v0.1.24 13 | 14 | - Upgrades Dockerfile to use Go 1.24.3 (released May 6 2025). 15 | - Sets `GOTOOLCHAIN=auto` in Dockerfile by default, to allow 16 | for transparent toolchain upgrading if the network 17 | configuration allows it. 18 | 19 | ## v0.1.23 20 | 21 | - Upgrades Dockerfile to use Go 1.24.0 (released Feb 11 2025). 22 | (https://github.com/sourcegraph/scip-go/pull/146) 23 | 24 | ## v0.1.22 25 | 26 | - Fixes a panic when using a custom GOPACKAGESDRIVER along 27 | with a build system other than the default Go build system 28 | (https://github.com/sourcegraph/scip-go/pull/138) 29 | - Optionally allows passing package patterns to scip-go for 30 | only indexing a subset of packages. 31 | (https://github.com/sourcegraph/scip-go/pull/139) 32 | - Upgrades Dockerfile to use Go 1.23.3 (released Oct 1 2024). 33 | (https://github.com/sourcegraph/scip-go/pull/141) 34 | 35 | ## v0.1.21 36 | 37 | - Upgrades Dockerfile to use Go 1.23.2 (released Oct 1 2024). 38 | (https://github.com/sourcegraph/scip-go/pull/136) 39 | 40 | ## v0.1.20 41 | 42 | - Fixes a bug which caused test files using the same package name 43 | as the main package to not be indexed. 44 | (https://github.com/sourcegraph/scip-go/pull/134) 45 | 46 | ## v0.1.19 47 | 48 | - Upgrades Dockerfile to use Go 1.23.1 (released on Sept 05 2024). 49 | - Removes incorrect warning about missing ASTs for packages 50 | only containing test files. 51 | (https://github.com/sourcegraph/scip-go/pull/129) 52 | 53 | ## v0.1.18 54 | 55 | - Adds more detailed logging for diagnosing hangs 56 | during indexing. 57 | (https://github.com/sourcegraph/scip-go/pull/126) 58 | 59 | ## v0.1.17 60 | 61 | - Fixes panic due to alias types in v0.1.16. 62 | (https://github.com/sourcegraph/scip-go/pull/121) 63 | 64 | ## v0.1.16 65 | 66 | NOTE: This release panics on alias types due to 67 | upstream Go bug [68877](https://github.com/golang/go/issues/68877); 68 | you can work around that by using `GODEBUG=gotypesalias=0`. 69 | 70 | - Updates the indexer and Dockerfile for Go 1.23.0 71 | (https://github.com/sourcegraph/scip-go/pull/116) 72 | 73 | 74 | ## v0.1.15 75 | 76 | - Updates the Dockerfile to use Go 1.22.5. 77 | (https://github.com/sourcegraph/scip-go/pull/111) 78 | 79 | ## v0.1.14 80 | 81 | - Fixes a bug with cross-repo navigation when depending 82 | on unpublished versions of libraries. 83 | (https://github.com/sourcegraph/scip-go/pull/99) 84 | 85 | ## v0.1.13 86 | 87 | - Adds workaround for a panic triggerd by the presence of multiple 88 | field definitions with the same anonymous type. 89 | (https://github.com/sourcegraph/scip-go/pull/96) 90 | 91 | ```go 92 | type T struct { 93 | A, B struct { B int } 94 | } 95 | ``` 96 | 97 | ## v0.1.12 98 | 99 | - Fixes the Dockerfile for the indexer. Due to a bug in the Dockerfile, 100 | the v0.1.11 release does not have any accompanying Docker image. 101 | (https://github.com/sourcegraph/scip-go/pull/86) 102 | 103 | ## v0.1.11 104 | 105 | - Updates the indexer to build using Go 1.22.1. 106 | (https://github.com/sourcegraph/scip-go/pull/81) 107 | 108 | ## v0.1.10 109 | 110 | - Updates the indexer to build using Go 1.21.5. 111 | (https://github.com/sourcegraph/scip-go/pull/50) 112 | 113 | ## v0.1.9 114 | 115 | - Fixes a bug where the indexer would emit an empty SCIP index 116 | when hitting a panic. 117 | (https://github.com/sourcegraph/scip-go/pull/62) 118 | 119 | ## v0.1.8 120 | 121 | - Fixed the version number emitted in SCIP indexes 122 | and printed by `scip-go --version`. 123 | (https://github.com/sourcegraph/scip-go/pull/60) 124 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/embedded/something.go: -------------------------------------------------------------------------------- 1 | package embedded 2 | // ^^^^^^^^ reference 0.1.test `sg/embedded`/ 3 | 4 | import "fmt" 5 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 6 | 7 | type RecentCommittersResults struct { 8 | // ^^^^^^^^^^^^^^^^^^^^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults# 9 | // documentation 10 | // > ```go 11 | // > type RecentCommittersResults struct 12 | // > ``` 13 | // documentation 14 | // > ```go 15 | // > struct { 16 | // > Nodes []struct { 17 | // > Authors struct { 18 | // > Nodes []struct { 19 | // > Date string 20 | // > Email string 21 | // > Name string 22 | // > User struct { 23 | // > Login string 24 | // > } 25 | // > AvatarURL string 26 | // > } 27 | // > } 28 | // > } 29 | // > PageInfo struct { 30 | // > HasNextPage bool 31 | // > } 32 | // > } 33 | // > ``` 34 | Nodes []struct { 35 | // ^^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#Nodes. 36 | // documentation 37 | // > ```go 38 | // > struct field Nodes []struct{Authors struct{Nodes []struct{Date string; Email string; Name string; User struct{Login string}; AvatarURL string}}} 39 | // > ``` 40 | Authors struct { 41 | // ^^^^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors. 42 | // documentation 43 | // > ```go 44 | // > struct field Authors struct{Nodes []struct{Date string; Email string; Name string; User struct{Login string}; AvatarURL string}} 45 | // > ``` 46 | Nodes []struct { 47 | // ^^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors.Nodes. 48 | // documentation 49 | // > ```go 50 | // > struct field Nodes []struct{Date string; Email string; Name string; User struct{Login string}; AvatarURL string} 51 | // > ``` 52 | Date string 53 | // ^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors.Nodes.Date. 54 | // documentation 55 | // > ```go 56 | // > struct field Date string 57 | // > ``` 58 | Email string 59 | // ^^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors.Nodes.Email. 60 | // documentation 61 | // > ```go 62 | // > struct field Email string 63 | // > ``` 64 | Name string 65 | // ^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors.Nodes.Name. 66 | // documentation 67 | // > ```go 68 | // > struct field Name string 69 | // > ``` 70 | User struct { 71 | // ^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors.Nodes.User. 72 | // documentation 73 | // > ```go 74 | // > struct field User struct{Login string} 75 | // > ``` 76 | Login string 77 | // ^^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors.Nodes.User.Login. 78 | // documentation 79 | // > ```go 80 | // > struct field Login string 81 | // > ``` 82 | } 83 | AvatarURL string 84 | // ^^^^^^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#Nodes.Authors.Nodes.AvatarURL. 85 | // documentation 86 | // > ```go 87 | // > struct field AvatarURL string 88 | // > ``` 89 | } 90 | } 91 | } 92 | PageInfo struct { 93 | // ^^^^^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#PageInfo. 94 | // documentation 95 | // > ```go 96 | // > struct field PageInfo struct{HasNextPage bool} 97 | // > ``` 98 | HasNextPage bool 99 | // ^^^^^^^^^^^ definition 0.1.test `sg/embedded`/RecentCommittersResults#PageInfo.HasNextPage. 100 | // documentation 101 | // > ```go 102 | // > struct field HasNextPage bool 103 | // > ``` 104 | } 105 | } 106 | 107 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/impls/impls.go: -------------------------------------------------------------------------------- 1 | package impls 2 | // ^^^^^ definition 0.1.test `sg/impls`/ 3 | // documentation 4 | // > package impls 5 | 6 | type I1 interface { 7 | // ^^ definition 0.1.test `sg/impls`/I1# 8 | // documentation 9 | // > ```go 10 | // > type I1 interface 11 | // > ``` 12 | // documentation 13 | // > ```go 14 | // > interface { 15 | // > F1() 16 | // > } 17 | // > ``` 18 | F1() 19 | // ^^ definition 0.1.test `sg/impls`/I1#F1. 20 | // documentation 21 | // > ```go 22 | // > func (I1).F1() 23 | // > ``` 24 | } 25 | 26 | type I1Clone interface { 27 | // ^^^^^^^ definition 0.1.test `sg/impls`/I1Clone# 28 | // documentation 29 | // > ```go 30 | // > type I1Clone interface 31 | // > ``` 32 | // documentation 33 | // > ```go 34 | // > interface { 35 | // > F1() 36 | // > } 37 | // > ``` 38 | F1() 39 | // ^^ definition 0.1.test `sg/impls`/I1Clone#F1. 40 | // documentation 41 | // > ```go 42 | // > func (I1Clone).F1() 43 | // > ``` 44 | } 45 | 46 | type IfaceOther interface { 47 | // ^^^^^^^^^^ definition 0.1.test `sg/impls`/IfaceOther# 48 | // documentation 49 | // > ```go 50 | // > type IfaceOther interface 51 | // > ``` 52 | // documentation 53 | // > ```go 54 | // > interface { 55 | // > Another() 56 | // > Something() 57 | // > } 58 | // > ``` 59 | Something() 60 | // ^^^^^^^^^ definition 0.1.test `sg/impls`/IfaceOther#Something. 61 | // documentation 62 | // > ```go 63 | // > func (IfaceOther).Something() 64 | // > ``` 65 | Another() 66 | // ^^^^^^^ definition 0.1.test `sg/impls`/IfaceOther#Another. 67 | // documentation 68 | // > ```go 69 | // > func (IfaceOther).Another() 70 | // > ``` 71 | } 72 | 73 | type T1 int 74 | // ^^ definition 0.1.test `sg/impls`/T1# 75 | // documentation 76 | // > ```go 77 | // > int 78 | // > ``` 79 | // relationship 0.1.test `sg/impls`/I1# implementation 80 | // relationship 0.1.test `sg/impls`/I1Clone# implementation 81 | 82 | //⌄ enclosing_range_start 0.1.test `sg/impls`/T1#F1(). 83 | func (r T1) F1() {} 84 | // ^ definition local 0 85 | // ^^ reference 0.1.test `sg/impls`/T1# 86 | // ^^ definition 0.1.test `sg/impls`/T1#F1(). 87 | // documentation 88 | // > ```go 89 | // > func (T1).F1() 90 | // > ``` 91 | // relationship 0.1.test `sg/impls`/I1#F1. implementation 92 | // relationship 0.1.test `sg/impls`/I1Clone#F1. implementation 93 | // ⌃ enclosing_range_end 0.1.test `sg/impls`/T1#F1(). 94 | 95 | type T2 int 96 | // ^^ definition 0.1.test `sg/impls`/T2# 97 | // documentation 98 | // > ```go 99 | // > int 100 | // > ``` 101 | // relationship 0.1.test `sg/impls`/I1# implementation 102 | // relationship 0.1.test `sg/impls`/I1Clone# implementation 103 | 104 | //⌄ enclosing_range_start 0.1.test `sg/impls`/T2#F1(). 105 | func (r T2) F1() {} 106 | // ^ definition local 1 107 | // ^^ reference 0.1.test `sg/impls`/T2# 108 | // ^^ definition 0.1.test `sg/impls`/T2#F1(). 109 | // documentation 110 | // > ```go 111 | // > func (T2).F1() 112 | // > ``` 113 | // relationship 0.1.test `sg/impls`/I1#F1. implementation 114 | // relationship 0.1.test `sg/impls`/I1Clone#F1. implementation 115 | // ⌃ enclosing_range_end 0.1.test `sg/impls`/T2#F1(). 116 | //⌄ enclosing_range_start 0.1.test `sg/impls`/T2#F2(). 117 | func (r T2) F2() {} 118 | // ^ definition local 2 119 | // ^^ reference 0.1.test `sg/impls`/T2# 120 | // ^^ definition 0.1.test `sg/impls`/T2#F2(). 121 | // documentation 122 | // > ```go 123 | // > func (T2).F2() 124 | // > ``` 125 | // ⌃ enclosing_range_end 0.1.test `sg/impls`/T2#F2(). 126 | 127 | -------------------------------------------------------------------------------- /internal/index/scip_test.go: -------------------------------------------------------------------------------- 1 | package index_test 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "net/url" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/sourcegraph/scip-go/internal/command" 13 | "github.com/sourcegraph/scip-go/internal/config" 14 | "github.com/sourcegraph/scip-go/internal/index" 15 | "github.com/sourcegraph/scip/bindings/go/scip" 16 | "github.com/sourcegraph/scip/bindings/go/scip/testutil" 17 | "google.golang.org/protobuf/proto" 18 | ) 19 | 20 | // Use "update-snapshots" to update snapshots 21 | var filter = flag.String("filter", "", "filenames to filter by") 22 | 23 | func TestSnapshots(t *testing.T) { 24 | snapshotRoot := getTestdataRoot(t) 25 | 26 | testutil.SnapshotTest(t, 27 | snapshotRoot, 28 | func(inputDirectory, outputDirectory string, sources []*scip.SourceFile) []*scip.SourceFile { 29 | fmt.Println("Indexing", inputDirectory, "outputDirectory", outputDirectory) 30 | 31 | if *filter != "" && !strings.Contains(inputDirectory, *filter) { 32 | return []*scip.SourceFile{} 33 | } 34 | 35 | scipIndex := scip.Index{} 36 | writer := func(msg proto.Message) { 37 | switch msg := msg.(type) { 38 | case *scip.Metadata: 39 | scipIndex.Metadata = msg 40 | case *scip.Document: 41 | scipIndex.Documents = append(scipIndex.Documents, msg) 42 | case *scip.SymbolInformation: 43 | scipIndex.ExternalSymbols = append(scipIndex.ExternalSymbols, msg) 44 | } 45 | } 46 | 47 | err := index.Index(writer, config.IndexOpts{ 48 | ModuleRoot: inputDirectory, 49 | ModuleVersion: "0.1.test", 50 | ModulePath: "sg/" + filepath.Base(inputDirectory), 51 | GoStdlibVersion: "go1.22", 52 | }) 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | 57 | symbolFormatter := scip.SymbolFormatter{ 58 | OnError: func(err error) error { return err }, 59 | IncludeScheme: func(scheme string) bool { return scheme == "local" }, 60 | IncludePackageManager: func(_ string) bool { return false }, 61 | IncludePackageName: func(name string) bool { return !strings.HasPrefix(name, "sg/") }, 62 | IncludePackageVersion: func(_ string) bool { return true }, 63 | IncludeDescriptor: func(_ string) bool { return true }, 64 | IncludeRawDescriptor: func(descriptor *scip.Descriptor) bool { return true }, 65 | IncludeDisambiguator: func(_ string) bool { return true }, 66 | } 67 | 68 | sourceFiles := []*scip.SourceFile{} 69 | for _, doc := range scipIndex.Documents { 70 | // Skip files outside of current directory 71 | if strings.HasPrefix(doc.RelativePath, "..") { 72 | continue 73 | } 74 | 75 | if *filter != "" && !strings.Contains(doc.RelativePath, *filter) { 76 | continue 77 | } 78 | 79 | sourcePath, _ := url.JoinPath(scipIndex.Metadata.ProjectRoot, doc.RelativePath) 80 | sourceUrl, _ := url.Parse(sourcePath) 81 | formatted, err := testutil.FormatSnapshot(doc, "//", symbolFormatter, sourceUrl.Path) 82 | if err != nil { 83 | t.Errorf("Failed to format document: %s // %s", sourceUrl.Path, err) 84 | } 85 | 86 | sourceFiles = append(sourceFiles, scip.NewSourceFile( 87 | doc.RelativePath, 88 | doc.RelativePath, 89 | formatted, 90 | )) 91 | } 92 | 93 | return sourceFiles 94 | }, 95 | ) 96 | } 97 | 98 | // getTestdataRoot returns the absolute path to the testdata directory of this repository. 99 | func getTestdataRoot(t *testing.T) string { 100 | wd, err := os.Getwd() 101 | if err != nil { 102 | t.Fatalf("unexpected error getting working directory: %s", err) 103 | } 104 | 105 | root, err := command.Run(wd, "git", "rev-parse", "--show-toplevel") 106 | if err != nil { 107 | t.Fatalf("unexpected error getting working directory: %s", err) 108 | } 109 | 110 | testdata, err := filepath.Abs(filepath.Join(root, "internal/testdata")) 111 | if err != nil { 112 | t.Fatalf("unexpected error getting absolute directory: %s", err) 113 | } 114 | 115 | return testdata 116 | } 117 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/initial/child_symbols.go: -------------------------------------------------------------------------------- 1 | package initial 2 | 3 | // Const is a constant equal to 5. It's the best constant I've ever written. 😹 4 | const Const = 5 5 | 6 | // Docs for the const block itself. 7 | const ( 8 | // ConstBlock1 is a constant in a block. 9 | ConstBlock1 = 1 10 | 11 | // ConstBlock2 is a constant in a block. 12 | ConstBlock2 = 2 13 | ) 14 | 15 | // Var is a variable interface. 16 | var Var Interface = &Struct{Field: "bar!"} 17 | 18 | // unexportedVar is an unexported variable interface. 19 | var unexportedVar Interface = &Struct{Field: "bar!"} 20 | 21 | // x has a builtin error type 22 | var x error 23 | 24 | var BigVar Interface = &Struct{ 25 | Field: "bar!", 26 | Anonymous: struct { 27 | FieldA int 28 | FieldB int 29 | FieldC int 30 | }{FieldA: 1337}, 31 | } 32 | 33 | // What are docs, really? 34 | // I can't say for sure, I don't write any. 35 | // But look, a CAT! 36 | // 37 | // |\ _,,,---,,_ 38 | // ZZZzz /,`.-'`' -. ;-;;,_ 39 | // |,4- ) )-,_. ,\ ( `'-' 40 | // '---''(_/--' `-'\_) 41 | // 42 | // It's sleeping! Some people write that as `sleeping` but Markdown 43 | // isn't allowed in Go docstrings, right? right?! 44 | var ( 45 | // This has some docs 46 | VarBlock1 = "if you're reading this" 47 | 48 | VarBlock2 = "hi" 49 | ) 50 | 51 | // Embedded is a struct, to be embedded in another struct. 52 | type Embedded struct { 53 | // EmbeddedField has some docs! 54 | EmbeddedField string 55 | Field string // conflicts with parent "Field" 56 | } 57 | 58 | type Struct struct { 59 | *Embedded 60 | Field string 61 | Anonymous struct { 62 | FieldA int 63 | FieldB int 64 | FieldC int 65 | } 66 | } 67 | 68 | // StructMethod has some docs! 69 | func (s *Struct) StructMethod() {} 70 | 71 | func (s *Struct) ImplementsInterface() string { return "hi!" } 72 | 73 | func (s *Struct) MachineLearning( 74 | param1 float32, // It's ML, I can't describe what this param is. 75 | 76 | // We call the below hyperparameters because, uhh, well: 77 | // 78 | // ,-. _,---._ __ / \ 79 | // / ) .-' `./ / \ 80 | // ( ( ,' `/ /| 81 | // \ `-" \'\ / | 82 | // `. , \ \ / | 83 | // /`. ,'-`----Y | 84 | // ( ; | ' 85 | // | ,-. ,-' | / 86 | // | | ( | hjw | / 87 | // ) | \ `.___________|/ 88 | // `--' `--' 89 | // 90 | hyperparam2 float32, 91 | hyperparam3 float32, 92 | ) float32 { 93 | // varShouldNotHaveDocs is in a function, should not have docs emitted. 94 | var varShouldNotHaveDocs int32 95 | 96 | // constShouldNotHaveDocs is in a function, should not have docs emitted. 97 | const constShouldNotHaveDocs = 5 98 | 99 | // typeShouldNotHaveDocs is in a function, should not have docs emitted. 100 | type typeShouldNotHaveDocs struct{ a string } 101 | 102 | // funcShouldNotHaveDocs is in a function, should not have docs emitted. 103 | funcShouldNotHaveDocs := func(a string) string { return "hello" } 104 | 105 | return param1 + (hyperparam2 * *hyperparam3) // lol is this all ML is? I'm gonna be rich 106 | } 107 | 108 | // Interface has docs too 109 | type Interface interface { 110 | ImplementsInterface() string 111 | } 112 | 113 | func NewInterface() Interface { return nil } 114 | 115 | var SortExportedFirst = 1 116 | 117 | var sortUnexportedSecond = 2 118 | 119 | var _sortUnderscoreLast = 3 120 | 121 | // Yeah this is some Go magic incantation which is common. 122 | // 123 | // ,_ _ 124 | // |\\_,-~/ 125 | // / _ _ | ,--. 126 | // ( @ @ ) / ,-' 127 | // \ _T_/-._( ( 128 | // / `. \ 129 | // | _ \ | 130 | // \ \ , / | 131 | // || |-_\__ / 132 | // ((_/`(____,-' 133 | var _ = Interface(&Struct{}) 134 | 135 | type _ = struct{} 136 | 137 | // crypto/tls/common_string.go uses this pattern.. 138 | func _() { 139 | } 140 | 141 | // Go can be fun 142 | type ( 143 | // And confusing 144 | X struct { 145 | bar string 146 | } 147 | 148 | Y struct { 149 | baz float64 150 | } 151 | ) 152 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/initial/type_declarations.go: -------------------------------------------------------------------------------- 1 | package initial 2 | // ^^^^^^^ reference 0.1.test `sg/initial`/ 3 | 4 | type LiteralType int 5 | // ^^^^^^^^^^^ definition 0.1.test `sg/initial`/LiteralType# 6 | // documentation 7 | // > ```go 8 | // > int 9 | // > ``` 10 | 11 | type FuncType func(LiteralType, int) bool 12 | // ^^^^^^^^ definition 0.1.test `sg/initial`/FuncType# 13 | // documentation 14 | // > ```go 15 | // > func(LiteralType, int) bool 16 | // > ``` 17 | // ^^^^^^^^^^^ reference 0.1.test `sg/initial`/LiteralType# 18 | 19 | type IfaceType interface { 20 | // ^^^^^^^^^ definition 0.1.test `sg/initial`/IfaceType# 21 | // documentation 22 | // > ```go 23 | // > type IfaceType interface 24 | // > ``` 25 | // documentation 26 | // > ```go 27 | // > interface { 28 | // > Method() LiteralType 29 | // > } 30 | // > ``` 31 | Method() LiteralType 32 | // ^^^^^^ definition 0.1.test `sg/initial`/IfaceType#Method. 33 | // documentation 34 | // > ```go 35 | // > func (IfaceType).Method() LiteralType 36 | // > ``` 37 | // ^^^^^^^^^^^ reference 0.1.test `sg/initial`/LiteralType# 38 | } 39 | 40 | type StructType struct { 41 | // ^^^^^^^^^^ definition 0.1.test `sg/initial`/StructType# 42 | // documentation 43 | // > ```go 44 | // > type StructType struct 45 | // > ``` 46 | // documentation 47 | // > ```go 48 | // > struct { 49 | // > m IfaceType 50 | // > f LiteralType 51 | // > anon struct { 52 | // > sub int 53 | // > } 54 | // > i interface { 55 | // > AnonMethod() bool 56 | // > } 57 | // > } 58 | // > ``` 59 | m IfaceType 60 | // ^ definition 0.1.test `sg/initial`/StructType#m. 61 | // documentation 62 | // > ```go 63 | // > struct field m sg/initial.IfaceType 64 | // > ``` 65 | // ^^^^^^^^^ reference 0.1.test `sg/initial`/IfaceType# 66 | f LiteralType 67 | // ^ definition 0.1.test `sg/initial`/StructType#f. 68 | // documentation 69 | // > ```go 70 | // > struct field f sg/initial.LiteralType 71 | // > ``` 72 | // ^^^^^^^^^^^ reference 0.1.test `sg/initial`/LiteralType# 73 | 74 | // anonymous struct 75 | anon struct { 76 | // ^^^^ definition 0.1.test `sg/initial`/StructType#anon. 77 | // documentation 78 | // > ```go 79 | // > struct field anon struct{sub int} 80 | // > ``` 81 | sub int 82 | // ^^^ definition 0.1.test `sg/initial`/StructType#anon.sub. 83 | // documentation 84 | // > ```go 85 | // > struct field sub int 86 | // > ``` 87 | } 88 | 89 | // interface within struct 90 | i interface { 91 | // ^ definition 0.1.test `sg/initial`/StructType#i. 92 | // documentation 93 | // > ```go 94 | // > struct field i interface{AnonMethod() bool} 95 | // > ``` 96 | AnonMethod() bool 97 | // ^^^^^^^^^^ definition 0.1.test `sg/initial`/StructType#i.AnonMethod. 98 | // documentation 99 | // > ```go 100 | // > func (interface).AnonMethod() bool 101 | // > ``` 102 | } 103 | } 104 | 105 | type DeclaredBefore struct{ DeclaredAfter } 106 | // ^^^^^^^^^^^^^^ definition 0.1.test `sg/initial`/DeclaredBefore# 107 | // documentation 108 | // > ```go 109 | // > type DeclaredBefore struct 110 | // > ``` 111 | // documentation 112 | // > ```go 113 | // > struct { 114 | // > DeclaredAfter 115 | // > } 116 | // > ``` 117 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/initial`/DeclaredBefore#DeclaredAfter. 118 | // documentation 119 | // > ```go 120 | // > struct field DeclaredAfter sg/initial.DeclaredAfter 121 | // > ``` 122 | // ^^^^^^^^^^^^^ reference 0.1.test `sg/initial`/DeclaredAfter# 123 | type DeclaredAfter struct{} 124 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/initial`/DeclaredAfter# 125 | // documentation 126 | // > ```go 127 | // > type DeclaredAfter struct 128 | // > ``` 129 | // documentation 130 | // > ```go 131 | // > struct{} 132 | // > ``` 133 | 134 | -------------------------------------------------------------------------------- /internal/visitors/visitor_type.go: -------------------------------------------------------------------------------- 1 | package visitors 2 | 3 | import ( 4 | "go/ast" 5 | 6 | "github.com/sourcegraph/scip-go/internal/document" 7 | "github.com/sourcegraph/scip-go/internal/symbols" 8 | "github.com/sourcegraph/scip/bindings/go/scip" 9 | "golang.org/x/tools/go/packages" 10 | ) 11 | 12 | func visitTypesInFile(doc *document.Document, pkg *packages.Package, file *ast.File) { 13 | visitor := typeVisitor{ 14 | pkg: pkg, 15 | doc: doc, 16 | scope: NewScope(pkg.PkgPath), 17 | } 18 | 19 | ast.Walk(visitor, file) 20 | } 21 | 22 | // typeVisitor collects the all the information for top-level structs 23 | // that can be imported by any other file (they do not have to be exported). 24 | // 25 | // For example, a struct `myStruct` can be imported by other files in the same 26 | // packages. So we need to make those field names global (we only have global 27 | // or file-local). 28 | type typeVisitor struct { 29 | doc *document.Document 30 | pkg *packages.Package 31 | 32 | scope *Scope 33 | curDecl *ast.GenDecl 34 | isInterface bool 35 | } 36 | 37 | func (v typeVisitor) Visit(n ast.Node) (w ast.Visitor) { 38 | if n == nil { 39 | return nil 40 | } 41 | 42 | switch node := n.(type) { 43 | case *ast.GenDecl: 44 | // Current declaration is required for some documentation parsing. 45 | // So we have to keep this here with us as we traverse more deeply 46 | v.curDecl = node 47 | 48 | if node.Doc != nil { 49 | ast.Walk(v, node.Doc) 50 | } 51 | 52 | for _, s := range node.Specs { 53 | switch spec := s.(type) { 54 | case *ast.TypeSpec: 55 | switch spec.Type.(type) { 56 | case *ast.InterfaceType: 57 | v.isInterface = true 58 | default: 59 | v.isInterface = false 60 | } 61 | } 62 | 63 | ast.Walk(v, s) 64 | } 65 | 66 | return nil 67 | 68 | case 69 | // Continue down file and decls 70 | *ast.File, 71 | 72 | // Toplevel types that are important 73 | *ast.StructType, 74 | *ast.InterfaceType, 75 | 76 | // Continue traversing subtypes 77 | *ast.FieldList, 78 | *ast.Ident: 79 | 80 | return v 81 | 82 | case *ast.TypeSpec: 83 | v.scope.push(node.Name.Name, scip.Descriptor_Type) 84 | defer func() { 85 | v.scope.pop() 86 | }() 87 | 88 | v.doc.SetNewSymbol( 89 | symbols.FromDescriptors(v.pkg, v.scope.descriptors...), 90 | v.curDecl, 91 | node.Name, 92 | ) 93 | 94 | ast.Walk(v, node.Type) 95 | case *ast.Field: 96 | // I think the only case of this is embedded fields. 97 | if len(node.Names) == 0 { 98 | // If we have an interface, these do not *declare* a new symbol, 99 | // they simply add another constraint. 100 | if v.isInterface { 101 | return v 102 | } 103 | 104 | names := getIdentOfTypeExpr(v.pkg, node.Type) 105 | for _, name := range names { 106 | embeddedSymbol := v.makeSymbol(&scip.Descriptor{ 107 | Name: name.Name, 108 | Suffix: scip.Descriptor_Term, 109 | }) 110 | 111 | // In this odd scenario, the definition is at embedded field level, 112 | // not wherever the name is. So that messes up our lookup table. 113 | v.doc.SetNewSymbolForPos(embeddedSymbol, node, name, name.Pos()) 114 | } 115 | } else { 116 | for _, name := range node.Names { 117 | v.doc.SetNewSymbol(v.makeSymbol(&scip.Descriptor{ 118 | Name: name.Name, 119 | Suffix: scip.Descriptor_Term, 120 | }), nil, name) 121 | 122 | switch typ := node.Type.(type) { 123 | case *ast.MapType: 124 | v.scope.push(name.Name, scip.Descriptor_Term) 125 | defer func() { 126 | v.scope.pop() 127 | }() 128 | 129 | ast.Walk(v, typ.Key) 130 | ast.Walk(v, typ.Value) 131 | 132 | case *ast.ArrayType: 133 | v.scope.push(name.Name, scip.Descriptor_Term) 134 | defer func() { 135 | v.scope.pop() 136 | }() 137 | 138 | ast.Walk(v, typ.Elt) 139 | 140 | case *ast.StructType, *ast.InterfaceType: 141 | // Current scope is now embedded in the anonymous struct 142 | // So we walk the rest of the type expression and save 143 | // the nested names 144 | v.scope.push(name.Name, scip.Descriptor_Term) 145 | defer func() { 146 | v.scope.pop() 147 | }() 148 | 149 | ast.Walk(v, node.Type) 150 | } 151 | } 152 | } 153 | } 154 | 155 | return nil 156 | } 157 | 158 | // Implements ast.Visitor 159 | var _ ast.Visitor = &typeVisitor{} 160 | 161 | func (s *typeVisitor) makeSymbol(descriptor *scip.Descriptor) string { 162 | return symbols.FromDescriptors(s.pkg, append(s.scope.descriptors, descriptor)...) 163 | } 164 | -------------------------------------------------------------------------------- /internal/visitors/visitors.go: -------------------------------------------------------------------------------- 1 | package visitors 2 | 3 | import ( 4 | "go/ast" 5 | "go/token" 6 | "go/types" 7 | "path/filepath" 8 | "strings" 9 | 10 | "github.com/sourcegraph/scip-go/internal/document" 11 | "github.com/sourcegraph/scip-go/internal/handler" 12 | "github.com/sourcegraph/scip-go/internal/lookup" 13 | "github.com/sourcegraph/scip/bindings/go/scip" 14 | "golang.org/x/tools/go/packages" 15 | ) 16 | 17 | func VisitPackageSyntax( 18 | moduleRoot string, 19 | pkg *packages.Package, 20 | pathToDocuments map[string]*document.Document, 21 | globalSymbols *lookup.Global, 22 | ) { 23 | pkgSymbols := lookup.NewPackageSymbols(pkg) 24 | // Iterate over all the files, collect any global symbols 25 | for _, f := range pkg.Syntax { 26 | 27 | abs := pkg.Fset.File(f.Package).Name() 28 | relative, _ := filepath.Rel(moduleRoot, abs) 29 | 30 | doc := visitSyntax(pkg, pkgSymbols, f, relative) 31 | 32 | // Save document for pass 2 33 | pathToDocuments[abs] = doc 34 | } 35 | 36 | globalSymbols.Add(pkgSymbols) 37 | } 38 | 39 | func visitSyntax(pkg *packages.Package, pkgSymbols *lookup.Package, f *ast.File, relative string) *document.Document { 40 | doc := document.NewDocument(relative, pkg, pkgSymbols) 41 | 42 | // TODO: Maybe we should do this before? we have traverse all 43 | // the fields first before, but now I think it's fine right here 44 | // .... maybe 45 | visitTypesInFile(doc, pkg, f) 46 | 47 | for _, decl := range f.Decls { 48 | switch decl := decl.(type) { 49 | case *ast.BadDecl: 50 | continue 51 | 52 | case *ast.GenDecl: 53 | switch decl.Tok { 54 | case token.IMPORT: 55 | // These do not create global symbols 56 | continue 57 | 58 | case token.TYPE: 59 | // We do this via visitTypesInFile above 60 | 61 | case token.VAR, token.CONST: 62 | // visit var 63 | visitVarDefinition(doc, pkg, decl) 64 | 65 | default: 66 | panic("Unhandled general declaration") 67 | } 68 | 69 | case *ast.FuncDecl: 70 | visitFunctionDefinition(doc, pkg, decl) 71 | } 72 | 73 | } 74 | 75 | return doc 76 | } 77 | 78 | func walkExprList(v ast.Visitor, list []ast.Expr) { 79 | for _, x := range list { 80 | ast.Walk(v, x) 81 | } 82 | } 83 | 84 | func walkDeclList(v ast.Visitor, list []ast.Decl) { 85 | for _, x := range list { 86 | ast.Walk(v, x) 87 | } 88 | } 89 | 90 | func descriptorTerm(name string) *scip.Descriptor { 91 | return &scip.Descriptor{ 92 | Name: name, 93 | Suffix: scip.Descriptor_Term, 94 | } 95 | } 96 | 97 | func scipRange(start, end token.Position, obj types.Object) []int32 { 98 | var adjustment int32 = 0 99 | if pkgName, ok := obj.(*types.PkgName); ok && strings.HasPrefix(pkgName.Name(), `"`) { 100 | adjustment = 1 101 | } 102 | 103 | startLine := int32(start.Line - 1) 104 | startColumn := int32(start.Column - 1) 105 | endLine := int32(end.Line - 1) 106 | endColumn := int32(end.Column - 1) 107 | if startLine != endLine { 108 | return []int32{startLine, startColumn + adjustment, endLine, endColumn - adjustment} 109 | } 110 | return []int32{startLine, startColumn + adjustment, endColumn - adjustment} 111 | } 112 | 113 | func getIdentOfTypeExpr(pkg *packages.Package, ty ast.Expr) []*ast.Ident { 114 | switch ty := ty.(type) { 115 | case *ast.Ident: 116 | return []*ast.Ident{ty} 117 | case *ast.SelectorExpr: 118 | return []*ast.Ident{ty.Sel} 119 | case *ast.StarExpr: 120 | return getIdentOfTypeExpr(pkg, ty.X) 121 | case *ast.IndexExpr: 122 | return getIdentOfTypeExpr(pkg, ty.X) 123 | case *ast.BinaryExpr: 124 | // As far as I can tell, binary exprs are ONLY for type constraints 125 | // and those don't really define anything on the struct. 126 | // 127 | // So far now, we'll just not return anything. 128 | // 129 | // return append(s.getIdentOfTypeExpr(ty.X), s.getIdentOfTypeExpr(ty.Y)...) 130 | return []*ast.Ident{} 131 | case *ast.UnaryExpr: 132 | return getIdentOfTypeExpr(pkg, ty.X) 133 | 134 | // TODO: This one does seem like something we should handle 135 | case *ast.IndexListExpr: 136 | return nil 137 | 138 | // TODO: Should see if any of these need better ident finders 139 | case *ast.InterfaceType: 140 | return nil 141 | case *ast.FuncType: 142 | return nil 143 | case *ast.FuncLit: 144 | return nil 145 | case *ast.MapType: 146 | return nil 147 | case *ast.ArrayType: 148 | return nil 149 | case *ast.ChanType: 150 | return nil 151 | 152 | default: 153 | _ = handler.ErrOrPanic("Unhandled named struct field: %T %+v\n%s", ty, ty, pkg.Fset.Position(ty.Pos())) 154 | return nil 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /internal/output/output.go: -------------------------------------------------------------------------------- 1 | package output 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | 9 | "github.com/charmbracelet/log" 10 | "github.com/efritz/pentimento" 11 | "github.com/sourcegraph/scip-go/internal/parallel" 12 | ) 13 | 14 | type Verbosity int 15 | 16 | const ( 17 | NoOutput Verbosity = iota 18 | DefaultOutput 19 | VerboseOutput 20 | VeryVerboseOutput 21 | VeryVeryVerboseOutput 22 | ) 23 | 24 | type Options struct { 25 | Verbosity Verbosity 26 | ShowAnimations bool 27 | } 28 | 29 | var opts Options = Options{ 30 | Verbosity: DefaultOutput, 31 | ShowAnimations: false, 32 | } 33 | 34 | // updateInterval is the duration between updates in withProgress. 35 | var updateInterval = time.Second / 4 36 | 37 | // ticker is the animated throbber used in printProgress. 38 | var ticker = pentimento.NewAnimatedString([]string{ 39 | "⠸", "⠼", 40 | "⠴", "⠦", 41 | "⠧", "⠇", 42 | "⠏", "⠋", 43 | "⠙", "⠹", 44 | }, updateInterval) 45 | 46 | var successPrefix = "✔" 47 | 48 | // WithProgress prints a spinner while the given function is active. 49 | func WithProgress(name string, fn func() error) error { 50 | ch := make(chan func() error, 1) 51 | ch <- fn 52 | close(ch) 53 | 54 | wg, errCh, count := parallel.Run(ch) 55 | WithProgressParallel(wg, name, count, 1) 56 | 57 | // Handle any associated errors 58 | select { 59 | case err := <-errCh: 60 | return err 61 | default: 62 | return nil 63 | } 64 | } 65 | 66 | // WithProgressParallel will continuously print progress to stdout until the given wait group 67 | // counter goes to zero. Progress is determined by the values of `c` (number of tasks completed) 68 | // and the value `n` (total number of tasks). 69 | func WithProgressParallel(wg *sync.WaitGroup, name string, c *uint64, n uint64) { 70 | sync := make(chan struct{}) 71 | go func() { 72 | wg.Wait() 73 | close(sync) 74 | }() 75 | 76 | withTitle(name, func(printer *pentimento.Printer) { 77 | for { 78 | select { 79 | case <-sync: 80 | return 81 | case <-time.After(updateInterval): 82 | } 83 | 84 | printProgress(printer, name, c, n) 85 | } 86 | }) 87 | } 88 | 89 | // withTitle invokes withTitleAnimated withTitleStatic depending on the value of animated. 90 | func withTitle(name string, fn func(printer *pentimento.Printer)) { 91 | if opts.Verbosity == NoOutput { 92 | fn(nil) 93 | } else if !opts.ShowAnimations || opts.Verbosity >= VeryVerboseOutput { 94 | withTitleStatic(name, opts.Verbosity, fn) 95 | } else { 96 | withTitleAnimated(name, opts.Verbosity, fn) 97 | } 98 | } 99 | 100 | // withTitleStatic invokes the given function with non-animated output. 101 | func withTitleStatic(name string, verbosity Verbosity, fn func(printer *pentimento.Printer)) { 102 | start := time.Now() 103 | fmt.Printf("%s\n", name) 104 | fn(nil) 105 | 106 | if verbosity > DefaultOutput { 107 | fmt.Printf("Finished in %s.\n\n", HumanElapsed(start)) 108 | } 109 | } 110 | 111 | // withTitleStatic invokes the given function with animated output. 112 | func withTitleAnimated(name string, verbosity Verbosity, fn func(printer *pentimento.Printer)) { 113 | start := time.Now() 114 | fmt.Printf("%s %s... ", ticker, name) 115 | 116 | _ = pentimento.PrintProgress(func(printer *pentimento.Printer) error { 117 | defer func() { 118 | _ = printer.Reset() 119 | }() 120 | 121 | fn(printer) 122 | return nil 123 | }) 124 | 125 | fmt.Printf("%s %s... Done (%s)\n", successPrefix, name, HumanElapsed(start)) 126 | } 127 | 128 | // printProgress outputs a throbber, the given name, and the given number of tasks completed to 129 | // the given printer. 130 | func printProgress(printer *pentimento.Printer, name string, c *uint64, n uint64) { 131 | if printer == nil { 132 | return 133 | } 134 | 135 | content := pentimento.NewContent() 136 | 137 | if c == nil { 138 | content.AddLine("%s %s...", ticker, name) 139 | } else { 140 | content.AddLine("%s %s... %d/%d\n", ticker, name, atomic.LoadUint64(c), n) 141 | } 142 | 143 | printer.WriteContent(content) 144 | } 145 | 146 | func SetOutputOptions(verb Verbosity, animation bool) { 147 | switch verb { 148 | case NoOutput: 149 | log.SetLevel(log.FatalLevel) 150 | case DefaultOutput: 151 | log.SetLevel(log.WarnLevel) 152 | case VerboseOutput: 153 | log.SetLevel(log.InfoLevel) 154 | case VeryVerboseOutput, VeryVeryVerboseOutput: 155 | log.SetLevel(log.DebugLevel) 156 | } 157 | opts.Verbosity = verb 158 | opts.ShowAnimations = animation 159 | } 160 | 161 | func Logf(format string, a ...any) { 162 | if opts.Verbosity >= VeryVeryVerboseOutput { 163 | log.Printf(format, a...) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/impls/remote_impls.go: -------------------------------------------------------------------------------- 1 | package impls 2 | // ^^^^^ reference 0.1.test `sg/impls`/ 3 | 4 | import "net/http" 5 | // ^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 6 | 7 | //⌄ enclosing_range_start 0.1.test `sg/impls`/Something(). 8 | func Something(r http.ResponseWriter) {} 9 | // ^^^^^^^^^ definition 0.1.test `sg/impls`/Something(). 10 | // documentation 11 | // > ```go 12 | // > func Something(r ResponseWriter) 13 | // > ``` 14 | // ^ definition local 0 15 | // ^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 16 | // ^^^^^^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ResponseWriter# 17 | // ⌃ enclosing_range_end 0.1.test `sg/impls`/Something(). 18 | 19 | type MyWriter struct{} 20 | // ^^^^^^^^ definition 0.1.test `sg/impls`/MyWriter# 21 | // documentation 22 | // > ```go 23 | // > type MyWriter struct 24 | // > ``` 25 | // documentation 26 | // > ```go 27 | // > struct{} 28 | // > ``` 29 | // relationship github.com/golang/go/src go1.22 `crypto/tls`/transcriptHash# implementation 30 | // relationship github.com/golang/go/src go1.22 `internal/bisect`/Writer# implementation 31 | // relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter# implementation 32 | // relationship github.com/golang/go/src go1.22 io/Writer# implementation 33 | 34 | //⌄ enclosing_range_start 0.1.test `sg/impls`/MyWriter#Header(). 35 | func (w MyWriter) Header() http.Header { panic("") } 36 | // ^ definition local 1 37 | // ^^^^^^^^ reference 0.1.test `sg/impls`/MyWriter# 38 | // ^^^^^^ definition 0.1.test `sg/impls`/MyWriter#Header(). 39 | // documentation 40 | // > ```go 41 | // > func (MyWriter).Header() Header 42 | // > ``` 43 | // relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Header. implementation 44 | // ^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 45 | // ^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Header# 46 | // ⌃ enclosing_range_end 0.1.test `sg/impls`/MyWriter#Header(). 47 | //⌄ enclosing_range_start 0.1.test `sg/impls`/MyWriter#Write(). 48 | func (w MyWriter) Write([]byte) (int, error) { panic("") } 49 | // ^ definition local 2 50 | // ^^^^^^^^ reference 0.1.test `sg/impls`/MyWriter# 51 | // ^^^^^ definition 0.1.test `sg/impls`/MyWriter#Write(). 52 | // documentation 53 | // > ```go 54 | // > func (MyWriter).Write([]byte) (int, error) 55 | // > ``` 56 | // relationship github.com/golang/go/src go1.22 `crypto/tls`/transcriptHash#Write. implementation 57 | // relationship github.com/golang/go/src go1.22 `internal/bisect`/Writer#Write. implementation 58 | // relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Write. implementation 59 | // relationship github.com/golang/go/src go1.22 io/Writer#Write. implementation 60 | // ⌃ enclosing_range_end 0.1.test `sg/impls`/MyWriter#Write(). 61 | //⌄ enclosing_range_start 0.1.test `sg/impls`/MyWriter#WriteHeader(). 62 | func (w MyWriter) WriteHeader(statusCode int) { panic("") } 63 | // ^ definition local 3 64 | // ^^^^^^^^ reference 0.1.test `sg/impls`/MyWriter# 65 | // ^^^^^^^^^^^ definition 0.1.test `sg/impls`/MyWriter#WriteHeader(). 66 | // documentation 67 | // > ```go 68 | // > func (MyWriter).WriteHeader(statusCode int) 69 | // > ``` 70 | // relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#WriteHeader. implementation 71 | // ^^^^^^^^^^ definition local 4 72 | // ⌃ enclosing_range_end 0.1.test `sg/impls`/MyWriter#WriteHeader(). 73 | 74 | //⌄ enclosing_range_start 0.1.test `sg/impls`/Another(). 75 | func Another() { 76 | // ^^^^^^^ definition 0.1.test `sg/impls`/Another(). 77 | // documentation 78 | // > ```go 79 | // > func Another() 80 | // > ``` 81 | Something(MyWriter{}) 82 | // ^^^^^^^^^ reference 0.1.test `sg/impls`/Something(). 83 | // ^^^^^^^^ reference 0.1.test `sg/impls`/MyWriter# 84 | } 85 | //⌃ enclosing_range_end 0.1.test `sg/impls`/Another(). 86 | 87 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/testdata/implementations_remote.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | // ^^^^^^^^ reference 0.1.test `sg/testdata`/ 3 | 4 | import "net/http" 5 | // ^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 6 | 7 | type implementsWriter struct{} 8 | // ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/testdata`/implementsWriter# 9 | // documentation 10 | // > ```go 11 | // > type implementsWriter struct 12 | // > ``` 13 | // documentation 14 | // > ```go 15 | // > struct{} 16 | // > ``` 17 | // relationship github.com/golang/go/src go1.22 `crypto/tls`/transcriptHash# implementation 18 | // relationship github.com/golang/go/src go1.22 `internal/bisect`/Writer# implementation 19 | // relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter# implementation 20 | // relationship github.com/golang/go/src go1.22 io/Writer# implementation 21 | 22 | //⌄ enclosing_range_start 0.1.test `sg/testdata`/implementsWriter#Header(). 23 | func (implementsWriter) Header() http.Header { panic("Just for how") } 24 | // ^^^^^^^^^^^^^^^^ reference 0.1.test `sg/testdata`/implementsWriter# 25 | // ^^^^^^ definition 0.1.test `sg/testdata`/implementsWriter#Header(). 26 | // documentation 27 | // > ```go 28 | // > func (implementsWriter).Header() Header 29 | // > ``` 30 | // relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Header. implementation 31 | // ^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 32 | // ^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/Header# 33 | // ⌃ enclosing_range_end 0.1.test `sg/testdata`/implementsWriter#Header(). 34 | //⌄ enclosing_range_start 0.1.test `sg/testdata`/implementsWriter#Write(). 35 | func (implementsWriter) Write([]byte) (int, error) { panic("Just for show") } 36 | // ^^^^^^^^^^^^^^^^ reference 0.1.test `sg/testdata`/implementsWriter# 37 | // ^^^^^ definition 0.1.test `sg/testdata`/implementsWriter#Write(). 38 | // documentation 39 | // > ```go 40 | // > func (implementsWriter).Write([]byte) (int, error) 41 | // > ``` 42 | // relationship github.com/golang/go/src go1.22 `crypto/tls`/transcriptHash#Write. implementation 43 | // relationship github.com/golang/go/src go1.22 `internal/bisect`/Writer#Write. implementation 44 | // relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#Write. implementation 45 | // relationship github.com/golang/go/src go1.22 io/Writer#Write. implementation 46 | // ⌃ enclosing_range_end 0.1.test `sg/testdata`/implementsWriter#Write(). 47 | //⌄ enclosing_range_start 0.1.test `sg/testdata`/implementsWriter#WriteHeader(). 48 | func (implementsWriter) WriteHeader(statusCode int) {} 49 | // ^^^^^^^^^^^^^^^^ reference 0.1.test `sg/testdata`/implementsWriter# 50 | // ^^^^^^^^^^^ definition 0.1.test `sg/testdata`/implementsWriter#WriteHeader(). 51 | // documentation 52 | // > ```go 53 | // > func (implementsWriter).WriteHeader(statusCode int) 54 | // > ``` 55 | // relationship github.com/golang/go/src go1.22 `net/http`/ResponseWriter#WriteHeader. implementation 56 | // ^^^^^^^^^^ definition local 0 57 | // ⌃ enclosing_range_end 0.1.test `sg/testdata`/implementsWriter#WriteHeader(). 58 | 59 | //⌄ enclosing_range_start 0.1.test `sg/testdata`/ShowsInSignature(). 60 | func ShowsInSignature(respWriter http.ResponseWriter) { 61 | // ^^^^^^^^^^^^^^^^ definition 0.1.test `sg/testdata`/ShowsInSignature(). 62 | // documentation 63 | // > ```go 64 | // > func ShowsInSignature(respWriter ResponseWriter) 65 | // > ``` 66 | // ^^^^^^^^^^ definition local 1 67 | // ^^^^ reference github.com/golang/go/src go1.22 `net/http`/ 68 | // ^^^^^^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ResponseWriter# 69 | respWriter.WriteHeader(1) 70 | // ^^^^^^^^^^ reference local 1 71 | // ^^^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/ResponseWriter#WriteHeader. 72 | } 73 | //⌃ enclosing_range_end 0.1.test `sg/testdata`/ShowsInSignature(). 74 | 75 | -------------------------------------------------------------------------------- /internal/loader/loader_test.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | "strings" 7 | "testing" 8 | 9 | "github.com/sourcegraph/scip-go/internal/config" 10 | "github.com/stretchr/testify/require" 11 | "golang.org/x/mod/module" 12 | "golang.org/x/tools/go/packages" 13 | ) 14 | 15 | func TestBuiltinFormat(t *testing.T) { 16 | wd, _ := os.Getwd() 17 | root, _ := filepath.Abs(filepath.Join(wd, "../../")) 18 | pkgConfig := getConfig(root, config.IndexOpts{}) 19 | pkgConfig.Tests = false 20 | 21 | pkgs, err := packages.Load(pkgConfig, "fmt") 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | 26 | if len(pkgs) != 1 { 27 | t.Fatalf("Too many packages: %s", pkgs) 28 | } 29 | 30 | fmtPkg := pkgs[0] 31 | 32 | if !IsStandardLib(fmtPkg) { 33 | t.Fatal("Package was not a builtin package: pre ensure") 34 | } 35 | 36 | // TODO: don't use nil? 37 | normalizePackage(&config.IndexOpts{}, fmtPkg) 38 | 39 | if !IsStandardLib(fmtPkg) { 40 | t.Fatal("Package was not a builtin package: post ensure") 41 | } 42 | } 43 | 44 | type normalizeTestCase struct { 45 | Raw string 46 | Normalized string 47 | } 48 | 49 | func TestNormalizePackageModuleVersion(t *testing.T) { 50 | cases := []normalizeTestCase{ 51 | { 52 | Raw: "v0.0.0-20180920160851-f15b22f93c73", 53 | Normalized: "f15b22f93c73", 54 | }, 55 | { 56 | Raw: "v0.3.1-0.20230414160720-beea233bdc0b", 57 | Normalized: "beea233bdc0b", 58 | }, 59 | { 60 | Raw: "v2.0.0-20180818164646-67afb5ed74ec", 61 | Normalized: "67afb5ed74ec", 62 | }, 63 | { 64 | Raw: "v1.1.1", 65 | Normalized: "v1.1.1", 66 | }, 67 | { 68 | Raw: "v1.0.0-beta.1", 69 | Normalized: "v1.0.0-beta.1", 70 | }, 71 | { 72 | Raw: "v0.0.0", 73 | Normalized: "v0.0.0", 74 | }, 75 | { 76 | Raw: "v2.0.0+incompatible", 77 | Normalized: "v2.0.0", 78 | }, 79 | { 80 | Raw: "", 81 | Normalized: ".", 82 | }, 83 | } 84 | 85 | for _, testCase := range cases { 86 | pkg := &packages.Package{ 87 | PkgPath: "github.com/fake_name/fake_module/fake_package", 88 | Module: &packages.Module{ 89 | Path: "github.com/fake_name/fake_module", 90 | Version: testCase.Raw, 91 | }, 92 | } 93 | normalizePackage(&config.IndexOpts{}, pkg) 94 | 95 | require.Equal(t, testCase.Normalized, pkg.Module.Version) 96 | } 97 | } 98 | 99 | func TestPackagePseudoVersion(t *testing.T) { 100 | wd, _ := os.Getwd() 101 | root, _ := filepath.Abs(filepath.Join(wd, "../../")) 102 | pkgConfig := getConfig(root, config.IndexOpts{}) 103 | pkgConfig.Tests = false 104 | 105 | pkgs, err := packages.Load(pkgConfig, "github.com/efritz/pentimento") 106 | require.Nil(t, err) 107 | 108 | require.Equal(t, 1, len(pkgs), "Too many packages") 109 | 110 | pkg := pkgs[0] 111 | 112 | require.True(t, module.IsPseudoVersion(pkg.Module.Version), "Package did not have a pseudo version: pre ensure") 113 | 114 | normalizePackage(&config.IndexOpts{}, pkg) 115 | 116 | require.Equal(t, "ade47d831101", pkg.Module.Version, "Package pseudo-version was not extracted into a sha: post ensure") 117 | } 118 | 119 | func TestPackageWithinModule(t *testing.T) { 120 | wd, _ := os.Getwd() 121 | root, _ := filepath.Abs(filepath.Join(wd, "../../")) 122 | 123 | config := getConfig(root, config.IndexOpts{}) 124 | config.Tests = false 125 | 126 | _, err := packages.Load(config, "./...") 127 | if err != nil { 128 | t.Fatal(err) 129 | } 130 | } 131 | 132 | func TestPentimentoPackage(t *testing.T) { 133 | // "github.com/efritz/pentimento" 134 | wd, _ := os.Getwd() 135 | root, _ := filepath.Abs(filepath.Join(wd, "../../")) 136 | 137 | config := getConfig(root, config.IndexOpts{}) 138 | config.Tests = false 139 | 140 | // TODO: Could possibly just load this way as well :) 141 | // packages.Load(config, "github.com/efritz/pentimento") 142 | pkgs, err := packages.Load(config, "./...") 143 | if err != nil { 144 | t.Fatal(err) 145 | } 146 | 147 | var pentimento *packages.Package 148 | for _, pkg := range pkgs { 149 | for _, imported := range pkg.Imports { 150 | if strings.Contains(imported.Name, "pentimento") { 151 | pentimento = imported 152 | break 153 | } 154 | } 155 | } 156 | 157 | if pentimento == nil { 158 | t.Fatal("Could not find pentimento dep") 159 | } 160 | 161 | if "pentimento" != pentimento.Name || 162 | "github.com/efritz/pentimento" != pentimento.PkgPath || 163 | "github.com/efritz/pentimento" != pentimento.Module.Path { 164 | 165 | t.Fatal("Did not match module") 166 | } 167 | // Name string = "pentimento" 168 | // PkgPath string = "github.com/efritz/pentimento" 169 | // Module: 170 | // Path string = "github.com/efritz/pentimento" 171 | // Version string = "v0.0.0-20190429011147-ade47d831101" 172 | } 173 | -------------------------------------------------------------------------------- /internal/visitors/visitor_var.go: -------------------------------------------------------------------------------- 1 | package visitors 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/token" 7 | 8 | "github.com/sourcegraph/scip-go/internal/document" 9 | "github.com/sourcegraph/scip-go/internal/handler" 10 | "github.com/sourcegraph/scip-go/internal/symbols" 11 | "github.com/sourcegraph/scip/bindings/go/scip" 12 | "golang.org/x/tools/go/packages" 13 | ) 14 | 15 | func visitVarDefinition(doc *document.Document, pkg *packages.Package, decl *ast.GenDecl) { 16 | ast.Walk(&varVisitor{ 17 | doc: doc, 18 | pkg: pkg, 19 | scope: NewScope(pkg.PkgPath), 20 | }, decl) 21 | } 22 | 23 | type varVisitor struct { 24 | doc *document.Document 25 | pkg *packages.Package 26 | 27 | curDecl ast.Decl 28 | scope *Scope 29 | } 30 | 31 | var _ ast.Visitor = &varVisitor{} 32 | 33 | func (v *varVisitor) Visit(n ast.Node) (w ast.Visitor) { 34 | if n == nil { 35 | return nil 36 | } 37 | 38 | switch node := n.(type) { 39 | case *ast.GenDecl: 40 | switch node.Tok { 41 | // Only traverse vars and consts 42 | case token.VAR, token.CONST: 43 | v.curDecl = node 44 | return v 45 | default: 46 | return nil 47 | } 48 | 49 | case *ast.ValueSpec: 50 | // Iterate over names, which are the only thing that can be definitions 51 | for _, name := range node.Names { 52 | symbol := v.makeSymbol(descriptorTerm(name.Name)) 53 | v.doc.SetNewSymbol(symbol, v.curDecl, name) 54 | } 55 | 56 | if len(node.Names) > 0 { 57 | var scopeName string 58 | if len(node.Names) == 1 { 59 | scopeName = node.Names[0].Name 60 | } else { 61 | position := v.pkg.Fset.Position(node.Pos()) 62 | scopeName = fmt.Sprintf("inline-%d-%d", position.Line, position.Column) 63 | } 64 | 65 | v.scope.push(scopeName, scip.Descriptor_Meta) 66 | if node.Type != nil { 67 | switch valueType := node.Type.(type) { 68 | case *ast.FuncType: 69 | // nothing 70 | case *ast.Ident: 71 | // nothing 72 | case *ast.ChanType: 73 | // TODO: Could have chan with new struct 74 | case *ast.ArrayType: 75 | // TODO: Could have array with new struct 76 | case *ast.MapType: 77 | // TODO: could have nested struct? 78 | case *ast.StarExpr: 79 | // TODO: could be *struct? 80 | case *ast.SelectorExpr: 81 | // TODO? 82 | case *ast.IndexExpr: 83 | // TODO: Generics, possibly need to travrerse 84 | case *ast.InterfaceType: 85 | ast.Walk(v, valueType) 86 | case *ast.StructType: 87 | // panic(fmt.Sprintf("TODO: handle type %T %s", valueType, v.pkg.Fset.Position(node.Pos()))) 88 | ast.Walk(v, valueType) 89 | default: 90 | // TODO: Consider how we could emit errors for users running this, not in dev mode 91 | _ = handler.ErrOrPanic("TODO: handle type %T %s", valueType, v.pkg.Fset.Position(node.Pos())) 92 | 93 | return 94 | } 95 | } 96 | 97 | walkExprList(v, node.Values) 98 | v.scope.pop() 99 | } 100 | 101 | return nil 102 | 103 | case *ast.Field: 104 | // I think the only case of this is embedded fields. 105 | if len(node.Names) == 0 { 106 | 107 | names := getIdentOfTypeExpr(v.pkg, node.Type) 108 | for _, name := range names { 109 | embeddedSymbol := v.makeSymbol(&scip.Descriptor{ 110 | Name: name.Name, 111 | Suffix: scip.Descriptor_Term, 112 | }) 113 | 114 | // In this odd scenario, the definition is at embedded field level, 115 | // not wherever the name is. So that messes up our lookup table. 116 | v.doc.SetNewSymbolForPos(embeddedSymbol, node, name, node.Pos()) 117 | } 118 | } else { 119 | for _, name := range node.Names { 120 | v.doc.SetNewSymbol(v.makeSymbol(&scip.Descriptor{ 121 | Name: name.Name, 122 | Suffix: scip.Descriptor_Term, 123 | }), nil, name) 124 | 125 | switch typ := node.Type.(type) { 126 | case *ast.MapType: 127 | v.scope.push(name.Name, scip.Descriptor_Term) 128 | defer func() { 129 | v.scope.pop() 130 | }() 131 | 132 | ast.Walk(v, typ.Key) 133 | ast.Walk(v, typ.Value) 134 | 135 | case *ast.ArrayType: 136 | v.scope.push(name.Name, scip.Descriptor_Term) 137 | defer func() { 138 | v.scope.pop() 139 | }() 140 | 141 | ast.Walk(v, typ.Elt) 142 | 143 | case *ast.StructType, *ast.InterfaceType: 144 | // Current scope is now embedded in the anonymous struct 145 | // So we walk the rest of the type expression and save 146 | // the nested names 147 | v.scope.push(name.Name, scip.Descriptor_Term) 148 | defer func() { 149 | v.scope.pop() 150 | }() 151 | 152 | ast.Walk(v, node.Type) 153 | } 154 | } 155 | } 156 | return nil 157 | 158 | default: 159 | return v 160 | } 161 | } 162 | 163 | func (s *varVisitor) makeSymbol(descriptor *scip.Descriptor) string { 164 | return symbols.FromDescriptors(s.pkg, append(s.scope.descriptors, descriptor)...) 165 | } 166 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/input/testdata/data.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "context" 5 | 6 | "sg/testdata/internal/secret" 7 | ) 8 | 9 | // TestInterface is an interface used for testing. 10 | type TestInterface interface { 11 | // Do does a test thing. 12 | Do(ctx context.Context, data string) (score int, _ error) 13 | } 14 | 15 | type ( 16 | // TestStruct is a struct used for testing. 17 | TestStruct struct { 18 | // SimpleA docs 19 | SimpleA int 20 | // SimpleB docs 21 | SimpleB int 22 | // SimpleC docs 23 | SimpleC int 24 | 25 | FieldWithTag string `json:"tag"` 26 | FieldWithAnonymousType struct { 27 | NestedA string 28 | NestedB string 29 | // NestedC docs 30 | NestedC string 31 | } 32 | 33 | EmptyStructField struct{} 34 | } 35 | 36 | TestEmptyStruct struct{} 37 | ) 38 | 39 | // Score is just a hardcoded number. 40 | const Score = uint64(42) 41 | const secretScore = secret.SecretScore 42 | 43 | const SomeString = "foobar" 44 | const LongString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tincidunt viverra aliquam. Phasellus finibus, arcu eu commodo porta, dui quam dictum ante, nec porta enim leo quis felis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Curabitur luctus orci tortor, non condimentum arcu bibendum ut. Proin sit amet vulputate lorem, ut egestas arcu. Curabitur quis sagittis mi. Aenean elit sem, imperdiet ut risus eget, varius varius erat.\nNullam lobortis tortor sed sodales consectetur. Aenean condimentum vehicula elit, eget interdum ante finibus nec. Mauris mollis, nulla eu vehicula rhoncus, eros lectus viverra tellus, ac hendrerit quam massa et felis. Nunc vestibulum diam a facilisis sollicitudin. Aenean nec varius metus. Sed nec diam nibh. Ut erat erat, suscipit et ante eget, tincidunt condimentum orci. Aenean nec facilisis augue, ac sodales ex. Nulla dictum hendrerit tempus. Aliquam fringilla tortor in massa molestie, quis bibendum nulla ullamcorper. Suspendisse congue laoreet elit, vitae consectetur orci facilisis non. Aliquam tempus ultricies sapien, rhoncus tincidunt nisl tincidunt eget. Aliquam nisi ante, rutrum eget viverra imperdiet, congue ut nunc. Donec mollis sed tellus vel placerat. Sed mi ex, fringilla a fermentum a, tincidunt eget lectus.\nPellentesque lacus nibh, accumsan eget feugiat nec, gravida eget urna. Donec quam velit, imperdiet in consequat eget, ultricies eget nunc. Curabitur interdum vel sem et euismod. Donec sed vulputate odio, sit amet bibendum tellus. Integer pellentesque nunc eu turpis cursus, vestibulum sodales ipsum posuere. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Ut at vestibulum sapien. In hac habitasse platea dictumst. Nullam sed lobortis urna, non bibendum ipsum. Sed in sapien quis purus semper fringilla. Integer ut egestas nulla, eu ornare lectus. Maecenas quis sapien condimentum, dignissim urna quis, hendrerit neque. Donec cursus sit amet metus eu mollis.\nSed scelerisque vitae odio non egestas. Cras hendrerit tortor mauris. Aenean quis imperdiet nulla, a viverra purus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Praesent finibus faucibus orci, sed ultrices justo iaculis ut. Ut libero massa, condimentum at elit non, fringilla iaculis quam. Sed sit amet ipsum placerat, tincidunt sem in, efficitur lacus. Curabitur ligula orci, tempus ut magna eget, sodales tristique odio.\nPellentesque in libero ac risus pretium ultrices. In hac habitasse platea dictumst. Curabitur a quam sed orci tempus luctus. Integer commodo nec odio quis consequat. Aenean vitae dapibus augue, nec dictum lectus. Etiam sit amet leo diam. Duis eu ligula venenatis, fermentum lacus vel, interdum odio. Vivamus sit amet libero vitae elit interdum cursus et eu erat. Cras interdum augue sit amet ex aliquet tempor. Praesent dolor nisl, convallis bibendum mauris a, euismod commodo ante. Phasellus non ipsum condimentum, molestie dolor quis, pretium nisi. Mauris augue urna, fermentum ut lacinia a, efficitur vitae odio. Praesent finibus nisl et dolor luctus faucibus. Donec eget lectus sed mi porttitor placerat ac eu odio." 45 | const ConstMath = 1 + (2+3)*5 46 | 47 | type StringAlias string 48 | 49 | const AliasedString StringAlias = "foobar" 50 | 51 | // Doer is similar to the test interface (but not the same). 52 | func (ts *TestStruct) Doer(ctx context.Context, data string) (score int, err error) { 53 | return Score, nil 54 | } 55 | 56 | // StructTagRegression is a struct that caused panic in the wild. Added here to 57 | // support a regression test. 58 | // 59 | // See https://github.com/tal-tech/go-zero/blob/11dd3d75ecceaa3f5772024fb3f26dec1ada8e9c/core/mapping/unmarshaler_test.go#L2272. 60 | type StructTagRegression struct { 61 | Value int `key:",range=[:}"` 62 | } 63 | 64 | type TestEqualsStruct = struct { 65 | Value int 66 | } 67 | 68 | type ShellStruct struct { 69 | // Ensure this field comes before the definition 70 | // so that we grab the correct one in our unit 71 | // tests. 72 | InnerStruct 73 | } 74 | 75 | type InnerStruct struct{} 76 | -------------------------------------------------------------------------------- /internal/testdata/snapshots/output/embedded/embedded.go: -------------------------------------------------------------------------------- 1 | package embedded 2 | // ^^^^^^^^ reference 0.1.test `sg/embedded`/ 3 | 4 | import ( 5 | "fmt" 6 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 7 | "os/exec" 8 | // ^^^^^^^ reference github.com/golang/go/src go1.22 `os/exec`/ 9 | ) 10 | 11 | type osExecCommand struct { 12 | // ^^^^^^^^^^^^^ definition 0.1.test `sg/embedded`/osExecCommand# 13 | // documentation 14 | // > ```go 15 | // > type osExecCommand struct 16 | // > ``` 17 | // documentation 18 | // > ```go 19 | // > struct { 20 | // > *Cmd 21 | // > } 22 | // > ``` 23 | // relationship github.com/golang/go/src go1.22 context/stringer# implementation 24 | // relationship github.com/golang/go/src go1.22 fmt/Stringer# implementation 25 | // relationship github.com/golang/go/src go1.22 runtime/stringer# implementation 26 | *exec.Cmd 27 | // ^^^^ reference github.com/golang/go/src go1.22 `os/exec`/ 28 | // ^^^ definition 0.1.test `sg/embedded`/osExecCommand#Cmd. 29 | // documentation 30 | // > ```go 31 | // > struct field Cmd *os/exec.Cmd 32 | // > ``` 33 | // ^^^ reference github.com/golang/go/src go1.22 `os/exec`/Cmd# 34 | } 35 | 36 | //⌄ enclosing_range_start 0.1.test `sg/embedded`/wrapExecCommand(). 37 | func wrapExecCommand(c *exec.Cmd) { 38 | // ^^^^^^^^^^^^^^^ definition 0.1.test `sg/embedded`/wrapExecCommand(). 39 | // documentation 40 | // > ```go 41 | // > func wrapExecCommand(c *Cmd) 42 | // > ``` 43 | // ^ definition local 0 44 | // ^^^^ reference github.com/golang/go/src go1.22 `os/exec`/ 45 | // ^^^ reference github.com/golang/go/src go1.22 `os/exec`/Cmd# 46 | _ = &osExecCommand{Cmd: c} 47 | // ^^^^^^^^^^^^^ reference 0.1.test `sg/embedded`/osExecCommand# 48 | // ^^^ reference 0.1.test `sg/embedded`/osExecCommand#Cmd. 49 | // ^ reference local 0 50 | } 51 | //⌃ enclosing_range_end 0.1.test `sg/embedded`/wrapExecCommand(). 52 | 53 | type Inner struct { 54 | // ^^^^^ definition 0.1.test `sg/embedded`/Inner# 55 | // documentation 56 | // > ```go 57 | // > type Inner struct 58 | // > ``` 59 | // documentation 60 | // > ```go 61 | // > struct { 62 | // > X int 63 | // > Y int 64 | // > Z int 65 | // > } 66 | // > ``` 67 | X int 68 | // ^ definition 0.1.test `sg/embedded`/Inner#X. 69 | // documentation 70 | // > ```go 71 | // > struct field X int 72 | // > ``` 73 | Y int 74 | // ^ definition 0.1.test `sg/embedded`/Inner#Y. 75 | // documentation 76 | // > ```go 77 | // > struct field Y int 78 | // > ``` 79 | Z int 80 | // ^ definition 0.1.test `sg/embedded`/Inner#Z. 81 | // documentation 82 | // > ```go 83 | // > struct field Z int 84 | // > ``` 85 | } 86 | 87 | type Outer struct { 88 | // ^^^^^ definition 0.1.test `sg/embedded`/Outer# 89 | // documentation 90 | // > ```go 91 | // > type Outer struct 92 | // > ``` 93 | // documentation 94 | // > ```go 95 | // > struct { 96 | // > Inner 97 | // > W int 98 | // > } 99 | // > ``` 100 | Inner 101 | // ^^^^^ definition 0.1.test `sg/embedded`/Outer#Inner. 102 | // documentation 103 | // > ```go 104 | // > struct field Inner sg/embedded.Inner 105 | // > ``` 106 | // ^^^^^ reference 0.1.test `sg/embedded`/Inner# 107 | W int 108 | // ^ definition 0.1.test `sg/embedded`/Outer#W. 109 | // documentation 110 | // > ```go 111 | // > struct field W int 112 | // > ``` 113 | } 114 | 115 | //⌄ enclosing_range_start 0.1.test `sg/embedded`/useOfCompositeStructs(). 116 | func useOfCompositeStructs() { 117 | // ^^^^^^^^^^^^^^^^^^^^^ definition 0.1.test `sg/embedded`/useOfCompositeStructs(). 118 | // documentation 119 | // > ```go 120 | // > func useOfCompositeStructs() 121 | // > ``` 122 | o := Outer{ 123 | // ^ definition local 1 124 | // ^^^^^ reference 0.1.test `sg/embedded`/Outer# 125 | Inner: Inner{ 126 | // ^^^^^ reference 0.1.test `sg/embedded`/Outer#Inner. 127 | // ^^^^^ reference 0.1.test `sg/embedded`/Inner# 128 | X: 1, 129 | // ^ reference 0.1.test `sg/embedded`/Inner#X. 130 | Y: 2, 131 | // ^ reference 0.1.test `sg/embedded`/Inner#Y. 132 | Z: 3, 133 | // ^ reference 0.1.test `sg/embedded`/Inner#Z. 134 | }, 135 | W: 4, 136 | // ^ reference 0.1.test `sg/embedded`/Outer#W. 137 | } 138 | 139 | fmt.Printf("> %d\n", o.X) 140 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 141 | // ^^^^^^ reference github.com/golang/go/src go1.22 fmt/Printf(). 142 | // ^ reference local 1 143 | // ^ reference 0.1.test `sg/embedded`/Inner#X. 144 | fmt.Println(o.Inner.Y) 145 | // ^^^ reference github.com/golang/go/src go1.22 fmt/ 146 | // ^^^^^^^ reference github.com/golang/go/src go1.22 fmt/Println(). 147 | // ^ reference local 1 148 | // ^^^^^ reference 0.1.test `sg/embedded`/Outer#Inner. 149 | // ^ reference 0.1.test `sg/embedded`/Inner#Y. 150 | } 151 | //⌃ enclosing_range_end 0.1.test `sg/embedded`/useOfCompositeStructs(). 152 | 153 | --------------------------------------------------------------------------------