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
5package types
6
7import (
8	"fmt"
9	"go/ast"
10	"go/token"
11	"regexp"
12	"strconv"
13	"strings"
14)
15
16// langCompat reports an error if the representation of a numeric
17// literal is not compatible with the current language version.
18func (check *Checker) langCompat(lit *ast.BasicLit) {
19	s := lit.Value
20	if len(s) <= 2 || check.allowVersion(check.pkg, 1, 13) {
21		return
22	}
23	// len(s) > 2
24	if strings.Contains(s, "_") {
25		check.errorf(lit, _InvalidLit, "underscores in numeric literals requires go1.13 or later")
26		return
27	}
28	if s[0] != '0' {
29		return
30	}
31	radix := s[1]
32	if radix == 'b' || radix == 'B' {
33		check.errorf(lit, _InvalidLit, "binary literals requires go1.13 or later")
34		return
35	}
36	if radix == 'o' || radix == 'O' {
37		check.errorf(lit, _InvalidLit, "0o/0O-style octal literals requires go1.13 or later")
38		return
39	}
40	if lit.Kind != token.INT && (radix == 'x' || radix == 'X') {
41		check.errorf(lit, _InvalidLit, "hexadecimal floating-point literals requires go1.13 or later")
42	}
43}
44
45// allowVersion reports whether the given package
46// is allowed to use version major.minor.
47func (check *Checker) allowVersion(pkg *Package, major, minor int) bool {
48	// We assume that imported packages have all been checked,
49	// so we only have to check for the local package.
50	if pkg != check.pkg {
51		return true
52	}
53	ma, mi := check.version.major, check.version.minor
54	return ma == 0 && mi == 0 || ma > major || ma == major && mi >= minor
55}
56
57type version struct {
58	major, minor int
59}
60
61// parseGoVersion parses a Go version string (such as "go1.12")
62// and returns the version, or an error. If s is the empty
63// string, the version is 0.0.
64func parseGoVersion(s string) (v version, err error) {
65	if s == "" {
66		return
67	}
68	matches := goVersionRx.FindStringSubmatch(s)
69	if matches == nil {
70		err = fmt.Errorf(`should be something like "go1.12"`)
71		return
72	}
73	v.major, err = strconv.Atoi(matches[1])
74	if err != nil {
75		return
76	}
77	v.minor, err = strconv.Atoi(matches[2])
78	return
79}
80
81// goVersionRx matches a Go version string, e.g. "go1.12".
82var goVersionRx = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`)
83