Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / godoc / versions.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // This file caches information about which standard library types, methods,
6 // and functions appeared in what version of Go
7
8 package godoc
9
10 import (
11         "bufio"
12         "go/build"
13         "log"
14         "os"
15         "path/filepath"
16         "strings"
17         "unicode"
18 )
19
20 // apiVersions is a map of packages to information about those packages'
21 // symbols and when they were added to Go.
22 //
23 // Only things added after Go1 are tracked. Version strings are of the
24 // form "1.1", "1.2", etc.
25 type apiVersions map[string]pkgAPIVersions // keyed by Go package ("net/http")
26
27 // pkgAPIVersions contains information about which version of Go added
28 // certain package symbols.
29 //
30 // Only things added after Go1 are tracked. Version strings are of the
31 // form "1.1", "1.2", etc.
32 type pkgAPIVersions struct {
33         typeSince   map[string]string            // "Server" -> "1.7"
34         methodSince map[string]map[string]string // "*Server" ->"Shutdown"->1.8
35         funcSince   map[string]string            // "NewServer" -> "1.7"
36         fieldSince  map[string]map[string]string // "ClientTrace" -> "Got1xxResponse" -> "1.11"
37 }
38
39 // sinceVersionFunc returns a string (such as "1.7") specifying which Go
40 // version introduced a symbol, unless it was introduced in Go1, in
41 // which case it returns the empty string.
42 //
43 // The kind is one of "type", "method", or "func".
44 //
45 // The receiver is only used for "methods" and specifies the receiver type,
46 // such as "*Server".
47 //
48 // The name is the symbol name ("Server") and the pkg is the package
49 // ("net/http").
50 func (v apiVersions) sinceVersionFunc(kind, receiver, name, pkg string) string {
51         pv := v[pkg]
52         switch kind {
53         case "func":
54                 return pv.funcSince[name]
55         case "type":
56                 return pv.typeSince[name]
57         case "method":
58                 return pv.methodSince[receiver][name]
59         }
60         return ""
61 }
62
63 // versionedRow represents an API feature, a parsed line of a
64 // $GOROOT/api/go.*txt file.
65 type versionedRow struct {
66         pkg        string // "net/http"
67         kind       string // "type", "func", "method", "field" TODO: "const", "var"
68         recv       string // for methods, the receiver type ("Server", "*Server")
69         name       string // name of type, (struct) field, func, method
70         structName string // for struct fields, the outer struct name
71 }
72
73 // versionParser parses $GOROOT/api/go*.txt files and stores them in in its rows field.
74 type versionParser struct {
75         res apiVersions // initialized lazily
76 }
77
78 func (vp *versionParser) parseFile(name string) error {
79         base := filepath.Base(name)
80         ver := strings.TrimPrefix(strings.TrimSuffix(base, ".txt"), "go")
81         if ver == "1" {
82                 return nil
83         }
84         f, err := os.Open(name)
85         if err != nil {
86                 return err
87         }
88         defer f.Close()
89
90         sc := bufio.NewScanner(f)
91         for sc.Scan() {
92                 row, ok := parseRow(sc.Text())
93                 if !ok {
94                         continue
95                 }
96                 if vp.res == nil {
97                         vp.res = make(apiVersions)
98                 }
99                 pkgi, ok := vp.res[row.pkg]
100                 if !ok {
101                         pkgi = pkgAPIVersions{
102                                 typeSince:   make(map[string]string),
103                                 methodSince: make(map[string]map[string]string),
104                                 funcSince:   make(map[string]string),
105                                 fieldSince:  make(map[string]map[string]string),
106                         }
107                         vp.res[row.pkg] = pkgi
108                 }
109                 switch row.kind {
110                 case "func":
111                         pkgi.funcSince[row.name] = ver
112                 case "type":
113                         pkgi.typeSince[row.name] = ver
114                 case "method":
115                         if _, ok := pkgi.methodSince[row.recv]; !ok {
116                                 pkgi.methodSince[row.recv] = make(map[string]string)
117                         }
118                         pkgi.methodSince[row.recv][row.name] = ver
119                 case "field":
120                         if _, ok := pkgi.fieldSince[row.structName]; !ok {
121                                 pkgi.fieldSince[row.structName] = make(map[string]string)
122                         }
123                         pkgi.fieldSince[row.structName][row.name] = ver
124                 }
125         }
126         return sc.Err()
127 }
128
129 func parseRow(s string) (vr versionedRow, ok bool) {
130         if !strings.HasPrefix(s, "pkg ") {
131                 // Skip comments, blank lines, etc.
132                 return
133         }
134         rest := s[len("pkg "):]
135         endPkg := strings.IndexFunc(rest, func(r rune) bool { return !(unicode.IsLetter(r) || r == '/' || unicode.IsDigit(r)) })
136         if endPkg == -1 {
137                 return
138         }
139         vr.pkg, rest = rest[:endPkg], rest[endPkg:]
140         if !strings.HasPrefix(rest, ", ") {
141                 // If the part after the pkg name isn't ", ", then it's a OS/ARCH-dependent line of the form:
142                 //   pkg syscall (darwin-amd64), const ImplementsGetwd = false
143                 // We skip those for now.
144                 return
145         }
146         rest = rest[len(", "):]
147
148         switch {
149         case strings.HasPrefix(rest, "type "):
150                 rest = rest[len("type "):]
151                 sp := strings.IndexByte(rest, ' ')
152                 if sp == -1 {
153                         return
154                 }
155                 vr.name, rest = rest[:sp], rest[sp+1:]
156                 if !strings.HasPrefix(rest, "struct, ") {
157                         vr.kind = "type"
158                         return vr, true
159                 }
160                 rest = rest[len("struct, "):]
161                 if i := strings.IndexByte(rest, ' '); i != -1 {
162                         vr.kind = "field"
163                         vr.structName = vr.name
164                         vr.name = rest[:i]
165                         return vr, true
166                 }
167         case strings.HasPrefix(rest, "func "):
168                 vr.kind = "func"
169                 rest = rest[len("func "):]
170                 if i := strings.IndexByte(rest, '('); i != -1 {
171                         vr.name = rest[:i]
172                         return vr, true
173                 }
174         case strings.HasPrefix(rest, "method "): // "method (*File) SetModTime(time.Time)"
175                 vr.kind = "method"
176                 rest = rest[len("method "):] // "(*File) SetModTime(time.Time)"
177                 sp := strings.IndexByte(rest, ' ')
178                 if sp == -1 {
179                         return
180                 }
181                 vr.recv = strings.Trim(rest[:sp], "()") // "*File"
182                 rest = rest[sp+1:]                      // SetMode(os.FileMode)
183                 paren := strings.IndexByte(rest, '(')
184                 if paren == -1 {
185                         return
186                 }
187                 vr.name = rest[:paren]
188                 return vr, true
189         }
190         return // TODO: handle more cases
191 }
192
193 // InitVersionInfo parses the $GOROOT/api/go*.txt API definition files to discover
194 // which API features were added in which Go releases.
195 func (c *Corpus) InitVersionInfo() {
196         var err error
197         c.pkgAPIInfo, err = parsePackageAPIInfo()
198         if err != nil {
199                 // TODO: consider making this fatal, after the Go 1.11 cycle.
200                 log.Printf("godoc: error parsing API version files: %v", err)
201         }
202 }
203
204 func parsePackageAPIInfo() (apiVersions, error) {
205         var apiGlob string
206         if os.Getenv("GOROOT") == "" {
207                 apiGlob = filepath.Join(build.Default.GOROOT, "api", "go*.txt")
208         } else {
209                 apiGlob = filepath.Join(os.Getenv("GOROOT"), "api", "go*.txt")
210         }
211
212         files, err := filepath.Glob(apiGlob)
213         if err != nil {
214                 return nil, err
215         }
216
217         vp := new(versionParser)
218         for _, f := range files {
219                 if err := vp.parseFile(f); err != nil {
220                         return nil, err
221                 }
222         }
223         return vp.res, nil
224 }