Import the go-sumtype tool.

Use the tools build pattern to ensure that go-sumtype is always available
for the build.

Signed-off-by: James Peach <jpeach@cloudflare.com>
This commit is contained in:
James Peach 2022-03-04 21:47:55 +11:00
parent 5431e0ca12
commit 33656a4c41
25 changed files with 3145 additions and 6 deletions

View File

@ -3,6 +3,8 @@ MSI_VERSION := $(shell git tag -l --sort=v:refname | grep "w" | tail -1 | cut
#MSI_VERSION expects the format of the tag to be: (wX.X.X). Starts with the w character to not break cfsetup.
#e.g. w3.0.1 or w4.2.10. It trims off the w character when creating the MSI.
GO_SUMTYPE := go run -mod=vendor github.com/sudarshan-reddy/go-sumtype
ifeq ($(ORIGINAL_NAME), true)
# Used for builds that want FIPS compilation but want the artifacts generated to still have the original name.
BINARY_NAME := cloudflared
@ -271,12 +273,8 @@ quic-deps:
.PHONY: vet
vet:
go vet -mod=vendor ./...
# go get github.com/sudarshan-reddy/go-sumtype (don't do this in build directory or this will cause vendor issues)
# Note: If you have github.com/BurntSushi/go-sumtype then you might have to use the repo above instead
# for now because it uses an older version of golang.org/x/tools.
which go-sumtype
go-sumtype $$(go list -mod=vendor ./...)
$(GO_SUMTYPE) $$(go list -mod=vendor ./...)
.PHONY: goimports
goimports:
for d in $$(go list -mod=readonly -f '{{.Dir}}' -a ./... | fgrep -v tunnelrpc) ; do goimports -format-only -local github.com/cloudflare/cloudflared -w $$d ; done
for d in $$(go list -mod=readonly -f '{{.Dir}}' -a ./... | fgrep -v tunnelrpc) ; do goimports -format-only -local github.com/cloudflare/cloudflared -w $$d ; done

1
go.mod
View File

@ -41,6 +41,7 @@ require (
github.com/rs/zerolog v1.20.0
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/stretchr/testify v1.7.0
github.com/sudarshan-reddy/go-sumtype v0.0.0-20210827105221-82eca7e5abb1
github.com/urfave/cli/v2 v2.3.0
go.uber.org/automaxprocs v1.4.0
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519

2
go.sum
View File

@ -562,6 +562,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/sudarshan-reddy/go-sumtype v0.0.0-20210827105221-82eca7e5abb1 h1:PfYPROl/6Zyspmiebusz0s/Bsr/Ju/U9TFwkPqzdL3U=
github.com/sudarshan-reddy/go-sumtype v0.0.0-20210827105221-82eca7e5abb1/go.mod h1:g+9pWOY/CQta4Y1Z7oTU9BcBGs2awNSLJ4aBRyvG784=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=

7
tools/tools.go Normal file
View File

@ -0,0 +1,7 @@
//go:build tools
package tools
import (
_ "github.com/sudarshan-reddy/go-sumtype"
)

View File

@ -0,0 +1 @@
language: go

3
vendor/github.com/sudarshan-reddy/go-sumtype/COPYING generated vendored Normal file
View File

@ -0,0 +1,3 @@
This project is dual-licensed under the Unlicense and MIT licenses.
You may use this code under the terms of either license.

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Andrew Gallant
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

124
vendor/github.com/sudarshan-reddy/go-sumtype/README.md generated vendored Normal file
View File

@ -0,0 +1,124 @@
go-sumtype
==========
A simple utility for running exhaustiveness checks on type switch statements.
Exhaustiveness checks are only run on interfaces that are declared to be
"sum types."
[![Linux build status](https://api.travis-ci.org/BurntSushi/go-sumtype.png)](https://travis-ci.org/BurntSushi/go-sumtype)
Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org).
This work was inspired by our code at
[Diffeo](https://diffeo.com).
### Installation
```go
$ go get github.com/BurntSushi/go-sumtype
```
For usage info, just run the command:
```
$ go-sumtype
```
Typical usage might look like this:
```
$ go-sumtype $(go list ./... | grep -v vendor)
```
### Usage
go-sumtype takes a list of Go package paths or files and looks for sum type
declarations in each package/file provided. Exhaustiveness checks are then
performed for each use of a declared sum type in a type switch statement.
Namely, `go-sumtype` will report an error for any type switch statement that
either lacks a `default` clause or does not account for all possible variants.
Declarations are provided in comments like so:
```
//go-sumtype:decl MySumType
```
`MySumType` must satisfy the following:
1. It is a type defined in the same package.
2. It is an interface.
3. It is *sealed*. That is, part of its interface definition contains an
unexported method.
`go-sumtype` will produce an error if any of the above is not true.
For valid declarations, `go-sumtype` will look for all occurrences in which a
value of type `MySumType` participates in a type switch statement. In those
occurrences, it will attempt to detect whether the type switch is exhaustive
or not. If it's not, `go-sumtype` will report an error. For example, running
`go-sumtype` on this source file:
```go
package main
//go-sumtype:decl MySumType
type MySumType interface {
sealed()
}
type VariantA struct{}
func (*VariantA) sealed() {}
type VariantB struct{}
func (*VariantB) sealed() {}
func main() {
switch MySumType(nil).(type) {
case *VariantA:
}
}
```
produces the following:
```
$ go-sumtype mysumtype.go
mysumtype.go:18:2: exhaustiveness check failed for sum type 'MySumType': missing cases for VariantB
```
Adding either a `default` clause or a clause to handle `*VariantB` will cause
exhaustive checks to pass.
As a special case, if the type switch statement contains a `default` clause
that always panics, then exhaustiveness checks are still performed.
### Details and motivation
Sum types are otherwise known as discriminated unions. That is, a sum type is
a finite set of disjoint values. In type systems that support sum types, the
language will guarantee that if one has a sum type `T`, then its value must
be one of its variants.
Go's type system does not support sum types. A typical proxy for representing
sum types in Go is to use an interface with an unexported method and define
each variant of the sum type in the same package to satisfy said interface.
This guarantees that the set of types that satisfy the interface is closed
at compile time. Performing case analysis on these types is then done with
a type switch statement, e.g., `switch x.(type) { ... }`. Each clause of the
type switch corresponds to a *variant* of the sum type. The downside of this
approach is that Go's type system is not aware of the set of variants, so it
cannot tell you whether case analysis over a sum type is complete or not.
The `go-sumtype` command recognizes this pattern, but it needs a small amount
of help to recognize which interfaces should be treated as sum types, which
is why the `//go-sumtype:decl ...` annotation is required. `go-sumtype` will
figure out all of the variants of a sum type by finding the set of types
defined in the same package that satisfy the interface specified by the
declaration.
The `go-sumtype` command will prove its worth when you need to add a variant
to an existing sum type. Running `go-sumtype` will tell you immediately which
case analyses need to be updated to account for the new variant.

24
vendor/github.com/sudarshan-reddy/go-sumtype/UNLICENSE generated vendored Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

183
vendor/github.com/sudarshan-reddy/go-sumtype/check.go generated vendored Normal file
View File

@ -0,0 +1,183 @@
package main
import (
"fmt"
"go/ast"
"go/token"
"go/types"
"sort"
"strings"
"golang.org/x/tools/go/packages"
)
// inexhaustiveError is returned from check for each occurrence of inexhaustive
// case analysis in a Go type switch statement.
type inexhaustiveError struct {
Pos token.Position
Def sumTypeDef
Missing []types.Object
}
func (e inexhaustiveError) Error() string {
return fmt.Sprintf(
"%s: exhaustiveness check failed for sum type '%s': missing cases for %s",
e.Pos, e.Def.Decl.TypeName, strings.Join(e.Names(), ", "))
}
// Names returns a sorted list of names corresponding to the missing variant
// cases.
func (e inexhaustiveError) Names() []string {
var list []string
for _, o := range e.Missing {
list = append(list, o.Name())
}
sort.Sort(sort.StringSlice(list))
return list
}
// check does exhaustiveness checking for the given sum type definitions in the
// given package. Every instance of inexhaustive case analysis is returned.
func check(pkg *packages.Package, defs []sumTypeDef) []error {
var errs []error
for _, astfile := range pkg.Syntax {
ast.Inspect(astfile, func(n ast.Node) bool {
swtch, ok := n.(*ast.TypeSwitchStmt)
if !ok {
return true
}
if err := checkSwitch(pkg, defs, swtch); err != nil {
errs = append(errs, err)
}
return true
})
}
return errs
}
// checkSwitch performs an exhaustiveness check on the given type switch
// statement. If the type switch is used on a sum type and does not cover
// all variants of that sum type, then an error is returned indicating which
// variants were missed.
//
// Note that if the type switch contains a non-panicing default case, then
// exhaustiveness checks are disabled.
func checkSwitch(
pkg *packages.Package,
defs []sumTypeDef,
swtch *ast.TypeSwitchStmt,
) error {
def, missing := missingVariantsInSwitch(pkg, defs, swtch)
if len(missing) > 0 {
return inexhaustiveError{
Pos: pkg.Fset.Position(swtch.Pos()),
Def: *def,
Missing: missing,
}
}
return nil
}
// missingVariantsInSwitch returns a list of missing variants corresponding to
// the given switch statement. The corresponding sum type definition is also
// returned. (If no sum type definition could be found, then no exhaustiveness
// checks are performed, and therefore, no missing variants are returned.)
func missingVariantsInSwitch(
pkg *packages.Package,
defs []sumTypeDef,
swtch *ast.TypeSwitchStmt,
) (*sumTypeDef, []types.Object) {
asserted := findTypeAssertExpr(swtch)
ty := pkg.TypesInfo.TypeOf(asserted)
def := findDef(defs, ty)
if def == nil {
// We couldn't find a corresponding sum type, so there's
// nothing we can do to check it.
return nil, nil
}
variantExprs, hasDefault := switchVariants(swtch)
if hasDefault && !defaultClauseAlwaysPanics(swtch) {
// A catch-all case defeats all exhaustiveness checks.
return def, nil
}
var variantTypes []types.Type
for _, expr := range variantExprs {
variantTypes = append(variantTypes, pkg.TypesInfo.TypeOf(expr))
}
return def, def.missing(variantTypes)
}
// switchVariants returns all case expressions found in a type switch. This
// includes expressions from cases that have a list of expressions.
func switchVariants(swtch *ast.TypeSwitchStmt) (exprs []ast.Expr, hasDefault bool) {
for _, stmt := range swtch.Body.List {
clause := stmt.(*ast.CaseClause)
if clause.List == nil {
hasDefault = true
} else {
exprs = append(exprs, clause.List...)
}
}
return
}
// defaultClauseAlwaysPanics returns true if the given switch statement has a
// default clause that always panics. Note that this is done on a best-effort
// basis. While there will never be any false positives, there may be false
// negatives.
//
// If the given switch statement has no default clause, then this function
// panics.
func defaultClauseAlwaysPanics(swtch *ast.TypeSwitchStmt) bool {
var clause *ast.CaseClause
for _, stmt := range swtch.Body.List {
c := stmt.(*ast.CaseClause)
if c.List == nil {
clause = c
break
}
}
if clause == nil {
panic("switch statement has no default clause")
}
if len(clause.Body) != 1 {
return false
}
exprStmt, ok := clause.Body[0].(*ast.ExprStmt)
if !ok {
return false
}
callExpr, ok := exprStmt.X.(*ast.CallExpr)
if !ok {
return false
}
fun, ok := callExpr.Fun.(*ast.Ident)
if !ok {
return false
}
return fun.Name == "panic"
}
// findTypeAssertExpr extracts the expression that is being type asserted from a
// type swtich statement.
func findTypeAssertExpr(swtch *ast.TypeSwitchStmt) ast.Expr {
var expr ast.Expr
if assign, ok := swtch.Assign.(*ast.AssignStmt); ok {
expr = assign.Rhs[0]
} else {
expr = swtch.Assign.(*ast.ExprStmt).X
}
return expr.(*ast.TypeAssertExpr).X
}
// findDef returns the sum type definition corresponding to the given type. If
// no such sum type definition exists, then nil is returned.
func findDef(defs []sumTypeDef, needle types.Type) *sumTypeDef {
for i := range defs {
def := &defs[i]
if types.Identical(needle.Underlying(), def.Ty) {
return def
}
}
return nil
}

110
vendor/github.com/sudarshan-reddy/go-sumtype/decl.go generated vendored Normal file
View File

@ -0,0 +1,110 @@
package main
import (
"bufio"
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"golang.org/x/tools/go/packages"
)
// sumTypeDecl is a declaration of a sum type in a Go source file.
type sumTypeDecl struct {
// The package path that contains this decl.
Package *packages.Package
// The type named by this decl.
TypeName string
// The file path where this declaration was found.
Path string
// The line number where this declaration was found.
Line int
}
// Location returns a short string describing where this declaration was found.
func (d sumTypeDecl) Location() string {
return fmt.Sprintf("%s:%d", d.Path, d.Line)
}
// findSumTypeDecls searches every package given for sum type declarations of
// the form `go-sumtype:decl ...`.
func findSumTypeDecls(pkgs []*packages.Package) ([]sumTypeDecl, error) {
var decls []sumTypeDecl
for _, pkg := range pkgs {
for _, filename := range pkg.CompiledGoFiles {
if filepath.Base(filename) == "C" {
// ignore (fake?) cgo files
continue
}
fileDecls, err := sumTypeDeclSearch(filename)
if err != nil {
return nil, err
}
for i := range fileDecls {
fileDecls[i].Package = pkg
}
decls = append(decls, fileDecls...)
}
}
return decls, nil
}
// sumTypeDeclSearch searches the given file for sum type declarations of the
// form `go-sumtype:decl ...`.
func sumTypeDeclSearch(path string) ([]sumTypeDecl, error) {
var decls []sumTypeDecl
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
lineNum := 0
scanner := bufio.NewScanner(f)
for scanner.Scan() {
lineNum++
line := scanner.Bytes()
if !isSumTypeDecl(line) {
continue
}
ty := parseSumTypeDecl(line)
if len(ty) == 0 {
continue
}
decls = append(decls, sumTypeDecl{
TypeName: ty,
Path: path,
Line: lineNum,
})
}
if err := scanner.Err(); err != nil {
// A scanner can puke if it hits a line that is too long.
// We assume such files won't contain any future decls and
// otherwise move on.
log.Printf("scan error reading '%s': %s", path, err)
}
return decls, nil
}
var reParseSumTypeDecl = regexp.MustCompile(`^//go-sumtype:decl\s+(\S+)\s*$`)
// parseSumTypeDecl parses the type name out of a sum type decl.
//
// If no such decl could be found, then this returns an empty string.
func parseSumTypeDecl(line []byte) string {
caps := reParseSumTypeDecl.FindSubmatch(line)
if len(caps) < 2 {
return ""
}
return string(caps[1])
}
// isSumTypeDecl returns true if and only if this line in a Go source file
// is a sum type decl.
func isSumTypeDecl(line []byte) bool {
variant1, variant2 := []byte("//go-sumtype:decl "), []byte("//go-sumtype:decl\t")
return bytes.HasPrefix(line, variant1) || bytes.HasPrefix(line, variant2)
}

156
vendor/github.com/sudarshan-reddy/go-sumtype/def.go generated vendored Normal file
View File

@ -0,0 +1,156 @@
package main
import (
"fmt"
"go/ast"
"go/types"
)
// unsealedError corresponds to a declared sum type whose interface is not
// sealed. A sealed interface requires at least one unexported method.
type unsealedError struct {
Decl sumTypeDecl
}
func (e unsealedError) Error() string {
return fmt.Sprintf(
"%s: interface '%s' is not sealed "+
"(sealing requires at least one unexported method)",
e.Decl.Location(), e.Decl.TypeName)
}
// notFoundError corresponds to a declared sum type whose type definition
// could not be found in the same Go package.
type notFoundError struct {
Decl sumTypeDecl
}
func (e notFoundError) Error() string {
return fmt.Sprintf("%s: type '%s' is not defined", e.Decl.Location(), e.Decl.TypeName)
}
// notInterfaceError corresponds to a declared sum type that does not
// correspond to an interface.
type notInterfaceError struct {
Decl sumTypeDecl
}
func (e notInterfaceError) Error() string {
return fmt.Sprintf("%s: type '%s' is not an interface", e.Decl.Location(), e.Decl.TypeName)
}
// sumTypeDef corresponds to the definition of a Go interface that is
// interpreted as a sum type. Its variants are determined by finding all types
// that implement said interface in the same package.
type sumTypeDef struct {
Decl sumTypeDecl
Ty *types.Interface
Variants []types.Object
}
// findSumTypeDefs attempts to find a Go type definition for each of the given
// sum type declarations. If no such sum type definition could be found for
// any of the given declarations, then an error is returned.
func findSumTypeDefs(decls []sumTypeDecl) ([]sumTypeDef, []error) {
var defs []sumTypeDef
var errs []error
for _, decl := range decls {
def, err := newSumTypeDef(decl.Package.Types, decl)
if err != nil {
errs = append(errs, err)
continue
}
if def == nil {
errs = append(errs, notFoundError{decl})
continue
}
defs = append(defs, *def)
}
return defs, errs
}
// newSumTypeDef attempts to extract a sum type definition from a single
// package. If no such type corresponds to the given decl, then this function
// returns a nil def and a nil error.
//
// If the decl corresponds to a type that isn't an interface containing at
// least one unexported method, then this returns an error.
func newSumTypeDef(pkg *types.Package, decl sumTypeDecl) (*sumTypeDef, error) {
obj := pkg.Scope().Lookup(decl.TypeName)
if obj == nil {
return nil, nil
}
iface, ok := obj.Type().Underlying().(*types.Interface)
if !ok {
return nil, notInterfaceError{decl}
}
hasUnexported := false
for i := 0; i < iface.NumMethods(); i++ {
if !iface.Method(i).Exported() {
hasUnexported = true
break
}
}
if !hasUnexported {
return nil, unsealedError{decl}
}
def := &sumTypeDef{
Decl: decl,
Ty: iface,
}
for _, name := range pkg.Scope().Names() {
obj, ok := pkg.Scope().Lookup(name).(*types.TypeName)
if !ok {
continue
}
ty := obj.Type()
if types.Identical(ty.Underlying(), iface) {
continue
}
if types.Implements(ty, iface) || types.Implements(types.NewPointer(ty), iface) {
def.Variants = append(def.Variants, obj)
}
}
return def, nil
}
func (def *sumTypeDef) String() string {
return def.Decl.TypeName
}
// missing returns a list of variants in this sum type that are not in the
// given list of types.
func (def *sumTypeDef) missing(tys []types.Type) []types.Object {
// TODO(ag): This is O(n^2). Fix that. /shrug
var missing []types.Object
for _, v := range def.Variants {
found := false
varty := indirect(v.Type())
for _, ty := range tys {
ty = indirect(ty)
if types.Identical(varty, ty) {
found = true
}
}
if !found {
missing = append(missing, v)
}
}
return missing
}
// indirect dereferences through an arbitrary number of pointer types.
func indirect(ty types.Type) types.Type {
if ty, ok := ty.(*types.Pointer); ok {
return indirect(ty.Elem())
}
return ty
}
func pkgFiles(pkg *ast.Package) []*ast.File {
var files []*ast.File
for _, file := range pkg.Files {
files = append(files, file)
}
return files
}

57
vendor/github.com/sudarshan-reddy/go-sumtype/doc.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
/*
go-sumtype takes a list of Go package paths or files and looks for sum type
declarations in each package/file provided. Exhaustiveness checks are then
performed for each use of a declared sum type in a type switch statement.
Namely, go-sumtype will report an error for any type switch statement that
either lacks a default clause or does not account for all possible variants.
Declarations are provided in comments like so:
//go-sumtype:decl MySumType
MySumType must satisfy the following:
1. It is a type defined in the same package.
2. It is an interface.
3. It is *sealed*. That is, part of its interface definition contains an
unexported method.
go-sumtype will produce an error if any of the above is not true.
For valid declarations, go-sumtype will look for all occurrences in which a
value of type MySumType participates in a type switch statement. In those
occurrences, it will attempt to detect whether the type switch is exhaustive
or not. If it's not, go-sumtype will report an error. For example:
$ cat mysumtype.go
package main
//go-sumtype:decl MySumType
type MySumType interface {
sealed()
}
type VariantA struct{}
func (a *VariantA) sealed() {}
type VariantB struct{}
func (b *VariantB) sealed() {}
func main() {
switch MySumType(nil).(type) {
case *VariantA:
}
}
$ go-sumtype mysumtype.go
mysumtype.go:18:2: exhaustiveness check failed for sum type 'MySumType': missing cases for VariantB
Adding either a default clause or a clause to handle *VariantB will cause
exhaustive checks to pass.
As a special case, if the type switch statement contains a default clause
that always panics, then exhaustiveness checks are still performed.
*/
package main

74
vendor/github.com/sudarshan-reddy/go-sumtype/main.go generated vendored Normal file
View File

@ -0,0 +1,74 @@
package main
import (
"log"
"os"
"strings"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/packages"
)
func main() {
log.SetFlags(0)
if len(os.Args) < 2 {
// TODO: Switch this to use golang.org/x/tools/go/packages.
log.Fatalf("Usage: go-sumtype <args>\n%s", loader.FromArgsUsage)
}
args := os.Args[1:]
pkgs, err := tycheckAll(args)
if err != nil {
log.Fatal(err)
}
if errs := run(pkgs); len(errs) > 0 {
var list []string
for _, err := range errs {
list = append(list, err.Error())
}
log.Fatal(strings.Join(list, "\n"))
}
}
func run(pkgs []*packages.Package) []error {
var errs []error
decls, err := findSumTypeDecls(pkgs)
if err != nil {
return []error{err}
}
defs, defErrs := findSumTypeDefs(decls)
errs = append(errs, defErrs...)
if len(defs) == 0 {
return errs
}
for _, pkg := range pkgs {
if pkgErrs := check(pkg, defs); pkgErrs != nil {
errs = append(errs, pkgErrs...)
}
}
return errs
}
func tycheckAll(args []string) ([]*packages.Package, error) {
conf := &packages.Config{
Mode: packages.LoadSyntax,
// Unfortunately, it appears including the test packages in
// this lint makes it difficult to do exhaustiveness checking.
// Namely, it appears that compiling the test version of a
// package introduces distinct types from the normal version
// of the package, which will always result in inexhaustive
// errors whenever a package both defines a sum type and has
// tests. (Specifically, using `package name`. Using `package
// name_test` is OK.)
//
// It's not clear what the best way to fix this is. :-(
Tests: false,
}
pkgs, err := packages.Load(conf, args...)
if err != nil {
return nil, err
}
return pkgs, nil
}

198
vendor/golang.org/x/tools/go/buildutil/allpackages.go generated vendored Normal file
View File

@ -0,0 +1,198 @@
// 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 buildutil provides utilities related to the go/build
// package in the standard library.
//
// All I/O is done via the build.Context file system interface, which must
// be concurrency-safe.
package buildutil // import "golang.org/x/tools/go/buildutil"
import (
"go/build"
"os"
"path/filepath"
"sort"
"strings"
"sync"
)
// AllPackages returns the package path of each Go package in any source
// directory of the specified build context (e.g. $GOROOT or an element
// of $GOPATH). Errors are ignored. The results are sorted.
// All package paths are canonical, and thus may contain "/vendor/".
//
// The result may include import paths for directories that contain no
// *.go files, such as "archive" (in $GOROOT/src).
//
// All I/O is done via the build.Context file system interface,
// which must be concurrency-safe.
//
func AllPackages(ctxt *build.Context) []string {
var list []string
ForEachPackage(ctxt, func(pkg string, _ error) {
list = append(list, pkg)
})
sort.Strings(list)
return list
}
// ForEachPackage calls the found function with the package path of
// each Go package it finds in any source directory of the specified
// build context (e.g. $GOROOT or an element of $GOPATH).
// All package paths are canonical, and thus may contain "/vendor/".
//
// If the package directory exists but could not be read, the second
// argument to the found function provides the error.
//
// All I/O is done via the build.Context file system interface,
// which must be concurrency-safe.
//
func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) {
ch := make(chan item)
var wg sync.WaitGroup
for _, root := range ctxt.SrcDirs() {
root := root
wg.Add(1)
go func() {
allPackages(ctxt, root, ch)
wg.Done()
}()
}
go func() {
wg.Wait()
close(ch)
}()
// All calls to found occur in the caller's goroutine.
for i := range ch {
found(i.importPath, i.err)
}
}
type item struct {
importPath string
err error // (optional)
}
// We use a process-wide counting semaphore to limit
// the number of parallel calls to ReadDir.
var ioLimit = make(chan bool, 20)
func allPackages(ctxt *build.Context, root string, ch chan<- item) {
root = filepath.Clean(root) + string(os.PathSeparator)
var wg sync.WaitGroup
var walkDir func(dir string)
walkDir = func(dir string) {
// Avoid .foo, _foo, and testdata directory trees.
base := filepath.Base(dir)
if base == "" || base[0] == '.' || base[0] == '_' || base == "testdata" {
return
}
pkg := filepath.ToSlash(strings.TrimPrefix(dir, root))
// Prune search if we encounter any of these import paths.
switch pkg {
case "builtin":
return
}
ioLimit <- true
files, err := ReadDir(ctxt, dir)
<-ioLimit
if pkg != "" || err != nil {
ch <- item{pkg, err}
}
for _, fi := range files {
fi := fi
if fi.IsDir() {
wg.Add(1)
go func() {
walkDir(filepath.Join(dir, fi.Name()))
wg.Done()
}()
}
}
}
walkDir(root)
wg.Wait()
}
// ExpandPatterns returns the set of packages matched by patterns,
// which may have the following forms:
//
// golang.org/x/tools/cmd/guru # a single package
// golang.org/x/tools/... # all packages beneath dir
// ... # the entire workspace.
//
// Order is significant: a pattern preceded by '-' removes matching
// packages from the set. For example, these patterns match all encoding
// packages except encoding/xml:
//
// encoding/... -encoding/xml
//
// A trailing slash in a pattern is ignored. (Path components of Go
// package names are separated by slash, not the platform's path separator.)
//
func ExpandPatterns(ctxt *build.Context, patterns []string) map[string]bool {
// TODO(adonovan): support other features of 'go list':
// - "std"/"cmd"/"all" meta-packages
// - "..." not at the end of a pattern
// - relative patterns using "./" or "../" prefix
pkgs := make(map[string]bool)
doPkg := func(pkg string, neg bool) {
if neg {
delete(pkgs, pkg)
} else {
pkgs[pkg] = true
}
}
// Scan entire workspace if wildcards are present.
// TODO(adonovan): opt: scan only the necessary subtrees of the workspace.
var all []string
for _, arg := range patterns {
if strings.HasSuffix(arg, "...") {
all = AllPackages(ctxt)
break
}
}
for _, arg := range patterns {
if arg == "" {
continue
}
neg := arg[0] == '-'
if neg {
arg = arg[1:]
}
if arg == "..." {
// ... matches all packages
for _, pkg := range all {
doPkg(pkg, neg)
}
} else if dir := strings.TrimSuffix(arg, "/..."); dir != arg {
// dir/... matches all packages beneath dir
for _, pkg := range all {
if strings.HasPrefix(pkg, dir) &&
(len(pkg) == len(dir) || pkg[len(dir)] == '/') {
doPkg(pkg, neg)
}
}
} else {
// single package
doPkg(strings.TrimSuffix(arg, "/"), neg)
}
}
return pkgs
}

113
vendor/golang.org/x/tools/go/buildutil/fakecontext.go generated vendored Normal file
View File

@ -0,0 +1,113 @@
// Copyright 2015 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 buildutil
import (
"fmt"
"go/build"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"sort"
"strings"
"time"
)
// FakeContext returns a build.Context for the fake file tree specified
// by pkgs, which maps package import paths to a mapping from file base
// names to contents.
//
// The fake Context has a GOROOT of "/go" and no GOPATH, and overrides
// the necessary file access methods to read from memory instead of the
// real file system.
//
// Unlike a real file tree, the fake one has only two levels---packages
// and files---so ReadDir("/go/src/") returns all packages under
// /go/src/ including, for instance, "math" and "math/big".
// ReadDir("/go/src/math/big") would return all the files in the
// "math/big" package.
//
func FakeContext(pkgs map[string]map[string]string) *build.Context {
clean := func(filename string) string {
f := path.Clean(filepath.ToSlash(filename))
// Removing "/go/src" while respecting segment
// boundaries has this unfortunate corner case:
if f == "/go/src" {
return ""
}
return strings.TrimPrefix(f, "/go/src/")
}
ctxt := build.Default // copy
ctxt.GOROOT = "/go"
ctxt.GOPATH = ""
ctxt.Compiler = "gc"
ctxt.IsDir = func(dir string) bool {
dir = clean(dir)
if dir == "" {
return true // needed by (*build.Context).SrcDirs
}
return pkgs[dir] != nil
}
ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) {
dir = clean(dir)
var fis []os.FileInfo
if dir == "" {
// enumerate packages
for importPath := range pkgs {
fis = append(fis, fakeDirInfo(importPath))
}
} else {
// enumerate files of package
for basename := range pkgs[dir] {
fis = append(fis, fakeFileInfo(basename))
}
}
sort.Sort(byName(fis))
return fis, nil
}
ctxt.OpenFile = func(filename string) (io.ReadCloser, error) {
filename = clean(filename)
dir, base := path.Split(filename)
content, ok := pkgs[path.Clean(dir)][base]
if !ok {
return nil, fmt.Errorf("file not found: %s", filename)
}
return ioutil.NopCloser(strings.NewReader(content)), nil
}
ctxt.IsAbsPath = func(path string) bool {
path = filepath.ToSlash(path)
// Don't rely on the default (filepath.Path) since on
// Windows, it reports virtual paths as non-absolute.
return strings.HasPrefix(path, "/")
}
return &ctxt
}
type byName []os.FileInfo
func (s byName) Len() int { return len(s) }
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
type fakeFileInfo string
func (fi fakeFileInfo) Name() string { return string(fi) }
func (fakeFileInfo) Sys() interface{} { return nil }
func (fakeFileInfo) ModTime() time.Time { return time.Time{} }
func (fakeFileInfo) IsDir() bool { return false }
func (fakeFileInfo) Size() int64 { return 0 }
func (fakeFileInfo) Mode() os.FileMode { return 0644 }
type fakeDirInfo string
func (fd fakeDirInfo) Name() string { return string(fd) }
func (fakeDirInfo) Sys() interface{} { return nil }
func (fakeDirInfo) ModTime() time.Time { return time.Time{} }
func (fakeDirInfo) IsDir() bool { return true }
func (fakeDirInfo) Size() int64 { return 0 }
func (fakeDirInfo) Mode() os.FileMode { return 0755 }

103
vendor/golang.org/x/tools/go/buildutil/overlay.go generated vendored Normal file
View File

@ -0,0 +1,103 @@
// Copyright 2016 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 buildutil
import (
"bufio"
"bytes"
"fmt"
"go/build"
"io"
"io/ioutil"
"path/filepath"
"strconv"
"strings"
)
// OverlayContext overlays a build.Context with additional files from
// a map. Files in the map take precedence over other files.
//
// In addition to plain string comparison, two file names are
// considered equal if their base names match and their directory
// components point at the same directory on the file system. That is,
// symbolic links are followed for directories, but not files.
//
// A common use case for OverlayContext is to allow editors to pass in
// a set of unsaved, modified files.
//
// Currently, only the Context.OpenFile function will respect the
// overlay. This may change in the future.
func OverlayContext(orig *build.Context, overlay map[string][]byte) *build.Context {
// TODO(dominikh): Implement IsDir, HasSubdir and ReadDir
rc := func(data []byte) (io.ReadCloser, error) {
return ioutil.NopCloser(bytes.NewBuffer(data)), nil
}
copy := *orig // make a copy
ctxt := &copy
ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
// Fast path: names match exactly.
if content, ok := overlay[path]; ok {
return rc(content)
}
// Slow path: check for same file under a different
// alias, perhaps due to a symbolic link.
for filename, content := range overlay {
if sameFile(path, filename) {
return rc(content)
}
}
return OpenFile(orig, path)
}
return ctxt
}
// ParseOverlayArchive parses an archive containing Go files and their
// contents. The result is intended to be used with OverlayContext.
//
//
// Archive format
//
// The archive consists of a series of files. Each file consists of a
// name, a decimal file size and the file contents, separated by
// newlines. No newline follows after the file contents.
func ParseOverlayArchive(archive io.Reader) (map[string][]byte, error) {
overlay := make(map[string][]byte)
r := bufio.NewReader(archive)
for {
// Read file name.
filename, err := r.ReadString('\n')
if err != nil {
if err == io.EOF {
break // OK
}
return nil, fmt.Errorf("reading archive file name: %v", err)
}
filename = filepath.Clean(strings.TrimSpace(filename))
// Read file size.
sz, err := r.ReadString('\n')
if err != nil {
return nil, fmt.Errorf("reading size of archive file %s: %v", filename, err)
}
sz = strings.TrimSpace(sz)
size, err := strconv.ParseUint(sz, 10, 32)
if err != nil {
return nil, fmt.Errorf("parsing size of archive file %s: %v", filename, err)
}
// Read file content.
content := make([]byte, size)
if _, err := io.ReadFull(r, content); err != nil {
return nil, fmt.Errorf("reading archive file %s: %v", filename, err)
}
overlay[filename] = content
}
return overlay, nil
}

79
vendor/golang.org/x/tools/go/buildutil/tags.go generated vendored Normal file
View File

@ -0,0 +1,79 @@
// Copyright 2015 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 buildutil
// This logic was copied from stringsFlag from $GOROOT/src/cmd/go/build.go.
import "fmt"
const TagsFlagDoc = "a list of `build tags` to consider satisfied during the build. " +
"For more information about build tags, see the description of " +
"build constraints in the documentation for the go/build package"
// TagsFlag is an implementation of the flag.Value and flag.Getter interfaces that parses
// a flag value in the same manner as go build's -tags flag and
// populates a []string slice.
//
// See $GOROOT/src/go/build/doc.go for description of build tags.
// See $GOROOT/src/cmd/go/doc.go for description of 'go build -tags' flag.
//
// Example:
// flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
type TagsFlag []string
func (v *TagsFlag) Set(s string) error {
var err error
*v, err = splitQuotedFields(s)
if *v == nil {
*v = []string{}
}
return err
}
func (v *TagsFlag) Get() interface{} { return *v }
func splitQuotedFields(s string) ([]string, error) {
// Split fields allowing '' or "" around elements.
// Quotes further inside the string do not count.
var f []string
for len(s) > 0 {
for len(s) > 0 && isSpaceByte(s[0]) {
s = s[1:]
}
if len(s) == 0 {
break
}
// Accepted quoted string. No unescaping inside.
if s[0] == '"' || s[0] == '\'' {
quote := s[0]
s = s[1:]
i := 0
for i < len(s) && s[i] != quote {
i++
}
if i >= len(s) {
return nil, fmt.Errorf("unterminated %c string", quote)
}
f = append(f, s[:i])
s = s[i+1:]
continue
}
i := 0
for i < len(s) && !isSpaceByte(s[i]) {
i++
}
f = append(f, s[:i])
s = s[i:]
}
return f, nil
}
func (v *TagsFlag) String() string {
return "<tagsFlag>"
}
func isSpaceByte(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}

212
vendor/golang.org/x/tools/go/buildutil/util.go generated vendored Normal file
View File

@ -0,0 +1,212 @@
// 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 buildutil
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
)
// ParseFile behaves like parser.ParseFile,
// but uses the build context's file system interface, if any.
//
// If file is not absolute (as defined by IsAbsPath), the (dir, file)
// components are joined using JoinPath; dir must be absolute.
//
// The displayPath function, if provided, is used to transform the
// filename that will be attached to the ASTs.
//
// TODO(adonovan): call this from go/loader.parseFiles when the tree thaws.
//
func ParseFile(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, file string, mode parser.Mode) (*ast.File, error) {
if !IsAbsPath(ctxt, file) {
file = JoinPath(ctxt, dir, file)
}
rd, err := OpenFile(ctxt, file)
if err != nil {
return nil, err
}
defer rd.Close() // ignore error
if displayPath != nil {
file = displayPath(file)
}
return parser.ParseFile(fset, file, rd, mode)
}
// ContainingPackage returns the package containing filename.
//
// If filename is not absolute, it is interpreted relative to working directory dir.
// All I/O is via the build context's file system interface, if any.
//
// The '...Files []string' fields of the resulting build.Package are not
// populated (build.FindOnly mode).
//
func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Package, error) {
if !IsAbsPath(ctxt, filename) {
filename = JoinPath(ctxt, dir, filename)
}
// We must not assume the file tree uses
// "/" always,
// `\` always,
// or os.PathSeparator (which varies by platform),
// but to make any progress, we are forced to assume that
// paths will not use `\` unless the PathSeparator
// is also `\`, thus we can rely on filepath.ToSlash for some sanity.
dirSlash := path.Dir(filepath.ToSlash(filename)) + "/"
// We assume that no source root (GOPATH[i] or GOROOT) contains any other.
for _, srcdir := range ctxt.SrcDirs() {
srcdirSlash := filepath.ToSlash(srcdir) + "/"
if importPath, ok := HasSubdir(ctxt, srcdirSlash, dirSlash); ok {
return ctxt.Import(importPath, dir, build.FindOnly)
}
}
return nil, fmt.Errorf("can't find package containing %s", filename)
}
// -- Effective methods of file system interface -------------------------
// (go/build.Context defines these as methods, but does not export them.)
// hasSubdir calls ctxt.HasSubdir (if not nil) or else uses
// the local file system to answer the question.
func HasSubdir(ctxt *build.Context, root, dir string) (rel string, ok bool) {
if f := ctxt.HasSubdir; f != nil {
return f(root, dir)
}
// Try using paths we received.
if rel, ok = hasSubdir(root, dir); ok {
return
}
// Try expanding symlinks and comparing
// expanded against unexpanded and
// expanded against expanded.
rootSym, _ := filepath.EvalSymlinks(root)
dirSym, _ := filepath.EvalSymlinks(dir)
if rel, ok = hasSubdir(rootSym, dir); ok {
return
}
if rel, ok = hasSubdir(root, dirSym); ok {
return
}
return hasSubdir(rootSym, dirSym)
}
func hasSubdir(root, dir string) (rel string, ok bool) {
const sep = string(filepath.Separator)
root = filepath.Clean(root)
if !strings.HasSuffix(root, sep) {
root += sep
}
dir = filepath.Clean(dir)
if !strings.HasPrefix(dir, root) {
return "", false
}
return filepath.ToSlash(dir[len(root):]), true
}
// FileExists returns true if the specified file exists,
// using the build context's file system interface.
func FileExists(ctxt *build.Context, path string) bool {
if ctxt.OpenFile != nil {
r, err := ctxt.OpenFile(path)
if err != nil {
return false
}
r.Close() // ignore error
return true
}
_, err := os.Stat(path)
return err == nil
}
// OpenFile behaves like os.Open,
// but uses the build context's file system interface, if any.
func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) {
if ctxt.OpenFile != nil {
return ctxt.OpenFile(path)
}
return os.Open(path)
}
// IsAbsPath behaves like filepath.IsAbs,
// but uses the build context's file system interface, if any.
func IsAbsPath(ctxt *build.Context, path string) bool {
if ctxt.IsAbsPath != nil {
return ctxt.IsAbsPath(path)
}
return filepath.IsAbs(path)
}
// JoinPath behaves like filepath.Join,
// but uses the build context's file system interface, if any.
func JoinPath(ctxt *build.Context, path ...string) string {
if ctxt.JoinPath != nil {
return ctxt.JoinPath(path...)
}
return filepath.Join(path...)
}
// IsDir behaves like os.Stat plus IsDir,
// but uses the build context's file system interface, if any.
func IsDir(ctxt *build.Context, path string) bool {
if ctxt.IsDir != nil {
return ctxt.IsDir(path)
}
fi, err := os.Stat(path)
return err == nil && fi.IsDir()
}
// ReadDir behaves like ioutil.ReadDir,
// but uses the build context's file system interface, if any.
func ReadDir(ctxt *build.Context, path string) ([]os.FileInfo, error) {
if ctxt.ReadDir != nil {
return ctxt.ReadDir(path)
}
return ioutil.ReadDir(path)
}
// SplitPathList behaves like filepath.SplitList,
// but uses the build context's file system interface, if any.
func SplitPathList(ctxt *build.Context, s string) []string {
if ctxt.SplitPathList != nil {
return ctxt.SplitPathList(s)
}
return filepath.SplitList(s)
}
// sameFile returns true if x and y have the same basename and denote
// the same file.
//
func sameFile(x, y string) bool {
if path.Clean(x) == path.Clean(y) {
return true
}
if filepath.Base(x) == filepath.Base(y) { // (optimisation)
if xi, err := os.Stat(x); err == nil {
if yi, err := os.Stat(y); err == nil {
return os.SameFile(xi, yi)
}
}
}
return false
}

