summaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/tools/internal/modindex
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/tools/internal/modindex')
-rw-r--r--vendor/golang.org/x/tools/internal/modindex/directories.go131
-rw-r--r--vendor/golang.org/x/tools/internal/modindex/index.go287
-rw-r--r--vendor/golang.org/x/tools/internal/modindex/lookup.go178
-rw-r--r--vendor/golang.org/x/tools/internal/modindex/modindex.go119
-rw-r--r--vendor/golang.org/x/tools/internal/modindex/symbols.go244
5 files changed, 0 insertions, 959 deletions
diff --git a/vendor/golang.org/x/tools/internal/modindex/directories.go b/vendor/golang.org/x/tools/internal/modindex/directories.go
deleted file mode 100644
index 9a963744b..000000000
--- a/vendor/golang.org/x/tools/internal/modindex/directories.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2024 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package modindex
-
-import (
- "fmt"
- "log"
- "os"
- "path/filepath"
- "regexp"
- "strings"
- "sync"
- "time"
-
- "golang.org/x/mod/semver"
- "golang.org/x/tools/internal/gopathwalk"
-)
-
-type directory struct {
- path string // relative to GOMODCACHE
- importPath string
- version string // semantic version
-}
-
-// bestDirByImportPath returns the best directory for each import
-// path, where "best" means most recent semantic version. These import
-// paths are inferred from the GOMODCACHE-relative dir names in dirs.
-func bestDirByImportPath(dirs []string) (map[string]directory, error) {
- dirsByPath := make(map[string]directory)
- for _, dir := range dirs {
- importPath, version, err := dirToImportPathVersion(dir)
- if err != nil {
- return nil, err
- }
- new := directory{
- path: dir,
- importPath: importPath,
- version: version,
- }
- if old, ok := dirsByPath[importPath]; !ok || compareDirectory(new, old) < 0 {
- dirsByPath[importPath] = new
- }
- }
- return dirsByPath, nil
-}
-
-// compareDirectory defines an ordering of path@version directories,
-// by descending version, then by ascending path.
-func compareDirectory(x, y directory) int {
- if sign := -semver.Compare(x.version, y.version); sign != 0 {
- return sign // latest first
- }
- return strings.Compare(string(x.path), string(y.path))
-}
-
-// modCacheRegexp splits a relpathpath into module, module version, and package.
-var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
-
-// dirToImportPathVersion computes import path and semantic version
-// from a GOMODCACHE-relative directory name.
-func dirToImportPathVersion(dir string) (string, string, error) {
- m := modCacheRegexp.FindStringSubmatch(string(dir))
- // m[1] is the module path
- // m[2] is the version major.minor.patch(-<pre release identifier)
- // m[3] is the rest of the package path
- if len(m) != 4 {
- return "", "", fmt.Errorf("bad dir %s", dir)
- }
- if !semver.IsValid(m[2]) {
- return "", "", fmt.Errorf("bad semantic version %s", m[2])
- }
- // ToSlash is required to convert Windows file paths
- // into Go package import paths.
- return filepath.ToSlash(m[1] + m[3]), m[2], nil
-}
-
-// findDirs returns an unordered list of relevant package directories,
-// relative to the specified module cache root. The result includes only
-// module dirs whose mtime is within (start, end).
-func findDirs(root string, start, end time.Time) []string {
- var (
- resMu sync.Mutex
- res []string
- )
-
- addDir := func(root gopathwalk.Root, dir string) {
- // TODO(pjw): do we need to check times?
- resMu.Lock()
- defer resMu.Unlock()
- res = append(res, relative(root.Path, dir))
- }
-
- skipDir := func(_ gopathwalk.Root, dir string) bool {
- // The cache directory is already ignored in gopathwalk.
- if filepath.Base(dir) == "internal" {
- return true
- }
-
- // Skip toolchains.
- if strings.Contains(dir, "toolchain@") {
- return true
- }
-
- // Don't look inside @ directories that are too old/new.
- if strings.Contains(filepath.Base(dir), "@") {
- st, err := os.Stat(dir)
- if err != nil {
- log.Printf("can't stat dir %s %v", dir, err)
- return true
- }
- mtime := st.ModTime()
- return mtime.Before(start) || mtime.After(end)
- }
-
- return false
- }
-
- // TODO(adonovan): parallelize this. Even with a hot buffer cache,
- // find $(go env GOMODCACHE) -type d
- // can easily take up a minute.
- roots := []gopathwalk.Root{{Path: root, Type: gopathwalk.RootModuleCache}}
- gopathwalk.WalkSkip(roots, addDir, skipDir, gopathwalk.Options{
- ModulesEnabled: true,
- Concurrency: 1, // TODO(pjw): adjust concurrency
- // Logf: log.Printf,
- })
-
- return res
-}
diff --git a/vendor/golang.org/x/tools/internal/modindex/index.go b/vendor/golang.org/x/tools/internal/modindex/index.go
deleted file mode 100644
index c41d1dd90..000000000
--- a/vendor/golang.org/x/tools/internal/modindex/index.go
+++ /dev/null
@@ -1,287 +0,0 @@
-// Copyright 2024 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package modindex
-
-import (
- "bufio"
- "crypto/sha256"
- "encoding/csv"
- "fmt"
- "io"
- "log"
- "os"
- "path/filepath"
- "strconv"
- "strings"
- "testing"
- "time"
-)
-
-/*
-The on-disk index ("payload") is a text file.
-The first 3 lines are header information containing CurrentVersion,
-the value of GOMODCACHE, and the validity date of the index.
-(This is when the code started building the index.)
-Following the header are sections of lines, one section for each
-import path. These sections are sorted by package name.
-The first line of each section, marked by a leading :, contains
-the package name, the import path, the name of the directory relative
-to GOMODCACHE, and its semantic version.
-The rest of each section consists of one line per exported symbol.
-The lines are sorted by the symbol's name and contain the name,
-an indication of its lexical type (C, T, V, F), and if it is the
-name of a function, information about the signature.
-
-The fields in the section header lines are separated by commas, and
-in the unlikely event this would be confusing, the csv package is used
-to write (and read) them.
-
-In the lines containing exported names, C=const, V=var, T=type, F=func.
-If it is a func, the next field is the number of returned values,
-followed by pairs consisting of formal parameter names and types.
-All these fields are separated by spaces. Any spaces in a type
-(e.g., chan struct{}) are replaced by $s on the disk. The $s are
-turned back into spaces when read.
-
-Here is an index header (the comments are not part of the index):
-0 // version (of the index format)
-/usr/local/google/home/pjw/go/pkg/mod // GOMODCACHE
-2024-09-11 18:55:09 // validity date of the index
-
-Here is an index section:
-:yaml,gopkg.in/yaml.v1,gopkg.in/yaml.v1@v1.0.0-20140924161607-9f9df34309c0,v1.0.0-20140924161607-9f9df34309c0
-Getter T
-Marshal F 2 in interface{}
-Setter T
-Unmarshal F 1 in []byte out interface{}
-
-The package name is yaml, the import path is gopkg.in/yaml.v1.
-Getter and Setter are types, and Marshal and Unmarshal are functions.
-The latter returns one value and has two arguments, 'in' and 'out'
-whose types are []byte and interface{}.
-*/
-
-// CurrentVersion tells readers about the format of the index.
-const CurrentVersion int = 0
-
-// Index is returned by [Read].
-type Index struct {
- Version int
- GOMODCACHE string // absolute path of Go module cache dir
- ValidAt time.Time // moment at which the index was up to date
- Entries []Entry
-}
-
-func (ix *Index) String() string {
- return fmt.Sprintf("Index(%s v%d has %d entries at %v)",
- ix.GOMODCACHE, ix.Version, len(ix.Entries), ix.ValidAt)
-}
-
-// An Entry contains information for an import path.
-type Entry struct {
- Dir string // package directory relative to GOMODCACHE; uses OS path separator
- ImportPath string
- PkgName string
- Version string
- Names []string // exported names and information
-}
-
-// IndexDir is where the module index is stored.
-// Each logical index entry consists of a pair of files:
-//
-// - the "payload" (index-VERSION-XXX), whose name is
-// randomized, holds the actual index; and
-// - the "link" (index-name-VERSION-HASH),
-// whose name is predictable, contains the
-// name of the payload file.
-//
-// Since the link file is small (<512B),
-// reads and writes to it may be assumed atomic.
-var IndexDir string = func() string {
- var dir string
- if testing.Testing() {
- dir = os.TempDir()
- } else {
- var err error
- dir, err = os.UserCacheDir()
- // shouldn't happen, but TempDir is better than
- // creating ./go/imports
- if err != nil {
- dir = os.TempDir()
- }
- }
- dir = filepath.Join(dir, "goimports")
- if err := os.MkdirAll(dir, 0777); err != nil {
- log.Printf("failed to create modcache index dir: %v", err)
- }
- return dir
-}()
-
-// Read reads the latest version of the on-disk index
-// for the specified Go module cache directory.
-// If there is no index, it returns a nil Index and an fs.ErrNotExist error.
-func Read(gomodcache string) (*Index, error) {
- gomodcache, err := filepath.Abs(gomodcache)
- if err != nil {
- return nil, err
- }
-
- // Read the "link" file for the specified gomodcache directory.
- // It names the payload file.
- content, err := os.ReadFile(filepath.Join(IndexDir, linkFileBasename(gomodcache)))
- if err != nil {
- return nil, err
- }
- payloadFile := filepath.Join(IndexDir, string(content))
-
- // Read the index out of the payload file.
- f, err := os.Open(payloadFile)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- return readIndexFrom(gomodcache, bufio.NewReader(f))
-}
-
-func readIndexFrom(gomodcache string, r io.Reader) (*Index, error) {
- scan := bufio.NewScanner(r)
-
- // version
- if !scan.Scan() {
- return nil, fmt.Errorf("unexpected scan error: %v", scan.Err())
- }
- version, err := strconv.Atoi(scan.Text())
- if err != nil {
- return nil, err
- }
- if version != CurrentVersion {
- return nil, fmt.Errorf("got version %d, expected %d", version, CurrentVersion)
- }
-
- // gomodcache
- if !scan.Scan() {
- return nil, fmt.Errorf("scanner error reading module cache dir: %v", scan.Err())
- }
- // TODO(pjw): need to check that this is the expected cache dir
- // so the tag should be passed in to this function
- if dir := string(scan.Text()); dir != gomodcache {
- return nil, fmt.Errorf("index file GOMODCACHE mismatch: got %q, want %q", dir, gomodcache)
- }
-
- // changed
- if !scan.Scan() {
- return nil, fmt.Errorf("scanner error reading index creation time: %v", scan.Err())
- }
- changed, err := time.ParseInLocation(time.DateTime, scan.Text(), time.Local)
- if err != nil {
- return nil, err
- }
-
- // entries
- var (
- curEntry *Entry
- entries []Entry
- )
- for scan.Scan() {
- v := scan.Text()
- if v[0] == ':' {
- if curEntry != nil {
- entries = append(entries, *curEntry)
- }
- // as directories may contain commas and quotes, they need to be read as csv.
- rdr := strings.NewReader(v[1:])
- cs := csv.NewReader(rdr)
- flds, err := cs.Read()
- if err != nil {
- return nil, err
- }
- if len(flds) != 4 {
- return nil, fmt.Errorf("header contains %d fields, not 4: %q", len(v), v)
- }
- curEntry = &Entry{
- PkgName: flds[0],
- ImportPath: flds[1],
- Dir: relative(gomodcache, flds[2]),
- Version: flds[3],
- }
- continue
- }
- curEntry.Names = append(curEntry.Names, v)
- }
- if err := scan.Err(); err != nil {
- return nil, fmt.Errorf("scanner failed while reading modindex entry: %v", err)
- }
- if curEntry != nil {
- entries = append(entries, *curEntry)
- }
-
- return &Index{
- Version: version,
- GOMODCACHE: gomodcache,
- ValidAt: changed,
- Entries: entries,
- }, nil
-}
-
-// write writes the index file and updates the index directory to refer to it.
-func write(gomodcache string, ix *Index) error {
- // Write the index into a payload file with a fresh name.
- f, err := os.CreateTemp(IndexDir, fmt.Sprintf("index-%d-*", CurrentVersion))
- if err != nil {
- return err // e.g. disk full, or index dir deleted
- }
- if err := writeIndexToFile(ix, bufio.NewWriter(f)); err != nil {
- _ = f.Close() // ignore error
- return err
- }
- if err := f.Close(); err != nil {
- return err
- }
-
- // Write the name of the payload file into a link file.
- indexDirFile := filepath.Join(IndexDir, linkFileBasename(gomodcache))
- content := []byte(filepath.Base(f.Name()))
- return os.WriteFile(indexDirFile, content, 0666)
-}
-
-func writeIndexToFile(x *Index, w *bufio.Writer) error {
- fmt.Fprintf(w, "%d\n", x.Version)
- fmt.Fprintf(w, "%s\n", x.GOMODCACHE)
- tm := x.ValidAt.Truncate(time.Second) // round the time down
- fmt.Fprintf(w, "%s\n", tm.Format(time.DateTime))
- for _, e := range x.Entries {
- if e.ImportPath == "" {
- continue // shouldn't happen
- }
- // PJW: maybe always write these headers as csv?
- if strings.ContainsAny(string(e.Dir), ",\"") {
- cw := csv.NewWriter(w)
- cw.Write([]string{":" + e.PkgName, e.ImportPath, string(e.Dir), e.Version})
- cw.Flush()
- } else {
- fmt.Fprintf(w, ":%s,%s,%s,%s\n", e.PkgName, e.ImportPath, e.Dir, e.Version)
- }
- for _, x := range e.Names {
- fmt.Fprintf(w, "%s\n", x)
- }
- }
- return w.Flush()
-}
-
-// linkFileBasename returns the base name of the link file in the
-// index directory that holds the name of the payload file for the
-// specified (absolute) Go module cache dir.
-func linkFileBasename(gomodcache string) string {
- // Note: coupled to logic in ./gomodindex/cmd.go. TODO: factor.
- h := sha256.Sum256([]byte(gomodcache)) // collision-resistant hash
- return fmt.Sprintf("index-name-%d-%032x", CurrentVersion, h)
-}
-
-func relative(base, file string) string {
- if rel, err := filepath.Rel(base, file); err == nil {
- return rel
- }
- return file
-}
diff --git a/vendor/golang.org/x/tools/internal/modindex/lookup.go b/vendor/golang.org/x/tools/internal/modindex/lookup.go
deleted file mode 100644
index 0c011a99b..000000000
--- a/vendor/golang.org/x/tools/internal/modindex/lookup.go
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2024 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package modindex
-
-import (
- "slices"
- "strconv"
- "strings"
-)
-
-type Candidate struct {
- PkgName string
- Name string
- Dir string
- ImportPath string
- Type LexType
- Deprecated bool
- // information for Funcs
- Results int16 // how many results
- Sig []Field // arg names and types
-}
-
-type Field struct {
- Arg, Type string
-}
-
-type LexType int8
-
-const (
- Const LexType = iota
- Var
- Type
- Func
-)
-
-// LookupAll only returns those Candidates whose import path
-// finds all the names.
-func (ix *Index) LookupAll(pkgName string, names ...string) map[string][]Candidate {
- // this can be made faster when benchmarks show that it needs to be
- names = uniquify(names)
- byImpPath := make(map[string][]Candidate)
- for _, nm := range names {
- cands := ix.Lookup(pkgName, nm, false)
- for _, c := range cands {
- byImpPath[c.ImportPath] = append(byImpPath[c.ImportPath], c)
- }
- }
- for k, v := range byImpPath {
- if len(v) != len(names) {
- delete(byImpPath, k)
- }
- }
- return byImpPath
-}
-
-// remove duplicates
-func uniquify(in []string) []string {
- if len(in) == 0 {
- return in
- }
- in = slices.Clone(in)
- slices.Sort(in)
- return slices.Compact(in)
-}
-
-// Lookup finds all the symbols in the index with the given PkgName and name.
-// If prefix is true, it finds all of these with name as a prefix.
-func (ix *Index) Lookup(pkgName, name string, prefix bool) []Candidate {
- loc, ok := slices.BinarySearchFunc(ix.Entries, pkgName, func(e Entry, pkg string) int {
- return strings.Compare(e.PkgName, pkgName)
- })
- if !ok {
- return nil // didn't find the package
- }
- var ans []Candidate
- // loc is the first entry for this package name, but there may be several
- for i := loc; i < len(ix.Entries); i++ {
- e := ix.Entries[i]
- if e.PkgName != pkgName {
- break // end of sorted package names
- }
- nloc, ok := slices.BinarySearchFunc(e.Names, name, func(s string, name string) int {
- if strings.HasPrefix(s, name) {
- return 0
- }
- if s < name {
- return -1
- }
- return 1
- })
- if !ok {
- continue // didn't find the name, nor any symbols with name as a prefix
- }
- for j := nloc; j < len(e.Names); j++ {
- nstr := e.Names[j]
- // benchmarks show this makes a difference when there are a lot of Possibilities
- flds := fastSplit(nstr)
- if !(flds[0] == name || prefix && strings.HasPrefix(flds[0], name)) {
- // past range of matching Names
- break
- }
- if len(flds) < 2 {
- continue // should never happen
- }
- px := Candidate{
- PkgName: pkgName,
- Name: flds[0],
- Dir: string(e.Dir),
- ImportPath: e.ImportPath,
- Type: asLexType(flds[1][0]),
- Deprecated: len(flds[1]) > 1 && flds[1][1] == 'D',
- }
- if px.Type == Func {
- n, err := strconv.Atoi(flds[2])
- if err != nil {
- continue // should never happen
- }
- px.Results = int16(n)
- if len(flds) >= 4 {
- sig := strings.Split(flds[3], " ")
- for i := range sig {
- // $ cannot otherwise occur. removing the spaces
- // almost works, but for chan struct{}, e.g.
- sig[i] = strings.Replace(sig[i], "$", " ", -1)
- }
- px.Sig = toFields(sig)
- }
- }
- ans = append(ans, px)
- }
- }
- return ans
-}
-
-func toFields(sig []string) []Field {
- ans := make([]Field, len(sig)/2)
- for i := range ans {
- ans[i] = Field{Arg: sig[2*i], Type: sig[2*i+1]}
- }
- return ans
-}
-
-// benchmarks show this is measurably better than strings.Split
-// split into first 4 fields separated by single space
-func fastSplit(x string) []string {
- ans := make([]string, 0, 4)
- nxt := 0
- start := 0
- for i := 0; i < len(x); i++ {
- if x[i] != ' ' {
- continue
- }
- ans = append(ans, x[start:i])
- nxt++
- start = i + 1
- if nxt >= 3 {
- break
- }
- }
- ans = append(ans, x[start:])
- return ans
-}
-
-func asLexType(c byte) LexType {
- switch c {
- case 'C':
- return Const
- case 'V':
- return Var
- case 'T':
- return Type
- case 'F':
- return Func
- }
- return -1
-}
diff --git a/vendor/golang.org/x/tools/internal/modindex/modindex.go b/vendor/golang.org/x/tools/internal/modindex/modindex.go
deleted file mode 100644
index 5fa285d98..000000000
--- a/vendor/golang.org/x/tools/internal/modindex/modindex.go
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2024 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package modindex contains code for building and searching an
-// [Index] of the Go module cache.
-package modindex
-
-// The directory containing the index, returned by
-// [IndexDir], contains a file index-name-<ver> that contains the name
-// of the current index. We believe writing that short file is atomic.
-// [Read] reads that file to get the file name of the index.
-// WriteIndex writes an index with a unique name and then
-// writes that name into a new version of index-name-<ver>.
-// (<ver> stands for the CurrentVersion of the index format.)
-
-import (
- "maps"
- "os"
- "path/filepath"
- "slices"
- "strings"
- "time"
-
- "golang.org/x/mod/semver"
-)
-
-// Update updates the index for the specified Go
-// module cache directory, creating it as needed.
-// On success it returns the current index.
-func Update(gomodcache string) (*Index, error) {
- prev, err := Read(gomodcache)
- if err != nil {
- if !os.IsNotExist(err) {
- return nil, err
- }
- prev = nil
- }
- return update(gomodcache, prev)
-}
-
-// update builds, writes, and returns the current index.
-//
-// If old is nil, the new index is built from all of GOMODCACHE;
-// otherwise it is built from the old index plus cache updates
-// since the previous index's time.
-func update(gomodcache string, old *Index) (*Index, error) {
- gomodcache, err := filepath.Abs(gomodcache)
- if err != nil {
- return nil, err
- }
- new, changed, err := build(gomodcache, old)
- if err != nil {
- return nil, err
- }
- if old == nil || changed {
- if err := write(gomodcache, new); err != nil {
- return nil, err
- }
- }
- return new, nil
-}
-
-// build returns a new index for the specified Go module cache (an
-// absolute path).
-//
-// If an old index is provided, only directories more recent than it
-// that it are scanned; older directories are provided by the old
-// Index.
-//
-// The boolean result indicates whether new entries were found.
-func build(gomodcache string, old *Index) (*Index, bool, error) {
- // Set the time window.
- var start time.Time // = dawn of time
- if old != nil {
- start = old.ValidAt
- }
- now := time.Now()
- end := now.Add(24 * time.Hour) // safely in the future
-
- // Enumerate GOMODCACHE package directories.
- // Choose the best (latest) package for each import path.
- pkgDirs := findDirs(gomodcache, start, end)
- dirByPath, err := bestDirByImportPath(pkgDirs)
- if err != nil {
- return nil, false, err
- }
-
- // For each import path it might occur only in
- // dirByPath, only in old, or in both.
- // If both, use the semantically later one.
- var entries []Entry
- if old != nil {
- for _, entry := range old.Entries {
- dir, ok := dirByPath[entry.ImportPath]
- if !ok || semver.Compare(dir.version, entry.Version) <= 0 {
- // New dir is missing or not more recent; use old entry.
- entries = append(entries, entry)
- delete(dirByPath, entry.ImportPath)
- }
- }
- }
-
- // Extract symbol information for all the new directories.
- newEntries := extractSymbols(gomodcache, maps.Values(dirByPath))
- entries = append(entries, newEntries...)
- slices.SortFunc(entries, func(x, y Entry) int {
- if n := strings.Compare(x.PkgName, y.PkgName); n != 0 {
- return n
- }
- return strings.Compare(x.ImportPath, y.ImportPath)
- })
-
- return &Index{
- GOMODCACHE: gomodcache,
- ValidAt: now, // time before the directories were scanned
- Entries: entries,
- }, len(newEntries) > 0, nil
-}
diff --git a/vendor/golang.org/x/tools/internal/modindex/symbols.go b/vendor/golang.org/x/tools/internal/modindex/symbols.go
deleted file mode 100644
index 8e9702d84..000000000
--- a/vendor/golang.org/x/tools/internal/modindex/symbols.go
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright 2024 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package modindex
-
-import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "go/types"
- "iter"
- "os"
- "path/filepath"
- "runtime"
- "slices"
- "strings"
- "sync"
-
- "golang.org/x/sync/errgroup"
-)
-
-// The name of a symbol contains information about the symbol:
-// <name> T for types, TD if the type is deprecated
-// <name> C for consts, CD if the const is deprecated
-// <name> V for vars, VD if the var is deprecated
-// and for funcs: <name> F <num of return values> (<arg-name> <arg-type>)*
-// any spaces in <arg-type> are replaced by $s so that the fields
-// of the name are space separated. F is replaced by FD if the func
-// is deprecated.
-type symbol struct {
- pkg string // name of the symbols's package
- name string // declared name
- kind string // T, C, V, or F, followed by D if deprecated
- sig string // signature information, for F
-}
-
-// extractSymbols returns a (new, unordered) array of Entries, one for
-// each provided package directory, describing its exported symbols.
-func extractSymbols(cwd string, dirs iter.Seq[directory]) []Entry {
- var (
- mu sync.Mutex
- entries []Entry
- )
-
- var g errgroup.Group
- g.SetLimit(max(2, runtime.GOMAXPROCS(0)/2))
- for dir := range dirs {
- g.Go(func() error {
- thedir := filepath.Join(cwd, string(dir.path))
- mode := parser.SkipObjectResolution | parser.ParseComments
-
- // Parse all Go files in dir and extract symbols.
- dirents, err := os.ReadDir(thedir)
- if err != nil {
- return nil // log this someday?
- }
- var syms []symbol
- for _, dirent := range dirents {
- if !strings.HasSuffix(dirent.Name(), ".go") ||
- strings.HasSuffix(dirent.Name(), "_test.go") {
- continue
- }
- fname := filepath.Join(thedir, dirent.Name())
- tr, err := parser.ParseFile(token.NewFileSet(), fname, nil, mode)
- if err != nil {
- continue // ignore errors, someday log them?
- }
- syms = append(syms, getFileExports(tr)...)
- }
-
- // Create an entry for the package.
- pkg, names := processSyms(syms)
- if pkg != "" {
- mu.Lock()
- defer mu.Unlock()
- entries = append(entries, Entry{
- PkgName: pkg,
- Dir: dir.path,
- ImportPath: dir.importPath,
- Version: dir.version,
- Names: names,
- })
- }
-
- return nil
- })
- }
- g.Wait() // ignore error
-
- return entries
-}
-
-func getFileExports(f *ast.File) []symbol {
- pkg := f.Name.Name
- if pkg == "main" || pkg == "" {
- return nil
- }
- var ans []symbol
- // should we look for //go:build ignore?
- for _, decl := range f.Decls {
- switch decl := decl.(type) {
- case *ast.FuncDecl:
- if decl.Recv != nil {
- // ignore methods, as we are completing package selections
- continue
- }
- name := decl.Name.Name
- dtype := decl.Type
- // not looking at dtype.TypeParams. That is, treating
- // generic functions just like non-generic ones.
- sig := dtype.Params
- kind := "F"
- if isDeprecated(decl.Doc) {
- kind += "D"
- }
- result := []string{fmt.Sprintf("%d", dtype.Results.NumFields())}
- for _, x := range sig.List {
- // This code creates a string representing the type.
- // TODO(pjw): it may be fragile:
- // 1. x.Type could be nil, perhaps in ill-formed code
- // 2. ExprString might someday change incompatibly to
- // include struct tags, which can be arbitrary strings
- if x.Type == nil {
- // Can this happen without a parse error? (Files with parse
- // errors are ignored in getSymbols)
- continue // maybe report this someday
- }
- tp := types.ExprString(x.Type)
- if len(tp) == 0 {
- // Can this happen?
- continue // maybe report this someday
- }
- // This is only safe if ExprString never returns anything with a $
- // The only place a $ can occur seems to be in a struct tag, which
- // can be an arbitrary string literal, and ExprString does not presently
- // print struct tags. So for this to happen the type of a formal parameter
- // has to be a explicit struct, e.g. foo(x struct{a int "$"}) and ExprString
- // would have to show the struct tag. Even testing for this case seems
- // a waste of effort, but let's remember the possibility
- if strings.Contains(tp, "$") {
- continue
- }
- tp = strings.Replace(tp, " ", "$", -1)
- if len(x.Names) == 0 {
- result = append(result, "_")
- result = append(result, tp)
- } else {
- for _, y := range x.Names {
- result = append(result, y.Name)
- result = append(result, tp)
- }
- }
- }
- sigs := strings.Join(result, " ")
- if s := newsym(pkg, name, kind, sigs); s != nil {
- ans = append(ans, *s)
- }
- case *ast.GenDecl:
- depr := isDeprecated(decl.Doc)
- switch decl.Tok {
- case token.CONST, token.VAR:
- tp := "V"
- if decl.Tok == token.CONST {
- tp = "C"
- }
- if depr {
- tp += "D"
- }
- for _, sp := range decl.Specs {
- for _, x := range sp.(*ast.ValueSpec).Names {
- if s := newsym(pkg, x.Name, tp, ""); s != nil {
- ans = append(ans, *s)
- }
- }
- }
- case token.TYPE:
- tp := "T"
- if depr {
- tp += "D"
- }
- for _, sp := range decl.Specs {
- if s := newsym(pkg, sp.(*ast.TypeSpec).Name.Name, tp, ""); s != nil {
- ans = append(ans, *s)
- }
- }
- }
- }
- }
- return ans
-}
-
-func newsym(pkg, name, kind, sig string) *symbol {
- if len(name) == 0 || !ast.IsExported(name) {
- return nil
- }
- sym := symbol{pkg: pkg, name: name, kind: kind, sig: sig}
- return &sym
-}
-
-func isDeprecated(doc *ast.CommentGroup) bool {
- if doc == nil {
- return false
- }
- // go.dev/wiki/Deprecated Paragraph starting 'Deprecated:'
- // This code fails for /* Deprecated: */, but it's the code from
- // gopls/internal/analysis/deprecated
- for line := range strings.SplitSeq(doc.Text(), "\n\n") {
- if strings.HasPrefix(line, "Deprecated:") {
- return true
- }
- }
- return false
-}
-
-// return the package name and the value for the symbols.
-// if there are multiple packages, choose one arbitrarily
-// the returned slice is sorted lexicographically
-func processSyms(syms []symbol) (string, []string) {
- if len(syms) == 0 {
- return "", nil
- }
- slices.SortFunc(syms, func(l, r symbol) int {
- return strings.Compare(l.name, r.name)
- })
- pkg := syms[0].pkg
- var names []string
- for _, s := range syms {
- if s.pkg != pkg {
- // Symbols came from two files in same dir
- // with different package declarations.
- continue
- }
- var nx string
- if s.sig != "" {
- nx = fmt.Sprintf("%s %s %s", s.name, s.kind, s.sig)
- } else {
- nx = fmt.Sprintf("%s %s", s.name, s.kind)
- }
- names = append(names, nx)
- }
- return pkg, names
-}