Skip to content

Commit a183da6

Browse files
committed
godoc: show earliest version when identifier was added
CL 85396 implemented parsePackageAPIInfo with the idea that each identifier shows up in exactly one of api/go*.txt files, when it was added. We now know that it may show up more than once, when the signature changes (generally in a compatible way, such as when existing types are replaced with aliases to an equivalent type). Modify the algorithm to parse the api/go*.txt files in reverse order, in order to find and display the earliest Go version when each identifier was first added. Fixes golang/go#44081. Updates golang/go#5778. Change-Id: I83171fd8c161d703f284011375d74b824c279d41 Reviewed-on: https://go-review.googlesource.com/c/tools/+/289089 Run-TryBot: Dmitri Shuralyov <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Trust: Dmitri Shuralyov <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 0d45b35 commit a183da6

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

versions.go

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"log"
1414
"os"
1515
"path/filepath"
16+
"sort"
17+
"strconv"
1618
"strings"
1719
"unicode"
1820
)
@@ -75,18 +77,22 @@ type versionParser struct {
7577
res apiVersions // initialized lazily
7678
}
7779

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.
7886
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-
}
8487
f, err := os.Open(name)
8588
if err != nil {
8689
return err
8790
}
8891
defer f.Close()
8992

93+
base := filepath.Base(name)
94+
ver := strings.TrimPrefix(strings.TrimSuffix(base, ".txt"), "go")
95+
9096
sc := bufio.NewScanner(f)
9197
for sc.Scan() {
9298
row, ok := parseRow(sc.Text())
@@ -108,15 +114,31 @@ func (vp *versionParser) parseFile(name string) error {
108114
}
109115
switch row.kind {
110116
case "func":
117+
if ver == "1" {
118+
delete(pkgi.funcSince, row.name)
119+
break
120+
}
111121
pkgi.funcSince[row.name] = ver
112122
case "type":
123+
if ver == "1" {
124+
delete(pkgi.typeSince, row.name)
125+
break
126+
}
113127
pkgi.typeSince[row.name] = ver
114128
case "method":
129+
if ver == "1" {
130+
delete(pkgi.methodSince[row.recv], row.name)
131+
break
132+
}
115133
if _, ok := pkgi.methodSince[row.recv]; !ok {
116134
pkgi.methodSince[row.recv] = make(map[string]string)
117135
}
118136
pkgi.methodSince[row.recv][row.name] = ver
119137
case "field":
138+
if ver == "1" {
139+
delete(pkgi.fieldSince[row.structName], row.name)
140+
break
141+
}
120142
if _, ok := pkgi.fieldSince[row.structName]; !ok {
121143
pkgi.fieldSince[row.structName] = make(map[string]string)
122144
}
@@ -214,6 +236,25 @@ func parsePackageAPIInfo() (apiVersions, error) {
214236
return nil, err
215237
}
216238

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]) })
217258
vp := new(versionParser)
218259
for _, f := range files {
219260
if err := vp.parseFile(f); err != nil {

versions_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ func TestAPIVersion(t *testing.T) {
113113
{"type", "strings", "Builder", "", "1.10"},
114114
{"method", "strings", "WriteString", "*Builder", "1.10"},
115115

116+
// Should get the earliest Go version when an identifier
117+
// was initially added, rather than a later version when
118+
// it may have been updated. See issue 44081.
119+
{"func", "os", "Chmod", "", ""}, // Go 1 era function, updated in Go 1.16.
120+
{"method", "os", "Readdir", "*File", ""}, // Go 1 era method, updated in Go 1.16.
121+
{"method", "os", "ReadDir", "*File", "1.16"}, // New to Go 1.16.
122+
116123
// Things from package syscall should never appear
117124
{"func", "syscall", "FchFlags", "", ""},
118125
{"type", "syscall", "Inet4Pktinfo", "", ""},

0 commit comments

Comments
 (0)