222
vendor/golang.org/x/tools/go/internal/cgo/cgo.go generated vendored Normal file
View File

@ -0,0 +1,222 @@
// 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 cgo handles cgo preprocessing of files containing `import "C"`.
//
// DESIGN
//
// The approach taken is to run the cgo processor on the package's
// CgoFiles and parse the output, faking the filenames of the
// resulting ASTs so that the synthetic file containing the C types is
// called "C" (e.g. "~/go/src/net/C") and the preprocessed files
// have their original names (e.g. "~/go/src/net/cgo_unix.go"),
// not the names of the actual temporary files.
//
// The advantage of this approach is its fidelity to 'go build'. The
// downside is that the token.Position.Offset for each AST node is
// incorrect, being an offset within the temporary file. Line numbers
// should still be correct because of the //line comments.
//
// The logic of this file is mostly plundered from the 'go build'
// tool, which also invokes the cgo preprocessor.
//
//
// REJECTED ALTERNATIVE
//
// An alternative approach that we explored is to extend go/types'
// Importer mechanism to provide the identity of the importing package
// so that each time `import "C"` appears it resolves to a different
// synthetic package containing just the objects needed in that case.
// The loader would invoke cgo but parse only the cgo_types.go file
// defining the package-level objects, discarding the other files
// resulting from preprocessing.
//
// The benefit of this approach would have been that source-level
// syntax information would correspond exactly to the original cgo
// file, with no preprocessing involved, making source tools like
// godoc, guru, and eg happy. However, the approach was rejected
// due to the additional complexity it would impose on go/types. (It
// made for a beautiful demo, though.)
//
// cgo files, despite their *.go extension, are not legal Go source
// files per the specification since they may refer to unexported
// members of package "C" such as C.int. Also, a function such as
// C.getpwent has in effect two types, one matching its C type and one
// which additionally returns (errno C.int). The cgo preprocessor
// uses name mangling to distinguish these two functions in the
// processed code, but go/types would need to duplicate this logic in
// its handling of function calls, analogous to the treatment of map
// lookups in which y=m[k] and y,ok=m[k] are both legal.
package cgo
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
"strings"
exec "golang.org/x/sys/execabs"
)
// ProcessFiles invokes the cgo preprocessor on bp.CgoFiles, parses
// the output and returns the resulting ASTs.
//
func ProcessFiles(bp *build.Package, fset *token.FileSet, DisplayPath func(path string) string, mode parser.Mode) ([]*ast.File, error) {
tmpdir, err := ioutil.TempDir("", strings.Replace(bp.ImportPath, "/", "_", -1)+"_C")
if err != nil {
return nil, err
}
defer os.RemoveAll(tmpdir)
pkgdir := bp.Dir
if DisplayPath != nil {
pkgdir = DisplayPath(pkgdir)
}
cgoFiles, cgoDisplayFiles, err := Run(bp, pkgdir, tmpdir, false)
if err != nil {
return nil, err
}
var files []*ast.File
for i := range cgoFiles {
rd, err := os.Open(cgoFiles[i])
if err != nil {
return nil, err
}
display := filepath.Join(bp.Dir, cgoDisplayFiles[i])
f, err := parser.ParseFile(fset, display, rd, mode)
rd.Close()
if err != nil {
return nil, err
}
files = append(files, f)
}
return files, nil
}
var cgoRe = regexp.MustCompile(`[/\\:]`)
// Run invokes the cgo preprocessor on bp.CgoFiles and returns two
// lists of files: the resulting processed files (in temporary
// directory tmpdir) and the corresponding names of the unprocessed files.
//
// Run is adapted from (*builder).cgo in
// $GOROOT/src/cmd/go/build.go, but these features are unsupported:
// Objective C, CGOPKGPATH, CGO_FLAGS.
//
// If useabs is set to true, absolute paths of the bp.CgoFiles will be passed in
// to the cgo preprocessor. This in turn will set the // line comments
// referring to those files to use absolute paths. This is needed for
// go/packages using the legacy go list support so it is able to find
// the original files.
func Run(bp *build.Package, pkgdir, tmpdir string, useabs bool) (files, displayFiles []string, err error) {
cgoCPPFLAGS, _, _, _ := cflags(bp, true)
_, cgoexeCFLAGS, _, _ := cflags(bp, false)
if len(bp.CgoPkgConfig) > 0 {
pcCFLAGS, err := pkgConfigFlags(bp)
if err != nil {
return nil, nil, err
}
cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
}
// Allows including _cgo_export.h from .[ch] files in the package.
cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", tmpdir)
// _cgo_gotypes.go (displayed "C") contains the type definitions.
files = append(files, filepath.Join(tmpdir, "_cgo_gotypes.go"))
displayFiles = append(displayFiles, "C")
for _, fn := range bp.CgoFiles {
// "foo.cgo1.go" (displayed "foo.go") is the processed Go source.
f := cgoRe.ReplaceAllString(fn[:len(fn)-len("go")], "_")
files = append(files, filepath.Join(tmpdir, f+"cgo1.go"))
displayFiles = append(displayFiles, fn)
}
var cgoflags []string
if bp.Goroot && bp.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_runtime_cgo=false")
}
if bp.Goroot && bp.ImportPath == "runtime/race" || bp.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_syscall=false")
}
var cgoFiles []string = bp.CgoFiles
if useabs {
cgoFiles = make([]string, len(bp.CgoFiles))
for i := range cgoFiles {
cgoFiles[i] = filepath.Join(pkgdir, bp.CgoFiles[i])
}
}
args := stringList(
"go", "tool", "cgo", "-objdir", tmpdir, cgoflags, "--",
cgoCPPFLAGS, cgoexeCFLAGS, cgoFiles,
)
if false {
log.Printf("Running cgo for package %q: %s (dir=%s)", bp.ImportPath, args, pkgdir)
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = pkgdir
cmd.Env = append(os.Environ(), "PWD="+pkgdir)
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return nil, nil, fmt.Errorf("cgo failed: %s: %s", args, err)
}
return files, displayFiles, nil
}
// -- unmodified from 'go build' ---------------------------------------
// Return the flags to use when invoking the C or C++ compilers, or cgo.
func cflags(p *build.Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) {
var defaults string
if def {
defaults = "-g -O2"
}
cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS)
ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS)
return
}
// envList returns the value of the given environment variable broken
// into fields, using the default value when the variable is empty.
func envList(key, def string) []string {
v := os.Getenv(key)
if v == "" {
v = def
}
return strings.Fields(v)
}
// stringList's arguments should be a sequence of string or []string values.
// stringList flattens them into a single []string.
func stringList(args ...interface{}) []string {
var x []string
for _, arg := range args {
switch arg := arg.(type) {
case []string:
x = append(x, arg...)
case string:
x = append(x, arg)
default:
panic("stringList: invalid argument")
}
}
return x
}

