Merge pull request #92212 from thockin/verify-typecheck-no-gopath

Make verify-typecheck not depend on GOPATH
This commit is contained in:
Kubernetes Prow Robot 2020-06-26 20:16:14 -07:00 committed by GitHub
commit fe4fae0343
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 407 additions and 438 deletions

View File

@ -366,3 +366,5 @@ cat <<__EOF__ > "vendor/OWNERS"
approvers:
- dep-approvers
__EOF__
kube::log::status "NOTE: don't forget to handle vendor/* files that were added or removed"

View File

@ -68,7 +68,7 @@ while IFS='' read -r line; do
all_packages+=("./$line")
done < <( hack/make-rules/helpers/cache_go_dirs.sh "${KUBE_ROOT}/_tmp/all_go_dirs" |
grep "^${FOCUS:-.}" |
grep -vE "(third_party|generated|clientset_generated|hack|/_)" |
grep -vE "(third_party|generated|clientset_generated|hack|testdata|/_)" |
grep -vE "$ignore_pattern" )
failing_packages=()

View File

@ -30,8 +30,12 @@ cd "${KUBE_ROOT}"
make --no-print-directory -C "${KUBE_ROOT}" generated_files
# As of June, 2020 the typecheck tool is written in terms of go/packages, but
# that library doesn't work well with multiple modules. Until that is done,
# force this tooling to run in a fake GOPATH.
ret=0
go run test/typecheck/main.go "$@" || ret=$?
hack/run-in-gopath.sh \
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

View File

