diff options
author | 2025-01-14 13:11:20 +0000 | |
---|---|---|
committer | 2025-01-14 13:11:20 +0000 | |
commit | b95498b8c206295b55767fa2d4855bee16233734 (patch) | |
tree | f8b95d8b5bdbcc062915ae4b59c7a6488fe9da90 /vendor/golang.org/x/tools/internal/modindex | |
parent | [chore]: Bump github.com/gin-contrib/gzip from 1.0.1 to 1.1.0 (#3639) (diff) | |
download | gotosocial-b95498b8c206295b55767fa2d4855bee16233734.tar.xz |
[chore]: Bump mvdan.cc/xurls/v2 from 2.5.0 to 2.6.0 (#3643)
Bumps [mvdan.cc/xurls/v2](https://github.com/mvdan/xurls) from 2.5.0 to 2.6.0.
- [Release notes](https://github.com/mvdan/xurls/releases)
- [Commits](https://github.com/mvdan/xurls/compare/v2.5.0...v2.6.0)
---
updated-dependencies:
- dependency-name: mvdan.cc/xurls/v2
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Diffstat (limited to 'vendor/golang.org/x/tools/internal/modindex')
-rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/directories.go | 135 | ||||
-rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/index.go | 262 | ||||
-rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/lookup.go | 145 | ||||
-rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/modindex.go | 164 | ||||
-rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/symbols.go | 189 | ||||
-rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/types.go | 25 |
6 files changed, 920 insertions, 0 deletions
diff --git a/vendor/golang.org/x/tools/internal/modindex/directories.go b/vendor/golang.org/x/tools/internal/modindex/directories.go new file mode 100644 index 000000000..1e1a02f23 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/modindex/directories.go @@ -0,0 +1,135 @@ +// 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" + "slices" + "strings" + "sync" + "time" + + "golang.org/x/mod/semver" + "golang.org/x/tools/internal/gopathwalk" +) + +type directory struct { + path Relpath + importPath string + version string // semantic version + syms []symbol +} + +// filterDirs groups the directories by import path, +// sorting the ones with the same import path by semantic version, +// most recent first. +func byImportPath(dirs []Relpath) (map[string][]*directory, error) { + ans := make(map[string][]*directory) // key is import path + for _, d := range dirs { + ip, sv, err := DirToImportPathVersion(d) + if err != nil { + return nil, err + } + ans[ip] = append(ans[ip], &directory{ + path: d, + importPath: ip, + version: sv, + }) + } + for k, v := range ans { + semanticSort(v) + ans[k] = v + } + return ans, nil +} + +// sort the directories by semantic version, latest first +func semanticSort(v []*directory) { + slices.SortFunc(v, func(l, r *directory) int { + if n := semver.Compare(l.version, r.version); n != 0 { + return -n // latest first + } + return strings.Compare(string(l.path), string(r.path)) + }) +} + +// modCacheRegexp splits a relpathpath into module, module version, and package. +var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) + +// DirToImportPathVersion computes import path and semantic version +func DirToImportPathVersion(dir Relpath) (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 for Windows. + return filepath.ToSlash(m[1] + m[3]), m[2], nil +} + +// a region controls what directories to look at, for +// updating the index incrementally, and for testing that. +// (for testing one builds an index as of A, incrementally +// updates it to B, and compares the result to an index build +// as of B.) +type region struct { + onlyAfter, onlyBefore time.Time + sync.Mutex + ans []Relpath +} + +func findDirs(root string, onlyAfter, onlyBefore time.Time) []Relpath { + roots := []gopathwalk.Root{{Path: root, Type: gopathwalk.RootModuleCache}} + // TODO(PJW): adjust concurrency + opts := gopathwalk.Options{ModulesEnabled: true, Concurrency: 1 /* ,Logf: log.Printf*/} + betw := ®ion{ + onlyAfter: onlyAfter, + onlyBefore: onlyBefore, + } + gopathwalk.WalkSkip(roots, betw.addDir, betw.skipDir, opts) + return betw.ans +} + +func (r *region) addDir(rt gopathwalk.Root, dir string) { + // do we need to check times? + r.Lock() + defer r.Unlock() + x := filepath.ToSlash(string(toRelpath(Abspath(rt.Path), dir))) + r.ans = append(r.ans, toRelpath(Abspath(rt.Path), x)) +} + +func (r *region) skipDir(_ gopathwalk.Root, dir string) bool { + // The cache directory is already ignored in gopathwalk\ + if filepath.Base(dir) == "internal" { + return true + } + if strings.Contains(dir, "toolchain@") { + return true + } + // don't look inside @ directories that are too old + 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 + } + if st.ModTime().Before(r.onlyAfter) { + return true + } + if st.ModTime().After(r.onlyBefore) { + return true + } + } + return false +} diff --git a/vendor/golang.org/x/tools/internal/modindex/index.go b/vendor/golang.org/x/tools/internal/modindex/index.go new file mode 100644 index 000000000..27b6dd832 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/modindex/index.go @@ -0,0 +1,262 @@ +// 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" + "encoding/csv" + "errors" + "fmt" + "hash/crc64" + "io" + "io/fs" + "log" + "os" + "path/filepath" + "strconv" + "strings" + "time" +) + +/* +The on-disk index 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 ReadIndex(). +type Index struct { + Version int + Cachedir Abspath // The directory containing the module cache + Changed time.Time // The index is up to date as of Changed + Entries []Entry +} + +// An Entry contains information for an import path. +type Entry struct { + Dir Relpath // directory in modcache + ImportPath string + PkgName string + Version string + //ModTime STime // is this useful? + Names []string // exported names and information +} + +// ReadIndex reads the latest version of the on-disk index +// for the cache directory cd. +// It returns (nil, nil) if there is no index, but returns +// a non-nil error if the index exists but could not be read. +func ReadIndex(cachedir string) (*Index, error) { + cachedir, err := filepath.Abs(cachedir) + if err != nil { + return nil, err + } + cd := Abspath(cachedir) + dir, err := IndexDir() + if err != nil { + return nil, err + } + base := indexNameBase(cd) + iname := filepath.Join(dir, base) + buf, err := os.ReadFile(iname) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return nil, nil + } + return nil, fmt.Errorf("cannot read %s: %w", iname, err) + } + fname := filepath.Join(dir, string(buf)) + fd, err := os.Open(fname) + if err != nil { + return nil, err + } + defer fd.Close() + r := bufio.NewReader(fd) + ix, err := readIndexFrom(cd, r) + if err != nil { + return nil, err + } + return ix, nil +} + +func readIndexFrom(cd Abspath, bx io.Reader) (*Index, error) { + b := bufio.NewScanner(bx) + var ans Index + // header + ok := b.Scan() + if !ok { + return nil, fmt.Errorf("unexpected scan error") + } + l := b.Text() + var err error + ans.Version, err = strconv.Atoi(l) + if err != nil { + return nil, err + } + if ans.Version != CurrentVersion { + return nil, fmt.Errorf("got version %d, expected %d", ans.Version, CurrentVersion) + } + if ok := b.Scan(); !ok { + return nil, fmt.Errorf("scanner error reading cachedir") + } + ans.Cachedir = Abspath(b.Text()) + if ok := b.Scan(); !ok { + return nil, fmt.Errorf("scanner error reading index creation time") + } + // TODO(pjw): need to check that this is the expected cachedir + // so the tag should be passed in to this function + ans.Changed, err = time.ParseInLocation(time.DateTime, b.Text(), time.Local) + if err != nil { + return nil, err + } + var curEntry *Entry + for b.Scan() { + v := b.Text() + if v[0] == ':' { + if curEntry != nil { + ans.Entries = append(ans.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: toRelpath(cd, flds[2]), Version: flds[3]} + continue + } + curEntry.Names = append(curEntry.Names, v) + } + if curEntry != nil { + ans.Entries = append(ans.Entries, *curEntry) + } + if err := b.Err(); err != nil { + return nil, fmt.Errorf("scanner failed %v", err) + } + return &ans, nil +} + +// write the index as a text file +func writeIndex(cachedir Abspath, ix *Index) error { + dir, err := IndexDir() + if err != nil { + return err + } + ipat := fmt.Sprintf("index-%d-*", CurrentVersion) + fd, err := os.CreateTemp(dir, ipat) + if err != nil { + return err // can this happen? + } + defer fd.Close() + if err := writeIndexToFile(ix, fd); err != nil { + return err + } + content := fd.Name() + content = filepath.Base(content) + base := indexNameBase(cachedir) + nm := filepath.Join(dir, base) + err = os.WriteFile(nm, []byte(content), 0666) + if err != nil { + return err + } + return nil +} + +func writeIndexToFile(x *Index, fd *os.File) error { + cnt := 0 + w := bufio.NewWriter(fd) + fmt.Fprintf(w, "%d\n", x.Version) + fmt.Fprintf(w, "%s\n", x.Cachedir) + // round the time down + tm := x.Changed.Add(-time.Second / 2) + 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), ",\"") { + log.Printf("DIR: %s", 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) + cnt++ + } + } + if err := w.Flush(); err != nil { + return err + } + return nil +} + +// tests can override this +var IndexDir = indexDir + +// IndexDir computes the directory containing the index +func indexDir() (string, error) { + dir, err := os.UserCacheDir() + if err != nil { + return "", fmt.Errorf("cannot open UserCacheDir, %w", err) + } + return filepath.Join(dir, "go", "imports"), nil +} + +// return the base name of the file containing the name of the current index +func indexNameBase(cachedir Abspath) string { + // crc64 is a way to convert path names into 16 hex digits. + h := crc64.Checksum([]byte(cachedir), crc64.MakeTable(crc64.ECMA)) + fname := fmt.Sprintf("index-name-%d-%016x", CurrentVersion, h) + return fname +} diff --git a/vendor/golang.org/x/tools/internal/modindex/lookup.go b/vendor/golang.org/x/tools/internal/modindex/lookup.go new file mode 100644 index 000000000..29d4e3d7a --- /dev/null +++ b/vendor/golang.org/x/tools/internal/modindex/lookup.go @@ -0,0 +1,145 @@ +// 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 + // 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 +) + +// 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(pkg, name string, prefix bool) []Candidate { + loc, ok := slices.BinarySearchFunc(ix.Entries, pkg, func(e Entry, pkg string) int { + return strings.Compare(e.PkgName, pkg) + }) + 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 severeal + for i := loc; i < len(ix.Entries); i++ { + e := ix.Entries[i] + if e.PkgName != pkg { + 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: pkg, + Name: flds[0], + Dir: string(e.Dir), + ImportPath: e.ImportPath, + Type: asLexType(flds[1][0]), + } + if flds[1] == "F" { + 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 := 0; i < len(sig); i++ { + // $ 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 := 0; i < len(ans); i++ { + ans[i] = Field{Arg: sig[2*i], Type: sig[2*i+1]} + } + return ans +} + +// benchmarks show this is measurably better than strings.Split +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 new file mode 100644 index 000000000..355a53e71 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/modindex/modindex.go @@ -0,0 +1,164 @@ +// 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 to +// the Go module cache. 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. +// ReadIndex 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.) +package modindex + +import ( + "path/filepath" + "slices" + "strings" + "time" + + "golang.org/x/mod/semver" +) + +// Create always creates a new index for the go module cache that is in cachedir. +func Create(cachedir string) error { + _, err := indexModCache(cachedir, true) + return err +} + +// Update the index for the go module cache that is in cachedir, +// If there is no existing index it will build one. +// If there are changed directories since the last index, it will +// write a new one and return true. Otherwise it returns false. +func Update(cachedir string) (bool, error) { + return indexModCache(cachedir, false) +} + +// indexModCache writes an index current as of when it is called. +// If clear is true the index is constructed from all of GOMODCACHE +// otherwise the index is constructed from the last previous index +// and the updates to the cache. It returns true if it wrote an index, +// false otherwise. +func indexModCache(cachedir string, clear bool) (bool, error) { + cachedir, err := filepath.Abs(cachedir) + if err != nil { + return false, err + } + cd := Abspath(cachedir) + future := time.Now().Add(24 * time.Hour) // safely in the future + ok, err := modindexTimed(future, cd, clear) + if err != nil { + return false, err + } + return ok, nil +} + +// modindexTimed writes an index current as of onlyBefore. +// If clear is true the index is constructed from all of GOMODCACHE +// otherwise the index is constructed from the last previous index +// and all the updates to the cache before onlyBefore. +// It returns true if it wrote a new index, false if it wrote nothing. +func modindexTimed(onlyBefore time.Time, cachedir Abspath, clear bool) (bool, error) { + var curIndex *Index + if !clear { + var err error + curIndex, err = ReadIndex(string(cachedir)) + if clear && err != nil { + return false, err + } + // TODO(pjw): check that most of those directories still exist + } + cfg := &work{ + onlyBefore: onlyBefore, + oldIndex: curIndex, + cacheDir: cachedir, + } + if curIndex != nil { + cfg.onlyAfter = curIndex.Changed + } + if err := cfg.buildIndex(); err != nil { + return false, err + } + if len(cfg.newIndex.Entries) == 0 && curIndex != nil { + // no changes from existing curIndex, don't write a new index + return false, nil + } + if err := cfg.writeIndex(); err != nil { + return false, err + } + return true, nil +} + +type work struct { + onlyBefore time.Time // do not use directories later than this + onlyAfter time.Time // only interested in directories after this + // directories from before onlyAfter come from oldIndex + oldIndex *Index + newIndex *Index + cacheDir Abspath +} + +func (w *work) buildIndex() error { + // The effective date of the new index should be at least + // slightly earlier than when the directories are scanned + // so set it now. + w.newIndex = &Index{Changed: time.Now(), Cachedir: w.cacheDir} + dirs := findDirs(string(w.cacheDir), w.onlyAfter, w.onlyBefore) + if len(dirs) == 0 { + return nil + } + newdirs, err := byImportPath(dirs) + if err != nil { + return err + } + // for each import path it might occur only in newdirs, + // only in w.oldIndex, or in both. + // If it occurs in both, use the semantically later one + if w.oldIndex != nil { + for _, e := range w.oldIndex.Entries { + found, ok := newdirs[e.ImportPath] + if !ok { + w.newIndex.Entries = append(w.newIndex.Entries, e) + continue // use this one, there is no new one + } + if semver.Compare(found[0].version, e.Version) > 0 { + // use the new one + } else { + // use the old one, forget the new one + w.newIndex.Entries = append(w.newIndex.Entries, e) + delete(newdirs, e.ImportPath) + } + } + } + // get symbol information for all the new diredtories + getSymbols(w.cacheDir, newdirs) + // assemble the new index entries + for k, v := range newdirs { + d := v[0] + pkg, names := processSyms(d.syms) + if pkg == "" { + continue // PJW: does this ever happen? + } + entry := Entry{ + PkgName: pkg, + Dir: d.path, + ImportPath: k, + Version: d.version, + Names: names, + } + w.newIndex.Entries = append(w.newIndex.Entries, entry) + } + // sort the entries in the new index + slices.SortFunc(w.newIndex.Entries, func(l, r Entry) int { + if n := strings.Compare(l.PkgName, r.PkgName); n != 0 { + return n + } + return strings.Compare(l.ImportPath, r.ImportPath) + }) + return nil +} + +func (w *work) writeIndex() error { + return writeIndex(w.cacheDir, w.newIndex) +} diff --git a/vendor/golang.org/x/tools/internal/modindex/symbols.go b/vendor/golang.org/x/tools/internal/modindex/symbols.go new file mode 100644 index 000000000..2e285ed99 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/modindex/symbols.go @@ -0,0 +1,189 @@ +// 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" + "os" + "path/filepath" + "slices" + "strings" + + "golang.org/x/sync/errgroup" +) + +// The name of a symbol contains information about the symbol: +// <name> T for types +// <name> C for consts +// <name> V for vars +// 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 +type symbol struct { + pkg string // name of the symbols's package + name string // declared name + kind string // T, C, V, or F + sig string // signature information, for F +} + +// find the symbols for the best directories +func getSymbols(cd Abspath, dirs map[string][]*directory) { + var g errgroup.Group + g.SetLimit(-1) // maybe throttle this some day + for _, vv := range dirs { + // throttling some day? + d := vv[0] + g.Go(func() error { + thedir := filepath.Join(string(cd), string(d.path)) + mode := parser.SkipObjectResolution + + fi, err := os.ReadDir(thedir) + if err != nil { + return nil // log this someday? + } + for _, fx := range fi { + if !strings.HasSuffix(fx.Name(), ".go") || strings.HasSuffix(fx.Name(), "_test.go") { + continue + } + fname := filepath.Join(thedir, fx.Name()) + tr, err := parser.ParseFile(token.NewFileSet(), fname, nil, mode) + if err != nil { + continue // ignore errors, someday log them? + } + d.syms = append(d.syms, getFileExports(tr)...) + } + return nil + }) + } + g.Wait() +} + +func getFileExports(f *ast.File) []symbol { + pkg := f.Name.Name + if pkg == "main" { + 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" + 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 explict 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 not ignore such pathologies + 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: + switch decl.Tok { + case token.CONST, token.VAR: + tp := "V" + if decl.Tok == token.CONST { + tp = "C" + } + 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: + for _, sp := range decl.Specs { + if s := newsym(pkg, sp.(*ast.TypeSpec).Name.Name, "T", ""); 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 +} + +// 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 { + var nx string + if s.pkg == pkg { + 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) + } else { + continue // PJW: do we want to keep track of these? + } + } + return pkg, names +} diff --git a/vendor/golang.org/x/tools/internal/modindex/types.go b/vendor/golang.org/x/tools/internal/modindex/types.go new file mode 100644 index 000000000..ece448863 --- /dev/null +++ b/vendor/golang.org/x/tools/internal/modindex/types.go @@ -0,0 +1,25 @@ +// 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 ( + "strings" +) + +// some special types to avoid confusions + +// distinguish various types of directory names. It's easy to get confused. +type Abspath string // absolute paths +type Relpath string // paths with GOMODCACHE prefix removed + +func toRelpath(cachedir Abspath, s string) Relpath { + if strings.HasPrefix(s, string(cachedir)) { + if s == string(cachedir) { + return Relpath("") + } + return Relpath(s[len(cachedir)+1:]) + } + return Relpath(s) +} |