View File

@ -0,0 +1,39 @@
// 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 cgo
import (
"errors"
"fmt"
"go/build"
exec "golang.org/x/sys/execabs"
"strings"
)
// pkgConfig runs pkg-config with the specified arguments and returns the flags it prints.
func pkgConfig(mode string, pkgs []string) (flags []string, err error) {
cmd := exec.Command("pkg-config", append([]string{mode}, pkgs...)...)
out, err := cmd.CombinedOutput()
if err != nil {
s := fmt.Sprintf("%s failed: %v", strings.Join(cmd.Args, " "), err)
if len(out) > 0 {
s = fmt.Sprintf("%s: %s", s, out)
}
return nil, errors.New(s)
}
if len(out) > 0 {
flags = strings.Fields(string(out))
}
return
}
// pkgConfigFlags calls pkg-config if needed and returns the cflags
// needed to build the package.
func pkgConfigFlags(p *build.Package) (cflags []string, err error) {
if len(p.CgoPkgConfig) == 0 {
return nil, nil
}
return pkgConfig("--cflags", p.CgoPkgConfig)
}

204
vendor/golang.org/x/tools/go/loader/doc.go generated vendored Normal file
View File

@ -0,0 +1,204 @@
// Copyright 2015 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 loader loads a complete Go program from source code, parsing
// and type-checking the initial packages plus their transitive closure
// of dependencies. The ASTs and the derived facts are retained for
// later use.
//
// Deprecated: This is an older API and does not have support
// for modules. Use golang.org/x/tools/go/packages instead.
//
// The package defines two primary types: Config, which specifies a
// set of initial packages to load and various other options; and
// Program, which is the result of successfully loading the packages
// specified by a configuration.
//
// The configuration can be set directly, but *Config provides various
// convenience methods to simplify the common cases, each of which can
// be called any number of times. Finally, these are followed by a
// call to Load() to actually load and type-check the program.
//
// var conf loader.Config
//
// // Use the command-line arguments to specify
// // a set of initial packages to load from source.
// // See FromArgsUsage for help.
// rest, err := conf.FromArgs(os.Args[1:], wantTests)
//
// // Parse the specified files and create an ad hoc package with path "foo".
// // All files must have the same 'package' declaration.
// conf.CreateFromFilenames("foo", "foo.go", "bar.go")
//
// // Create an ad hoc package with path "foo" from
// // the specified already-parsed files.
// // All ASTs must have the same 'package' declaration.
// conf.CreateFromFiles("foo", parsedFiles)
//
// // Add "runtime" to the set of packages to be loaded.
// conf.Import("runtime")
//
// // Adds "fmt" and "fmt_test" to the set of packages
// // to be loaded. "fmt" will include *_test.go files.
// conf.ImportWithTests("fmt")
//
// // Finally, load all the packages specified by the configuration.
// prog, err := conf.Load()
//
// See examples_test.go for examples of API usage.
//
//
// CONCEPTS AND TERMINOLOGY
//
// The WORKSPACE is the set of packages accessible to the loader. The
// workspace is defined by Config.Build, a *build.Context. The
// default context treats subdirectories of $GOROOT and $GOPATH as
// packages, but this behavior may be overridden.
//
// An AD HOC package is one specified as a set of source files on the
// command line. In the simplest case, it may consist of a single file
// such as $GOROOT/src/net/http/triv.go.
//
// EXTERNAL TEST packages are those comprised of a set of *_test.go
// files all with the same 'package foo_test' declaration, all in the
// same directory. (go/build.Package calls these files XTestFiles.)
//
// An IMPORTABLE package is one that can be referred to by some import
// spec. Every importable package is uniquely identified by its
// PACKAGE PATH or just PATH, a string such as "fmt", "encoding/json",
// or "cmd/vendor/golang.org/x/arch/x86/x86asm". A package path
// typically denotes a subdirectory of the workspace.
//
// An import declaration uses an IMPORT PATH to refer to a package.
// Most import declarations use the package path as the import path.
//
// Due to VENDORING (https://golang.org/s/go15vendor), the
// interpretation of an import path may depend on the directory in which
// it appears. To resolve an import path to a package path, go/build
// must search the enclosing directories for a subdirectory named
// "vendor".
//
// ad hoc packages and external test packages are NON-IMPORTABLE. The
// path of an ad hoc package is inferred from the package
// declarations of its files and is therefore not a unique package key.
// For example, Config.CreatePkgs may specify two initial ad hoc
// packages, both with path "main".
//
// An AUGMENTED package is an importable package P plus all the
// *_test.go files with same 'package foo' declaration as P.
// (go/build.Package calls these files TestFiles.)
//
// The INITIAL packages are those specified in the configuration. A
// DEPENDENCY is a package loaded to satisfy an import in an initial
// package or another dependency.
//
package loader
// IMPLEMENTATION NOTES
//
// 'go test', in-package test files, and import cycles
// ---------------------------------------------------
//
// An external test package may depend upon members of the augmented
// package that are not in the unaugmented package, such as functions
// that expose internals. (See bufio/export_test.go for an example.)
// So, the loader must ensure that for each external test package
// it loads, it also augments the corresponding non-test package.
//
// The import graph over n unaugmented packages must be acyclic; the
// import graph over n-1 unaugmented packages plus one augmented
// package must also be acyclic. ('go test' relies on this.) But the
// import graph over n augmented packages may contain cycles.
//
// First, all the (unaugmented) non-test packages and their
// dependencies are imported in the usual way; the loader reports an
// error if it detects an import cycle.
//
// Then, each package P for which testing is desired is augmented by
// the list P' of its in-package test files, by calling
// (*types.Checker).Files. This arrangement ensures that P' may
// reference definitions within P, but P may not reference definitions
// within P'. Furthermore, P' may import any other package, including
// ones that depend upon P, without an import cycle error.
//
// Consider two packages A and B, both of which have lists of
// in-package test files we'll call A' and B', and which have the
// following import graph edges:
// B imports A
// B' imports A
// A' imports B
// This last edge would be expected to create an error were it not
// for the special type-checking discipline above.
// Cycles of size greater than two are possible. For example:
// compress/bzip2/bzip2_test.go (package bzip2) imports "io/ioutil"
// io/ioutil/tempfile_test.go (package ioutil) imports "regexp"
// regexp/exec_test.go (package regexp) imports "compress/bzip2"
//
//
// Concurrency
// -----------
//
// Let us define the import dependency graph as follows. Each node is a
// list of files passed to (Checker).Files at once. Many of these lists
// are the production code of an importable Go package, so those nodes
// are labelled by the package's path. The remaining nodes are
// ad hoc packages and lists of in-package *_test.go files that augment
// an importable package; those nodes have no label.
//
// The edges of the graph represent import statements appearing within a
// file. An edge connects a node (a list of files) to the node it
// imports, which is importable and thus always labelled.
//
// Loading is controlled by this dependency graph.
//
// To reduce I/O latency, we start loading a package's dependencies
// asynchronously as soon as we've parsed its files and enumerated its
// imports (scanImports). This performs a preorder traversal of the
// import dependency graph.
//
// To exploit hardware parallelism, we type-check unrelated packages in
// parallel, where "unrelated" means not ordered by the partial order of
// the import dependency graph.
//
// We use a concurrency-safe non-blocking cache (importer.imported) to
// record the results of type-checking, whether success or failure. An
// entry is created in this cache by startLoad the first time the
// package is imported. The first goroutine to request an entry becomes
// responsible for completing the task and broadcasting completion to
// subsequent requestors, which block until then.
//
// Type checking occurs in (parallel) postorder: we cannot type-check a
// set of files until we have loaded and type-checked all of their
// immediate dependencies (and thus all of their transitive
// dependencies). If the input were guaranteed free of import cycles,
// this would be trivial: we could simply wait for completion of the
// dependencies and then invoke the typechecker.
//
// But as we saw in the 'go test' section above, some cycles in the
// import graph over packages are actually legal, so long as the
// cycle-forming edge originates in the in-package test files that
// augment the package. This explains why the nodes of the import
// dependency graph are not packages, but lists of files: the unlabelled
// nodes avoid the cycles. Consider packages A and B where B imports A
// and A's in-package tests AT import B. The naively constructed import
// graph over packages would contain a cycle (A+AT) --> B --> (A+AT) but
// the graph over lists of files is AT --> B --> A, where AT is an
// unlabelled node.
//
// Awaiting completion of the dependencies in a cyclic graph would
// deadlock, so we must materialize the import dependency graph (as
// importer.graph) and check whether each import edge forms a cycle. If
// x imports y, and the graph already contains a path from y to x, then
// there is an import cycle, in which case the processing of x must not
// wait for the completion of processing of y.
//
// When the type-checker makes a callback (doImport) to the loader for a
// given import edge, there are two possible cases. In the normal case,
// the dependency has already been completely type-checked; doImport
// does a cache lookup and returns it. In the cyclic case, the entry in
// the cache is still necessarily incomplete, indicating a cycle. We
// perform the cycle check again to obtain the error message, and return
// the error.
//
// The result of using concurrency is about a 2.5x speedup for stdlib_test.