@ -1,3 +1,4 @@
# gazelle:exclude testdata
package(default_visibility = ["//visibility:public"])
load(
@ -16,10 +17,7 @@ go_library(
name = "go_default_library",
srcs = ["main.go"],
importpath = "k8s.io/kubernetes/test/typecheck",
deps = [
"//third_party/go-srcimporter:go_default_library",
"//vendor/golang.org/x/crypto/ssh/terminal:go_default_library",
],
deps = ["//vendor/golang.org/x/tools/go/packages:go_default_library"],
)
filegroup(
@ -43,5 +41,12 @@ go_binary(
go_test(
name = "go_default_test",
srcs = ["main_test.go"],
# The "../../" is because $(location) gives a relative path, but
# relative to some root for which I can't find a variable. Empirically
# this is 2 levels up (since this file is 2 levels down from the repo
# root). Hack.
args = ["--go=../../$(location @go_sdk//:bin/go)"],
data = ["@go_sdk//:bin/go"] + glob(["testdata/**"]),
embed = [":go_default_library"],
deps = ["//vendor/golang.org/x/tools/go/packages:go_default_library"],
)

View File

@ -20,11 +20,6 @@ package main
import (
"flag"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"go/types"
"io"
"log"
"os"
@ -32,12 +27,9 @@ import (
"sort"
"strings"
"sync"
"sync/atomic"
"time"
"golang.org/x/crypto/ssh/terminal"
srcimporter "k8s.io/kubernetes/third_party/go-srcimporter"
"golang.org/x/tools/go/packages"
)
var (
@ -51,9 +43,6 @@ var (
tags = flag.String("tags", "", "comma-separated list of build tags to apply in addition to go's defaults")
ignoreDirs = flag.String("ignore-dirs", "", "comma-separated list of directories to ignore in addition to the default hardcoded list including staging, vendor, and hidden dirs")
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{
@ -63,8 +52,6 @@ var (
"linux/arm64", "linux/ppc64le",
"linux/s390x", "darwin/386",
}
darwinPlatString = "darwin/386,darwin/amd64"
windowsPlatString = "windows/386,windows/amd64"
// directories we always ignore
standardIgnoreDirs = []string{
@ -88,168 +75,29 @@ var (
}
)
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{}
errors []string
}
func newAnalyzer(platform string) *analyzer {
ctx := build.Default
func newConfig(platform string) *packages.Config {
platSplit := strings.Split(platform, "/")
ctx.GOOS, ctx.GOARCH = platSplit[0], platSplit[1]
ctx.CgoEnabled = true
if *tags != "" {
tagsSplit := strings.Split(*tags, ",")
ctx.BuildTags = append(ctx.BuildTags, tagsSplit...)
}
// add selinux tag explicitly
ctx.BuildTags = append(ctx.BuildTags, "selinux")
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) {
a.errors = append(a.errors, err.Error())
if *serial {
fmt.Fprintf(os.Stderr, "%sERROR(%s) %s\n", logPrefix, a.platform, err)
}
a.failed = true
}
func (a *analyzer) dumpAndResetErrors() []string {
es := a.errors
a.errors = nil
return es
}
// 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()
if *skipTest && strings.HasSuffix(fpath, "_test.go") {
continue
}
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)
}
}
goos, goarch := platSplit[0], platSplit[1]
mode := packages.NeedName | packages.NeedFiles | packages.NeedTypes | packages.NeedSyntax | packages.NeedDeps | packages.NeedImports
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)
}
mode = mode | packages.NeedTypesInfo
}
env := append(os.Environ(),
"CGO_ENABLED=1",
fmt.Sprintf("GOOS=%s", goos),
fmt.Sprintf("GOARCH=%s", goarch))
tagstr := "selinux"
if *tags != "" {
tagstr = tagstr + "," + *tags
}
flags := []string{"-tags", tagstr}
return nil
return &packages.Config{
Mode: mode,
Env: env,
BuildFlags: flags,
Tests: !(*skipTest),
}
}
type collector struct {
@ -257,6 +105,27 @@ type collector struct {
ignoreDirs []string
}
func newCollector(ignoreDirs string) collector {
c := collector{
ignoreDirs: append([]string(nil), standardIgnoreDirs...),
}
if ignoreDirs != "" {
c.ignoreDirs = append(c.ignoreDirs, strings.Split(ignoreDirs, ",")...)
}
return c
}
func (c *collector) walk(roots []string) error {
for _, root := range roots {
err := filepath.Walk(root, c.handlePath)
if err != nil {
return err
}
}
sort.Strings(c.dirs)
return nil
}
// handlePath walks the filesystem recursively, collecting directories,
// ignoring some unneeded directories (hidden/vendored) that are handled
// specially later.
@ -265,63 +134,115 @@ func (c *collector) handlePath(path string, info os.FileInfo, err error) error {
return err
}
if info.IsDir() {
name := info.Name()
// Ignore hidden directories (.git, .cache, etc)
if len(path) > 1 && path[0] == '.' {
if (len(name) > 1 && (name[0] == '.' || name[0] == '_')) || name == "testdata" {
if *verbose {
fmt.Printf("DBG: skipping dir %s\n", path)
}
return filepath.SkipDir
}
for _, dir := range c.ignoreDirs {
if path == dir {
if *verbose {
fmt.Printf("DBG: ignoring dir %s\n", path)
}
return filepath.SkipDir
}
}
c.dirs = append(c.dirs, path)
// Make dirs into relative pkg names.
// NOTE: can't use filepath.Join because it elides the leading "./"
pkg := path
if !strings.HasPrefix(pkg, "./") {
pkg = "./" + pkg
}
c.dirs = append(c.dirs, pkg)
if *verbose {
fmt.Printf("DBG: added dir %s\n", path)
}
}
return nil
}
type analyzerResult struct {
platform string
dir string
errors []string
func (c *collector) verify(plat string) ([]string, error) {
errors := []packages.Error{}
start := time.Now()
config := newConfig(plat)
rootPkgs, err := packages.Load(config, c.dirs...)
if err != nil {
return nil, err
}
// Recursively import all deps and flatten to one list.
allMap := map[string]*packages.Package{}
for _, pkg := range rootPkgs {
if *verbose {
serialFprintf(os.Stdout, "pkg %q has %d GoFiles\n", pkg.PkgPath, len(pkg.GoFiles))
}
allMap[pkg.PkgPath] = pkg
if len(pkg.Imports) > 0 {
for _, imp := range pkg.Imports {
if *verbose {
serialFprintf(os.Stdout, "pkg %q imports %q\n", pkg.PkgPath, imp.PkgPath)
}
allMap[imp.PkgPath] = imp
}
}
}
keys := make([]string, 0, len(allMap))
for k := range allMap {
keys = append(keys, k)
}
sort.Strings(keys)
allList := make([]*packages.Package, 0, len(keys))
for _, k := range keys {
allList = append(allList, allMap[k])
}
for _, pkg := range allList {
if len(pkg.GoFiles) > 0 {
if len(pkg.Errors) > 0 {
errors = append(errors, pkg.Errors...)
}
}
if *defuses {
for id, obj := range pkg.TypesInfo.Defs {
serialFprintf(os.Stdout, "%s: %q defines %v\n",
pkg.Fset.Position(id.Pos()), id.Name, obj)
}
for id, obj := range pkg.TypesInfo.Uses {
serialFprintf(os.Stdout, "%s: %q uses %v\n",
pkg.Fset.Position(id.Pos()), id.Name, obj)
}
}
}
if *timings {
serialFprintf(os.Stdout, "%s took %.1fs\n", plat, time.Since(start).Seconds())
}
return dedup(errors), nil
}
func dedupeErrors(out io.Writer, results chan analyzerResult, nDirs, nPlatforms int) {
pkgRes := make(map[string][]analyzerResult)
for done := 0; done < nDirs; {
res := <-results
pkgRes[res.dir] = append(pkgRes[res.dir], res)
if len(pkgRes[res.dir]) != nPlatforms {
continue // expect more results for dir
func dedup(errors []packages.Error) []string {
ret := []string{}
m := map[string]bool{}
for _, e := range errors {
es := e.Error()
if !m[es] {
ret = append(ret, es)
m[es] = true
}
done++
// Collect list of platforms for each error
errPlats := map[string][]string{}
for _, res := range pkgRes[res.dir] {
for _, err := range res.errors {
errPlats[err] = append(errPlats[err], res.platform)
}
}
// Print each error (in the same order!) once.
for _, res := range pkgRes[res.dir] {
for _, err := range res.errors {
if errPlats[err] == nil {
continue // already printed
}
sort.Strings(errPlats[err])
plats := strings.Join(errPlats[err], ",")
if len(errPlats[err]) == len(crossPlatforms) {
plats = "all"
} else if plats == darwinPlatString {
plats = "darwin"
} else if plats == windowsPlatString {
plats = "windows"
}
fmt.Fprintf(out, "%sERROR(%s) %s\n", logPrefix, plats, err)
delete(errPlats, err)
}
}
delete(pkgRes, res.dir)
}
return ret
}
var outMu sync.Mutex
func serialFprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
outMu.Lock()
defer outMu.Unlock()
return fmt.Fprintf(w, format, a...)
}
func main() {
@ -336,97 +257,53 @@ func main() {
args = append(args, ".")
}
c := collector{
ignoreDirs: append([]string(nil), standardIgnoreDirs...),
}
if *ignoreDirs != "" {
c.ignoreDirs = append(c.ignoreDirs, strings.Split(*ignoreDirs, ",")...)
c := newCollector(*ignoreDirs)
if err := c.walk(args); err != nil {
log.Fatalf("Error walking: %v", err)
}
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[:]
plats := crossPlatforms[:]
if *platforms != "" {
ps = strings.Split(*platforms, ",")
plats = strings.Split(*platforms, ",")
} else if !*cross {
ps = ps[:1]
plats = plats[: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))
var results chan analyzerResult
if !*serial {
results = make(chan analyzerResult)
var failMu sync.Mutex
failed := false
for _, plat := range plats {
wg.Add(1)
go func() {
dedupeErrors(os.Stderr, results, len(c.dirs), len(ps))
wg.Done()
}()
}
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 results != nil {
results <- analyzerResult{p, dir, a.dumpAndResetErrors()}
fn := func(plat string) {
f := false
serialFprintf(os.Stdout, "type-checking %s\n", plat)
errors, err := c.verify(plat)
if err != nil {
serialFprintf(os.Stderr, "ERROR(%s): failed to verify: %v\n", plat, err)
} else if len(errors) > 0 {
for _, e := range errors {
// Special case CGo errors which may depend on headers we
// don't have.
if !strings.HasSuffix(e, "could not import C (no metadata for C)") {
f = true
serialFprintf(os.Stderr, "ERROR(%s): %s\n", plat, e)
}
}
}
if a.failed {
statuses[i] = 1
}
if *timings {
fmt.Printf("%s took %.1fs\n", p, time.Since(start).Seconds())
}
failMu.Lock()
failed = failed || f
failMu.Unlock()
wg.Done()
}
if *serial {
fn(i, p)
fn(plat)
} else {
go fn(i, p)
go fn(plat)
}
}
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)
}
if failed {
os.Exit(1)
}
}

View File

@ -17,131 +17,57 @@ limitations under the License.
package main
import (
"bytes"
"errors"
"fmt"
"go/ast"
"go/parser"
"io/ioutil"
"flag"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/tools/go/packages"
)
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")
// This exists because `go` is not always in the PATH when running CI.
var goBinary = flag.String("go", "", "path to a `go` binary")
func TestVerify(t *testing.T) {
// x/tools/packages is going to literally exec `go`, so it needs some
// setup.
setEnvVars()
tcs := []struct {
path string
expect int
}{
{"./testdata/good", 0},
{"./testdata/bad", 18},
}
for _, tc := range tcs {
c := newCollector("")
if err := c.walk([]string{tc.path}); err != nil {
t.Fatalf("error walking %s: %v", tc.path, err)
}
}`, map[string]string{"linux/amd64": "", "windows/amd64": "test.go:4:18: 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"}},
errs, err := c.verify("linux/amd64")
if err != nil {
t.Errorf("unexpected error: %v", err)
} else if len(errs) != tc.expect {
t.Errorf("Expected %d errors, got %d: %v", tc.expect, len(errs), errs)
}
}
}
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)
func setEnvVars() {
if *goBinary != "" {
newPath := filepath.Dir(*goBinary)
curPath := os.Getenv("PATH")
if curPath != "" {
newPath = newPath + ":" + curPath
}
err = ioutil.WriteFile(path, []byte(data), 0644)
if err != nil {
t.Fatal(err)
}
fmt.Println(path)
os.Setenv("PATH", newPath)
}
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)
}
}
}
}
if os.Getenv("HOME") == "" {
os.Setenv("HOME", "/tmp")
}
}
@ -159,37 +85,38 @@ func TestHandlePath(t *testing.T) {
}
}
func TestDedupeErrors(t *testing.T) {
func TestDedup(t *testing.T) {
testcases := []struct {
nPlatforms int
results []analyzerResult
expected string
}{
{1, []analyzerResult{}, ""},
{1, []analyzerResult{{"linux/arm", "test", nil}}, ""},
{1, []analyzerResult{
{"linux/arm", "test", []string{"a"}}},
"ERROR(linux/arm) a\n"},
{3, []analyzerResult{
{"linux/arm", "test", []string{"a"}},
{"windows/386", "test", []string{"b"}},
{"windows/amd64", "test", []string{"b", "c"}}},
"ERROR(linux/arm) a\n" +
"ERROR(windows) b\n" +
"ERROR(windows/amd64) c\n"},
}
for _, tc := range testcases {
out := &bytes.Buffer{}
results := make(chan analyzerResult, len(tc.results))
for _, res := range tc.results {
results <- res
}
close(results)
dedupeErrors(out, results, len(tc.results)/tc.nPlatforms, tc.nPlatforms)
outString := out.String()
if outString != tc.expected {
t.Errorf("dedupeErrors(%v) = '%s', expected '%s'",
tc.results, outString, tc.expected)
input []packages.Error
expected int
}{{
input: nil,
expected: 0,
}, {
input: []packages.Error{
{Pos: "file:7", Msg: "message", Kind: packages.ParseError},
},
expected: 1,
}, {
input: []packages.Error{
{Pos: "file:7", Msg: "message1", Kind: packages.ParseError},
{Pos: "file:8", Msg: "message2", Kind: packages.ParseError},
},
expected: 2,
}, {
input: []packages.Error{
{Pos: "file:7", Msg: "message1", Kind: packages.ParseError},
{Pos: "file:8", Msg: "message2", Kind: packages.ParseError},
{Pos: "file:7", Msg: "message1", Kind: packages.ParseError},
},
expected: 2,
}}
for i, tc := range testcases {
out := dedup(tc.input)
if len(out) != tc.expected {
t.Errorf("[%d] dedup(%v) = '%v', expected %d",
i, tc.input, out, tc.expected)
}
}
}

52
test/typecheck/testdata/bad/bad.go vendored Normal file
View File

@ -0,0 +1,52 @@
/*
Copyright 2020 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 "fmt"
var i int = int16(0)
var pi *int = new(int16)
var i16 int16 = int(0)
var pi16 *int16 = new(int)
var i32 int32 = int64(0)
var pi32 *int32 = new(int64)
var i64 int64 = int32(0)
var pi64 *int64 = new(int32)
var f32 float32 = float64(0.0)
var pf32 *float32 = new(float64)
var f64 float64 = float32(0.0)
var pf64 *float64 = new(float32)
var str string = false
var pstr *string = new(bool)
type struc struct {
i int
f float64
s string
}
var stru struc = &struc{}
var pstru *struc = struc{}
var sli []int = map[string]int{"zero": 0}
var ma map[string]int = []int{0}
func main() {
fmt.Println("hello, world!")
}

View File

@ -0,0 +1,25 @@
/*
Copyright 2020 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 "fmt"
var i int = int16(0)
func main() {
fmt.Println("hello, world!")
}

52
test/typecheck/testdata/good/good.go vendored Normal file
View File

@ -0,0 +1,52 @@
/*
Copyright 2020 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 "fmt"
var i int = int(0)
var pi *int = new(int)
var i16 int16 = int16(0)
var pi16 *int16 = new(int16)
var i32 int32 = int32(0)
var pi32 *int32 = new(int32)
var i64 int64 = int64(0)
var pi64 *int64 = new(int64)
var f32 float32 = float32(0.0)
var pf32 *float32 = new(float32)
var f64 float64 = float64(0.0)
var pf64 *float64 = new(float64)
var str string = "a string"
var pstr *string = new(string)
type struc struct {
i int
f float64
s string
}
var stru struc = struc{}
var pstru *struc = &struc{}
var sli []int = []int{0}
var ma map[string]int = map[string]int{"zero": 0}
func main() {
fmt.Println("hello, world!")
}

View File

@ -0,0 +1,25 @@
/*
Copyright 2020 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 "fmt"
var i int = int16(0)
func main() {
fmt.Println("hello, world!")
}