go/types, types2: collect per-file Go version in Info.FileVersions

The go/types changes are matching but the API changes are hidden
for now, pending acceptance of a respective proposal.

Change-Id: I2e38ff215ddbdcf93f182d3e70c03802d3ca4338
Reviewed-on: https://go-review.googlesource.com/c/go/+/515135
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2023-08-01 18:02:41 -07:00 committed by Gopher Robot
parent d2ee7821d3
commit e59eaee2e3
6 changed files with 133 additions and 2 deletions

View File

@ -288,6 +288,11 @@ type Info struct {
// in source order. Variables without an initialization expression do not
// appear in this list.
InitOrder []*Initializer
// FileVersions maps a file's position base to the file's Go version.
// If the file doesn't specify a version and Config.GoVersion is not
// given, the reported version is the zero version (Major, Minor = 0, 0).
FileVersions map[*syntax.PosBase]Version
}
func (info *Info) recordTypes() bool {
@ -421,6 +426,12 @@ func (init *Initializer) String() string {
return buf.String()
}
// A Version represents a released Go version.
type Version struct {
Major int
Minor int
}
// Check type-checks a package and returns the resulting package object and
// the first error if any. Additionally, if info != nil, Check populates each
// of the non-nil maps in the Info struct.

View File

@ -2764,3 +2764,42 @@ var _ = f(1, 2)
t.Errorf("src1: unexpected error: got %v", err)
}
}
func TestFileVersions(t *testing.T) {
for _, test := range []struct {
moduleVersion string
fileVersion string
want Version
}{
{"", "", Version{0, 0}}, // no versions specified
{"go1.19", "", Version{1, 19}}, // module version specified
{"", "go1.20", Version{0, 0}}, // file upgrade ignored
{"go1.19", "go1.20", Version{1, 20}}, // file upgrade permitted
{"go1.20", "go1.19", Version{1, 20}}, // file downgrade not permitted
{"go1.21", "go1.19", Version{1, 19}}, // file downgrade permitted (module version is >= go1.21)
} {
var src string
if test.fileVersion != "" {
src = "//go:build " + test.fileVersion + "\n"
}
src += "package p"
conf := Config{GoVersion: test.moduleVersion}
versions := make(map[*syntax.PosBase]Version)
var info Info
info.FileVersions = versions
mustTypecheck(src, &conf, &info)
n := 0
for _, v := range info.FileVersions {
want := test.want
if v.Major != want.Major || v.Minor != want.Minor {
t.Errorf("%q: unexpected file version: got %v, want %v", src, v, want)
}
n++
}
if n != 1 {
t.Errorf("%q: incorrect number of map entries: got %d", src, n)
}
}
}

View File

@ -285,6 +285,8 @@ func (check *Checker) initFiles(files []*syntax.File) {
}
for _, file := range check.files {
fbase := base(file.Pos()) // fbase may be nil for tests
check.recordFileVersion(fbase, check.version) // record package version (possibly zero version)
v, _ := parseGoVersion(file.GoVersion)
if v.major > 0 {
if v.equal(check.version) {
@ -309,7 +311,8 @@ func (check *Checker) initFiles(files []*syntax.File) {
if check.posVers == nil {
check.posVers = make(map[*syntax.PosBase]version)
}
check.posVers[base(file.Pos())] = v
check.posVers[fbase] = v
check.recordFileVersion(fbase, v) // overwrite package version
}
}
}
@ -673,3 +676,9 @@ func (check *Checker) recordScope(node syntax.Node, scope *Scope) {
m[node] = scope
}
}
func (check *Checker) recordFileVersion(fbase *syntax.PosBase, v version) {
if m := check.FileVersions; m != nil {
m[fbase] = Version{v.major, v.minor}
}
}

View File

@ -285,6 +285,11 @@ type Info struct {
// in source order. Variables without an initialization expression do not
// appear in this list.
InitOrder []*Initializer
// _FileVersions maps a file to the file's Go version.
// If the file doesn't specify a version and Config.GoVersion is not
// given, the reported version is the zero version (Major, Minor = 0, 0).
_FileVersions map[*token.File]_Version
}
func (info *Info) recordTypes() bool {
@ -409,6 +414,12 @@ func (init *Initializer) String() string {
return buf.String()
}
// A _Version represents a released Go version.
type _Version struct {
_Major int
_Minor int
}
// Check type-checks a package and returns the resulting package object and
// the first error if any. Additionally, if info != nil, Check populates each
// of the non-nil maps in the Info struct.

View File

@ -2773,3 +2773,55 @@ var _ = f(1, 2)
t.Errorf("src1: unexpected error: got %v", err)
}
}
func TestFileVersions(t *testing.T) {
for _, test := range []struct {
moduleVersion string
fileVersion string
want Version
}{
{"", "", Version{0, 0}}, // no versions specified
{"go1.19", "", Version{1, 19}}, // module version specified
{"", "go1.20", Version{0, 0}}, // file upgrade ignored
{"go1.19", "go1.20", Version{1, 20}}, // file upgrade permitted
{"go1.20", "go1.19", Version{1, 20}}, // file downgrade not permitted
{"go1.21", "go1.19", Version{1, 19}}, // file downgrade permitted (module version is >= go1.21)
} {
var src string
if test.fileVersion != "" {
src = "//go:build " + test.fileVersion + "\n"
}
src += "package p"
conf := Config{GoVersion: test.moduleVersion}
versions := make(map[*token.File]Version)
var info Info
*_FileVersionsAddr(&info) = versions
mustTypecheck(src, &conf, &info)
n := 0
for _, v := range versions {
want := test.want
if v.Major != want.Major || v.Minor != want.Minor {
t.Errorf("%q: unexpected file version: got %v, want %v", src, v, want)
}
n++
}
if n != 1 {
t.Errorf("%q: incorrect number of map entries: got %d", src, n)
}
}
}
// Version must match types._Version exactly.
// TODO(gri) remove this declaration once types.Version is exported.
type Version struct {
Major int
Minor int
}
// _FileVersionsAddr(conf) returns the address of the field info._FileVersions.
func _FileVersionsAddr(info *Info) *map[*token.File]Version {
v := reflect.Indirect(reflect.ValueOf(info))
return (*map[*token.File]Version)(v.FieldByName("_FileVersions").Addr().UnsafePointer())
}

View File

@ -288,6 +288,8 @@ func (check *Checker) initFiles(files []*ast.File) {
}
for _, file := range check.files {
tfile := check.fset.File(file.FileStart)
check.recordFileVersion(tfile, check.version) // record package version (possibly zero version)
v, _ := parseGoVersion(file.GoVersion)
if v.major > 0 {
if v.equal(check.version) {
@ -312,7 +314,8 @@ func (check *Checker) initFiles(files []*ast.File) {
if check.posVers == nil {
check.posVers = make(map[*token.File]version)
}
check.posVers[check.fset.File(file.FileStart)] = v
check.posVers[tfile] = v
check.recordFileVersion(tfile, v) // overwrite package version
}
}
}
@ -636,3 +639,9 @@ func (check *Checker) recordScope(node ast.Node, scope *Scope) {
m[node] = scope
}
}
func (check *Checker) recordFileVersion(tfile *token.File, v version) {
if m := check._FileVersions; m != nil {
m[tfile] = _Version{v.major, v.minor}
}
}