1078
vendor/golang.org/x/tools/go/loader/loader.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

124
vendor/golang.org/x/tools/go/loader/util.go generated vendored Normal file
View File

@ -0,0 +1,124 @@
// 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 loader
import (
"go/ast"
"go/build"
"go/parser"
"go/token"
"io"
"os"
"strconv"
"sync"
"golang.org/x/tools/go/buildutil"
)
// We use a counting semaphore to limit
// the number of parallel I/O calls per process.
var ioLimit = make(chan bool, 10)
// parseFiles parses the Go source files within directory dir and
// returns the ASTs of the ones that could be at least partially parsed,
// along with a list of I/O and parse errors encountered.
//
// I/O is done via ctxt, which may specify a virtual file system.
// displayPath is used to transform the filenames attached to the ASTs.
//
func parseFiles(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, files []string, mode parser.Mode) ([]*ast.File, []error) {
if displayPath == nil {
displayPath = func(path string) string { return path }
}
var wg sync.WaitGroup
n := len(files)
parsed := make([]*ast.File, n)
errors := make([]error, n)
for i, file := range files {
if !buildutil.IsAbsPath(ctxt, file) {
file = buildutil.JoinPath(ctxt, dir, file)
}
wg.Add(1)
go func(i int, file string) {
ioLimit <- true // wait
defer func() {
wg.Done()
<-ioLimit // signal
}()
var rd io.ReadCloser
var err error
if ctxt.OpenFile != nil {
rd, err = ctxt.OpenFile(file)
} else {
rd, err = os.Open(file)
}
if err != nil {
errors[i] = err // open failed
return
}
// ParseFile may return both an AST and an error.
parsed[i], errors[i] = parser.ParseFile(fset, displayPath(file), rd, mode)
rd.Close()
}(i, file)
}
wg.Wait()
// Eliminate nils, preserving order.
var o int
for _, f := range parsed {
if f != nil {
parsed[o] = f
o++
}
}
parsed = parsed[:o]
o = 0
for _, err := range errors {
if err != nil {
errors[o] = err
o++
}
}
errors = errors[:o]
return parsed, errors
}
// scanImports returns the set of all import paths from all
// import specs in the specified files.
func scanImports(files []*ast.File) map[string]bool {
imports := make(map[string]bool)
for _, f := range files {
for _, decl := range f.Decls {
if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
for _, spec := range decl.Specs {
spec := spec.(*ast.ImportSpec)
// NB: do not assume the program is well-formed!
path, err := strconv.Unquote(spec.Path.Value)
if err != nil {
continue // quietly ignore the error
}
if path == "C" {
continue // skip pseudopackage
}
imports[path] = true
}
}
}
}
return imports
}
// ---------- Internal helpers ----------
// TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos)
func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
p := int(pos)
base := f.Base()
return base <= p && p < base+f.Size()
}

6
vendor/modules.txt vendored
View File

@ -332,6 +332,9 @@ github.com/russross/blackfriday/v2
## explicit; go 1.13
github.com/stretchr/testify/assert
github.com/stretchr/testify/require
# github.com/sudarshan-reddy/go-sumtype v0.0.0-20210827105221-82eca7e5abb1
## explicit; go 1.16
github.com/sudarshan-reddy/go-sumtype
# github.com/urfave/cli/v2 v2.3.0 => github.com/ipostelnik/cli/v2 v2.3.1-0.20210324024421-b6ea8234fe3d
## explicit; go 1.11
github.com/urfave/cli/v2
@ -419,9 +422,12 @@ golang.org/x/text/unicode/norm
## explicit; go 1.17
golang.org/x/tools/go/ast/astutil
golang.org/x/tools/go/ast/inspector
golang.org/x/tools/go/buildutil
golang.org/x/tools/go/gcexportdata
golang.org/x/tools/go/internal/cgo
golang.org/x/tools/go/internal/gcimporter
golang.org/x/tools/go/internal/packagesdriver
golang.org/x/tools/go/loader
golang.org/x/tools/go/packages
golang.org/x/tools/imports
golang.org/x/tools/internal/event