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