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  )
    14  
    15  // A goVersion is a Go language version string of the form "go1.%d"
    16  // where d is the minor version number. goVersion strings don't
    17  // contain release numbers ("go1.20.1" is not a valid goVersion).
    18  type goVersion string
    19  
    20  // asGoVersion returns v as a goVersion (e.g., "go1.20.1" becomes "go1.20").
    21  // If v is not a valid Go version, the result is the empty string.
    22  func asGoVersion(v string) goVersion {
    23  	return goVersion(version.Lang(v))
    24  }
    25  
    26  // isValid reports whether v is a valid Go version.
    27  func (v goVersion) isValid() bool {
    28  	return v != ""
    29  }
    30  
    31  // cmp returns -1, 0, or +1 depending on whether x < y, x == y, or x > y,
    32  // interpreted as Go versions.
    33  func (x goVersion) cmp(y goVersion) int {
    34  	return version.Compare(string(x), string(y))
    35  }
    36  
    37  var (
    38  	// Go versions that introduced language changes
    39  	go1_9  = asGoVersion("go1.9")
    40  	go1_13 = asGoVersion("go1.13")
    41  	go1_14 = asGoVersion("go1.14")
    42  	go1_17 = asGoVersion("go1.17")
    43  	go1_18 = asGoVersion("go1.18")
    44  	go1_20 = asGoVersion("go1.20")
    45  	go1_21 = asGoVersion("go1.21")
    46  	go1_22 = asGoVersion("go1.22")
    47  	go1_23 = asGoVersion("go1.23")
    48  
    49  	// current (deployed) Go version
    50  	go_current = asGoVersion(fmt.Sprintf("go1.%d", goversion.Version))
    51  )
    52  
    53  // allowVersion reports whether the current package at the given position
    54  // is allowed to use version v. If the position is unknown, the specified
    55  // module version (Config.GoVersion) is used. If that version is invalid,
    56  // allowVersion returns true.
    57  func (check *Checker) allowVersion(at positioner, v goVersion) bool {
    58  	fileVersion := check.conf.GoVersion
    59  	if pos := at.Pos(); pos.IsValid() {
    60  		fileVersion = check.versions[check.fileFor(pos)]
    61  	}
    62  
    63  	// We need asGoVersion (which calls version.Lang) below
    64  	// because fileVersion may be the (unaltered) Config.GoVersion
    65  	// string which may contain dot-release information.
    66  	version := asGoVersion(fileVersion)
    67  
    68  	return !version.isValid() || version.cmp(v) >= 0
    69  }
    70  
    71  // verifyVersionf is like allowVersion but also accepts a format string and arguments
    72  // which are used to report a version error if allowVersion returns false. It uses the
    73  // current package.
    74  func (check *Checker) verifyVersionf(at positioner, v goVersion, format string, args ...interface{}) bool {
    75  	if !check.allowVersion(at, v) {
    76  		check.versionErrorf(at, v, format, args...)
    77  		return false
    78  	}
    79  	return true
    80  }
    81  
    82  // TODO(gri) Consider a more direct (position-independent) mechanism
    83  //           to identify which file we're in so that version checks
    84  //           work correctly in the absence of correct position info.
    85  
    86  // fileFor returns the *ast.File which contains the position pos.
    87  // If there are no files, the result is nil.
    88  // The position must be valid.
    89  func (check *Checker) fileFor(pos token.Pos) *ast.File {
    90  	assert(pos.IsValid())
    91  	// Eval and CheckExpr tests may not have any source files.
    92  	if len(check.files) == 0 {
    93  		return nil
    94  	}
    95  	for _, file := range check.files {
    96  		if file.FileStart <= pos && pos < file.FileEnd {
    97  			return file
    98  		}
    99  	}
   100  	panic(check.sprintf("file not found for pos = %d (%s)", int(pos), check.fset.Position(pos)))
   101  }
   102  

View as plain text