mirror of https://github.com/golang/go.git
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 <dmitshur@golang.org> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Go Bot <gobot@golang.org> Trust: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
5ab06b02d6
commit
6140657873
|
|
@ -13,6 +13,8 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
|
@ -75,18 +77,22 @@ type versionParser struct {
|
|||
res apiVersions // initialized lazily
|
||||
}
|
||||
|
||||
// parseFile parses the named $GOROOT/api/goVERSION.txt file.
|
||||
//
|
||||
// For each row, it updates the corresponding entry in
|
||||
// vp.res to VERSION, overwriting any previous value.
|
||||
// As a special case, if goVERSION is "go1", it deletes
|
||||
// from the map instead.
|
||||
func (vp *versionParser) parseFile(name string) error {
|
||||
base := filepath.Base(name)
|
||||
ver := strings.TrimPrefix(strings.TrimSuffix(base, ".txt"), "go")
|
||||
if ver == "1" {
|
||||
return nil
|
||||
}
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
base := filepath.Base(name)
|
||||
ver := strings.TrimPrefix(strings.TrimSuffix(base, ".txt"), "go")
|
||||
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
row, ok := parseRow(sc.Text())
|
||||
|
|
@ -108,15 +114,31 @@ func (vp *versionParser) parseFile(name string) error {
|
|||
}
|
||||
switch row.kind {
|
||||
case "func":
|
||||
if ver == "1" {
|
||||
delete(pkgi.funcSince, row.name)
|
||||
break
|
||||
}
|
||||
pkgi.funcSince[row.name] = ver
|
||||
case "type":
|
||||
if ver == "1" {
|
||||
delete(pkgi.typeSince, row.name)
|
||||
break
|
||||
}
|
||||
pkgi.typeSince[row.name] = ver
|
||||
case "method":
|
||||
if ver == "1" {
|
||||
delete(pkgi.methodSince[row.recv], row.name)
|
||||
break
|
||||
}
|
||||
if _, ok := pkgi.methodSince[row.recv]; !ok {
|
||||
pkgi.methodSince[row.recv] = make(map[string]string)
|
||||
}
|
||||
pkgi.methodSince[row.recv][row.name] = ver
|
||||
case "field":
|
||||
if ver == "1" {
|
||||
delete(pkgi.fieldSince[row.structName], row.name)
|
||||
break
|
||||
}
|
||||
if _, ok := pkgi.fieldSince[row.structName]; !ok {
|
||||
pkgi.fieldSince[row.structName] = make(map[string]string)
|
||||
}
|
||||
|
|
@ -214,6 +236,25 @@ func parsePackageAPIInfo() (apiVersions, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Process files in go1.n, go1.n-1, ..., go1.2, go1.1, go1 order.
|
||||
//
|
||||
// It's rare, but the signature of an identifier may change
|
||||
// (for example, a function that accepts a type replaced with
|
||||
// an alias), and so an existing symbol may show up again in
|
||||
// a later api/go1.N.txt file. Parsing in reverse version
|
||||
// order means we end up with the earliest version of Go
|
||||
// when the symbol was added. See golang.org/issue/44081.
|
||||
//
|
||||
ver := func(name string) int {
|
||||
base := filepath.Base(name)
|
||||
ver := strings.TrimPrefix(strings.TrimSuffix(base, ".txt"), "go1.")
|
||||
if ver == "go1" {
|
||||
return 0
|
||||
}
|
||||
v, _ := strconv.Atoi(ver)
|
||||
return v
|
||||
}
|
||||
sort.Slice(files, func(i, j int) bool { return ver(files[i]) > ver(files[j]) })
|
||||
vp := new(versionParser)
|
||||
for _, f := range files {
|
||||
if err := vp.parseFile(f); err != nil {
|
||||
|
|
|
|||
|
|
@ -113,6 +113,13 @@ func TestAPIVersion(t *testing.T) {
|
|||
{"type", "strings", "Builder", "", "1.10"},
|
||||
{"method", "strings", "WriteString", "*Builder", "1.10"},
|
||||
|
||||
// Should get the earliest Go version when an identifier
|
||||
// was initially added, rather than a later version when
|
||||
// it may have been updated. See issue 44081.
|
||||
{"func", "os", "Chmod", "", ""}, // Go 1 era function, updated in Go 1.16.
|
||||
{"method", "os", "Readdir", "*File", ""}, // Go 1 era method, updated in Go 1.16.
|
||||
{"method", "os", "ReadDir", "*File", "1.16"}, // New to Go 1.16.
|
||||
|
||||
// Things from package syscall should never appear
|
||||
{"func", "syscall", "FchFlags", "", ""},
|
||||
{"type", "syscall", "Inet4Pktinfo", "", ""},
|
||||
|
|
|
|||
Loading…
Reference in New Issue