Unverified Commit aa13f3fa authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #59289 from rmmh/semantic-check

Automatic merge from submit-queue (batch tested with PRs 53689, 56880, 55856, 59289, 60249). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Add test/typecheck, a fast typecheck for all build platforms. Add test/typecheck, a fast typecheck for all build platforms. Most of the time spent compiling is spent optimizing and linking binary code. Most errors occur at the syntax or semantic (type) layers. Go's compiler is importable as a normal package, so we can do fast syntax and type checking for the 10 platforms we build on. This currently takes ~6 minutes of CPU time (parallelized). This makes presubmit cross builds superfluous, since it should catch most cross-build breaks (generally Unix and 64-bit assumptions). Example output: ```$ time go run test/typecheck/main.go type-checking: linux/amd64, windows/386, darwin/amd64, linux/arm, linux/386, windows/amd64, linux/arm64, linux/ppc64le, linux/s390x, darwin/386 ERROR(windows/amd64) pkg/proxy/ipvs/proxier.go:1708:27: ENXIO not declared by package unix ERROR(windows/386) pkg/proxy/ipvs/proxier.go:1708:27: ENXIO not declared by package unix real 0m45.083s user 6m15.504s sys 1m14.000s ``` ```release-note NONE ```
parents 88cfcb45 1b371135
......@@ -186,21 +186,104 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["server_test.go"],
srcs = [
"server_test.go",
] + select({
"@io_bazel_rules_go//go/platform:android": [
"server_others_test.go",
],
"@io_bazel_rules_go//go/platform:darwin": [
"server_others_test.go",
],
"@io_bazel_rules_go//go/platform:dragonfly": [
"server_others_test.go",
],
"@io_bazel_rules_go//go/platform:freebsd": [
"server_others_test.go",
],
"@io_bazel_rules_go//go/platform:linux": [
"server_others_test.go",
],
"@io_bazel_rules_go//go/platform:nacl": [
"server_others_test.go",
],
"@io_bazel_rules_go//go/platform:netbsd": [
"server_others_test.go",
],
"@io_bazel_rules_go//go/platform:openbsd": [
"server_others_test.go",
],
"@io_bazel_rules_go//go/platform:plan9": [
"server_others_test.go",
],
"@io_bazel_rules_go//go/platform:solaris": [
"server_others_test.go",
],
"//conditions:default": [],
}),
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/features:go_default_library",
"//pkg/proxy/apis/kubeproxyconfig:go_default_library",
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/configz:go_default_library",
"//pkg/util/iptables:go_default_library",
"//pkg/util/pointer:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
] + select({
"@io_bazel_rules_go//go/platform:android": [
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/iptables:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
"@io_bazel_rules_go//go/platform:darwin": [
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/iptables:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
"@io_bazel_rules_go//go/platform:dragonfly": [
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/iptables:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
"@io_bazel_rules_go//go/platform:freebsd": [
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/iptables:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
"@io_bazel_rules_go//go/platform:linux": [
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/iptables:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
"@io_bazel_rules_go//go/platform:nacl": [
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/iptables:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
"@io_bazel_rules_go//go/platform:netbsd": [
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/iptables:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
"@io_bazel_rules_go//go/platform:openbsd": [
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/iptables:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
"@io_bazel_rules_go//go/platform:plan9": [
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/iptables:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
"@io_bazel_rules_go//go/platform:solaris": [
"//pkg/proxy/ipvs:go_default_library",
"//pkg/util/iptables:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
"//conditions:default": [],
}),
)
filegroup(
......
// +build !windows
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package app
import (
"fmt"
"testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/proxy/ipvs"
"k8s.io/kubernetes/pkg/util/iptables"
)
func Test_getProxyMode(t *testing.T) {
var cases = []struct {
flag string
annotationKey string
annotationVal string
iptablesVersion string
ipsetVersion string
kmods []string
kernelCompat bool
iptablesError error
ipsetError error
expected string
}{
{ // flag says userspace
flag: "userspace",
expected: proxyModeUserspace,
},
{ // flag says iptables, error detecting version
flag: "iptables",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // flag says iptables, version too low
flag: "iptables",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // flag says iptables, version ok, kernel not compatible
flag: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // flag says iptables, version ok, kernel is compatible
flag: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // detect, error
flag: "",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // detect, version too low
flag: "",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // detect, version ok, kernel not compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // detect, version ok, kernel is compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // specify ipvs, feature gateway disabled, iptables version ok, kernel is compatible
flag: "ipvs",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // specify ipvs, feature gateway disabled, iptables version too low
flag: "ipvs",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // specify ipvs, feature gateway disabled, iptables version ok, kernel is not compatible
flag: "ipvs",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
}
for i, c := range cases {
versioner := &fakeIPTablesVersioner{c.iptablesVersion, c.iptablesError}
kcompater := &fakeKernelCompatTester{c.kernelCompat}
ipsetver := &fakeIPSetVersioner{c.ipsetVersion, c.ipsetError}
khandler := &fakeKernelHandler{c.kmods}
r := getProxyMode(c.flag, versioner, khandler, ipsetver, kcompater)
if r != c.expected {
t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r)
}
}
}
// This is a coarse test, but it offers some modicum of confidence as the code is evolved.
func Test_getProxyModeEnableFeatureGateway(t *testing.T) {
// enable IPVS feature gateway
utilfeature.DefaultFeatureGate.Set("SupportIPVSProxyMode=true")
var cases = []struct {
flag string
iptablesVersion string
ipsetVersion string
kernelCompat bool
iptablesError error
ipsetError error
mods []string
expected string
}{
{ // flag says userspace
flag: "userspace",
expected: proxyModeUserspace,
},
{ // flag says iptables, error detecting version
flag: "iptables",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // flag says iptables, version too low
flag: "iptables",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // flag says iptables, version ok, kernel not compatible
flag: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // flag says iptables, version ok, kernel is compatible
flag: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // detect, error
flag: "",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // detect, version too low
flag: "",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // detect, version ok, kernel not compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // detect, version ok, kernel is compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // detect, version ok, kernel is compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // flag says ipvs, ipset version ok, kernel modules installed
flag: "ipvs",
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
ipsetVersion: ipvs.MinIPSetCheckVersion,
expected: proxyModeIPVS,
},
{ // flag says ipvs, ipset version too low, fallback on iptables mode
flag: "ipvs",
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
ipsetVersion: "0.0",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // flag says ipvs, bad ipset version, fallback on iptables mode
flag: "ipvs",
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
ipsetVersion: "a.b.c",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // flag says ipvs, required kernel modules are not installed, fallback on iptables mode
flag: "ipvs",
mods: []string{"foo", "bar", "baz"},
ipsetVersion: ipvs.MinIPSetCheckVersion,
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // flag says ipvs, required kernel modules are not installed, iptables version too old, fallback on userspace mode
flag: "ipvs",
mods: []string{"foo", "bar", "baz"},
ipsetVersion: ipvs.MinIPSetCheckVersion,
iptablesVersion: "0.0.0",
kernelCompat: true,
expected: proxyModeUserspace,
},
{ // flag says ipvs, ipset version too low, iptables version too old, kernel not compatible, fallback on userspace mode
flag: "ipvs",
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
ipsetVersion: "0.0",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
}
for i, c := range cases {
versioner := &fakeIPTablesVersioner{c.iptablesVersion, c.iptablesError}
kcompater := &fakeKernelCompatTester{c.kernelCompat}
ipsetver := &fakeIPSetVersioner{c.ipsetVersion, c.ipsetError}
khandle := &fakeKernelHandler{c.mods}
r := getProxyMode(c.flag, versioner, khandle, ipsetver, kcompater)
if r != c.expected {
t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r)
}
}
}
......@@ -28,13 +28,10 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff"
utilfeature "k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/proxy/apis/kubeproxyconfig"
"k8s.io/kubernetes/pkg/proxy/ipvs"
"k8s.io/kubernetes/pkg/util/configz"
"k8s.io/kubernetes/pkg/util/iptables"
utilpointer "k8s.io/kubernetes/pkg/util/pointer"
)
......@@ -55,6 +52,10 @@ func (fake *fakeIPTablesVersioner) GetVersion() (string, error) {
return fake.version, fake.err
}
func (fake *fakeIPTablesVersioner) IsCompatible() error {
return fake.err
}
type fakeIPSetVersioner struct {
version string // what to return
err error // what to return
......@@ -84,232 +85,6 @@ func (fake *fakeKernelHandler) GetModules() ([]string, error) {
return fake.modules, nil
}
func Test_getProxyMode(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("skipping on non-Linux")
}
var cases = []struct {
flag string
annotationKey string
annotationVal string
iptablesVersion string
ipsetVersion string
kmods []string
kernelCompat bool
iptablesError error
ipsetError error
expected string
}{
{ // flag says userspace
flag: "userspace",
expected: proxyModeUserspace,
},
{ // flag says iptables, error detecting version
flag: "iptables",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // flag says iptables, version too low
flag: "iptables",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // flag says iptables, version ok, kernel not compatible
flag: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // flag says iptables, version ok, kernel is compatible
flag: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // detect, error
flag: "",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // detect, version too low
flag: "",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // detect, version ok, kernel not compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // detect, version ok, kernel is compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // specify ipvs, feature gateway disabled, iptables version ok, kernel is compatible
flag: "ipvs",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // specify ipvs, feature gateway disabled, iptables version too low
flag: "ipvs",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // specify ipvs, feature gateway disabled, iptables version ok, kernel is not compatible
flag: "ipvs",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
}
for i, c := range cases {
versioner := &fakeIPTablesVersioner{c.iptablesVersion, c.iptablesError}
kcompater := &fakeKernelCompatTester{c.kernelCompat}
ipsetver := &fakeIPSetVersioner{c.ipsetVersion, c.ipsetError}
khandler := &fakeKernelHandler{c.kmods}
r := getProxyMode(c.flag, versioner, khandler, ipsetver, kcompater)
if r != c.expected {
t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r)
}
}
}
// This is a coarse test, but it offers some modicum of confidence as the code is evolved.
func Test_getProxyModeEnableFeatureGateway(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("skipping on non-Linux")
}
// enable IPVS feature gateway
utilfeature.DefaultFeatureGate.Set("SupportIPVSProxyMode=true")
var cases = []struct {
flag string
iptablesVersion string
ipsetVersion string
kernelCompat bool
iptablesError error
ipsetError error
mods []string
expected string
}{
{ // flag says userspace
flag: "userspace",
expected: proxyModeUserspace,
},
{ // flag says iptables, error detecting version
flag: "iptables",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // flag says iptables, version too low
flag: "iptables",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // flag says iptables, version ok, kernel not compatible
flag: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // flag says iptables, version ok, kernel is compatible
flag: "iptables",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // detect, error
flag: "",
iptablesError: fmt.Errorf("oops!"),
expected: proxyModeUserspace,
},
{ // detect, version too low
flag: "",
iptablesVersion: "0.0.0",
expected: proxyModeUserspace,
},
{ // detect, version ok, kernel not compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
{ // detect, version ok, kernel is compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // detect, version ok, kernel is compatible
flag: "",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // flag says ipvs, ipset version ok, kernel modules installed
flag: "ipvs",
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
ipsetVersion: ipvs.MinIPSetCheckVersion,
expected: proxyModeIPVS,
},
{ // flag says ipvs, ipset version too low, fallback on iptables mode
flag: "ipvs",
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
ipsetVersion: "0.0",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // flag says ipvs, bad ipset version, fallback on iptables mode
flag: "ipvs",
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
ipsetVersion: "a.b.c",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // flag says ipvs, required kernel modules are not installed, fallback on iptables mode
flag: "ipvs",
mods: []string{"foo", "bar", "baz"},
ipsetVersion: ipvs.MinIPSetCheckVersion,
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: true,
expected: proxyModeIPTables,
},
{ // flag says ipvs, required kernel modules are not installed, iptables version too old, fallback on userspace mode
flag: "ipvs",
mods: []string{"foo", "bar", "baz"},
ipsetVersion: ipvs.MinIPSetCheckVersion,
iptablesVersion: "0.0.0",
kernelCompat: true,
expected: proxyModeUserspace,
},
{ // flag says ipvs, ipset version too low, iptables version too old, kernel not compatible, fallback on userspace mode
flag: "ipvs",
mods: []string{"ip_vs", "ip_vs_rr", "ip_vs_wrr", "ip_vs_sh", "nf_conntrack_ipv4"},
ipsetVersion: "0.0",
iptablesVersion: iptables.MinCheckVersion,
kernelCompat: false,
expected: proxyModeUserspace,
},
}
for i, c := range cases {
versioner := &fakeIPTablesVersioner{c.iptablesVersion, c.iptablesError}
kcompater := &fakeKernelCompatTester{c.kernelCompat}
ipsetver := &fakeIPSetVersioner{c.ipsetVersion, c.ipsetError}
khandle := &fakeKernelHandler{c.mods}
r := getProxyMode(c.flag, versioner, khandle, ipsetver, kcompater)
if r != c.expected {
t.Errorf("Case[%d] Expected %q, got %q", i, c.expected, r)
}
}
}
// This test verifies that NewProxyServer does not crash when CleanupAndExit is true.
func TestProxyServerWithCleanupAndExit(t *testing.T) {
// Each bind address below is a separate test case
......
......@@ -30,6 +30,7 @@ EXCLUDED_PATTERNS=(
"verify-linkcheck.sh" # runs in separate Jenkins job once per day due to high network usage
"verify-test-owners.sh" # TODO(rmmh): figure out how to avoid endless conflicts
"verify-*-dockerized.sh" # Don't run any scripts that intended to be run dockerized
"verify-typecheck.sh" # runs in separate typecheck job
)
# Only run whitelisted fast checks in quick mode.
......
#!/bin/bash
# Copyright 2018 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
source "${KUBE_ROOT}/hack/lib/init.sh"
kube::golang::verify_go_version
cd "${KUBE_ROOT}"
if [[ ! -f test/e2e/generated/bindata.go ]]; then
make --no-print-directory -C "${KUBE_ROOT}" verify_generated_files
fi
ret=0
go run test/typecheck/main.go "$@" || ret=$?
if [[ $ret -ne 0 ]]; then
echo "!!! Type Check has failed. This may cause cross platform build failures." >&2
echo "!!! Please see https://git.k8s.io/kubernetes/test/typecheck for more information." >&2
exit 1
fi
......@@ -308,7 +308,6 @@ func TestGetNodeConditionPredicate(t *testing.T) {
func TestProcessServiceUpdate(t *testing.T) {
var controller *ServiceController
var cloud *fakecloud.FakeCloud
//A pair of old and new loadbalancer IP address
oldLBIP := "192.168.1.1"
......@@ -327,7 +326,7 @@ func TestProcessServiceUpdate(t *testing.T) {
svc: defaultExternalService(),
updateFn: func(svc *v1.Service) *v1.Service {
controller, cloud, _ = newController()
controller, _, _ = newController()
controller.cache.getOrCreate("validKey")
return svc
......@@ -398,7 +397,6 @@ func TestProcessServiceUpdate(t *testing.T) {
func TestSyncService(t *testing.T) {
var controller *ServiceController
var cloud *fakecloud.FakeCloud
testCases := []struct {
testName string
......@@ -410,7 +408,7 @@ func TestSyncService(t *testing.T) {
testName: "if an invalid service name is synced",
key: "invalid/key/string",
updateFn: func() {
controller, cloud, _ = newController()
controller, _, _ = newController()
},
expectedFn: func(e error) error {
......@@ -429,7 +427,7 @@ func TestSyncService(t *testing.T) {
testName: "if an invalid service is synced",
key: "somethingelse",
updateFn: func() {
controller, cloud, _ = newController()
controller, _, _ = newController()
srv := controller.cache.getOrCreate("external-balancer")
srv.state = defaultExternalService()
},
......@@ -443,7 +441,7 @@ func TestSyncService(t *testing.T) {
key: "external-balancer",
updateFn: func() {
testSvc := defaultExternalService()
controller, cloud, _ = newController()
controller, _, _ = newController()
controller.enqueueService(testSvc)
svc := controller.cache.getOrCreate("external-balancer")
svc.state = testSvc
......
......@@ -109,7 +109,7 @@ var (
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName(v1.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount / 2)),
v1.ResourceName(v1.ResourceMemory): resource.MustParse(strconv.FormatInt(standardMemoryAmount/2, 10)),
v1.ResourceName(v1.ResourceCPU): resource.MustParse("5m"),
},
Limits: v1.ResourceList{
......@@ -127,7 +127,7 @@ var (
{
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceName(v1.ResourceMemory): resource.MustParse(strconv.Itoa(standardMemoryAmount - 1)),
v1.ResourceName(v1.ResourceMemory): resource.MustParse(strconv.FormatInt(standardMemoryAmount-1, 10)),
v1.ResourceName(v1.ResourceCPU): resource.MustParse("5m"),
},
},
......
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
......@@ -13,6 +7,7 @@ go_library(
"local.go",
],
importpath = "k8s.io/kubernetes/pkg/volume/local",
visibility = ["//visibility:public"],
deps = [
"//pkg/kubelet/events:go_default_library",
"//pkg/util/keymutex:go_default_library",
......@@ -32,16 +27,35 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["local_test.go"],
srcs = select({
"@io_bazel_rules_go//go/platform:darwin": [
"local_test.go",
],
"@io_bazel_rules_go//go/platform:linux": [
"local_test.go",
],
"//conditions:default": [],
}),
embed = [":go_default_library"],
deps = [
"//pkg/volume:go_default_library",
"//pkg/volume/testing:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/client-go/util/testing:go_default_library",
],
deps = select({
"@io_bazel_rules_go//go/platform:darwin": [
"//pkg/volume:go_default_library",
"//pkg/volume/testing:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/client-go/util/testing:go_default_library",
],
"@io_bazel_rules_go//go/platform:linux": [
"//pkg/volume:go_default_library",
"//pkg/volume/testing:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/client-go/util/testing:go_default_library",
],
"//conditions:default": [],
}),
)
filegroup(
......@@ -55,4 +69,5 @@ filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// +build linux darwin
/*
Copyright 2017 The Kubernetes Authors.
......
......@@ -27,6 +27,7 @@ import (
// util.go uses api.Codecs.LegacyCodec so import this package to do some
// resource initialization.
"hash/fnv"
_ "k8s.io/kubernetes/pkg/apis/core/install"
"k8s.io/kubernetes/pkg/apis/core/v1/helper"
"k8s.io/kubernetes/pkg/util/mount"
......@@ -547,12 +548,12 @@ func createVolumeSpecWithMountOption(name string, mountOptions string, spec v1.P
return &volume.Spec{PersistentVolume: pv}
}
func checkFnv32(t *testing.T, s string, expected int) {
func checkFnv32(t *testing.T, s string, expected uint32) {
h := fnv.New32()
h.Write([]byte(s))
h.Sum32()
if int(h.Sum32()) != expected {
if h.Sum32() != expected {
t.Fatalf("hash of %q was %v, expected %v", s, h.Sum32(), expected)
}
}
......
......@@ -45,9 +45,7 @@ func TestNewWithDelegate(t *testing.T) {
t.Fatal("unable to create fake client set")
}
delegateHealthzCalled := false
delegateConfig.HealthzChecks = append(delegateConfig.HealthzChecks, healthz.NamedCheck("delegate-health", func(r *http.Request) error {
delegateHealthzCalled = true
return fmt.Errorf("delegate failed healthcheck")
}))
......@@ -74,9 +72,7 @@ func TestNewWithDelegate(t *testing.T) {
wrappingConfig.LoopbackClientConfig = &rest.Config{}
wrappingConfig.SwaggerConfig = DefaultSwaggerConfig()
wrappingHealthzCalled := false
wrappingConfig.HealthzChecks = append(wrappingConfig.HealthzChecks, healthz.NamedCheck("wrapping-health", func(r *http.Request) error {
wrappingHealthzCalled = true
return fmt.Errorf("wrapping failed healthcheck")
}))
......
......@@ -27,16 +27,96 @@ go_test(
name = "go_default_test",
srcs = [
"envelope_test.go",
"grpc_service_unix_test.go",
],
] + select({
"@io_bazel_rules_go//go/platform:android": [
"grpc_service_unix_test.go",
],
"@io_bazel_rules_go//go/platform:darwin": [
"grpc_service_unix_test.go",
],
"@io_bazel_rules_go//go/platform:dragonfly": [
"grpc_service_unix_test.go",
],
"@io_bazel_rules_go//go/platform:freebsd": [
"grpc_service_unix_test.go",
],
"@io_bazel_rules_go//go/platform:linux": [
"grpc_service_unix_test.go",
],
"@io_bazel_rules_go//go/platform:nacl": [
"grpc_service_unix_test.go",
],
"@io_bazel_rules_go//go/platform:netbsd": [
"grpc_service_unix_test.go",
],
"@io_bazel_rules_go//go/platform:openbsd": [
"grpc_service_unix_test.go",
],
"@io_bazel_rules_go//go/platform:plan9": [
"grpc_service_unix_test.go",
],
"@io_bazel_rules_go//go/platform:solaris": [
"grpc_service_unix_test.go",
],
"//conditions:default": [],
}),
embed = [":go_default_library"],
deps = [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/aes:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
] + select({
"@io_bazel_rules_go//go/platform:android": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
"@io_bazel_rules_go//go/platform:darwin": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
"@io_bazel_rules_go//go/platform:dragonfly": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
"@io_bazel_rules_go//go/platform:freebsd": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
"@io_bazel_rules_go//go/platform:linux": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
"@io_bazel_rules_go//go/platform:nacl": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
"@io_bazel_rules_go//go/platform:netbsd": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
"@io_bazel_rules_go//go/platform:openbsd": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
"@io_bazel_rules_go//go/platform:plan9": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
"@io_bazel_rules_go//go/platform:solaris": [
"//vendor/golang.org/x/sys/unix:go_default_library",
"//vendor/google.golang.org/grpc:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library",
],
"//conditions:default": [],
}),
)
filegroup(
......
// +build !windows
/*
Copyright 2017 The Kubernetes Authors.
......@@ -15,9 +17,6 @@ limitations under the License.
*/
// Package envelope transforms values for storage at rest using a Envelope provider
// +build !windows
package envelope
import (
......
......@@ -21,6 +21,7 @@ filegroup(
"//test/list:all-srcs",
"//test/soak/cauldron:all-srcs",
"//test/soak/serve_hostnames:all-srcs",
"//test/typecheck:all-srcs",
"//test/utils:all-srcs",
],
tags = ["automanaged"],
......
......@@ -104,25 +104,20 @@ func newPodOnNode(c clientset.Interface, namespace, podName, nodeName string) er
var _ = SIGDescribe("Network Partition [Disruptive] [Slow]", func() {
f := framework.NewDefaultFramework("network-partition")
var systemPodsNo int32
var c clientset.Interface
var ns string
ignoreLabels := framework.ImagePullerLabels
var group string
BeforeEach(func() {
c = f.ClientSet
ns = f.Namespace.Name
systemPods, err := framework.GetPodsInNamespace(c, ns, ignoreLabels)
_, err := framework.GetPodsInNamespace(c, ns, ignoreLabels)
Expect(err).NotTo(HaveOccurred())
systemPodsNo = int32(len(systemPods))
// TODO(foxish): Re-enable testing on gce after kubernetes#56787 is fixed.
framework.SkipUnlessProviderIs("gke", "aws")
if strings.Index(framework.TestContext.CloudConfig.NodeInstanceGroup, ",") >= 0 {
framework.Failf("Test dose not support cluster setup with more than one MIG: %s", framework.TestContext.CloudConfig.NodeInstanceGroup)
} else {
group = framework.TestContext.CloudConfig.NodeInstanceGroup
}
})
......
......@@ -41,7 +41,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/client-go/discovery:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset:go_default_library",
"//vendor/k8s.io/metrics/pkg/client/custom_metrics:go_default_library",
],
)
......
......@@ -32,7 +32,6 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/client-go/discovery"
kubeaggrcs "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/test/e2e/framework"
customclient "k8s.io/metrics/pkg/client/custom_metrics"
)
......@@ -50,13 +49,11 @@ var _ = instrumentation.SIGDescribe("Stackdriver Monitoring", func() {
f := framework.NewDefaultFramework("stackdriver-monitoring")
var kubeClient clientset.Interface
var kubeAggrClient kubeaggrcs.Interface
var customMetricsClient customclient.CustomMetricsClient
var discoveryClient *discovery.DiscoveryClient
It("should run Custom Metrics - Stackdriver Adapter [Feature:StackdriverCustomMetrics]", func() {
kubeClient = f.ClientSet
kubeAggrClient = f.AggregatorClient
config, err := framework.LoadConfig()
if err != nil {
framework.Failf("Failed to load config: %s", err)
......
......@@ -26,7 +26,6 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/wait"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/cloudprovider"
gcecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/gce"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
......@@ -40,14 +39,12 @@ var _ = SIGDescribe("Services [Feature:GCEAlphaFeature][Slow]", func() {
f := framework.NewDefaultFramework("services")
var cs clientset.Interface
var internalClientset internalclientset.Interface
serviceLBNames := []string{}
BeforeEach(func() {
// This test suite requires the GCE environment.
framework.SkipUnlessProviderIs("gce")
cs = f.ClientSet
internalClientset = f.InternalClientset
})
AfterEach(func() {
......
......@@ -372,7 +372,6 @@ var _ = SIGDescribe("kubelet", func() {
var (
nfsServerPod *v1.Pod
nfsIP string
NFSconfig framework.VolumeTestConfig
pod *v1.Pod // client pod
)
......@@ -390,7 +389,7 @@ var _ = SIGDescribe("kubelet", func() {
BeforeEach(func() {
framework.SkipUnlessProviderIs(framework.ProvidersWithSSH...)
NFSconfig, nfsServerPod, nfsIP = framework.NewNFSServer(c, ns, []string{"-G", "777", "/exports"})
_, nfsServerPod, nfsIP = framework.NewNFSServer(c, ns, []string{"-G", "777", "/exports"})
})
AfterEach(func() {
......
......@@ -31,7 +31,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
......
......@@ -32,7 +32,6 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
utiluuid "k8s.io/apimachinery/pkg/util/uuid"
"k8s.io/apimachinery/pkg/watch"
clientset "k8s.io/client-go/kubernetes"
......@@ -349,7 +348,6 @@ var _ = SIGDescribe("Density", func() {
var nodeCpuCapacity int64
var nodeMemCapacity int64
var nodes *v1.NodeList
var masters sets.String
testCaseBaseName := "density"
missingMeasurements := 0
......@@ -417,7 +415,7 @@ var _ = SIGDescribe("Density", func() {
ns = f.Namespace.Name
testPhaseDurations = timer.NewTestPhaseTimer()
masters, nodes = framework.GetMasterAndWorkerNodesOrDie(c)
_, nodes = framework.GetMasterAndWorkerNodesOrDie(c)
nodeCount = len(nodes.Items)
Expect(nodeCount).NotTo(BeZero())
......
......@@ -29,7 +29,6 @@ import (
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/uuid"
clientset "k8s.io/client-go/kubernetes"
priorityutil "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/util"
......@@ -62,7 +61,6 @@ var _ = SIGDescribe("SchedulerPriorities [Serial]", func() {
var nodeList *v1.NodeList
var systemPodsNo int
var ns string
var masterNodes sets.String
f := framework.NewDefaultFramework("sched-priority")
ignoreLabels := framework.ImagePullerLabels
......@@ -75,7 +73,7 @@ var _ = SIGDescribe("SchedulerPriorities [Serial]", func() {
nodeList = &v1.NodeList{}
framework.WaitForAllNodesHealthy(cs, time.Minute)
masterNodes, nodeList = framework.GetMasterAndWorkerNodesOrDie(cs)
_, nodeList = framework.GetMasterAndWorkerNodesOrDie(cs)
err := framework.CheckTestingNSDeletedExcept(cs, ns)
framework.ExpectNoError(err)
......
......@@ -147,14 +147,12 @@ const (
// - lack of eviction of short-tolerating pod after taint removal.
var _ = SIGDescribe("NoExecuteTaintManager [Serial]", func() {
var cs clientset.Interface
var nodeList *v1.NodeList
var ns string
f := framework.NewDefaultFramework("taint-control")
BeforeEach(func() {
cs = f.ClientSet
ns = f.Namespace.Name
nodeList = &v1.NodeList{}
framework.WaitForAllNodesHealthy(cs, time.Minute)
......
......@@ -182,7 +182,6 @@ var _ = utils.SIGDescribe("CSI Volumes [Feature:CSI]", func() {
ns *v1.Namespace
node v1.Node
config framework.VolumeTestConfig
suffix string
)
BeforeEach(func() {
......@@ -197,7 +196,6 @@ var _ = utils.SIGDescribe("CSI Volumes [Feature:CSI]", func() {
ServerNodeName: node.Name,
WaitForCompletion: true,
}
suffix = ns.Name
})
// Create one of these for each of the drivers to be tested
......
......@@ -90,7 +90,6 @@ go_test(
"docker_test.go",
"dockershim_checkpoint_test.go",
"dynamic_kubelet_config_test.go",
"e2e_node_suite_test.go",
"eviction_test.go",
"garbage_collector_test.go",
"gke_environment_test.go",
......@@ -109,6 +108,7 @@ go_test(
"@io_bazel_rules_go//go/platform:linux": [
"container_manager_test.go",
"density_test.go",
"e2e_node_suite_test.go",
"node_container_manager_test.go",
"resource_usage_test.go",
"restart_test.go",
......@@ -137,23 +137,17 @@ go_test(
"//pkg/kubelet/metrics:go_default_library",
"//pkg/kubelet/types:go_default_library",
"//pkg/security/apparmor:go_default_library",
"//test/e2e/common:go_default_library",
"//test/e2e/framework:go_default_library",
"//test/e2e_node/services:go_default_library",
"//test/e2e_node/system:go_default_library",
"//test/utils/image:go_default_library",
"//vendor/github.com/blang/semver:go_default_library",
"//vendor/github.com/coreos/go-systemd/util:go_default_library",
"//vendor/github.com/davecgh/go-spew/spew:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/kardianos/osext:go_default_library",
"//vendor/github.com/onsi/ginkgo:go_default_library",
"//vendor/github.com/onsi/ginkgo/config:go_default_library",
"//vendor/github.com/onsi/ginkgo/reporters:go_default_library",
"//vendor/github.com/onsi/gomega:go_default_library",
"//vendor/github.com/onsi/gomega/gstruct:go_default_library",
"//vendor/github.com/onsi/gomega/types:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
......@@ -164,15 +158,21 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/uuid:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
] + select({
"@io_bazel_rules_go//go/platform:linux": [
"//test/e2e/common:go_default_library",
"//test/e2e/framework/metrics:go_default_library",
"//test/e2e_node/system:go_default_library",
"//test/utils:go_default_library",
"//vendor/github.com/kardianos/osext:go_default_library",
"//vendor/github.com/onsi/ginkgo/config:go_default_library",
"//vendor/github.com/onsi/ginkgo/reporters:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
],
"//conditions:default": [],
......
......@@ -202,7 +202,7 @@ func enableCPUManagerInKubelet(f *framework.Framework) (oldCfg *kubeletconfig.Ku
}
func runCPUManagerTests(f *framework.Framework) {
var cpuCap, cpuAlloc, cpuRes int64
var cpuCap, cpuAlloc int64
var oldCfg *kubeletconfig.KubeletConfiguration
var cpuListString, expAllowedCPUsListRegex string
var cpuList []int
......@@ -213,7 +213,7 @@ func runCPUManagerTests(f *framework.Framework) {
var pod, pod1, pod2 *v1.Pod
It("should assign CPUs as expected based on the Pod spec", func() {
cpuCap, cpuAlloc, cpuRes = getLocalNodeCPUDetails(f)
cpuCap, cpuAlloc, _ = getLocalNodeCPUDetails(f)
// Skip CPU Manager tests altogether if the CPU capacity < 2.
if cpuCap < 2 {
......
// +build linux
/*
Copyright 2016 The Kubernetes Authors.
......
......@@ -8,7 +8,12 @@ load(
go_library(
name = "go_default_library",
srcs = ["mt.go"],
srcs = select({
"@io_bazel_rules_go//go/platform:linux": [
"mt.go",
],
"//conditions:default": [],
}),
importpath = "k8s.io/kubernetes/test/images/mounttest",
)
......
// +build linux
/*
Copyright 2015 The Kubernetes Authors.
......
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
)
go_binary(
name = "list",
embed = [":go_default_library"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "k8s.io/kubernetes/test/typecheck",
deps = [
"//test/typecheck/srcimporter:go_default_library",
"//third_party/forked/golang/go/types:go_default_library",
"//vendor/golang.org/x/crypto/ssh/terminal:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//test/typecheck/srcimporter:all-srcs",
],
tags = ["automanaged"],
)
go_binary(
name = "typecheck",
embed = [":go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["main_test.go"],
embed = [":go_default_library"],
)
Typecheck does cross-platform typechecking of source code for all Go build
platforms.
The primary benefit is speed: a full Kubernetes cross-build takes 20 minutes
and >40GB of RAM, while this takes under 2 minutes and <8GB of RAM.
It uses Go's built-in parsing and typechecking libraries (go/parser and
go/types), which unfortunately are not what the go compiler uses. Occasional
mismatches will occur, but overall they correspond closely.
Failures can be ignored if they don't block the build:
Things go/types errors on that go build doesn't:
True errors (according to the spec):
These should be fixed whenever possible. Ignore if a fix isn't possible
or is in progress (e.g., vendored code).
- unused variables in closures
False errors:
These should be ignored and reported upstream if applicable.
- type checking mismatches between staging and generated types
Things go build fails on that we don't:
Please send examples of this to rmmh@ and extend this list.
- CGo errors, including syntax and linker errors.
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// do a fast type check of kubernetes code, for all platforms.
package main
import (
"flag"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"log"
"os"
"path/filepath"
"sort"
"strings"
"sync"
"sync/atomic"
"time"
"golang.org/x/crypto/ssh/terminal"
// TODO(rmmh): remove this when golang/go#23712 is fixed, and the
// fix is the current minimum Go version to build Kubernetes.
"k8s.io/kubernetes/test/typecheck/srcimporter"
"k8s.io/kubernetes/third_party/forked/golang/go/types"
)
var (
verbose = flag.Bool("verbose", false, "print more information")
cross = flag.Bool("cross", true, "build for all platforms")
platforms = flag.String("platform", "", "comma-separated list of platforms to typecheck")
timings = flag.Bool("time", false, "output times taken for each phase")
defuses = flag.Bool("defuse", false, "output defs/uses")
serial = flag.Bool("serial", false, "don't type check platforms in parallel")
isTerminal = terminal.IsTerminal(int(os.Stdout.Fd()))
logPrefix = ""
// When processed in order, windows and darwin are early to make
// interesting OS-based errors happen earlier.
crossPlatforms = []string{
"linux/amd64", "windows/386",
"darwin/amd64", "linux/arm",
"linux/386", "windows/amd64",
"linux/arm64", "linux/ppc64le",
"linux/s390x", "darwin/386",
}
)
type analyzer struct {
fset *token.FileSet // positions are relative to fset
conf types.Config
ctx build.Context
failed bool
platform string
donePaths map[string]interface{}
}
func newAnalyzer(platform string) *analyzer {
ctx := build.Default
platSplit := strings.Split(platform, "/")
ctx.GOOS, ctx.GOARCH = platSplit[0], platSplit[1]
ctx.CgoEnabled = true
a := &analyzer{
platform: platform,
fset: token.NewFileSet(),
ctx: ctx,
donePaths: make(map[string]interface{}),
}
a.conf = types.Config{
FakeImportC: true,
Error: a.handleError,
Sizes: types.SizesFor("gc", a.ctx.GOARCH),
}
a.conf.Importer = srcimporter.New(
&a.ctx, a.fset, make(map[string]*types.Package))
if *verbose {
fmt.Printf("context: %#v\n", ctx)
}
return a
}
func (a *analyzer) handleError(err error) {
if e, ok := err.(types.Error); ok {
// useful for some ignores:
// path := e.Fset.Position(e.Pos).String()
ignore := false
// TODO(rmmh): read ignores from a file, so this code can
// be Kubernetes-agnostic. Unused ignores should be treated as
// errors, to ensure coverage isn't overly broad.
if strings.Contains(e.Msg, "GetOpenAPIDefinitions") {
// TODO(rmmh): figure out why this happens.
// cmd/kube-apiserver/app/server.go:392:70
// test/integration/framework/master_utils.go:131:84
ignore = true
}
if ignore {
if *verbose {
fmt.Println("ignoring error:", err)
}
return
}
}
// TODO(rmmh): dedup errors across platforms?
fmt.Fprintf(os.Stderr, "%sERROR(%s) %s\n", logPrefix, a.platform, err)
a.failed = true
}
// collect extracts test metadata from a file.
func (a *analyzer) collect(dir string) {
if _, ok := a.donePaths[dir]; ok {
return
}
a.donePaths[dir] = nil
// Create the AST by parsing src.
fs, err := parser.ParseDir(a.fset, dir, nil, parser.AllErrors)
if err != nil {
fmt.Println(logPrefix+"ERROR(syntax)", err)
a.failed = true
return
}
if len(fs) > 1 && *verbose {
fmt.Println("multiple packages in dir:", dir)
}
for _, p := range fs {
// returns first error, but a.handleError deals with it
files := a.filterFiles(p.Files)
if *verbose {
fmt.Printf("path: %s package: %s files: ", dir, p.Name)
for _, f := range files {
fname := filepath.Base(a.fset.File(f.Pos()).Name())
fmt.Printf("%s ", fname)
}
fmt.Printf("\n")
}
a.typeCheck(dir, files)
}
}
// filterFiles restricts a list of files to only those that should be built by
// the current platform. This includes both build suffixes (_windows.go) and build
// tags ("// +build !linux" at the beginning).
func (a *analyzer) filterFiles(fs map[string]*ast.File) []*ast.File {
files := []*ast.File{}
for _, f := range fs {
fpath := a.fset.File(f.Pos()).Name()
dir, name := filepath.Split(fpath)
matches, err := a.ctx.MatchFile(dir, name)
if err != nil {
fmt.Fprintf(os.Stderr, "%sERROR reading %s: %s\n", logPrefix, fpath, err)
a.failed = true
continue
}
if matches {
files = append(files, f)
}
}
return files
}
func (a *analyzer) typeCheck(dir string, files []*ast.File) error {
info := types.Info{
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
}
// NOTE: this type check does a *recursive* import, but srcimporter
// doesn't do a full type check (ignores function bodies)-- this has
// some additional overhead.
//
// This means that we need to ensure that typeCheck runs on all
// code we will be compiling.
//
// TODO(rmmh): Customize our forked srcimporter to do this better.
pkg, err := a.conf.Check(dir, a.fset, files, &info)
if err != nil {
return err // type error
}
// A significant fraction of vendored code only compiles on Linux,
// but it's only imported by code that has build-guards for Linux.
// Track vendored code to type-check it in a second pass.
for _, imp := range pkg.Imports() {
if strings.HasPrefix(imp.Path(), "k8s.io/kubernetes/vendor/") {
vendorPath := imp.Path()[len("k8s.io/kubernetes/"):]
if *verbose {
fmt.Println("recursively checking vendor path:", vendorPath)
}
a.collect(vendorPath)
}
}
if *defuses {
for id, obj := range info.Defs {
fmt.Printf("%s: %q defines %v\n",
a.fset.Position(id.Pos()), id.Name, obj)
}
for id, obj := range info.Uses {
fmt.Printf("%s: %q uses %v\n",
a.fset.Position(id.Pos()), id.Name, obj)
}
}
return nil
}
type collector struct {
dirs []string
}
// handlePath walks the filesystem recursively, collecting directories,
// ignoring some unneeded directories (hidden/vendored) that are handled
// specially later.
func (c *collector) handlePath(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
// Ignore hidden directories (.git, .cache, etc)
if len(path) > 1 && path[0] == '.' ||
// Staging code is symlinked from vendor/k8s.io, and uses import
// paths as if it were inside of vendor/. It fails typechecking
// inside of staging/, but works when typechecked as part of vendor/.
path == "staging" ||
// OS-specific vendor code tends to be imported by OS-specific
// packages. We recursively typecheck imported vendored packages for
// each OS, but don't typecheck everything for every OS.
path == "vendor" ||
path == "_output" ||
// This is a weird one. /testdata/ is *mostly* ignored by Go,
// and this translates to kubernetes/vendor not working.
// edit/record.go doesn't compile without gopkg.in/yaml.v2
// in $GOSRC/$GOROOT (both typecheck and the shell script).
path == "pkg/kubectl/cmd/testdata/edit" {
return filepath.SkipDir
}
c.dirs = append(c.dirs, path)
}
return nil
}
func main() {
flag.Parse()
args := flag.Args()
if *verbose {
*serial = true // to avoid confusing interleaved logs
}
if len(args) == 0 {
args = append(args, ".")
}
c := collector{}
for _, arg := range args {
err := filepath.Walk(arg, c.handlePath)
if err != nil {
log.Fatalf("Error walking: %v", err)
}
}
sort.Strings(c.dirs)
ps := crossPlatforms[:]
if *platforms != "" {
ps = strings.Split(*platforms, ",")
} else if !*cross {
ps = ps[:1]
}
fmt.Println("type-checking: ", strings.Join(ps, ", "))
var wg sync.WaitGroup
var processedDirs int64
var currentWork int64 // (dir_index << 8) | platform_index
statuses := make([]int, len(ps))
for i, p := range ps {
wg.Add(1)
fn := func(i int, p string) {
start := time.Now()
a := newAnalyzer(p)
for n, dir := range c.dirs {
a.collect(dir)
atomic.AddInt64(&processedDirs, 1)
atomic.StoreInt64(&currentWork, int64(n<<8|i))
}
if a.failed {
statuses[i] = 1
}
if *timings {
fmt.Printf("%s took %.1fs\n", p, time.Since(start).Seconds())
}
wg.Done()
}
if *serial {
fn(i, p)
} else {
go fn(i, p)
}
}
if isTerminal {
logPrefix = "\r" // clear status bar when printing
// Display a status bar so devs can estimate completion times.
wg.Add(1)
go func() {
total := len(ps) * len(c.dirs)
for proc := 0; ; proc = int(atomic.LoadInt64(&processedDirs)) {
work := atomic.LoadInt64(&currentWork)
dir := c.dirs[work>>8]
platform := ps[work&0xFF]
if len(dir) > 80 {
dir = dir[:80]
}
fmt.Printf("\r%d/%d \033[2m%-13s\033[0m %-80s", proc, total, platform, dir)
if proc == total {
fmt.Println()
break
}
time.Sleep(50 * time.Millisecond)
}
wg.Done()
}()
}
wg.Wait()
for _, status := range statuses {
if status != 0 {
os.Exit(status)
}
}
}
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"errors"
"fmt"
"go/ast"
"go/parser"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
)
var packageCases = []struct {
code string
errs map[string]string
}{
// Empty: no problems!
{"", map[string]string{"linux/amd64": ""}},
// Slightly less empty: no problems!
{"func getRandomNumber() int { return 4; }", map[string]string{"darwin/386": ""}},
// Fixed in #59243
{`import "golang.org/x/sys/unix"
func f(err error) {
if err != unix.ENXIO {
panic("woops")
}
}`, map[string]string{"linux/amd64": "", "windows/amd64": "test.go:4:13: ENXIO not declared by package unix"}},
// Fixed in #51984
{`import "golang.org/x/sys/unix"
const linuxHugetlbfsMagic = 0x958458f6
func IsHugeTlbfs() bool {
buf := unix.Statfs_t{}
unix.Statfs("/tmp/", &buf)
return buf.Type == linuxHugetlbfsMagic
}`, map[string]string{
"linux/amd64": "",
"linux/386": "test.go:7:22: linuxHugetlbfsMagic (untyped int constant 2508478710) overflows int32",
}},
// Fixed in #51873
{`var a = map[string]interface{}{"num1": 9223372036854775807}`,
map[string]string{"linux/arm": "test.go:2:40: 9223372036854775807 (untyped int constant) overflows int"}},
}
var testFiles = map[string]string{
"golang.org/x/sys/unix/empty.go": `package unix`,
"golang.org/x/sys/unix/errno_linux.go": `// +build linux
package unix
type Errno string
func (e Errno) Error() string { return string(e) }
var ENXIO = Errno("3")`,
"golang.org/x/sys/unix/ztypes_linux_amd64.go": `// +build amd64,linux
package unix
type Statfs_t struct {
Type int64
}
func Statfs(path string, statfs *Statfs_t) {}
`,
"golang.org/x/sys/unix/ztypes_linux_386.go": `// +build i386,linux
package unix
type Statfs_t struct {
Type int32
}
func Statfs(path string, statfs *Statfs_t) {}
`,
}
func TestHandlePackage(t *testing.T) {
// When running in Bazel, we don't have access to Go source code. Fake it instead!
tmpDir, err := ioutil.TempDir("", "test_typecheck")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)
for path, data := range testFiles {
path := filepath.Join(tmpDir, "src", path)
err := os.MkdirAll(filepath.Dir(path), 0755)
if err != nil {
t.Fatal(err)
}
err = ioutil.WriteFile(path, []byte(data), 0644)
if err != nil {
t.Fatal(err)
}
fmt.Println(path)
}
for _, test := range packageCases {
for platform, expectedErr := range test.errs {
a := newAnalyzer(platform)
// Make Imports happen relative to our faked up GOROOT.
a.ctx.GOROOT = tmpDir
a.ctx.GOPATH = ""
errs := []string{}
a.conf.Error = func(err error) {
errs = append(errs, err.Error())
}
code := "package test\n" + test.code
parsed, err := parser.ParseFile(a.fset, "test.go", strings.NewReader(code), parser.AllErrors)
if err != nil {
t.Fatal(err)
}
a.typeCheck(tmpDir, []*ast.File{parsed})
if expectedErr == "" {
if len(errs) > 0 {
t.Errorf("code:\n%s\ngot %v\nwant %v",
code, errs, expectedErr)
}
} else {
if len(errs) != 1 {
t.Errorf("code:\n%s\ngot %v\nwant %v",
code, errs, expectedErr)
} else {
if errs[0] != expectedErr {
t.Errorf("code:\n%s\ngot %v\nwant %v",
code, errs[0], expectedErr)
}
}
}
}
}
}
func TestHandlePath(t *testing.T) {
c := collector{}
e := errors.New("ex")
i, _ := os.Stat(".") // i.IsDir() == true
if c.handlePath("foo", nil, e) != e {
t.Error("handlePath not returning errors")
}
if c.handlePath("vendor", i, nil) != filepath.SkipDir {
t.Error("should skip vendor")
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["srcimporter.go"],
importpath = "k8s.io/kubernetes/test/typecheck/srcimporter",
visibility = ["//visibility:public"],
deps = ["//third_party/forked/golang/go/types:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Forked from go's go/internal/srcimporter
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package srcimporter implements importing directly
// from source files rather than installed packages.
package srcimporter
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"path/filepath"
"sync"
"k8s.io/kubernetes/third_party/forked/golang/go/types"
)
// An Importer provides the context for importing packages from source code.
type Importer struct {
ctxt *build.Context
fset *token.FileSet
sizes types.Sizes
packages map[string]*types.Package
}
// New returns a new Importer for the given context, file set, and map
// of packages. The context is used to resolve import paths to package paths,
// and identifying the files belonging to the package. If the context provides
// non-nil file system functions, they are used instead of the regular package
// os functions. The file set is used to track position information of package
// files; and imported packages are added to the packages map.
func New(ctxt *build.Context, fset *token.FileSet, packages map[string]*types.Package) *Importer {
return &Importer{
ctxt: ctxt,
fset: fset,
sizes: types.SizesFor(ctxt.Compiler, ctxt.GOARCH), // uses go/types default if GOARCH not found
packages: packages,
}
}
// Importing is a sentinel taking the place in Importer.packages
// for a package that is in the process of being imported.
var importing types.Package
// Import (path) is a shortcut for ImportFrom(path, "", 0).
func (p *Importer) Import(path string) (*types.Package, error) {
return p.ImportFrom(path, "", 0)
}
// ImportFrom imports the package with the given import path resolved from the given srcDir,
// adds the new package to the set of packages maintained by the importer, and returns the
// package. Package path resolution and file system operations are controlled by the context
// maintained with the importer. The import mode must be zero but is otherwise ignored.
// Packages that are not comprised entirely of pure Go files may fail to import because the
// type checker may not be able to determine all exported entities (e.g. due to cgo dependencies).
func (p *Importer) ImportFrom(path, srcDir string, mode types.ImportMode) (*types.Package, error) {
if mode != 0 {
panic("non-zero import mode")
}
// determine package path (do vendor resolution)
var bp *build.Package
var err error
switch {
default:
if abs, err := p.absPath(srcDir); err == nil { // see issue #14282
srcDir = abs
}
bp, err = p.ctxt.Import(path, srcDir, build.FindOnly)
case build.IsLocalImport(path):
// "./x" -> "srcDir/x"
bp, err = p.ctxt.ImportDir(filepath.Join(srcDir, path), build.FindOnly)
case p.isAbsPath(path):
return nil, fmt.Errorf("invalid absolute import path %q", path)
}
if err != nil {
return nil, err // err may be *build.NoGoError - return as is
}
// package unsafe is known to the type checker
if bp.ImportPath == "unsafe" {
return types.Unsafe, nil
}
// no need to re-import if the package was imported completely before
pkg := p.packages[bp.ImportPath]
if pkg != nil {
if pkg == &importing {
return nil, fmt.Errorf("import cycle through package %q", bp.ImportPath)
}
if !pkg.Complete() {
// Package exists but is not complete - we cannot handle this
// at the moment since the source importer replaces the package
// wholesale rather than augmenting it (see #19337 for details).
// Return incomplete package with error (see #16088).
return pkg, fmt.Errorf("reimported partially imported package %q", bp.ImportPath)
}
return pkg, nil
}
p.packages[bp.ImportPath] = &importing
defer func() {
// clean up in case of error
// TODO(gri) Eventually we may want to leave a (possibly empty)
// package in the map in all cases (and use that package to
// identify cycles). See also issue 16088.
if p.packages[bp.ImportPath] == &importing {
p.packages[bp.ImportPath] = nil
}
}()
// collect package files
bp, err = p.ctxt.ImportDir(bp.Dir, 0)
if err != nil {
return nil, err // err may be *build.NoGoError - return as is
}
var filenames []string
filenames = append(filenames, bp.GoFiles...)
filenames = append(filenames, bp.CgoFiles...)
files, err := p.parseFiles(bp.Dir, filenames)
if err != nil {
return nil, err
}
// type-check package files
var firstHardErr error
conf := types.Config{
IgnoreFuncBodies: true,
FakeImportC: true,
// continue type-checking after the first error
Error: func(err error) {
if firstHardErr == nil && !err.(types.Error).Soft {
firstHardErr = err
}
},
Importer: p,
Sizes: p.sizes,
}
pkg, err = conf.Check(bp.ImportPath, p.fset, files, nil)
if err != nil {
// If there was a hard error it is possibly unsafe
// to use the package as it may not be fully populated.
// Do not return it (see also #20837, #20855).
if firstHardErr != nil {
pkg = nil
err = firstHardErr // give preference to first hard error over any soft error
}
return pkg, fmt.Errorf("type-checking package %q failed (%v)", bp.ImportPath, err)
}
if firstHardErr != nil {
// this can only happen if we have a bug in go/types
panic("package is not safe yet no error was returned")
}
p.packages[bp.ImportPath] = pkg
return pkg, nil
}
func (p *Importer) parseFiles(dir string, filenames []string) ([]*ast.File, error) {
open := p.ctxt.OpenFile // possibly nil
files := make([]*ast.File, len(filenames))
errors := make([]error, len(filenames))
var wg sync.WaitGroup
wg.Add(len(filenames))
for i, filename := range filenames {
go func(i int, filepath string) {
defer wg.Done()
if open != nil {
src, err := open(filepath)
if err != nil {
errors[i] = fmt.Errorf("opening package file %s failed (%v)", filepath, err)
return
}
files[i], errors[i] = parser.ParseFile(p.fset, filepath, src, 0)
src.Close() // ignore Close error - parsing may have succeeded which is all we need
} else {
// Special-case when ctxt doesn't provide a custom OpenFile and use the
// parser's file reading mechanism directly. This appears to be quite a
// bit faster than opening the file and providing an io.ReaderCloser in
// both cases.
// TODO(gri) investigate performance difference (issue #19281)
files[i], errors[i] = parser.ParseFile(p.fset, filepath, nil, 0)
}
}(i, p.joinPath(dir, filename))
}
wg.Wait()
// if there are errors, return the first one for deterministic results
for _, err := range errors {
if err != nil {
return nil, err
}
}
return files, nil
}
// context-controlled file system operations
func (p *Importer) absPath(path string) (string, error) {
// TODO(gri) This should be using p.ctxt.AbsPath which doesn't
// exist but probably should. See also issue #14282.
return filepath.Abs(path)
}
func (p *Importer) isAbsPath(path string) bool {
if f := p.ctxt.IsAbsPath; f != nil {
return f(path)
}
return filepath.IsAbs(path)
}
func (p *Importer) joinPath(elem ...string) string {
if f := p.ctxt.JoinPath; f != nil {
return f(elem...)
}
return filepath.Join(elem...)
}
......@@ -18,6 +18,7 @@ filegroup(
"//third_party/forked/etcd237/pkg/fileutil:all-srcs",
"//third_party/forked/etcd237/wal:all-srcs",
"//third_party/forked/golang/expansion:all-srcs",
"//third_party/forked/golang/go/types:all-srcs",
"//third_party/forked/golang/reflect:all-srcs",
"//third_party/forked/golang/template:all-srcs",
"//third_party/forked/gonum/graph:all-srcs",
......
licenses(["notice"])
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"api.go",
"assignments.go",
"builtins.go",
"call.go",
"check.go",
"conversions.go",
"decl.go",
"errors.go",
"eval.go",
"expr.go",
"exprstring.go",
"initorder.go",
"labels.go",
"lookup.go",
"methodset.go",
"object.go",
"objset.go",
"operand.go",
"ordering.go",
"package.go",
"predicates.go",
"resolver.go",
"return.go",
"scope.go",
"selection.go",
"sizes.go",
"stmt.go",
"type.go",
"typestring.go",
"typexpr.go",
"universe.go",
],
importpath = "k8s.io/kubernetes/third_party/forked/golang/go/types",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
This is go/types forked from go@236abdb46b (just after 1.10) and cherry-picked
1006f703ffc, which fixes https://github.com/golang/go/issues/23712.
It can be removed when all builders are >= go1.11.
Do *not* update this to newer code if https://github.com/golang/go/issues/23914
has not been fixed!
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements initialization and assignment checks.
package types
import (
"go/ast"
"go/token"
)
// assignment reports whether x can be assigned to a variable of type T,
// if necessary by attempting to convert untyped values to the appropriate
// type. context describes the context in which the assignment takes place.
// Use T == nil to indicate assignment to an untyped blank identifier.
// x.mode is set to invalid if the assignment failed.
func (check *Checker) assignment(x *operand, T Type, context string) {
check.singleValue(x)
switch x.mode {
case invalid:
return // error reported before
case constant_, variable, mapindex, value, commaok:
// ok
default:
unreachable()
}
if isUntyped(x.typ) {
target := T
// spec: "If an untyped constant is assigned to a variable of interface
// type or the blank identifier, the constant is first converted to type
// bool, rune, int, float64, complex128 or string respectively, depending
// on whether the value is a boolean, rune, integer, floating-point, complex,
// or string constant."
if T == nil || IsInterface(T) {
if T == nil && x.typ == Typ[UntypedNil] {
check.errorf(x.pos(), "use of untyped nil in %s", context)
x.mode = invalid
return
}
target = Default(x.typ)
}
check.convertUntyped(x, target)
if x.mode == invalid {
return
}
}
// x.typ is typed
// spec: "If a left-hand side is the blank identifier, any typed or
// non-constant value except for the predeclared identifier nil may
// be assigned to it."
if T == nil {
return
}
if reason := ""; !x.assignableTo(check.conf, T, &reason) {
if reason != "" {
check.errorf(x.pos(), "cannot use %s as %s value in %s: %s", x, T, context, reason)
} else {
check.errorf(x.pos(), "cannot use %s as %s value in %s", x, T, context)
}
x.mode = invalid
}
}
func (check *Checker) initConst(lhs *Const, x *operand) {
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return
}
// rhs must be a constant
if x.mode != constant_ {
check.errorf(x.pos(), "%s is not constant", x)
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return
}
assert(isConstType(x.typ))
// If the lhs doesn't have a type yet, use the type of x.
if lhs.typ == nil {
lhs.typ = x.typ
}
check.assignment(x, lhs.typ, "constant declaration")
if x.mode == invalid {
return
}
lhs.val = x.val
}
func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return nil
}
// If the lhs doesn't have a type yet, use the type of x.
if lhs.typ == nil {
typ := x.typ
if isUntyped(typ) {
// convert untyped types to default types
if typ == Typ[UntypedNil] {
check.errorf(x.pos(), "use of untyped nil in %s", context)
lhs.typ = Typ[Invalid]
return nil
}
typ = Default(typ)
}
lhs.typ = typ
}
check.assignment(x, lhs.typ, context)
if x.mode == invalid {
return nil
}
return x.typ
}
func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
if x.mode == invalid || x.typ == Typ[Invalid] {
return nil
}
// Determine if the lhs is a (possibly parenthesized) identifier.
ident, _ := unparen(lhs).(*ast.Ident)
// Don't evaluate lhs if it is the blank identifier.
if ident != nil && ident.Name == "_" {
check.recordDef(ident, nil)
check.assignment(x, nil, "assignment to _ identifier")
if x.mode == invalid {
return nil
}
return x.typ
}
// If the lhs is an identifier denoting a variable v, this assignment
// is not a 'use' of v. Remember current value of v.used and restore
// after evaluating the lhs via check.expr.
var v *Var
var v_used bool
if ident != nil {
if _, obj := check.scope.LookupParent(ident.Name, token.NoPos); obj != nil {
// It's ok to mark non-local variables, but ignore variables
// from other packages to avoid potential race conditions with
// dot-imported variables.
if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg {
v = w
v_used = v.used
}
}
}
var z operand
check.expr(&z, lhs)
if v != nil {
v.used = v_used // restore v.used
}
if z.mode == invalid || z.typ == Typ[Invalid] {
return nil
}
// spec: "Each left-hand side operand must be addressable, a map index
// expression, or the blank identifier. Operands may be parenthesized."
switch z.mode {
case invalid:
return nil
case variable, mapindex:
// ok
default:
if sel, ok := z.expr.(*ast.SelectorExpr); ok {
var op operand
check.expr(&op, sel.X)
if op.mode == mapindex {
check.errorf(z.pos(), "cannot assign to struct field %s in map", ExprString(z.expr))
return nil
}
}
check.errorf(z.pos(), "cannot assign to %s", &z)
return nil
}
check.assignment(x, z.typ, "assignment")
if x.mode == invalid {
return nil
}
return x.typ
}
// If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement.
func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) {
l := len(lhs)
get, r, commaOk := unpack(func(x *operand, i int) { check.multiExpr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
if get == nil || l != r {
// invalidate lhs and use rhs
for _, obj := range lhs {
if obj.typ == nil {
obj.typ = Typ[Invalid]
}
}
if get == nil {
return // error reported by unpack
}
check.useGetter(get, r)
if returnPos.IsValid() {
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
return
}
check.errorf(rhs[0].Pos(), "cannot initialize %d variables with %d values", l, r)
return
}
context := "assignment"
if returnPos.IsValid() {
context = "return statement"
}
var x operand
if commaOk {
var a [2]Type
for i := range a {
get(&x, i)
a[i] = check.initVar(lhs[i], &x, context)
}
check.recordCommaOkTypes(rhs[0], a)
return
}
for i, lhs := range lhs {
get(&x, i)
check.initVar(lhs, &x, context)
}
}
func (check *Checker) assignVars(lhs, rhs []ast.Expr) {
l := len(lhs)
get, r, commaOk := unpack(func(x *operand, i int) { check.multiExpr(x, rhs[i]) }, len(rhs), l == 2)
if get == nil {
check.useLHS(lhs...)
return // error reported by unpack
}
if l != r {
check.useGetter(get, r)
check.errorf(rhs[0].Pos(), "cannot assign %d values to %d variables", r, l)
return
}
var x operand
if commaOk {
var a [2]Type
for i := range a {
get(&x, i)
a[i] = check.assignVar(lhs[i], &x)
}
check.recordCommaOkTypes(rhs[0], a)
return
}
for i, lhs := range lhs {
get(&x, i)
check.assignVar(lhs, &x)
}
}
func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
scope := check.scope
// collect lhs variables
var newVars []*Var
var lhsVars = make([]*Var, len(lhs))
for i, lhs := range lhs {
var obj *Var
if ident, _ := lhs.(*ast.Ident); ident != nil {
// Use the correct obj if the ident is redeclared. The
// variable's scope starts after the declaration; so we
// must use Scope.Lookup here and call Scope.Insert
// (via check.declare) later.
name := ident.Name
if alt := scope.Lookup(name); alt != nil {
// redeclared object must be a variable
if alt, _ := alt.(*Var); alt != nil {
obj = alt
} else {
check.errorf(lhs.Pos(), "cannot assign to %s", lhs)
}
check.recordUse(ident, alt)
} else {
// declare new variable, possibly a blank (_) variable
obj = NewVar(ident.Pos(), check.pkg, name, nil)
if name != "_" {
newVars = append(newVars, obj)
}
check.recordDef(ident, obj)
}
} else {
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
}
if obj == nil {
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
}
lhsVars[i] = obj
}
check.initVars(lhsVars, rhs, token.NoPos)
// declare new variables
if len(newVars) > 0 {
// spec: "The scope of a constant or variable identifier declared inside
// a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
// for short variable declarations) and ends at the end of the innermost
// containing block."
scopePos := rhs[len(rhs)-1].End()
for _, obj := range newVars {
check.declare(scope, nil, obj, scopePos) // recordObject already called
}
} else {
check.softErrorf(pos, "no new variables on left side of :=")
}
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements typechecking of conversions.
package types
import "go/constant"
// Conversion type-checks the conversion T(x).
// The result is in x.
func (check *Checker) conversion(x *operand, T Type) {
constArg := x.mode == constant_
var ok bool
switch {
case constArg && isConstType(T):
// constant conversion
switch t := T.Underlying().(*Basic); {
case representableConst(x.val, check.conf, t, &x.val):
ok = true
case isInteger(x.typ) && isString(t):
codepoint := int64(-1)
if i, ok := constant.Int64Val(x.val); ok {
codepoint = i
}
// If codepoint < 0 the absolute value is too large (or unknown) for
// conversion. This is the same as converting any other out-of-range
// value - let string(codepoint) do the work.
x.val = constant.MakeString(string(codepoint))
ok = true
}
case x.convertibleTo(check.conf, T):
// non-constant conversion
x.mode = value
ok = true
}
if !ok {
check.errorf(x.pos(), "cannot convert %s to %s", x, T)
x.mode = invalid
return
}
// The conversion argument types are final. For untyped values the
// conversion provides the type, per the spec: "A constant may be
// given a type explicitly by a constant declaration or conversion,...".
if isUntyped(x.typ) {
final := T
// - For conversions to interfaces, use the argument's default type.
// - For conversions of untyped constants to non-constant types, also
// use the default type (e.g., []byte("foo") should report string
// not []byte as type for the constant "foo").
// - Keep untyped nil for untyped nil arguments.
// - For integer to string conversions, keep the argument type.
// (See also the TODO below.)
if IsInterface(T) || constArg && !isConstType(T) {
final = Default(x.typ)
} else if isInteger(x.typ) && isString(T) {
final = x.typ
}
check.updateExprType(x.expr, final, true)
}
x.typ = T
}
// TODO(gri) convertibleTo checks if T(x) is valid. It assumes that the type
// of x is fully known, but that's not the case for say string(1<<s + 1.0):
// Here, the type of 1<<s + 1.0 will be UntypedFloat which will lead to the
// (correct!) refusal of the conversion. But the reported error is essentially
// "cannot convert untyped float value to string", yet the correct error (per
// the spec) is that we cannot shift a floating-point value: 1 in 1<<s should
// be converted to UntypedFloat because of the addition of 1.0. Fixing this
// is tricky because we'd have to run updateExprType on the argument first.
// (Issue #21982.)
func (x *operand) convertibleTo(conf *Config, T Type) bool {
// "x is assignable to T"
if x.assignableTo(conf, T, nil) {
return true
}
// "x's type and T have identical underlying types if tags are ignored"
V := x.typ
Vu := V.Underlying()
Tu := T.Underlying()
if IdenticalIgnoreTags(Vu, Tu) {
return true
}
// "x's type and T are unnamed pointer types and their pointer base types
// have identical underlying types if tags are ignored"
if V, ok := V.(*Pointer); ok {
if T, ok := T.(*Pointer); ok {
if IdenticalIgnoreTags(V.base.Underlying(), T.base.Underlying()) {
return true
}
}
}
// "x's type and T are both integer or floating point types"
if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) {
return true
}
// "x's type and T are both complex types"
if isComplex(V) && isComplex(T) {
return true
}
// "x is an integer or a slice of bytes or runes and T is a string type"
if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
return true
}
// "x is a string and T is a slice of bytes or runes"
if isString(V) && isBytesOrRunes(Tu) {
return true
}
// package unsafe:
// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
return true
}
// "and vice versa"
if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
return true
}
return false
}
func isUintptr(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.kind == Uintptr
}
func isUnsafePointer(typ Type) bool {
// TODO(gri): Is this (typ.Underlying() instead of just typ) correct?
// The spec does not say so, but gc claims it is. See also
// issue 6326.
t, ok := typ.Underlying().(*Basic)
return ok && t.kind == UnsafePointer
}
func isPointer(typ Type) bool {
_, ok := typ.Underlying().(*Pointer)
return ok
}
func isBytesOrRunes(typ Type) bool {
if s, ok := typ.(*Slice); ok {
t, ok := s.elem.Underlying().(*Basic)
return ok && (t.kind == Byte || t.kind == Rune)
}
return false
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements various error reporters.
package types
import (
"fmt"
"go/ast"
"go/token"
"strings"
)
func assert(p bool) {
if !p {
panic("assertion failed")
}
}
func unreachable() {
panic("unreachable")
}
func (check *Checker) qualifier(pkg *Package) string {
if pkg != check.pkg {
return pkg.path
}
return ""
}
func (check *Checker) sprintf(format string, args ...interface{}) string {
for i, arg := range args {
switch a := arg.(type) {
case nil:
arg = "<nil>"
case operand:
panic("internal error: should always pass *operand")
case *operand:
arg = operandString(a, check.qualifier)
case token.Pos:
arg = check.fset.Position(a).String()
case ast.Expr:
arg = ExprString(a)
case Object:
arg = ObjectString(a, check.qualifier)
case Type:
arg = TypeString(a, check.qualifier)
}
args[i] = arg
}
return fmt.Sprintf(format, args...)
}
func (check *Checker) trace(pos token.Pos, format string, args ...interface{}) {
fmt.Printf("%s:\t%s%s\n",
check.fset.Position(pos),
strings.Repeat(". ", check.indent),
check.sprintf(format, args...),
)
}
// dump is only needed for debugging
func (check *Checker) dump(format string, args ...interface{}) {
fmt.Println(check.sprintf(format, args...))
}
func (check *Checker) err(pos token.Pos, msg string, soft bool) {
err := Error{check.fset, pos, msg, soft}
if check.firstErr == nil {
check.firstErr = err
}
f := check.conf.Error
if f == nil {
panic(bailout{}) // report only first error
}
f(err)
}
func (check *Checker) error(pos token.Pos, msg string) {
check.err(pos, msg, false)
}
func (check *Checker) errorf(pos token.Pos, format string, args ...interface{}) {
check.err(pos, check.sprintf(format, args...), false)
}
func (check *Checker) softErrorf(pos token.Pos, format string, args ...interface{}) {
check.err(pos, check.sprintf(format, args...), true)
}
func (check *Checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
check.errorf(pos, "invalid AST: "+format, args...)
}
func (check *Checker) invalidArg(pos token.Pos, format string, args ...interface{}) {
check.errorf(pos, "invalid argument: "+format, args...)
}
func (check *Checker) invalidOp(pos token.Pos, format string, args ...interface{}) {
check.errorf(pos, "invalid operation: "+format, args...)
}
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types
import (
"fmt"
"go/parser"
"go/token"
)
// Eval returns the type and, if constant, the value for the
// expression expr, evaluated at position pos of package pkg,
// which must have been derived from type-checking an AST with
// complete position information relative to the provided file
// set.
//
// If the expression contains function literals, their bodies
// are ignored (i.e., the bodies are not type-checked).
//
// If pkg == nil, the Universe scope is used and the provided
// position pos is ignored. If pkg != nil, and pos is invalid,
// the package scope is used. Otherwise, pos must belong to the
// package.
//
// An error is returned if pos is not within the package or
// if the node cannot be evaluated.
//
// Note: Eval should not be used instead of running Check to compute
// types and values, but in addition to Check. Eval will re-evaluate
// its argument each time, and it also does not know about the context
// in which an expression is used (e.g., an assignment). Thus, top-
// level untyped constants will return an untyped type rather then the
// respective context-specific type.
//
func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (TypeAndValue, error) {
// determine scope
var scope *Scope
if pkg == nil {
scope = Universe
pos = token.NoPos
} else if !pos.IsValid() {
scope = pkg.scope
} else {
// The package scope extent (position information) may be
// incorrect (files spread across a wide range of fset
// positions) - ignore it and just consider its children
// (file scopes).
for _, fscope := range pkg.scope.children {
if scope = fscope.Innermost(pos); scope != nil {
break
}
}
if scope == nil || debug {
s := scope
for s != nil && s != pkg.scope {
s = s.parent
}
// s == nil || s == pkg.scope
if s == nil {
return TypeAndValue{}, fmt.Errorf("no position %s found in package %s", fset.Position(pos), pkg.name)
}
}
}
// parse expressions
node, err := parser.ParseExprFrom(fset, "eval", expr, 0)
if err != nil {
return TypeAndValue{}, err
}
// initialize checker
check := NewChecker(nil, fset, pkg, nil)
check.scope = scope
check.pos = pos
defer check.handleBailout(&err)
// evaluate node
var x operand
check.rawExpr(&x, node, nil)
return TypeAndValue{x.mode, x.typ, x.val}, err
}
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements printing of expressions.
package types
import (
"bytes"
"go/ast"
)
// ExprString returns the (possibly shortened) string representation for x.
// Shortened representations are suitable for user interfaces but may not
// necessarily follow Go syntax.
func ExprString(x ast.Expr) string {
var buf bytes.Buffer
WriteExpr(&buf, x)
return buf.String()
}
// WriteExpr writes the (possibly shortened) string representation for x to buf.
// Shortened representations are suitable for user interfaces but may not
// necessarily follow Go syntax.
func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
// The AST preserves source-level parentheses so there is
// no need to introduce them here to correct for different
// operator precedences. (This assumes that the AST was
// generated by a Go parser.)
switch x := x.(type) {
default:
buf.WriteString("(bad expr)") // nil, ast.BadExpr, ast.KeyValueExpr
case *ast.Ident:
buf.WriteString(x.Name)
case *ast.Ellipsis:
buf.WriteString("...")
if x.Elt != nil {
WriteExpr(buf, x.Elt)
}
case *ast.BasicLit:
buf.WriteString(x.Value)
case *ast.FuncLit:
buf.WriteByte('(')
WriteExpr(buf, x.Type)
buf.WriteString(" literal)") // shortened
case *ast.CompositeLit:
buf.WriteByte('(')
WriteExpr(buf, x.Type)
buf.WriteString(" literal)") // shortened
case *ast.ParenExpr:
buf.WriteByte('(')
WriteExpr(buf, x.X)
buf.WriteByte(')')
case *ast.SelectorExpr:
WriteExpr(buf, x.X)
buf.WriteByte('.')
buf.WriteString(x.Sel.Name)
case *ast.IndexExpr:
WriteExpr(buf, x.X)
buf.WriteByte('[')
WriteExpr(buf, x.Index)
buf.WriteByte(']')
case *ast.SliceExpr:
WriteExpr(buf, x.X)
buf.WriteByte('[')
if x.Low != nil {
WriteExpr(buf, x.Low)
}
buf.WriteByte(':')
if x.High != nil {
WriteExpr(buf, x.High)
}
if x.Slice3 {
buf.WriteByte(':')
if x.Max != nil {
WriteExpr(buf, x.Max)
}
}
buf.WriteByte(']')
case *ast.TypeAssertExpr:
WriteExpr(buf, x.X)
buf.WriteString(".(")
WriteExpr(buf, x.Type)
buf.WriteByte(')')
case *ast.CallExpr:
WriteExpr(buf, x.Fun)
buf.WriteByte('(')
for i, arg := range x.Args {
if i > 0 {
buf.WriteString(", ")
}
WriteExpr(buf, arg)
}
if x.Ellipsis.IsValid() {
buf.WriteString("...")
}
buf.WriteByte(')')
case *ast.StarExpr:
buf.WriteByte('*')
WriteExpr(buf, x.X)
case *ast.UnaryExpr:
buf.WriteString(x.Op.String())
WriteExpr(buf, x.X)
case *ast.BinaryExpr:
WriteExpr(buf, x.X)
buf.WriteByte(' ')
buf.WriteString(x.Op.String())
buf.WriteByte(' ')
WriteExpr(buf, x.Y)
case *ast.ArrayType:
buf.WriteByte('[')
if x.Len != nil {
WriteExpr(buf, x.Len)
}
buf.WriteByte(']')
WriteExpr(buf, x.Elt)
case *ast.StructType:
buf.WriteString("struct{")
writeFieldList(buf, x.Fields, "; ", false)
buf.WriteByte('}')
case *ast.FuncType:
buf.WriteString("func")
writeSigExpr(buf, x)
case *ast.InterfaceType:
buf.WriteString("interface{")
writeFieldList(buf, x.Methods, "; ", true)
buf.WriteByte('}')
case *ast.MapType:
buf.WriteString("map[")
WriteExpr(buf, x.Key)
buf.WriteByte(']')
WriteExpr(buf, x.Value)
case *ast.ChanType:
var s string
switch x.Dir {
case ast.SEND:
s = "chan<- "
case ast.RECV:
s = "<-chan "
default:
s = "chan "
}
buf.WriteString(s)
WriteExpr(buf, x.Value)
}
}
func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) {
buf.WriteByte('(')
writeFieldList(buf, sig.Params, ", ", false)
buf.WriteByte(')')
res := sig.Results
n := res.NumFields()
if n == 0 {
// no result
return
}
buf.WriteByte(' ')
if n == 1 && len(res.List[0].Names) == 0 {
// single unnamed result
WriteExpr(buf, res.List[0].Type)
return
}
// multiple or named result(s)
buf.WriteByte('(')
writeFieldList(buf, res, ", ", false)
buf.WriteByte(')')
}
func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface bool) {
for i, f := range fields.List {
if i > 0 {
buf.WriteString(sep)
}
// field list names
for i, name := range f.Names {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(name.Name)
}
// types of interface methods consist of signatures only
if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface {
writeSigExpr(buf, sig)
continue
}
// named fields are separated with a blank from the field type
if len(f.Names) > 0 {
buf.WriteByte(' ')
}
WriteExpr(buf, f.Type)
// ignore tag
}
}
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
// Build this command explicitly: go build gotype.go
/*
The gotype command, like the front-end of a Go compiler, parses and
type-checks a single Go package. Errors are reported if the analysis
fails; otherwise gotype is quiet (unless -v is set).
Without a list of paths, gotype reads from standard input, which
must provide a single Go source file defining a complete package.
With a single directory argument, gotype checks the Go files in
that directory, comprising a single package. Use -t to include the
(in-package) _test.go files. Use -x to type check only external
test files.
Otherwise, each path must be the filename of a Go file belonging
to the same package.
Imports are processed by importing directly from the source of
imported packages (default), or by importing from compiled and
installed packages (by setting -c to the respective compiler).
The -c flag must be set to a compiler ("gc", "gccgo") when type-
checking packages containing imports with relative import paths
(import "./mypkg") because the source importer cannot know which
files to include for such packages.
Usage:
gotype [flags] [path...]
The flags are:
-t
include local test files in a directory (ignored if -x is provided)
-x
consider only external test files in a directory
-e
report all errors (not just the first 10)
-v
verbose mode
-c
compiler used for installed packages (gc, gccgo, or source); default: source
Flags controlling additional output:
-ast
print AST (forces -seq)
-trace
print parse trace (forces -seq)
-comments
parse comments (ignored unless -ast or -trace is provided)
Examples:
To check the files a.go, b.go, and c.go:
gotype a.go b.go c.go
To check an entire package including (in-package) tests in the directory dir and print the processed files:
gotype -t -v dir
To check the external test package (if any) in the current directory, based on installed packages compiled with
cmd/compile:
gotype -c=gc -x .
To verify the output of a pipe:
echo "package foo" | gotype
*/
package main
import (
"flag"
"fmt"
"go/ast"
"go/build"
"go/importer"
"go/parser"
"go/scanner"
"go/token"
"go/types"
"io/ioutil"
"os"
"path/filepath"
"sync"
"time"
)
var (
// main operation modes
testFiles = flag.Bool("t", false, "include in-package test files in a directory")
xtestFiles = flag.Bool("x", false, "consider only external test files in a directory")
allErrors = flag.Bool("e", false, "report all errors, not just the first 10")
verbose = flag.Bool("v", false, "verbose mode")
compiler = flag.String("c", "source", "compiler used for installed packages (gc, gccgo, or source)")
// additional output control
printAST = flag.Bool("ast", false, "print AST (forces -seq)")
printTrace = flag.Bool("trace", false, "print parse trace (forces -seq)")
parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)")
)
var (
fset = token.NewFileSet()
errorCount = 0
sequential = false
parserMode parser.Mode
)
func initParserMode() {
if *allErrors {
parserMode |= parser.AllErrors
}
if *printAST {
sequential = true
}
if *printTrace {
parserMode |= parser.Trace
sequential = true
}
if *parseComments && (*printAST || *printTrace) {
parserMode |= parser.ParseComments
}
}
const usageString = `usage: gotype [flags] [path ...]
The gotype command, like the front-end of a Go compiler, parses and
type-checks a single Go package. Errors are reported if the analysis
fails; otherwise gotype is quiet (unless -v is set).
Without a list of paths, gotype reads from standard input, which
must provide a single Go source file defining a complete package.
With a single directory argument, gotype checks the Go files in
that directory, comprising a single package. Use -t to include the
(in-package) _test.go files. Use -x to type check only external
test files.
Otherwise, each path must be the filename of a Go file belonging
to the same package.
Imports are processed by importing directly from the source of
imported packages (default), or by importing from compiled and
installed packages (by setting -c to the respective compiler).
The -c flag must be set to a compiler ("gc", "gccgo") when type-
checking packages containing imports with relative import paths
(import "./mypkg") because the source importer cannot know which
files to include for such packages.
`
func usage() {
fmt.Fprintln(os.Stderr, usageString)
flag.PrintDefaults()
os.Exit(2)
}
func report(err error) {
scanner.PrintError(os.Stderr, err)
if list, ok := err.(scanner.ErrorList); ok {
errorCount += len(list)
return
}
errorCount++
}
// parse may be called concurrently
func parse(filename string, src interface{}) (*ast.File, error) {
if *verbose {
fmt.Println(filename)
}
file, err := parser.ParseFile(fset, filename, src, parserMode) // ok to access fset concurrently
if *printAST {
ast.Print(fset, file)
}
return file, err
}
func parseStdin() (*ast.File, error) {
src, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return nil, err
}
return parse("<standard input>", src)
}
func parseFiles(dir string, filenames []string) ([]*ast.File, error) {
files := make([]*ast.File, len(filenames))
errors := make([]error, len(filenames))
var wg sync.WaitGroup
for i, filename := range filenames {
wg.Add(1)
go func(i int, filepath string) {
defer wg.Done()
files[i], errors[i] = parse(filepath, nil)
}(i, filepath.Join(dir, filename))
if sequential {
wg.Wait()
}
}
wg.Wait()
// if there are errors, return the first one for deterministic results
for _, err := range errors {
if err != nil {
return nil, err
}
}
return files, nil
}
func parseDir(dir string) ([]*ast.File, error) {
ctxt := build.Default
pkginfo, err := ctxt.ImportDir(dir, 0)
if _, nogo := err.(*build.NoGoError); err != nil && !nogo {
return nil, err
}
if *xtestFiles {
return parseFiles(dir, pkginfo.XTestGoFiles)
}
filenames := append(pkginfo.GoFiles, pkginfo.CgoFiles...)
if *testFiles {
filenames = append(filenames, pkginfo.TestGoFiles...)
}
return parseFiles(dir, filenames)
}
func getPkgFiles(args []string) ([]*ast.File, error) {
if len(args) == 0 {
// stdin
file, err := parseStdin()
if err != nil {
return nil, err
}
return []*ast.File{file}, nil
}
if len(args) == 1 {
// possibly a directory
path := args[0]
info, err := os.Stat(path)
if err != nil {
return nil, err
}
if info.IsDir() {
return parseDir(path)
}
}
// list of files
return parseFiles("", args)
}
func checkPkgFiles(files []*ast.File) {
type bailout struct{}
// if checkPkgFiles is called multiple times, set up conf only once
conf := types.Config{
FakeImportC: true,
Error: func(err error) {
if !*allErrors && errorCount >= 10 {
panic(bailout{})
}
report(err)
},
Importer: importer.For(*compiler, nil),
Sizes: types.SizesFor(build.Default.Compiler, build.Default.GOARCH),
}
defer func() {
switch p := recover().(type) {
case nil, bailout:
// normal return or early exit
default:
// re-panic
panic(p)
}
}()
const path = "pkg" // any non-empty string will do for now
conf.Check(path, fset, files, nil)
}
func printStats(d time.Duration) {
fileCount := 0
lineCount := 0
fset.Iterate(func(f *token.File) bool {
fileCount++
lineCount += f.LineCount()
return true
})
fmt.Printf(
"%s (%d files, %d lines, %d lines/s)\n",
d, fileCount, lineCount, int64(float64(lineCount)/d.Seconds()),
)
}
func main() {
flag.Usage = usage
flag.Parse()
initParserMode()
start := time.Now()
files, err := getPkgFiles(flag.Args())
if err != nil {
report(err)
os.Exit(2)
}
checkPkgFiles(files)
if errorCount > 0 {
os.Exit(2)
}
if *verbose {
printStats(time.Since(start))
}
}
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types
import (
"container/heap"
"fmt"
)
// initOrder computes the Info.InitOrder for package variables.
func (check *Checker) initOrder() {
// An InitOrder may already have been computed if a package is
// built from several calls to (*Checker).Files. Clear it.
check.Info.InitOrder = check.Info.InitOrder[:0]
// Compute the object dependency graph and initialize
// a priority queue with the list of graph nodes.
pq := nodeQueue(dependencyGraph(check.objMap))
heap.Init(&pq)
const debug = false
if debug {
fmt.Printf("Computing initialization order for %s\n\n", check.pkg)
fmt.Println("Object dependency graph:")
for obj, d := range check.objMap {
// only print objects that may appear in the dependency graph
if obj, _ := obj.(dependency); obj != nil {
if len(d.deps) > 0 {
fmt.Printf("\t%s depends on\n", obj.Name())
for dep := range d.deps {
fmt.Printf("\t\t%s\n", dep.Name())
}
} else {
fmt.Printf("\t%s has no dependencies\n", obj.Name())
}
}
}
fmt.Println()
fmt.Println("Transposed object dependency graph (functions eliminated):")
for _, n := range pq {
fmt.Printf("\t%s depends on %d nodes\n", n.obj.Name(), n.ndeps)
for p := range n.pred {
fmt.Printf("\t\t%s is dependent\n", p.obj.Name())
}
}
fmt.Println()
fmt.Println("Processing nodes:")
}
// Determine initialization order by removing the highest priority node
// (the one with the fewest dependencies) and its edges from the graph,
// repeatedly, until there are no nodes left.
// In a valid Go program, those nodes always have zero dependencies (after
// removing all incoming dependencies), otherwise there are initialization
// cycles.
emitted := make(map[*declInfo]bool)
for len(pq) > 0 {
// get the next node
n := heap.Pop(&pq).(*graphNode)
if debug {
fmt.Printf("\t%s (src pos %d) depends on %d nodes now\n",
n.obj.Name(), n.obj.order(), n.ndeps)
}
// if n still depends on other nodes, we have a cycle
if n.ndeps > 0 {
cycle := findPath(check.objMap, n.obj, n.obj, make(objSet))
// If n.obj is not part of the cycle (e.g., n.obj->b->c->d->c),
// cycle will be nil. Don't report anything in that case since
// the cycle is reported when the algorithm gets to an object
// in the cycle.
// Furthermore, once an object in the cycle is encountered,
// the cycle will be broken (dependency count will be reduced
// below), and so the remaining nodes in the cycle don't trigger
// another error (unless they are part of multiple cycles).
if cycle != nil {
check.reportCycle(cycle)
}
// Ok to continue, but the variable initialization order
// will be incorrect at this point since it assumes no
// cycle errors.
}
// reduce dependency count of all dependent nodes
// and update priority queue
for p := range n.pred {
p.ndeps--
heap.Fix(&pq, p.index)
}
// record the init order for variables with initializers only
v, _ := n.obj.(*Var)
info := check.objMap[v]
if v == nil || !info.hasInitializer() {
continue
}
// n:1 variable declarations such as: a, b = f()
// introduce a node for each lhs variable (here: a, b);
// but they all have the same initializer - emit only
// one, for the first variable seen
if emitted[info] {
continue // initializer already emitted, if any
}
emitted[info] = true
infoLhs := info.lhs // possibly nil (see declInfo.lhs field comment)
if infoLhs == nil {
infoLhs = []*Var{v}
}
init := &Initializer{infoLhs, info.init}
check.Info.InitOrder = append(check.Info.InitOrder, init)
}
if debug {
fmt.Println()
fmt.Println("Initialization order:")
for _, init := range check.Info.InitOrder {
fmt.Printf("\t%s\n", init)
}
fmt.Println()
}
}
// findPath returns the (reversed) list of objects []Object{to, ... from}
// such that there is a path of object dependencies from 'from' to 'to'.
// If there is no such path, the result is nil.
func findPath(objMap map[Object]*declInfo, from, to Object, visited objSet) []Object {
if visited[from] {
return nil // node already seen
}
visited[from] = true
for d := range objMap[from].deps {
if d == to {
return []Object{d}
}
if P := findPath(objMap, d, to, visited); P != nil {
return append(P, d)
}
}
return nil
}
// reportCycle reports an error for the given cycle.
func (check *Checker) reportCycle(cycle []Object) {
obj := cycle[0]
check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name())
// subtle loop: print cycle[i] for i = 0, n-1, n-2, ... 1 for len(cycle) = n
for i := len(cycle) - 1; i >= 0; i-- {
check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
obj = cycle[i]
}
// print cycle[0] again to close the cycle
check.errorf(obj.Pos(), "\t%s", obj.Name())
}
// ----------------------------------------------------------------------------
// Object dependency graph
// A dependency is an object that may be a dependency in an initialization
// expression. Only constants, variables, and functions can be dependencies.
// Constants are here because constant expression cycles are reported during
// initialization order computation.
type dependency interface {
Object
isDependency()
}
// A graphNode represents a node in the object dependency graph.
// Each node p in n.pred represents an edge p->n, and each node
// s in n.succ represents an edge n->s; with a->b indicating that
// a depends on b.
type graphNode struct {
obj dependency // object represented by this node
pred, succ nodeSet // consumers and dependencies of this node (lazily initialized)
index int // node index in graph slice/priority queue
ndeps int // number of outstanding dependencies before this object can be initialized
}
type nodeSet map[*graphNode]bool
func (s *nodeSet) add(p *graphNode) {
if *s == nil {
*s = make(nodeSet)
}
(*s)[p] = true
}
// dependencyGraph computes the object dependency graph from the given objMap,
// with any function nodes removed. The resulting graph contains only constants
// and variables.
func dependencyGraph(objMap map[Object]*declInfo) []*graphNode {
// M is the dependency (Object) -> graphNode mapping
M := make(map[dependency]*graphNode)
for obj := range objMap {
// only consider nodes that may be an initialization dependency
if obj, _ := obj.(dependency); obj != nil {
M[obj] = &graphNode{obj: obj}
}
}
// compute edges for graph M
// (We need to include all nodes, even isolated ones, because they still need
// to be scheduled for initialization in correct order relative to other nodes.)
for obj, n := range M {
// for each dependency obj -> d (= deps[i]), create graph edges n->s and s->n
for d := range objMap[obj].deps {
// only consider nodes that may be an initialization dependency
if d, _ := d.(dependency); d != nil {
d := M[d]
n.succ.add(d)
d.pred.add(n)
}
}
}
// remove function nodes and collect remaining graph nodes in G
// (Mutually recursive functions may introduce cycles among themselves
// which are permitted. Yet such cycles may incorrectly inflate the dependency
// count for variables which in turn may not get scheduled for initialization
// in correct order.)
var G []*graphNode
for obj, n := range M {
if _, ok := obj.(*Func); ok {
// connect each predecessor p of n with each successor s
// and drop the function node (don't collect it in G)
for p := range n.pred {
// ignore self-cycles
if p != n {
// Each successor s of n becomes a successor of p, and
// each predecessor p of n becomes a predecessor of s.
for s := range n.succ {
// ignore self-cycles
if s != n {
p.succ.add(s)
s.pred.add(p)
delete(s.pred, n) // remove edge to n
}
}
delete(p.succ, n) // remove edge to n
}
}
} else {
// collect non-function nodes
G = append(G, n)
}
}
// fill in index and ndeps fields
for i, n := range G {
n.index = i
n.ndeps = len(n.succ)
}
return G
}
// ----------------------------------------------------------------------------
// Priority queue
// nodeQueue implements the container/heap interface;
// a nodeQueue may be used as a priority queue.
type nodeQueue []*graphNode
func (a nodeQueue) Len() int { return len(a) }
func (a nodeQueue) Swap(i, j int) {
x, y := a[i], a[j]
a[i], a[j] = y, x
x.index, y.index = j, i
}
func (a nodeQueue) Less(i, j int) bool {
x, y := a[i], a[j]
// nodes are prioritized by number of incoming dependencies (1st key)
// and source order (2nd key)
return x.ndeps < y.ndeps || x.ndeps == y.ndeps && x.obj.order() < y.obj.order()
}
func (a *nodeQueue) Push(x interface{}) {
panic("unreachable")
}
func (a *nodeQueue) Pop() interface{} {
n := len(*a)
x := (*a)[n-1]
x.index = -1 // for safety
*a = (*a)[:n-1]
return x
}
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types
import (
"go/ast"
"go/token"
)
// labels checks correct label use in body.
func (check *Checker) labels(body *ast.BlockStmt) {
// set of all labels in this body
all := NewScope(nil, body.Pos(), body.End(), "label")
fwdJumps := check.blockBranches(all, nil, nil, body.List)
// If there are any forward jumps left, no label was found for
// the corresponding goto statements. Either those labels were
// never defined, or they are inside blocks and not reachable
// for the respective gotos.
for _, jmp := range fwdJumps {
var msg string
name := jmp.Label.Name
if alt := all.Lookup(name); alt != nil {
msg = "goto %s jumps into block"
alt.(*Label).used = true // avoid another error
} else {
msg = "label %s not declared"
}
check.errorf(jmp.Label.Pos(), msg, name)
}
// spec: "It is illegal to define a label that is never used."
for _, obj := range all.elems {
if lbl := obj.(*Label); !lbl.used {
check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
}
}
}
// A block tracks label declarations in a block and its enclosing blocks.
type block struct {
parent *block // enclosing block
lstmt *ast.LabeledStmt // labeled statement to which this block belongs, or nil
labels map[string]*ast.LabeledStmt // allocated lazily
}
// insert records a new label declaration for the current block.
// The label must not have been declared before in any block.
func (b *block) insert(s *ast.LabeledStmt) {
name := s.Label.Name
if debug {
assert(b.gotoTarget(name) == nil)
}
labels := b.labels
if labels == nil {
labels = make(map[string]*ast.LabeledStmt)
b.labels = labels
}
labels[name] = s
}
// gotoTarget returns the labeled statement in the current
// or an enclosing block with the given label name, or nil.
func (b *block) gotoTarget(name string) *ast.LabeledStmt {
for s := b; s != nil; s = s.parent {
if t := s.labels[name]; t != nil {
return t
}
}
return nil
}
// enclosingTarget returns the innermost enclosing labeled
// statement with the given label name, or nil.
func (b *block) enclosingTarget(name string) *ast.LabeledStmt {
for s := b; s != nil; s = s.parent {
if t := s.lstmt; t != nil && t.Label.Name == name {
return t
}
}
return nil
}
// blockBranches processes a block's statement list and returns the set of outgoing forward jumps.
// all is the scope of all declared labels, parent the set of labels declared in the immediately
// enclosing block, and lstmt is the labeled statement this block is associated with (or nil).
func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt {
b := &block{parent: parent, lstmt: lstmt}
var (
varDeclPos token.Pos
fwdJumps, badJumps []*ast.BranchStmt
)
// All forward jumps jumping over a variable declaration are possibly
// invalid (they may still jump out of the block and be ok).
// recordVarDecl records them for the given position.
recordVarDecl := func(pos token.Pos) {
varDeclPos = pos
badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps
}
jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool {
if varDeclPos.IsValid() {
for _, bad := range badJumps {
if jmp == bad {
return true
}
}
}
return false
}
blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) {
// Unresolved forward jumps inside the nested block
// become forward jumps in the current block.
fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...)
}
var stmtBranches func(ast.Stmt)
stmtBranches = func(s ast.Stmt) {
switch s := s.(type) {
case *ast.DeclStmt:
if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR {
recordVarDecl(d.Pos())
}
case *ast.LabeledStmt:
// declare non-blank label
if name := s.Label.Name; name != "_" {
lbl := NewLabel(s.Label.Pos(), check.pkg, name)
if alt := all.Insert(lbl); alt != nil {
check.softErrorf(lbl.pos, "label %s already declared", name)
check.reportAltDecl(alt)
// ok to continue
} else {
b.insert(s)
check.recordDef(s.Label, lbl)
}
// resolve matching forward jumps and remove them from fwdJumps
i := 0
for _, jmp := range fwdJumps {
if jmp.Label.Name == name {
// match
lbl.used = true
check.recordUse(jmp.Label, lbl)
if jumpsOverVarDecl(jmp) {
check.softErrorf(
jmp.Label.Pos(),
"goto %s jumps over variable declaration at line %d",
name,
check.fset.Position(varDeclPos).Line,
)
// ok to continue
}
} else {
// no match - record new forward jump
fwdJumps[i] = jmp
i++
}
}
fwdJumps = fwdJumps[:i]
lstmt = s
}
stmtBranches(s.Stmt)
case *ast.BranchStmt:
if s.Label == nil {
return // checked in 1st pass (check.stmt)
}
// determine and validate target
name := s.Label.Name
switch s.Tok {
case token.BREAK:
// spec: "If there is a label, it must be that of an enclosing
// "for", "switch", or "select" statement, and that is the one
// whose execution terminates."
valid := false
if t := b.enclosingTarget(name); t != nil {
switch t.Stmt.(type) {
case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt:
valid = true
}
}
if !valid {
check.errorf(s.Label.Pos(), "invalid break label %s", name)
return
}
case token.CONTINUE:
// spec: "If there is a label, it must be that of an enclosing
// "for" statement, and that is the one whose execution advances."
valid := false
if t := b.enclosingTarget(name); t != nil {
switch t.Stmt.(type) {
case *ast.ForStmt, *ast.RangeStmt:
valid = true
}
}
if !valid {
check.errorf(s.Label.Pos(), "invalid continue label %s", name)
return
}
case token.GOTO:
if b.gotoTarget(name) == nil {
// label may be declared later - add branch to forward jumps
fwdJumps = append(fwdJumps, s)
return
}
default:
check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name)
return
}
// record label use
obj := all.Lookup(name)
obj.(*Label).used = true
check.recordUse(s.Label, obj)
case *ast.AssignStmt:
if s.Tok == token.DEFINE {
recordVarDecl(s.Pos())
}
case *ast.BlockStmt:
blockBranches(lstmt, s.List)
case *ast.IfStmt:
stmtBranches(s.Body)
if s.Else != nil {
stmtBranches(s.Else)
}
case *ast.CaseClause:
blockBranches(nil, s.Body)
case *ast.SwitchStmt:
stmtBranches(s.Body)
case *ast.TypeSwitchStmt:
stmtBranches(s.Body)
case *ast.CommClause:
blockBranches(nil, s.Body)
case *ast.SelectStmt:
stmtBranches(s.Body)
case *ast.ForStmt:
stmtBranches(s.Body)
case *ast.RangeStmt:
stmtBranches(s.Body)
}
}
for _, s := range list {
stmtBranches(s)
}
return fwdJumps
}
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements method sets.
package types
import (
"bytes"
"fmt"
"sort"
)
// A MethodSet is an ordered set of concrete or abstract (interface) methods;
// a method is a MethodVal selection, and they are ordered by ascending m.Obj().Id().
// The zero value for a MethodSet is a ready-to-use empty method set.
type MethodSet struct {
list []*Selection
}
func (s *MethodSet) String() string {
if s.Len() == 0 {
return "MethodSet {}"
}
var buf bytes.Buffer
fmt.Fprintln(&buf, "MethodSet {")
for _, f := range s.list {
fmt.Fprintf(&buf, "\t%s\n", f)
}
fmt.Fprintln(&buf, "}")
return buf.String()
}
// Len returns the number of methods in s.
func (s *MethodSet) Len() int { return len(s.list) }
// At returns the i'th method in s for 0 <= i < s.Len().
func (s *MethodSet) At(i int) *Selection { return s.list[i] }
// Lookup returns the method with matching package and name, or nil if not found.
func (s *MethodSet) Lookup(pkg *Package, name string) *Selection {
if s.Len() == 0 {
return nil
}
key := Id(pkg, name)
i := sort.Search(len(s.list), func(i int) bool {
m := s.list[i]
return m.obj.Id() >= key
})
if i < len(s.list) {
m := s.list[i]
if m.obj.Id() == key {
return m
}
}
return nil
}
// Shared empty method set.
var emptyMethodSet MethodSet
// NewMethodSet returns the method set for the given type T.
// It always returns a non-nil method set, even if it is empty.
func NewMethodSet(T Type) *MethodSet {
// WARNING: The code in this function is extremely subtle - do not modify casually!
// This function and lookupFieldOrMethod should be kept in sync.
// method set up to the current depth, allocated lazily
var base methodSet
typ, isPtr := deref(T)
// *typ where typ is an interface has no methods.
if isPtr && IsInterface(typ) {
return &emptyMethodSet
}
// Start with typ as single entry at shallowest depth.
current := []embeddedType{{typ, nil, isPtr, false}}
// Named types that we have seen already, allocated lazily.
// Used to avoid endless searches in case of recursive types.
// Since only Named types can be used for recursive types, we
// only need to track those.
// (If we ever allow type aliases to construct recursive types,
// we must use type identity rather than pointer equality for
// the map key comparison, as we do in consolidateMultiples.)
var seen map[*Named]bool
// collect methods at current depth
for len(current) > 0 {
var next []embeddedType // embedded types found at current depth
// field and method sets at current depth, allocated lazily
var fset fieldSet
var mset methodSet
for _, e := range current {
typ := e.typ
// If we have a named type, we may have associated methods.
// Look for those first.
if named, _ := typ.(*Named); named != nil {
if seen[named] {
// We have seen this type before, at a more shallow depth
// (note that multiples of this type at the current depth
// were consolidated before). The type at that depth shadows
// this same type at the current depth, so we can ignore
// this one.
continue
}
if seen == nil {
seen = make(map[*Named]bool)
}
seen[named] = true
mset = mset.add(named.methods, e.index, e.indirect, e.multiples)
// continue with underlying type
typ = named.underlying
}
switch t := typ.(type) {
case *Struct:
for i, f := range t.fields {
fset = fset.add(f, e.multiples)
// Embedded fields are always of the form T or *T where
// T is a type name. If typ appeared multiple times at
// this depth, f.Type appears multiple times at the next
// depth.
if f.anonymous {
typ, isPtr := deref(f.typ)
// TODO(gri) optimization: ignore types that can't
// have fields or methods (only Named, Struct, and
// Interface types need to be considered).
next = append(next, embeddedType{typ, concat(e.index, i), e.indirect || isPtr, e.multiples})
}
}
case *Interface:
mset = mset.add(t.allMethods, e.index, true, e.multiples)
}
}
// Add methods and collisions at this depth to base if no entries with matching
// names exist already.
for k, m := range mset {
if _, found := base[k]; !found {
// Fields collide with methods of the same name at this depth.
if _, found := fset[k]; found {
m = nil // collision
}
if base == nil {
base = make(methodSet)
}
base[k] = m
}
}
// Multiple fields with matching names collide at this depth and shadow all
// entries further down; add them as collisions to base if no entries with
// matching names exist already.
for k, f := range fset {
if f == nil {
if _, found := base[k]; !found {
if base == nil {
base = make(methodSet)
}
base[k] = nil // collision
}
}
}
current = consolidateMultiples(next)
}
if len(base) == 0 {
return &emptyMethodSet
}
// collect methods
var list []*Selection
for _, m := range base {
if m != nil {
m.recv = T
list = append(list, m)
}
}
// sort by unique name
sort.Slice(list, func(i, j int) bool {
return list[i].obj.Id() < list[j].obj.Id()
})
return &MethodSet{list}
}
// A fieldSet is a set of fields and name collisions.
// A collision indicates that multiple fields with the
// same unique id appeared.
type fieldSet map[string]*Var // a nil entry indicates a name collision
// Add adds field f to the field set s.
// If multiples is set, f appears multiple times
// and is treated as a collision.
func (s fieldSet) add(f *Var, multiples bool) fieldSet {
if s == nil {
s = make(fieldSet)
}
key := f.Id()
// if f is not in the set, add it
if !multiples {
if _, found := s[key]; !found {
s[key] = f
return s
}
}
s[key] = nil // collision
return s
}
// A methodSet is a set of methods and name collisions.
// A collision indicates that multiple methods with the
// same unique id appeared.
type methodSet map[string]*Selection // a nil entry indicates a name collision
// Add adds all functions in list to the method set s.
// If multiples is set, every function in list appears multiple times
// and is treated as a collision.
func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet {
if len(list) == 0 {
return s
}
if s == nil {
s = make(methodSet)
}
for i, f := range list {
key := f.Id()
// if f is not in the set, add it
if !multiples {
// TODO(gri) A found method may not be added because it's not in the method set
// (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method
// set and may not collide with the first one, thus leading to a false positive.
// Is that possible? Investigate.
if _, found := s[key]; !found && (indirect || !ptrRecv(f)) {
s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect}
continue
}
}
s[key] = nil // collision
}
return s
}
// ptrRecv reports whether the receiver is of the form *T.
// The receiver must exist.
func ptrRecv(f *Func) bool {
_, isPtr := deref(f.typ.(*Signature).recv.typ)
return isPtr
}
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements objsets.
//
// An objset is similar to a Scope but objset elements
// are identified by their unique id, instead of their
// object name.
package types
// An objset is a set of objects identified by their unique id.
// The zero value for objset is a ready-to-use empty objset.
type objset map[string]Object // initialized lazily
// insert attempts to insert an object obj into objset s.
// If s already contains an alternative object alt with
// the same name, insert leaves s unchanged and returns alt.
// Otherwise it inserts obj and returns nil.
func (s *objset) insert(obj Object) Object {
id := obj.Id()
if alt := (*s)[id]; alt != nil {
return alt
}
if *s == nil {
*s = make(map[string]Object)
}
(*s)[id] = obj
return nil
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file defines operands and associated operations.
package types
import (
"bytes"
"go/ast"
"go/constant"
"go/token"
)
// An operandMode specifies the (addressing) mode of an operand.
type operandMode byte
const (
invalid operandMode = iota // operand is invalid
novalue // operand represents no value (result of a function call w/o result)
builtin // operand is a built-in function
typexpr // operand is a type
constant_ // operand is a constant; the operand's typ is a Basic type
variable // operand is an addressable variable
mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
value // operand is a computed value
commaok // like value, but operand may be used in a comma,ok expression
)
var operandModeString = [...]string{
invalid: "invalid operand",
novalue: "no value",
builtin: "built-in",
typexpr: "type",
constant_: "constant",
variable: "variable",
mapindex: "map index expression",
value: "value",
commaok: "comma, ok expression",
}
// An operand represents an intermediate value during type checking.
// Operands have an (addressing) mode, the expression evaluating to
// the operand, the operand's type, a value for constants, and an id
// for built-in functions.
// The zero value of operand is a ready to use invalid operand.
//
type operand struct {
mode operandMode
expr ast.Expr
typ Type
val constant.Value
id builtinId
}
// pos returns the position of the expression corresponding to x.
// If x is invalid the position is token.NoPos.
//
func (x *operand) pos() token.Pos {
// x.expr may not be set if x is invalid
if x.expr == nil {
return token.NoPos
}
return x.expr.Pos()
}
// Operand string formats
// (not all "untyped" cases can appear due to the type system,
// but they fall out naturally here)
//
// mode format
//
// invalid <expr> ( <mode> )
// novalue <expr> ( <mode> )
// builtin <expr> ( <mode> )
// typexpr <expr> ( <mode> )
//
// constant <expr> (<untyped kind> <mode> )
// constant <expr> ( <mode> of type <typ>)
// constant <expr> (<untyped kind> <mode> <val> )
// constant <expr> ( <mode> <val> of type <typ>)
//
// variable <expr> (<untyped kind> <mode> )
// variable <expr> ( <mode> of type <typ>)
//
// mapindex <expr> (<untyped kind> <mode> )
// mapindex <expr> ( <mode> of type <typ>)
//
// value <expr> (<untyped kind> <mode> )
// value <expr> ( <mode> of type <typ>)
//
// commaok <expr> (<untyped kind> <mode> )
// commaok <expr> ( <mode> of type <typ>)
//
func operandString(x *operand, qf Qualifier) string {
var buf bytes.Buffer
var expr string
if x.expr != nil {
expr = ExprString(x.expr)
} else {
switch x.mode {
case builtin:
expr = predeclaredFuncs[x.id].name
case typexpr:
expr = TypeString(x.typ, qf)
case constant_:
expr = x.val.String()
}
}
// <expr> (
if expr != "" {
buf.WriteString(expr)
buf.WriteString(" (")
}
// <untyped kind>
hasType := false
switch x.mode {
case invalid, novalue, builtin, typexpr:
// no type
default:
// should have a type, but be cautious (don't crash during printing)
if x.typ != nil {
if isUntyped(x.typ) {
buf.WriteString(x.typ.(*Basic).name)
buf.WriteByte(' ')
break
}
hasType = true
}
}
// <mode>
buf.WriteString(operandModeString[x.mode])
// <val>
if x.mode == constant_ {
if s := x.val.String(); s != expr {
buf.WriteByte(' ')
buf.WriteString(s)
}
}
// <typ>
if hasType {
if x.typ != Typ[Invalid] {
buf.WriteString(" of type ")
WriteType(&buf, x.typ, qf)
} else {
buf.WriteString(" with invalid type")
}
}
// )
if expr != "" {
buf.WriteByte(')')
}
return buf.String()
}
func (x *operand) String() string {
return operandString(x, nil)
}
// setConst sets x to the untyped constant for literal lit.
func (x *operand) setConst(tok token.Token, lit string) {
var kind BasicKind
switch tok {
case token.INT:
kind = UntypedInt
case token.FLOAT:
kind = UntypedFloat
case token.IMAG:
kind = UntypedComplex
case token.CHAR:
kind = UntypedRune
case token.STRING:
kind = UntypedString
default:
unreachable()
}
x.mode = constant_
x.typ = Typ[kind]
x.val = constant.MakeFromLiteral(lit, tok, 0)
}
// isNil reports whether x is the nil value.
func (x *operand) isNil() bool {
return x.mode == value && x.typ == Typ[UntypedNil]
}
// TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
// checker.representable, and checker.assignment are
// overlapping in functionality. Need to simplify and clean up.
// assignableTo reports whether x is assignable to a variable of type T.
// If the result is false and a non-nil reason is provided, it may be set
// to a more detailed explanation of the failure (result != "").
func (x *operand) assignableTo(conf *Config, T Type, reason *string) bool {
if x.mode == invalid || T == Typ[Invalid] {
return true // avoid spurious errors
}
V := x.typ
// x's type is identical to T
if Identical(V, T) {
return true
}
Vu := V.Underlying()
Tu := T.Underlying()
// x is an untyped value representable by a value of type T
// TODO(gri) This is borrowing from checker.convertUntyped and
// checker.representable. Need to clean up.
if isUntyped(Vu) {
switch t := Tu.(type) {
case *Basic:
if x.isNil() && t.kind == UnsafePointer {
return true
}
if x.mode == constant_ {
return representableConst(x.val, conf, t, nil)
}
// The result of a comparison is an untyped boolean,
// but may not be a constant.
if Vb, _ := Vu.(*Basic); Vb != nil {
return Vb.kind == UntypedBool && isBoolean(Tu)
}
case *Interface:
return x.isNil() || t.Empty()
case *Pointer, *Signature, *Slice, *Map, *Chan:
return x.isNil()
}
}
// Vu is typed
// x's type V and T have identical underlying types
// and at least one of V or T is not a named type
if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
return true
}
// T is an interface type and x implements T
if Ti, ok := Tu.(*Interface); ok {
if m, wrongType := MissingMethod(x.typ, Ti, true); m != nil /* Implements(x.typ, Ti) */ {
if reason != nil {
if wrongType {
*reason = "wrong type for method " + m.Name()
} else {
*reason = "missing method " + m.Name()
}
}
return false
}
return true
}
// x is a bidirectional channel value, T is a channel
// type, x's type V and T have identical element types,
// and at least one of V or T is not a named type
if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
return !isNamed(V) || !isNamed(T)
}
}
return false
}
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements resolveOrder.
package types
import (
"go/ast"
"sort"
)
// resolveOrder computes the order in which package-level objects
// must be type-checked.
//
// Interface types appear first in the list, sorted topologically
// by dependencies on embedded interfaces that are also declared
// in this package, followed by all other objects sorted in source
// order.
//
// TODO(gri) Consider sorting all types by dependencies here, and
// in the process check _and_ report type cycles. This may simplify
// the full type-checking phase.
//
func (check *Checker) resolveOrder() []Object {
var ifaces, others []Object
// collect interface types with their dependencies, and all other objects
for obj := range check.objMap {
if ityp := check.interfaceFor(obj); ityp != nil {
ifaces = append(ifaces, obj)
// determine dependencies on embedded interfaces
for _, f := range ityp.Methods.List {
if len(f.Names) == 0 {
// Embedded interface: The type must be a (possibly
// qualified) identifier denoting another interface.
// Imported interfaces are already fully resolved,
// so we can ignore qualified identifiers.
if ident, _ := f.Type.(*ast.Ident); ident != nil {
embedded := check.pkg.scope.Lookup(ident.Name)
if check.interfaceFor(embedded) != nil {
check.objMap[obj].addDep(embedded)
}
}
}
}
} else {
others = append(others, obj)
}
}
// final object order
var order []Object
// sort interface types topologically by dependencies,
// and in source order if there are no dependencies
sort.Sort(inSourceOrder(ifaces))
visited := make(objSet)
for _, obj := range ifaces {
check.appendInPostOrder(&order, obj, visited)
}
// sort everything else in source order
sort.Sort(inSourceOrder(others))
return append(order, others...)
}
// interfaceFor returns the AST interface denoted by obj, or nil.
func (check *Checker) interfaceFor(obj Object) *ast.InterfaceType {
tname, _ := obj.(*TypeName)
if tname == nil {
return nil // not a type
}
d := check.objMap[obj]
if d == nil {
check.dump("%s: %s should have been declared", obj.Pos(), obj.Name())
unreachable()
}
if d.typ == nil {
return nil // invalid AST - ignore (will be handled later)
}
ityp, _ := d.typ.(*ast.InterfaceType)
return ityp
}
func (check *Checker) appendInPostOrder(order *[]Object, obj Object, visited objSet) {
if visited[obj] {
// We've already seen this object; either because it's
// already added to order, or because we have a cycle.
// In both cases we stop. Cycle errors are reported
// when type-checking types.
return
}
visited[obj] = true
d := check.objMap[obj]
for _, obj := range orderedSetObjects(d.deps) {
check.appendInPostOrder(order, obj, visited)
}
*order = append(*order, obj)
}
func orderedSetObjects(set objSet) []Object {
list := make([]Object, len(set))
i := 0
for obj := range set {
// we don't care about the map element value
list[i] = obj
i++
}
sort.Sort(inSourceOrder(list))
return list
}
// inSourceOrder implements the sort.Sort interface.
type inSourceOrder []Object
func (a inSourceOrder) Len() int { return len(a) }
func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() }
func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package types
import (
"fmt"
"go/token"
)
// A Package describes a Go package.
type Package struct {
path string
name string
scope *Scope
complete bool
imports []*Package
fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
}
// NewPackage returns a new Package for the given package path and name.
// The package is not complete and contains no explicit imports.
func NewPackage(path, name string) *Package {
scope := NewScope(Universe, token.NoPos, token.NoPos, fmt.Sprintf("package %q", path))
return &Package{path: path, name: name, scope: scope}
}
// Path returns the package path.
func (pkg *Package) Path() string { return pkg.path }
// Name returns the package name.
func (pkg *Package) Name() string { return pkg.name }
// SetName sets the package name.
func (pkg *Package) SetName(name string) { pkg.name = name }
// Scope returns the (complete or incomplete) package scope
// holding the objects declared at package level (TypeNames,
// Consts, Vars, and Funcs).
func (pkg *Package) Scope() *Scope { return pkg.scope }
// A package is complete if its scope contains (at least) all
// exported objects; otherwise it is incomplete.
func (pkg *Package) Complete() bool { return pkg.complete }
// MarkComplete marks a package as complete.
func (pkg *Package) MarkComplete() { pkg.complete = true }
// Imports returns the list of packages directly imported by
// pkg; the list is in source order.
//
// If pkg was loaded from export data, Imports includes packages that
// provide package-level objects referenced by pkg. This may be more or
// less than the set of packages directly imported by pkg's source code.
func (pkg *Package) Imports() []*Package { return pkg.imports }
// SetImports sets the list of explicitly imported packages to list.
// It is the caller's responsibility to make sure list elements are unique.
func (pkg *Package) SetImports(list []*Package) { pkg.imports = list }
func (pkg *Package) String() string {
return fmt.Sprintf("package %s (%q)", pkg.name, pkg.path)
}
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file implements commonly used type predicates.
package types
import "sort"
func isNamed(typ Type) bool {
if _, ok := typ.(*Basic); ok {
return ok
}
_, ok := typ.(*Named)
return ok
}
func isBoolean(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsBoolean != 0
}
func isInteger(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsInteger != 0
}
func isUnsigned(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsUnsigned != 0
}
func isFloat(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsFloat != 0
}
func isComplex(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsComplex != 0
}
func isNumeric(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsNumeric != 0
}
func isString(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsString != 0
}
func isTyped(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return !ok || t.info&IsUntyped == 0
}
func isUntyped(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsUntyped != 0
}
func isOrdered(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsOrdered != 0
}
func isConstType(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.info&IsConstType != 0
}
// IsInterface reports whether typ is an interface type.
func IsInterface(typ Type) bool {
_, ok := typ.Underlying().(*Interface)
return ok
}
// Comparable reports whether values of type T are comparable.
func Comparable(T Type) bool {
switch t := T.Underlying().(type) {
case *Basic:
// assume invalid types to be comparable
// to avoid follow-up errors
return t.kind != UntypedNil
case *Pointer, *Interface, *Chan:
return true
case *Struct:
for _, f := range t.fields {
if !Comparable(f.typ) {
return false
}
}
return true
case *Array:
return Comparable(t.elem)
}
return false
}
// hasNil reports whether a type includes the nil value.
func hasNil(typ Type) bool {
switch t := typ.Underlying().(type) {
case *Basic:
return t.kind == UnsafePointer
case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
return true
}
return false
}
// Identical reports whether x and y are identical types.
// Receivers of Signature types are ignored.
func Identical(x, y Type) bool {
return identical(x, y, true, nil)
}
// IdenticalIgnoreTags reports whether x and y are identical types if tags are ignored.
// Receivers of Signature types are ignored.
func IdenticalIgnoreTags(x, y Type) bool {
return identical(x, y, false, nil)
}
// An ifacePair is a node in a stack of interface type pairs compared for identity.
type ifacePair struct {
x, y *Interface
prev *ifacePair
}
func (p *ifacePair) identical(q *ifacePair) bool {
return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
}
func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
if x == y {
return true
}
switch x := x.(type) {
case *Basic:
// Basic types are singletons except for the rune and byte
// aliases, thus we cannot solely rely on the x == y check
// above. See also comment in TypeName.IsAlias.
if y, ok := y.(*Basic); ok {
return x.kind == y.kind
}
case *Array:
// Two array types are identical if they have identical element types
// and the same array length.
if y, ok := y.(*Array); ok {
// If one or both array lengths are unknown (< 0) due to some error,
// assume they are the same to avoid spurious follow-on errors.
return (x.len < 0 || y.len < 0 || x.len == y.len) && identical(x.elem, y.elem, cmpTags, p)
}
case *Slice:
// Two slice types are identical if they have identical element types.
if y, ok := y.(*Slice); ok {
return identical(x.elem, y.elem, cmpTags, p)
}
case *Struct:
// Two struct types are identical if they have the same sequence of fields,
// and if corresponding fields have the same names, and identical types,
// and identical tags. Two anonymous fields are considered to have the same
// name. Lower-case field names from different packages are always different.
if y, ok := y.(*Struct); ok {
if x.NumFields() == y.NumFields() {
for i, f := range x.fields {
g := y.fields[i]
if f.anonymous != g.anonymous ||
cmpTags && x.Tag(i) != y.Tag(i) ||
!f.sameId(g.pkg, g.name) ||
!identical(f.typ, g.typ, cmpTags, p) {
return false
}
}
return true
}
}
case *Pointer:
// Two pointer types are identical if they have identical base types.
if y, ok := y.(*Pointer); ok {
return identical(x.base, y.base, cmpTags, p)
}
case *Tuple:
// Two tuples types are identical if they have the same number of elements
// and corresponding elements have identical types.
if y, ok := y.(*Tuple); ok {
if x.Len() == y.Len() {
if x != nil {
for i, v := range x.vars {
w := y.vars[i]
if !identical(v.typ, w.typ, cmpTags, p) {
return false
}
}
}
return true
}
}
case *Signature:
// Two function types are identical if they have the same number of parameters
// and result values, corresponding parameter and result types are identical,
// and either both functions are variadic or neither is. Parameter and result
// names are not required to match.
if y, ok := y.(*Signature); ok {
return x.variadic == y.variadic &&
identical(x.params, y.params, cmpTags, p) &&
identical(x.results, y.results, cmpTags, p)
}
case *Interface:
// Two interface types are identical if they have the same set of methods with
// the same names and identical function types. Lower-case method names from
// different packages are always different. The order of the methods is irrelevant.
if y, ok := y.(*Interface); ok {
a := x.allMethods
b := y.allMethods
if len(a) == len(b) {
// Interface types are the only types where cycles can occur
// that are not "terminated" via named types; and such cycles
// can only be created via method parameter types that are
// anonymous interfaces (directly or indirectly) embedding
// the current interface. Example:
//
// type T interface {
// m() interface{T}
// }
//
// If two such (differently named) interfaces are compared,
// endless recursion occurs if the cycle is not detected.
//
// If x and y were compared before, they must be equal
// (if they were not, the recursion would have stopped);
// search the ifacePair stack for the same pair.
//
// This is a quadratic algorithm, but in practice these stacks
// are extremely short (bounded by the nesting depth of interface
// type declarations that recur via parameter types, an extremely
// rare occurrence). An alternative implementation might use a
// "visited" map, but that is probably less efficient overall.
q := &ifacePair{x, y, p}
for p != nil {
if p.identical(q) {
return true // same pair was compared before
}
p = p.prev
}
if debug {
assert(sort.IsSorted(byUniqueMethodName(a)))
assert(sort.IsSorted(byUniqueMethodName(b)))
}
for i, f := range a {
g := b[i]
if f.Id() != g.Id() || !identical(f.typ, g.typ, cmpTags, q) {
return false
}
}
return true
}
}
case *Map:
// Two map types are identical if they have identical key and value types.
if y, ok := y.(*Map); ok {
return identical(x.key, y.key, cmpTags, p) && identical(x.elem, y.elem, cmpTags, p)
}
case *Chan:
// Two channel types are identical if they have identical value types
// and the same direction.
if y, ok := y.(*Chan); ok {
return x.dir == y.dir && identical(x.elem, y.elem, cmpTags, p)
}
case *Named:
// Two named types are identical if their type names originate
// in the same type declaration.
if y, ok := y.(*Named); ok {
return x.obj == y.obj
}
case nil:
default:
unreachable()
}
return false
}
// Default returns the default "typed" type for an "untyped" type;
// it returns the incoming type for all other types. The default type
// for untyped nil is untyped nil.
//
func Default(typ Type) Type {
if t, ok := typ.(*Basic); ok {
switch t.kind {
case UntypedBool:
return Typ[Bool]
case UntypedInt:
return Typ[Int]
case UntypedRune:
return universeRune // use 'rune' name
case UntypedFloat:
return Typ[Float64]
case UntypedComplex:
return Typ[Complex128]
case UntypedString:
return Typ[String]
}
}
return typ
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment