1 // Copyright 2023 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 // This is a fork of internal/gover for use by x/tools until
6 // go1.21 and earlier are no longer supported by x/tools.
7 8 package versions
9 10 import "strings"
11 12 // A gover is a parsed Go gover: major[.Minor[.Patch]][kind[pre]]
13 // The numbers are the original decimal strings to avoid integer overflows
14 // and since there is very little actual math. (Probably overflow doesn't matter in practice,
15 // but at the time this code was written, there was an existing test that used
16 // go1.99999999999, which does not fit in an int on 32-bit platforms.
17 // The "big decimal" representation avoids the problem entirely.)
18 type gover struct {
19 major string // decimal
20 minor string // decimal or ""
21 patch string // decimal or ""
22 kind string // "", "alpha", "beta", "rc"
23 pre string // decimal or ""
24 }
25 26 // compare returns -1, 0, or +1 depending on whether
27 // x < y, x == y, or x > y, interpreted as toolchain versions.
28 // The versions x and y must not begin with a "go" prefix: just "1.21" not "go1.21".
29 // Malformed versions compare less than well-formed versions and equal to each other.
30 // The language version "1.21" compares less than the release candidate and eventual releases "1.21rc1" and "1.21.0".
31 func compare(x, y string) int {
32 vx := parse(x)
33 vy := parse(y)
34 35 if c := cmpInt(vx.major, vy.major); c != 0 {
36 return c
37 }
38 if c := cmpInt(vx.minor, vy.minor); c != 0 {
39 return c
40 }
41 if c := cmpInt(vx.patch, vy.patch); c != 0 {
42 return c
43 }
44 if c := strings.Compare(vx.kind, vy.kind); c != 0 { // "" < alpha < beta < rc
45 return c
46 }
47 if c := cmpInt(vx.pre, vy.pre); c != 0 {
48 return c
49 }
50 return 0
51 }
52 53 // lang returns the Go language version. For example, lang("1.2.3") == "1.2".
54 func lang(x string) string {
55 v := parse(x)
56 if v.minor == "" || v.major == "1" && v.minor == "0" {
57 return v.major
58 }
59 return v.major + "." + v.minor
60 }
61 62 // isValid reports whether the version x is valid.
63 func isValid(x string) bool {
64 return parse(x) != gover{}
65 }
66 67 // parse parses the Go version string x into a version.
68 // It returns the zero version if x is malformed.
69 func parse(x string) gover {
70 var v gover
71 72 // Parse major version.
73 var ok bool
74 v.major, x, ok = cutInt(x)
75 if !ok {
76 return gover{}
77 }
78 if x == "" {
79 // Interpret "1" as "1.0.0".
80 v.minor = "0"
81 v.patch = "0"
82 return v
83 }
84 85 // Parse . before minor version.
86 if x[0] != '.' {
87 return gover{}
88 }
89 90 // Parse minor version.
91 v.minor, x, ok = cutInt(x[1:])
92 if !ok {
93 return gover{}
94 }
95 if x == "" {
96 // Patch missing is same as "0" for older versions.
97 // Starting in Go 1.21, patch missing is different from explicit .0.
98 if cmpInt(v.minor, "21") < 0 {
99 v.patch = "0"
100 }
101 return v
102 }
103 104 // Parse patch if present.
105 if x[0] == '.' {
106 v.patch, x, ok = cutInt(x[1:])
107 if !ok || x != "" {
108 // Note that we are disallowing prereleases (alpha, beta, rc) for patch releases here (x != "").
109 // Allowing them would be a bit confusing because we already have:
110 // 1.21 < 1.21rc1
111 // But a prerelease of a patch would have the opposite effect:
112 // 1.21.3rc1 < 1.21.3
113 // We've never needed them before, so let's not start now.
114 return gover{}
115 }
116 return v
117 }
118 119 // Parse prerelease.
120 i := 0
121 for i < len(x) && (x[i] < '0' || '9' < x[i]) {
122 if x[i] < 'a' || 'z' < x[i] {
123 return gover{}
124 }
125 i++
126 }
127 if i == 0 {
128 return gover{}
129 }
130 v.kind, x = x[:i], x[i:]
131 if x == "" {
132 return v
133 }
134 v.pre, x, ok = cutInt(x)
135 if !ok || x != "" {
136 return gover{}
137 }
138 139 return v
140 }
141 142 // cutInt scans the leading decimal number at the start of x to an integer
143 // and returns that value and the rest of the string.
144 func cutInt(x string) (n, rest string, ok bool) {
145 i := 0
146 for i < len(x) && '0' <= x[i] && x[i] <= '9' {
147 i++
148 }
149 if i == 0 || x[0] == '0' && i != 1 { // no digits or unnecessary leading zero
150 return "", "", false
151 }
152 return x[:i], x[i:], true
153 }
154 155 // cmpInt returns cmp.Compare(x, y) interpreting x and y as decimal numbers.
156 // (Copied from golang.org/x/mod/semver's compareInt.)
157 func cmpInt(x, y string) int {
158 if x == y {
159 return 0
160 }
161 if len(x) < len(y) {
162 return -1
163 }
164 if len(x) > len(y) {
165 return +1
166 }
167 if x < y {
168 return -1
169 } else {
170 return +1
171 }
172 }
173