diff options
Diffstat (limited to 'vendor/golang.org/x/tools/internal/modindex')
| -rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/directories.go | 131 | ||||
| -rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/index.go | 287 | ||||
| -rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/lookup.go | 178 | ||||
| -rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/modindex.go | 119 | ||||
| -rw-r--r-- | vendor/golang.org/x/tools/internal/modindex/symbols.go | 244 |
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 -} |
