]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: collect per-file Go version in Info.FileVersions
authorRobert Griesemer <gri@golang.org>
Wed, 2 Aug 2023 01:02:41 +0000 (18:02 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 3 Aug 2023 18:56:29 +0000 (18:56 +0000)
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>

src/cmd/compile/internal/types2/api.go
src/cmd/compile/internal/types2/api_test.go
src/cmd/compile/internal/types2/check.go
src/go/types/api.go
src/go/types/api_test.go
src/go/types/check.go

index d0c0cdb8f9d2936c8fdaa661387488831cf5a64c..48cafe03e7645efeb537d2f97dec9793e407fd73 100644 (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.
index d76c6cdfd7210a23623fa3a579953536c0d1aea9..4cabad2e9e28b468e7fbb9b1992ceefbdaedcd20 100644 (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)
+               }
+       }
+}
index 5412e876bd9b5ded4feb428a88cc822d985c3668..0a24eb2dabfe9269e26d807f545596de1042549a 100644 (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}
+       }
+}
index ad4c1a2e9fa36ef930104b378da36af14cc09695..2dd5a3e3c08933dbdb43af183866a6bfe3e718bb 100644 (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.
index 6a607829ac6f4cb355da0781818e8f2cb707f1c7..6370786772b3e9247bfebddd33711b67c08b1ea6 100644 (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())
+}
index 6301323c2a8bdb320f3269a8b80b8ebe18d5e4d4..f3ad85dff913d6510a9a773ff32c4e2d056d2478 100644 (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}
+       }
+}