.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / 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         "sort"
17         "strconv"
18         "strings"
19         "unicode"
20 )
21
22 // apiVersions is a map of packages to information about those packages'
23 // symbols and when they were added to Go.
24 //
25 // Only things added after Go1 are tracked. Version strings are of the
26 // form "1.1", "1.2", etc.
27 type apiVersions map[string]pkgAPIVersions // keyed by Go package ("net/http")
28
29 // pkgAPIVersions contains information about which version of Go added
30 // certain package symbols.
31 //
32 // Only things added after Go1 are tracked. Version strings are of the
33 // form "1.1", "1.2", etc.
34 type pkgAPIVersions struct {
35         typeSince   map[string]string            // "Server" -> "1.7"
36         methodSince map[string]map[string]string // "*Server" ->"Shutdown"->1.8
37         funcSince   map[string]string            // "NewServer" -> "1.7"
38         fieldSince  map[string]map[string]string // "ClientTrace" -> "Got1xxResponse" -> "1.11"
39 }
40
41 // sinceVersionFunc returns a string (such as "1.7") specifying which Go
42 // version introduced a symbol, unless it was introduced in Go1, in
43 // which case it returns the empty string.
44 //
45 // The kind is one of "type", "method", or "func".
46 //
47 // The receiver is only used for "methods" and specifies the receiver type,
48 // such as "*Server".
49 //
50 // The name is the symbol name ("Server") and the pkg is the package
51 // ("net/http").
52 func (v apiVersions) sinceVersionFunc(kind, receiver, name, pkg string) string {
53         pv := v[pkg]
54         switch kind {
55         case "func":
56                 return pv.funcSince[name]
57         case "type":
58                 return pv.typeSince[name]
59         case "method":
60                 return pv.methodSince[receiver][name]
61         }
62         return ""
63 }
64
65 // versionedRow represents an API feature, a parsed line of a
66 // $GOROOT/api/go.*txt file.
67 type versionedRow struct {
68         pkg        string // "net/http"
69         kind       string // "type", "func", "method", "field" TODO: "const", "var"
70         recv       string // for methods, the receiver type ("Server", "*Server")
71         name       string // name of type, (struct) field, func, method
72         structName string // for struct fields, the outer struct name
73 }
74
75 // versionParser parses $GOROOT/api/go*.txt files and stores them in in its rows field.
76 type versionParser struct {
77         res apiVersions // initialized lazily
78 }
79
80 // parseFile parses the named $GOROOT/api/goVERSION.txt file.
81 //
82 // For each row, it updates the corresponding entry in
83 // vp.res to VERSION, overwriting any previous value.
84 // As a special case, if goVERSION is "go1", it deletes
85 // from the map instead.
86 func (vp *versionParser) parseFile(name string) error {
87         f, err := os.Open(name)
88         if err != nil {
89                 return err
90         }
91         defer f.Close()
92
93         base := filepath.Base(name)
94         ver := strings.TrimPrefix(strings.TrimSuffix(base, ".txt"), "go")
95
96         sc := bufio.NewScanner(f)
97         for sc.Scan() {
98                 row, ok := parseRow(sc.Text())
99                 if !ok {
100                         continue
101                 }
102                 if vp.res == nil {
103                         vp.res = make(apiVersions)
104                 }
105                 pkgi, ok := vp.res[row.pkg]
106                 if !ok {
107                         pkgi = pkgAPIVersions{
108                                 typeSince:   make(map[string]string),
109                                 methodSince: make(map[string]map[string]string),
110                                 funcSince:   make(map[string]string),
111                                 fieldSince:  make(map[string]map[string]string),
112                         }
113                         vp.res[row.pkg] = pkgi
114                 }
115                 switch row.kind {
116                 case "func":
117                         if ver == "1" {
118                                 delete(pkgi.funcSince, row.name)
119                                 break
120                         }
121                         pkgi.funcSince[row.name] = ver
122                 case "type":
123                         if ver == "1" {
124                                 delete(pkgi.typeSince, row.name)
125                                 break
126                         }
127                         pkgi.typeSince[row.name] = ver
128                 case "method":
129                         if ver == "1" {
130                                 delete(pkgi.methodSince[row.recv], row.name)
131                                 break
132                         }
133                         if _, ok := pkgi.methodSince[row.recv]; !ok {
134                                 pkgi.methodSince[row.recv] = make(map[string]string)
135                         }
136                         pkgi.methodSince[row.recv][row.name] = ver
137                 case "field":
138                         if ver == "1" {
139                                 delete(pkgi.fieldSince[row.structName], row.name)
140                                 break
141                         }
142                         if _, ok := pkgi.fieldSince[row.structName]; !ok {
143                                 pkgi.fieldSince[row.structName] = make(map[string]string)
144                         }
145                         pkgi.fieldSince[row.structName][row.name] = ver
146                 }
147         }
148         return sc.Err()
149 }
150
151 func parseRow(s string) (vr versionedRow, ok bool) {
152         if !strings.HasPrefix(s, "pkg ") {
153                 // Skip comments, blank lines, etc.
154                 return
155         }
156         rest := s[len("pkg "):]
157         endPkg := strings.IndexFunc(rest, func(r rune) bool { return !(unicode.IsLetter(r) || r == '/' || unicode.IsDigit(r)) })
158         if endPkg == -1 {
159                 return
160         }
161         vr.pkg, rest = rest[:endPkg], rest[endPkg:]
162         if !strings.HasPrefix(rest, ", ") {
163                 // If the part after the pkg name isn't ", ", then it's a OS/ARCH-dependent line of the form:
164                 //   pkg syscall (darwin-amd64), const ImplementsGetwd = false
165                 // We skip those for now.
166                 return
167         }
168         rest = rest[len(", "):]
169
170         switch {
171         case strings.HasPrefix(rest, "type "):
172                 rest = rest[len("type "):]
173                 sp := strings.IndexByte(rest, ' ')
174                 if sp == -1 {
175                         return
176                 }
177                 vr.name, rest = rest[:sp], rest[sp+1:]
178                 if !strings.HasPrefix(rest, "struct, ") {
179                         vr.kind = "type"
180                         return vr, true
181                 }
182                 rest = rest[len("struct, "):]
183                 if i := strings.IndexByte(rest, ' '); i != -1 {
184                         vr.kind = "field"
185                         vr.structName = vr.name
186                         vr.name = rest[:i]
187                         return vr, true
188                 }
189         case strings.HasPrefix(rest, "func "):
190                 vr.kind = "func"
191                 rest = rest[len("func "):]
192                 if i := strings.IndexByte(rest, '('); i != -1 {
193                         vr.name = rest[:i]
194                         return vr, true
195                 }
196         case strings.HasPrefix(rest, "method "): // "method (*File) SetModTime(time.Time)"
197                 vr.kind = "method"
198                 rest = rest[len("method "):] // "(*File) SetModTime(time.Time)"
199                 sp := strings.IndexByte(rest, ' ')
200                 if sp == -1 {
201                         return
202                 }
203                 vr.recv = strings.Trim(rest[:sp], "()") // "*File"
204                 rest = rest[sp+1:]                      // SetMode(os.FileMode)
205                 paren := strings.IndexByte(rest, '(')
206                 if paren == -1 {
207                         return
208                 }
209                 vr.name = rest[:paren]
210                 return vr, true
211         }
212         return // TODO: handle more cases
213 }
214
215 // InitVersionInfo parses the $GOROOT/api/go*.txt API definition files to discover
216 // which API features were added in which Go releases.
217 func (c *Corpus) InitVersionInfo() {
218         var err error
219         c.pkgAPIInfo, err = parsePackageAPIInfo()
220         if err != nil {
221                 // TODO: consider making this fatal, after the Go 1.11 cycle.
222                 log.Printf("godoc: error parsing API version files: %v", err)
223         }
224 }
225
226 func parsePackageAPIInfo() (apiVersions, error) {
227         var apiGlob string
228         if os.Getenv("GOROOT") == "" {
229                 apiGlob = filepath.Join(build.Default.GOROOT, "api", "go*.txt")
230         } else {
231                 apiGlob = filepath.Join(os.Getenv("GOROOT"), "api", "go*.txt")
232         }
233
234         files, err := filepath.Glob(apiGlob)
235         if err != nil {
236                 return nil, err
237         }
238
239         // Process files in go1.n, go1.n-1, ..., go1.2, go1.1, go1 order.
240         //
241         // It's rare, but the signature of an identifier may change
242         // (for example, a function that accepts a type replaced with
243         // an alias), and so an existing symbol may show up again in
244         // a later api/go1.N.txt file. Parsing in reverse version
245         // order means we end up with the earliest version of Go
246         // when the symbol was added. See golang.org/issue/44081.
247         //
248         ver := func(name string) int {
249                 base := filepath.Base(name)
250                 ver := strings.TrimPrefix(strings.TrimSuffix(base, ".txt"), "go1.")
251                 if ver == "go1" {
252                         return 0
253                 }
254                 v, _ := strconv.Atoi(ver)
255                 return v
256         }
257         sort.Slice(files, func(i, j int) bool { return ver(files[i]) > ver(files[j]) })
258         vp := new(versionParser)
259         for _, f := range files {
260                 if err := vp.parseFile(f); err != nil {
261                         return nil, err
262                 }
263         }
264         return vp.res, nil
265 }