diff --git a/cmd/lsif-go/index.go b/cmd/lsif-go/index.go index c22f87e7..016a06dd 100644 --- a/cmd/lsif-go/index.go +++ b/cmd/lsif-go/index.go @@ -12,7 +12,7 @@ import ( "github.com/sourcegraph/sourcegraph/lib/codeintel/lsif/protocol/writer" ) -func writeIndex(repositoryRoot, repositoryRemote, projectRoot, moduleName, moduleVersion string, dependencies map[string]gomod.Module, outFile string, outputOptions output.Options) error { +func writeIndex(repositoryRoot, repositoryRemote, projectRoot, moduleName, moduleVersion string, dependencies map[string]gomod.GoModule, outFile string, outputOptions output.Options) error { start := time.Now() out, err := os.Create(outFile) diff --git a/go.mod b/go.mod index ce9bf97a..2bdbfc70 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/slimsag/godocmd v0.0.0-20161025000126-a1005ad29fe3 github.com/sourcegraph/lsif-static-doc v0.0.0-20210728160750-2383b286c98b github.com/sourcegraph/sourcegraph/lib v0.0.0-20210728181912-41b2ce5ea74c + golang.org/x/mod v0.4.2 // indirect golang.org/x/tools v0.1.3 mvdan.cc/gofumpt v0.1.1 // indirect ) diff --git a/internal/gomod/dependencies.go b/internal/gomod/dependencies.go index 3c0544a3..ade765b1 100644 --- a/internal/gomod/dependencies.go +++ b/internal/gomod/dependencies.go @@ -2,6 +2,7 @@ package gomod import ( "encoding/json" + "errors" "fmt" "io" "log" @@ -17,7 +18,7 @@ import ( "golang.org/x/tools/go/vcs" ) -type Module struct { +type GoModule struct { Name string Version string } @@ -26,7 +27,7 @@ type Module struct { // and version as declared by the go.mod file in the current directory. The given root module // and version are used to resolve replace directives with local file paths. The root module // is expected to be a resolved import path (a valid URL, including a scheme). -func ListDependencies(dir, rootModule, rootVersion string, outputOptions output.Options) (dependencies map[string]Module, err error) { +func ListDependencies(dir, rootModule, rootVersion string, outputOptions output.Options) (dependencies map[string]GoModule, err error) { if !isModule(dir) { log.Println("WARNING: No go.mod file found in current directory.") return nil, nil @@ -39,7 +40,21 @@ func ListDependencies(dir, rootModule, rootVersion string, outputOptions output. return } - dependencies, err = parseGoListOutput(output, rootVersion) + // The reason we run this command separate is because we want the + // information about this package specifically. Currently, it seems + // that "go list all" will place the current modules information first + // in the list, but we don't know that that is guaranteed. + // + // Because of that, we do a separate execution to guarantee we get only + // this package information to use to determine the corresponding + // goVersion. + modOutput, err := command.Run(dir, "go", "list", "-mod=readonly", "-m", "-json") + if err != nil { + err = fmt.Errorf("failed to list module info: %v\n%s", err, output) + return + } + + dependencies, err = parseGoListOutput(output, modOutput, rootVersion) if err != nil { return } @@ -61,6 +76,9 @@ type jsonModule struct { Name string `json:"Path"` Version string `json:"Version"` Replace *jsonModule `json:"Replace"` + + // The Golang version required for this module + GoVersion string `json:"GoVersion"` } // parseGoListOutput parse the JSON output of `go list -m`. This method returns a map from @@ -68,8 +86,8 @@ type jsonModule struct { // replacement directives specified in go.mod. Replace directives indicating a local file path // will create a module with the given root version, which is expected to be the same version // as the module being indexed. -func parseGoListOutput(output, rootVersion string) (map[string]Module, error) { - dependencies := map[string]Module{} +func parseGoListOutput(output, modOutput, rootVersion string) (map[string]GoModule, error) { + dependencies := map[string]GoModule{} decoder := json.NewDecoder(strings.NewReader(output)) for { @@ -95,15 +113,60 @@ func parseGoListOutput(output, rootVersion string) (map[string]Module, error) { module.Version = rootVersion } - dependencies[importPath] = Module{ + dependencies[importPath] = GoModule{ Name: module.Name, Version: cleanVersion(module.Version), } } + var thisModule jsonModule + if err := json.NewDecoder(strings.NewReader(modOutput)).Decode(&thisModule); err != nil { + return nil, err + } + + if thisModule.GoVersion == "" { + return nil, errors.New("could not find GoVersion for current module") + } + + setGolangDependency(dependencies, thisModule.GoVersion) + return dependencies, nil } +// The repository to find the source code for golang. +var golangRepository = "github.com/golang/go" + +func setGolangDependency(dependencies map[string]GoModule, goVersion string) { + dependencies[golangRepository] = GoModule{ + Name: golangRepository, + + // The reason we prefix version with "go" is because in golang/go, all the release + // tags are prefixed with "go". So turn "1.15" -> "go1.15" + Version: fmt.Sprintf("go%s", goVersion), + } +} + +func GetGolangDependency(dependencies map[string]GoModule) GoModule { + return dependencies[golangRepository] +} + +// NormalizeMonikerPackage returns a normalized path to ensure that all +// standard library paths are handled the same. Primarily to make sure +// that both the golangRepository and "std/" paths are normalized. +func NormalizeMonikerPackage(path string) string { + // When indexing _within_ the golang/go repository, `std/` is prefixed + // to packages. So we trim that here just to be sure that we keep + // consistent names. + normalizedPath := strings.TrimPrefix(path, "std/") + + if !isStandardlibPackge(normalizedPath) { + return path + } + + // Make sure we don't see double "std/" in the package for the moniker + return fmt.Sprintf("%s/std/%s", golangRepository, normalizedPath) +} + // versionPattern matches a versioning ending in a 12-digit sha, e.g., vX.Y.Z.-yyyymmddhhmmss-abcdefabcdef var versionPattern = regexp.MustCompile(`^.*-([a-f0-9]{12})$`) @@ -147,20 +210,18 @@ func resolveImportPaths(rootModule string, modules []string) map[string]string { // Try to resolve the import path if it looks like a local path name, err := resolveLocalPath(name, rootModule) if err != nil { - log.Println(fmt.Sprintf("WARNING: Failed to resolve %s (%s).", name, err)) + log.Println(fmt.Sprintf("WARNING: Failed to resolve local %s (%s).", name, err)) continue } // Determine path suffix relative to the import path - repoRoot, err := vcs.RepoRootForImportPath(name, false) - if err != nil { - log.Println(fmt.Sprintf("WARNING: Failed to resolve %s (%s).", name, err)) + resolved, ok := resolveRepoRootForImportPath(name) + if !ok { continue } - suffix := strings.TrimPrefix(name, repoRoot.Root) m.Lock() - namesToResolve[originalName] = repoRoot.Repo + suffix + namesToResolve[originalName] = resolved m.Unlock() } }() @@ -170,6 +231,28 @@ func resolveImportPaths(rootModule string, modules []string) map[string]string { return namesToResolve } +// resolveRepoRootForImportPath will get the resolved name after handling vsc RepoRoots and any +// necessary handling of the standard library +func resolveRepoRootForImportPath(name string) (string, bool) { + // When indexing golang/go, there are some references to the package "std" itself. + // Generally, "std/" is not referenced directly (it is just assumed when you have "fmt" or similar + // in your imports), but inside of golang/go, it is directly referenced. + // + // In that case, we just return it directly, there is no other resolving to do. + if name == "std" { + return name, true + } + + repoRoot, err := vcs.RepoRootForImportPath(name, false) + if err != nil { + log.Println(fmt.Sprintf("WARNING: Failed to resolve repo %s (%s) %s.", name, err, repoRoot)) + return "", false + } + + suffix := strings.TrimPrefix(name, repoRoot.Root) + return repoRoot.Repo + suffix, true +} + // resolveLocalPath converts the given name to an import path if it looks like a local path based on // the given root module. The root module, if non-empty, is expected to be a resolved import path // (a valid URL, including a scheme). If the name does not look like a local path, it will be returned @@ -193,10 +276,10 @@ func resolveLocalPath(name, rootModule string) (string, error) { // mapImportPaths replace each module name with the value in the given resolved import paths // map. If the module name is not present in the map, no change is made to the module value. -func mapImportPaths(dependencies map[string]Module, resolvedImportPaths map[string]string) { +func mapImportPaths(dependencies map[string]GoModule, resolvedImportPaths map[string]string) { for importPath, module := range dependencies { if name, ok := resolvedImportPaths[module.Name]; ok { - dependencies[importPath] = Module{ + dependencies[importPath] = GoModule{ Name: name, Version: module.Version, } diff --git a/internal/gomod/dependencies_test.go b/internal/gomod/dependencies_test.go index cec2b419..1c875a23 100644 --- a/internal/gomod/dependencies_test.go +++ b/internal/gomod/dependencies_test.go @@ -59,12 +59,23 @@ func TestParseGoListOutput(t *testing.T) { } ` - modules, err := parseGoListOutput(output, "v1.2.3") + modOutput := ` + { + "Path": "github.com/sourcegraph/lsif-go", + "Main": true, + "Dir": "/home/tjdevries/sourcegraph/lsif-go.git/asdf", + "GoMod": "/home/tjdevries/sourcegraph/lsif-go.git/asdf/go.mod", + "GoVersion": "1.15" + } + ` + + modules, err := parseGoListOutput(output, modOutput, "v1.2.3") if err != nil { t.Fatalf("unexpected error: %s", err) } - expected := map[string]Module{ + expected := map[string]GoModule{ + "github.com/golang/go": {Name: "github.com/golang/go", Version: "go1.15"}, "github.com/gavv/httpexpect": {Name: "github.com/gavv/httpexpect", Version: "v2.0.0"}, "github.com/getsentry/raven-go": {Name: "github.com/getsentry/raven-go", Version: "v0.2.0"}, "github.com/gfleury/go-bitbucket-v1": {Name: "github.com/gfleury/go-bitbucket-v1", Version: "e5170e3280fb"}, @@ -125,3 +136,24 @@ func TestResolveImportPaths(t *testing.T) { t.Errorf("unexpected import paths (-want +got): %s", diff) } } + +func TestNormalizeMonikerPackage(t *testing.T) { + testCases := map[string]string{ + "fmt": "github.com/golang/go/std/fmt", + + // This happens sometimes in the standard library, that we have "std/" prefixed. + "std/hash": "github.com/golang/go/std/hash", + + // User libs should be unchanged. + "github.com/sourcegraph/sourcegraph/lib": "github.com/sourcegraph/sourcegraph/lib", + + // Unknown libs should not be changed (for example, custom proxy) + "myCustomPackage": "myCustomPackage", + } + + for path, expected := range testCases { + if diff := cmp.Diff(expected, NormalizeMonikerPackage(path)); diff != "" { + t.Errorf("unexpected normalized moniker package (-want +got): %s", diff) + } + } +} diff --git a/internal/gomod/stdlib.go b/internal/gomod/stdlib.go new file mode 100644 index 00000000..7429520d --- /dev/null +++ b/internal/gomod/stdlib.go @@ -0,0 +1,227 @@ +// THIS FILE IS GENERATED. SEE ./scripts/gen_stdlib_map.sh +// Generated by: go version go1.16.6 linux/amd64 +package gomod + +// isStandardlibPackge determines whether a package is in the standard library +// or not. At this point, it checks whether the package name is one of those +// that is found from running "go list std" in the latest released go version. +func isStandardlibPackge(pkg string) bool { + _, ok := standardLibraryMap[pkg] + return ok +} + +var contained = struct{}{} + +// This list is calculated from "go list std". +var standardLibraryMap = map[string]interface{}{ + "archive/tar": contained, + "archive/zip": contained, + "bufio": contained, + "bytes": contained, + "compress/bzip2": contained, + "compress/flate": contained, + "compress/gzip": contained, + "compress/lzw": contained, + "compress/zlib": contained, + "container/heap": contained, + "container/list": contained, + "container/ring": contained, + "context": contained, + "crypto": contained, + "crypto/aes": contained, + "crypto/cipher": contained, + "crypto/des": contained, + "crypto/dsa": contained, + "crypto/ecdsa": contained, + "crypto/ed25519": contained, + "crypto/ed25519/internal/edwards25519": contained, + "crypto/elliptic": contained, + "crypto/hmac": contained, + "crypto/internal/randutil": contained, + "crypto/internal/subtle": contained, + "crypto/md5": contained, + "crypto/rand": contained, + "crypto/rc4": contained, + "crypto/rsa": contained, + "crypto/sha1": contained, + "crypto/sha256": contained, + "crypto/sha512": contained, + "crypto/subtle": contained, + "crypto/tls": contained, + "crypto/x509": contained, + "crypto/x509/pkix": contained, + "database/sql": contained, + "database/sql/driver": contained, + "debug/dwarf": contained, + "debug/elf": contained, + "debug/gosym": contained, + "debug/macho": contained, + "debug/pe": contained, + "debug/plan9obj": contained, + "embed": contained, + "embed/internal/embedtest": contained, + "encoding": contained, + "encoding/ascii85": contained, + "encoding/asn1": contained, + "encoding/base32": contained, + "encoding/base64": contained, + "encoding/binary": contained, + "encoding/csv": contained, + "encoding/gob": contained, + "encoding/hex": contained, + "encoding/json": contained, + "encoding/pem": contained, + "encoding/xml": contained, + "errors": contained, + "expvar": contained, + "flag": contained, + "fmt": contained, + "go/ast": contained, + "go/build": contained, + "go/build/constraint": contained, + "go/constant": contained, + "go/doc": contained, + "go/format": contained, + "go/importer": contained, + "go/internal/gccgoimporter": contained, + "go/internal/gcimporter": contained, + "go/internal/srcimporter": contained, + "go/parser": contained, + "go/printer": contained, + "go/scanner": contained, + "go/token": contained, + "go/types": contained, + "hash": contained, + "hash/adler32": contained, + "hash/crc32": contained, + "hash/crc64": contained, + "hash/fnv": contained, + "hash/maphash": contained, + "html": contained, + "html/template": contained, + "image": contained, + "image/color": contained, + "image/color/palette": contained, + "image/draw": contained, + "image/gif": contained, + "image/internal/imageutil": contained, + "image/jpeg": contained, + "image/png": contained, + "index/suffixarray": contained, + "internal/bytealg": contained, + "internal/cfg": contained, + "internal/cpu": contained, + "internal/execabs": contained, + "internal/fmtsort": contained, + "internal/goroot": contained, + "internal/goversion": contained, + "internal/lazyregexp": contained, + "internal/lazytemplate": contained, + "internal/nettrace": contained, + "internal/obscuretestdata": contained, + "internal/oserror": contained, + "internal/poll": contained, + "internal/profile": contained, + "internal/race": contained, + "internal/reflectlite": contained, + "internal/singleflight": contained, + "internal/syscall/execenv": contained, + "internal/syscall/unix": contained, + "internal/sysinfo": contained, + "internal/testenv": contained, + "internal/testlog": contained, + "internal/trace": contained, + "internal/unsafeheader": contained, + "internal/xcoff": contained, + "io": contained, + "io/fs": contained, + "io/ioutil": contained, + "log": contained, + "log/syslog": contained, + "math": contained, + "math/big": contained, + "math/bits": contained, + "math/cmplx": contained, + "math/rand": contained, + "mime": contained, + "mime/multipart": contained, + "mime/quotedprintable": contained, + "net": contained, + "net/http": contained, + "net/http/cgi": contained, + "net/http/cookiejar": contained, + "net/http/fcgi": contained, + "net/http/httptest": contained, + "net/http/httptrace": contained, + "net/http/httputil": contained, + "net/http/internal": contained, + "net/http/pprof": contained, + "net/internal/socktest": contained, + "net/mail": contained, + "net/rpc": contained, + "net/rpc/jsonrpc": contained, + "net/smtp": contained, + "net/textproto": contained, + "net/url": contained, + "os": contained, + "os/exec": contained, + "os/signal": contained, + "os/signal/internal/pty": contained, + "os/user": contained, + "path": contained, + "path/filepath": contained, + "plugin": contained, + "reflect": contained, + "regexp": contained, + "regexp/syntax": contained, + "runtime": contained, + "runtime/cgo": contained, + "runtime/debug": contained, + "runtime/internal/atomic": contained, + "runtime/internal/math": contained, + "runtime/internal/sys": contained, + "runtime/metrics": contained, + "runtime/pprof": contained, + "runtime/race": contained, + "runtime/trace": contained, + "sort": contained, + "strconv": contained, + "strings": contained, + "sync": contained, + "sync/atomic": contained, + "syscall": contained, + "testing": contained, + "testing/fstest": contained, + "testing/internal/testdeps": contained, + "testing/iotest": contained, + "testing/quick": contained, + "text/scanner": contained, + "text/tabwriter": contained, + "text/template": contained, + "text/template/parse": contained, + "time": contained, + "time/tzdata": contained, + "unicode": contained, + "unicode/utf16": contained, + "unicode/utf8": contained, + "unsafe": contained, + "vendor/golang.org/x/crypto/chacha20": contained, + "vendor/golang.org/x/crypto/chacha20poly1305": contained, + "vendor/golang.org/x/crypto/cryptobyte": contained, + "vendor/golang.org/x/crypto/cryptobyte/asn1": contained, + "vendor/golang.org/x/crypto/curve25519": contained, + "vendor/golang.org/x/crypto/hkdf": contained, + "vendor/golang.org/x/crypto/internal/subtle": contained, + "vendor/golang.org/x/crypto/poly1305": contained, + "vendor/golang.org/x/net/dns/dnsmessage": contained, + "vendor/golang.org/x/net/http/httpguts": contained, + "vendor/golang.org/x/net/http/httpproxy": contained, + "vendor/golang.org/x/net/http2/hpack": contained, + "vendor/golang.org/x/net/idna": contained, + "vendor/golang.org/x/net/nettest": contained, + "vendor/golang.org/x/sys/cpu": contained, + "vendor/golang.org/x/text/secure/bidirule": contained, + "vendor/golang.org/x/text/transform": contained, + "vendor/golang.org/x/text/unicode/bidi": contained, + "vendor/golang.org/x/text/unicode/norm": contained, +} diff --git a/internal/gomod/stdlib_test.go b/internal/gomod/stdlib_test.go new file mode 100644 index 00000000..b3bca138 --- /dev/null +++ b/internal/gomod/stdlib_test.go @@ -0,0 +1,29 @@ +package gomod + +import "testing" + +func TestStdLib(t *testing.T) { + expectedStdlib := []string{ + "fmt", + "database/sql", + "net/http/httptrace", + } + + for _, testCase := range expectedStdlib { + if !isStandardlibPackge(testCase) { + t.Errorf(`"%s" should be marked as a standard library package`, testCase) + } + } + + expectedUserlib := []string{ + "github.com/sourcegraph/lsif-go/internal/command", + "github.com/sourcegraph/lsif-go/internal/output", + "myCustomName/hello", + } + + for _, testCase := range expectedUserlib { + if isStandardlibPackge(testCase) { + t.Errorf(`"%s" should not be marked as a standard library package`, testCase) + } + } +} diff --git a/internal/indexer/indexer.go b/internal/indexer/indexer.go index ee9b4425..50226a24 100644 --- a/internal/indexer/indexer.go +++ b/internal/indexer/indexer.go @@ -26,7 +26,7 @@ type Indexer struct { toolInfo protocol.ToolInfo // metadata vertex payload moduleName string // name of this module moduleVersion string // version of this module - dependencies map[string]gomod.Module // parsed module data + dependencies map[string]gomod.GoModule // parsed module data emitter *writer.Emitter // LSIF data emitter outputOptions output.Options // What to print to stdout/stderr @@ -71,7 +71,7 @@ func New( toolInfo protocol.ToolInfo, moduleName string, moduleVersion string, - dependencies map[string]gomod.Module, + dependencies map[string]gomod.GoModule, jsonWriter writer.JSONWriter, packageDataCache *PackageDataCache, outputOptions output.Options, diff --git a/internal/indexer/moniker.go b/internal/indexer/moniker.go index d6763e73..f52ef3e6 100644 --- a/internal/indexer/moniker.go +++ b/internal/indexer/moniker.go @@ -5,6 +5,7 @@ import ( "go/types" "strings" + "github.com/sourcegraph/lsif-go/internal/gomod" "golang.org/x/tools/go/packages" ) @@ -17,7 +18,7 @@ func (i *Indexer) emitExportMoniker(sourceID uint64, p *packages.Package, obj ty return } - packageName := monikerPackage(obj) + packageName := makeMonikerPackage(obj) if strings.HasPrefix(packageName, "_"+i.projectRoot) { packageName = i.repositoryRemote + strings.TrimSuffix(packageName[len(i.projectRoot)+1:], "_test") } @@ -25,7 +26,7 @@ func (i *Indexer) emitExportMoniker(sourceID uint64, p *packages.Package, obj ty // Emit export moniker (uncached as these are on unique definitions) monikerID := i.emitter.EmitMoniker("export", "gomod", joinMonikerParts( packageName, - monikerIdentifier(i.packageDataCache, p, obj), + makeMonikerIdentifier(i.packageDataCache, p, obj), )) // Lazily emit package information vertex and attach it to moniker @@ -53,7 +54,8 @@ func joinMonikerParts(parts ...string) string { // the moniker vertex and the package information vertex representing the dependency containing // the identifier. func (i *Indexer) emitImportMoniker(sourceID uint64, p *packages.Package, obj types.Object) { - pkg := monikerPackage(obj) + pkg := makeMonikerPackage(obj) + monikerIdentifier := joinMonikerParts(pkg, makeMonikerIdentifier(i.packageDataCache, p, obj)) for _, moduleName := range packagePrefixes(pkg) { if module, ok := i.dependencies[moduleName]; ok { @@ -61,7 +63,6 @@ func (i *Indexer) emitImportMoniker(sourceID uint64, p *packages.Package, obj ty packageInformationID := i.ensurePackageInformation(module.Name, module.Version) // Lazily emit moniker vertex - monikerIdentifier := joinMonikerParts(pkg, monikerIdentifier(i.packageDataCache, p, obj)) monikerID := i.ensureImportMoniker(monikerIdentifier, packageInformationID) // Attach moniker to source element and stop after first match @@ -133,20 +134,23 @@ func (i *Indexer) ensureImportMoniker(identifier string, packageInformationID ui return monikerID } -// monikerPackage returns the package prefix used to construct a unique moniker for the given object. +// makeMonikerPackage returns the package prefix used to construct a unique moniker for the given object. // A full moniker has the form `{package prefix}:{identifier suffix}`. -func monikerPackage(obj types.Object) string { +func makeMonikerPackage(obj types.Object) string { + var pkgName string if v, ok := obj.(*types.PkgName); ok { - return strings.Trim(v.Name(), `"`) + pkgName = strings.Trim(v.Name(), `"`) + } else { + pkgName = obj.Pkg().Path() } - return obj.Pkg().Path() + return gomod.NormalizeMonikerPackage(pkgName) } -// monikerIdentifier returns the identifier suffix used to construct a unique moniker for the given object. +// makeMonikerIdentifier returns the identifier suffix used to construct a unique moniker for the given object. // A full moniker has the form `{package prefix}:{identifier suffix}`. The identifier is meant to act as a // qualified type path to the given object (e.g. `StructName.FieldName` or `StructName.MethodName`). -func monikerIdentifier(packageDataCache *PackageDataCache, p *packages.Package, obj types.Object) string { +func makeMonikerIdentifier(packageDataCache *PackageDataCache, p *packages.Package, obj types.Object) string { if _, ok := obj.(*types.PkgName); ok { // Packages are identified uniquely by their package prefix return "" diff --git a/internal/indexer/moniker_test.go b/internal/indexer/moniker_test.go index 6eb0bae4..56d25d3c 100644 --- a/internal/indexer/moniker_test.go +++ b/internal/indexer/moniker_test.go @@ -117,7 +117,7 @@ func TestEmitImportMoniker(t *testing.T) { w := &capturingWriter{} indexer := &Indexer{ - dependencies: map[string]gomod.Module{ + dependencies: map[string]gomod.GoModule{ "github.com/test/pkg/sub1": {Name: "github.com/test/pkg/sub1", Version: "1.2.3-deadbeef"}, }, emitter: writer.NewEmitter(w), @@ -182,7 +182,7 @@ func TestMonikerIdentifierBasic(t *testing.T) { packages := getTestPackages(t) p, obj := findUseByName(t, packages, "Score") - if identifier := monikerIdentifier(NewPackageDataCache(), p, obj); identifier != "Score" { + if identifier := makeMonikerIdentifier(NewPackageDataCache(), p, obj); identifier != "Score" { t.Errorf("unexpected moniker identifier. want=%q have=%q", "Score", identifier) } } @@ -191,7 +191,7 @@ func TestMonikerIdentifierPackageName(t *testing.T) { packages := getTestPackages(t) p, obj := findUseByName(t, packages, "sync") - if identifier := monikerIdentifier(NewPackageDataCache(), p, obj); identifier != "" { + if identifier := makeMonikerIdentifier(NewPackageDataCache(), p, obj); identifier != "" { t.Errorf("unexpected moniker identifier. want=%q have=%q", "", identifier) } } @@ -200,7 +200,7 @@ func TestMonikerIdentifierSignature(t *testing.T) { packages := getTestPackages(t) p, obj := findDefinitionByName(t, packages, "Doer") - if identifier := monikerIdentifier(NewPackageDataCache(), p, obj); identifier != "TestStruct.Doer" { + if identifier := makeMonikerIdentifier(NewPackageDataCache(), p, obj); identifier != "TestStruct.Doer" { t.Errorf("unexpected moniker identifier. want=%q have=%q", "TestStruct.Doer", identifier) } } @@ -209,7 +209,7 @@ func TestMonikerIdentifierField(t *testing.T) { packages := getTestPackages(t) p, obj := findDefinitionByName(t, packages, "NestedB") - if identifier := monikerIdentifier(NewPackageDataCache(), p, obj); identifier != "TestStruct.FieldWithAnonymousType.NestedB" { + if identifier := makeMonikerIdentifier(NewPackageDataCache(), p, obj); identifier != "TestStruct.FieldWithAnonymousType.NestedB" { t.Errorf("unexpected moniker identifier. want=%q have=%q", "TestStruct.FieldWithAnonymousType.NestedB", identifier) } } @@ -218,7 +218,7 @@ func TestMonikerEmbeddedField(t *testing.T) { packages := getTestPackages(t) p, obj := findDefinitionByName(t, packages, "InnerStruct") - if identifier := monikerIdentifier(NewPackageDataCache(), p, obj); identifier != "ShellStruct.InnerStruct" { + if identifier := makeMonikerIdentifier(NewPackageDataCache(), p, obj); identifier != "ShellStruct.InnerStruct" { t.Errorf("unexpected moniker identifier. want=%q have=%q", "ShellStruct.InnerStruct", identifier) } } diff --git a/scripts/gen_stdlib_map.sh b/scripts/gen_stdlib_map.sh new file mode 100755 index 00000000..4b0625d3 --- /dev/null +++ b/scripts/gen_stdlib_map.sh @@ -0,0 +1,29 @@ +#!/bin/bash + + +cat > ./internal/gomod/stdlib.go << EOT +// THIS FILE IS GENERATED. SEE ./scripts/gen_stdlib_map.sh +EOT + +echo "// Generated by: $(go version)" >> ./internal/gomod/stdlib.go + +cat >> ./internal/gomod/stdlib.go << EOT +package gomod + +// isStandardlibPackge determines whether a package is in the standard library +// or not. At this point, it checks whether the package name is one of those +// that is found from running "go list std" in the latest released go version. +func isStandardlibPackge(pkg string) bool { + _, ok := standardLibraryMap[pkg] + return ok +} + +var contained = struct{}{} + +// This list is calculated from "go list std". +var standardLibraryMap = map[string]interface{}{ +EOT +go list std | awk '{ print "\""$0"\": contained,"}' >> ./internal/gomod/stdlib.go +echo "}" >> ./internal/gomod/stdlib.go + +go fmt ./internal/gomod/stdlib.go