Source file src/go/types/version.go

     1  // Copyright 2021 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 types
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/token"
    11  	"go/version"
    12  	"internal/goversion"
    13  	"strings"
    14  )
    15  
    16  // A goVersion is a Go language version string of the form "go1.%d"
    17  // where d is the minor version number. goVersion strings don't
    18  // contain release numbers ("go1.20.1" is not a valid goVersion).
    19  type goVersion string
    20  
    21  // asGoVersion returns v as a goVersion (e.g., "go1.20.1" becomes "go1.20").
    22  // If v is not a valid Go version, the result is the empty string.
    23  func asGoVersion(v string) goVersion {
    24  	return goVersion(version.Lang(v))
    25  }
    26  
    27  // isValid reports whether v is a valid Go version.
    28  func (v goVersion) isValid() bool {
    29  	return v != ""
    30  }
    31  
    32  // cmp returns -1, 0, or +1 depending on whether x < y, x == y, or x > y,
    33  // interpreted as Go versions.
    34  func (x goVersion) cmp(y goVersion) int {
    35  	return version.Compare(string(x), string(y))
    36  }
    37  
    38  var (
    39  	// Go versions that introduced language changes
    40  	go1_9  = asGoVersion("go1.9")
    41  	go1_13 = asGoVersion("go1.13")
    42  	go1_14 = asGoVersion("go1.14")
    43  	go1_17 = asGoVersion("go1.17")
    44  	go1_18 = asGoVersion("go1.18")
    45  	go1_20 = asGoVersion("go1.20")
    46  	go1_21 = asGoVersion("go1.21")
    47  	go1_22 = asGoVersion("go1.22")
    48  
    49  	// current (deployed) Go version
    50  	go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
    51  )
    52  
    53  // langCompat reports an error if the representation of a numeric
    54  // literal is not compatible with the current language version.
    55  func (check *Checker) langCompat(lit *ast.BasicLit) {
    56  	s := lit.Value
    57  	if len(s) <= 2 || check.allowVersion(check.pkg, lit, go1_13) {
    58  		return
    59  	}
    60  	// len(s) > 2
    61  	if strings.Contains(s, "_") {
    62  		check.versionErrorf(lit, go1_13, "underscores in numeric literals")
    63  		return
    64  	}
    65  	if s[0] != '0' {
    66  		return
    67  	}
    68  	radix := s[1]
    69  	if radix == 'b' || radix == 'B' {
    70  		check.versionErrorf(lit, go1_13, "binary literals")
    71  		return
    72  	}
    73  	if radix == 'o' || radix == 'O' {
    74  		check.versionErrorf(lit, go1_13, "0o/0O-style octal literals")
    75  		return
    76  	}
    77  	if lit.Kind != token.INT && (radix == 'x' || radix == 'X') {
    78  		check.versionErrorf(lit, go1_13, "hexadecimal floating-point literals")
    79  	}
    80  }
    81  
    82  // allowVersion reports whether the given package is allowed to use version v.
    83  func (check *Checker) allowVersion(pkg *Package, at positioner, v goVersion) bool {
    84  	// We assume that imported packages have all been checked,
    85  	// so we only have to check for the local package.
    86  	if pkg != check.pkg {
    87  		return true
    88  	}
    89  
    90  	// If no explicit file version is specified,
    91  	// fileVersion corresponds to the module version.
    92  	var fileVersion goVersion
    93  	if pos := at.Pos(); pos.IsValid() {
    94  		// We need version.Lang below because file versions
    95  		// can be (unaltered) Config.GoVersion strings that
    96  		// may contain dot-release information.
    97  		fileVersion = asGoVersion(check.versions[check.fileFor(pos)])
    98  	}
    99  	return !fileVersion.isValid() || fileVersion.cmp(v) >= 0
   100  }
   101  
   102  // verifyVersionf is like allowVersion but also accepts a format string and arguments
   103  // which are used to report a version error if allowVersion returns false. It uses the
   104  // current package.
   105  func (check *Checker) verifyVersionf(at positioner, v goVersion, format string, args ...interface{}) bool {
   106  	if !check.allowVersion(check.pkg, at, v) {
   107  		check.versionErrorf(at, v, format, args...)
   108  		return false
   109  	}
   110  	return true
   111  }
   112  
   113  // TODO(gri) Consider a more direct (position-independent) mechanism
   114  //           to identify which file we're in so that version checks
   115  //           work correctly in the absence of correct position info.
   116  
   117  // fileFor returns the *ast.File which contains the position pos.
   118  // If there are no files, the result is nil.
   119  // The position must be valid.
   120  func (check *Checker) fileFor(pos token.Pos) *ast.File {
   121  	assert(pos.IsValid())
   122  	// Eval and CheckExpr tests may not have any source files.
   123  	if len(check.files) == 0 {
   124  		return nil
   125  	}
   126  	for _, file := range check.files {
   127  		if file.FileStart <= pos && pos < file.FileEnd {
   128  			return file
   129  		}
   130  	}
   131  	panic(check.sprintf("file not found for pos = %d (%s)", int(pos), check.fset.Position(pos)))
   132  }
   133  

View as plain text