whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
// Copyright 2024 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 versions
|
||||
|
||||
import "go/build/constraint"
|
||||
|
||||
// ConstraintGoVersion is constraint.GoVersion (if built with go1.21+).
|
||||
// Otherwise nil.
|
||||
//
|
||||
// Deprecate once x/tools is after go1.21.
|
||||
var ConstraintGoVersion func(x constraint.Expr) string
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
package versions
|
||||
|
||||
import "go/build/constraint"
|
||||
|
||||
func init() {
|
||||
ConstraintGoVersion = constraint.GoVersion
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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 versions
|
||||
|
||||
// This file contains predicates for working with file versions to
|
||||
// decide when a tool should consider a language feature enabled.
|
||||
|
||||
// GoVersions that features in x/tools can be gated to.
|
||||
const (
|
||||
Go1_18 = "go1.18"
|
||||
Go1_19 = "go1.19"
|
||||
Go1_20 = "go1.20"
|
||||
Go1_21 = "go1.21"
|
||||
Go1_22 = "go1.22"
|
||||
)
|
||||
|
||||
// Future is an invalid unknown Go version sometime in the future.
|
||||
// Do not use directly with Compare.
|
||||
const Future = ""
|
||||
|
||||
// AtLeast reports whether the file version v comes after a Go release.
|
||||
//
|
||||
// Use this predicate to enable a behavior once a certain Go release
|
||||
// has happened (and stays enabled in the future).
|
||||
func AtLeast(v, release string) bool {
|
||||
if v == Future {
|
||||
return true // an unknown future version is always after y.
|
||||
}
|
||||
return Compare(Lang(v), Lang(release)) >= 0
|
||||
}
|
||||
|
||||
// Before reports whether the file version v is strictly before a Go release.
|
||||
//
|
||||
// Use this predicate to disable a behavior once a certain Go release
|
||||
// has happened (and stays enabled in the future).
|
||||
func Before(v, release string) bool {
|
||||
if v == Future {
|
||||
return false // an unknown future version happens after y.
|
||||
}
|
||||
return Compare(Lang(v), Lang(release)) < 0
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// 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.
|
||||
|
||||
// This is a fork of internal/gover for use by x/tools until
|
||||
// go1.21 and earlier are no longer supported by x/tools.
|
||||
|
||||
package versions
|
||||
|
||||
import "strings"
|
||||
|
||||
// A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]]
|
||||
// The numbers are the original decimal strings to avoid integer overflows
|
||||
// and since there is very little actual math. (Probably overflow doesn't matter in practice,
|
||||
// but at the time this code was written, there was an existing test that used
|
||||
// go1.99999999999, which does not fit in an int on 32-bit platforms.
|
||||
// The "big decimal" representation avoids the problem entirely.)
|
||||
type gover struct {
|
||||
major string // decimal
|
||||
minor string // decimal or ""
|
||||
patch string // decimal or ""
|
||||
kind string // "", "alpha", "beta", "rc"
|
||||
pre string // decimal or ""
|
||||
}
|
||||
|
||||
// compare returns -1, 0, or +1 depending on whether
|
||||
// x < y, x == y, or x > y, interpreted as toolchain versions.
|
||||
// The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21".
|
||||
// Malformed versions compare less than well-formed versions and equal to each other.
|
||||
// The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0".
|
||||
func compare(x, y string) int {
|
||||
vx := parse(x)
|
||||
vy := parse(y)
|
||||
|
||||
if c := cmpInt(vx.major, vy.major); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := cmpInt(vx.minor, vy.minor); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := cmpInt(vx.patch, vy.patch); c != 0 {
|
||||
return c
|
||||
}
|
||||
if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc
|
||||
return c
|
||||
}
|
||||
if c := cmpInt(vx.pre, vy.pre); c != 0 {
|
||||
return c
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// lang returns the Go language version. For example, lang("1.2.3") == "1.2".
|
||||
func lang(x string) string {
|
||||
v := parse(x)
|
||||
if v.minor == "" || v.major == "1" && v.minor == "0" {
|
||||
return v.major
|
||||
}
|
||||
return v.major + "." + v.minor
|
||||
}
|
||||
|
||||
// isValid reports whether the version x is valid.
|
||||
func isValid(x string) bool {
|
||||
return parse(x) != gover{}
|
||||
}
|
||||
|
||||
// parse parses the Go version string x into a version.
|
||||
// It returns the zero version if x is malformed.
|
||||
func parse(x string) gover {
|
||||
var v gover
|
||||
|
||||
// Parse major version.
|
||||
var ok bool
|
||||
v.major, x, ok = cutInt(x)
|
||||
if !ok {
|
||||
return gover{}
|
||||
}
|
||||
if x == "" {
|
||||
// Interpret "1" as "1.0.0".
|
||||
v.minor = "0"
|
||||
v.patch = "0"
|
||||
return v
|
||||
}
|
||||
|
||||
// Parse . before minor version.
|
||||
if x[0] != '.' {
|
||||
return gover{}
|
||||
}
|
||||
|
||||
// Parse minor version.
|
||||
v.minor, x, ok = cutInt(x[1:])
|
||||
if !ok {
|
||||
return gover{}
|
||||
}
|
||||
if x == "" {
|
||||
// Patch missing is same as "0" for older versions.
|
||||
// Starting in Go 1.21, patch missing is different from explicit .0.
|
||||
if cmpInt(v.minor, "21") < 0 {
|
||||
v.patch = "0"
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Parse patch if present.
|
||||
if x[0] == '.' {
|
||||
v.patch, x, ok = cutInt(x[1:])
|
||||
if !ok || x != "" {
|
||||
// Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != "").
|
||||
// Allowing them would be a bit confusing because we already have:
|
||||
// 1.21 < 1.21rc1
|
||||
// But a prerelease of a patch would have the opposite effect:
|
||||
// 1.21.3rc1 < 1.21.3
|
||||
// We've never needed them before, so let's not start now.
|
||||
return gover{}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Parse prerelease.
|
||||
i := 0
|
||||
for i < len(x) && (x[i] < '0' || '9' < x[i]) {
|
||||
if x[i] < 'a' || 'z' < x[i] {
|
||||
return gover{}
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i == 0 {
|
||||
return gover{}
|
||||
}
|
||||
v.kind, x = x[:i], x[i:]
|
||||
if x == "" {
|
||||
return v
|
||||
}
|
||||
v.pre, x, ok = cutInt(x)
|
||||
if !ok || x != "" {
|
||||
return gover{}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
// cutInt scans the leading decimal number at the start of x to an integer
|
||||
// and returns that value and the rest of the string.
|
||||
func cutInt(x string) (n, rest string, ok bool) {
|
||||
i := 0
|
||||
for i < len(x) && '0' <= x[i] && x[i] <= '9' {
|
||||
i++
|
||||
}
|
||||
if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero
|
||||
return "", "", false
|
||||
}
|
||||
return x[:i], x[i:], true
|
||||
}
|
||||
|
||||
// cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers.
|
||||
// (Copied from golang.org/x/mod/semver's compareInt.)
|
||||
func cmpInt(x, y string) int {
|
||||
if x == y {
|
||||
return 0
|
||||
}
|
||||
if len(x) < len(y) {
|
||||
return -1
|
||||
}
|
||||
if len(x) > len(y) {
|
||||
return +1
|
||||
}
|
||||
if x < y {
|
||||
return -1
|
||||
} else {
|
||||
return +1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2024 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 versions
|
||||
|
||||
// toolchain is maximum version (<1.22) that the go toolchain used
|
||||
// to build the current tool is known to support.
|
||||
//
|
||||
// When a tool is built with >=1.22, the value of toolchain is unused.
|
||||
//
|
||||
// x/tools does not support building with go <1.18. So we take this
|
||||
// as the minimum possible maximum.
|
||||
var toolchain string = Go1_18
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build go1.19
|
||||
// +build go1.19
|
||||
|
||||
package versions
|
||||
|
||||
func init() {
|
||||
if Compare(toolchain, Go1_19) < 0 {
|
||||
toolchain = Go1_19
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build go1.20
|
||||
// +build go1.20
|
||||
|
||||
package versions
|
||||
|
||||
func init() {
|
||||
if Compare(toolchain, Go1_20) < 0 {
|
||||
toolchain = Go1_20
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
package versions
|
||||
|
||||
func init() {
|
||||
if Compare(toolchain, Go1_21) < 0 {
|
||||
toolchain = Go1_21
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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 versions
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// GoVersion returns the Go version of the type package.
|
||||
// It returns zero if no version can be determined.
|
||||
func GoVersion(pkg *types.Package) string {
|
||||
// TODO(taking): x/tools can call GoVersion() [from 1.21] after 1.25.
|
||||
if pkg, ok := any(pkg).(interface{ GoVersion() string }); ok {
|
||||
return pkg.GoVersion()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
//go:build !go1.22
|
||||
// +build !go1.22
|
||||
|
||||
package versions
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// FileVersion returns a language version (<=1.21) derived from runtime.Version()
|
||||
// or an unknown future version.
|
||||
func FileVersion(info *types.Info, file *ast.File) string {
|
||||
// In x/tools built with Go <= 1.21, we do not have Info.FileVersions
|
||||
// available. We use a go version derived from the toolchain used to
|
||||
// compile the tool by default.
|
||||
// This will be <= go1.21. We take this as the maximum version that
|
||||
// this tool can support.
|
||||
//
|
||||
// There are no features currently in x/tools that need to tell fine grained
|
||||
// differences for versions <1.22.
|
||||
return toolchain
|
||||
}
|
||||
|
||||
// InitFileVersions is a noop when compiled with this Go version.
|
||||
func InitFileVersions(*types.Info) {}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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.
|
||||
|
||||
//go:build go1.22
|
||||
// +build go1.22
|
||||
|
||||
package versions
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// FileVersion returns a file's Go version.
|
||||
// The reported version is an unknown Future version if a
|
||||
// version cannot be determined.
|
||||
func FileVersion(info *types.Info, file *ast.File) string {
|
||||
// In tools built with Go >= 1.22, the Go version of a file
|
||||
// follow a cascades of sources:
|
||||
// 1) types.Info.FileVersion, which follows the cascade:
|
||||
// 1.a) file version (ast.File.GoVersion),
|
||||
// 1.b) the package version (types.Config.GoVersion), or
|
||||
// 2) is some unknown Future version.
|
||||
//
|
||||
// File versions require a valid package version to be provided to types
|
||||
// in Config.GoVersion. Config.GoVersion is either from the package's module
|
||||
// or the toolchain (go run). This value should be provided by go/packages
|
||||
// or unitchecker.Config.GoVersion.
|
||||
if v := info.FileVersions[file]; IsValid(v) {
|
||||
return v
|
||||
}
|
||||
// Note: we could instead return runtime.Version() [if valid].
|
||||
// This would act as a max version on what a tool can support.
|
||||
return Future
|
||||
}
|
||||
|
||||
// InitFileVersions initializes info to record Go versions for Go files.
|
||||
func InitFileVersions(info *types.Info) {
|
||||
info.FileVersions = make(map[*ast.File]string)
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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 versions_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/importer"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/internal/testenv"
|
||||
"golang.org/x/tools/internal/versions"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
testenv.NeedsGo1Point(t, 22)
|
||||
|
||||
var contents = map[string]string{
|
||||
"gobuild.go": `
|
||||
//go:build go1.23
|
||||
package p
|
||||
`,
|
||||
"noversion.go": `
|
||||
package p
|
||||
`,
|
||||
}
|
||||
type fileTest struct {
|
||||
fname string
|
||||
want string
|
||||
}
|
||||
for _, item := range []struct {
|
||||
goversion string
|
||||
pversion string
|
||||
tests []fileTest
|
||||
}{
|
||||
{"", "", []fileTest{{"noversion.go", ""}, {"gobuild.go", ""}}},
|
||||
{"go1.22", "go1.22", []fileTest{{"noversion.go", "go1.22"}, {"gobuild.go", "go1.23"}}},
|
||||
} {
|
||||
name := fmt.Sprintf("types.Config{GoVersion:%q}", item.goversion)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
fset := token.NewFileSet()
|
||||
files := make([]*ast.File, len(item.tests))
|
||||
for i, test := range item.tests {
|
||||
files[i] = parse(t, fset, test.fname, contents[test.fname])
|
||||
}
|
||||
pkg, info := typeCheck(t, fset, files, item.goversion)
|
||||
if got, want := versions.GoVersion(pkg), item.pversion; versions.Compare(got, want) != 0 {
|
||||
t.Errorf("GoVersion()=%q. expected %q", got, want)
|
||||
}
|
||||
if got := versions.FileVersion(info, nil); got != "" {
|
||||
t.Errorf(`FileVersions(nil)=%q. expected ""`, got)
|
||||
}
|
||||
for i, test := range item.tests {
|
||||
if got, want := versions.FileVersion(info, files[i]), test.want; got != want {
|
||||
t.Errorf("FileVersions(%s)=%q. expected %q", test.fname, got, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func parse(t *testing.T, fset *token.FileSet, name, src string) *ast.File {
|
||||
file, err := parser.ParseFile(fset, name, src, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return file
|
||||
}
|
||||
|
||||
func typeCheck(t *testing.T, fset *token.FileSet, files []*ast.File, goversion string) (*types.Package, *types.Info) {
|
||||
conf := types.Config{
|
||||
Importer: importer.Default(),
|
||||
GoVersion: goversion,
|
||||
}
|
||||
info := types.Info{}
|
||||
versions.InitFileVersions(&info)
|
||||
pkg, err := conf.Check("", fset, files, &info)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return pkg, &info
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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 versions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Note: If we use build tags to use go/versions when go >=1.22,
|
||||
// we run into go.dev/issue/53737. Under some operations users would see an
|
||||
// import of "go/versions" even if they would not compile the file.
|
||||
// For example, during `go get -u ./...` (go.dev/issue/64490) we do not try to include
|
||||
// For this reason, this library just a clone of go/versions for the moment.
|
||||
|
||||
// Lang returns the Go language version for version x.
|
||||
// If x is not a valid version, Lang returns the empty string.
|
||||
// For example:
|
||||
//
|
||||
// Lang("go1.21rc2") = "go1.21"
|
||||
// Lang("go1.21.2") = "go1.21"
|
||||
// Lang("go1.21") = "go1.21"
|
||||
// Lang("go1") = "go1"
|
||||
// Lang("bad") = ""
|
||||
// Lang("1.21") = ""
|
||||
func Lang(x string) string {
|
||||
v := lang(stripGo(x))
|
||||
if v == "" {
|
||||
return ""
|
||||
}
|
||||
return x[:2+len(v)] // "go"+v without allocation
|
||||
}
|
||||
|
||||
// Compare returns -1, 0, or +1 depending on whether
|
||||
// x < y, x == y, or x > y, interpreted as Go versions.
|
||||
// The versions x and y must begin with a "go" prefix: "go1.21" not "1.21".
|
||||
// Invalid versions, including the empty string, compare less than
|
||||
// valid versions and equal to each other.
|
||||
// The language version "go1.21" compares less than the
|
||||
// release candidate and eventual releases "go1.21rc1" and "go1.21.0".
|
||||
// Custom toolchain suffixes are ignored during comparison:
|
||||
// "go1.21.0" and "go1.21.0-bigcorp" are equal.
|
||||
func Compare(x, y string) int { return compare(stripGo(x), stripGo(y)) }
|
||||
|
||||
// IsValid reports whether the version x is valid.
|
||||
func IsValid(x string) bool { return isValid(stripGo(x)) }
|
||||
|
||||
// stripGo converts from a "go1.21" version to a "1.21" version.
|
||||
// If v does not start with "go", stripGo returns the empty string (a known invalid version).
|
||||
func stripGo(v string) string {
|
||||
v, _, _ = strings.Cut(v, "-") // strip -bigcorp suffix.
|
||||
if len(v) < 2 || v[:2] != "go" {
|
||||
return ""
|
||||
}
|
||||
return v[2:]
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
// 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 versions_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/internal/testenv"
|
||||
"golang.org/x/tools/internal/versions"
|
||||
)
|
||||
|
||||
func TestIsValid(t *testing.T) {
|
||||
// valid versions
|
||||
for _, x := range []string{
|
||||
"go1.21",
|
||||
"go1.21.2",
|
||||
"go1.21rc",
|
||||
"go1.21rc2",
|
||||
"go0.0", // ??
|
||||
"go1",
|
||||
"go2",
|
||||
"go1.20.0-bigcorp",
|
||||
} {
|
||||
if !versions.IsValid(x) {
|
||||
t.Errorf("expected versions.IsValid(%q) to hold", x)
|
||||
}
|
||||
}
|
||||
|
||||
// invalid versions
|
||||
for _, x := range []string{
|
||||
"",
|
||||
"bad",
|
||||
"1.21",
|
||||
"v1.21",
|
||||
"go",
|
||||
"goAA",
|
||||
"go2_3",
|
||||
"go1.BB",
|
||||
"go1.21.",
|
||||
"go1.21.2_2",
|
||||
"go1.21rc_2",
|
||||
"go1.21rc2_",
|
||||
"go1.600+auto",
|
||||
} {
|
||||
if versions.IsValid(x) {
|
||||
t.Errorf("expected versions.IsValid(%q) to not hold", x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionComparisons(t *testing.T) {
|
||||
for _, item := range []struct {
|
||||
x, y string
|
||||
want int
|
||||
}{
|
||||
// All comparisons of go2, go1.21.2, go1.21rc2, go1.21rc2, go1, go0.0, "", bad
|
||||
{"go2", "go2", 0},
|
||||
{"go2", "go1.21.2", +1},
|
||||
{"go2", "go1.21rc2", +1},
|
||||
{"go2", "go1.21rc", +1},
|
||||
{"go2", "go1.21", +1},
|
||||
{"go2", "go1", +1},
|
||||
{"go2", "go0.0", +1},
|
||||
{"go2", "", +1},
|
||||
{"go2", "bad", +1},
|
||||
{"go1.21.2", "go1.21.2", 0},
|
||||
{"go1.21.2", "go1.21rc2", +1},
|
||||
{"go1.21.2", "go1.21rc", +1},
|
||||
{"go1.21.2", "go1.21", +1},
|
||||
{"go1.21.2", "go1", +1},
|
||||
{"go1.21.2", "go0.0", +1},
|
||||
{"go1.21.2", "", +1},
|
||||
{"go1.21.2", "bad", +1},
|
||||
{"go1.21rc2", "go1.21rc2", 0},
|
||||
{"go1.21rc2", "go1.21rc", +1},
|
||||
{"go1.21rc2", "go1.21", +1},
|
||||
{"go1.21rc2", "go1", +1},
|
||||
{"go1.21rc2", "go0.0", +1},
|
||||
{"go1.21rc2", "", +1},
|
||||
{"go1.21rc2", "bad", +1},
|
||||
{"go1.21rc", "go1.21rc", 0},
|
||||
{"go1.21rc", "go1.21", +1},
|
||||
{"go1.21rc", "go1", +1},
|
||||
{"go1.21rc", "go0.0", +1},
|
||||
{"go1.21rc", "", +1},
|
||||
{"go1.21rc", "bad", +1},
|
||||
{"go1.21", "go1.21", 0},
|
||||
{"go1.21", "go1", +1},
|
||||
{"go1.21", "go0.0", +1},
|
||||
{"go1.21", "", +1},
|
||||
{"go1.21", "bad", +1},
|
||||
{"go1", "go1", 0},
|
||||
{"go1", "go0.0", +1},
|
||||
{"go1", "", +1},
|
||||
{"go1", "bad", +1},
|
||||
{"go0.0", "go0.0", 0},
|
||||
{"go0.0", "", +1},
|
||||
{"go0.0", "bad", +1},
|
||||
{"", "", 0},
|
||||
{"", "bad", 0},
|
||||
{"bad", "bad", 0},
|
||||
// Other tests.
|
||||
{"go1.20", "go1.20.0-bigcorp", 0},
|
||||
{"go1.21", "go1.21.0-bigcorp", -1}, // Starting in Go 1.21, patch missing is different from explicit .0.
|
||||
{"go1.21.0", "go1.21.0-bigcorp", 0}, // Starting in Go 1.21, patch missing is different from explicit .0.
|
||||
{"go1.19rc1", "go1.19", -1},
|
||||
} {
|
||||
got := versions.Compare(item.x, item.y)
|
||||
if got != item.want {
|
||||
t.Errorf("versions.Compare(%q, %q)=%d. expected %d", item.x, item.y, got, item.want)
|
||||
}
|
||||
reverse := versions.Compare(item.y, item.x)
|
||||
if reverse != -got {
|
||||
t.Errorf("versions.Compare(%q, %q)=%d. expected %d", item.y, item.x, reverse, -got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLang(t *testing.T) {
|
||||
for _, item := range []struct {
|
||||
x string
|
||||
want string
|
||||
}{
|
||||
// valid
|
||||
{"go1.21rc2", "go1.21"},
|
||||
{"go1.21.2", "go1.21"},
|
||||
{"go1.21", "go1.21"},
|
||||
{"go1", "go1"},
|
||||
// invalid
|
||||
{"bad", ""},
|
||||
{"1.21", ""},
|
||||
} {
|
||||
if got := versions.Lang(item.x); got != item.want {
|
||||
t.Errorf("versions.Lang(%q)=%q. expected %q", item.x, got, item.want)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestKnown(t *testing.T) {
|
||||
for _, v := range [...]string{
|
||||
versions.Go1_18,
|
||||
versions.Go1_19,
|
||||
versions.Go1_20,
|
||||
versions.Go1_21,
|
||||
versions.Go1_22,
|
||||
} {
|
||||
if !versions.IsValid(v) {
|
||||
t.Errorf("Expected known version %q to be valid.", v)
|
||||
}
|
||||
if v != versions.Lang(v) {
|
||||
t.Errorf("Expected known version %q == Lang(%q).", v, versions.Lang(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAtLeast(t *testing.T) {
|
||||
for _, item := range [...]struct {
|
||||
v, release string
|
||||
want bool
|
||||
}{
|
||||
{versions.Future, versions.Go1_22, true},
|
||||
{versions.Go1_22, versions.Go1_22, true},
|
||||
{"go1.21", versions.Go1_22, false},
|
||||
{"invalid", versions.Go1_22, false},
|
||||
} {
|
||||
if got := versions.AtLeast(item.v, item.release); got != item.want {
|
||||
t.Errorf("AtLeast(%q, %q)=%v. wanted %v", item.v, item.release, got, item.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBefore(t *testing.T) {
|
||||
for _, item := range [...]struct {
|
||||
v, release string
|
||||
want bool
|
||||
}{
|
||||
{versions.Future, versions.Go1_22, false},
|
||||
{versions.Go1_22, versions.Go1_22, false},
|
||||
{"go1.21", versions.Go1_22, true},
|
||||
{"invalid", versions.Go1_22, true}, // invalid < Go1_22
|
||||
} {
|
||||
if got := versions.Before(item.v, item.release); got != item.want {
|
||||
t.Errorf("Before(%q, %q)=%v. wanted %v", item.v, item.release, got, item.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileVersions122(t *testing.T) {
|
||||
testenv.NeedsGo1Point(t, 22)
|
||||
|
||||
const source = `
|
||||
package P
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, "hello.go", source, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, conf := range []types.Config{
|
||||
{GoVersion: versions.Go1_22},
|
||||
{}, // GoVersion is unset.
|
||||
} {
|
||||
info := &types.Info{}
|
||||
versions.InitFileVersions(info)
|
||||
|
||||
_, err = conf.Check("P", fset, []*ast.File{f}, info)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
v := versions.FileVersion(info, f)
|
||||
if !versions.AtLeast(v, versions.Go1_22) {
|
||||
t.Errorf("versions.AtLeast(%q, %q) expected to hold", v, versions.Go1_22)
|
||||
}
|
||||
|
||||
if versions.Before(v, versions.Go1_22) {
|
||||
t.Errorf("versions.AtLeast(%q, %q) expected to be false", v, versions.Go1_22)
|
||||
}
|
||||
|
||||
if conf.GoVersion == "" && v != versions.Future {
|
||||
t.Error("Expected the FileVersion to be the Future when conf.GoVersion is unset")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFileVersions121(t *testing.T) {
|
||||
testenv.SkipAfterGo1Point(t, 21)
|
||||
|
||||
// If <1.22, info and file are ignored.
|
||||
v := versions.FileVersion(nil, nil)
|
||||
oneof := map[string]bool{
|
||||
versions.Go1_18: true,
|
||||
versions.Go1_19: true,
|
||||
versions.Go1_20: true,
|
||||
versions.Go1_21: true,
|
||||
}
|
||||
if !oneof[v] {
|
||||
t.Errorf("FileVersion(...)=%q expected to be a known go version <1.22", v)
|
||||
}
|
||||
|
||||
if versions.AtLeast(v, versions.Go1_22) {
|
||||
t.Errorf("versions.AtLeast(%q, %q) expected to be false", v, versions.Go1_22)
|
||||
}
|
||||
|
||||
if !versions.Before(v, versions.Go1_22) {
|
||||
t.Errorf("versions.Before(%q, %q) expected to hold", v, versions.Go1_22)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user