aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/go1.21.txt1
-rw-r--r--src/cmd/compile/internal/types2/package.go21
-rw-r--r--src/cmd/compile/internal/types2/sizeof_test.go2
-rw-r--r--src/cmd/compile/internal/types2/version.go23
-rw-r--r--src/go/build/deps_test.go2
-rw-r--r--src/go/types/check.go41
-rw-r--r--src/go/types/package.go21
-rw-r--r--src/go/types/sizeof_test.go2
-rw-r--r--src/go/types/version.go23
-rw-r--r--src/go/types/version_test.go24
10 files changed, 105 insertions, 55 deletions
diff --git a/api/go1.21.txt b/api/go1.21.txt
index 6435d10914..c8ca3df2e6 100644
--- a/api/go1.21.txt
+++ b/api/go1.21.txt
@@ -174,6 +174,7 @@ pkg go/build, type Package struct, Directives []Directive #56986
pkg go/build, type Package struct, TestDirectives []Directive #56986
pkg go/build, type Package struct, XTestDirectives []Directive #56986
pkg go/token, method (*File) Lines() []int #57708
+pkg go/types, method (*Package) GoVersion() string #61175
pkg html/template, const ErrJSTemplate = 12 #59584
pkg html/template, const ErrJSTemplate ErrorCode #59584
pkg io/fs, func FormatDirEntry(DirEntry) string #54451
diff --git a/src/cmd/compile/internal/types2/package.go b/src/cmd/compile/internal/types2/package.go
index 61670f6718..e08099d81f 100644
--- a/src/cmd/compile/internal/types2/package.go
+++ b/src/cmd/compile/internal/types2/package.go
@@ -10,13 +10,14 @@ import (
// A Package describes a Go package.
type Package struct {
- path string
- name string
- scope *Scope
- imports []*Package
- complete bool
- fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
- cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go
+ path string
+ name string
+ scope *Scope
+ imports []*Package
+ complete bool
+ fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
+ cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go
+ goVersion string // minimum Go version required for package (by Config.GoVersion, typically from go.mod)
}
// NewPackage returns a new Package for the given package path and name.
@@ -35,6 +36,12 @@ func (pkg *Package) Name() string { return pkg.name }
// SetName sets the package name.
func (pkg *Package) SetName(name string) { pkg.name = name }
+// GoVersion returns the minimum Go version required by this package.
+// If the minimum version is unknown, GoVersion returns the empty string.
+// Individual source files may specify a different minimum Go version,
+// as reported in the [go/ast.File.GoVersion] field.
+func (pkg *Package) GoVersion() string { return pkg.goVersion }
+
// Scope returns the (complete or incomplete) package scope
// holding the objects declared at package level (TypeNames,
// Consts, Vars, and Funcs).
diff --git a/src/cmd/compile/internal/types2/sizeof_test.go b/src/cmd/compile/internal/types2/sizeof_test.go
index af82b3fa7a..740dbc9276 100644
--- a/src/cmd/compile/internal/types2/sizeof_test.go
+++ b/src/cmd/compile/internal/types2/sizeof_test.go
@@ -47,7 +47,7 @@ func TestSizeof(t *testing.T) {
// Misc
{Scope{}, 60, 104},
- {Package{}, 36, 72},
+ {Package{}, 44, 88},
{_TypeSet{}, 28, 56},
}
diff --git a/src/cmd/compile/internal/types2/version.go b/src/cmd/compile/internal/types2/version.go
index 7d01b829a9..e525f16470 100644
--- a/src/cmd/compile/internal/types2/version.go
+++ b/src/cmd/compile/internal/types2/version.go
@@ -6,7 +6,6 @@ package types2
import (
"cmd/compile/internal/syntax"
- "errors"
"fmt"
"strings"
)
@@ -44,23 +43,24 @@ var (
go1_21 = version{1, 21}
)
-var errVersionSyntax = errors.New("invalid Go version syntax")
-
// parseGoVersion parses a Go version string (such as "go1.12")
// and returns the version, or an error. If s is the empty
// string, the version is 0.0.
func parseGoVersion(s string) (v version, err error) {
+ bad := func() (version, error) {
+ return version{}, fmt.Errorf("invalid Go version syntax %q", s)
+ }
if s == "" {
return
}
if !strings.HasPrefix(s, "go") {
- return version{}, errVersionSyntax
+ return bad()
}
s = s[len("go"):]
i := 0
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
if i >= 10 || i == 0 && s[i] == '0' {
- return version{}, errVersionSyntax
+ return bad()
}
v.major = 10*v.major + int(s[i]) - '0'
}
@@ -68,7 +68,7 @@ func parseGoVersion(s string) (v version, err error) {
return
}
if i == 0 || s[i] != '.' {
- return version{}, errVersionSyntax
+ return bad()
}
s = s[i+1:]
if s == "0" {
@@ -81,14 +81,15 @@ func parseGoVersion(s string) (v version, err error) {
i = 0
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
if i >= 10 || i == 0 && s[i] == '0' {
- return version{}, errVersionSyntax
+ return bad()
}
v.minor = 10*v.minor + int(s[i]) - '0'
}
- if i > 0 && i == len(s) {
- return
- }
- return version{}, errVersionSyntax
+ // Accept any suffix after the minor number.
+ // We are only looking for the language version (major.minor)
+ // but want to accept any valid Go version, like go1.21.0
+ // and go1.21rc2.
+ return
}
// langCompat reports an error if the representation of a numeric
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index be8ac30f9d..2f335068b8 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -286,7 +286,7 @@ var depsRules = `
math/big, go/token
< go/constant;
- container/heap, go/constant, go/parser, internal/types/errors
+ container/heap, go/constant, go/parser, internal/goversion, internal/types/errors
< go/types;
# The vast majority of standard library packages should not be resorting to regexp.
diff --git a/src/go/types/check.go b/src/go/types/check.go
index 5381b5db68..591de5f329 100644
--- a/src/go/types/check.go
+++ b/src/go/types/check.go
@@ -12,6 +12,7 @@ import (
"go/ast"
"go/constant"
"go/token"
+ "internal/goversion"
. "internal/types/errors"
)
@@ -98,11 +99,12 @@ type Checker struct {
fset *token.FileSet
pkg *Package
*Info
- version version // accepted language version
- nextID uint64 // unique Id for type parameters (first valid Id is 1)
- objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
- impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
- valids instanceLookup // valid *Named (incl. instantiated) types per the validType check
+ version version // accepted language version
+ versionErr error // version error, delayed from NewChecker
+ nextID uint64 // unique Id for type parameters (first valid Id is 1)
+ objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
+ impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
+ valids instanceLookup // valid *Named (incl. instantiated) types per the validType check
// pkgPathMap maps package names to the set of distinct import paths we've
// seen for that name, anywhere in the import graph. It is used for
@@ -233,20 +235,21 @@ func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Ch
info = new(Info)
}
- version, err := parseGoVersion(conf.GoVersion)
- if err != nil {
- panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err))
+ version, versionErr := parseGoVersion(conf.GoVersion)
+ if pkg != nil {
+ pkg.goVersion = conf.GoVersion
}
return &Checker{
- conf: conf,
- ctxt: conf.Context,
- fset: fset,
- pkg: pkg,
- Info: info,
- version: version,
- objMap: make(map[Object]*declInfo),
- impMap: make(map[importKey]*Package),
+ conf: conf,
+ ctxt: conf.Context,
+ fset: fset,
+ pkg: pkg,
+ Info: info,
+ version: version,
+ versionErr: versionErr,
+ objMap: make(map[Object]*declInfo),
+ impMap: make(map[importKey]*Package),
}
}
@@ -342,6 +345,12 @@ func (check *Checker) Files(files []*ast.File) error { return check.checkFiles(f
var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")
func (check *Checker) checkFiles(files []*ast.File) (err error) {
+ if check.versionErr != nil {
+ return check.versionErr
+ }
+ if check.version.after(version{1, goversion.Version}) {
+ return fmt.Errorf("package requires newer Go version %v", check.version)
+ }
if check.conf.FakeImportC && check.conf.go115UsesCgo {
return errBadCgo
}
diff --git a/src/go/types/package.go b/src/go/types/package.go
index 7aa62fb7a3..0f52d5f489 100644
--- a/src/go/types/package.go
+++ b/src/go/types/package.go
@@ -12,13 +12,14 @@ import (
// A Package describes a Go package.
type Package struct {
- path string
- name string
- scope *Scope
- imports []*Package
- complete bool
- fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
- cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go
+ path string
+ name string
+ scope *Scope
+ imports []*Package
+ complete bool
+ fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
+ cgo bool // uses of this package will be rewritten into uses of declarations from _cgo_gotypes.go
+ goVersion string // minimum Go version required for package (by Config.GoVersion, typically from go.mod)
}
// NewPackage returns a new Package for the given package path and name.
@@ -37,6 +38,12 @@ func (pkg *Package) Name() string { return pkg.name }
// SetName sets the package name.
func (pkg *Package) SetName(name string) { pkg.name = name }
+// GoVersion returns the minimum Go version required by this package.
+// If the minimum version is unknown, GoVersion returns the empty string.
+// Individual source files may specify a different minimum Go version,
+// as reported in the [go/ast.File.GoVersion] field.
+func (pkg *Package) GoVersion() string { return pkg.goVersion }
+
// Scope returns the (complete or incomplete) package scope
// holding the objects declared at package level (TypeNames,
// Consts, Vars, and Funcs).
diff --git a/src/go/types/sizeof_test.go b/src/go/types/sizeof_test.go
index f17a1781f5..9e5b5f8b20 100644
--- a/src/go/types/sizeof_test.go
+++ b/src/go/types/sizeof_test.go
@@ -46,7 +46,7 @@ func TestSizeof(t *testing.T) {
// Misc
{Scope{}, 44, 88},
- {Package{}, 36, 72},
+ {Package{}, 44, 88},
{_TypeSet{}, 28, 56},
}
for _, test := range tests {
diff --git a/src/go/types/version.go b/src/go/types/version.go
index 07a42a79ee..108d9b34a0 100644
--- a/src/go/types/version.go
+++ b/src/go/types/version.go
@@ -5,7 +5,6 @@
package types
import (
- "errors"
"fmt"
"go/ast"
"go/token"
@@ -45,23 +44,24 @@ var (
go1_21 = version{1, 21}
)
-var errVersionSyntax = errors.New("invalid Go version syntax")
-
// parseGoVersion parses a Go version string (such as "go1.12")
// and returns the version, or an error. If s is the empty
// string, the version is 0.0.
func parseGoVersion(s string) (v version, err error) {
+ bad := func() (version, error) {
+ return version{}, fmt.Errorf("invalid Go version syntax %q", s)
+ }
if s == "" {
return
}
if !strings.HasPrefix(s, "go") {
- return version{}, errVersionSyntax
+ return bad()
}
s = s[len("go"):]
i := 0
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
if i >= 10 || i == 0 && s[i] == '0' {
- return version{}, errVersionSyntax
+ return bad()
}
v.major = 10*v.major + int(s[i]) - '0'
}
@@ -69,7 +69,7 @@ func parseGoVersion(s string) (v version, err error) {
return
}
if i == 0 || s[i] != '.' {
- return version{}, errVersionSyntax
+ return bad()
}
s = s[i+1:]
if s == "0" {
@@ -82,14 +82,15 @@ func parseGoVersion(s string) (v version, err error) {
i = 0
for ; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
if i >= 10 || i == 0 && s[i] == '0' {
- return version{}, errVersionSyntax
+ return bad()
}
v.minor = 10*v.minor + int(s[i]) - '0'
}
- if i > 0 && i == len(s) {
- return
- }
- return version{}, errVersionSyntax
+ // Accept any suffix after the minor number.
+ // We are only looking for the language version (major.minor)
+ // but want to accept any valid Go version, like go1.21.0
+ // and go1.21rc2.
+ return
}
// langCompat reports an error if the representation of a numeric
diff --git a/src/go/types/version_test.go b/src/go/types/version_test.go
new file mode 100644
index 0000000000..dc9becf9e1
--- /dev/null
+++ b/src/go/types/version_test.go
@@ -0,0 +1,24 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import "testing"
+
+var parseGoVersionTests = []struct {
+ in string
+ out version
+}{
+ {"go1.21", version{1, 21}},
+ {"go1.21.0", version{1, 21}},
+ {"go1.21rc2", version{1, 21}},
+}
+
+func TestParseGoVersion(t *testing.T) {
+ for _, tt := range parseGoVersionTests {
+ if out, err := parseGoVersion(tt.in); out != tt.out || err != nil {
+ t.Errorf("parseGoVersion(%q) = %v, %v, want %v, nil", tt.in, out, err, tt.out)
+ }
+ }
+}