github.com/AndrienkoAleksandr/go@v0.0.19/src/go/build/constraint/vers.go (about)

     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  package constraint
     6  
     7  import (
     8  	"strconv"
     9  	"strings"
    10  )
    11  
    12  // GoVersion returns the minimum Go version implied by a given build expression.
    13  // If the expression can be satisfied without any Go version tags, GoVersion returns an empty string.
    14  //
    15  // For example:
    16  //
    17  //	GoVersion(linux && go1.22) = "go1.22"
    18  //	GoVersion((linux && go1.22) || (windows && go1.20)) = "go1.20" => go1.20
    19  //	GoVersion(linux) = ""
    20  //	GoVersion(linux || (windows && go1.22)) = ""
    21  //	GoVersion(!go1.22) = ""
    22  //
    23  // GoVersion assumes that any tag or negated tag may independently be true,
    24  // so that its analysis can be purely structural, without SAT solving.
    25  // “Impossible” subexpressions may therefore affect the result.
    26  //
    27  // For example:
    28  //
    29  //	GoVersion((linux && !linux && go1.20) || go1.21) = "go1.20"
    30  func GoVersion(x Expr) string {
    31  	v := minVersion(x, +1)
    32  	if v < 0 {
    33  		return ""
    34  	}
    35  	if v == 0 {
    36  		return "go1"
    37  	}
    38  	return "go1." + strconv.Itoa(v)
    39  }
    40  
    41  // minVersion returns the minimum Go major version (9 for go1.9)
    42  // implied by expression z, or if sign < 0, by expression !z.
    43  func minVersion(z Expr, sign int) int {
    44  	switch z := z.(type) {
    45  	default:
    46  		return -1
    47  	case *AndExpr:
    48  		op := andVersion
    49  		if sign < 0 {
    50  			op = orVersion
    51  		}
    52  		return op(minVersion(z.X, sign), minVersion(z.Y, sign))
    53  	case *OrExpr:
    54  		op := orVersion
    55  		if sign < 0 {
    56  			op = andVersion
    57  		}
    58  		return op(minVersion(z.X, sign), minVersion(z.Y, sign))
    59  	case *NotExpr:
    60  		return minVersion(z.X, -sign)
    61  	case *TagExpr:
    62  		if sign < 0 {
    63  			// !foo implies nothing
    64  			return -1
    65  		}
    66  		if z.Tag == "go1" {
    67  			return 0
    68  		}
    69  		_, v, _ := stringsCut(z.Tag, "go1.")
    70  		n, err := strconv.Atoi(v)
    71  		if err != nil {
    72  			// not a go1.N tag
    73  			return -1
    74  		}
    75  		return n
    76  	}
    77  }
    78  
    79  // TODO: Delete, replace calls with strings.Cut once Go bootstrap toolchain is bumped.
    80  func stringsCut(s, sep string) (before, after string, found bool) {
    81  	if i := strings.Index(s, sep); i >= 0 {
    82  		return s[:i], s[i+len(sep):], true
    83  	}
    84  	return s, "", false
    85  }
    86  
    87  // andVersion returns the minimum Go version
    88  // implied by the AND of two minimum Go versions,
    89  // which is the max of the versions.
    90  func andVersion(x, y int) int {
    91  	if x > y {
    92  		return x
    93  	}
    94  	return y
    95  }
    96  
    97  // orVersion returns the minimum Go version
    98  // implied by the OR of two minimum Go versions,
    99  // which is the min of the versions.
   100  func orVersion(x, y int) int {
   101  	if x < y {
   102  		return x
   103  	}
   104  	return y
   105  }