Update Gopkg.*

Amine Hilaly created

Change summary

Gopkg.lock                                                           |   22 
Gopkg.toml                                                           |    2 
vendor/github.com/99designs/gqlgen/api/generate.go                   |   76 
vendor/github.com/99designs/gqlgen/api/option.go                     |   20 
vendor/github.com/99designs/gqlgen/cmd/ambient.go                    |    2 
vendor/github.com/99designs/gqlgen/cmd/gen.go                        |   34 
vendor/github.com/99designs/gqlgen/cmd/init.go                       |   66 
vendor/github.com/99designs/gqlgen/cmd/root.go                       |   11 
vendor/github.com/99designs/gqlgen/codegen/args.go                   |  104 
vendor/github.com/99designs/gqlgen/codegen/args.gotpl                |   43 
vendor/github.com/99designs/gqlgen/codegen/build.go                  |  194 
vendor/github.com/99designs/gqlgen/codegen/codegen.go                |  179 
vendor/github.com/99designs/gqlgen/codegen/complexity.go             |   11 
vendor/github.com/99designs/gqlgen/codegen/config.go                 |  273 
vendor/github.com/99designs/gqlgen/codegen/config/binder.go          |  451 
vendor/github.com/99designs/gqlgen/codegen/config/config.go          |  408 
vendor/github.com/99designs/gqlgen/codegen/data.go                   |  168 
vendor/github.com/99designs/gqlgen/codegen/directive.go              |  119 
vendor/github.com/99designs/gqlgen/codegen/directive_build.go        |   48 
vendor/github.com/99designs/gqlgen/codegen/enum.go                   |   12 
vendor/github.com/99designs/gqlgen/codegen/enum_build.go             |   39 
vendor/github.com/99designs/gqlgen/codegen/field.go                  |  394 
vendor/github.com/99designs/gqlgen/codegen/field.gotpl               |   62 
vendor/github.com/99designs/gqlgen/codegen/generate.go               |   15 
vendor/github.com/99designs/gqlgen/codegen/generated!.gotpl          |  104 
vendor/github.com/99designs/gqlgen/codegen/input.gotpl               |   56 
vendor/github.com/99designs/gqlgen/codegen/input_build.go            |   96 
vendor/github.com/99designs/gqlgen/codegen/interface.go              |   58 
vendor/github.com/99designs/gqlgen/codegen/interface.gotpl           |   20 
vendor/github.com/99designs/gqlgen/codegen/interface_build.go        |   53 
vendor/github.com/99designs/gqlgen/codegen/model.go                  |   17 
vendor/github.com/99designs/gqlgen/codegen/models_build.go           |   91 
vendor/github.com/99designs/gqlgen/codegen/object.go                 |  483 
vendor/github.com/99designs/gqlgen/codegen/object.gotpl              |   77 
vendor/github.com/99designs/gqlgen/codegen/object_build.go           |  181 
vendor/github.com/99designs/gqlgen/codegen/templates/args.gotpl      |   13 
vendor/github.com/99designs/gqlgen/codegen/templates/data.go         |    4 
vendor/github.com/99designs/gqlgen/codegen/templates/import.go       |   46 
vendor/github.com/99designs/gqlgen/codegen/templates/input.gotpl     |   28 
vendor/github.com/99designs/gqlgen/codegen/templates/interface.gotpl |   18 
vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl    |   91 
vendor/github.com/99designs/gqlgen/codegen/templates/object.gotpl    |   69 
vendor/github.com/99designs/gqlgen/codegen/templates/resolver.gotpl  |   44 
vendor/github.com/99designs/gqlgen/codegen/templates/templates.go    |  452 
vendor/github.com/99designs/gqlgen/codegen/type.go                   |  174 
vendor/github.com/99designs/gqlgen/codegen/type.gotpl                |  131 
vendor/github.com/99designs/gqlgen/codegen/type_build.go             |  100 
vendor/github.com/99designs/gqlgen/codegen/util.go                   |  358 
vendor/github.com/99designs/gqlgen/docs/content/_introduction.md     |    1 
vendor/github.com/99designs/gqlgen/graphql/bool.go                   |    2 
vendor/github.com/99designs/gqlgen/graphql/context.go                |   69 
vendor/github.com/99designs/gqlgen/graphql/error.go                  |    4 
vendor/github.com/99designs/gqlgen/graphql/exec.go                   |   10 
vendor/github.com/99designs/gqlgen/graphql/fieldset.go               |   63 
vendor/github.com/99designs/gqlgen/graphql/id.go                     |   21 
vendor/github.com/99designs/gqlgen/graphql/int.go                    |   50 
vendor/github.com/99designs/gqlgen/graphql/introspection/type.go     |   20 
vendor/github.com/99designs/gqlgen/graphql/jsonw.go                  |   31 
vendor/github.com/99designs/gqlgen/graphql/root.go                   |    7 
vendor/github.com/99designs/gqlgen/graphql/string.go                 |   59 
vendor/github.com/99designs/gqlgen/graphql/version.go                |    2 
vendor/github.com/99designs/gqlgen/handler/graphql.go                |   38 
vendor/github.com/99designs/gqlgen/handler/playground.go             |   19 
vendor/github.com/99designs/gqlgen/handler/websocket.go              |   30 
vendor/github.com/99designs/gqlgen/internal/code/compare.go          |  163 
vendor/github.com/99designs/gqlgen/internal/code/imports.go          |   60 
vendor/github.com/99designs/gqlgen/internal/code/util.go             |   56 
vendor/github.com/99designs/gqlgen/internal/gopath/gopath.go         |   37 
vendor/github.com/99designs/gqlgen/internal/imports/prune.go         |   22 
vendor/github.com/99designs/gqlgen/plugin/modelgen/models.go         |  207 
vendor/github.com/99designs/gqlgen/plugin/modelgen/models.gotpl      |   85 
vendor/github.com/99designs/gqlgen/plugin/plugin.go                  |   20 
vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.go    |   53 
vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.gotpl |   40 
vendor/github.com/99designs/gqlgen/plugin/servergen/server.go        |   49 
vendor/github.com/99designs/gqlgen/plugin/servergen/server.gotpl     |   16 
vendor/golang.org/x/tools/go/buildutil/allpackages.go                |  198 
vendor/golang.org/x/tools/go/buildutil/fakecontext.go                |  109 
vendor/golang.org/x/tools/go/buildutil/overlay.go                    |  103 
vendor/golang.org/x/tools/go/buildutil/tags.go                       |   75 
vendor/golang.org/x/tools/go/buildutil/util.go                       |  212 
vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go            |  109 
vendor/golang.org/x/tools/go/gcexportdata/importer.go                |   73 
vendor/golang.org/x/tools/go/gcexportdata/main.go                    |   99 
vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go          |  852 
vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go          | 1028 
vendor/golang.org/x/tools/go/internal/gcimporter/exportdata.go       |   93 
vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go       | 1051 
vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go          |  598 
vendor/golang.org/x/tools/go/internal/gcimporter/isAlias18.go        |   13 
vendor/golang.org/x/tools/go/internal/gcimporter/isAlias19.go        |   13 
vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go   |   21 
vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go   |   13 
vendor/golang.org/x/tools/go/loader/doc.go                           |  205 
vendor/golang.org/x/tools/go/loader/loader.go                        | 1078 
vendor/golang.org/x/tools/go/loader/util.go                          |  124 
vendor/golang.org/x/tools/go/packages/doc.go                         |  269 
vendor/golang.org/x/tools/go/packages/external.go                    |   68 
vendor/golang.org/x/tools/go/packages/golist.go                      |  337 
vendor/golang.org/x/tools/go/packages/golist_fallback.go             |  282 
vendor/golang.org/x/tools/go/packages/packages.go                    |  824 
vendor/golang.org/x/tools/go/types/typeutil/imports.go               |   31 
vendor/golang.org/x/tools/go/types/typeutil/map.go                   |  313 
vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go        |   72 
vendor/golang.org/x/tools/go/types/typeutil/ui.go                    |   52 
105 files changed, 10,079 insertions(+), 4,987 deletions(-)

Detailed changes

Gopkg.lock πŸ”—

@@ -2,22 +2,28 @@
 
 
 [[projects]]
-  digest = "1:e8f6639eaa399c8595b9a2dee175514a9f3842888dc080e2776360dc604150dc"
+  digest = "1:d28d302ab337b9ea3af38c04429e198ab422c1e026592aebcf7aec66ebbc4645"
   name = "github.com/99designs/gqlgen"
   packages = [
+    "api",
     "cmd",
     "codegen",
+    "codegen/config",
     "codegen/templates",
     "complexity",
     "graphql",
     "graphql/introspection",
     "handler",
-    "internal/gopath",
+    "internal/code",
     "internal/imports",
+    "plugin",
+    "plugin/modelgen",
+    "plugin/resolvergen",
+    "plugin/servergen",
   ]
   pruneopts = "UT"
-  revision = "da1e07f5876c0fb79cbad19006f7135be08590d6"
-  version = "v0.7.2"
+  revision = "010a79b66f08732cb70d133dcab297a8ee895572"
+  version = "v0.8.3"
 
 [[projects]]
   branch = "master"
@@ -382,13 +388,15 @@
 
 [[projects]]
   branch = "master"
-  digest = "1:d22891f2d4a24a531ae01994abae377ec9d8a45ec8849aa95c27dc36014b8c24"
+  digest = "1:090a56ffcfac7f9095b601438e520f4b3609f3442afbaf151f750e2e377c3508"
   name = "golang.org/x/tools"
   packages = [
     "go/ast/astutil",
-    "go/buildutil",
+    "go/gcexportdata",
     "go/internal/cgo",
-    "go/loader",
+    "go/internal/gcimporter",
+    "go/packages",
+    "go/types/typeutil",
     "imports",
     "internal/fastwalk",
   ]

Gopkg.toml πŸ”—

@@ -58,7 +58,7 @@
 
 [[constraint]]
   name = "github.com/99designs/gqlgen"
-  version = "0.7.1"
+  version = "0.8.3"
 
 [[constraint]]
   name = "github.com/MichaelMure/gocui"

vendor/github.com/99designs/gqlgen/api/generate.go πŸ”—

@@ -0,0 +1,76 @@
+package api
+
+import (
+	"syscall"
+
+	"github.com/99designs/gqlgen/codegen"
+	"github.com/99designs/gqlgen/codegen/config"
+	"github.com/99designs/gqlgen/plugin"
+	"github.com/99designs/gqlgen/plugin/modelgen"
+	"github.com/99designs/gqlgen/plugin/resolvergen"
+	"github.com/pkg/errors"
+	"golang.org/x/tools/go/packages"
+)
+
+func Generate(cfg *config.Config, option ...Option) error {
+	_ = syscall.Unlink(cfg.Exec.Filename)
+	_ = syscall.Unlink(cfg.Model.Filename)
+
+	plugins := []plugin.Plugin{
+		modelgen.New(),
+		resolvergen.New(),
+	}
+
+	for _, o := range option {
+		o(cfg, &plugins)
+	}
+
+	for _, p := range plugins {
+		if mut, ok := p.(plugin.ConfigMutator); ok {
+			err := mut.MutateConfig(cfg)
+			if err != nil {
+				return errors.Wrap(err, p.Name())
+			}
+		}
+	}
+	// Merge again now that the generated models have been injected into the typemap
+	data, err := codegen.BuildData(cfg)
+	if err != nil {
+		return errors.Wrap(err, "merging failed")
+	}
+
+	if err = codegen.GenerateCode(data); err != nil {
+		return errors.Wrap(err, "generating core failed")
+	}
+
+	for _, p := range plugins {
+		if mut, ok := p.(plugin.CodeGenerator); ok {
+			err := mut.GenerateCode(data)
+			if err != nil {
+				return errors.Wrap(err, p.Name())
+			}
+		}
+	}
+
+	if err := validate(cfg); err != nil {
+		return errors.Wrap(err, "validation failed")
+	}
+
+	return nil
+}
+
+func validate(cfg *config.Config) error {
+	roots := []string{cfg.Exec.ImportPath()}
+	if cfg.Model.IsDefined() {
+		roots = append(roots, cfg.Model.ImportPath())
+	}
+
+	if cfg.Resolver.IsDefined() {
+		roots = append(roots, cfg.Resolver.ImportPath())
+	}
+	_, err := packages.Load(&packages.Config{Mode: packages.LoadTypes | packages.LoadSyntax}, roots...)
+	if err != nil {
+		return errors.Wrap(err, "validation failed")
+	}
+	return nil
+}

vendor/github.com/99designs/gqlgen/api/option.go πŸ”—

@@ -0,0 +1,20 @@
+package api
+
+import (
+	"github.com/99designs/gqlgen/codegen/config"
+	"github.com/99designs/gqlgen/plugin"
+)
+
+type Option func(cfg *config.Config, plugins *[]plugin.Plugin)
+
+func NoPlugins() Option {
+	return func(cfg *config.Config, plugins *[]plugin.Plugin) {
+		*plugins = nil
+	}
+}
+
+func AddPlugin(p plugin.Plugin) Option {
+	return func(cfg *config.Config, plugins *[]plugin.Plugin) {
+		*plugins = append(*plugins, p)
+	}
+}

vendor/github.com/99designs/gqlgen/cmd/gen.go πŸ”—

@@ -2,10 +2,10 @@ package cmd
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 
-	"github.com/99designs/gqlgen/codegen"
+	"github.com/99designs/gqlgen/api"
+	"github.com/99designs/gqlgen/codegen/config"
 	"github.com/pkg/errors"
 	"github.com/urfave/cli"
 )
@@ -18,43 +18,27 @@ var genCmd = cli.Command{
 		cli.StringFlag{Name: "config, c", Usage: "the config filename"},
 	},
 	Action: func(ctx *cli.Context) {
-		var config *codegen.Config
+		var cfg *config.Config
 		var err error
 		if configFilename := ctx.String("config"); configFilename != "" {
-			config, err = codegen.LoadConfig(configFilename)
+			cfg, err = config.LoadConfig(configFilename)
 			if err != nil {
 				fmt.Fprintln(os.Stderr, err.Error())
 				os.Exit(1)
 			}
 		} else {
-			config, err = codegen.LoadConfigFromDefaultLocations()
+			cfg, err = config.LoadConfigFromDefaultLocations()
 			if os.IsNotExist(errors.Cause(err)) {
-				config = codegen.DefaultConfig()
+				cfg = config.DefaultConfig()
 			} else if err != nil {
 				fmt.Fprintln(os.Stderr, err.Error())
-				os.Exit(1)
-			}
-		}
-
-		for _, filename := range config.SchemaFilename {
-			var schemaRaw []byte
-			schemaRaw, err = ioutil.ReadFile(filename)
-			if err != nil {
-				fmt.Fprintln(os.Stderr, "unable to open schema: "+err.Error())
-				os.Exit(1)
+				os.Exit(2)
 			}
-			config.SchemaStr[filename] = string(schemaRaw)
-		}
-
-		if err = config.Check(); err != nil {
-			fmt.Fprintln(os.Stderr, "invalid config format: "+err.Error())
-			os.Exit(1)
 		}
 
-		err = codegen.Generate(*config)
-		if err != nil {
+		if err = api.Generate(cfg); err != nil {
 			fmt.Fprintln(os.Stderr, err.Error())
-			os.Exit(2)
+			os.Exit(3)
 		}
 	},
 }

vendor/github.com/99designs/gqlgen/cmd/init.go πŸ”—

@@ -7,10 +7,13 @@ import (
 	"os"
 	"strings"
 
-	"github.com/99designs/gqlgen/codegen"
+	"github.com/99designs/gqlgen/api"
+	"github.com/99designs/gqlgen/plugin/servergen"
+
+	"github.com/99designs/gqlgen/codegen/config"
 	"github.com/pkg/errors"
 	"github.com/urfave/cli"
-	"gopkg.in/yaml.v2"
+	yaml "gopkg.in/yaml.v2"
 )
 
 var configComment = `
@@ -68,46 +71,27 @@ var initCmd = cli.Command{
 	},
 }
 
-func GenerateGraphServer(config *codegen.Config, serverFilename string) {
-	for _, filename := range config.SchemaFilename {
-		schemaRaw, err := ioutil.ReadFile(filename)
-		if err != nil {
-			fmt.Fprintln(os.Stderr, "unable to open schema: "+err.Error())
-			os.Exit(1)
-		}
-		config.SchemaStr[filename] = string(schemaRaw)
-	}
-
-	if err := config.Check(); err != nil {
-		fmt.Fprintln(os.Stderr, "invalid config format: "+err.Error())
-		os.Exit(1)
-	}
-
-	if err := codegen.Generate(*config); err != nil {
-		fmt.Fprintln(os.Stderr, err.Error())
-		os.Exit(1)
-	}
-
-	if err := codegen.GenerateServer(*config, serverFilename); err != nil {
+func GenerateGraphServer(cfg *config.Config, serverFilename string) {
+	err := api.Generate(cfg, api.AddPlugin(servergen.New(serverFilename)))
+	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
-		os.Exit(1)
 	}
 
 	fmt.Fprintf(os.Stdout, "Exec \"go run ./%s\" to start GraphQL server\n", serverFilename)
 }
 
-func initConfig(ctx *cli.Context) *codegen.Config {
-	var config *codegen.Config
+func initConfig(ctx *cli.Context) *config.Config {
+	var cfg *config.Config
 	var err error
 	configFilename := ctx.String("config")
 	if configFilename != "" {
-		config, err = codegen.LoadConfig(configFilename)
+		cfg, err = config.LoadConfig(configFilename)
 	} else {
-		config, err = codegen.LoadConfigFromDefaultLocations()
+		cfg, err = config.LoadConfigFromDefaultLocations()
 	}
 
-	if config != nil {
-		fmt.Fprintf(os.Stderr, "init failed: a configuration file already exists at %s\n", config.FilePath)
+	if cfg != nil {
+		fmt.Fprintf(os.Stderr, "init failed: a configuration file already exists\n")
 		os.Exit(1)
 	}
 
@@ -119,9 +103,9 @@ func initConfig(ctx *cli.Context) *codegen.Config {
 	if configFilename == "" {
 		configFilename = "gqlgen.yml"
 	}
-	config = codegen.DefaultConfig()
+	cfg = config.DefaultConfig()
 
-	config.Resolver = codegen.PackageConfig{
+	cfg.Resolver = config.PackageConfig{
 		Filename: "resolver.go",
 		Type:     "Resolver",
 	}
@@ -129,23 +113,21 @@ func initConfig(ctx *cli.Context) *codegen.Config {
 	var buf bytes.Buffer
 	buf.WriteString(strings.TrimSpace(configComment))
 	buf.WriteString("\n\n")
-	{
-		var b []byte
-		b, err = yaml.Marshal(config)
-		if err != nil {
-			fmt.Fprintln(os.Stderr, "unable to marshal yaml: "+err.Error())
-			os.Exit(1)
-		}
-		buf.Write(b)
+	var b []byte
+	b, err = yaml.Marshal(cfg)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "unable to marshal yaml: "+err.Error())
+		os.Exit(1)
 	}
+	buf.Write(b)
 
 	err = ioutil.WriteFile(configFilename, buf.Bytes(), 0644)
 	if err != nil {
-		fmt.Fprintln(os.Stderr, "unable to write config file: "+err.Error())
+		fmt.Fprintln(os.Stderr, "unable to write cfg file: "+err.Error())
 		os.Exit(1)
 	}
 
-	return config
+	return cfg
 }
 
 func initSchema(schemaFilename string) {

vendor/github.com/99designs/gqlgen/cmd/root.go πŸ”—

@@ -7,7 +7,6 @@ import (
 	"os"
 
 	"github.com/99designs/gqlgen/graphql"
-	"github.com/99designs/gqlgen/internal/gopath"
 	"github.com/urfave/cli"
 
 	// Required since otherwise dep will prune away these unused packages before codegen has a chance to run
@@ -23,14 +22,6 @@ func Execute() {
 	app.Flags = genCmd.Flags
 	app.Version = graphql.Version
 	app.Before = func(context *cli.Context) error {
-		pwd, err := os.Getwd()
-		if err != nil {
-			return fmt.Errorf("unable to determine current workding dir: %s\n", err.Error())
-		}
-
-		if !gopath.Contains(pwd) {
-			return fmt.Errorf("gqlgen must be run from inside your $GOPATH\n")
-		}
 		if context.Bool("verbose") {
 			log.SetFlags(0)
 		} else {
@@ -47,7 +38,7 @@ func Execute() {
 	}
 
 	if err := app.Run(os.Args); err != nil {
-		fmt.Fprintf(os.Stderr, err.Error())
+		fmt.Fprint(os.Stderr, err.Error())
 		os.Exit(1)
 	}
 }

vendor/github.com/99designs/gqlgen/codegen/args.go πŸ”—

@@ -0,0 +1,104 @@
+package codegen
+
+import (
+	"fmt"
+	"go/types"
+	"strings"
+
+	"github.com/99designs/gqlgen/codegen/config"
+	"github.com/99designs/gqlgen/codegen/templates"
+	"github.com/pkg/errors"
+	"github.com/vektah/gqlparser/ast"
+)
+
+type ArgSet struct {
+	Args     []*FieldArgument
+	FuncDecl string
+}
+
+type FieldArgument struct {
+	*ast.ArgumentDefinition
+	TypeReference *config.TypeReference
+	VarName       string      // The name of the var in go
+	Object        *Object     // A link back to the parent object
+	Default       interface{} // The default value
+	Directives    []*Directive
+	Value         interface{} // value set in Data
+}
+
+func (f *FieldArgument) Stream() bool {
+	return f.Object != nil && f.Object.Stream
+}
+
+func (b *builder) buildArg(obj *Object, arg *ast.ArgumentDefinition) (*FieldArgument, error) {
+	tr, err := b.Binder.TypeReference(arg.Type, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	argDirs, err := b.getDirectives(arg.Directives)
+	if err != nil {
+		return nil, err
+	}
+	newArg := FieldArgument{
+		ArgumentDefinition: arg,
+		TypeReference:      tr,
+		Object:             obj,
+		VarName:            templates.ToGoPrivate(arg.Name),
+		Directives:         argDirs,
+	}
+
+	if arg.DefaultValue != nil {
+		newArg.Default, err = arg.DefaultValue.Value(nil)
+		if err != nil {
+			return nil, errors.Errorf("default value is not valid: %s", err.Error())
+		}
+	}
+
+	return &newArg, nil
+}
+
+func (b *builder) bindArgs(field *Field, params *types.Tuple) error {
+	var newArgs []*FieldArgument
+
+nextArg:
+	for j := 0; j < params.Len(); j++ {
+		param := params.At(j)
+		for _, oldArg := range field.Args {
+			if strings.EqualFold(oldArg.Name, param.Name()) {
+				tr, err := b.Binder.TypeReference(oldArg.Type, param.Type())
+				if err != nil {
+					return err
+				}
+				oldArg.TypeReference = tr
+
+				newArgs = append(newArgs, oldArg)
+				continue nextArg
+			}
+		}
+
+		// no matching arg found, abort
+		return fmt.Errorf("arg %s not in schema", param.Name())
+	}
+
+	field.Args = newArgs
+	return nil
+}
+
+func (a *Data) Args() map[string][]*FieldArgument {
+	ret := map[string][]*FieldArgument{}
+	for _, o := range a.Objects {
+		for _, f := range o.Fields {
+			if len(f.Args) > 0 {
+				ret[f.ArgsFunc()] = f.Args
+			}
+		}
+	}
+
+	for _, d := range a.Directives {
+		if len(d.Args) > 0 {
+			ret[d.ArgsFunc()] = d.Args
+		}
+	}
+	return ret
+}

vendor/github.com/99designs/gqlgen/codegen/args.gotpl πŸ”—

@@ -0,0 +1,43 @@
+{{ range $name, $args := .Args }}
+func (ec *executionContext) {{ $name }}(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
+	var err error
+	args := map[string]interface{}{}
+	{{- range $i, $arg := . }}
+		var arg{{$i}} {{ $arg.TypeReference.GO | ref}}
+		if tmp, ok := rawArgs[{{$arg.Name|quote}}]; ok {
+			{{- if $arg.Directives }}
+				getArg0 := func(ctx context.Context) (interface{}, error) { return ec.{{ $arg.TypeReference.UnmarshalFunc }}(ctx, tmp) }
+
+				{{- range $i, $directive := $arg.Directives }}
+					getArg{{add $i 1}} := func(ctx context.Context) (res interface{}, err error) {
+						{{- range $dArg := $directive.Args }}
+							{{- if and $dArg.TypeReference.IsPtr ( notNil "Value" $dArg ) }}
+								{{ $dArg.VarName }} := {{ $dArg.Value | dump }}
+							{{- end }}
+						{{- end }}
+						n := getArg{{$i}}
+						return ec.directives.{{$directive.Name|ucFirst}}({{$directive.ResolveArgs "tmp" "n" }})
+					}
+				{{- end }}
+
+				tmp, err = getArg{{$arg.Directives|len}}(ctx)
+				if err != nil {
+					return nil, err
+				}
+				if data, ok := tmp.({{ $arg.TypeReference.GO | ref }}) ; ok {
+					arg{{$i}} = data
+				} else {
+					return nil, fmt.Errorf(`unexpected type %T from directive, should be {{ $arg.TypeReference.GO }}`, tmp)
+				}
+			{{- else }}
+				arg{{$i}}, err = ec.{{ $arg.TypeReference.UnmarshalFunc }}(ctx, tmp)
+				if err != nil {
+					return nil, err
+				}
+			{{- end }}
+		}
+		args[{{$arg.Name|quote}}] = arg{{$i}}
+	{{- end }}
+	return args, nil
+}
+{{ end }}

vendor/github.com/99designs/gqlgen/codegen/build.go πŸ”—

@@ -1,194 +0,0 @@
-package codegen
-
-import (
-	"fmt"
-	"go/build"
-	"go/types"
-	"os"
-
-	"github.com/pkg/errors"
-	"golang.org/x/tools/go/loader"
-)
-
-type Build struct {
-	PackageName      string
-	Objects          Objects
-	Inputs           Objects
-	Interfaces       []*Interface
-	QueryRoot        *Object
-	MutationRoot     *Object
-	SubscriptionRoot *Object
-	SchemaRaw        map[string]string
-	SchemaFilename   SchemaFilenames
-	Directives       []*Directive
-}
-
-type ModelBuild struct {
-	PackageName string
-	Models      []Model
-	Enums       []Enum
-}
-
-type ResolverBuild struct {
-	PackageName   string
-	ResolverType  string
-	Objects       Objects
-	ResolverFound bool
-}
-
-type ServerBuild struct {
-	PackageName         string
-	ExecPackageName     string
-	ResolverPackageName string
-}
-
-// Create a list of models that need to be generated
-func (cfg *Config) models() (*ModelBuild, error) {
-	namedTypes := cfg.buildNamedTypes()
-
-	progLoader := cfg.newLoaderWithoutErrors()
-
-	prog, err := progLoader.Load()
-	if err != nil {
-		return nil, errors.Wrap(err, "loading failed")
-	}
-
-	cfg.bindTypes(namedTypes, cfg.Model.Dir(), prog)
-
-	models, err := cfg.buildModels(namedTypes, prog)
-	if err != nil {
-		return nil, err
-	}
-	return &ModelBuild{
-		PackageName: cfg.Model.Package,
-		Models:      models,
-		Enums:       cfg.buildEnums(namedTypes),
-	}, nil
-}
-
-// bind a schema together with some code to generate a Build
-func (cfg *Config) resolver() (*ResolverBuild, error) {
-	progLoader := cfg.newLoaderWithoutErrors()
-	progLoader.Import(cfg.Resolver.ImportPath())
-
-	prog, err := progLoader.Load()
-	if err != nil {
-		return nil, err
-	}
-
-	destDir := cfg.Resolver.Dir()
-
-	namedTypes := cfg.buildNamedTypes()
-
-	cfg.bindTypes(namedTypes, destDir, prog)
-
-	objects, err := cfg.buildObjects(namedTypes, prog)
-	if err != nil {
-		return nil, err
-	}
-
-	def, _ := findGoType(prog, cfg.Resolver.ImportPath(), cfg.Resolver.Type)
-	resolverFound := def != nil
-
-	return &ResolverBuild{
-		PackageName:   cfg.Resolver.Package,
-		Objects:       objects,
-		ResolverType:  cfg.Resolver.Type,
-		ResolverFound: resolverFound,
-	}, nil
-}
-
-func (cfg *Config) server(destDir string) *ServerBuild {
-	return &ServerBuild{
-		PackageName:         cfg.Resolver.Package,
-		ExecPackageName:     cfg.Exec.ImportPath(),
-		ResolverPackageName: cfg.Resolver.ImportPath(),
-	}
-}
-
-// bind a schema together with some code to generate a Build
-func (cfg *Config) bind() (*Build, error) {
-	namedTypes := cfg.buildNamedTypes()
-
-	progLoader := cfg.newLoaderWithoutErrors()
-	prog, err := progLoader.Load()
-	if err != nil {
-		return nil, errors.Wrap(err, "loading failed")
-	}
-
-	cfg.bindTypes(namedTypes, cfg.Exec.Dir(), prog)
-
-	objects, err := cfg.buildObjects(namedTypes, prog)
-	if err != nil {
-		return nil, err
-	}
-
-	inputs, err := cfg.buildInputs(namedTypes, prog)
-	if err != nil {
-		return nil, err
-	}
-	directives, err := cfg.buildDirectives(namedTypes)
-	if err != nil {
-		return nil, err
-	}
-
-	b := &Build{
-		PackageName:    cfg.Exec.Package,
-		Objects:        objects,
-		Interfaces:     cfg.buildInterfaces(namedTypes, prog),
-		Inputs:         inputs,
-		SchemaRaw:      cfg.SchemaStr,
-		SchemaFilename: cfg.SchemaFilename,
-		Directives:     directives,
-	}
-
-	if cfg.schema.Query != nil {
-		b.QueryRoot = b.Objects.ByName(cfg.schema.Query.Name)
-	} else {
-		return b, fmt.Errorf("query entry point missing")
-	}
-
-	if cfg.schema.Mutation != nil {
-		b.MutationRoot = b.Objects.ByName(cfg.schema.Mutation.Name)
-	}
-
-	if cfg.schema.Subscription != nil {
-		b.SubscriptionRoot = b.Objects.ByName(cfg.schema.Subscription.Name)
-	}
-	return b, nil
-}
-
-func (cfg *Config) validate() error {
-	progLoader := cfg.newLoaderWithErrors()
-	_, err := progLoader.Load()
-	return err
-}
-
-func (cfg *Config) newLoaderWithErrors() loader.Config {
-	conf := loader.Config{}
-
-	for _, pkg := range cfg.Models.referencedPackages() {
-		conf.Import(pkg)
-	}
-	return conf
-}
-
-func (cfg *Config) newLoaderWithoutErrors() loader.Config {
-	conf := cfg.newLoaderWithErrors()
-	conf.AllowErrors = true
-	conf.TypeChecker = types.Config{
-		Error: func(e error) {},
-	}
-	return conf
-}
-
-func resolvePkg(pkgName string) (string, error) {
-	cwd, _ := os.Getwd()
-
-	pkg, err := build.Default.Import(pkgName, cwd, build.FindOnly)
-	if err != nil {
-		return "", err
-	}
-
-	return pkg.ImportPath, nil
-}

vendor/github.com/99designs/gqlgen/codegen/codegen.go πŸ”—

@@ -1,179 +0,0 @@
-package codegen
-
-import (
-	"log"
-	"os"
-	"path/filepath"
-	"regexp"
-	"syscall"
-
-	"github.com/99designs/gqlgen/codegen/templates"
-	"github.com/pkg/errors"
-	"github.com/vektah/gqlparser"
-	"github.com/vektah/gqlparser/ast"
-	"github.com/vektah/gqlparser/gqlerror"
-)
-
-func Generate(cfg Config) error {
-	if err := cfg.normalize(); err != nil {
-		return err
-	}
-
-	_ = syscall.Unlink(cfg.Exec.Filename)
-	_ = syscall.Unlink(cfg.Model.Filename)
-
-	modelsBuild, err := cfg.models()
-	if err != nil {
-		return errors.Wrap(err, "model plan failed")
-	}
-	if len(modelsBuild.Models) > 0 || len(modelsBuild.Enums) > 0 {
-		if err = templates.RenderToFile("models.gotpl", cfg.Model.Filename, modelsBuild); err != nil {
-			return err
-		}
-
-		for _, model := range modelsBuild.Models {
-			modelCfg := cfg.Models[model.GQLType]
-			modelCfg.Model = cfg.Model.ImportPath() + "." + model.GoType
-			cfg.Models[model.GQLType] = modelCfg
-		}
-
-		for _, enum := range modelsBuild.Enums {
-			modelCfg := cfg.Models[enum.GQLType]
-			modelCfg.Model = cfg.Model.ImportPath() + "." + enum.GoType
-			cfg.Models[enum.GQLType] = modelCfg
-		}
-	}
-
-	build, err := cfg.bind()
-	if err != nil {
-		return errors.Wrap(err, "exec plan failed")
-	}
-
-	if err := templates.RenderToFile("generated.gotpl", cfg.Exec.Filename, build); err != nil {
-		return err
-	}
-
-	if cfg.Resolver.IsDefined() {
-		if err := generateResolver(cfg); err != nil {
-			return errors.Wrap(err, "generating resolver failed")
-		}
-	}
-
-	if err := cfg.validate(); err != nil {
-		return errors.Wrap(err, "validation failed")
-	}
-
-	return nil
-}
-
-func GenerateServer(cfg Config, filename string) error {
-	if err := cfg.Exec.normalize(); err != nil {
-		return errors.Wrap(err, "exec")
-	}
-	if err := cfg.Resolver.normalize(); err != nil {
-		return errors.Wrap(err, "resolver")
-	}
-
-	serverFilename := abs(filename)
-	serverBuild := cfg.server(filepath.Dir(serverFilename))
-
-	if _, err := os.Stat(serverFilename); os.IsNotExist(errors.Cause(err)) {
-		err = templates.RenderToFile("server.gotpl", serverFilename, serverBuild)
-		if err != nil {
-			return errors.Wrap(err, "generate server failed")
-		}
-	} else {
-		log.Printf("Skipped server: %s already exists\n", serverFilename)
-	}
-	return nil
-}
-
-func generateResolver(cfg Config) error {
-	resolverBuild, err := cfg.resolver()
-	if err != nil {
-		return errors.Wrap(err, "resolver build failed")
-	}
-	filename := cfg.Resolver.Filename
-
-	if resolverBuild.ResolverFound {
-		log.Printf("Skipped resolver: %s.%s already exists\n", cfg.Resolver.ImportPath(), cfg.Resolver.Type)
-		return nil
-	}
-
-	if _, err := os.Stat(filename); os.IsNotExist(errors.Cause(err)) {
-		if err := templates.RenderToFile("resolver.gotpl", filename, resolverBuild); err != nil {
-			return err
-		}
-	} else {
-		log.Printf("Skipped resolver: %s already exists\n", filename)
-	}
-
-	return nil
-}
-
-func (cfg *Config) normalize() error {
-	if err := cfg.Model.normalize(); err != nil {
-		return errors.Wrap(err, "model")
-	}
-
-	if err := cfg.Exec.normalize(); err != nil {
-		return errors.Wrap(err, "exec")
-	}
-
-	if cfg.Resolver.IsDefined() {
-		if err := cfg.Resolver.normalize(); err != nil {
-			return errors.Wrap(err, "resolver")
-		}
-	}
-
-	builtins := TypeMap{
-		"__Directive":  {Model: "github.com/99designs/gqlgen/graphql/introspection.Directive"},
-		"__Type":       {Model: "github.com/99designs/gqlgen/graphql/introspection.Type"},
-		"__Field":      {Model: "github.com/99designs/gqlgen/graphql/introspection.Field"},
-		"__EnumValue":  {Model: "github.com/99designs/gqlgen/graphql/introspection.EnumValue"},
-		"__InputValue": {Model: "github.com/99designs/gqlgen/graphql/introspection.InputValue"},
-		"__Schema":     {Model: "github.com/99designs/gqlgen/graphql/introspection.Schema"},
-		"Int":          {Model: "github.com/99designs/gqlgen/graphql.Int"},
-		"Float":        {Model: "github.com/99designs/gqlgen/graphql.Float"},
-		"String":       {Model: "github.com/99designs/gqlgen/graphql.String"},
-		"Boolean":      {Model: "github.com/99designs/gqlgen/graphql.Boolean"},
-		"ID":           {Model: "github.com/99designs/gqlgen/graphql.ID"},
-		"Time":         {Model: "github.com/99designs/gqlgen/graphql.Time"},
-		"Map":          {Model: "github.com/99designs/gqlgen/graphql.Map"},
-	}
-
-	if cfg.Models == nil {
-		cfg.Models = TypeMap{}
-	}
-	for typeName, entry := range builtins {
-		if !cfg.Models.Exists(typeName) {
-			cfg.Models[typeName] = entry
-		}
-	}
-
-	var sources []*ast.Source
-	for _, filename := range cfg.SchemaFilename {
-		sources = append(sources, &ast.Source{Name: filename, Input: cfg.SchemaStr[filename]})
-	}
-
-	var err *gqlerror.Error
-	cfg.schema, err = gqlparser.LoadSchema(sources...)
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-var invalidPackageNameChar = regexp.MustCompile(`[^\w]`)
-
-func sanitizePackageName(pkg string) string {
-	return invalidPackageNameChar.ReplaceAllLiteralString(filepath.Base(pkg), "_")
-}
-
-func abs(path string) string {
-	absPath, err := filepath.Abs(path)
-	if err != nil {
-		panic(err)
-	}
-	return filepath.ToSlash(absPath)
-}

vendor/github.com/99designs/gqlgen/codegen/config.go πŸ”—

@@ -1,273 +0,0 @@
-package codegen
-
-import (
-	"fmt"
-	"go/build"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"sort"
-	"strings"
-
-	"github.com/99designs/gqlgen/internal/gopath"
-	"github.com/pkg/errors"
-	"github.com/vektah/gqlparser/ast"
-	"gopkg.in/yaml.v2"
-)
-
-var cfgFilenames = []string{".gqlgen.yml", "gqlgen.yml", "gqlgen.yaml"}
-
-// DefaultConfig creates a copy of the default config
-func DefaultConfig() *Config {
-	return &Config{
-		SchemaFilename: SchemaFilenames{"schema.graphql"},
-		SchemaStr:      map[string]string{},
-		Model:          PackageConfig{Filename: "models_gen.go"},
-		Exec:           PackageConfig{Filename: "generated.go"},
-	}
-}
-
-// LoadConfigFromDefaultLocations looks for a config file in the current directory, and all parent directories
-// walking up the tree. The closest config file will be returned.
-func LoadConfigFromDefaultLocations() (*Config, error) {
-	cfgFile, err := findCfg()
-	if err != nil {
-		return nil, err
-	}
-
-	err = os.Chdir(filepath.Dir(cfgFile))
-	if err != nil {
-		return nil, errors.Wrap(err, "unable to enter config dir")
-	}
-	return LoadConfig(cfgFile)
-}
-
-// LoadConfig reads the gqlgen.yml config file
-func LoadConfig(filename string) (*Config, error) {
-	config := DefaultConfig()
-
-	b, err := ioutil.ReadFile(filename)
-	if err != nil {
-		return nil, errors.Wrap(err, "unable to read config")
-	}
-
-	if err := yaml.UnmarshalStrict(b, config); err != nil {
-		return nil, errors.Wrap(err, "unable to parse config")
-	}
-
-	preGlobbing := config.SchemaFilename
-	config.SchemaFilename = SchemaFilenames{}
-	for _, f := range preGlobbing {
-		matches, err := filepath.Glob(f)
-		if err != nil {
-			return nil, errors.Wrapf(err, "failed to glob schema filename %s", f)
-		}
-
-		for _, m := range matches {
-			if config.SchemaFilename.Has(m) {
-				continue
-			}
-			config.SchemaFilename = append(config.SchemaFilename, m)
-		}
-	}
-
-	config.FilePath = filename
-	config.SchemaStr = map[string]string{}
-
-	return config, nil
-}
-
-type Config struct {
-	SchemaFilename SchemaFilenames   `yaml:"schema,omitempty"`
-	SchemaStr      map[string]string `yaml:"-"`
-	Exec           PackageConfig     `yaml:"exec"`
-	Model          PackageConfig     `yaml:"model"`
-	Resolver       PackageConfig     `yaml:"resolver,omitempty"`
-	Models         TypeMap           `yaml:"models,omitempty"`
-	StructTag      string            `yaml:"struct_tag,omitempty"`
-
-	FilePath string `yaml:"-"`
-
-	schema *ast.Schema `yaml:"-"`
-}
-
-type PackageConfig struct {
-	Filename string `yaml:"filename,omitempty"`
-	Package  string `yaml:"package,omitempty"`
-	Type     string `yaml:"type,omitempty"`
-}
-
-type TypeMapEntry struct {
-	Model  string                  `yaml:"model"`
-	Fields map[string]TypeMapField `yaml:"fields,omitempty"`
-}
-
-type TypeMapField struct {
-	Resolver  bool   `yaml:"resolver"`
-	FieldName string `yaml:"fieldName"`
-}
-
-type SchemaFilenames []string
-
-func (a *SchemaFilenames) UnmarshalYAML(unmarshal func(interface{}) error) error {
-	var single string
-	err := unmarshal(&single)
-	if err == nil {
-		*a = []string{single}
-		return nil
-	}
-
-	var multi []string
-	err = unmarshal(&multi)
-	if err != nil {
-		return err
-	}
-
-	*a = multi
-	return nil
-}
-
-func (a SchemaFilenames) Has(file string) bool {
-	for _, existing := range a {
-		if existing == file {
-			return true
-		}
-	}
-	return false
-}
-
-func (c *PackageConfig) normalize() error {
-	if c.Filename == "" {
-		return errors.New("Filename is required")
-	}
-	c.Filename = abs(c.Filename)
-	// If Package is not set, first attempt to load the package at the output dir. If that fails
-	// fallback to just the base dir name of the output filename.
-	if c.Package == "" {
-		cwd, _ := os.Getwd()
-		pkg, _ := build.Default.Import(c.ImportPath(), cwd, 0)
-		if pkg.Name != "" {
-			c.Package = pkg.Name
-		} else {
-			c.Package = filepath.Base(c.Dir())
-		}
-	}
-	c.Package = sanitizePackageName(c.Package)
-	return nil
-}
-
-func (c *PackageConfig) ImportPath() string {
-	return gopath.MustDir2Import(c.Dir())
-}
-
-func (c *PackageConfig) Dir() string {
-	return filepath.Dir(c.Filename)
-}
-
-func (c *PackageConfig) Check() error {
-	if strings.ContainsAny(c.Package, "./\\") {
-		return fmt.Errorf("package should be the output package name only, do not include the output filename")
-	}
-	if c.Filename != "" && !strings.HasSuffix(c.Filename, ".go") {
-		return fmt.Errorf("filename should be path to a go source file")
-	}
-	return nil
-}
-
-func (c *PackageConfig) IsDefined() bool {
-	return c.Filename != ""
-}
-
-func (cfg *Config) Check() error {
-	if err := cfg.Models.Check(); err != nil {
-		return errors.Wrap(err, "config.models")
-	}
-	if err := cfg.Exec.Check(); err != nil {
-		return errors.Wrap(err, "config.exec")
-	}
-	if err := cfg.Model.Check(); err != nil {
-		return errors.Wrap(err, "config.model")
-	}
-	if err := cfg.Resolver.Check(); err != nil {
-		return errors.Wrap(err, "config.resolver")
-	}
-	return nil
-}
-
-type TypeMap map[string]TypeMapEntry
-
-func (tm TypeMap) Exists(typeName string) bool {
-	_, ok := tm[typeName]
-	return ok
-}
-
-func (tm TypeMap) Check() error {
-	for typeName, entry := range tm {
-		if strings.LastIndex(entry.Model, ".") < strings.LastIndex(entry.Model, "/") {
-			return fmt.Errorf("model %s: invalid type specifier \"%s\" - you need to specify a struct to map to", typeName, entry.Model)
-		}
-	}
-	return nil
-}
-
-func (tm TypeMap) referencedPackages() []string {
-	var pkgs []string
-
-	for _, typ := range tm {
-		if typ.Model == "map[string]interface{}" {
-			continue
-		}
-		pkg, _ := pkgAndType(typ.Model)
-		if pkg == "" || inStrSlice(pkgs, pkg) {
-			continue
-		}
-		pkgs = append(pkgs, pkg)
-	}
-
-	sort.Slice(pkgs, func(i, j int) bool {
-		return pkgs[i] > pkgs[j]
-	})
-	return pkgs
-}
-
-func inStrSlice(haystack []string, needle string) bool {
-	for _, v := range haystack {
-		if needle == v {
-			return true
-		}
-	}
-
-	return false
-}
-
-// findCfg searches for the config file in this directory and all parents up the tree
-// looking for the closest match
-func findCfg() (string, error) {
-	dir, err := os.Getwd()
-	if err != nil {
-		return "", errors.Wrap(err, "unable to get working dir to findCfg")
-	}
-
-	cfg := findCfgInDir(dir)
-
-	for cfg == "" && dir != filepath.Dir(dir) {
-		dir = filepath.Dir(dir)
-		cfg = findCfgInDir(dir)
-	}
-
-	if cfg == "" {
-		return "", os.ErrNotExist
-	}
-
-	return cfg, nil
-}
-
-func findCfgInDir(dir string) string {
-	for _, cfgName := range cfgFilenames {
-		path := filepath.Join(dir, cfgName)
-		if _, err := os.Stat(path); err == nil {
-			return path
-		}
-	}
-	return ""
-}

vendor/github.com/99designs/gqlgen/codegen/config/binder.go πŸ”—

@@ -0,0 +1,451 @@
+package config
+
+import (
+	"fmt"
+	"go/token"
+	"go/types"
+
+	"github.com/99designs/gqlgen/codegen/templates"
+	"github.com/99designs/gqlgen/internal/code"
+	"github.com/pkg/errors"
+	"github.com/vektah/gqlparser/ast"
+	"golang.org/x/tools/go/packages"
+)
+
+// Binder connects graphql types to golang types using static analysis
+type Binder struct {
+	pkgs       []*packages.Package
+	schema     *ast.Schema
+	cfg        *Config
+	References []*TypeReference
+}
+
+func (c *Config) NewBinder(s *ast.Schema) (*Binder, error) {
+	pkgs, err := packages.Load(&packages.Config{Mode: packages.LoadTypes | packages.LoadSyntax}, c.Models.ReferencedPackages()...)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, p := range pkgs {
+		for _, e := range p.Errors {
+			if e.Kind == packages.ListError {
+				return nil, p.Errors[0]
+			}
+		}
+	}
+
+	return &Binder{
+		pkgs:   pkgs,
+		schema: s,
+		cfg:    c,
+	}, nil
+}
+
+func (b *Binder) TypePosition(typ types.Type) token.Position {
+	named, isNamed := typ.(*types.Named)
+	if !isNamed {
+		return token.Position{
+			Filename: "unknown",
+		}
+	}
+
+	return b.ObjectPosition(named.Obj())
+}
+
+func (b *Binder) ObjectPosition(typ types.Object) token.Position {
+	if typ == nil {
+		return token.Position{
+			Filename: "unknown",
+		}
+	}
+	pkg := b.getPkg(typ.Pkg().Path())
+	return pkg.Fset.Position(typ.Pos())
+}
+
+func (b *Binder) FindType(pkgName string, typeName string) (types.Type, error) {
+	obj, err := b.FindObject(pkgName, typeName)
+	if err != nil {
+		return nil, err
+	}
+
+	if fun, isFunc := obj.(*types.Func); isFunc {
+		return fun.Type().(*types.Signature).Params().At(0).Type(), nil
+	}
+	return obj.Type(), nil
+}
+
+func (b *Binder) getPkg(find string) *packages.Package {
+	for _, p := range b.pkgs {
+		if code.NormalizeVendor(find) == code.NormalizeVendor(p.PkgPath) {
+			return p
+		}
+	}
+	return nil
+}
+
+var MapType = types.NewMap(types.Typ[types.String], types.NewInterfaceType(nil, nil).Complete())
+var InterfaceType = types.NewInterfaceType(nil, nil)
+
+func (b *Binder) DefaultUserObject(name string) (types.Type, error) {
+	models := b.cfg.Models[name].Model
+	if len(models) == 0 {
+		return nil, fmt.Errorf(name + " not found in typemap")
+	}
+
+	if models[0] == "map[string]interface{}" {
+		return MapType, nil
+	}
+
+	if models[0] == "interface{}" {
+		return InterfaceType, nil
+	}
+
+	pkgName, typeName := code.PkgAndType(models[0])
+	if pkgName == "" {
+		return nil, fmt.Errorf("missing package name for %s", name)
+	}
+
+	obj, err := b.FindObject(pkgName, typeName)
+	if err != nil {
+		return nil, err
+	}
+
+	return obj.Type(), nil
+}
+
+func (b *Binder) FindObject(pkgName string, typeName string) (types.Object, error) {
+	if pkgName == "" {
+		return nil, fmt.Errorf("package cannot be nil")
+	}
+	fullName := typeName
+	if pkgName != "" {
+		fullName = pkgName + "." + typeName
+	}
+
+	pkg := b.getPkg(pkgName)
+	if pkg == nil {
+		return nil, errors.Errorf("required package was not loaded: %s", fullName)
+	}
+
+	// function based marshalers take precedence
+	for astNode, def := range pkg.TypesInfo.Defs {
+		// only look at defs in the top scope
+		if def == nil || def.Parent() == nil || def.Parent() != pkg.Types.Scope() {
+			continue
+		}
+
+		if astNode.Name == "Marshal"+typeName {
+			return def, nil
+		}
+	}
+
+	// then look for types directly
+	for astNode, def := range pkg.TypesInfo.Defs {
+		// only look at defs in the top scope
+		if def == nil || def.Parent() == nil || def.Parent() != pkg.Types.Scope() {
+			continue
+		}
+
+		if astNode.Name == typeName {
+			return def, nil
+		}
+	}
+
+	return nil, errors.Errorf("unable to find type %s\n", fullName)
+}
+
+func (b *Binder) PointerTo(ref *TypeReference) *TypeReference {
+	newRef := &TypeReference{
+		GO:          types.NewPointer(ref.GO),
+		GQL:         ref.GQL,
+		CastType:    ref.CastType,
+		Definition:  ref.Definition,
+		Unmarshaler: ref.Unmarshaler,
+		Marshaler:   ref.Marshaler,
+		IsMarshaler: ref.IsMarshaler,
+	}
+
+	b.References = append(b.References, newRef)
+	return newRef
+}
+
+// TypeReference is used by args and field types. The Definition can refer to both input and output types.
+type TypeReference struct {
+	Definition  *ast.Definition
+	GQL         *ast.Type
+	GO          types.Type
+	CastType    types.Type  // Before calling marshalling functions cast from/to this base type
+	Marshaler   *types.Func // When using external marshalling functions this will point to the Marshal function
+	Unmarshaler *types.Func // When using external marshalling functions this will point to the Unmarshal function
+	IsMarshaler bool        // Does the type implement graphql.Marshaler and graphql.Unmarshaler
+}
+
+func (ref *TypeReference) Elem() *TypeReference {
+	if p, isPtr := ref.GO.(*types.Pointer); isPtr {
+		return &TypeReference{
+			GO:          p.Elem(),
+			GQL:         ref.GQL,
+			CastType:    ref.CastType,
+			Definition:  ref.Definition,
+			Unmarshaler: ref.Unmarshaler,
+			Marshaler:   ref.Marshaler,
+			IsMarshaler: ref.IsMarshaler,
+		}
+	}
+
+	if ref.IsSlice() {
+		return &TypeReference{
+			GO:          ref.GO.(*types.Slice).Elem(),
+			GQL:         ref.GQL.Elem,
+			CastType:    ref.CastType,
+			Definition:  ref.Definition,
+			Unmarshaler: ref.Unmarshaler,
+			Marshaler:   ref.Marshaler,
+			IsMarshaler: ref.IsMarshaler,
+		}
+	}
+	return nil
+}
+
+func (t *TypeReference) IsPtr() bool {
+	_, isPtr := t.GO.(*types.Pointer)
+	return isPtr
+}
+
+func (t *TypeReference) IsNilable() bool {
+	_, isPtr := t.GO.(*types.Pointer)
+	_, isMap := t.GO.(*types.Map)
+	_, isInterface := t.GO.(*types.Interface)
+	return isPtr || isMap || isInterface
+}
+
+func (t *TypeReference) IsSlice() bool {
+	_, isSlice := t.GO.(*types.Slice)
+	return t.GQL.Elem != nil && isSlice
+}
+
+func (t *TypeReference) IsNamed() bool {
+	_, isSlice := t.GO.(*types.Named)
+	return isSlice
+}
+
+func (t *TypeReference) IsStruct() bool {
+	_, isStruct := t.GO.Underlying().(*types.Struct)
+	return isStruct
+}
+
+func (t *TypeReference) IsScalar() bool {
+	return t.Definition.Kind == ast.Scalar
+}
+
+func (t *TypeReference) HasIsZero() bool {
+	it := t.GO
+	if ptr, isPtr := it.(*types.Pointer); isPtr {
+		it = ptr.Elem()
+	}
+	namedType, ok := it.(*types.Named)
+	if !ok {
+		return false
+	}
+
+	for i := 0; i < namedType.NumMethods(); i++ {
+		switch namedType.Method(i).Name() {
+		case "IsZero":
+			return true
+		}
+	}
+	return false
+}
+
+func (t *TypeReference) UniquenessKey() string {
+	var nullability = "O"
+	if t.GQL.NonNull {
+		nullability = "N"
+	}
+
+	return nullability + t.Definition.Name + "2" + templates.TypeIdentifier(t.GO)
+}
+
+func (t *TypeReference) MarshalFunc() string {
+	if t.Definition == nil {
+		panic(errors.New("Definition missing for " + t.GQL.Name()))
+	}
+
+	if t.Definition.Kind == ast.InputObject {
+		return ""
+	}
+
+	return "marshal" + t.UniquenessKey()
+}
+
+func (t *TypeReference) UnmarshalFunc() string {
+	if t.Definition == nil {
+		panic(errors.New("Definition missing for " + t.GQL.Name()))
+	}
+
+	if !t.Definition.IsInputType() {
+		return ""
+	}
+
+	return "unmarshal" + t.UniquenessKey()
+}
+
+func (b *Binder) PushRef(ret *TypeReference) {
+	b.References = append(b.References, ret)
+}
+
+func isMap(t types.Type) bool {
+	if t == nil {
+		return true
+	}
+	_, ok := t.(*types.Map)
+	return ok
+}
+
+func isIntf(t types.Type) bool {
+	if t == nil {
+		return true
+	}
+	_, ok := t.(*types.Interface)
+	return ok
+}
+
+func (b *Binder) TypeReference(schemaType *ast.Type, bindTarget types.Type) (ret *TypeReference, err error) {
+	var pkgName, typeName string
+	def := b.schema.Types[schemaType.Name()]
+	defer func() {
+		if err == nil && ret != nil {
+			b.PushRef(ret)
+		}
+	}()
+
+	if len(b.cfg.Models[schemaType.Name()].Model) == 0 {
+		return nil, fmt.Errorf("%s was not found", schemaType.Name())
+	}
+
+	for _, model := range b.cfg.Models[schemaType.Name()].Model {
+		if model == "map[string]interface{}" {
+			if !isMap(bindTarget) {
+				continue
+			}
+			return &TypeReference{
+				Definition: def,
+				GQL:        schemaType,
+				GO:         MapType,
+			}, nil
+		}
+
+		if model == "interface{}" {
+			if !isIntf(bindTarget) {
+				continue
+			}
+			return &TypeReference{
+				Definition: def,
+				GQL:        schemaType,
+				GO:         InterfaceType,
+			}, nil
+		}
+
+		pkgName, typeName = code.PkgAndType(model)
+		if pkgName == "" {
+			return nil, fmt.Errorf("missing package name for %s", schemaType.Name())
+		}
+
+		ref := &TypeReference{
+			Definition: def,
+			GQL:        schemaType,
+		}
+
+		obj, err := b.FindObject(pkgName, typeName)
+		if err != nil {
+			return nil, err
+		}
+
+		if fun, isFunc := obj.(*types.Func); isFunc {
+			ref.GO = fun.Type().(*types.Signature).Params().At(0).Type()
+			ref.Marshaler = fun
+			ref.Unmarshaler = types.NewFunc(0, fun.Pkg(), "Unmarshal"+typeName, nil)
+		} else if hasMethod(obj.Type(), "MarshalGQL") && hasMethod(obj.Type(), "UnmarshalGQL") {
+			ref.GO = obj.Type()
+			ref.IsMarshaler = true
+		} else if underlying := basicUnderlying(obj.Type()); underlying != nil && underlying.Kind() == types.String {
+			// Special case for named types wrapping strings. Used by default enum implementations.
+
+			ref.GO = obj.Type()
+			ref.CastType = underlying
+
+			underlyingRef, err := b.TypeReference(&ast.Type{NamedType: "String"}, nil)
+			if err != nil {
+				return nil, err
+			}
+
+			ref.Marshaler = underlyingRef.Marshaler
+			ref.Unmarshaler = underlyingRef.Unmarshaler
+		} else {
+			ref.GO = obj.Type()
+		}
+
+		ref.GO = b.CopyModifiersFromAst(schemaType, ref.GO)
+
+		if bindTarget != nil {
+			if err = code.CompatibleTypes(ref.GO, bindTarget); err != nil {
+				continue
+			}
+			ref.GO = bindTarget
+		}
+
+		return ref, nil
+	}
+
+	return nil, fmt.Errorf("%s has type compatible with %s", schemaType.Name(), bindTarget.String())
+}
+
+func (b *Binder) CopyModifiersFromAst(t *ast.Type, base types.Type) types.Type {
+	if t.Elem != nil {
+		return types.NewSlice(b.CopyModifiersFromAst(t.Elem, base))
+	}
+
+	var isInterface bool
+	if named, ok := base.(*types.Named); ok {
+		_, isInterface = named.Underlying().(*types.Interface)
+	}
+
+	if !isInterface && !t.NonNull {
+		return types.NewPointer(base)
+	}
+
+	return base
+}
+
+func hasMethod(it types.Type, name string) bool {
+	if ptr, isPtr := it.(*types.Pointer); isPtr {
+		it = ptr.Elem()
+	}
+	namedType, ok := it.(*types.Named)
+	if !ok {
+		return false
+	}
+
+	for i := 0; i < namedType.NumMethods(); i++ {
+		if namedType.Method(i).Name() == name {
+			return true
+		}
+	}
+	return false
+}
+
+func basicUnderlying(it types.Type) *types.Basic {
+	if ptr, isPtr := it.(*types.Pointer); isPtr {
+		it = ptr.Elem()
+	}
+	namedType, ok := it.(*types.Named)
+	if !ok {
+		return nil
+	}
+
+	if basic, ok := namedType.Underlying().(*types.Basic); ok {
+		return basic
+	}
+
+	return nil
+}

vendor/github.com/99designs/gqlgen/codegen/config/config.go πŸ”—

@@ -0,0 +1,408 @@
+package config
+
+import (
+	"fmt"
+	"go/types"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	"github.com/99designs/gqlgen/internal/code"
+	"github.com/pkg/errors"
+	"github.com/vektah/gqlparser"
+	"github.com/vektah/gqlparser/ast"
+	yaml "gopkg.in/yaml.v2"
+)
+
+type Config struct {
+	SchemaFilename StringList    `yaml:"schema,omitempty"`
+	Exec           PackageConfig `yaml:"exec"`
+	Model          PackageConfig `yaml:"model"`
+	Resolver       PackageConfig `yaml:"resolver,omitempty"`
+	Models         TypeMap       `yaml:"models,omitempty"`
+	StructTag      string        `yaml:"struct_tag,omitempty"`
+}
+
+var cfgFilenames = []string{".gqlgen.yml", "gqlgen.yml", "gqlgen.yaml"}
+
+// DefaultConfig creates a copy of the default config
+func DefaultConfig() *Config {
+	return &Config{
+		SchemaFilename: StringList{"schema.graphql"},
+		Model:          PackageConfig{Filename: "models_gen.go"},
+		Exec:           PackageConfig{Filename: "generated.go"},
+	}
+}
+
+// LoadConfigFromDefaultLocations looks for a config file in the current directory, and all parent directories
+// walking up the tree. The closest config file will be returned.
+func LoadConfigFromDefaultLocations() (*Config, error) {
+	cfgFile, err := findCfg()
+	if err != nil {
+		return nil, err
+	}
+
+	err = os.Chdir(filepath.Dir(cfgFile))
+	if err != nil {
+		return nil, errors.Wrap(err, "unable to enter config dir")
+	}
+	return LoadConfig(cfgFile)
+}
+
+// LoadConfig reads the gqlgen.yml config file
+func LoadConfig(filename string) (*Config, error) {
+	config := DefaultConfig()
+
+	b, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return nil, errors.Wrap(err, "unable to read config")
+	}
+
+	if err := yaml.UnmarshalStrict(b, config); err != nil {
+		return nil, errors.Wrap(err, "unable to parse config")
+	}
+
+	preGlobbing := config.SchemaFilename
+	config.SchemaFilename = StringList{}
+	for _, f := range preGlobbing {
+		matches, err := filepath.Glob(f)
+		if err != nil {
+			return nil, errors.Wrapf(err, "failed to glob schema filename %s", f)
+		}
+
+		for _, m := range matches {
+			if config.SchemaFilename.Has(m) {
+				continue
+			}
+			config.SchemaFilename = append(config.SchemaFilename, m)
+		}
+	}
+
+	return config, nil
+}
+
+type PackageConfig struct {
+	Filename string `yaml:"filename,omitempty"`
+	Package  string `yaml:"package,omitempty"`
+	Type     string `yaml:"type,omitempty"`
+}
+
+type TypeMapEntry struct {
+	Model  StringList              `yaml:"model"`
+	Fields map[string]TypeMapField `yaml:"fields,omitempty"`
+}
+
+type TypeMapField struct {
+	Resolver  bool   `yaml:"resolver"`
+	FieldName string `yaml:"fieldName"`
+}
+
+type StringList []string
+
+func (a *StringList) UnmarshalYAML(unmarshal func(interface{}) error) error {
+	var single string
+	err := unmarshal(&single)
+	if err == nil {
+		*a = []string{single}
+		return nil
+	}
+
+	var multi []string
+	err = unmarshal(&multi)
+	if err != nil {
+		return err
+	}
+
+	*a = multi
+	return nil
+}
+
+func (a StringList) Has(file string) bool {
+	for _, existing := range a {
+		if existing == file {
+			return true
+		}
+	}
+	return false
+}
+
+func (c *PackageConfig) normalize() error {
+	if c.Filename == "" {
+		return errors.New("Filename is required")
+	}
+	c.Filename = abs(c.Filename)
+	// If Package is not set, first attempt to load the package at the output dir. If that fails
+	// fallback to just the base dir name of the output filename.
+	if c.Package == "" {
+		c.Package = code.NameForPackage(c.ImportPath())
+	}
+
+	return nil
+}
+
+func (c *PackageConfig) ImportPath() string {
+	return code.ImportPathForDir(c.Dir())
+}
+
+func (c *PackageConfig) Dir() string {
+	return filepath.Dir(c.Filename)
+}
+
+func (c *PackageConfig) Check() error {
+	if strings.ContainsAny(c.Package, "./\\") {
+		return fmt.Errorf("package should be the output package name only, do not include the output filename")
+	}
+	if c.Filename != "" && !strings.HasSuffix(c.Filename, ".go") {
+		return fmt.Errorf("filename should be path to a go source file")
+	}
+
+	return c.normalize()
+}
+
+func (c *PackageConfig) Pkg() *types.Package {
+	return types.NewPackage(c.ImportPath(), c.Dir())
+}
+
+func (c *PackageConfig) IsDefined() bool {
+	return c.Filename != ""
+}
+
+func (c *Config) Check() error {
+	if err := c.Models.Check(); err != nil {
+		return errors.Wrap(err, "config.models")
+	}
+	if err := c.Exec.Check(); err != nil {
+		return errors.Wrap(err, "config.exec")
+	}
+	if err := c.Model.Check(); err != nil {
+		return errors.Wrap(err, "config.model")
+	}
+	if c.Resolver.IsDefined() {
+		if err := c.Resolver.Check(); err != nil {
+			return errors.Wrap(err, "config.resolver")
+		}
+	}
+
+	// check packages names against conflict, if present in the same dir
+	// and check filenames for uniqueness
+	packageConfigList := []PackageConfig{
+		c.Model,
+		c.Exec,
+		c.Resolver,
+	}
+	filesMap := make(map[string]bool)
+	pkgConfigsByDir := make(map[string]PackageConfig)
+	for _, current := range packageConfigList {
+		_, fileFound := filesMap[current.Filename]
+		if fileFound {
+			return fmt.Errorf("filename %s defined more than once", current.Filename)
+		}
+		filesMap[current.Filename] = true
+		previous, inSameDir := pkgConfigsByDir[current.Dir()]
+		if inSameDir && current.Package != previous.Package {
+			return fmt.Errorf("filenames %s and %s are in the same directory but have different package definitions", stripPath(current.Filename), stripPath(previous.Filename))
+		}
+		pkgConfigsByDir[current.Dir()] = current
+	}
+
+	return c.normalize()
+}
+
+func stripPath(path string) string {
+	return filepath.Base(path)
+}
+
+type TypeMap map[string]TypeMapEntry
+
+func (tm TypeMap) Exists(typeName string) bool {
+	_, ok := tm[typeName]
+	return ok
+}
+
+func (tm TypeMap) UserDefined(typeName string) bool {
+	m, ok := tm[typeName]
+	return ok && len(m.Model) > 0
+}
+
+func (tm TypeMap) Check() error {
+	for typeName, entry := range tm {
+		for _, model := range entry.Model {
+			if strings.LastIndex(model, ".") < strings.LastIndex(model, "/") {
+				return fmt.Errorf("model %s: invalid type specifier \"%s\" - you need to specify a struct to map to", typeName, entry.Model)
+			}
+		}
+	}
+	return nil
+}
+
+func (tm TypeMap) ReferencedPackages() []string {
+	var pkgs []string
+
+	for _, typ := range tm {
+		for _, model := range typ.Model {
+			if model == "map[string]interface{}" || model == "interface{}" {
+				continue
+			}
+			pkg, _ := code.PkgAndType(model)
+			if pkg == "" || inStrSlice(pkgs, pkg) {
+				continue
+			}
+			pkgs = append(pkgs, code.QualifyPackagePath(pkg))
+		}
+	}
+
+	sort.Slice(pkgs, func(i, j int) bool {
+		return pkgs[i] > pkgs[j]
+	})
+	return pkgs
+}
+
+func (tm TypeMap) Add(Name string, goType string) {
+	modelCfg := tm[Name]
+	modelCfg.Model = append(modelCfg.Model, goType)
+	tm[Name] = modelCfg
+}
+
+func inStrSlice(haystack []string, needle string) bool {
+	for _, v := range haystack {
+		if needle == v {
+			return true
+		}
+	}
+
+	return false
+}
+
+// findCfg searches for the config file in this directory and all parents up the tree
+// looking for the closest match
+func findCfg() (string, error) {
+	dir, err := os.Getwd()
+	if err != nil {
+		return "", errors.Wrap(err, "unable to get working dir to findCfg")
+	}
+
+	cfg := findCfgInDir(dir)
+
+	for cfg == "" && dir != filepath.Dir(dir) {
+		dir = filepath.Dir(dir)
+		cfg = findCfgInDir(dir)
+	}
+
+	if cfg == "" {
+		return "", os.ErrNotExist
+	}
+
+	return cfg, nil
+}
+
+func findCfgInDir(dir string) string {
+	for _, cfgName := range cfgFilenames {
+		path := filepath.Join(dir, cfgName)
+		if _, err := os.Stat(path); err == nil {
+			return path
+		}
+	}
+	return ""
+}
+
+func (c *Config) normalize() error {
+	if err := c.Model.normalize(); err != nil {
+		return errors.Wrap(err, "model")
+	}
+
+	if err := c.Exec.normalize(); err != nil {
+		return errors.Wrap(err, "exec")
+	}
+
+	if c.Resolver.IsDefined() {
+		if err := c.Resolver.normalize(); err != nil {
+			return errors.Wrap(err, "resolver")
+		}
+	}
+
+	if c.Models == nil {
+		c.Models = TypeMap{}
+	}
+
+	return nil
+}
+
+func (c *Config) InjectBuiltins(s *ast.Schema) {
+	builtins := TypeMap{
+		"__Directive":         {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Directive"}},
+		"__DirectiveLocation": {Model: StringList{"github.com/99designs/gqlgen/graphql.String"}},
+		"__Type":              {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Type"}},
+		"__TypeKind":          {Model: StringList{"github.com/99designs/gqlgen/graphql.String"}},
+		"__Field":             {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Field"}},
+		"__EnumValue":         {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.EnumValue"}},
+		"__InputValue":        {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.InputValue"}},
+		"__Schema":            {Model: StringList{"github.com/99designs/gqlgen/graphql/introspection.Schema"}},
+		"Float":               {Model: StringList{"github.com/99designs/gqlgen/graphql.Float"}},
+		"String":              {Model: StringList{"github.com/99designs/gqlgen/graphql.String"}},
+		"Boolean":             {Model: StringList{"github.com/99designs/gqlgen/graphql.Boolean"}},
+		"Int": {Model: StringList{
+			"github.com/99designs/gqlgen/graphql.Int",
+			"github.com/99designs/gqlgen/graphql.Int32",
+			"github.com/99designs/gqlgen/graphql.Int64",
+		}},
+		"ID": {
+			Model: StringList{
+				"github.com/99designs/gqlgen/graphql.ID",
+				"github.com/99designs/gqlgen/graphql.IntID",
+			},
+		},
+	}
+
+	for typeName, entry := range builtins {
+		if !c.Models.Exists(typeName) {
+			c.Models[typeName] = entry
+		}
+	}
+
+	// These are additional types that are injected if defined in the schema as scalars.
+	extraBuiltins := TypeMap{
+		"Time": {Model: StringList{"github.com/99designs/gqlgen/graphql.Time"}},
+		"Map":  {Model: StringList{"github.com/99designs/gqlgen/graphql.Map"}},
+	}
+
+	for typeName, entry := range extraBuiltins {
+		if t, ok := s.Types[typeName]; !c.Models.Exists(typeName) && ok && t.Kind == ast.Scalar {
+			c.Models[typeName] = entry
+		}
+	}
+}
+
+func (c *Config) LoadSchema() (*ast.Schema, map[string]string, error) {
+	schemaStrings := map[string]string{}
+
+	var sources []*ast.Source
+
+	for _, filename := range c.SchemaFilename {
+		filename = filepath.ToSlash(filename)
+		var err error
+		var schemaRaw []byte
+		schemaRaw, err = ioutil.ReadFile(filename)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, "unable to open schema: "+err.Error())
+			os.Exit(1)
+		}
+		schemaStrings[filename] = string(schemaRaw)
+		sources = append(sources, &ast.Source{Name: filename, Input: schemaStrings[filename]})
+	}
+
+	schema, err := gqlparser.LoadSchema(sources...)
+	if err != nil {
+		return nil, nil, err
+	}
+	return schema, schemaStrings, nil
+}
+
+func abs(path string) string {
+	absPath, err := filepath.Abs(path)
+	if err != nil {
+		panic(err)
+	}
+	return filepath.ToSlash(absPath)
+}

vendor/github.com/99designs/gqlgen/codegen/data.go πŸ”—

@@ -0,0 +1,168 @@
+package codegen
+
+import (
+	"fmt"
+	"sort"
+
+	"github.com/99designs/gqlgen/codegen/config"
+	"github.com/pkg/errors"
+	"github.com/vektah/gqlparser/ast"
+)
+
+// Data is a unified model of the code to be generated. Plugins may modify this structure to do things like implement
+// resolvers or directives automatically (eg grpc, validation)
+type Data struct {
+	Config          *config.Config
+	Schema          *ast.Schema
+	SchemaStr       map[string]string
+	Directives      map[string]*Directive
+	Objects         Objects
+	Inputs          Objects
+	Interfaces      map[string]*Interface
+	ReferencedTypes map[string]*config.TypeReference
+	ComplexityRoots map[string]*Object
+
+	QueryRoot        *Object
+	MutationRoot     *Object
+	SubscriptionRoot *Object
+}
+
+type builder struct {
+	Config     *config.Config
+	Schema     *ast.Schema
+	SchemaStr  map[string]string
+	Binder     *config.Binder
+	Directives map[string]*Directive
+}
+
+func BuildData(cfg *config.Config) (*Data, error) {
+	b := builder{
+		Config: cfg,
+	}
+
+	var err error
+	b.Schema, b.SchemaStr, err = cfg.LoadSchema()
+	if err != nil {
+		return nil, err
+	}
+
+	err = cfg.Check()
+	if err != nil {
+		return nil, err
+	}
+
+	cfg.InjectBuiltins(b.Schema)
+
+	b.Binder, err = b.Config.NewBinder(b.Schema)
+	if err != nil {
+		return nil, err
+	}
+
+	b.Directives, err = b.buildDirectives()
+	if err != nil {
+		return nil, err
+	}
+
+	dataDirectives := make(map[string]*Directive)
+	for name, d := range b.Directives {
+		if !d.Builtin {
+			dataDirectives[name] = d
+		}
+	}
+
+	s := Data{
+		Config:     cfg,
+		Directives: dataDirectives,
+		Schema:     b.Schema,
+		SchemaStr:  b.SchemaStr,
+		Interfaces: map[string]*Interface{},
+	}
+
+	for _, schemaType := range b.Schema.Types {
+		switch schemaType.Kind {
+		case ast.Object:
+			obj, err := b.buildObject(schemaType)
+			if err != nil {
+				return nil, errors.Wrap(err, "unable to build object definition")
+			}
+
+			s.Objects = append(s.Objects, obj)
+		case ast.InputObject:
+			input, err := b.buildObject(schemaType)
+			if err != nil {
+				return nil, errors.Wrap(err, "unable to build input definition")
+			}
+
+			s.Inputs = append(s.Inputs, input)
+
+		case ast.Union, ast.Interface:
+			s.Interfaces[schemaType.Name] = b.buildInterface(schemaType)
+		}
+	}
+
+	if s.Schema.Query != nil {
+		s.QueryRoot = s.Objects.ByName(s.Schema.Query.Name)
+	} else {
+		return nil, fmt.Errorf("query entry point missing")
+	}
+
+	if s.Schema.Mutation != nil {
+		s.MutationRoot = s.Objects.ByName(s.Schema.Mutation.Name)
+	}
+
+	if s.Schema.Subscription != nil {
+		s.SubscriptionRoot = s.Objects.ByName(s.Schema.Subscription.Name)
+	}
+
+	if err := b.injectIntrospectionRoots(&s); err != nil {
+		return nil, err
+	}
+
+	s.ReferencedTypes, err = b.buildTypes()
+	if err != nil {
+		return nil, err
+	}
+
+	sort.Slice(s.Objects, func(i, j int) bool {
+		return s.Objects[i].Definition.Name < s.Objects[j].Definition.Name
+	})
+
+	sort.Slice(s.Inputs, func(i, j int) bool {
+		return s.Inputs[i].Definition.Name < s.Inputs[j].Definition.Name
+	})
+
+	return &s, nil
+}
+
+func (b *builder) injectIntrospectionRoots(s *Data) error {
+	obj := s.Objects.ByName(b.Schema.Query.Name)
+	if obj == nil {
+		return fmt.Errorf("root query type must be defined")
+	}
+
+	__type, err := b.buildField(obj, &ast.FieldDefinition{
+		Name: "__type",
+		Type: ast.NamedType("__Type", nil),
+		Arguments: []*ast.ArgumentDefinition{
+			{
+				Name: "name",
+				Type: ast.NonNullNamedType("String", nil),
+			},
+		},
+	})
+	if err != nil {
+		return err
+	}
+
+	__schema, err := b.buildField(obj, &ast.FieldDefinition{
+		Name: "__schema",
+		Type: ast.NamedType("__Schema", nil),
+	})
+	if err != nil {
+		return err
+	}
+
+	obj.Fields = append(obj.Fields, __type, __schema)
+
+	return nil
+}

vendor/github.com/99designs/gqlgen/codegen/directive.go πŸ”—

@@ -4,11 +4,101 @@ import (
 	"fmt"
 	"strconv"
 	"strings"
+
+	"github.com/99designs/gqlgen/codegen/templates"
+	"github.com/pkg/errors"
+	"github.com/vektah/gqlparser/ast"
 )
 
 type Directive struct {
-	Name string
-	Args []FieldArgument
+	Name    string
+	Args    []*FieldArgument
+	Builtin bool
+}
+
+func (b *builder) buildDirectives() (map[string]*Directive, error) {
+	directives := make(map[string]*Directive, len(b.Schema.Directives))
+
+	for name, dir := range b.Schema.Directives {
+		if _, ok := directives[name]; ok {
+			return nil, errors.Errorf("directive with name %s already exists", name)
+		}
+
+		var builtin bool
+		if name == "skip" || name == "include" || name == "deprecated" {
+			builtin = true
+		}
+
+		var args []*FieldArgument
+		for _, arg := range dir.Arguments {
+			tr, err := b.Binder.TypeReference(arg.Type, nil)
+			if err != nil {
+				return nil, err
+			}
+
+			newArg := &FieldArgument{
+				ArgumentDefinition: arg,
+				TypeReference:      tr,
+				VarName:            templates.ToGoPrivate(arg.Name),
+			}
+
+			if arg.DefaultValue != nil {
+				var err error
+				newArg.Default, err = arg.DefaultValue.Value(nil)
+				if err != nil {
+					return nil, errors.Errorf("default value for directive argument %s(%s) is not valid: %s", dir.Name, arg.Name, err.Error())
+				}
+			}
+			args = append(args, newArg)
+		}
+
+		directives[name] = &Directive{
+			Name:    name,
+			Args:    args,
+			Builtin: builtin,
+		}
+	}
+
+	return directives, nil
+}
+
+func (b *builder) getDirectives(list ast.DirectiveList) ([]*Directive, error) {
+	dirs := make([]*Directive, len(list))
+	for i, d := range list {
+		argValues := make(map[string]interface{}, len(d.Arguments))
+		for _, da := range d.Arguments {
+			val, err := da.Value.Value(nil)
+			if err != nil {
+				return nil, err
+			}
+			argValues[da.Name] = val
+		}
+		def, ok := b.Directives[d.Name]
+		if !ok {
+			return nil, fmt.Errorf("directive %s not found", d.Name)
+		}
+
+		var args []*FieldArgument
+		for _, a := range def.Args {
+			value := a.Default
+			if argValue, ok := argValues[a.Name]; ok {
+				value = argValue
+			}
+			args = append(args, &FieldArgument{
+				ArgumentDefinition: a.ArgumentDefinition,
+				Value:              value,
+				VarName:            a.VarName,
+				TypeReference:      a.TypeReference,
+			})
+		}
+		dirs[i] = &Directive{
+			Name: d.Name,
+			Args: args,
+		}
+
+	}
+
+	return dirs, nil
 }
 
 func (d *Directive) ArgsFunc() string {
@@ -23,7 +113,28 @@ func (d *Directive) CallArgs() string {
 	args := []string{"ctx", "obj", "n"}
 
 	for _, arg := range d.Args {
-		args = append(args, "args["+strconv.Quote(arg.GQLName)+"].("+arg.Signature()+")")
+		args = append(args, "args["+strconv.Quote(arg.Name)+"].("+templates.CurrentImports.LookupType(arg.TypeReference.GO)+")")
+	}
+
+	return strings.Join(args, ", ")
+}
+
+func (d *Directive) ResolveArgs(obj string, next string) string {
+	args := []string{"ctx", obj, next}
+
+	for _, arg := range d.Args {
+		dArg := "&" + arg.VarName
+		if !arg.TypeReference.IsPtr() {
+			if arg.Value != nil {
+				dArg = templates.Dump(arg.Value)
+			} else {
+				dArg = templates.Dump(arg.Default)
+			}
+		} else if arg.Value == nil && arg.Default == nil {
+			dArg = "nil"
+		}
+
+		args = append(args, dArg)
 	}
 
 	return strings.Join(args, ", ")
@@ -33,7 +144,7 @@ func (d *Directive) Declaration() string {
 	res := ucFirst(d.Name) + " func(ctx context.Context, obj interface{}, next graphql.Resolver"
 
 	for _, arg := range d.Args {
-		res += fmt.Sprintf(", %s %s", arg.GoVarName, arg.Signature())
+		res += fmt.Sprintf(", %s %s", arg.Name, templates.CurrentImports.LookupType(arg.TypeReference.GO))
 	}
 
 	res += ") (res interface{}, err error)"

vendor/github.com/99designs/gqlgen/codegen/directive_build.go πŸ”—

@@ -1,48 +0,0 @@
-package codegen
-
-import (
-	"sort"
-
-	"github.com/pkg/errors"
-)
-
-func (cfg *Config) buildDirectives(types NamedTypes) ([]*Directive, error) {
-	var directives []*Directive
-
-	for name, dir := range cfg.schema.Directives {
-		if name == "skip" || name == "include" || name == "deprecated" {
-			continue
-		}
-
-		var args []FieldArgument
-		for _, arg := range dir.Arguments {
-			newArg := FieldArgument{
-				GQLName:   arg.Name,
-				Type:      types.getType(arg.Type),
-				GoVarName: sanitizeArgName(arg.Name),
-			}
-
-			if !newArg.Type.IsInput && !newArg.Type.IsScalar {
-				return nil, errors.Errorf("%s cannot be used as argument of directive %s(%s) only input and scalar types are allowed", arg.Type, dir.Name, arg.Name)
-			}
-
-			if arg.DefaultValue != nil {
-				var err error
-				newArg.Default, err = arg.DefaultValue.Value(nil)
-				if err != nil {
-					return nil, errors.Errorf("default value for directive argument %s(%s) is not valid: %s", dir.Name, arg.Name, err.Error())
-				}
-			}
-			args = append(args, newArg)
-		}
-
-		directives = append(directives, &Directive{
-			Name: name,
-			Args: args,
-		})
-	}
-
-	sort.Slice(directives, func(i, j int) bool { return directives[i].Name < directives[j].Name })
-
-	return directives, nil
-}

vendor/github.com/99designs/gqlgen/codegen/enum_build.go πŸ”—

@@ -1,39 +0,0 @@
-package codegen
-
-import (
-	"sort"
-	"strings"
-
-	"github.com/99designs/gqlgen/codegen/templates"
-	"github.com/vektah/gqlparser/ast"
-)
-
-func (cfg *Config) buildEnums(types NamedTypes) []Enum {
-	var enums []Enum
-
-	for _, typ := range cfg.schema.Types {
-		namedType := types[typ.Name]
-		if typ.Kind != ast.Enum || strings.HasPrefix(typ.Name, "__") || namedType.IsUserDefined {
-			continue
-		}
-
-		var values []EnumValue
-		for _, v := range typ.EnumValues {
-			values = append(values, EnumValue{v.Name, v.Description})
-		}
-
-		enum := Enum{
-			NamedType:   namedType,
-			Values:      values,
-			Description: typ.Description,
-		}
-		enum.GoType = templates.ToCamel(enum.GQLType)
-		enums = append(enums, enum)
-	}
-
-	sort.Slice(enums, func(i, j int) bool {
-		return enums[i].GQLType < enums[j].GQLType
-	})
-
-	return enums
-}

vendor/github.com/99designs/gqlgen/codegen/field.go πŸ”—

@@ -0,0 +1,394 @@
+package codegen
+
+import (
+	"fmt"
+	"go/types"
+	"log"
+	"reflect"
+	"strconv"
+	"strings"
+
+	"github.com/99designs/gqlgen/codegen/config"
+	"github.com/99designs/gqlgen/codegen/templates"
+	"github.com/pkg/errors"
+	"github.com/vektah/gqlparser/ast"
+)
+
+type Field struct {
+	*ast.FieldDefinition
+
+	TypeReference    *config.TypeReference
+	GoFieldType      GoFieldType      // The field type in go, if any
+	GoReceiverName   string           // The name of method & var receiver in go, if any
+	GoFieldName      string           // The name of the method or var in go, if any
+	IsResolver       bool             // Does this field need a resolver
+	Args             []*FieldArgument // A list of arguments to be passed to this field
+	MethodHasContext bool             // If this is bound to a go method, does the method also take a context
+	NoErr            bool             // If this is bound to a go method, does that method have an error as the second argument
+	Object           *Object          // A link back to the parent object
+	Default          interface{}      // The default value
+	Directives       []*Directive
+}
+
+func (b *builder) buildField(obj *Object, field *ast.FieldDefinition) (*Field, error) {
+	dirs, err := b.getDirectives(field.Directives)
+	if err != nil {
+		return nil, err
+	}
+
+	f := Field{
+		FieldDefinition: field,
+		Object:          obj,
+		Directives:      dirs,
+		GoFieldName:     templates.ToGo(field.Name),
+		GoFieldType:     GoFieldVariable,
+		GoReceiverName:  "obj",
+	}
+
+	if field.DefaultValue != nil {
+		var err error
+		f.Default, err = field.DefaultValue.Value(nil)
+		if err != nil {
+			return nil, errors.Errorf("default value %s is not valid: %s", field.Name, err.Error())
+		}
+	}
+
+	for _, arg := range field.Arguments {
+		newArg, err := b.buildArg(obj, arg)
+		if err != nil {
+			return nil, err
+		}
+		f.Args = append(f.Args, newArg)
+	}
+
+	if err = b.bindField(obj, &f); err != nil {
+		f.IsResolver = true
+		log.Println(err.Error())
+	}
+
+	if f.IsResolver && !f.TypeReference.IsPtr() && f.TypeReference.IsStruct() {
+		f.TypeReference = b.Binder.PointerTo(f.TypeReference)
+	}
+
+	return &f, nil
+}
+
+func (b *builder) bindField(obj *Object, f *Field) error {
+	defer func() {
+		if f.TypeReference == nil {
+			tr, err := b.Binder.TypeReference(f.Type, nil)
+			if err != nil {
+				panic(err)
+			}
+			f.TypeReference = tr
+		}
+	}()
+
+	switch {
+	case f.Name == "__schema":
+		f.GoFieldType = GoFieldMethod
+		f.GoReceiverName = "ec"
+		f.GoFieldName = "introspectSchema"
+		return nil
+	case f.Name == "__type":
+		f.GoFieldType = GoFieldMethod
+		f.GoReceiverName = "ec"
+		f.GoFieldName = "introspectType"
+		return nil
+	case obj.Root:
+		f.IsResolver = true
+		return nil
+	case b.Config.Models[obj.Name].Fields[f.Name].Resolver:
+		f.IsResolver = true
+		return nil
+	case obj.Type == config.MapType:
+		f.GoFieldType = GoFieldMap
+		return nil
+	case b.Config.Models[obj.Name].Fields[f.Name].FieldName != "":
+		f.GoFieldName = b.Config.Models[obj.Name].Fields[f.Name].FieldName
+	}
+
+	target, err := b.findBindTarget(obj.Type.(*types.Named), f.GoFieldName)
+	if err != nil {
+		return err
+	}
+
+	pos := b.Binder.ObjectPosition(target)
+
+	switch target := target.(type) {
+	case nil:
+		objPos := b.Binder.TypePosition(obj.Type)
+		return fmt.Errorf(
+			"%s:%d adding resolver method for %s.%s, nothing matched",
+			objPos.Filename,
+			objPos.Line,
+			obj.Name,
+			f.Name,
+		)
+
+	case *types.Func:
+		sig := target.Type().(*types.Signature)
+		if sig.Results().Len() == 1 {
+			f.NoErr = true
+		} else if sig.Results().Len() != 2 {
+			return fmt.Errorf("method has wrong number of args")
+		}
+		params := sig.Params()
+		// If the first argument is the context, remove it from the comparison and set
+		// the MethodHasContext flag so that the context will be passed to this model's method
+		if params.Len() > 0 && params.At(0).Type().String() == "context.Context" {
+			f.MethodHasContext = true
+			vars := make([]*types.Var, params.Len()-1)
+			for i := 1; i < params.Len(); i++ {
+				vars[i-1] = params.At(i)
+			}
+			params = types.NewTuple(vars...)
+		}
+
+		if err = b.bindArgs(f, params); err != nil {
+			return errors.Wrapf(err, "%s:%d", pos.Filename, pos.Line)
+		}
+
+		result := sig.Results().At(0)
+		tr, err := b.Binder.TypeReference(f.Type, result.Type())
+		if err != nil {
+			return err
+		}
+
+		// success, args and return type match. Bind to method
+		f.GoFieldType = GoFieldMethod
+		f.GoReceiverName = "obj"
+		f.GoFieldName = target.Name()
+		f.TypeReference = tr
+
+		return nil
+
+	case *types.Var:
+		tr, err := b.Binder.TypeReference(f.Type, target.Type())
+		if err != nil {
+			return err
+		}
+
+		// success, bind to var
+		f.GoFieldType = GoFieldVariable
+		f.GoReceiverName = "obj"
+		f.GoFieldName = target.Name()
+		f.TypeReference = tr
+
+		return nil
+	default:
+		panic(fmt.Errorf("unknown bind target %T for %s", target, f.Name))
+	}
+}
+
+// findField attempts to match the name to a struct field with the following
+// priorites:
+// 1. Any method with a matching name
+// 2. Any Fields with a struct tag (see config.StructTag)
+// 3. Any fields with a matching name
+// 4. Same logic again for embedded fields
+func (b *builder) findBindTarget(named *types.Named, name string) (types.Object, error) {
+	for i := 0; i < named.NumMethods(); i++ {
+		method := named.Method(i)
+		if !method.Exported() {
+			continue
+		}
+
+		if !strings.EqualFold(method.Name(), name) {
+			continue
+		}
+
+		return method, nil
+	}
+
+	strukt, ok := named.Underlying().(*types.Struct)
+	if !ok {
+		return nil, fmt.Errorf("not a struct")
+	}
+	return b.findBindStructTarget(strukt, name)
+}
+
+func (b *builder) findBindStructTarget(strukt *types.Struct, name string) (types.Object, error) {
+	// struct tags have the highest priority
+	if b.Config.StructTag != "" {
+		var foundField *types.Var
+		for i := 0; i < strukt.NumFields(); i++ {
+			field := strukt.Field(i)
+			if !field.Exported() {
+				continue
+			}
+			tags := reflect.StructTag(strukt.Tag(i))
+			if val, ok := tags.Lookup(b.Config.StructTag); ok && equalFieldName(val, name) {
+				if foundField != nil {
+					return nil, errors.Errorf("tag %s is ambigious; multiple fields have the same tag value of %s", b.Config.StructTag, val)
+				}
+
+				foundField = field
+			}
+		}
+		if foundField != nil {
+			return foundField, nil
+		}
+	}
+
+	// Then matching field names
+	for i := 0; i < strukt.NumFields(); i++ {
+		field := strukt.Field(i)
+		if !field.Exported() {
+			continue
+		}
+		if equalFieldName(field.Name(), name) { // aqui!
+			return field, nil
+		}
+	}
+
+	// Then look in embedded structs
+	for i := 0; i < strukt.NumFields(); i++ {
+		field := strukt.Field(i)
+		if !field.Exported() {
+			continue
+		}
+
+		if !field.Anonymous() {
+			continue
+		}
+
+		fieldType := field.Type()
+		if ptr, ok := fieldType.(*types.Pointer); ok {
+			fieldType = ptr.Elem()
+		}
+
+		switch fieldType := fieldType.(type) {
+		case *types.Named:
+			f, err := b.findBindTarget(fieldType, name)
+			if err != nil {
+				return nil, err
+			}
+			if f != nil {
+				return f, nil
+			}
+		case *types.Struct:
+			f, err := b.findBindStructTarget(fieldType, name)
+			if err != nil {
+				return nil, err
+			}
+			if f != nil {
+				return f, nil
+			}
+		default:
+			panic(fmt.Errorf("unknown embedded field type %T", field.Type()))
+		}
+	}
+
+	return nil, nil
+}
+
+func (f *Field) HasDirectives() bool {
+	return len(f.Directives) > 0
+}
+
+func (f *Field) IsReserved() bool {
+	return strings.HasPrefix(f.Name, "__")
+}
+
+func (f *Field) IsMethod() bool {
+	return f.GoFieldType == GoFieldMethod
+}
+
+func (f *Field) IsVariable() bool {
+	return f.GoFieldType == GoFieldVariable
+}
+
+func (f *Field) IsMap() bool {
+	return f.GoFieldType == GoFieldMap
+}
+
+func (f *Field) IsConcurrent() bool {
+	if f.Object.DisableConcurrency {
+		return false
+	}
+	return f.MethodHasContext || f.IsResolver
+}
+
+func (f *Field) GoNameUnexported() string {
+	return templates.ToGoPrivate(f.Name)
+}
+
+func (f *Field) ShortInvocation() string {
+	return fmt.Sprintf("%s().%s(%s)", f.Object.Definition.Name, f.GoFieldName, f.CallArgs())
+}
+
+func (f *Field) ArgsFunc() string {
+	if len(f.Args) == 0 {
+		return ""
+	}
+
+	return "field_" + f.Object.Definition.Name + "_" + f.Name + "_args"
+}
+
+func (f *Field) ResolverType() string {
+	if !f.IsResolver {
+		return ""
+	}
+
+	return fmt.Sprintf("%s().%s(%s)", f.Object.Definition.Name, f.GoFieldName, f.CallArgs())
+}
+
+func (f *Field) ShortResolverDeclaration() string {
+	res := "(ctx context.Context"
+
+	if !f.Object.Root {
+		res += fmt.Sprintf(", obj *%s", templates.CurrentImports.LookupType(f.Object.Type))
+	}
+	for _, arg := range f.Args {
+		res += fmt.Sprintf(", %s %s", arg.VarName, templates.CurrentImports.LookupType(arg.TypeReference.GO))
+	}
+
+	result := templates.CurrentImports.LookupType(f.TypeReference.GO)
+	if f.Object.Stream {
+		result = "<-chan " + result
+	}
+
+	res += fmt.Sprintf(") (%s, error)", result)
+	return res
+}
+
+func (f *Field) ComplexitySignature() string {
+	res := fmt.Sprintf("func(childComplexity int")
+	for _, arg := range f.Args {
+		res += fmt.Sprintf(", %s %s", arg.VarName, templates.CurrentImports.LookupType(arg.TypeReference.GO))
+	}
+	res += ") int"
+	return res
+}
+
+func (f *Field) ComplexityArgs() string {
+	var args []string
+	for _, arg := range f.Args {
+		args = append(args, "args["+strconv.Quote(arg.Name)+"].("+templates.CurrentImports.LookupType(arg.TypeReference.GO)+")")
+	}
+
+	return strings.Join(args, ", ")
+}
+
+func (f *Field) CallArgs() string {
+	var args []string
+
+	if f.IsResolver {
+		args = append(args, "rctx")
+
+		if !f.Object.Root {
+			args = append(args, "obj")
+		}
+	} else {
+		if f.MethodHasContext {
+			args = append(args, "ctx")
+		}
+	}
+
+	for _, arg := range f.Args {
+		args = append(args, "args["+strconv.Quote(arg.Name)+"].("+templates.CurrentImports.LookupType(arg.TypeReference.GO)+")")
+	}
+
+	return strings.Join(args, ", ")
+}

vendor/github.com/99designs/gqlgen/codegen/templates/field.gotpl β†’ vendor/github.com/99designs/gqlgen/codegen/field.gotpl πŸ”—

@@ -1,19 +1,19 @@
-{{ $field := . }}
-{{ $object := $field.Object }}
+{{- range $object := .Objects }}{{- range $field := $object.Fields }}
 
 {{- if $object.Stream }}
-	func (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField) func() graphql.Marshaler {
+	func (ec *executionContext) _{{$object.Name}}_{{$field.Name}}(ctx context.Context, field graphql.CollectedField) func() graphql.Marshaler {
+		ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{
+			Field: field,
+			Args:  nil,
+		})
 		{{- if $field.Args }}
 			rawArgs := field.ArgumentMap(ec.Variables)
-			args, err := {{ $field.ArgsFunc }}(rawArgs)
+			args, err := ec.{{ $field.ArgsFunc }}(ctx,rawArgs)
 			if err != nil {
 				ec.Error(ctx, err)
 				return nil
 			}
 		{{- end }}
-		ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{
-			Field: field,
-		})
 		// FIXME: subscriptions are missing request middleware stack https://github.com/99designs/gqlgen/issues/259
 		//          and Tracer stack
 		rctx := ctx
@@ -27,35 +27,51 @@
 			if !ok {
 				return nil
 			}
-			var out graphql.OrderedMap
-			out.Add(field.Alias, func() graphql.Marshaler { {{ $field.WriteJson }} }())
-			return &out
+			return graphql.WriterFunc(func(w io.Writer) {
+				w.Write([]byte{'{'})
+				graphql.MarshalString(field.Alias).MarshalGQL(w)
+				w.Write([]byte{':'})
+				ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res).MarshalGQL(w)
+				w.Write([]byte{'}'})
+			})
 		}
 	}
 {{ else }}
-	// nolint: vetshadow
-	func (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField, {{if not $object.Root}}obj *{{$object.FullName}}{{end}}) graphql.Marshaler {
+	func (ec *executionContext) _{{$object.Name}}_{{$field.Name}}(ctx context.Context, field graphql.CollectedField{{ if not $object.Root }}, obj {{$object.Reference | ref}}{{end}}) graphql.Marshaler {
 		ctx = ec.Tracer.StartFieldExecution(ctx, field)
 		defer func () { ec.Tracer.EndFieldExecution(ctx) }()
+		rctx := &graphql.ResolverContext{
+			Object: {{$object.Name|quote}},
+			Field: field,
+			Args:  nil,
+			IsMethod: {{or $field.IsMethod $field.IsResolver}},
+		}
+		ctx = graphql.WithResolverContext(ctx, rctx)
 		{{- if $field.Args }}
 			rawArgs := field.ArgumentMap(ec.Variables)
-			args, err := {{ $field.ArgsFunc }}(rawArgs)
+			args, err := ec.{{ $field.ArgsFunc }}(ctx,rawArgs)
 			if err != nil {
 				ec.Error(ctx, err)
 				return graphql.Null
 			}
+			rctx.Args = args
 		{{- end }}
-		rctx := &graphql.ResolverContext{
-			Object: {{$object.GQLType|quote}},
-			Args: {{if $field.Args }}args{{else}}nil{{end}},
-			Field: field,
-		}
-		ctx = graphql.WithResolverContext(ctx, rctx)
 		ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)
 		resTmp := ec.FieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(rctx context.Context) (interface{}, error) {
 			ctx = rctx  // use context from middleware stack in children
 			{{- if $field.IsResolver }}
 				return ec.resolvers.{{ $field.ShortInvocation }}
+			{{- else if $field.IsMap }}
+				switch v := {{$field.GoReceiverName}}[{{$field.Name|quote}}].(type) {
+				case {{$field.TypeReference.GO | ref}}:
+					return v, nil
+				case {{$field.TypeReference.Elem.GO | ref}}:
+					return &v, nil
+				case nil:
+					return ({{$field.TypeReference.GO | ref}})(nil), nil
+				default:
+					return nil, fmt.Errorf("unexpected type %T for field %s", v, {{ $field.Name | quote}})
+				}
 			{{- else if $field.IsMethod }}
 				{{- if $field.NoErr }}
 					return {{$field.GoReceiverName}}.{{$field.GoFieldName}}({{ $field.CallArgs }}), nil
@@ -67,16 +83,18 @@
 			{{- end }}
 		})
 		if resTmp == nil {
-			{{- if $field.ASTType.NonNull }}
+			{{- if $field.TypeReference.GQL.NonNull }}
 				if !ec.HasError(rctx) {
 					ec.Errorf(ctx, "must not be null")
 				}
 			{{- end }}
 			return graphql.Null
 		}
-		res := resTmp.({{$field.Signature}})
+		res := resTmp.({{$field.TypeReference.GO | ref}})
 		rctx.Result = res
 		ctx = ec.Tracer.StartFieldChildExecution(ctx)
-		{{ $field.WriteJson }}
+		return ec.{{ $field.TypeReference.MarshalFunc }}(ctx, field.Selections, res)
 	}
 {{ end }}
+
+{{- end }}{{- end}}

vendor/github.com/99designs/gqlgen/codegen/generate.go πŸ”—

@@ -0,0 +1,15 @@
+package codegen
+
+import (
+	"github.com/99designs/gqlgen/codegen/templates"
+)
+
+func GenerateCode(data *Data) error {
+	return templates.Render(templates.Options{
+		PackageName:     data.Config.Exec.Package,
+		Filename:        data.Config.Exec.Filename,
+		Data:            data,
+		RegionTags:      true,
+		GeneratedHeader: true,
+	})
+}

vendor/github.com/99designs/gqlgen/codegen/templates/generated.gotpl β†’ vendor/github.com/99designs/gqlgen/codegen/generated!.gotpl πŸ”—

@@ -1,24 +1,17 @@
-// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
+{{ reserveImport "context"  }}
+{{ reserveImport "fmt"  }}
+{{ reserveImport "io"  }}
+{{ reserveImport "strconv"  }}
+{{ reserveImport "time"  }}
+{{ reserveImport "sync"  }}
+{{ reserveImport "errors"  }}
+{{ reserveImport "bytes"  }}
 
-package {{ .PackageName }}
+{{ reserveImport "github.com/vektah/gqlparser" }}
+{{ reserveImport "github.com/vektah/gqlparser/ast" }}
+{{ reserveImport "github.com/99designs/gqlgen/graphql" }}
+{{ reserveImport "github.com/99designs/gqlgen/graphql/introspection" }}
 
-import (
-	%%%IMPORTS%%%
-
-	{{ reserveImport "context"  }}
-	{{ reserveImport "fmt"  }}
-	{{ reserveImport "io"  }}
-	{{ reserveImport "strconv"  }}
-	{{ reserveImport "time"  }}
-	{{ reserveImport "sync"  }}
-	{{ reserveImport "errors"  }}
-	{{ reserveImport "bytes"  }}
-
-	{{ reserveImport "github.com/vektah/gqlparser" }}
-	{{ reserveImport "github.com/vektah/gqlparser/ast" }}
-	{{ reserveImport "github.com/99designs/gqlgen/graphql" }}
-	{{ reserveImport "github.com/99designs/gqlgen/graphql/introspection" }}
-)
 
 // NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.
 func NewExecutableSchema(cfg Config) graphql.ExecutableSchema {
@@ -38,7 +31,7 @@ type Config struct {
 type ResolverRoot interface {
 {{- range $object := .Objects -}}
 	{{ if $object.HasResolvers -}}
-		{{$object.GQLType}}() {{$object.GQLType}}Resolver
+		{{$object.Name}}() {{$object.Name}}Resolver
 	{{ end }}
 {{- end }}
 }
@@ -52,10 +45,10 @@ type DirectiveRoot struct {
 type ComplexityRoot struct {
 {{ range $object := .Objects }}
 	{{ if not $object.IsReserved -}}
-		{{ $object.GQLType|toCamel }} struct {
-		{{ range $field := $object.Fields -}}
+		{{ $object.Name|go }} struct {
+		{{ range $field := $object.UniqueFields -}}
 			{{ if not $field.IsReserved -}}
-				{{ $field.GQLName|toCamel }} {{ $field.ComplexitySignature }}
+				{{ $field.GoFieldName }} {{ $field.ComplexitySignature }}
 			{{ end }}
 		{{- end }}
 		}
@@ -65,32 +58,16 @@ type ComplexityRoot struct {
 
 {{ range $object := .Objects -}}
 	{{ if $object.HasResolvers }}
-		type {{$object.GQLType}}Resolver interface {
+		type {{$object.Name}}Resolver interface {
 		{{ range $field := $object.Fields -}}
-			{{ $field.ShortResolverDeclaration }}
+			{{- if $field.IsResolver }}
+				{{- $field.GoFieldName}}{{ $field.ShortResolverDeclaration }}
+			{{- end }}
 		{{ end }}
 		}
 	{{- end }}
 {{- end }}
 
-{{ range $object := .Objects -}}
-	{{ range $field := $object.Fields -}}
-		{{ if $field.Args }}
-			func {{ $field.ArgsFunc }}(rawArgs map[string]interface{}) (map[string]interface{}, error) {
-			{{ template "args.gotpl" $field.Args }}
-			}
-		{{ end }}
-	{{ end }}
-{{- end }}
-
-{{ range $directive := .Directives }}
-	{{ if $directive.Args }}
-		func {{ $directive.ArgsFunc }}(rawArgs map[string]interface{}) (map[string]interface{}, error) {
-		{{ template "args.gotpl" $directive.Args }}
-		}
-	{{ end }}
-{{ end }}
-
 type executableSchema struct {
 	resolvers  ResolverRoot
 	directives DirectiveRoot
@@ -102,22 +79,24 @@ func (e *executableSchema) Schema() *ast.Schema {
 }
 
 func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) {
+	ec := executionContext{nil, e}
+	_ = ec
 	switch typeName + "." + field {
 	{{ range $object := .Objects }}
 		{{ if not $object.IsReserved }}
-			{{ range $field := $object.Fields }}
+			{{ range $field := $object.UniqueFields }}
 				{{ if not $field.IsReserved }}
-					case "{{$object.GQLType}}.{{$field.GQLName}}":
-						if e.complexity.{{$object.GQLType|toCamel}}.{{$field.GQLName|toCamel}} == nil {
+					case "{{$object.Name}}.{{$field.GoFieldName}}":
+						if e.complexity.{{$object.Name|go}}.{{$field.GoFieldName}} == nil {
 							break
 						}
 						{{ if $field.Args }}
-							args, err := {{ $field.ArgsFunc }}(rawArgs)
+							args, err := ec.{{ $field.ArgsFunc }}(context.TODO(),rawArgs)
 							if err != nil {
 								return 0, false
 							}
 						{{ end }}
-						return e.complexity.{{$object.GQLType|toCamel}}.{{$field.GQLName|toCamel}}(childComplexity{{if $field.Args}}, {{$field.ComplexityArgs}} {{end}}), true
+						return e.complexity.{{$object.Name|go}}.{{$field.GoFieldName}}(childComplexity{{if $field.Args}}, {{$field.ComplexityArgs}} {{end}}), true
 				{{ end }}
 			{{ end }}
 		{{ end }}
@@ -131,7 +110,7 @@ func (e *executableSchema) Query(ctx context.Context, op *ast.OperationDefinitio
 		ec := executionContext{graphql.GetRequestContext(ctx), e}
 
 		buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {
-			data := ec._{{.QueryRoot.GQLType}}(ctx, op.SelectionSet)
+			data := ec._{{.QueryRoot.Name}}(ctx, op.SelectionSet)
 			var buf bytes.Buffer
 			data.MarshalGQL(&buf)
 			return buf.Bytes()
@@ -140,7 +119,8 @@ func (e *executableSchema) Query(ctx context.Context, op *ast.OperationDefinitio
 		return &graphql.Response{
 			Data:       buf,
 			Errors:     ec.Errors,
-			Extensions: ec.Extensions,		}
+			Extensions: ec.Extensions,
+		}
 	{{- else }}
 		return graphql.ErrorResponse(ctx, "queries are not supported")
 	{{- end }}
@@ -151,7 +131,7 @@ func (e *executableSchema) Mutation(ctx context.Context, op *ast.OperationDefini
 		ec := executionContext{graphql.GetRequestContext(ctx), e}
 
 		buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {
-			data := ec._{{.MutationRoot.GQLType}}(ctx, op.SelectionSet)
+			data := ec._{{.MutationRoot.Name}}(ctx, op.SelectionSet)
 			var buf bytes.Buffer
 			data.MarshalGQL(&buf)
 			return buf.Bytes()
@@ -171,7 +151,7 @@ func (e *executableSchema) Subscription(ctx context.Context, op *ast.OperationDe
 	{{- if .SubscriptionRoot }}
 		ec := executionContext{graphql.GetRequestContext(ctx), e}
 
-		next := ec._{{.SubscriptionRoot.GQLType}}(ctx, op.SelectionSet)
+		next := ec._{{.SubscriptionRoot.Name}}(ctx, op.SelectionSet)
 		if ec.Errors != nil {
 			return graphql.OneShot(&graphql.Response{Data: []byte("null"), Errors: ec.Errors})
 		}
@@ -209,22 +189,6 @@ type executionContext struct {
 	*executableSchema
 }
 
-{{- range $object := .Objects }}
-	{{ template "object.gotpl" $object }}
-
-	{{- range $field := $object.Fields }}
-		{{ template "field.gotpl" $field }}
-	{{ end }}
-{{- end}}
-
-{{- range $interface := .Interfaces }}
-	{{ template "interface.gotpl" $interface }}
-{{- end }}
-
-{{- range $input := .Inputs }}
-	{{ template "input.gotpl" $input }}
-{{- end }}
-
 func (ec *executionContext) FieldMiddleware(ctx context.Context, obj interface{}, next graphql.Resolver) (ret interface{}) {
 	defer func() {
 		if r := recover(); r != nil {
@@ -241,7 +205,7 @@ func (ec *executionContext) FieldMiddleware(ctx context.Context, obj interface{}
 			if ec.directives.{{$directive.Name|ucFirst}} != nil {
 				{{- if $directive.Args }}
 					rawArgs := d.ArgumentMap(ec.Variables)
-					args, err := {{ $directive.ArgsFunc }}(rawArgs)
+					args, err := ec.{{ $directive.ArgsFunc }}(ctx,rawArgs)
 					if err != nil {
 						ec.Error(ctx, err)
 						return nil
@@ -279,7 +243,7 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er
 }
 
 var parsedSchema = gqlparser.MustLoadSchema(
-	{{- range $filename, $schema := .SchemaRaw }}
+	{{- range $filename, $schema := .SchemaStr }}
 		&ast.Source{Name: {{$filename|quote}}, Input: {{$schema|rawQuote}}},
 	{{- end }}
 )

vendor/github.com/99designs/gqlgen/codegen/input.gotpl πŸ”—

@@ -0,0 +1,56 @@
+{{- range $input := .Inputs }}
+	{{- if not .HasUnmarshal }}
+	func (ec *executionContext) unmarshalInput{{ .Name }}(ctx context.Context, v interface{}) ({{.Type | ref}}, error) {
+		var it {{.Type | ref}}
+		var asMap = v.(map[string]interface{})
+		{{ range $field := .Fields}}
+			{{- if $field.Default}}
+				if _, present := asMap[{{$field.Name|quote}}] ; !present {
+					asMap[{{$field.Name|quote}}] = {{ $field.Default | dump }}
+				}
+			{{- end}}
+		{{- end }}
+
+		for k, v := range asMap {
+			switch k {
+			{{- range $field := .Fields }}
+			case {{$field.Name|quote}}:
+				var err error
+				{{- if $field.Directives }}
+					getField0 := func(ctx context.Context) (interface{}, error) { return ec.{{ $field.TypeReference.UnmarshalFunc }}(ctx, v) }
+
+					{{- range $i, $directive := $field.Directives }}
+						getField{{add $i 1}} := func(ctx context.Context) (res interface{}, err error) {
+							{{- range $dArg := $directive.Args }}
+								{{- if and $dArg.TypeReference.IsPtr ( notNil "Value" $dArg ) }}
+									{{ $dArg.VarName }} := {{ $dArg.Value | dump }}
+								{{- end }}
+							{{- end }}
+							n := getField{{$i}}
+							return ec.directives.{{$directive.Name|ucFirst}}({{$directive.ResolveArgs "it" "n" }})
+						}
+					{{- end }}
+
+					tmp, err := getField{{$field.Directives|len}}(ctx)
+					if err != nil {
+						return it, err
+					}
+					if data, ok := tmp.({{ $field.TypeReference.GO | ref }}) ; ok {
+						it.{{$field.GoFieldName}} = data
+					} else {
+						return it, fmt.Errorf(`unexpected type %T from directive, should be {{ $field.TypeReference.GO }}`, tmp)
+					}
+				{{- else }}
+					it.{{$field.GoFieldName}}, err = ec.{{ $field.TypeReference.UnmarshalFunc }}(ctx, v)
+					if err != nil {
+						return it, err
+					}
+				{{- end }}
+			{{- end }}
+			}
+		}
+
+		return it, nil
+	}
+	{{- end }}
+{{ end }}

vendor/github.com/99designs/gqlgen/codegen/input_build.go πŸ”—

@@ -1,96 +0,0 @@
-package codegen
-
-import (
-	"go/types"
-	"sort"
-
-	"github.com/pkg/errors"
-	"github.com/vektah/gqlparser/ast"
-	"golang.org/x/tools/go/loader"
-)
-
-func (cfg *Config) buildInputs(namedTypes NamedTypes, prog *loader.Program) (Objects, error) {
-	var inputs Objects
-
-	for _, typ := range cfg.schema.Types {
-		switch typ.Kind {
-		case ast.InputObject:
-			input, err := cfg.buildInput(namedTypes, typ)
-			if err != nil {
-				return nil, err
-			}
-
-			def, err := findGoType(prog, input.Package, input.GoType)
-			if err != nil {
-				return nil, errors.Wrap(err, "cannot find type")
-			}
-			if def != nil {
-				input.Marshaler = buildInputMarshaler(typ, def)
-				bindErrs := bindObject(def.Type(), input, cfg.StructTag)
-				if len(bindErrs) > 0 {
-					return nil, bindErrs
-				}
-			}
-
-			inputs = append(inputs, input)
-		}
-	}
-
-	sort.Slice(inputs, func(i, j int) bool {
-		return inputs[i].GQLType < inputs[j].GQLType
-	})
-
-	return inputs, nil
-}
-
-func (cfg *Config) buildInput(types NamedTypes, typ *ast.Definition) (*Object, error) {
-	obj := &Object{NamedType: types[typ.Name]}
-	typeEntry, entryExists := cfg.Models[typ.Name]
-
-	for _, field := range typ.Fields {
-		newField := Field{
-			GQLName: field.Name,
-			Type:    types.getType(field.Type),
-			Object:  obj,
-		}
-
-		if entryExists {
-			if typeField, ok := typeEntry.Fields[field.Name]; ok {
-				newField.GoFieldName = typeField.FieldName
-			}
-		}
-
-		if field.DefaultValue != nil {
-			var err error
-			newField.Default, err = field.DefaultValue.Value(nil)
-			if err != nil {
-				return nil, errors.Errorf("default value for %s.%s is not valid: %s", typ.Name, field.Name, err.Error())
-			}
-		}
-
-		if !newField.Type.IsInput && !newField.Type.IsScalar {
-			return nil, errors.Errorf("%s cannot be used as a field of %s. only input and scalar types are allowed", newField.GQLType, obj.GQLType)
-		}
-
-		obj.Fields = append(obj.Fields, newField)
-
-	}
-	return obj, nil
-}
-
-// if user has implemented an UnmarshalGQL method on the input type manually, use it
-// otherwise we will generate one.
-func buildInputMarshaler(typ *ast.Definition, def types.Object) *Ref {
-	switch def := def.(type) {
-	case *types.TypeName:
-		namedType := def.Type().(*types.Named)
-		for i := 0; i < namedType.NumMethods(); i++ {
-			method := namedType.Method(i)
-			if method.Name() == "UnmarshalGQL" {
-				return nil
-			}
-		}
-	}
-
-	return &Ref{GoType: typ.Name}
-}

vendor/github.com/99designs/gqlgen/codegen/interface.go πŸ”—

@@ -1,13 +1,63 @@
 package codegen
 
-type Interface struct {
-	*NamedType
+import (
+	"go/types"
+
+	"github.com/vektah/gqlparser/ast"
+)
 
+type Interface struct {
+	*ast.Definition
+	Type         types.Type
 	Implementors []InterfaceImplementor
+	InTypemap    bool
 }
 
 type InterfaceImplementor struct {
-	ValueReceiver bool
+	*ast.Definition
+
+	Interface *Interface
+	Type      types.Type
+}
+
+func (b *builder) buildInterface(typ *ast.Definition) *Interface {
+	obj, err := b.Binder.DefaultUserObject(typ.Name)
+	if err != nil {
+		panic(err)
+	}
+
+	i := &Interface{
+		Definition: typ,
+		Type:       obj,
+		InTypemap:  b.Config.Models.UserDefined(typ.Name),
+	}
+
+	for _, implementor := range b.Schema.GetPossibleTypes(typ) {
+		obj, err := b.Binder.DefaultUserObject(implementor.Name)
+		if err != nil {
+			panic(err)
+		}
+
+		i.Implementors = append(i.Implementors, InterfaceImplementor{
+			Definition: implementor,
+			Type:       obj,
+			Interface:  i,
+		})
+	}
+
+	return i
+}
+
+func (i *InterfaceImplementor) ValueReceiver() bool {
+	interfaceType, err := findGoInterface(i.Interface.Type)
+	if interfaceType == nil || err != nil {
+		return true
+	}
+
+	implementorType, err := findGoNamedType(i.Type)
+	if implementorType == nil || err != nil {
+		return true
+	}
 
-	*NamedType
+	return types.Implements(implementorType, interfaceType)
 }

vendor/github.com/99designs/gqlgen/codegen/interface.gotpl πŸ”—

@@ -0,0 +1,20 @@
+{{- range $interface := .Interfaces }}
+
+func (ec *executionContext) _{{$interface.Name}}(ctx context.Context, sel ast.SelectionSet, obj *{{$interface.Type | ref}}) graphql.Marshaler {
+	switch obj := (*obj).(type) {
+	case nil:
+		return graphql.Null
+	{{- range $implementor := $interface.Implementors }}
+		{{- if $implementor.ValueReceiver }}
+			case {{$implementor.Type | ref}}:
+				return ec._{{$implementor.Name}}(ctx, sel, &obj)
+		{{- end}}
+		case *{{$implementor.Type | ref}}:
+			return ec._{{$implementor.Name}}(ctx, sel, obj)
+	{{- end }}
+	default:
+		panic(fmt.Errorf("unexpected type %T", obj))
+	}
+}
+
+{{- end }}

vendor/github.com/99designs/gqlgen/codegen/interface_build.go πŸ”—

@@ -1,53 +0,0 @@
-package codegen
-
-import (
-	"go/types"
-	"sort"
-
-	"github.com/vektah/gqlparser/ast"
-	"golang.org/x/tools/go/loader"
-)
-
-func (cfg *Config) buildInterfaces(types NamedTypes, prog *loader.Program) []*Interface {
-	var interfaces []*Interface
-	for _, typ := range cfg.schema.Types {
-		if typ.Kind == ast.Union || typ.Kind == ast.Interface {
-			interfaces = append(interfaces, cfg.buildInterface(types, typ, prog))
-		}
-	}
-
-	sort.Slice(interfaces, func(i, j int) bool {
-		return interfaces[i].GQLType < interfaces[j].GQLType
-	})
-
-	return interfaces
-}
-
-func (cfg *Config) buildInterface(types NamedTypes, typ *ast.Definition, prog *loader.Program) *Interface {
-	i := &Interface{NamedType: types[typ.Name]}
-
-	for _, implementor := range cfg.schema.GetPossibleTypes(typ) {
-		t := types[implementor.Name]
-
-		i.Implementors = append(i.Implementors, InterfaceImplementor{
-			NamedType:     t,
-			ValueReceiver: cfg.isValueReceiver(types[typ.Name], t, prog),
-		})
-	}
-
-	return i
-}
-
-func (cfg *Config) isValueReceiver(intf *NamedType, implementor *NamedType, prog *loader.Program) bool {
-	interfaceType, err := findGoInterface(prog, intf.Package, intf.GoType)
-	if interfaceType == nil || err != nil {
-		return true
-	}
-
-	implementorType, err := findGoNamedType(prog, implementor.Package, implementor.GoType)
-	if implementorType == nil || err != nil {
-		return true
-	}
-
-	return types.Implements(implementorType, interfaceType)
-}

vendor/github.com/99designs/gqlgen/codegen/model.go πŸ”—

@@ -1,17 +0,0 @@
-package codegen
-
-type Model struct {
-	*NamedType
-	Description string
-	Fields      []ModelField
-	Implements  []*NamedType
-}
-
-type ModelField struct {
-	*Type
-	GQLName     string
-	GoFieldName string
-	GoFKName    string
-	GoFKType    string
-	Description string
-}

vendor/github.com/99designs/gqlgen/codegen/models_build.go πŸ”—

@@ -1,91 +0,0 @@
-package codegen
-
-import (
-	"sort"
-
-	"github.com/vektah/gqlparser/ast"
-	"golang.org/x/tools/go/loader"
-)
-
-func (cfg *Config) buildModels(types NamedTypes, prog *loader.Program) ([]Model, error) {
-	var models []Model
-
-	for _, typ := range cfg.schema.Types {
-		var model Model
-		switch typ.Kind {
-		case ast.Object:
-			obj, err := cfg.buildObject(types, typ)
-			if err != nil {
-				return nil, err
-			}
-			if obj.Root || obj.IsUserDefined {
-				continue
-			}
-			model = cfg.obj2Model(obj)
-		case ast.InputObject:
-			obj, err := cfg.buildInput(types, typ)
-			if err != nil {
-				return nil, err
-			}
-			if obj.IsUserDefined {
-				continue
-			}
-			model = cfg.obj2Model(obj)
-		case ast.Interface, ast.Union:
-			intf := cfg.buildInterface(types, typ, prog)
-			if intf.IsUserDefined {
-				continue
-			}
-			model = int2Model(intf)
-		default:
-			continue
-		}
-		model.Description = typ.Description // It's this or change both obj2Model and buildObject
-
-		models = append(models, model)
-	}
-
-	sort.Slice(models, func(i, j int) bool {
-		return models[i].GQLType < models[j].GQLType
-	})
-
-	return models, nil
-}
-
-func (cfg *Config) obj2Model(obj *Object) Model {
-	model := Model{
-		NamedType:  obj.NamedType,
-		Implements: obj.Implements,
-		Fields:     []ModelField{},
-	}
-
-	model.GoType = ucFirst(obj.GQLType)
-	model.Marshaler = &Ref{GoType: obj.GoType}
-
-	for i := range obj.Fields {
-		field := &obj.Fields[i]
-		mf := ModelField{Type: field.Type, GQLName: field.GQLName}
-
-		if field.GoFieldName != "" {
-			mf.GoFieldName = field.GoFieldName
-		} else {
-			mf.GoFieldName = field.GoNameExported()
-		}
-
-		model.Fields = append(model.Fields, mf)
-	}
-
-	return model
-}
-
-func int2Model(obj *Interface) Model {
-	model := Model{
-		NamedType: obj.NamedType,
-		Fields:    []ModelField{},
-	}
-
-	model.GoType = ucFirst(obj.GQLType)
-	model.Marshaler = &Ref{GoType: obj.GoType}
-
-	return model
-}

vendor/github.com/99designs/gqlgen/codegen/object.go πŸ”—

@@ -1,13 +1,13 @@
 package codegen
 
 import (
-	"bytes"
-	"fmt"
+	"go/types"
 	"strconv"
 	"strings"
-	"text/template"
 	"unicode"
 
+	"github.com/99designs/gqlgen/codegen/config"
+	"github.com/pkg/errors"
 	"github.com/vektah/gqlparser/ast"
 )
 
@@ -17,337 +17,150 @@ const (
 	GoFieldUndefined GoFieldType = iota
 	GoFieldMethod
 	GoFieldVariable
+	GoFieldMap
 )
 
 type Object struct {
-	*NamedType
+	*ast.Definition
 
-	Fields             []Field
-	Satisfies          []string
-	Implements         []*NamedType
-	ResolverInterface  *Ref
+	Type               types.Type
+	ResolverInterface  types.Type
 	Root               bool
+	Fields             []*Field
+	Implements         []*ast.Definition
 	DisableConcurrency bool
 	Stream             bool
+	Directives         []*Directive
 }
 
-type Field struct {
-	*Type
-	Description      string          // Description of a field
-	GQLName          string          // The name of the field in graphql
-	GoFieldType      GoFieldType     // The field type in go, if any
-	GoReceiverName   string          // The name of method & var receiver in go, if any
-	GoFieldName      string          // The name of the method or var in go, if any
-	Args             []FieldArgument // A list of arguments to be passed to this field
-	ForceResolver    bool            // Should be emit Resolver method
-	MethodHasContext bool            // If this is bound to a go method, does the method also take a context
-	NoErr            bool            // If this is bound to a go method, does that method have an error as the second argument
-	Object           *Object         // A link back to the parent object
-	Default          interface{}     // The default value
-}
-
-type FieldArgument struct {
-	*Type
-
-	GQLName   string      // The name of the argument in graphql
-	GoVarName string      // The name of the var in go
-	Object    *Object     // A link back to the parent object
-	Default   interface{} // The default value
-}
-
-type Objects []*Object
-
-func (o *Object) Implementors() string {
-	satisfiedBy := strconv.Quote(o.GQLType)
-	for _, s := range o.Satisfies {
-		satisfiedBy += ", " + strconv.Quote(s)
-	}
-	return "[]string{" + satisfiedBy + "}"
-}
-
-func (o *Object) HasResolvers() bool {
-	for _, f := range o.Fields {
-		if f.IsResolver() {
-			return true
+func (b *builder) buildObject(typ *ast.Definition) (*Object, error) {
+	dirs, err := b.getDirectives(typ.Directives)
+	if err != nil {
+		return nil, errors.Wrap(err, typ.Name)
+	}
+
+	obj := &Object{
+		Definition:         typ,
+		Root:               b.Schema.Query == typ || b.Schema.Mutation == typ || b.Schema.Subscription == typ,
+		DisableConcurrency: typ == b.Schema.Mutation,
+		Stream:             typ == b.Schema.Subscription,
+		Directives:         dirs,
+		ResolverInterface: types.NewNamed(
+			types.NewTypeName(0, b.Config.Exec.Pkg(), typ.Name+"Resolver", nil),
+			nil,
+			nil,
+		),
+	}
+
+	if !obj.Root {
+		goObject, err := b.Binder.DefaultUserObject(typ.Name)
+		if err != nil {
+			return nil, err
 		}
+		obj.Type = goObject
 	}
-	return false
-}
 
-func (o *Object) IsConcurrent() bool {
-	for _, f := range o.Fields {
-		if f.IsConcurrent() {
-			return true
-		}
+	for _, intf := range b.Schema.GetImplements(typ) {
+		obj.Implements = append(obj.Implements, b.Schema.Types[intf.Name])
 	}
-	return false
-}
-
-func (o *Object) IsReserved() bool {
-	return strings.HasPrefix(o.GQLType, "__")
-}
 
-func (f *Field) IsResolver() bool {
-	return f.GoFieldName == ""
-}
-
-func (f *Field) IsReserved() bool {
-	return strings.HasPrefix(f.GQLName, "__")
-}
-
-func (f *Field) IsMethod() bool {
-	return f.GoFieldType == GoFieldMethod
-}
+	for _, field := range typ.Fields {
+		if strings.HasPrefix(field.Name, "__") {
+			continue
+		}
 
-func (f *Field) IsVariable() bool {
-	return f.GoFieldType == GoFieldVariable
-}
+		var f *Field
+		f, err = b.buildField(obj, field)
+		if err != nil {
+			return nil, err
+		}
 
-func (f *Field) IsConcurrent() bool {
-	if f.Object.DisableConcurrency {
-		return false
+		obj.Fields = append(obj.Fields, f)
 	}
-	return f.MethodHasContext || f.IsResolver()
-}
 
-func (f *Field) GoNameExported() string {
-	return lintName(ucFirst(f.GQLName))
+	return obj, nil
 }
 
-func (f *Field) GoNameUnexported() string {
-	return lintName(f.GQLName)
-}
-
-func (f *Field) ShortInvocation() string {
-	if !f.IsResolver() {
-		return ""
+func (o *Object) Reference() types.Type {
+	switch o.Type.(type) {
+	case *types.Pointer, *types.Slice, *types.Map:
+		return o.Type
 	}
 
-	return fmt.Sprintf("%s().%s(%s)", f.Object.GQLType, f.GoNameExported(), f.CallArgs())
+	return types.NewPointer(o.Type)
 }
 
-func (f *Field) ArgsFunc() string {
-	if len(f.Args) == 0 {
-		return ""
-	}
-
-	return "field_" + f.Object.GQLType + "_" + f.GQLName + "_args"
-}
+type Objects []*Object
 
-func (f *Field) ResolverType() string {
-	if !f.IsResolver() {
-		return ""
+func (o *Object) Implementors() string {
+	satisfiedBy := strconv.Quote(o.Name)
+	for _, s := range o.Implements {
+		satisfiedBy += ", " + strconv.Quote(s.Name)
 	}
-
-	return fmt.Sprintf("%s().%s(%s)", f.Object.GQLType, f.GoNameExported(), f.CallArgs())
+	return "[]string{" + satisfiedBy + "}"
 }
 
-func (f *Field) ShortResolverDeclaration() string {
-	if !f.IsResolver() {
-		return ""
-	}
-	res := fmt.Sprintf("%s(ctx context.Context", f.GoNameExported())
-
-	if !f.Object.Root {
-		res += fmt.Sprintf(", obj *%s", f.Object.FullName())
-	}
-	for _, arg := range f.Args {
-		res += fmt.Sprintf(", %s %s", arg.GoVarName, arg.Signature())
-	}
-
-	result := f.Signature()
-	if f.Object.Stream {
-		result = "<-chan " + result
+func (o *Object) HasResolvers() bool {
+	for _, f := range o.Fields {
+		if f.IsResolver {
+			return true
+		}
 	}
-
-	res += fmt.Sprintf(") (%s, error)", result)
-	return res
+	return false
 }
 
-func (f *Field) ResolverDeclaration() string {
-	if !f.IsResolver() {
-		return ""
-	}
-	res := fmt.Sprintf("%s_%s(ctx context.Context", f.Object.GQLType, f.GoNameUnexported())
-
-	if !f.Object.Root {
-		res += fmt.Sprintf(", obj *%s", f.Object.FullName())
+func (o *Object) HasUnmarshal() bool {
+	if o.Type == config.MapType {
+		return true
 	}
-	for _, arg := range f.Args {
-		res += fmt.Sprintf(", %s %s", arg.GoVarName, arg.Signature())
-	}
-
-	result := f.Signature()
-	if f.Object.Stream {
-		result = "<-chan " + result
+	for i := 0; i < o.Type.(*types.Named).NumMethods(); i++ {
+		switch o.Type.(*types.Named).Method(i).Name() {
+		case "UnmarshalGQL":
+			return true
+		}
 	}
-
-	res += fmt.Sprintf(") (%s, error)", result)
-	return res
+	return false
 }
 
-func (f *Field) ComplexitySignature() string {
-	res := fmt.Sprintf("func(childComplexity int")
-	for _, arg := range f.Args {
-		res += fmt.Sprintf(", %s %s", arg.GoVarName, arg.Signature())
+func (o *Object) HasDirectives() bool {
+	if len(o.Directives) > 0 {
+		return true
 	}
-	res += ") int"
-	return res
-}
-
-func (f *Field) ComplexityArgs() string {
-	var args []string
-	for _, arg := range f.Args {
-		args = append(args, "args["+strconv.Quote(arg.GQLName)+"].("+arg.Signature()+")")
+	for _, f := range o.Fields {
+		if f.HasDirectives() {
+			return true
+		}
 	}
 
-	return strings.Join(args, ", ")
+	return false
 }
 
-func (f *Field) CallArgs() string {
-	var args []string
-
-	if f.IsResolver() {
-		args = append(args, "rctx")
-
-		if !f.Object.Root {
-			args = append(args, "obj")
-		}
-	} else {
-		if f.MethodHasContext {
-			args = append(args, "ctx")
+func (o *Object) IsConcurrent() bool {
+	for _, f := range o.Fields {
+		if f.IsConcurrent() {
+			return true
 		}
 	}
-
-	for _, arg := range f.Args {
-		args = append(args, "args["+strconv.Quote(arg.GQLName)+"].("+arg.Signature()+")")
-	}
-
-	return strings.Join(args, ", ")
-}
-
-// should be in the template, but its recursive and has a bunch of args
-func (f *Field) WriteJson() string {
-	return f.doWriteJson("res", f.Type.Modifiers, f.ASTType, false, 1)
+	return false
 }
 
-func (f *Field) doWriteJson(val string, remainingMods []string, astType *ast.Type, isPtr bool, depth int) string {
-	switch {
-	case len(remainingMods) > 0 && remainingMods[0] == modPtr:
-		return tpl(`
-			if {{.val}} == nil {
-				{{- if .nonNull }}
-					if !ec.HasError(rctx) {
-						ec.Errorf(ctx, "must not be null")
-					}
-				{{- end }}
-				return graphql.Null
-			}
-			{{.next }}`, map[string]interface{}{
-			"val":     val,
-			"nonNull": astType.NonNull,
-			"next":    f.doWriteJson(val, remainingMods[1:], astType, true, depth+1),
-		})
-
-	case len(remainingMods) > 0 && remainingMods[0] == modList:
-		if isPtr {
-			val = "*" + val
-		}
-		var arr = "arr" + strconv.Itoa(depth)
-		var index = "idx" + strconv.Itoa(depth)
-		var usePtr bool
-		if len(remainingMods) == 1 && !isPtr {
-			usePtr = true
-		}
-
-		return tpl(`
-			{{.arr}} := make(graphql.Array, len({{.val}}))
-			{{ if and .top (not .isScalar) }} var wg sync.WaitGroup {{ end }}
-			{{ if not .isScalar }}
-				isLen1 := len({{.val}}) == 1
-				if !isLen1 {
-					wg.Add(len({{.val}}))
-				}
-			{{ end }}
-			for {{.index}} := range {{.val}} {
-				{{- if not .isScalar }}
-					{{.index}} := {{.index}}
-					rctx := &graphql.ResolverContext{
-						Index: &{{.index}},
-						Result: {{ if .usePtr }}&{{end}}{{.val}}[{{.index}}],
-					}
-					ctx := graphql.WithResolverContext(ctx, rctx)
-					f := func({{.index}} int) {
-						if !isLen1 {
-							defer wg.Done()
-						}
-						{{.arr}}[{{.index}}] = func() graphql.Marshaler {
-							{{ .next }}
-						}()
-					}
-					if isLen1 {
-						f({{.index}})
-					} else {
-						go f({{.index}})
-					}
-				{{ else }}
-					{{.arr}}[{{.index}}] = func() graphql.Marshaler {
-						{{ .next }}
-					}()
-				{{- end}}
-			}
-			{{ if and .top (not .isScalar) }} wg.Wait() {{ end }}
-			return {{.arr}}`, map[string]interface{}{
-			"val":      val,
-			"arr":      arr,
-			"index":    index,
-			"top":      depth == 1,
-			"arrayLen": len(val),
-			"isScalar": f.IsScalar,
-			"usePtr":   usePtr,
-			"next":     f.doWriteJson(val+"["+index+"]", remainingMods[1:], astType.Elem, false, depth+1),
-		})
-
-	case f.IsScalar:
-		if isPtr {
-			val = "*" + val
-		}
-		return f.Marshal(val)
-
-	default:
-		if !isPtr {
-			val = "&" + val
-		}
-		return tpl(`
-			return ec._{{.type}}(ctx, field.Selections, {{.val}})`, map[string]interface{}{
-			"type": f.GQLType,
-			"val":  val,
-		})
-	}
+func (o *Object) IsReserved() bool {
+	return strings.HasPrefix(o.Definition.Name, "__")
 }
 
-func (f *FieldArgument) Stream() bool {
-	return f.Object != nil && f.Object.Stream
+func (o *Object) Description() string {
+	return o.Definition.Description
 }
 
 func (os Objects) ByName(name string) *Object {
 	for i, o := range os {
-		if strings.EqualFold(o.GQLType, name) {
+		if strings.EqualFold(o.Definition.Name, name) {
 			return os[i]
 		}
 	}
 	return nil
 }
 
-func tpl(tpl string, vars map[string]interface{}) string {
-	b := &bytes.Buffer{}
-	err := template.Must(template.New("inline").Parse(tpl)).Execute(b, vars)
-	if err != nil {
-		panic(err)
-	}
-	return b.String()
-}
-
 func ucFirst(s string) string {
 	if s == "" {
 		return ""
@@ -357,117 +170,3 @@ func ucFirst(s string) string {
 	r[0] = unicode.ToUpper(r[0])
 	return string(r)
 }
-
-// copy from https://github.com/golang/lint/blob/06c8688daad7faa9da5a0c2f163a3d14aac986ca/lint.go#L679
-
-// lintName returns a different name if it should be different.
-func lintName(name string) (should string) {
-	// Fast path for simple cases: "_" and all lowercase.
-	if name == "_" {
-		return name
-	}
-	allLower := true
-	for _, r := range name {
-		if !unicode.IsLower(r) {
-			allLower = false
-			break
-		}
-	}
-	if allLower {
-		return name
-	}
-
-	// Split camelCase at any lower->upper transition, and split on underscores.
-	// Check each word for common initialisms.
-	runes := []rune(name)
-	w, i := 0, 0 // index of start of word, scan
-	for i+1 <= len(runes) {
-		eow := false // whether we hit the end of a word
-		if i+1 == len(runes) {
-			eow = true
-		} else if runes[i+1] == '_' {
-			// underscore; shift the remainder forward over any run of underscores
-			eow = true
-			n := 1
-			for i+n+1 < len(runes) && runes[i+n+1] == '_' {
-				n++
-			}
-
-			// Leave at most one underscore if the underscore is between two digits
-			if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
-				n--
-			}
-
-			copy(runes[i+1:], runes[i+n+1:])
-			runes = runes[:len(runes)-n]
-		} else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
-			// lower->non-lower
-			eow = true
-		}
-		i++
-		if !eow {
-			continue
-		}
-
-		// [w,i) is a word.
-		word := string(runes[w:i])
-		if u := strings.ToUpper(word); commonInitialisms[u] {
-			// Keep consistent case, which is lowercase only at the start.
-			if w == 0 && unicode.IsLower(runes[w]) {
-				u = strings.ToLower(u)
-			}
-			// All the common initialisms are ASCII,
-			// so we can replace the bytes exactly.
-			copy(runes[w:], []rune(u))
-		} else if w > 0 && strings.ToLower(word) == word {
-			// already all lowercase, and not the first word, so uppercase the first character.
-			runes[w] = unicode.ToUpper(runes[w])
-		}
-		w = i
-	}
-	return string(runes)
-}
-
-// commonInitialisms is a set of common initialisms.
-// Only add entries that are highly unlikely to be non-initialisms.
-// For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
-var commonInitialisms = map[string]bool{
-	"ACL":   true,
-	"API":   true,
-	"ASCII": true,
-	"CPU":   true,
-	"CSS":   true,
-	"DNS":   true,
-	"EOF":   true,
-	"GUID":  true,
-	"HTML":  true,
-	"HTTP":  true,
-	"HTTPS": true,
-	"ID":    true,
-	"IP":    true,
-	"JSON":  true,
-	"LHS":   true,
-	"QPS":   true,
-	"RAM":   true,
-	"RHS":   true,
-	"RPC":   true,
-	"SLA":   true,
-	"SMTP":  true,
-	"SQL":   true,
-	"SSH":   true,
-	"TCP":   true,
-	"TLS":   true,
-	"TTL":   true,
-	"UDP":   true,
-	"UI":    true,
-	"UID":   true,
-	"UUID":  true,
-	"URI":   true,
-	"URL":   true,
-	"UTF8":  true,
-	"VM":    true,
-	"XML":   true,
-	"XMPP":  true,
-	"XSRF":  true,
-	"XSS":   true,
-}

vendor/github.com/99designs/gqlgen/codegen/object.gotpl πŸ”—

@@ -0,0 +1,77 @@
+{{- range $object := .Objects }}
+
+var {{ $object.Name|lcFirst}}Implementors = {{$object.Implementors}}
+
+{{- if .Stream }}
+func (ec *executionContext) _{{$object.Name}}(ctx context.Context, sel ast.SelectionSet) func() graphql.Marshaler {
+	fields := graphql.CollectFields(ctx, sel, {{$object.Name|lcFirst}}Implementors)
+	ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{
+		Object: {{$object.Name|quote}},
+	})
+	if len(fields) != 1 {
+		ec.Errorf(ctx, "must subscribe to exactly one stream")
+		return nil
+	}
+
+	switch fields[0].Name {
+	{{- range $field := $object.Fields }}
+	case "{{$field.Name}}":
+		return ec._{{$object.Name}}_{{$field.Name}}(ctx, fields[0])
+	{{- end }}
+	default:
+		panic("unknown field " + strconv.Quote(fields[0].Name))
+	}
+}
+{{- else }}
+func (ec *executionContext) _{{$object.Name}}(ctx context.Context, sel ast.SelectionSet{{ if not $object.Root }},obj {{$object.Reference | ref }}{{ end }}) graphql.Marshaler {
+	fields := graphql.CollectFields(ctx, sel, {{$object.Name|lcFirst}}Implementors)
+	{{if $object.Root}}
+		ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{
+			Object: {{$object.Name|quote}},
+		})
+	{{end}}
+
+	out := graphql.NewFieldSet(fields)
+	invalid := false
+	for i, field := range fields {
+		switch field.Name {
+		case "__typename":
+			out.Values[i] = graphql.MarshalString({{$object.Name|quote}})
+		{{- range $field := $object.Fields }}
+		case "{{$field.Name}}":
+			{{- if $field.IsConcurrent }}
+				field := field
+				out.Concurrently(i, func() (res graphql.Marshaler) {
+					defer func() {
+						if r := recover(); r != nil {
+							ec.Error(ctx, ec.Recover(ctx, r))
+						}
+					}()
+					res = ec._{{$object.Name}}_{{$field.Name}}(ctx, field{{if not $object.Root}}, obj{{end}})
+					{{- if $field.TypeReference.GQL.NonNull }}
+						if res == graphql.Null {
+							invalid = true
+						}
+					{{- end }}
+					return res
+				})
+			{{- else }}
+				out.Values[i] = ec._{{$object.Name}}_{{$field.Name}}(ctx, field{{if not $object.Root}}, obj{{end}})
+				{{- if $field.TypeReference.GQL.NonNull }}
+					if out.Values[i] == graphql.Null {
+						invalid = true
+					}
+				{{- end }}
+			{{- end }}
+		{{- end }}
+		default:
+			panic("unknown field " + strconv.Quote(field.Name))
+		}
+	}
+	out.Dispatch()
+	if invalid { return graphql.Null }
+	return out
+}
+{{- end }}
+
+{{- end }}

vendor/github.com/99designs/gqlgen/codegen/object_build.go πŸ”—

@@ -1,181 +0,0 @@
-package codegen
-
-import (
-	"log"
-	"sort"
-
-	"github.com/pkg/errors"
-	"github.com/vektah/gqlparser/ast"
-	"golang.org/x/tools/go/loader"
-)
-
-func (cfg *Config) buildObjects(types NamedTypes, prog *loader.Program) (Objects, error) {
-	var objects Objects
-
-	for _, typ := range cfg.schema.Types {
-		if typ.Kind != ast.Object {
-			continue
-		}
-
-		obj, err := cfg.buildObject(types, typ)
-		if err != nil {
-			return nil, err
-		}
-
-		def, err := findGoType(prog, obj.Package, obj.GoType)
-		if err != nil {
-			return nil, err
-		}
-		if def != nil {
-			for _, bindErr := range bindObject(def.Type(), obj, cfg.StructTag) {
-				log.Println(bindErr.Error())
-				log.Println("  Adding resolver method")
-			}
-		}
-
-		objects = append(objects, obj)
-	}
-
-	sort.Slice(objects, func(i, j int) bool {
-		return objects[i].GQLType < objects[j].GQLType
-	})
-
-	return objects, nil
-}
-
-var keywords = []string{
-	"break",
-	"default",
-	"func",
-	"interface",
-	"select",
-	"case",
-	"defer",
-	"go",
-	"map",
-	"struct",
-	"chan",
-	"else",
-	"goto",
-	"package",
-	"switch",
-	"const",
-	"fallthrough",
-	"if",
-	"range",
-	"type",
-	"continue",
-	"for",
-	"import",
-	"return",
-	"var",
-}
-
-// sanitizeArgName prevents collisions with go keywords for arguments to resolver functions
-func sanitizeArgName(name string) string {
-	for _, k := range keywords {
-		if name == k {
-			return name + "Arg"
-		}
-	}
-	return name
-}
-
-func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition) (*Object, error) {
-	obj := &Object{NamedType: types[typ.Name]}
-	typeEntry, entryExists := cfg.Models[typ.Name]
-
-	obj.ResolverInterface = &Ref{GoType: obj.GQLType + "Resolver"}
-
-	if typ == cfg.schema.Query {
-		obj.Root = true
-	}
-
-	if typ == cfg.schema.Mutation {
-		obj.Root = true
-		obj.DisableConcurrency = true
-	}
-
-	if typ == cfg.schema.Subscription {
-		obj.Root = true
-		obj.Stream = true
-	}
-
-	obj.Satisfies = append(obj.Satisfies, typ.Interfaces...)
-
-	for _, intf := range cfg.schema.GetImplements(typ) {
-		obj.Implements = append(obj.Implements, types[intf.Name])
-	}
-
-	for _, field := range typ.Fields {
-		if typ == cfg.schema.Query && field.Name == "__type" {
-			obj.Fields = append(obj.Fields, Field{
-				Type:           &Type{types["__Schema"], []string{modPtr}, ast.NamedType("__Schema", nil), nil},
-				GQLName:        "__schema",
-				GoFieldType:    GoFieldMethod,
-				GoReceiverName: "ec",
-				GoFieldName:    "introspectSchema",
-				Object:         obj,
-				Description:    field.Description,
-			})
-			continue
-		}
-		if typ == cfg.schema.Query && field.Name == "__schema" {
-			obj.Fields = append(obj.Fields, Field{
-				Type:           &Type{types["__Type"], []string{modPtr}, ast.NamedType("__Schema", nil), nil},
-				GQLName:        "__type",
-				GoFieldType:    GoFieldMethod,
-				GoReceiverName: "ec",
-				GoFieldName:    "introspectType",
-				Args: []FieldArgument{
-					{GQLName: "name", Type: &Type{types["String"], []string{}, ast.NamedType("String", nil), nil}, Object: &Object{}},
-				},
-				Object: obj,
-			})
-			continue
-		}
-
-		var forceResolver bool
-		var goName string
-		if entryExists {
-			if typeField, ok := typeEntry.Fields[field.Name]; ok {
-				goName = typeField.FieldName
-				forceResolver = typeField.Resolver
-			}
-		}
-
-		var args []FieldArgument
-		for _, arg := range field.Arguments {
-			newArg := FieldArgument{
-				GQLName:   arg.Name,
-				Type:      types.getType(arg.Type),
-				Object:    obj,
-				GoVarName: sanitizeArgName(arg.Name),
-			}
-
-			if !newArg.Type.IsInput && !newArg.Type.IsScalar {
-				return nil, errors.Errorf("%s cannot be used as argument of %s.%s. only input and scalar types are allowed", arg.Type, obj.GQLType, field.Name)
-			}
-
-			if arg.DefaultValue != nil {
-				var err error
-				newArg.Default, err = arg.DefaultValue.Value(nil)
-				if err != nil {
-					return nil, errors.Errorf("default value for %s.%s is not valid: %s", typ.Name, field.Name, err.Error())
-				}
-			}
-			args = append(args, newArg)
-		}
-
-		obj.Fields = append(obj.Fields, Field{
-			GQLName:       field.Name,
-			Type:          types.getType(field.Type),
-			Args:          args,
-			Object:        obj,
-			GoFieldName:   goName,
-			ForceResolver: forceResolver,
-		})
-	}
-
-	return obj, nil
-}

vendor/github.com/99designs/gqlgen/codegen/templates/args.gotpl πŸ”—

@@ -1,13 +0,0 @@
-	args := map[string]interface{}{}
-	{{- range $i, $arg := . }}
-		var arg{{$i}} {{$arg.Signature }}
-		if tmp, ok := rawArgs[{{$arg.GQLName|quote}}]; ok {
-			var err error
-			{{$arg.Unmarshal (print "arg" $i) "tmp" }}
-			if err != nil {
-				return nil, err
-			}
-		}
-		args[{{$arg.GQLName|quote}}] = arg{{$i}}
-	{{- end }}
-	return args, nil

vendor/github.com/99designs/gqlgen/codegen/templates/data.go πŸ”—

@@ -1,13 +0,0 @@
-package templates
-
-var data = map[string]string{
-	"args.gotpl":      "\targs := map[string]interface{}{}\n\t{{- range $i, $arg := . }}\n\t\tvar arg{{$i}} {{$arg.Signature }}\n\t\tif tmp, ok := rawArgs[{{$arg.GQLName|quote}}]; ok {\n\t\t\tvar err error\n\t\t\t{{$arg.Unmarshal (print \"arg\" $i) \"tmp\" }}\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\targs[{{$arg.GQLName|quote}}] = arg{{$i}}\n\t{{- end }}\n\treturn args, nil\n",

vendor/github.com/99designs/gqlgen/codegen/templates/import.go πŸ”—

@@ -2,10 +2,10 @@ package templates
 
 import (
 	"fmt"
-	"go/build"
+	"go/types"
 	"strconv"
 
-	"github.com/99designs/gqlgen/internal/gopath"
+	"github.com/99designs/gqlgen/internal/code"
 )
 
 type Import struct {
@@ -38,43 +38,42 @@ func (s *Imports) String() string {
 	return res
 }
 
-func (s *Imports) Reserve(path string, aliases ...string) string {
+func (s *Imports) Reserve(path string, aliases ...string) (string, error) {
 	if path == "" {
 		panic("empty ambient import")
 	}
 
 	// if we are referencing our own package we dont need an import
-	if gopath.MustDir2Import(s.destDir) == path {
-		return ""
-	}
-
-	pkg, err := build.Default.Import(path, s.destDir, 0)
-	if err != nil {
-		panic(err)
+	if code.ImportPathForDir(s.destDir) == path {
+		return "", nil
 	}
 
+	name := code.NameForPackage(path)
 	var alias string
 	if len(aliases) != 1 {
-		alias = pkg.Name
+		alias = name
 	} else {
 		alias = aliases[0]
 	}
 
 	if existing := s.findByPath(path); existing != nil {
-		panic("ambient import already exists")
+		if existing.Alias == alias {
+			return "", nil
+		}
+		return "", fmt.Errorf("ambient import already exists")
 	}
 
 	if alias := s.findByAlias(alias); alias != nil {
-		panic("ambient import collides on an alias")
+		return "", fmt.Errorf("ambient import collides on an alias")
 	}
 
 	s.imports = append(s.imports, &Import{
-		Name:  pkg.Name,
+		Name:  name,
 		Path:  path,
 		Alias: alias,
 	})
 
-	return ""
+	return "", nil
 }
 
 func (s *Imports) Lookup(path string) string {
@@ -82,8 +81,10 @@ func (s *Imports) Lookup(path string) string {
 		return ""
 	}
 
+	path = code.NormalizeVendor(path)
+
 	// if we are referencing our own package we dont need an import
-	if gopath.MustDir2Import(s.destDir) == path {
+	if code.ImportPathForDir(s.destDir) == path {
 		return ""
 	}
 
@@ -91,13 +92,8 @@ func (s *Imports) Lookup(path string) string {
 		return existing.Alias
 	}
 
-	pkg, err := build.Default.Import(path, s.destDir, 0)
-	if err != nil {
-		panic(err)
-	}
-
 	imp := &Import{
-		Name: pkg.Name,
+		Name: code.NameForPackage(path),
 		Path: path,
 	}
 	s.imports = append(s.imports, imp)
@@ -116,6 +112,12 @@ func (s *Imports) Lookup(path string) string {
 	return imp.Alias
 }
 
+func (s *Imports) LookupType(t types.Type) string {
+	return types.TypeString(t, func(i *types.Package) string {
+		return s.Lookup(i.Path())
+	})
+}
+
 func (s Imports) findByPath(importPath string) *Import {
 	for _, imp := range s.imports {
 		if imp.Path == importPath {

vendor/github.com/99designs/gqlgen/codegen/templates/input.gotpl πŸ”—

@@ -1,28 +0,0 @@
-	{{- if .IsMarshaled }}
-	func Unmarshal{{ .GQLType }}(v interface{}) ({{.FullName}}, error) {
-		var it {{.FullName}}
-		var asMap = v.(map[string]interface{})
-		{{ range $field := .Fields}}
-			{{- if $field.Default}}
-				if _, present := asMap[{{$field.GQLName|quote}}] ; !present {
-					asMap[{{$field.GQLName|quote}}] = {{ $field.Default | dump }}
-				}
-			{{- end}}
-		{{- end }}
-
-		for k, v := range asMap {
-			switch k {
-			{{- range $field := .Fields }}
-			case {{$field.GQLName|quote}}:
-				var err error
-				{{ $field.Unmarshal (print "it." $field.GoFieldName) "v" }}
-				if err != nil {
-					return it, err
-				}
-			{{- end }}
-			}
-		}
-
-		return it, nil
-	}
-	{{- end }}

vendor/github.com/99designs/gqlgen/codegen/templates/interface.gotpl πŸ”—

@@ -1,18 +0,0 @@
-{{- $interface := . }}
-
-func (ec *executionContext) _{{$interface.GQLType}}(ctx context.Context, sel ast.SelectionSet, obj *{{$interface.FullName}}) graphql.Marshaler {
-	switch obj := (*obj).(type) {
-	case nil:
-		return graphql.Null
-	{{- range $implementor := $interface.Implementors }}
-		{{- if $implementor.ValueReceiver }}
-			case {{$implementor.FullName}}:
-				return ec._{{$implementor.GQLType}}(ctx, sel, &obj)
-		{{- end}}
-		case *{{$implementor.FullName}}:
-			return ec._{{$implementor.GQLType}}(ctx, sel, obj)
-	{{- end }}
-	default:
-		panic(fmt.Errorf("unexpected type %T", obj))
-	}
-}

vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl πŸ”—

@@ -1,91 +0,0 @@
-// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.
-
-package {{ .PackageName }}
-
-import (
-	%%%IMPORTS%%%
-
-	{{ reserveImport "context"  }}
-	{{ reserveImport "fmt"  }}
-	{{ reserveImport "io"  }}
-	{{ reserveImport "strconv"  }}
-	{{ reserveImport "time"  }}
-	{{ reserveImport "sync"  }}
-	{{ reserveImport "errors"  }}
-	{{ reserveImport "bytes"  }}
-
-	{{ reserveImport "github.com/vektah/gqlparser" }}
-	{{ reserveImport "github.com/vektah/gqlparser/ast" }}
-	{{ reserveImport "github.com/99designs/gqlgen/graphql" }}
-	{{ reserveImport "github.com/99designs/gqlgen/graphql/introspection" }}
-)
-
-{{ range $model := .Models }}
-	{{with .Description}} {{.|prefixLines "// "}} {{end}}
-	{{- if .IsInterface }}
-		type {{.GoType}} interface {
-			Is{{.GoType}}()
-		}
-	{{- else }}
-		type {{.GoType}} struct {
-			{{- range $field := .Fields }}
-				{{- with .Description}}
-					{{.|prefixLines "// "}}
-				{{- end}}
-				{{- if $field.GoFieldName }}
-					{{ $field.GoFieldName }} {{$field.Signature}} `json:"{{$field.GQLName}}"`
-				{{- else }}
-					{{ $field.GoFKName }} {{$field.GoFKType}}
-				{{- end }}
-			{{- end }}
-		}
-
-		{{- range $iface := .Implements }}
-			func ({{$model.GoType}}) Is{{$iface.GoType}}() {}
-		{{- end }}
-
-	{{- end }}
-{{- end}}
-
-{{ range $enum := .Enums }}
-	{{with .Description}}{{.|prefixLines "// "}} {{end}}
-	type {{.GoType}} string
-	const (
-	{{- range $value := .Values}}
-		{{- with .Description}}
-			{{.|prefixLines "// "}}
-		{{- end}}
-		{{$enum.GoType}}{{ .Name|toCamel }} {{$enum.GoType}} = {{.Name|quote}}
-	{{- end }}
-	)
-
-	func (e {{.GoType}}) IsValid() bool {
-		switch e {
-		case {{ range $index, $element := .Values}}{{if $index}},{{end}}{{ $enum.GoType }}{{ $element.Name|toCamel }}{{end}}:
-			return true
-		}
-		return false
-	}
-
-	func (e {{.GoType}}) String() string {
-		return string(e)
-	}
-
-	func (e *{{.GoType}}) UnmarshalGQL(v interface{}) error {
-		str, ok := v.(string)
-		if !ok {
-			return fmt.Errorf("enums must be strings")
-		}
-
-		*e = {{.GoType}}(str)
-		if !e.IsValid() {
-			return fmt.Errorf("%s is not a valid {{.GQLType}}", str)
-		}
-		return nil
-	}
-
-	func (e {{.GoType}}) MarshalGQL(w io.Writer) {
-		fmt.Fprint(w, strconv.Quote(e.String()))
-	}
-
-{{- end }}

vendor/github.com/99designs/gqlgen/codegen/templates/object.gotpl πŸ”—

@@ -1,69 +0,0 @@
-{{ $object := . }}
-
-var {{ $object.GQLType|lcFirst}}Implementors = {{$object.Implementors}}
-
-// nolint: gocyclo, errcheck, gas, goconst
-{{- if .Stream }}
-func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.SelectionSet) func() graphql.Marshaler {
-	fields := graphql.CollectFields(ctx, sel, {{$object.GQLType|lcFirst}}Implementors)
-	ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{
-		Object: {{$object.GQLType|quote}},
-	})
-	if len(fields) != 1 {
-		ec.Errorf(ctx, "must subscribe to exactly one stream")
-		return nil
-	}
-
-	switch fields[0].Name {
-	{{- range $field := $object.Fields }}
-	case "{{$field.GQLName}}":
-		return ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, fields[0])
-	{{- end }}
-	default:
-		panic("unknown field " + strconv.Quote(fields[0].Name))
-	}
-}
-{{- else }}
-func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.SelectionSet{{if not $object.Root}}, obj *{{$object.FullName}} {{end}}) graphql.Marshaler {
-	fields := graphql.CollectFields(ctx, sel, {{$object.GQLType|lcFirst}}Implementors)
-	{{if $object.Root}}
-		ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{
-			Object: {{$object.GQLType|quote}},
-		})
-	{{end}}
-
-	{{if $object.IsConcurrent}} var wg sync.WaitGroup {{end}}
-	out := graphql.NewOrderedMap(len(fields))
-	invalid := false
-	for i, field := range fields {
-		out.Keys[i] = field.Alias
-
-		switch field.Name {
-		case "__typename":
-			out.Values[i] = graphql.MarshalString({{$object.GQLType|quote}})
-		{{- range $field := $object.Fields }}
-		case "{{$field.GQLName}}":
-			{{- if $field.IsConcurrent }}
-				wg.Add(1)
-				go func(i int, field graphql.CollectedField) {
-			{{- end }}
-				out.Values[i] = ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, field{{if not $object.Root}}, obj{{end}})
-				{{- if $field.ASTType.NonNull }}
-					if out.Values[i] == graphql.Null {
-						invalid = true
-					}
-				{{- end }}
-			{{- if $field.IsConcurrent }}
-					wg.Done()
-				}(i, field)
-			{{- end }}
-		{{- end }}
-		default:
-			panic("unknown field " + strconv.Quote(field.Name))
-		}
-	}
-	{{if $object.IsConcurrent}} wg.Wait() {{end}}
-	if invalid { return graphql.Null }
-	return out
-}
-{{- end }}

vendor/github.com/99designs/gqlgen/codegen/templates/resolver.gotpl πŸ”—

@@ -1,44 +0,0 @@
-package {{ .PackageName }}
-
-import (
-	%%%IMPORTS%%%
-
-	{{ reserveImport "context"  }}
-	{{ reserveImport "fmt"  }}
-	{{ reserveImport "io"  }}
-	{{ reserveImport "strconv"  }}
-	{{ reserveImport "time"  }}
-	{{ reserveImport "sync"  }}
-	{{ reserveImport "errors"  }}
-	{{ reserveImport "bytes"  }}
-
-	{{ reserveImport "github.com/99designs/gqlgen/handler" }}
-	{{ reserveImport "github.com/vektah/gqlparser" }}
-	{{ reserveImport "github.com/vektah/gqlparser/ast" }}
-	{{ reserveImport "github.com/99designs/gqlgen/graphql" }}
-	{{ reserveImport "github.com/99designs/gqlgen/graphql/introspection" }}
-)
-
-type {{.ResolverType}} struct {}
-
-{{ range $object := .Objects -}}
-	{{- if $object.HasResolvers -}}
-		func (r *{{$.ResolverType}}) {{$object.GQLType}}() {{ $object.ResolverInterface.FullName }} {
-			return &{{lcFirst $object.GQLType}}Resolver{r}
-		}
-	{{ end -}}
-{{ end }}
-
-{{ range $object := .Objects -}}
-	{{- if $object.HasResolvers -}}
-		type {{lcFirst $object.GQLType}}Resolver struct { *Resolver }
-
-		{{ range $field := $object.Fields -}}
-			{{- if $field.IsResolver -}}
-			func (r *{{lcFirst $object.GQLType}}Resolver) {{ $field.ShortResolverDeclaration }} {
-				panic("not implemented")
-			}
-			{{ end -}}
-		{{ end -}}
-	{{ end -}}
-{{ end }}

vendor/github.com/99designs/gqlgen/codegen/templates/templates.go πŸ”—

@@ -1,13 +1,14 @@
-//go:generate go run ./inliner/inliner.go
-
 package templates
 
 import (
 	"bytes"
 	"fmt"
+	"go/types"
 	"io/ioutil"
 	"os"
 	"path/filepath"
+	"reflect"
+	"runtime"
 	"sort"
 	"strconv"
 	"strings"
@@ -15,40 +16,141 @@ import (
 	"unicode"
 
 	"github.com/99designs/gqlgen/internal/imports"
-
 	"github.com/pkg/errors"
 )
 
 // this is done with a global because subtemplates currently get called in functions. Lets aim to remove this eventually.
 var CurrentImports *Imports
 
-func Run(name string, tpldata interface{}) (*bytes.Buffer, error) {
-	t := template.New("").Funcs(template.FuncMap{
-		"ucFirst":       ucFirst,
-		"lcFirst":       lcFirst,
-		"quote":         strconv.Quote,
-		"rawQuote":      rawQuote,
-		"toCamel":       ToCamel,
-		"dump":          dump,
-		"prefixLines":   prefixLines,
-		"reserveImport": CurrentImports.Reserve,
-		"lookupImport":  CurrentImports.Lookup,
+type Options struct {
+	PackageName     string
+	Filename        string
+	RegionTags      bool
+	GeneratedHeader bool
+	Data            interface{}
+	Funcs           template.FuncMap
+}
+
+func Render(cfg Options) error {
+	if CurrentImports != nil {
+		panic(fmt.Errorf("recursive or concurrent call to RenderToFile detected"))
+	}
+	CurrentImports = &Imports{destDir: filepath.Dir(cfg.Filename)}
+
+	// load path relative to calling source file
+	_, callerFile, _, _ := runtime.Caller(1)
+	rootDir := filepath.Dir(callerFile)
+
+	funcs := Funcs()
+	for n, f := range cfg.Funcs {
+		funcs[n] = f
+	}
+	t := template.New("").Funcs(funcs)
+
+	var roots []string
+	// load all the templates in the directory
+	err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+		name := filepath.ToSlash(strings.TrimPrefix(path, rootDir+string(os.PathSeparator)))
+		if !strings.HasSuffix(info.Name(), ".gotpl") {
+			return nil
+		}
+		b, err := ioutil.ReadFile(path)
+		if err != nil {
+			return err
+		}
+
+		t, err = t.New(name).Parse(string(b))
+		if err != nil {
+			return errors.Wrap(err, cfg.Filename)
+		}
+
+		roots = append(roots, name)
+
+		return nil
 	})
+	if err != nil {
+		return errors.Wrap(err, "locating templates")
+	}
 
-	for filename, data := range data {
-		_, err := t.New(filename).Parse(data)
+	// then execute all the important looking ones in order, adding them to the same file
+	sort.Slice(roots, func(i, j int) bool {
+		// important files go first
+		if strings.HasSuffix(roots[i], "!.gotpl") {
+			return true
+		}
+		if strings.HasSuffix(roots[j], "!.gotpl") {
+			return false
+		}
+		return roots[i] < roots[j]
+	})
+	var buf bytes.Buffer
+	for _, root := range roots {
+		if cfg.RegionTags {
+			buf.WriteString("\n// region    " + center(70, "*", " "+root+" ") + "\n")
+		}
+		err = t.Lookup(root).Execute(&buf, cfg.Data)
 		if err != nil {
-			panic(err)
+			return errors.Wrap(err, root)
+		}
+		if cfg.RegionTags {
+			buf.WriteString("\n// endregion " + center(70, "*", " "+root+" ") + "\n")
 		}
 	}
 
-	buf := &bytes.Buffer{}
-	err := t.Lookup(name).Execute(buf, tpldata)
+	var result bytes.Buffer
+	if cfg.GeneratedHeader {
+		result.WriteString("// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.\n\n")
+	}
+	result.WriteString("package ")
+	result.WriteString(cfg.PackageName)
+	result.WriteString("\n\n")
+	result.WriteString("import (\n")
+	result.WriteString(CurrentImports.String())
+	result.WriteString(")\n")
+	_, err = buf.WriteTo(&result)
 	if err != nil {
-		return nil, err
+		return err
 	}
+	CurrentImports = nil
 
-	return buf, nil
+	return write(cfg.Filename, result.Bytes())
+}
+
+func center(width int, pad string, s string) string {
+	if len(s)+2 > width {
+		return s
+	}
+	lpad := (width - len(s)) / 2
+	rpad := width - (lpad + len(s))
+	return strings.Repeat(pad, lpad) + s + strings.Repeat(pad, rpad)
+}
+
+func Funcs() template.FuncMap {
+	return template.FuncMap{
+		"ucFirst":       ucFirst,
+		"lcFirst":       lcFirst,
+		"quote":         strconv.Quote,
+		"rawQuote":      rawQuote,
+		"dump":          Dump,
+		"ref":           ref,
+		"ts":            TypeIdentifier,
+		"call":          Call,
+		"prefixLines":   prefixLines,
+		"notNil":        notNil,
+		"reserveImport": CurrentImports.Reserve,
+		"lookupImport":  CurrentImports.Lookup,
+		"go":            ToGo,
+		"goPrivate":     ToGoPrivate,
+		"add": func(a, b int) int {
+			return a + b
+		},
+		"render": func(filename string, tpldata interface{}) (*bytes.Buffer, error) {
+			return render(resolveName(filename, 0), tpldata)
+		},
+	}
 }
 
 func ucFirst(s string) string {
@@ -74,37 +176,276 @@ func isDelimiter(c rune) bool {
 	return c == '-' || c == '_' || unicode.IsSpace(c)
 }
 
-func ToCamel(s string) string {
-	buffer := make([]rune, 0, len(s))
-	upper := true
-	lastWasUpper := false
+func ref(p types.Type) string {
+	return CurrentImports.LookupType(p)
+}
 
-	for _, c := range s {
-		if isDelimiter(c) {
-			upper = true
+var pkgReplacer = strings.NewReplacer(
+	"/", "αš‹",
+	".", "αš—",
+	"-", "αš‘",
+)
+
+func TypeIdentifier(t types.Type) string {
+	res := ""
+	for {
+		switch it := t.(type) {
+		case *types.Pointer:
+			t.Underlying()
+			res += "αš–"
+			t = it.Elem()
+		case *types.Slice:
+			res += "αš•"
+			t = it.Elem()
+		case *types.Named:
+			res += pkgReplacer.Replace(it.Obj().Pkg().Path())
+			res += "ᚐ"
+			res += it.Obj().Name()
+			return res
+		case *types.Basic:
+			res += it.Name()
+			return res
+		case *types.Map:
+			res += "map"
+			return res
+		case *types.Interface:
+			res += "interface"
+			return res
+		default:
+			panic(fmt.Errorf("unexpected type %T", it))
+		}
+	}
+}
+
+func Call(p *types.Func) string {
+	pkg := CurrentImports.Lookup(p.Pkg().Path())
+
+	if pkg != "" {
+		pkg += "."
+	}
+
+	if p.Type() != nil {
+		// make sure the returned type is listed in our imports.
+		ref(p.Type().(*types.Signature).Results().At(0).Type())
+	}
+
+	return pkg + p.Name()
+}
+
+func ToGo(name string) string {
+	runes := make([]rune, 0, len(name))
+
+	wordWalker(name, func(info *wordInfo) {
+		word := info.Word
+		if info.MatchCommonInitial {
+			word = strings.ToUpper(word)
+		} else if !info.HasCommonInitial {
+			if strings.ToUpper(word) == word || strings.ToLower(word) == word {
+				// FOO or foo β†’ Foo
+				// FOo β†’ FOo
+				word = ucFirst(strings.ToLower(word))
+			}
+		}
+		runes = append(runes, []rune(word)...)
+	})
+
+	return string(runes)
+}
+
+func ToGoPrivate(name string) string {
+	runes := make([]rune, 0, len(name))
+
+	first := true
+	wordWalker(name, func(info *wordInfo) {
+		word := info.Word
+		if first {
+			if strings.ToUpper(word) == word || strings.ToLower(word) == word {
+				// ID β†’ id, CAMEL β†’ camel
+				word = strings.ToLower(info.Word)
+			} else {
+				// ITicket β†’ iTicket
+				word = lcFirst(info.Word)
+			}
+			first = false
+		} else if info.MatchCommonInitial {
+			word = strings.ToUpper(word)
+		} else if !info.HasCommonInitial {
+			word = ucFirst(strings.ToLower(word))
+		}
+		runes = append(runes, []rune(word)...)
+	})
+
+	return sanitizeKeywords(string(runes))
+}
+
+type wordInfo struct {
+	Word               string
+	MatchCommonInitial bool
+	HasCommonInitial   bool
+}
+
+// This function is based on the following code.
+// https://github.com/golang/lint/blob/06c8688daad7faa9da5a0c2f163a3d14aac986ca/lint.go#L679
+func wordWalker(str string, f func(*wordInfo)) {
+	runes := []rune(str)
+	w, i := 0, 0 // index of start of word, scan
+	hasCommonInitial := false
+	for i+1 <= len(runes) {
+		eow := false // whether we hit the end of a word
+		if i+1 == len(runes) {
+			eow = true
+		} else if isDelimiter(runes[i+1]) {
+			// underscore; shift the remainder forward over any run of underscores
+			eow = true
+			n := 1
+			for i+n+1 < len(runes) && isDelimiter(runes[i+n+1]) {
+				n++
+			}
+
+			// Leave at most one underscore if the underscore is between two digits
+			if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
+				n--
+			}
+
+			copy(runes[i+1:], runes[i+n+1:])
+			runes = runes[:len(runes)-n]
+		} else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
+			// lower->non-lower
+			eow = true
+		}
+		i++
+
+		// [w,i) is a word.
+		word := string(runes[w:i])
+		if !eow && commonInitialisms[word] && !unicode.IsLower(runes[i]) {
+			// through
+			// split IDFoo β†’ ID, Foo
+			// but URLs β†’ URLs
+		} else if !eow {
+			if commonInitialisms[word] {
+				hasCommonInitial = true
+			}
 			continue
 		}
-		if !lastWasUpper && unicode.IsUpper(c) {
-			upper = true
+
+		matchCommonInitial := false
+		if commonInitialisms[strings.ToUpper(word)] {
+			hasCommonInitial = true
+			matchCommonInitial = true
 		}
 
-		if upper {
-			buffer = append(buffer, unicode.ToUpper(c))
-		} else {
-			buffer = append(buffer, unicode.ToLower(c))
+		f(&wordInfo{
+			Word:               word,
+			MatchCommonInitial: matchCommonInitial,
+			HasCommonInitial:   hasCommonInitial,
+		})
+		hasCommonInitial = false
+		w = i
+	}
+}
+
+var keywords = []string{
+	"break",
+	"default",
+	"func",
+	"interface",
+	"select",
+	"case",
+	"defer",
+	"go",
+	"map",
+	"struct",
+	"chan",
+	"else",
+	"goto",
+	"package",
+	"switch",
+	"const",
+	"fallthrough",
+	"if",
+	"range",
+	"type",
+	"continue",
+	"for",
+	"import",
+	"return",
+	"var",
+	"_",
+}
+
+// sanitizeKeywords prevents collisions with go keywords for arguments to resolver functions
+func sanitizeKeywords(name string) string {
+	for _, k := range keywords {
+		if name == k {
+			return name + "Arg"
 		}
-		upper = false
-		lastWasUpper = unicode.IsUpper(c)
 	}
+	return name
+}
 
-	return string(buffer)
+// commonInitialisms is a set of common initialisms.
+// Only add entries that are highly unlikely to be non-initialisms.
+// For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
+var commonInitialisms = map[string]bool{
+	"ACL":   true,
+	"API":   true,
+	"ASCII": true,
+	"CPU":   true,
+	"CSS":   true,
+	"DNS":   true,
+	"EOF":   true,
+	"GUID":  true,
+	"HTML":  true,
+	"HTTP":  true,
+	"HTTPS": true,
+	"ID":    true,
+	"IP":    true,
+	"JSON":  true,
+	"LHS":   true,
+	"QPS":   true,
+	"RAM":   true,
+	"RHS":   true,
+	"RPC":   true,
+	"SLA":   true,
+	"SMTP":  true,
+	"SQL":   true,
+	"SSH":   true,
+	"TCP":   true,
+	"TLS":   true,
+	"TTL":   true,
+	"UDP":   true,
+	"UI":    true,
+	"UID":   true,
+	"UUID":  true,
+	"URI":   true,
+	"URL":   true,
+	"UTF8":  true,
+	"VM":    true,
+	"XML":   true,
+	"XMPP":  true,
+	"XSRF":  true,
+	"XSS":   true,
 }
 
 func rawQuote(s string) string {
 	return "`" + strings.Replace(s, "`", "`+\"`\"+`", -1) + "`"
 }
 
-func dump(val interface{}) string {
+func notNil(field string, data interface{}) bool {
+	v := reflect.ValueOf(data)
+
+	if v.Kind() == reflect.Ptr {
+		v = v.Elem()
+	}
+	if v.Kind() != reflect.Struct {
+		return false
+	}
+	val := v.FieldByName(field)
+
+	return val.IsValid() && !val.IsNil()
+}
+
+func Dump(val interface{}) string {
 	switch val := val.(type) {
 	case int:
 		return strconv.Itoa(val)
@@ -121,7 +462,7 @@ func dump(val interface{}) string {
 	case []interface{}:
 		var parts []string
 		for _, part := range val {
-			parts = append(parts, dump(part))
+			parts = append(parts, Dump(part))
 		}
 		return "[]interface{}{" + strings.Join(parts, ",") + "}"
 	case map[string]interface{}:
@@ -138,7 +479,7 @@ func dump(val interface{}) string {
 
 			buf.WriteString(strconv.Quote(key))
 			buf.WriteString(":")
-			buf.WriteString(dump(data))
+			buf.WriteString(Dump(data))
 			buf.WriteString(",")
 		}
 		buf.WriteString("}")
@@ -152,22 +493,33 @@ func prefixLines(prefix, s string) string {
 	return prefix + strings.Replace(s, "\n", "\n"+prefix, -1)
 }
 
-func RenderToFile(tpl string, filename string, data interface{}) error {
-	if CurrentImports != nil {
-		panic(fmt.Errorf("recursive or concurrent call to RenderToFile detected"))
+func resolveName(name string, skip int) string {
+	if name[0] == '.' {
+		// load path relative to calling source file
+		_, callerFile, _, _ := runtime.Caller(skip + 1)
+		return filepath.Join(filepath.Dir(callerFile), name[1:])
 	}
-	CurrentImports = &Imports{destDir: filepath.Dir(filename)}
 
-	var buf *bytes.Buffer
-	buf, err := Run(tpl, data)
+	// load path relative to this directory
+	_, callerFile, _, _ := runtime.Caller(0)
+	return filepath.Join(filepath.Dir(callerFile), name)
+}
+
+func render(filename string, tpldata interface{}) (*bytes.Buffer, error) {
+	t := template.New("").Funcs(Funcs())
+
+	b, err := ioutil.ReadFile(filename)
 	if err != nil {
-		return errors.Wrap(err, filename+" generation failed")
+		return nil, err
 	}
 
-	b := bytes.Replace(buf.Bytes(), []byte("%%%IMPORTS%%%"), []byte(CurrentImports.String()), -1)
-	CurrentImports = nil
+	t, err = t.New(filepath.Base(filename)).Parse(string(b))
+	if err != nil {
+		panic(err)
+	}
 
-	return write(filename, b)
+	buf := &bytes.Buffer{}
+	return buf, t.Execute(buf, tpldata)
 }
 
 func write(filename string, b []byte) error {

vendor/github.com/99designs/gqlgen/codegen/type.go πŸ”—

@@ -1,174 +1,18 @@
 package codegen
 
 import (
-	"strconv"
-	"strings"
-
-	"github.com/99designs/gqlgen/codegen/templates"
-
-	"github.com/vektah/gqlparser/ast"
-)
-
-type NamedTypes map[string]*NamedType
-
-type NamedType struct {
-	Ref
-	IsScalar    bool
-	IsInterface bool
-	IsInput     bool
-	GQLType     string // Name of the graphql type
-	Marshaler   *Ref   // If this type has an external marshaler this will be set
-}
-
-type Ref struct {
-	GoType        string // Name of the go type
-	Package       string // the package the go type lives in
-	IsUserDefined bool   // does the type exist in the typemap
-}
-
-type Type struct {
-	*NamedType
-
-	Modifiers   []string
-	ASTType     *ast.Type
-	AliasedType *Ref
-}
-
-const (
-	modList = "[]"
-	modPtr  = "*"
+	"github.com/99designs/gqlgen/codegen/config"
 )
 
-func (t Ref) FullName() string {
-	return t.PkgDot() + t.GoType
-}
-
-func (t Ref) PkgDot() string {
-	name := templates.CurrentImports.Lookup(t.Package)
-	if name == "" {
-		return ""
-
-	}
-
-	return name + "."
-}
-
-func (t Type) Signature() string {
-	if t.AliasedType != nil {
-		return strings.Join(t.Modifiers, "") + t.AliasedType.FullName()
-	}
-	return strings.Join(t.Modifiers, "") + t.FullName()
-}
-
-func (t Type) FullSignature() string {
-	pkg := ""
-	if t.Package != "" {
-		pkg = t.Package + "."
-	}
-
-	return strings.Join(t.Modifiers, "") + pkg + t.GoType
-}
+func (b *builder) buildTypes() (map[string]*config.TypeReference, error) {
+	ret := map[string]*config.TypeReference{}
 
-func (t Type) IsPtr() bool {
-	return len(t.Modifiers) > 0 && t.Modifiers[0] == modPtr
-}
+	for _, ref := range b.Binder.References {
+		for ref != nil {
+			ret[ref.UniquenessKey()] = ref
 
-func (t *Type) StripPtr() {
-	if !t.IsPtr() {
-		return
+			ref = ref.Elem()
+		}
 	}
-	t.Modifiers = t.Modifiers[0 : len(t.Modifiers)-1]
-}
-
-func (t Type) IsSlice() bool {
-	return len(t.Modifiers) > 0 && t.Modifiers[0] == modList ||
-		len(t.Modifiers) > 1 && t.Modifiers[0] == modPtr && t.Modifiers[1] == modList
-}
-
-func (t NamedType) IsMarshaled() bool {
-	return t.Marshaler != nil
-}
-
-func (t Type) Unmarshal(result, raw string) string {
-	return t.unmarshal(result, raw, t.Modifiers, 1)
-}
-
-func (t Type) unmarshal(result, raw string, remainingMods []string, depth int) string {
-	switch {
-	case len(remainingMods) > 0 && remainingMods[0] == modPtr:
-		ptr := "ptr" + strconv.Itoa(depth)
-		return tpl(`var {{.ptr}} {{.mods}}{{.t.FullName}}
-			if {{.raw}} != nil {
-				{{.next}}
-				{{.result}} = &{{.ptr -}}
-			}
-		`, map[string]interface{}{
-			"ptr":    ptr,
-			"t":      t,
-			"raw":    raw,
-			"result": result,
-			"mods":   strings.Join(remainingMods[1:], ""),
-			"next":   t.unmarshal(ptr, raw, remainingMods[1:], depth+1),
-		})
-
-	case len(remainingMods) > 0 && remainingMods[0] == modList:
-		var rawIf = "rawIf" + strconv.Itoa(depth)
-		var index = "idx" + strconv.Itoa(depth)
-
-		return tpl(`var {{.rawSlice}} []interface{}
-			if {{.raw}} != nil {
-				if tmp1, ok := {{.raw}}.([]interface{}); ok {
-					{{.rawSlice}} = tmp1
-				} else {
-					{{.rawSlice}} = []interface{}{ {{.raw}} }
-				}
-			}
-			{{.result}} = make({{.type}}, len({{.rawSlice}}))
-			for {{.index}} := range {{.rawSlice}} {
-				{{ .next -}}
-			}`, map[string]interface{}{
-			"raw":      raw,
-			"rawSlice": rawIf,
-			"index":    index,
-			"result":   result,
-			"type":     strings.Join(remainingMods, "") + t.NamedType.FullName(),
-			"next":     t.unmarshal(result+"["+index+"]", rawIf+"["+index+"]", remainingMods[1:], depth+1),
-		})
-	}
-
-	realResult := result
-	if t.AliasedType != nil {
-		result = "castTmp"
-	}
-
-	return tpl(`{{- if .t.AliasedType }}
-			var castTmp {{.t.FullName}}
-		{{ end }}
-			{{- if eq .t.GoType "map[string]interface{}" }}
-				{{- .result }} = {{.raw}}.(map[string]interface{})
-			{{- else if .t.Marshaler }}
-				{{- .result }}, err = {{ .t.Marshaler.PkgDot }}Unmarshal{{.t.Marshaler.GoType}}({{.raw}})
-			{{- else -}}
-				err = (&{{.result}}).UnmarshalGQL({{.raw}})
-			{{- end }}
-		{{- if .t.AliasedType }}
-			{{ .realResult }} = {{.t.AliasedType.FullName}}(castTmp)
-		{{- end }}`, map[string]interface{}{
-		"realResult": realResult,
-		"result":     result,
-		"raw":        raw,
-		"t":          t,
-	})
-}
-
-func (t Type) Marshal(val string) string {
-	if t.AliasedType != nil {
-		val = t.GoType + "(" + val + ")"
-	}
-
-	if t.Marshaler != nil {
-		return "return " + t.Marshaler.PkgDot() + "Marshal" + t.Marshaler.GoType + "(" + val + ")"
-	}
-
-	return "return " + val
+	return ret, nil
 }

vendor/github.com/99designs/gqlgen/codegen/type.gotpl πŸ”—

@@ -0,0 +1,131 @@
+{{- range $type := .ReferencedTypes }}
+	{{ with $type.UnmarshalFunc }}
+		func (ec *executionContext) {{ . }}(ctx context.Context, v interface{}) ({{ $type.GO | ref }}, error) {
+			{{- if $type.IsNilable }}
+				if v == nil { return nil, nil }
+			{{- end }}
+			{{- if $type.IsPtr }}
+				res, err := ec.{{ $type.Elem.UnmarshalFunc }}(ctx, v)
+				return &res, err
+			{{- else if $type.IsSlice }}
+				var vSlice []interface{}
+				if v != nil {
+					if tmp1, ok := v.([]interface{}); ok {
+						vSlice = tmp1
+					} else {
+						vSlice = []interface{}{ v }
+					}
+				}
+				var err error
+				res := make([]{{$type.GO.Elem | ref}}, len(vSlice))
+				for i := range vSlice {
+					res[i], err = ec.{{ $type.Elem.UnmarshalFunc }}(ctx, vSlice[i])
+					if err != nil {
+						return nil, err
+					}
+				}
+				return res, nil
+			{{- else }}
+				{{- if $type.Unmarshaler }}
+					{{- if $type.CastType }}
+						tmp, err := {{ $type.Unmarshaler | call }}(v)
+						return {{ $type.GO | ref }}(tmp), err
+					{{- else}}
+						return {{ $type.Unmarshaler | call }}(v)
+					{{- end }}
+				{{- else if eq ($type.GO | ref) "map[string]interface{}" }}
+					return v.(map[string]interface{}), nil
+				{{- else if $type.IsMarshaler -}}
+					var res {{ $type.GO | ref }}
+					return res, res.UnmarshalGQL(v)
+				{{- else }}
+					return ec.unmarshalInput{{ $type.GQL.Name }}(ctx, v)
+				{{- end }}
+			{{- end }}
+		}
+	{{- end }}
+
+	{{ with $type.MarshalFunc }}
+		func (ec *executionContext) {{ . }}(ctx context.Context, sel ast.SelectionSet, v {{ $type.GO | ref }}) graphql.Marshaler {
+			{{- if $type.IsNilable }}
+				if v == nil {
+					{{- if $type.GQL.NonNull }}
+						if !ec.HasError(graphql.GetResolverContext(ctx)) {
+							ec.Errorf(ctx, "must not be null")
+						}
+					{{- end }}
+					return graphql.Null
+				}
+			{{- else if $type.HasIsZero }}
+				if v.IsZero() {
+					{{- if $type.GQL.NonNull }}
+						if !ec.HasError(graphql.GetResolverContext(ctx)) {
+							ec.Errorf(ctx, "must not be null")
+						}
+					{{- end }}
+					return graphql.Null
+				}
+			{{- end }}
+
+			{{- if $type.IsSlice }}
+				{{- if not $type.GQL.NonNull }}
+					if v == nil {
+						return graphql.Null
+					}
+				{{- end }}
+				ret := make(graphql.Array, len(v))
+				{{- if not $type.IsScalar }}
+					var wg sync.WaitGroup
+					isLen1 := len(v) == 1
+					if !isLen1 {
+						wg.Add(len(v))
+					}
+				{{- end }}
+				for i := range v {
+					{{- if not $type.IsScalar }}
+						i := i
+						rctx := &graphql.ResolverContext{
+							Index: &i,
+							Result: &v[i],
+						}
+						ctx := graphql.WithResolverContext(ctx, rctx)
+						f := func(i int) {
+							defer func() {
+                        		if r := recover(); r != nil {
+                        			ec.Error(ctx, ec.Recover(ctx, r))
+                        			ret = nil
+                        		}
+                        	}()
+							if !isLen1 {
+								defer wg.Done()
+							}
+							ret[i] = ec.{{ $type.Elem.MarshalFunc }}(ctx, sel, v[i])
+						}
+						if isLen1 {
+							f(i)
+						} else {
+							go f(i)
+						}
+					{{ else }}
+						ret[i] = ec.{{ $type.Elem.MarshalFunc }}(ctx, sel, v[i])
+					{{- end}}
+				}
+				{{ if not $type.IsScalar }} wg.Wait() {{ end }}
+				return ret
+			{{- else }}
+
+				{{- if $type.IsMarshaler }}
+					return v
+				{{- else if $type.Marshaler }}
+					{{- if $type.IsPtr }}
+						return ec.{{ $type.Elem.MarshalFunc }}(ctx, sel, *v)
+					{{- else }}
+						return {{ $type.Marshaler | call }}({{- if $type.CastType }}{{ $type.CastType | ref }}(v){{else}}v{{- end }})
+					{{- end }}
+				{{- else }}
+					return ec._{{$type.Definition.Name}}(ctx, sel, {{ if not $type.IsNilable}}&{{end}} v)
+				{{- end }}
+			{{- end }}
+		}
+	{{- end }}
+{{- end }}

vendor/github.com/99designs/gqlgen/codegen/type_build.go πŸ”—

@@ -1,100 +0,0 @@
-package codegen
-
-import (
-	"go/types"
-	"strings"
-
-	"github.com/vektah/gqlparser/ast"
-	"golang.org/x/tools/go/loader"
-)
-
-// namedTypeFromSchema objects for every graphql type, including scalars. There should only be one instance of Type for each thing
-func (cfg *Config) buildNamedTypes() NamedTypes {
-	types := map[string]*NamedType{}
-	for _, schemaType := range cfg.schema.Types {
-		t := namedTypeFromSchema(schemaType)
-
-		if userEntry, ok := cfg.Models[t.GQLType]; ok && userEntry.Model != "" {
-			t.IsUserDefined = true
-			t.Package, t.GoType = pkgAndType(userEntry.Model)
-		} else if t.IsScalar {
-			t.Package = "github.com/99designs/gqlgen/graphql"
-			t.GoType = "String"
-		}
-
-		types[t.GQLType] = t
-	}
-	return types
-}
-
-func (cfg *Config) bindTypes(namedTypes NamedTypes, destDir string, prog *loader.Program) {
-	for _, t := range namedTypes {
-		if t.Package == "" {
-			continue
-		}
-
-		def, _ := findGoType(prog, t.Package, "Marshal"+t.GoType)
-		switch def := def.(type) {
-		case *types.Func:
-			sig := def.Type().(*types.Signature)
-			cpy := t.Ref
-			t.Marshaler = &cpy
-
-			t.Package, t.GoType = pkgAndType(sig.Params().At(0).Type().String())
-		}
-	}
-}
-
-// namedTypeFromSchema objects for every graphql type, including primitives.
-// don't recurse into object fields or interfaces yet, lets make sure we have collected everything first.
-func namedTypeFromSchema(schemaType *ast.Definition) *NamedType {
-	switch schemaType.Kind {
-	case ast.Scalar, ast.Enum:
-		return &NamedType{GQLType: schemaType.Name, IsScalar: true}
-	case ast.Interface, ast.Union:
-		return &NamedType{GQLType: schemaType.Name, IsInterface: true}
-	case ast.InputObject:
-		return &NamedType{GQLType: schemaType.Name, IsInput: true}
-	default:
-		return &NamedType{GQLType: schemaType.Name}
-	}
-}
-
-// take a string in the form github.com/package/blah.Type and split it into package and type
-func pkgAndType(name string) (string, string) {
-	parts := strings.Split(name, ".")
-	if len(parts) == 1 {
-		return "", name
-	}
-
-	return normalizeVendor(strings.Join(parts[:len(parts)-1], ".")), parts[len(parts)-1]
-}
-
-func (n NamedTypes) getType(t *ast.Type) *Type {
-	orig := t
-	var modifiers []string
-	for {
-		if t.Elem != nil {
-			modifiers = append(modifiers, modList)
-			t = t.Elem
-		} else {
-			if !t.NonNull {
-				modifiers = append(modifiers, modPtr)
-			}
-			if n[t.NamedType] == nil {
-				panic("missing type " + t.NamedType)
-			}
-			res := &Type{
-				NamedType: n[t.NamedType],
-				Modifiers: modifiers,
-				ASTType:   orig,
-			}
-
-			if res.IsInterface {
-				res.StripPtr()
-			}
-
-			return res
-		}
-	}
-}

vendor/github.com/99designs/gqlgen/codegen/util.go πŸ”—

@@ -1,65 +1,30 @@
 package codegen
 
 import (
-	"fmt"
 	"go/types"
-	"reflect"
-	"regexp"
 	"strings"
 
 	"github.com/pkg/errors"
-	"golang.org/x/tools/go/loader"
 )
 
-func findGoType(prog *loader.Program, pkgName string, typeName string) (types.Object, error) {
-	if pkgName == "" {
-		return nil, nil
-	}
-	fullName := typeName
-	if pkgName != "" {
-		fullName = pkgName + "." + typeName
-	}
-
-	pkgName, err := resolvePkg(pkgName)
-	if err != nil {
-		return nil, errors.Errorf("unable to resolve package for %s: %s\n", fullName, err.Error())
-	}
-
-	pkg := prog.Imported[pkgName]
-	if pkg == nil {
-		return nil, errors.Errorf("required package was not loaded: %s", fullName)
-	}
-
-	for astNode, def := range pkg.Defs {
-		if astNode.Name != typeName || def.Parent() == nil || def.Parent() != pkg.Pkg.Scope() {
-			continue
-		}
-
-		return def, nil
-	}
-
-	return nil, errors.Errorf("unable to find type %s\n", fullName)
-}
-
-func findGoNamedType(prog *loader.Program, pkgName string, typeName string) (*types.Named, error) {
-	def, err := findGoType(prog, pkgName, typeName)
-	if err != nil {
-		return nil, err
-	}
+func findGoNamedType(def types.Type) (*types.Named, error) {
 	if def == nil {
 		return nil, nil
 	}
 
-	namedType, ok := def.Type().(*types.Named)
+	namedType, ok := def.(*types.Named)
 	if !ok {
-		return nil, errors.Errorf("expected %s to be a named type, instead found %T\n", typeName, def.Type())
+		return nil, errors.Errorf("expected %s to be a named type, instead found %T\n", def.String(), def)
 	}
 
 	return namedType, nil
 }
 
-func findGoInterface(prog *loader.Program, pkgName string, typeName string) (*types.Interface, error) {
-	namedType, err := findGoNamedType(prog, pkgName, typeName)
+func findGoInterface(def types.Type) (*types.Interface, error) {
+	if def == nil {
+		return nil, nil
+	}
+	namedType, err := findGoNamedType(def)
 	if err != nil {
 		return nil, err
 	}
@@ -69,319 +34,14 @@ func findGoInterface(prog *loader.Program, pkgName string, typeName string) (*ty
 
 	underlying, ok := namedType.Underlying().(*types.Interface)
 	if !ok {
-		return nil, errors.Errorf("expected %s to be a named interface, instead found %s", typeName, namedType.String())
+		return nil, errors.Errorf("expected %s to be a named interface, instead found %s", def.String(), namedType.String())
 	}
 
 	return underlying, nil
 }
 
-func findMethod(typ *types.Named, name string) *types.Func {
-	for i := 0; i < typ.NumMethods(); i++ {
-		method := typ.Method(i)
-		if !method.Exported() {
-			continue
-		}
-
-		if strings.EqualFold(method.Name(), name) {
-			return method
-		}
-	}
-
-	if s, ok := typ.Underlying().(*types.Struct); ok {
-		for i := 0; i < s.NumFields(); i++ {
-			field := s.Field(i)
-			if !field.Anonymous() {
-				continue
-			}
-
-			if named, ok := field.Type().(*types.Named); ok {
-				if f := findMethod(named, name); f != nil {
-					return f
-				}
-			}
-		}
-	}
-
-	return nil
-}
-
 func equalFieldName(source, target string) bool {
 	source = strings.Replace(source, "_", "", -1)
 	target = strings.Replace(target, "_", "", -1)
 	return strings.EqualFold(source, target)
 }
-
-// findField attempts to match the name to a struct field with the following
-// priorites:
-// 1. If struct tag is passed then struct tag has highest priority
-// 2. Field in an embedded struct
-// 3. Actual Field name
-func findField(typ *types.Struct, name, structTag string) (*types.Var, error) {
-	var foundField *types.Var
-	foundFieldWasTag := false
-
-	for i := 0; i < typ.NumFields(); i++ {
-		field := typ.Field(i)
-
-		if structTag != "" {
-			tags := reflect.StructTag(typ.Tag(i))
-			if val, ok := tags.Lookup(structTag); ok {
-				if equalFieldName(val, name) {
-					if foundField != nil && foundFieldWasTag {
-						return nil, errors.Errorf("tag %s is ambigious; multiple fields have the same tag value of %s", structTag, val)
-					}
-
-					foundField = field
-					foundFieldWasTag = true
-				}
-			}
-		}
-
-		if field.Anonymous() {
-
-			fieldType := field.Type()
-
-			if ptr, ok := fieldType.(*types.Pointer); ok {
-				fieldType = ptr.Elem()
-			}
-
-			// Type.Underlying() returns itself for all types except types.Named, where it returns a struct type.
-			// It should be safe to always call.
-			if named, ok := fieldType.Underlying().(*types.Struct); ok {
-				f, err := findField(named, name, structTag)
-				if err != nil && !strings.HasPrefix(err.Error(), "no field named") {
-					return nil, err
-				}
-				if f != nil && foundField == nil {
-					foundField = f
-				}
-			}
-		}
-
-		if !field.Exported() {
-			continue
-		}
-
-		if equalFieldName(field.Name(), name) && foundField == nil { // aqui!
-			foundField = field
-		}
-	}
-
-	if foundField == nil {
-		return nil, fmt.Errorf("no field named %s", name)
-	}
-
-	return foundField, nil
-}
-
-type BindError struct {
-	object    *Object
-	field     *Field
-	typ       types.Type
-	methodErr error
-	varErr    error
-}
-
-func (b BindError) Error() string {
-	return fmt.Sprintf(
-		"Unable to bind %s.%s to %s\n  %s\n  %s",
-		b.object.GQLType,
-		b.field.GQLName,
-		b.typ.String(),
-		b.methodErr.Error(),
-		b.varErr.Error(),
-	)
-}
-
-type BindErrors []BindError
-
-func (b BindErrors) Error() string {
-	var errs []string
-	for _, err := range b {
-		errs = append(errs, err.Error())
-	}
-	return strings.Join(errs, "\n\n")
-}
-
-func bindObject(t types.Type, object *Object, structTag string) BindErrors {
-	var errs BindErrors
-	for i := range object.Fields {
-		field := &object.Fields[i]
-
-		if field.ForceResolver {
-			continue
-		}
-
-		// first try binding to a method
-		methodErr := bindMethod(t, field)
-		if methodErr == nil {
-			continue
-		}
-
-		// otherwise try binding to a var
-		varErr := bindVar(t, field, structTag)
-
-		if varErr != nil {
-			errs = append(errs, BindError{
-				object:    object,
-				typ:       t,
-				field:     field,
-				varErr:    varErr,
-				methodErr: methodErr,
-			})
-		}
-	}
-	return errs
-}
-
-func bindMethod(t types.Type, field *Field) error {
-	namedType, ok := t.(*types.Named)
-	if !ok {
-		return fmt.Errorf("not a named type")
-	}
-
-	goName := field.GQLName
-	if field.GoFieldName != "" {
-		goName = field.GoFieldName
-	}
-	method := findMethod(namedType, goName)
-	if method == nil {
-		return fmt.Errorf("no method named %s", field.GQLName)
-	}
-	sig := method.Type().(*types.Signature)
-
-	if sig.Results().Len() == 1 {
-		field.NoErr = true
-	} else if sig.Results().Len() != 2 {
-		return fmt.Errorf("method has wrong number of args")
-	}
-	params := sig.Params()
-	// If the first argument is the context, remove it from the comparison and set
-	// the MethodHasContext flag so that the context will be passed to this model's method
-	if params.Len() > 0 && params.At(0).Type().String() == "context.Context" {
-		field.MethodHasContext = true
-		vars := make([]*types.Var, params.Len()-1)
-		for i := 1; i < params.Len(); i++ {
-			vars[i-1] = params.At(i)
-		}
-		params = types.NewTuple(vars...)
-	}
-
-	newArgs, err := matchArgs(field, params)
-	if err != nil {
-		return err
-	}
-
-	result := sig.Results().At(0)
-	if err := validateTypeBinding(field, result.Type()); err != nil {
-		return errors.Wrap(err, "method has wrong return type")
-	}
-
-	// success, args and return type match. Bind to method
-	field.GoFieldType = GoFieldMethod
-	field.GoReceiverName = "obj"
-	field.GoFieldName = method.Name()
-	field.Args = newArgs
-	return nil
-}
-
-func bindVar(t types.Type, field *Field, structTag string) error {
-	underlying, ok := t.Underlying().(*types.Struct)
-	if !ok {
-		return fmt.Errorf("not a struct")
-	}
-
-	goName := field.GQLName
-	if field.GoFieldName != "" {
-		goName = field.GoFieldName
-	}
-	structField, err := findField(underlying, goName, structTag)
-	if err != nil {
-		return err
-	}
-
-	if err := validateTypeBinding(field, structField.Type()); err != nil {
-		return errors.Wrap(err, "field has wrong type")
-	}
-
-	// success, bind to var
-	field.GoFieldType = GoFieldVariable
-	field.GoReceiverName = "obj"
-	field.GoFieldName = structField.Name()
-	return nil
-}
-
-func matchArgs(field *Field, params *types.Tuple) ([]FieldArgument, error) {
-	var newArgs []FieldArgument
-
-nextArg:
-	for j := 0; j < params.Len(); j++ {
-		param := params.At(j)
-		for _, oldArg := range field.Args {
-			if strings.EqualFold(oldArg.GQLName, param.Name()) {
-				if !field.ForceResolver {
-					oldArg.Type.Modifiers = modifiersFromGoType(param.Type())
-				}
-				newArgs = append(newArgs, oldArg)
-				continue nextArg
-			}
-		}
-
-		// no matching arg found, abort
-		return nil, fmt.Errorf("arg %s not found on method", param.Name())
-	}
-	return newArgs, nil
-}
-
-func validateTypeBinding(field *Field, goType types.Type) error {
-	gqlType := normalizeVendor(field.Type.FullSignature())
-	goTypeStr := normalizeVendor(goType.String())
-
-	if equalTypes(goTypeStr, gqlType) {
-		field.Type.Modifiers = modifiersFromGoType(goType)
-		return nil
-	}
-
-	// deal with type aliases
-	underlyingStr := normalizeVendor(goType.Underlying().String())
-	if equalTypes(underlyingStr, gqlType) {
-		field.Type.Modifiers = modifiersFromGoType(goType)
-		pkg, typ := pkgAndType(goType.String())
-		field.AliasedType = &Ref{GoType: typ, Package: pkg}
-		return nil
-	}
-
-	return fmt.Errorf("%s is not compatible with %s", gqlType, goTypeStr)
-}
-
-func modifiersFromGoType(t types.Type) []string {
-	var modifiers []string
-	for {
-		switch val := t.(type) {
-		case *types.Pointer:
-			modifiers = append(modifiers, modPtr)
-			t = val.Elem()
-		case *types.Array:
-			modifiers = append(modifiers, modList)
-			t = val.Elem()
-		case *types.Slice:
-			modifiers = append(modifiers, modList)
-			t = val.Elem()
-		default:
-			return modifiers
-		}
-	}
-}
-
-var modsRegex = regexp.MustCompile(`^(\*|\[\])*`)
-
-func normalizeVendor(pkg string) string {
-	modifiers := modsRegex.FindAllString(pkg, 1)[0]
-	pkg = strings.TrimPrefix(pkg, modifiers)
-	parts := strings.Split(pkg, "/vendor/")
-	return modifiers + parts[len(parts)-1]
-}
-
-func equalTypes(goType string, gqlType string) bool {
-	return goType == gqlType || "*"+goType == gqlType || goType == "*"+gqlType || strings.Replace(goType, "[]*", "[]", -1) == gqlType
-}

vendor/github.com/99designs/gqlgen/graphql/bool.go πŸ”—

@@ -19,7 +19,7 @@ func MarshalBoolean(b bool) Marshaler {
 func UnmarshalBoolean(v interface{}) (bool, error) {
 	switch v := v.(type) {
 	case string:
-		return "true" == strings.ToLower(v), nil
+		return strings.ToLower(v) == "true", nil
 	case int:
 		return v != 0, nil
 	case bool:

vendor/github.com/99designs/gqlgen/graphql/context.go πŸ”—

@@ -12,6 +12,7 @@ import (
 type Resolver func(ctx context.Context) (res interface{}, err error)
 type FieldMiddleware func(ctx context.Context, next Resolver) (res interface{}, err error)
 type RequestMiddleware func(ctx context.Context, next func(ctx context.Context) []byte) []byte
+type ComplexityLimitFunc func(ctx context.Context) int
 
 type RequestContext struct {
 	RawQuery  string
@@ -71,12 +72,10 @@ const (
 )
 
 func GetRequestContext(ctx context.Context) *RequestContext {
-	val := ctx.Value(request)
-	if val == nil {
-		return nil
+	if val, ok := ctx.Value(request).(*RequestContext); ok {
+		return val
 	}
-
-	return val.(*RequestContext)
+	return nil
 }
 
 func WithRequestContext(ctx context.Context, rc *RequestContext) context.Context {
@@ -95,6 +94,8 @@ type ResolverContext struct {
 	Index *int
 	// The result object of resolver
 	Result interface{}
+	// IsMethod indicates if the resolver is a method
+	IsMethod bool
 }
 
 func (r *ResolverContext) Path() []interface{} {
@@ -117,8 +118,10 @@ func (r *ResolverContext) Path() []interface{} {
 }
 
 func GetResolverContext(ctx context.Context) *ResolverContext {
-	val, _ := ctx.Value(resolver).(*ResolverContext)
-	return val
+	if val, ok := ctx.Value(resolver).(*ResolverContext); ok {
+		return val
+	}
+	return nil
 }
 
 func WithResolverContext(ctx context.Context, rc *ResolverContext) context.Context {
@@ -132,6 +135,24 @@ func CollectFieldsCtx(ctx context.Context, satisfies []string) []CollectedField
 	return CollectFields(ctx, resctx.Field.Selections, satisfies)
 }
 
+// CollectAllFields returns a slice of all GraphQL field names that were selected for the current resolver context.
+// The slice will contain the unique set of all field names requested regardless of fragment type conditions.
+func CollectAllFields(ctx context.Context) []string {
+	resctx := GetResolverContext(ctx)
+	collected := CollectFields(ctx, resctx.Field.Selections, nil)
+	uniq := make([]string, 0, len(collected))
+Next:
+	for _, f := range collected {
+		for _, name := range uniq {
+			if name == f.Name {
+				continue Next
+			}
+		}
+		uniq = append(uniq, f.Name)
+	}
+	return uniq
+}
+
 // Errorf sends an error string to the client, passing it through the formatter.
 func (c *RequestContext) Errorf(ctx context.Context, format string, args ...interface{}) {
 	c.errorsMu.Lock()
@@ -217,3 +238,37 @@ func (c *RequestContext) RegisterExtension(key string, value interface{}) error
 	c.Extensions[key] = value
 	return nil
 }
+
+// ChainFieldMiddleware add chain by FieldMiddleware
+func ChainFieldMiddleware(handleFunc ...FieldMiddleware) FieldMiddleware {
+	n := len(handleFunc)
+
+	if n > 1 {
+		lastI := n - 1
+		return func(ctx context.Context, next Resolver) (interface{}, error) {
+			var (
+				chainHandler Resolver
+				curI         int
+			)
+			chainHandler = func(currentCtx context.Context) (interface{}, error) {
+				if curI == lastI {
+					return next(currentCtx)
+				}
+				curI++
+				res, err := handleFunc[curI](currentCtx, chainHandler)
+				curI--
+				return res, err
+
+			}
+			return handleFunc[0](ctx, chainHandler)
+		}
+	}
+
+	if n == 1 {
+		return handleFunc[0]
+	}
+
+	return func(ctx context.Context, next Resolver) (interface{}, error) {
+		return next(ctx)
+	}
+}

vendor/github.com/99designs/gqlgen/graphql/error.go πŸ”—

@@ -14,7 +14,9 @@ type ExtendedError interface {
 
 func DefaultErrorPresenter(ctx context.Context, err error) *gqlerror.Error {
 	if gqlerr, ok := err.(*gqlerror.Error); ok {
-		gqlerr.Path = GetResolverContext(ctx).Path()
+		if gqlerr.Path == nil {
+			gqlerr.Path = GetResolverContext(ctx).Path()
+		}
 		return gqlerr
 	}
 

vendor/github.com/99designs/gqlgen/graphql/exec.go πŸ”—

@@ -16,6 +16,9 @@ type ExecutableSchema interface {
 	Subscription(ctx context.Context, op *ast.OperationDefinition) func() *Response
 }
 
+// CollectFields returns the set of fields from an ast.SelectionSet where all collected fields satisfy at least one of the GraphQL types
+// passed through satisfies. Providing an empty or nil slice for satisfies will return collect all fields regardless of fragment
+// type conditions.
 func CollectFields(ctx context.Context, selSet ast.SelectionSet, satisfies []string) []CollectedField {
 	return collectFields(GetRequestContext(ctx), selSet, satisfies, map[string]bool{})
 }
@@ -35,7 +38,10 @@ func collectFields(reqCtx *RequestContext, selSet ast.SelectionSet, satisfies []
 
 			f.Selections = append(f.Selections, sel.SelectionSet...)
 		case *ast.InlineFragment:
-			if !shouldIncludeNode(sel.Directives, reqCtx.Variables) || !instanceOf(sel.TypeCondition, satisfies) {
+			if !shouldIncludeNode(sel.Directives, reqCtx.Variables) {
+				continue
+			}
+			if len(satisfies) > 0 && !instanceOf(sel.TypeCondition, satisfies) {
 				continue
 			}
 			for _, childField := range collectFields(reqCtx, sel.SelectionSet, satisfies, visited) {
@@ -59,7 +65,7 @@ func collectFields(reqCtx *RequestContext, selSet ast.SelectionSet, satisfies []
 				panic(fmt.Errorf("missing fragment %s", fragmentName))
 			}
 
-			if !instanceOf(fragment.TypeCondition, satisfies) {
+			if len(satisfies) > 0 && !instanceOf(fragment.TypeCondition, satisfies) {
 				continue
 			}
 

vendor/github.com/99designs/gqlgen/graphql/fieldset.go πŸ”—

@@ -0,0 +1,63 @@
+package graphql
+
+import (
+	"io"
+	"sync"
+)
+
+type FieldSet struct {
+	fields  []CollectedField
+	Values  []Marshaler
+	delayed []delayedResult
+}
+
+type delayedResult struct {
+	i int
+	f func() Marshaler
+}
+
+func NewFieldSet(fields []CollectedField) *FieldSet {
+	return &FieldSet{
+		fields: fields,
+		Values: make([]Marshaler, len(fields)),
+	}
+}
+
+func (m *FieldSet) Concurrently(i int, f func() Marshaler) {
+	m.delayed = append(m.delayed, delayedResult{i: i, f: f})
+}
+
+func (m *FieldSet) Dispatch() {
+	if len(m.delayed) == 1 {
+		// only one concurrent task, no need to spawn a goroutine or deal create waitgroups
+		d := m.delayed[0]
+		m.Values[d.i] = d.f()
+	} else if len(m.delayed) > 1 {
+		// more than one concurrent task, use the main goroutine to do one, only spawn goroutines for the others
+
+		var wg sync.WaitGroup
+		for _, d := range m.delayed[1:] {
+			wg.Add(1)
+			go func(d delayedResult) {
+				m.Values[d.i] = d.f()
+				wg.Done()
+			}(d)
+		}
+
+		m.Values[m.delayed[0].i] = m.delayed[0].f()
+		wg.Wait()
+	}
+}
+
+func (m *FieldSet) MarshalGQL(writer io.Writer) {
+	writer.Write(openBrace)
+	for i, field := range m.fields {
+		if i != 0 {
+			writer.Write(comma)
+		}
+		writeQuotedString(writer, field.Alias)
+		writer.Write(colon)
+		m.Values[i].MarshalGQL(writer)
+	}
+	writer.Write(closeBrace)
+}

vendor/github.com/99designs/gqlgen/graphql/id.go πŸ”—

@@ -34,3 +34,24 @@ func UnmarshalID(v interface{}) (string, error) {
 		return "", fmt.Errorf("%T is not a string", v)
 	}
 }
+
+func MarshalIntID(i int) Marshaler {
+	return WriterFunc(func(w io.Writer) {
+		writeQuotedString(w, strconv.Itoa(i))
+	})
+}
+
+func UnmarshalIntID(v interface{}) (int, error) {
+	switch v := v.(type) {
+	case string:
+		return strconv.Atoi(v)
+	case int:
+		return v, nil
+	case int64:
+		return int(v), nil
+	case json.Number:
+		return strconv.Atoi(string(v))
+	default:
+		return 0, fmt.Errorf("%T is not an int", v)
+	}
+}

vendor/github.com/99designs/gqlgen/graphql/int.go πŸ”—

@@ -27,3 +27,53 @@ func UnmarshalInt(v interface{}) (int, error) {
 		return 0, fmt.Errorf("%T is not an int", v)
 	}
 }
+
+func MarshalInt64(i int64) Marshaler {
+	return WriterFunc(func(w io.Writer) {
+		io.WriteString(w, strconv.FormatInt(i, 10))
+	})
+}
+
+func UnmarshalInt64(v interface{}) (int64, error) {
+	switch v := v.(type) {
+	case string:
+		return strconv.ParseInt(v, 10, 64)
+	case int:
+		return int64(v), nil
+	case int64:
+		return v, nil
+	case json.Number:
+		return strconv.ParseInt(string(v), 10, 64)
+	default:
+		return 0, fmt.Errorf("%T is not an int", v)
+	}
+}
+
+func MarshalInt32(i int32) Marshaler {
+	return WriterFunc(func(w io.Writer) {
+		io.WriteString(w, strconv.FormatInt(int64(i), 10))
+	})
+}
+
+func UnmarshalInt32(v interface{}) (int32, error) {
+	switch v := v.(type) {
+	case string:
+		iv, err := strconv.ParseInt(v, 10, 32)
+		if err != nil {
+			return 0, err
+		}
+		return int32(iv), nil
+	case int:
+		return int32(v), nil
+	case int64:
+		return int32(v), nil
+	case json.Number:
+		iv, err := strconv.ParseInt(string(v), 10, 32)
+		if err != nil {
+			return 0, err
+		}
+		return int32(iv), nil
+	default:
+		return 0, fmt.Errorf("%T is not an int", v)
+	}
+}

vendor/github.com/99designs/gqlgen/graphql/introspection/type.go πŸ”—

@@ -62,9 +62,9 @@ func (t *Type) Description() string {
 
 func (t *Type) Fields(includeDeprecated bool) []Field {
 	if t.def == nil || (t.def.Kind != ast.Object && t.def.Kind != ast.Interface) {
-		return nil
+		return []Field{}
 	}
-	var fields []Field
+	fields := []Field{}
 	for _, f := range t.def.Fields {
 		if strings.HasPrefix(f.Name, "__") {
 			continue
@@ -93,10 +93,10 @@ func (t *Type) Fields(includeDeprecated bool) []Field {
 
 func (t *Type) InputFields() []InputValue {
 	if t.def == nil || t.def.Kind != ast.InputObject {
-		return nil
+		return []InputValue{}
 	}
 
-	var res []InputValue
+	res := []InputValue{}
 	for _, f := range t.def.Fields {
 		res = append(res, InputValue{
 			Name:         f.Name,
@@ -118,10 +118,10 @@ func defaultValue(value *ast.Value) *string {
 
 func (t *Type) Interfaces() []Type {
 	if t.def == nil || t.def.Kind != ast.Object {
-		return nil
+		return []Type{}
 	}
 
-	var res []Type
+	res := []Type{}
 	for _, intf := range t.def.Interfaces {
 		res = append(res, *WrapTypeFromDef(t.schema, t.schema.Types[intf]))
 	}
@@ -131,10 +131,10 @@ func (t *Type) Interfaces() []Type {
 
 func (t *Type) PossibleTypes() []Type {
 	if t.def == nil || (t.def.Kind != ast.Interface && t.def.Kind != ast.Union) {
-		return nil
+		return []Type{}
 	}
 
-	var res []Type
+	res := []Type{}
 	for _, pt := range t.schema.GetPossibleTypes(t.def) {
 		res = append(res, *WrapTypeFromDef(t.schema, pt))
 	}
@@ -143,10 +143,10 @@ func (t *Type) PossibleTypes() []Type {
 
 func (t *Type) EnumValues(includeDeprecated bool) []EnumValue {
 	if t.def == nil || t.def.Kind != ast.Enum {
-		return nil
+		return []EnumValue{}
 	}
 
-	var res []EnumValue
+	res := []EnumValue{}
 	for _, val := range t.def.EnumValues {
 		res = append(res, EnumValue{
 			Name:        val.Name,

vendor/github.com/99designs/gqlgen/graphql/jsonw.go πŸ”—

@@ -2,7 +2,6 @@ package graphql
 
 import (
 	"io"
-	"strconv"
 )
 
 var nullLit = []byte(`null`)
@@ -27,42 +26,12 @@ type Unmarshaler interface {
 	UnmarshalGQL(v interface{}) error
 }
 
-type OrderedMap struct {
-	Keys   []string
-	Values []Marshaler
-}
-
 type WriterFunc func(writer io.Writer)
 
 func (f WriterFunc) MarshalGQL(w io.Writer) {
 	f(w)
 }
 
-func NewOrderedMap(len int) *OrderedMap {
-	return &OrderedMap{
-		Keys:   make([]string, len),
-		Values: make([]Marshaler, len),
-	}
-}
-
-func (m *OrderedMap) Add(key string, value Marshaler) {
-	m.Keys = append(m.Keys, key)
-	m.Values = append(m.Values, value)
-}
-
-func (m *OrderedMap) MarshalGQL(writer io.Writer) {
-	writer.Write(openBrace)
-	for i, key := range m.Keys {
-		if i != 0 {
-			writer.Write(comma)
-		}
-		io.WriteString(writer, strconv.Quote(key))
-		writer.Write(colon)
-		m.Values[i].MarshalGQL(writer)
-	}
-	writer.Write(closeBrace)
-}
-
 type Array []Marshaler
 
 func (a Array) MarshalGQL(writer io.Writer) {

vendor/github.com/99designs/gqlgen/graphql/string.go πŸ”—

@@ -10,37 +10,42 @@ const encodeHex = "0123456789ABCDEF"
 
 func MarshalString(s string) Marshaler {
 	return WriterFunc(func(w io.Writer) {
-		start := 0
-		io.WriteString(w, `"`)
-
-		for i, c := range s {
-			if c < 0x20 || c == '\\' || c == '"' {
-				io.WriteString(w, s[start:i])
-
-				switch c {
-				case '\t':
-					io.WriteString(w, `\t`)
-				case '\r':
-					io.WriteString(w, `\r`)
-				case '\n':
-					io.WriteString(w, `\n`)
-				case '\\':
-					io.WriteString(w, `\\`)
-				case '"':
-					io.WriteString(w, `\"`)
-				default:
-					io.WriteString(w, `\u00`)
-					w.Write([]byte{encodeHex[c>>4], encodeHex[c&0xf]})
-				}
-
-				start = i + 1
+		writeQuotedString(w, s)
+	})
+}
+
+func writeQuotedString(w io.Writer, s string) {
+	start := 0
+	io.WriteString(w, `"`)
+
+	for i, c := range s {
+		if c < 0x20 || c == '\\' || c == '"' {
+			io.WriteString(w, s[start:i])
+
+			switch c {
+			case '\t':
+				io.WriteString(w, `\t`)
+			case '\r':
+				io.WriteString(w, `\r`)
+			case '\n':
+				io.WriteString(w, `\n`)
+			case '\\':
+				io.WriteString(w, `\\`)
+			case '"':
+				io.WriteString(w, `\"`)
+			default:
+				io.WriteString(w, `\u00`)
+				w.Write([]byte{encodeHex[c>>4], encodeHex[c&0xf]})
 			}
+
+			start = i + 1
 		}
+	}
 
-		io.WriteString(w, s[start:])
-		io.WriteString(w, `"`)
-	})
+	io.WriteString(w, s[start:])
+	io.WriteString(w, `"`)
 }
+
 func UnmarshalString(v interface{}) (string, error) {
 	switch v := v.(type) {
 	case string:

vendor/github.com/99designs/gqlgen/handler/graphql.go πŸ”—

@@ -34,6 +34,7 @@ type Config struct {
 	requestHook                     graphql.RequestMiddleware
 	tracer                          graphql.Tracer
 	complexityLimit                 int
+	complexityLimitFunc             graphql.ComplexityLimitFunc
 	disableIntrospection            bool
 	connectionKeepAlivePingInterval time.Duration
 }
@@ -60,11 +61,9 @@ func (c *Config) newRequestContext(es graphql.ExecutableSchema, doc *ast.QueryDo
 
 	if hook := c.tracer; hook != nil {
 		reqCtx.Tracer = hook
-	} else {
-		reqCtx.Tracer = &graphql.NopTracer{}
 	}
 
-	if c.complexityLimit > 0 {
+	if c.complexityLimit > 0 || c.complexityLimitFunc != nil {
 		reqCtx.ComplexityLimit = c.complexityLimit
 		operationComplexity := complexity.Calculate(es, op, variables)
 		reqCtx.OperationComplexity = operationComplexity
@@ -112,6 +111,15 @@ func ComplexityLimit(limit int) Option {
 	}
 }
 
+// ComplexityLimitFunc allows you to define a function to dynamically set the maximum query complexity that is allowed
+// to be executed.
+// If a query is submitted that exceeds the limit, a 422 status code will be returned.
+func ComplexityLimitFunc(complexityLimitFunc graphql.ComplexityLimitFunc) Option {
+	return func(cfg *Config) {
+		cfg.complexityLimitFunc = complexityLimitFunc
+	}
+}
+
 // ResolverMiddleware allows you to define a function that will be called around every resolver,
 // useful for logging.
 func ResolverMiddleware(middleware graphql.FieldMiddleware) Option {
@@ -243,19 +251,23 @@ func CacheSize(size int) Option {
 	}
 }
 
-const DefaultCacheSize = 1000
-
-// WebsocketKeepAliveDuration allows you to reconfigure the keepAlive behavior.
-// By default, keep-alive is disabled.
+// WebsocketKeepAliveDuration allows you to reconfigure the keepalive behavior.
+// By default, keepalive is enabled with a DefaultConnectionKeepAlivePingInterval
+// duration. Set handler.connectionKeepAlivePingInterval = 0 to disable keepalive
+// altogether.
 func WebsocketKeepAliveDuration(duration time.Duration) Option {
 	return func(cfg *Config) {
 		cfg.connectionKeepAlivePingInterval = duration
 	}
 }
 
+const DefaultCacheSize = 1000
+const DefaultConnectionKeepAlivePingInterval = 25 * time.Second
+
 func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc {
 	cfg := &Config{
-		cacheSize: DefaultCacheSize,
+		cacheSize:                       DefaultCacheSize,
+		connectionKeepAlivePingInterval: DefaultConnectionKeepAlivePingInterval,
 		upgrader: websocket.Upgrader{
 			ReadBufferSize:  1024,
 			WriteBufferSize: 1024,
@@ -269,7 +281,7 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc
 	var cache *lru.Cache
 	if cfg.cacheSize > 0 {
 		var err error
-		cache, err = lru.New(DefaultCacheSize)
+		cache, err = lru.New(cfg.cacheSize)
 		if err != nil {
 			// An error is only returned for non-positive cache size
 			// and we already checked for that.
@@ -305,10 +317,11 @@ func (gh *graphqlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if strings.Contains(r.Header.Get("Upgrade"), "websocket") {
-		connectWs(gh.exec, w, r, gh.cfg)
+		connectWs(gh.exec, w, r, gh.cfg, gh.cache)
 		return
 	}
 
+	w.Header().Set("Content-Type", "application/json")
 	var reqParams params
 	switch r.Method {
 	case http.MethodGet:
@@ -330,7 +343,6 @@ func (gh *graphqlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		w.WriteHeader(http.StatusMethodNotAllowed)
 		return
 	}
-	w.Header().Set("Content-Type", "application/json")
 
 	ctx := r.Context()
 
@@ -379,6 +391,10 @@ func (gh *graphqlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		}
 	}()
 
+	if gh.cfg.complexityLimitFunc != nil {
+		reqCtx.ComplexityLimit = gh.cfg.complexityLimitFunc(ctx)
+	}
+
 	if reqCtx.ComplexityLimit > 0 && reqCtx.OperationComplexity > reqCtx.ComplexityLimit {
 		sendErrorf(w, http.StatusUnprocessableEntity, "operation has complexity %d, which exceeds the limit of %d", reqCtx.OperationComplexity, reqCtx.ComplexityLimit)
 		return

vendor/github.com/99designs/gqlgen/handler/playground.go πŸ”—

@@ -11,9 +11,12 @@ var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
 	<meta charset=utf-8/>
 	<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
 	<link rel="shortcut icon" href="https://graphcool-playground.netlify.com/favicon.png">
-	<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/static/css/index.css"/>
-	<link rel="shortcut icon" href="//cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/favicon.png"/>
-	<script src="//cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/static/js/middleware.js"></script>
+	<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/static/css/index.css" 
+		integrity="{{ .cssSRI }}" crossorigin="anonymous"/>
+	<link rel="shortcut icon" href="https://cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/favicon.png"
+		integrity="{{ .faviconSRI }}" crossorigin="anonymous"/>
+	<script src="https://cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/static/js/middleware.js"
+		integrity="{{ .jsSRI }}" crossorigin="anonymous"></script>
 	<title>{{.title}}</title>
 </head>
 <body>
@@ -42,10 +45,14 @@ var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html>
 
 func Playground(title string, endpoint string) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("Content-Type", "text/html")
 		err := page.Execute(w, map[string]string{
-			"title":    title,
-			"endpoint": endpoint,
-			"version":  "1.7.8",
+			"title":      title,
+			"endpoint":   endpoint,
+			"version":    "1.7.20",
+			"cssSRI":     "sha256-cS9Vc2OBt9eUf4sykRWukeFYaInL29+myBmFDSa7F/U=",
+			"faviconSRI": "sha256-GhTyE+McTU79R4+pRO6ih+4TfsTOrpPwD8ReKFzb3PM=",
+			"jsSRI":      "sha256-4QG1Uza2GgGdlBL3RCBCGtGeZB6bDbsw8OltCMGeJsA=",
 		})
 		if err != nil {
 			panic(err)

vendor/github.com/99designs/gqlgen/handler/websocket.go πŸ”—

@@ -12,6 +12,7 @@ import (
 
 	"github.com/99designs/gqlgen/graphql"
 	"github.com/gorilla/websocket"
+	"github.com/hashicorp/golang-lru"
 	"github.com/vektah/gqlparser"
 	"github.com/vektah/gqlparser/ast"
 	"github.com/vektah/gqlparser/gqlerror"
@@ -44,12 +45,13 @@ type wsConnection struct {
 	active          map[string]context.CancelFunc
 	mu              sync.Mutex
 	cfg             *Config
+	cache           *lru.Cache
 	keepAliveTicker *time.Ticker
 
 	initPayload InitPayload
 }
 
-func connectWs(exec graphql.ExecutableSchema, w http.ResponseWriter, r *http.Request, cfg *Config) {
+func connectWs(exec graphql.ExecutableSchema, w http.ResponseWriter, r *http.Request, cfg *Config, cache *lru.Cache) {
 	ws, err := cfg.upgrader.Upgrade(w, r, http.Header{
 		"Sec-Websocket-Protocol": []string{"graphql-ws"},
 	})
@@ -65,6 +67,7 @@ func connectWs(exec graphql.ExecutableSchema, w http.ResponseWriter, r *http.Req
 		conn:   ws,
 		ctx:    r.Context(),
 		cfg:    cfg,
+		cache:  cache,
 	}
 
 	if !conn.init() {
@@ -176,10 +179,27 @@ func (c *wsConnection) subscribe(message *operationMessage) bool {
 		return false
 	}
 
-	doc, qErr := gqlparser.LoadQuery(c.exec.Schema(), reqParams.Query)
-	if qErr != nil {
-		c.sendError(message.ID, qErr...)
-		return true
+	var (
+		doc      *ast.QueryDocument
+		cacheHit bool
+	)
+	if c.cache != nil {
+		val, ok := c.cache.Get(reqParams.Query)
+		if ok {
+			doc = val.(*ast.QueryDocument)
+			cacheHit = true
+		}
+	}
+	if !cacheHit {
+		var qErr gqlerror.List
+		doc, qErr = gqlparser.LoadQuery(c.exec.Schema(), reqParams.Query)
+		if qErr != nil {
+			c.sendError(message.ID, qErr...)
+			return true
+		}
+		if c.cache != nil {
+			c.cache.Add(reqParams.Query, doc)
+		}
 	}
 
 	op := doc.Operations.ForName(reqParams.OperationName)

vendor/github.com/99designs/gqlgen/internal/code/compare.go πŸ”—

@@ -0,0 +1,163 @@
+package code
+
+import (
+	"fmt"
+	"go/types"
+)
+
+// CompatibleTypes isnt a strict comparison, it allows for pointer differences
+func CompatibleTypes(expected types.Type, actual types.Type) error {
+	//fmt.Println("Comparing ", expected.String(), actual.String())
+
+	// Special case to deal with pointer mismatches
+	{
+		expectedPtr, expectedIsPtr := expected.(*types.Pointer)
+		actualPtr, actualIsPtr := actual.(*types.Pointer)
+
+		if expectedIsPtr && actualIsPtr {
+			return CompatibleTypes(expectedPtr.Elem(), actualPtr.Elem())
+		}
+		if expectedIsPtr && !actualIsPtr {
+			return CompatibleTypes(expectedPtr.Elem(), actual)
+		}
+		if !expectedIsPtr && actualIsPtr {
+			return CompatibleTypes(expected, actualPtr.Elem())
+		}
+	}
+
+	switch expected := expected.(type) {
+	case *types.Slice:
+		if actual, ok := actual.(*types.Slice); ok {
+			return CompatibleTypes(expected.Elem(), actual.Elem())
+		}
+
+	case *types.Array:
+		if actual, ok := actual.(*types.Array); ok {
+			if expected.Len() != actual.Len() {
+				return fmt.Errorf("array length differs")
+			}
+
+			return CompatibleTypes(expected.Elem(), actual.Elem())
+		}
+
+	case *types.Basic:
+		if actual, ok := actual.(*types.Basic); ok {
+			if actual.Kind() != expected.Kind() {
+				return fmt.Errorf("basic kind differs, %s != %s", expected.Name(), actual.Name())
+			}
+
+			return nil
+		}
+
+	case *types.Struct:
+		if actual, ok := actual.(*types.Struct); ok {
+			if expected.NumFields() != actual.NumFields() {
+				return fmt.Errorf("number of struct fields differ")
+			}
+
+			for i := 0; i < expected.NumFields(); i++ {
+				if expected.Field(i).Name() != actual.Field(i).Name() {
+					return fmt.Errorf("struct field %d name differs, %s != %s", i, expected.Field(i).Name(), actual.Field(i).Name())
+				}
+				if err := CompatibleTypes(expected.Field(i).Type(), actual.Field(i).Type()); err != nil {
+					return err
+				}
+			}
+			return nil
+		}
+
+	case *types.Tuple:
+		if actual, ok := actual.(*types.Tuple); ok {
+			if expected.Len() != actual.Len() {
+				return fmt.Errorf("tuple length differs, %d != %d", expected.Len(), actual.Len())
+			}
+
+			for i := 0; i < expected.Len(); i++ {
+				if err := CompatibleTypes(expected.At(i).Type(), actual.At(i).Type()); err != nil {
+					return err
+				}
+			}
+
+			return nil
+		}
+
+	case *types.Signature:
+		if actual, ok := actual.(*types.Signature); ok {
+			if err := CompatibleTypes(expected.Params(), actual.Params()); err != nil {
+				return err
+			}
+			if err := CompatibleTypes(expected.Results(), actual.Results()); err != nil {
+				return err
+			}
+
+			return nil
+		}
+	case *types.Interface:
+		if actual, ok := actual.(*types.Interface); ok {
+			if expected.NumMethods() != actual.NumMethods() {
+				return fmt.Errorf("interface method count differs, %d != %d", expected.NumMethods(), actual.NumMethods())
+			}
+
+			for i := 0; i < expected.NumMethods(); i++ {
+				if expected.Method(i).Name() != actual.Method(i).Name() {
+					return fmt.Errorf("interface method %d name differs, %s != %s", i, expected.Method(i).Name(), actual.Method(i).Name())
+				}
+				if err := CompatibleTypes(expected.Method(i).Type(), actual.Method(i).Type()); err != nil {
+					return err
+				}
+			}
+
+			return nil
+		}
+
+	case *types.Map:
+		if actual, ok := actual.(*types.Map); ok {
+			if err := CompatibleTypes(expected.Key(), actual.Key()); err != nil {
+				return err
+			}
+
+			if err := CompatibleTypes(expected.Elem(), actual.Elem()); err != nil {
+				return err
+			}
+
+			return nil
+		}
+
+	case *types.Chan:
+		if actual, ok := actual.(*types.Chan); ok {
+			return CompatibleTypes(expected.Elem(), actual.Elem())
+		}
+
+	case *types.Named:
+		if actual, ok := actual.(*types.Named); ok {
+			if NormalizeVendor(expected.Obj().Pkg().Path()) != NormalizeVendor(actual.Obj().Pkg().Path()) {
+				return fmt.Errorf(
+					"package name of named type differs, %s != %s",
+					NormalizeVendor(expected.Obj().Pkg().Path()),
+					NormalizeVendor(actual.Obj().Pkg().Path()),
+				)
+			}
+
+			if expected.Obj().Name() != actual.Obj().Name() {
+				return fmt.Errorf(
+					"named type name differs, %s != %s",
+					NormalizeVendor(expected.Obj().Name()),
+					NormalizeVendor(actual.Obj().Name()),
+				)
+			}
+
+			return nil
+		}
+
+		// Before models are generated all missing references will be Invalid Basic references.
+		// lets assume these are valid too.
+		if actual, ok := actual.(*types.Basic); ok && actual.Kind() == types.Invalid {
+			return nil
+		}
+
+	default:
+		return fmt.Errorf("missing support for %T", expected)
+	}
+
+	return fmt.Errorf("type mismatch %T != %T", expected, actual)
+}

vendor/github.com/99designs/gqlgen/internal/code/imports.go πŸ”—

@@ -0,0 +1,60 @@
+package code
+
+import (
+	"errors"
+	"path/filepath"
+	"sync"
+
+	"golang.org/x/tools/go/packages"
+)
+
+var pathForDirCache = sync.Map{}
+
+// ImportPathFromDir takes an *absolute* path and returns a golang import path for the package, and returns an error if it isn't on the gopath
+func ImportPathForDir(dir string) string {
+	if v, ok := pathForDirCache.Load(dir); ok {
+		return v.(string)
+	}
+
+	p, _ := packages.Load(&packages.Config{
+		Dir: dir,
+	}, ".")
+
+	// If the dir dosent exist yet, keep walking up the directory tree trying to find a match
+	if len(p) != 1 {
+		parent, err := filepath.Abs(filepath.Join(dir, ".."))
+		if err != nil {
+			panic(err)
+		}
+		// Walked all the way to the root and didnt find anything :'(
+		if parent == dir {
+			return ""
+		}
+		return ImportPathForDir(parent) + "/" + filepath.Base(dir)
+	}
+
+	pathForDirCache.Store(dir, p[0].PkgPath)
+
+	return p[0].PkgPath
+}
+
+var nameForPackageCache = sync.Map{}
+
+func NameForPackage(importPath string) string {
+	if importPath == "" {
+		panic(errors.New("import path can not be empty"))
+	}
+	if v, ok := nameForPackageCache.Load(importPath); ok {
+		return v.(string)
+	}
+	importPath = QualifyPackagePath(importPath)
+	p, _ := packages.Load(nil, importPath)
+
+	if len(p) != 1 || p[0].Name == "" {
+		return SanitizePackageName(filepath.Base(importPath))
+	}
+
+	nameForPackageCache.Store(importPath, p[0].Name)
+
+	return p[0].Name
+}

vendor/github.com/99designs/gqlgen/internal/code/util.go πŸ”—

@@ -0,0 +1,56 @@
+package code
+
+import (
+	"go/build"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+)
+
+// take a string in the form github.com/package/blah.Type and split it into package and type
+func PkgAndType(name string) (string, string) {
+	parts := strings.Split(name, ".")
+	if len(parts) == 1 {
+		return "", name
+	}
+
+	return strings.Join(parts[:len(parts)-1], "."), parts[len(parts)-1]
+}
+
+var modsRegex = regexp.MustCompile(`^(\*|\[\])*`)
+
+// NormalizeVendor takes a qualified package path and turns it into normal one.
+// eg .
+// github.com/foo/vendor/github.com/99designs/gqlgen/graphql becomes
+// github.com/99designs/gqlgen/graphql
+func NormalizeVendor(pkg string) string {
+	modifiers := modsRegex.FindAllString(pkg, 1)[0]
+	pkg = strings.TrimPrefix(pkg, modifiers)
+	parts := strings.Split(pkg, "/vendor/")
+	return modifiers + parts[len(parts)-1]
+}
+
+// QualifyPackagePath takes an import and fully qualifies it with a vendor dir, if one is required.
+// eg .
+// github.com/99designs/gqlgen/graphql becomes
+// github.com/foo/vendor/github.com/99designs/gqlgen/graphql
+//
+// x/tools/packages only supports 'qualified package paths' so this will need to be done prior to calling it
+// See https://github.com/golang/go/issues/30289
+func QualifyPackagePath(importPath string) string {
+	wd, _ := os.Getwd()
+
+	pkg, err := build.Import(importPath, wd, 0)
+	if err != nil {
+		return importPath
+	}
+
+	return pkg.ImportPath
+}
+
+var invalidPackageNameChar = regexp.MustCompile(`[^\w]`)
+
+func SanitizePackageName(pkg string) string {
+	return invalidPackageNameChar.ReplaceAllLiteralString(filepath.Base(pkg), "_")
+}

vendor/github.com/99designs/gqlgen/internal/gopath/gopath.go πŸ”—

@@ -1,37 +0,0 @@
-package gopath
-
-import (
-	"fmt"
-	"go/build"
-	"path/filepath"
-	"strings"
-)
-
-var NotFound = fmt.Errorf("not on GOPATH")
-
-// Contains returns true if the given directory is in the GOPATH
-func Contains(dir string) bool {
-	_, err := Dir2Import(dir)
-	return err == nil
-}
-
-// Dir2Import takes an *absolute* path and returns a golang import path for the package, and returns an error if it isn't on the gopath
-func Dir2Import(dir string) (string, error) {
-	dir = filepath.ToSlash(dir)
-	for _, gopath := range filepath.SplitList(build.Default.GOPATH) {
-		gopath = filepath.ToSlash(filepath.Join(gopath, "src"))
-		if len(gopath) < len(dir) && strings.EqualFold(gopath, dir[0:len(gopath)]) {
-			return dir[len(gopath)+1:], nil
-		}
-	}
-	return "", NotFound
-}
-
-// MustDir2Import takes an *absolute* path and returns a golang import path for the package, and panics if it isn't on the gopath
-func MustDir2Import(dir string) string {
-	pkg, err := Dir2Import(dir)
-	if err != nil {
-		panic(err)
-	}
-	return pkg
-}

vendor/github.com/99designs/gqlgen/internal/imports/prune.go πŸ”—

@@ -5,16 +5,15 @@ package imports
 import (
 	"bytes"
 	"go/ast"
-	"go/build"
 	"go/parser"
 	"go/printer"
 	"go/token"
-	"path/filepath"
 	"strings"
 
-	"golang.org/x/tools/imports"
+	"github.com/99designs/gqlgen/internal/code"
 
 	"golang.org/x/tools/go/ast/astutil"
+	"golang.org/x/tools/imports"
 )
 
 type visitFn func(node ast.Node)
@@ -54,12 +53,6 @@ func getUnusedImports(file ast.Node, filename string) (map[string]string, error)
 	imported := map[string]*ast.ImportSpec{}
 	used := map[string]bool{}
 
-	abs, err := filepath.Abs(filename)
-	if err != nil {
-		return nil, err
-	}
-	srcDir := filepath.Dir(abs)
-
 	ast.Walk(visitFn(func(node ast.Node) {
 		if node == nil {
 			return
@@ -75,7 +68,7 @@ func getUnusedImports(file ast.Node, filename string) (map[string]string, error)
 				break
 			}
 
-			local := importPathToName(ipath, srcDir)
+			local := code.NameForPackage(ipath)
 
 			imported[local] = v
 		case *ast.SelectorExpr:
@@ -108,12 +101,3 @@ func getUnusedImports(file ast.Node, filename string) (map[string]string, error)
 
 	return unusedImport, nil
 }
-
-func importPathToName(importPath, srcDir string) (packageName string) {
-	pkg, err := build.Default.Import(importPath, srcDir, 0)
-	if err != nil {
-		return ""
-	}
-
-	return pkg.Name
-}

vendor/github.com/99designs/gqlgen/plugin/modelgen/models.go πŸ”—

@@ -0,0 +1,207 @@
+package modelgen
+
+import (
+	"go/types"
+	"sort"
+
+	"github.com/99designs/gqlgen/codegen/config"
+	"github.com/99designs/gqlgen/codegen/templates"
+	"github.com/99designs/gqlgen/internal/code"
+	"github.com/99designs/gqlgen/plugin"
+	"github.com/vektah/gqlparser/ast"
+)
+
+type ModelBuild struct {
+	PackageName string
+	Interfaces  []*Interface
+	Models      []*Object
+	Enums       []*Enum
+	Scalars     []string
+}
+
+type Interface struct {
+	Description string
+	Name        string
+}
+
+type Object struct {
+	Description string
+	Name        string
+	Fields      []*Field
+	Implements  []string
+}
+
+type Field struct {
+	Description string
+	Name        string
+	Type        types.Type
+	Tag         string
+}
+
+type Enum struct {
+	Description string
+	Name        string
+	Values      []*EnumValue
+}
+
+type EnumValue struct {
+	Description string
+	Name        string
+}
+
+func New() plugin.Plugin {
+	return &Plugin{}
+}
+
+type Plugin struct{}
+
+var _ plugin.ConfigMutator = &Plugin{}
+
+func (m *Plugin) Name() string {
+	return "modelgen"
+}
+
+func (m *Plugin) MutateConfig(cfg *config.Config) error {
+	if err := cfg.Check(); err != nil {
+		return err
+	}
+
+	schema, _, err := cfg.LoadSchema()
+	if err != nil {
+		return err
+	}
+
+	cfg.InjectBuiltins(schema)
+
+	binder, err := cfg.NewBinder(schema)
+	if err != nil {
+		return err
+	}
+
+	b := &ModelBuild{
+		PackageName: cfg.Model.Package,
+	}
+
+	for _, schemaType := range schema.Types {
+		if cfg.Models.UserDefined(schemaType.Name) {
+			continue
+		}
+
+		switch schemaType.Kind {
+		case ast.Interface, ast.Union:
+			it := &Interface{
+				Description: schemaType.Description,
+				Name:        schemaType.Name,
+			}
+
+			b.Interfaces = append(b.Interfaces, it)
+		case ast.Object, ast.InputObject:
+			if schemaType == schema.Query || schemaType == schema.Mutation || schemaType == schema.Subscription {
+				continue
+			}
+			it := &Object{
+				Description: schemaType.Description,
+				Name:        schemaType.Name,
+			}
+
+			for _, implementor := range schema.GetImplements(schemaType) {
+				it.Implements = append(it.Implements, implementor.Name)
+			}
+
+			for _, field := range schemaType.Fields {
+				var typ types.Type
+
+				if cfg.Models.UserDefined(field.Type.Name()) {
+					pkg, typeName := code.PkgAndType(cfg.Models[field.Type.Name()].Model[0])
+					typ, err = binder.FindType(pkg, typeName)
+					if err != nil {
+						return err
+					}
+				} else {
+					fieldDef := schema.Types[field.Type.Name()]
+					switch fieldDef.Kind {
+					case ast.Scalar:
+						// no user defined model, referencing a default scalar
+						typ = types.NewNamed(
+							types.NewTypeName(0, cfg.Model.Pkg(), "string", nil),
+							nil,
+							nil,
+						)
+					case ast.Interface, ast.Union:
+						// no user defined model, referencing a generated interface type
+						typ = types.NewNamed(
+							types.NewTypeName(0, cfg.Model.Pkg(), templates.ToGo(field.Type.Name()), nil),
+							types.NewInterfaceType([]*types.Func{}, []types.Type{}),
+							nil,
+						)
+					default:
+						// no user defined model, must reference another generated model
+						typ = types.NewNamed(
+							types.NewTypeName(0, cfg.Model.Pkg(), templates.ToGo(field.Type.Name()), nil),
+							nil,
+							nil,
+						)
+					}
+				}
+
+				name := field.Name
+				if nameOveride := cfg.Models[schemaType.Name].Fields[field.Name].FieldName; nameOveride != "" {
+					name = nameOveride
+				}
+
+				it.Fields = append(it.Fields, &Field{
+					Name:        name,
+					Type:        binder.CopyModifiersFromAst(field.Type, typ),
+					Description: field.Description,
+					Tag:         `json:"` + field.Name + `"`,
+				})
+			}
+
+			b.Models = append(b.Models, it)
+		case ast.Enum:
+			it := &Enum{
+				Name:        schemaType.Name,
+				Description: schemaType.Description,
+			}
+
+			for _, v := range schemaType.EnumValues {
+				it.Values = append(it.Values, &EnumValue{
+					Name:        v.Name,
+					Description: v.Description,
+				})
+			}
+
+			b.Enums = append(b.Enums, it)
+		case ast.Scalar:
+			b.Scalars = append(b.Scalars, schemaType.Name)
+		}
+	}
+
+	sort.Slice(b.Enums, func(i, j int) bool { return b.Enums[i].Name < b.Enums[j].Name })
+	sort.Slice(b.Models, func(i, j int) bool { return b.Models[i].Name < b.Models[j].Name })
+	sort.Slice(b.Interfaces, func(i, j int) bool { return b.Interfaces[i].Name < b.Interfaces[j].Name })
+
+	for _, it := range b.Enums {
+		cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+templates.ToGo(it.Name))
+	}
+	for _, it := range b.Models {
+		cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+templates.ToGo(it.Name))
+	}
+	for _, it := range b.Interfaces {
+		cfg.Models.Add(it.Name, cfg.Model.ImportPath()+"."+templates.ToGo(it.Name))
+	}
+	for _, it := range b.Scalars {
+		cfg.Models.Add(it, "github.com/99designs/gqlgen/graphql.String")
+	}
+
+	if len(b.Models) == 0 && len(b.Enums) == 0 {
+		return nil
+	}
+
+	return templates.Render(templates.Options{
+		PackageName:     cfg.Model.Package,
+		Filename:        cfg.Model.Filename,
+		Data:            b,
+		GeneratedHeader: true,
+	})
+}

vendor/github.com/99designs/gqlgen/plugin/modelgen/models.gotpl πŸ”—

@@ -0,0 +1,85 @@
+{{ reserveImport "context"  }}
+{{ reserveImport "fmt"  }}
+{{ reserveImport "io"  }}
+{{ reserveImport "strconv"  }}
+{{ reserveImport "time"  }}
+{{ reserveImport "sync"  }}
+{{ reserveImport "errors"  }}
+{{ reserveImport "bytes"  }}
+
+{{ reserveImport "github.com/vektah/gqlparser" }}
+{{ reserveImport "github.com/vektah/gqlparser/ast" }}
+{{ reserveImport "github.com/99designs/gqlgen/graphql" }}
+{{ reserveImport "github.com/99designs/gqlgen/graphql/introspection" }}
+
+{{- range $model := .Interfaces }}
+	{{ with .Description }} {{.|prefixLines "// "}} {{ end }}
+	type {{.Name|go }} interface {
+		Is{{.Name|go }}()
+	}
+{{- end }}
+
+{{ range $model := .Models }}
+	{{with .Description }} {{.|prefixLines "// "}} {{end}}
+	type {{ .Name|go }} struct {
+		{{- range $field := .Fields }}
+			{{- with .Description }}
+				{{.|prefixLines "// "}}
+			{{- end}}
+			{{ $field.Name|go }} {{$field.Type | ref}} `{{$field.Tag}}`
+		{{- end }}
+	}
+
+	{{- range $iface := .Implements }}
+		func ({{ $model.Name|go }}) Is{{ $iface }}() {}
+	{{- end }}
+{{- end}}
+
+{{ range $enum := .Enums }}
+	{{ with .Description|go }} {{.|prefixLines "// "}} {{end}}
+	type {{.Name|go }} string
+	const (
+	{{- range $value := .Values}}
+		{{- with .Description}}
+			{{.|prefixLines "// "}}
+		{{- end}}
+		{{ $enum.Name|go }}{{ .Name|go }} {{$enum.Name|go }} = {{.Name|quote}}
+	{{- end }}
+	)
+
+	var All{{.Name|go }} = []{{ .Name|go }}{
+	{{- range $value := .Values}}
+		{{$enum.Name|go }}{{ .Name|go }},
+	{{- end }}
+	}
+
+	func (e {{.Name|go }}) IsValid() bool {
+		switch e {
+		case {{ range $index, $element := .Values}}{{if $index}},{{end}}{{ $enum.Name|go }}{{ $element.Name|go }}{{end}}:
+			return true
+		}
+		return false
+	}
+
+	func (e {{.Name|go }}) String() string {
+		return string(e)
+	}
+
+	func (e *{{.Name|go }}) UnmarshalGQL(v interface{}) error {
+		str, ok := v.(string)
+		if !ok {
+			return fmt.Errorf("enums must be strings")
+		}
+
+		*e = {{ .Name|go }}(str)
+		if !e.IsValid() {
+			return fmt.Errorf("%s is not a valid {{ .Name }}", str)
+		}
+		return nil
+	}
+
+	func (e {{.Name|go }}) MarshalGQL(w io.Writer) {
+		fmt.Fprint(w, strconv.Quote(e.String()))
+	}
+
+{{- end }}

vendor/github.com/99designs/gqlgen/plugin/plugin.go πŸ”—

@@ -0,0 +1,20 @@
+// plugin package interfaces are EXPERIMENTAL.
+
+package plugin
+
+import (
+	"github.com/99designs/gqlgen/codegen"
+	"github.com/99designs/gqlgen/codegen/config"
+)
+
+type Plugin interface {
+	Name() string
+}
+
+type ConfigMutator interface {
+	MutateConfig(cfg *config.Config) error
+}
+
+type CodeGenerator interface {
+	GenerateCode(cfg *codegen.Data) error
+}

vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.go πŸ”—

@@ -0,0 +1,53 @@
+package resolvergen
+
+import (
+	"log"
+	"os"
+
+	"github.com/99designs/gqlgen/codegen"
+	"github.com/99designs/gqlgen/codegen/templates"
+	"github.com/99designs/gqlgen/plugin"
+	"github.com/pkg/errors"
+)
+
+func New() plugin.Plugin {
+	return &Plugin{}
+}
+
+type Plugin struct{}
+
+var _ plugin.CodeGenerator = &Plugin{}
+
+func (m *Plugin) Name() string {
+	return "resovlergen"
+}
+func (m *Plugin) GenerateCode(data *codegen.Data) error {
+	if !data.Config.Resolver.IsDefined() {
+		return nil
+	}
+
+	resolverBuild := &ResolverBuild{
+		Data:         data,
+		PackageName:  data.Config.Resolver.Package,
+		ResolverType: data.Config.Resolver.Type,
+	}
+	filename := data.Config.Resolver.Filename
+
+	if _, err := os.Stat(filename); os.IsNotExist(errors.Cause(err)) {
+		return templates.Render(templates.Options{
+			PackageName: data.Config.Resolver.Package,
+			Filename:    data.Config.Resolver.Filename,
+			Data:        resolverBuild,
+		})
+	}
+
+	log.Printf("Skipped resolver: %s already exists\n", filename)
+	return nil
+}
+
+type ResolverBuild struct {
+	*codegen.Data
+
+	PackageName  string
+	ResolverType string
+}

vendor/github.com/99designs/gqlgen/plugin/resolvergen/resolver.gotpl πŸ”—

@@ -0,0 +1,40 @@
+// THIS CODE IS A STARTING POINT ONLY. IT WILL NOT BE UPDATED WITH SCHEMA CHANGES.
+
+{{ reserveImport "context"  }}
+{{ reserveImport "fmt"  }}
+{{ reserveImport "io"  }}
+{{ reserveImport "strconv"  }}
+{{ reserveImport "time"  }}
+{{ reserveImport "sync"  }}
+{{ reserveImport "errors"  }}
+{{ reserveImport "bytes"  }}
+
+{{ reserveImport "github.com/99designs/gqlgen/handler" }}
+{{ reserveImport "github.com/vektah/gqlparser" }}
+{{ reserveImport "github.com/vektah/gqlparser/ast" }}
+{{ reserveImport "github.com/99designs/gqlgen/graphql" }}
+{{ reserveImport "github.com/99designs/gqlgen/graphql/introspection" }}
+
+type {{.ResolverType}} struct {}
+
+{{ range $object := .Objects -}}
+	{{- if $object.HasResolvers -}}
+		func (r *{{$.ResolverType}}) {{$object.Name}}() {{ $object.ResolverInterface | ref }} {
+			return &{{lcFirst $object.Name}}Resolver{r}
+		}
+	{{ end -}}
+{{ end }}
+
+{{ range $object := .Objects -}}
+	{{- if $object.HasResolvers -}}
+		type {{lcFirst $object.Name}}Resolver struct { *Resolver }
+
+		{{ range $field := $object.Fields -}}
+			{{- if $field.IsResolver -}}
+			func (r *{{lcFirst $object.Name}}Resolver) {{$field.GoFieldName}}{{ $field.ShortResolverDeclaration }} {
+				panic("not implemented")
+			}
+			{{ end -}}
+		{{ end -}}
+	{{ end -}}
+{{ end }}

vendor/github.com/99designs/gqlgen/plugin/servergen/server.go πŸ”—

@@ -0,0 +1,49 @@
+package servergen
+
+import (
+	"log"
+	"os"
+
+	"github.com/99designs/gqlgen/codegen"
+	"github.com/99designs/gqlgen/codegen/templates"
+	"github.com/99designs/gqlgen/plugin"
+	"github.com/pkg/errors"
+)
+
+func New(filename string) plugin.Plugin {
+	return &Plugin{filename}
+}
+
+type Plugin struct {
+	filename string
+}
+
+var _ plugin.CodeGenerator = &Plugin{}
+
+func (m *Plugin) Name() string {
+	return "servergen"
+}
+func (m *Plugin) GenerateCode(data *codegen.Data) error {
+	serverBuild := &ServerBuild{
+		ExecPackageName:     data.Config.Exec.ImportPath(),
+		ResolverPackageName: data.Config.Resolver.ImportPath(),
+	}
+
+	if _, err := os.Stat(m.filename); os.IsNotExist(errors.Cause(err)) {
+		return templates.Render(templates.Options{
+			PackageName: "main",
+			Filename:    m.filename,
+			Data:        serverBuild,
+		})
+	}
+
+	log.Printf("Skipped server: %s already exists\n", m.filename)
+	return nil
+}
+
+type ServerBuild struct {
+	codegen.Data
+
+	ExecPackageName     string
+	ResolverPackageName string
+}

vendor/github.com/99designs/gqlgen/codegen/templates/server.gotpl β†’ vendor/github.com/99designs/gqlgen/plugin/servergen/server.gotpl πŸ”—

@@ -1,14 +1,8 @@
-package main
-
-import (
-	%%%IMPORTS%%%
-
-	{{ reserveImport "context" }}
-	{{ reserveImport "log" }}
-	{{ reserveImport "net/http" }}
-	{{ reserveImport "os" }}
-	{{ reserveImport "github.com/99designs/gqlgen/handler" }}
-)
+{{ reserveImport "context" }}
+{{ reserveImport "log" }}
+{{ reserveImport "net/http" }}
+{{ reserveImport "os" }}
+{{ reserveImport "github.com/99designs/gqlgen/handler" }}
 
 const defaultPort = "8080"
 

vendor/golang.org/x/tools/go/buildutil/allpackages.go πŸ”—

@@ -1,198 +0,0 @@
-// 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
-}

vendor/golang.org/x/tools/go/buildutil/fakecontext.go πŸ”—

@@ -1,109 +0,0 @@
-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 }

vendor/golang.org/x/tools/go/buildutil/overlay.go πŸ”—

@@ -1,103 +0,0 @@
-// 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
-// newlinews. 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
-}

vendor/golang.org/x/tools/go/buildutil/tags.go πŸ”—

@@ -1,75 +0,0 @@
-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'
-}

vendor/golang.org/x/tools/go/buildutil/util.go πŸ”—

@@ -1,212 +0,0 @@
-// 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
-}

vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go πŸ”—

@@ -0,0 +1,109 @@
+// 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 gcexportdata provides functions for locating, reading, and
+// writing export data files containing type information produced by the
+// gc compiler.  This package supports go1.7 export data format and all
+// later versions.
+//
+// Although it might seem convenient for this package to live alongside
+// go/types in the standard library, this would cause version skew
+// problems for developer tools that use it, since they must be able to
+// consume the outputs of the gc compiler both before and after a Go
+// update such as from Go 1.7 to Go 1.8.  Because this package lives in
+// golang.org/x/tools, sites can update their version of this repo some
+// time before the Go 1.8 release and rebuild and redeploy their
+// developer tools, which will then be able to consume both Go 1.7 and
+// Go 1.8 export data files, so they will work before and after the
+// Go update. (See discussion at https://github.com/golang/go/issues/15651.)
+//
+package gcexportdata // import "golang.org/x/tools/go/gcexportdata"
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"go/token"
+	"go/types"
+	"io"
+	"io/ioutil"
+
+	"golang.org/x/tools/go/internal/gcimporter"
+)
+
+// Find returns the name of an object (.o) or archive (.a) file
+// containing type information for the specified import path,
+// using the workspace layout conventions of go/build.
+// If no file was found, an empty filename is returned.
+//
+// A relative srcDir is interpreted relative to the current working directory.
+//
+// Find also returns the package's resolved (canonical) import path,
+// reflecting the effects of srcDir and vendoring on importPath.
+func Find(importPath, srcDir string) (filename, path string) {
+	return gcimporter.FindPkg(importPath, srcDir)
+}
+
+// NewReader returns a reader for the export data section of an object
+// (.o) or archive (.a) file read from r.  The new reader may provide
+// additional trailing data beyond the end of the export data.
+func NewReader(r io.Reader) (io.Reader, error) {
+	buf := bufio.NewReader(r)
+	_, err := gcimporter.FindExportData(buf)
+	// If we ever switch to a zip-like archive format with the ToC
+	// at the end, we can return the correct portion of export data,
+	// but for now we must return the entire rest of the file.
+	return buf, err
+}
+
+// Read reads export data from in, decodes it, and returns type
+// information for the package.
+// The package name is specified by path.
+// File position information is added to fset.
+//
+// Read may inspect and add to the imports map to ensure that references
+// within the export data to other packages are consistent.  The caller
+// must ensure that imports[path] does not exist, or exists but is
+// incomplete (see types.Package.Complete), and Read inserts the
+// resulting package into this map entry.
+//
+// On return, the state of the reader is undefined.
+func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
+	data, err := ioutil.ReadAll(in)
+	if err != nil {
+		return nil, fmt.Errorf("reading export data for %q: %v", path, err)
+	}
+
+	if bytes.HasPrefix(data, []byte("!<arch>")) {
+		return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path)
+	}
+
+	// The App Engine Go runtime v1.6 uses the old export data format.
+	// TODO(adonovan): delete once v1.7 has been around for a while.
+	if bytes.HasPrefix(data, []byte("package ")) {
+		return gcimporter.ImportData(imports, path, path, bytes.NewReader(data))
+	}
+
+	// The indexed export format starts with an 'i'; the older
+	// binary export format starts with a 'c', 'd', or 'v'
+	// (from "version"). Select appropriate importer.
+	if len(data) > 0 && data[0] == 'i' {
+		_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
+		return pkg, err
+	}
+
+	_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
+	return pkg, err
+}
+
+// Write writes encoded type information for the specified package to out.
+// The FileSet provides file position information for named objects.
+func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
+	b, err := gcimporter.BExportData(fset, pkg)
+	if err != nil {
+		return err
+	}
+	_, err = out.Write(b)
+	return err
+}

vendor/golang.org/x/tools/go/gcexportdata/importer.go πŸ”—

@@ -0,0 +1,73 @@
+// 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 gcexportdata
+
+import (
+	"fmt"
+	"go/token"
+	"go/types"
+	"os"
+)
+
+// NewImporter returns a new instance of the types.Importer interface
+// that reads type information from export data files written by gc.
+// The Importer also satisfies types.ImporterFrom.
+//
+// Export data files are located using "go build" workspace conventions
+// and the build.Default context.
+//
+// Use this importer instead of go/importer.For("gc", ...) to avoid the
+// version-skew problems described in the documentation of this package,
+// or to control the FileSet or access the imports map populated during
+// package loading.
+//
+func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
+	return importer{fset, imports}
+}
+
+type importer struct {
+	fset    *token.FileSet
+	imports map[string]*types.Package
+}
+
+func (imp importer) Import(importPath string) (*types.Package, error) {
+	return imp.ImportFrom(importPath, "", 0)
+}
+
+func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) {
+	filename, path := Find(importPath, srcDir)
+	if filename == "" {
+		if importPath == "unsafe" {
+			// Even for unsafe, call Find first in case
+			// the package was vendored.
+			return types.Unsafe, nil
+		}
+		return nil, fmt.Errorf("can't find import: %s", importPath)
+	}
+
+	if pkg, ok := imp.imports[path]; ok && pkg.Complete() {
+		return pkg, nil // cache hit
+	}
+
+	// open file
+	f, err := os.Open(filename)
+	if err != nil {
+		return nil, err
+	}
+	defer func() {
+		f.Close()
+		if err != nil {
+			// add file name to error
+			err = fmt.Errorf("reading export data: %s: %v", filename, err)
+		}
+	}()
+
+	r, err := NewReader(f)
+	if err != nil {
+		return nil, err
+	}
+
+	return Read(r, imp.fset, imp.imports, path)
+}

vendor/golang.org/x/tools/go/gcexportdata/main.go πŸ”—

@@ -0,0 +1,99 @@
+// 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.
+
+// +build ignore
+
+// The gcexportdata command is a diagnostic tool that displays the
+// contents of gc export data files.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"go/token"
+	"go/types"
+	"log"
+	"os"
+
+	"golang.org/x/tools/go/gcexportdata"
+	"golang.org/x/tools/go/types/typeutil"
+)
+
+var packageFlag = flag.String("package", "", "alternative package to print")
+
+func main() {
+	log.SetPrefix("gcexportdata: ")
+	log.SetFlags(0)
+	flag.Usage = func() {
+		fmt.Fprintln(os.Stderr, "usage: gcexportdata [-package path] file.a")
+	}
+	flag.Parse()
+	if flag.NArg() != 1 {
+		flag.Usage()
+		os.Exit(2)
+	}
+	filename := flag.Args()[0]
+
+	f, err := os.Open(filename)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	r, err := gcexportdata.NewReader(f)
+	if err != nil {
+		log.Fatalf("%s: %s", filename, err)
+	}
+
+	// Decode the package.
+	const primary = "<primary>"
+	imports := make(map[string]*types.Package)
+	fset := token.NewFileSet()
+	pkg, err := gcexportdata.Read(r, fset, imports, primary)
+	if err != nil {
+		log.Fatalf("%s: %s", filename, err)
+	}
+
+	// Optionally select an indirectly mentioned package.
+	if *packageFlag != "" {
+		pkg = imports[*packageFlag]
+		if pkg == nil {
+			fmt.Fprintf(os.Stderr, "export data file %s does not mention %s; has:\n",
+				filename, *packageFlag)
+			for p := range imports {
+				if p != primary {
+					fmt.Fprintf(os.Stderr, "\t%s\n", p)
+				}
+			}
+			os.Exit(1)
+		}
+	}
+
+	// Print all package-level declarations, including non-exported ones.
+	fmt.Printf("package %s\n", pkg.Name())
+	for _, imp := range pkg.Imports() {
+		fmt.Printf("import %q\n", imp.Path())
+	}
+	qual := func(p *types.Package) string {
+		if pkg == p {
+			return ""
+		}
+		return p.Name()
+	}
+	scope := pkg.Scope()
+	for _, name := range scope.Names() {
+		obj := scope.Lookup(name)
+		fmt.Printf("%s: %s\n",
+			fset.Position(obj.Pos()),
+			types.ObjectString(obj, qual))
+
+		// For types, print each method.
+		if _, ok := obj.(*types.TypeName); ok {
+			for _, method := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
+				fmt.Printf("%s: %s\n",
+					fset.Position(method.Obj().Pos()),
+					types.SelectionString(method, qual))
+			}
+		}
+	}
+}

vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go πŸ”—

@@ -0,0 +1,852 @@
+// 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.
+
+// Binary package export.
+// This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go;
+// see that file for specification of the format.
+
+package gcimporter
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"go/ast"
+	"go/constant"
+	"go/token"
+	"go/types"
+	"math"
+	"math/big"
+	"sort"
+	"strings"
+)
+
+// If debugFormat is set, each integer and string value is preceded by a marker
+// and position information in the encoding. This mechanism permits an importer
+// to recognize immediately when it is out of sync. The importer recognizes this
+// mode automatically (i.e., it can import export data produced with debugging
+// support even if debugFormat is not set at the time of import). This mode will
+// lead to massively larger export data (by a factor of 2 to 3) and should only
+// be enabled during development and debugging.
+//
+// NOTE: This flag is the first flag to enable if importing dies because of
+// (suspected) format errors, and whenever a change is made to the format.
+const debugFormat = false // default: false
+
+// If trace is set, debugging output is printed to std out.
+const trace = false // default: false
+
+// Current export format version. Increase with each format change.
+// Note: The latest binary (non-indexed) export format is at version 6.
+//       This exporter is still at level 4, but it doesn't matter since
+//       the binary importer can handle older versions just fine.
+// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE
+// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMEMTED HERE
+// 4: type name objects support type aliases, uses aliasTag
+// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
+// 2: removed unused bool in ODCL export (compiler only)
+// 1: header format change (more regular), export package for _ struct fields
+// 0: Go1.7 encoding
+const exportVersion = 4
+
+// trackAllTypes enables cycle tracking for all types, not just named
+// types. The existing compiler invariants assume that unnamed types
+// that are not completely set up are not used, or else there are spurious
+// errors.
+// If disabled, only named types are tracked, possibly leading to slightly
+// less efficient encoding in rare cases. It also prevents the export of
+// some corner-case type declarations (but those are not handled correctly
+// with with the textual export format either).
+// TODO(gri) enable and remove once issues caused by it are fixed
+const trackAllTypes = false
+
+type exporter struct {
+	fset *token.FileSet
+	out  bytes.Buffer
+
+	// object -> index maps, indexed in order of serialization
+	strIndex map[string]int
+	pkgIndex map[*types.Package]int
+	typIndex map[types.Type]int
+
+	// position encoding
+	posInfoFormat bool
+	prevFile      string
+	prevLine      int
+
+	// debugging support
+	written int // bytes written
+	indent  int // for trace
+}
+
+// internalError represents an error generated inside this package.
+type internalError string
+
+func (e internalError) Error() string { return "gcimporter: " + string(e) }
+
+func internalErrorf(format string, args ...interface{}) error {
+	return internalError(fmt.Sprintf(format, args...))
+}
+
+// BExportData returns binary export data for pkg.
+// If no file set is provided, position info will be missing.
+func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			if ierr, ok := e.(internalError); ok {
+				err = ierr
+				return
+			}
+			// Not an internal error; panic again.
+			panic(e)
+		}
+	}()
+
+	p := exporter{
+		fset:          fset,
+		strIndex:      map[string]int{"": 0}, // empty string is mapped to 0
+		pkgIndex:      make(map[*types.Package]int),
+		typIndex:      make(map[types.Type]int),
+		posInfoFormat: true, // TODO(gri) might become a flag, eventually
+	}
+
+	// write version info
+	// The version string must start with "version %d" where %d is the version
+	// number. Additional debugging information may follow after a blank; that
+	// text is ignored by the importer.
+	p.rawStringln(fmt.Sprintf("version %d", exportVersion))
+	var debug string
+	if debugFormat {
+		debug = "debug"
+	}
+	p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly
+	p.bool(trackAllTypes)
+	p.bool(p.posInfoFormat)
+
+	// --- generic export data ---
+
+	// populate type map with predeclared "known" types
+	for index, typ := range predeclared {
+		p.typIndex[typ] = index
+	}
+	if len(p.typIndex) != len(predeclared) {
+		return nil, internalError("duplicate entries in type map?")
+	}
+
+	// write package data
+	p.pkg(pkg, true)
+	if trace {
+		p.tracef("\n")
+	}
+
+	// write objects
+	objcount := 0
+	scope := pkg.Scope()
+	for _, name := range scope.Names() {
+		if !ast.IsExported(name) {
+			continue
+		}
+		if trace {
+			p.tracef("\n")
+		}
+		p.obj(scope.Lookup(name))
+		objcount++
+	}
+
+	// indicate end of list
+	if trace {
+		p.tracef("\n")
+	}
+	p.tag(endTag)
+
+	// for self-verification only (redundant)
+	p.int(objcount)
+
+	if trace {
+		p.tracef("\n")
+	}
+
+	// --- end of export data ---
+
+	return p.out.Bytes(), nil
+}
+
+func (p *exporter) pkg(pkg *types.Package, emptypath bool) {
+	if pkg == nil {
+		panic(internalError("unexpected nil pkg"))
+	}
+
+	// if we saw the package before, write its index (>= 0)
+	if i, ok := p.pkgIndex[pkg]; ok {
+		p.index('P', i)
+		return
+	}
+
+	// otherwise, remember the package, write the package tag (< 0) and package data
+	if trace {
+		p.tracef("P%d = { ", len(p.pkgIndex))
+		defer p.tracef("} ")
+	}
+	p.pkgIndex[pkg] = len(p.pkgIndex)
+
+	p.tag(packageTag)
+	p.string(pkg.Name())
+	if emptypath {
+		p.string("")
+	} else {
+		p.string(pkg.Path())
+	}
+}
+
+func (p *exporter) obj(obj types.Object) {
+	switch obj := obj.(type) {
+	case *types.Const:
+		p.tag(constTag)
+		p.pos(obj)
+		p.qualifiedName(obj)
+		p.typ(obj.Type())
+		p.value(obj.Val())
+
+	case *types.TypeName:
+		if isAlias(obj) {
+			p.tag(aliasTag)
+			p.pos(obj)
+			p.qualifiedName(obj)
+		} else {
+			p.tag(typeTag)
+		}
+		p.typ(obj.Type())
+
+	case *types.Var:
+		p.tag(varTag)
+		p.pos(obj)
+		p.qualifiedName(obj)
+		p.typ(obj.Type())
+
+	case *types.Func:
+		p.tag(funcTag)
+		p.pos(obj)
+		p.qualifiedName(obj)
+		sig := obj.Type().(*types.Signature)
+		p.paramList(sig.Params(), sig.Variadic())
+		p.paramList(sig.Results(), false)
+
+	default:
+		panic(internalErrorf("unexpected object %v (%T)", obj, obj))
+	}
+}
+
+func (p *exporter) pos(obj types.Object) {
+	if !p.posInfoFormat {
+		return
+	}
+
+	file, line := p.fileLine(obj)
+	if file == p.prevFile {
+		// common case: write line delta
+		// delta == 0 means different file or no line change
+		delta := line - p.prevLine
+		p.int(delta)
+		if delta == 0 {
+			p.int(-1) // -1 means no file change
+		}
+	} else {
+		// different file
+		p.int(0)
+		// Encode filename as length of common prefix with previous
+		// filename, followed by (possibly empty) suffix. Filenames
+		// frequently share path prefixes, so this can save a lot
+		// of space and make export data size less dependent on file
+		// path length. The suffix is unlikely to be empty because
+		// file names tend to end in ".go".
+		n := commonPrefixLen(p.prevFile, file)
+		p.int(n)           // n >= 0
+		p.string(file[n:]) // write suffix only
+		p.prevFile = file
+		p.int(line)
+	}
+	p.prevLine = line
+}
+
+func (p *exporter) fileLine(obj types.Object) (file string, line int) {
+	if p.fset != nil {
+		pos := p.fset.Position(obj.Pos())
+		file = pos.Filename
+		line = pos.Line
+	}
+	return
+}
+
+func commonPrefixLen(a, b string) int {
+	if len(a) > len(b) {
+		a, b = b, a
+	}
+	// len(a) <= len(b)
+	i := 0
+	for i < len(a) && a[i] == b[i] {
+		i++
+	}
+	return i
+}
+
+func (p *exporter) qualifiedName(obj types.Object) {
+	p.string(obj.Name())
+	p.pkg(obj.Pkg(), false)
+}
+
+func (p *exporter) typ(t types.Type) {
+	if t == nil {
+		panic(internalError("nil type"))
+	}
+
+	// Possible optimization: Anonymous pointer types *T where
+	// T is a named type are common. We could canonicalize all
+	// such types *T to a single type PT = *T. This would lead
+	// to at most one *T entry in typIndex, and all future *T's
+	// would be encoded as the respective index directly. Would
+	// save 1 byte (pointerTag) per *T and reduce the typIndex
+	// size (at the cost of a canonicalization map). We can do
+	// this later, without encoding format change.
+
+	// if we saw the type before, write its index (>= 0)
+	if i, ok := p.typIndex[t]; ok {
+		p.index('T', i)
+		return
+	}
+
+	// otherwise, remember the type, write the type tag (< 0) and type data
+	if trackAllTypes {
+		if trace {
+			p.tracef("T%d = {>\n", len(p.typIndex))
+			defer p.tracef("<\n} ")
+		}
+		p.typIndex[t] = len(p.typIndex)
+	}
+
+	switch t := t.(type) {
+	case *types.Named:
+		if !trackAllTypes {
+			// if we don't track all types, track named types now
+			p.typIndex[t] = len(p.typIndex)
+		}
+
+		p.tag(namedTag)
+		p.pos(t.Obj())
+		p.qualifiedName(t.Obj())
+		p.typ(t.Underlying())
+		if !types.IsInterface(t) {
+			p.assocMethods(t)
+		}
+
+	case *types.Array:
+		p.tag(arrayTag)
+		p.int64(t.Len())
+		p.typ(t.Elem())
+
+	case *types.Slice:
+		p.tag(sliceTag)
+		p.typ(t.Elem())
+
+	case *dddSlice:
+		p.tag(dddTag)
+		p.typ(t.elem)
+
+	case *types.Struct:
+		p.tag(structTag)
+		p.fieldList(t)
+
+	case *types.Pointer:
+		p.tag(pointerTag)
+		p.typ(t.Elem())
+
+	case *types.Signature:
+		p.tag(signatureTag)
+		p.paramList(t.Params(), t.Variadic())
+		p.paramList(t.Results(), false)
+
+	case *types.Interface:
+		p.tag(interfaceTag)
+		p.iface(t)
+
+	case *types.Map:
+		p.tag(mapTag)
+		p.typ(t.Key())
+		p.typ(t.Elem())
+
+	case *types.Chan:
+		p.tag(chanTag)
+		p.int(int(3 - t.Dir())) // hack
+		p.typ(t.Elem())
+
+	default:
+		panic(internalErrorf("unexpected type %T: %s", t, t))
+	}
+}
+
+func (p *exporter) assocMethods(named *types.Named) {
+	// Sort methods (for determinism).
+	var methods []*types.Func
+	for i := 0; i < named.NumMethods(); i++ {
+		methods = append(methods, named.Method(i))
+	}
+	sort.Sort(methodsByName(methods))
+
+	p.int(len(methods))
+
+	if trace && methods != nil {
+		p.tracef("associated methods {>\n")
+	}
+
+	for i, m := range methods {
+		if trace && i > 0 {
+			p.tracef("\n")
+		}
+
+		p.pos(m)
+		name := m.Name()
+		p.string(name)
+		if !exported(name) {
+			p.pkg(m.Pkg(), false)
+		}
+
+		sig := m.Type().(*types.Signature)
+		p.paramList(types.NewTuple(sig.Recv()), false)
+		p.paramList(sig.Params(), sig.Variadic())
+		p.paramList(sig.Results(), false)
+		p.int(0) // dummy value for go:nointerface pragma - ignored by importer
+	}
+
+	if trace && methods != nil {
+		p.tracef("<\n} ")
+	}
+}
+
+type methodsByName []*types.Func
+
+func (x methodsByName) Len() int           { return len(x) }
+func (x methodsByName) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
+func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() }
+
+func (p *exporter) fieldList(t *types.Struct) {
+	if trace && t.NumFields() > 0 {
+		p.tracef("fields {>\n")
+		defer p.tracef("<\n} ")
+	}
+
+	p.int(t.NumFields())
+	for i := 0; i < t.NumFields(); i++ {
+		if trace && i > 0 {
+			p.tracef("\n")
+		}
+		p.field(t.Field(i))
+		p.string(t.Tag(i))
+	}
+}
+
+func (p *exporter) field(f *types.Var) {
+	if !f.IsField() {
+		panic(internalError("field expected"))
+	}
+
+	p.pos(f)
+	p.fieldName(f)
+	p.typ(f.Type())
+}
+
+func (p *exporter) iface(t *types.Interface) {
+	// TODO(gri): enable importer to load embedded interfaces,
+	// then emit Embeddeds and ExplicitMethods separately here.
+	p.int(0)
+
+	n := t.NumMethods()
+	if trace && n > 0 {
+		p.tracef("methods {>\n")
+		defer p.tracef("<\n} ")
+	}
+	p.int(n)
+	for i := 0; i < n; i++ {
+		if trace && i > 0 {
+			p.tracef("\n")
+		}
+		p.method(t.Method(i))
+	}
+}
+
+func (p *exporter) method(m *types.Func) {
+	sig := m.Type().(*types.Signature)
+	if sig.Recv() == nil {
+		panic(internalError("method expected"))
+	}
+
+	p.pos(m)
+	p.string(m.Name())
+	if m.Name() != "_" && !ast.IsExported(m.Name()) {
+		p.pkg(m.Pkg(), false)
+	}
+
+	// interface method; no need to encode receiver.
+	p.paramList(sig.Params(), sig.Variadic())
+	p.paramList(sig.Results(), false)
+}
+
+func (p *exporter) fieldName(f *types.Var) {
+	name := f.Name()
+
+	if f.Anonymous() {
+		// anonymous field - we distinguish between 3 cases:
+		// 1) field name matches base type name and is exported
+		// 2) field name matches base type name and is not exported
+		// 3) field name doesn't match base type name (alias name)
+		bname := basetypeName(f.Type())
+		if name == bname {
+			if ast.IsExported(name) {
+				name = "" // 1) we don't need to know the field name or package
+			} else {
+				name = "?" // 2) use unexported name "?" to force package export
+			}
+		} else {
+			// 3) indicate alias and export name as is
+			// (this requires an extra "@" but this is a rare case)
+			p.string("@")
+		}
+	}
+
+	p.string(name)
+	if name != "" && !ast.IsExported(name) {
+		p.pkg(f.Pkg(), false)
+	}
+}
+
+func basetypeName(typ types.Type) string {
+	switch typ := deref(typ).(type) {
+	case *types.Basic:
+		return typ.Name()
+	case *types.Named:
+		return typ.Obj().Name()
+	default:
+		return "" // unnamed type
+	}
+}
+
+func (p *exporter) paramList(params *types.Tuple, variadic bool) {
+	// use negative length to indicate unnamed parameters
+	// (look at the first parameter only since either all
+	// names are present or all are absent)
+	n := params.Len()
+	if n > 0 && params.At(0).Name() == "" {
+		n = -n
+	}
+	p.int(n)
+	for i := 0; i < params.Len(); i++ {
+		q := params.At(i)
+		t := q.Type()
+		if variadic && i == params.Len()-1 {
+			t = &dddSlice{t.(*types.Slice).Elem()}
+		}
+		p.typ(t)
+		if n > 0 {
+			name := q.Name()
+			p.string(name)
+			if name != "_" {
+				p.pkg(q.Pkg(), false)
+			}
+		}
+		p.string("") // no compiler-specific info
+	}
+}
+
+func (p *exporter) value(x constant.Value) {
+	if trace {
+		p.tracef("= ")
+	}
+
+	switch x.Kind() {
+	case constant.Bool:
+		tag := falseTag
+		if constant.BoolVal(x) {
+			tag = trueTag
+		}
+		p.tag(tag)
+
+	case constant.Int:
+		if v, exact := constant.Int64Val(x); exact {
+			// common case: x fits into an int64 - use compact encoding
+			p.tag(int64Tag)
+			p.int64(v)
+			return
+		}
+		// uncommon case: large x - use float encoding
+		// (powers of 2 will be encoded efficiently with exponent)
+		p.tag(floatTag)
+		p.float(constant.ToFloat(x))
+
+	case constant.Float:
+		p.tag(floatTag)
+		p.float(x)
+
+	case constant.Complex:
+		p.tag(complexTag)
+		p.float(constant.Real(x))
+		p.float(constant.Imag(x))
+
+	case constant.String:
+		p.tag(stringTag)
+		p.string(constant.StringVal(x))
+
+	case constant.Unknown:
+		// package contains type errors
+		p.tag(unknownTag)
+
+	default:
+		panic(internalErrorf("unexpected value %v (%T)", x, x))
+	}
+}
+
+func (p *exporter) float(x constant.Value) {
+	if x.Kind() != constant.Float {
+		panic(internalErrorf("unexpected constant %v, want float", x))
+	}
+	// extract sign (there is no -0)
+	sign := constant.Sign(x)
+	if sign == 0 {
+		// x == 0
+		p.int(0)
+		return
+	}
+	// x != 0
+
+	var f big.Float
+	if v, exact := constant.Float64Val(x); exact {
+		// float64
+		f.SetFloat64(v)
+	} else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int {
+		// TODO(gri): add big.Rat accessor to constant.Value.
+		r := valueToRat(num)
+		f.SetRat(r.Quo(r, valueToRat(denom)))
+	} else {
+		// Value too large to represent as a fraction => inaccessible.
+		// TODO(gri): add big.Float accessor to constant.Value.
+		f.SetFloat64(math.MaxFloat64) // FIXME
+	}
+
+	// extract exponent such that 0.5 <= m < 1.0
+	var m big.Float
+	exp := f.MantExp(&m)
+
+	// extract mantissa as *big.Int
+	// - set exponent large enough so mant satisfies mant.IsInt()
+	// - get *big.Int from mant
+	m.SetMantExp(&m, int(m.MinPrec()))
+	mant, acc := m.Int(nil)
+	if acc != big.Exact {
+		panic(internalError("internal error"))
+	}
+
+	p.int(sign)
+	p.int(exp)
+	p.string(string(mant.Bytes()))
+}
+
+func valueToRat(x constant.Value) *big.Rat {
+	// Convert little-endian to big-endian.
+	// I can't believe this is necessary.
+	bytes := constant.Bytes(x)
+	for i := 0; i < len(bytes)/2; i++ {
+		bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i]
+	}
+	return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes))
+}
+
+func (p *exporter) bool(b bool) bool {
+	if trace {
+		p.tracef("[")
+		defer p.tracef("= %v] ", b)
+	}
+
+	x := 0
+	if b {
+		x = 1
+	}
+	p.int(x)
+	return b
+}
+
+// ----------------------------------------------------------------------------
+// Low-level encoders
+
+func (p *exporter) index(marker byte, index int) {
+	if index < 0 {
+		panic(internalError("invalid index < 0"))
+	}
+	if debugFormat {
+		p.marker('t')
+	}
+	if trace {
+		p.tracef("%c%d ", marker, index)
+	}
+	p.rawInt64(int64(index))
+}
+
+func (p *exporter) tag(tag int) {
+	if tag >= 0 {
+		panic(internalError("invalid tag >= 0"))
+	}
+	if debugFormat {
+		p.marker('t')
+	}
+	if trace {
+		p.tracef("%s ", tagString[-tag])
+	}
+	p.rawInt64(int64(tag))
+}
+
+func (p *exporter) int(x int) {
+	p.int64(int64(x))
+}
+
+func (p *exporter) int64(x int64) {
+	if debugFormat {
+		p.marker('i')
+	}
+	if trace {
+		p.tracef("%d ", x)
+	}
+	p.rawInt64(x)
+}
+
+func (p *exporter) string(s string) {
+	if debugFormat {
+		p.marker('s')
+	}
+	if trace {
+		p.tracef("%q ", s)
+	}
+	// if we saw the string before, write its index (>= 0)
+	// (the empty string is mapped to 0)
+	if i, ok := p.strIndex[s]; ok {
+		p.rawInt64(int64(i))
+		return
+	}
+	// otherwise, remember string and write its negative length and bytes
+	p.strIndex[s] = len(p.strIndex)
+	p.rawInt64(-int64(len(s)))
+	for i := 0; i < len(s); i++ {
+		p.rawByte(s[i])
+	}
+}
+
+// marker emits a marker byte and position information which makes
+// it easy for a reader to detect if it is "out of sync". Used for
+// debugFormat format only.
+func (p *exporter) marker(m byte) {
+	p.rawByte(m)
+	// Enable this for help tracking down the location
+	// of an incorrect marker when running in debugFormat.
+	if false && trace {
+		p.tracef("#%d ", p.written)
+	}
+	p.rawInt64(int64(p.written))
+}
+
+// rawInt64 should only be used by low-level encoders.
+func (p *exporter) rawInt64(x int64) {
+	var tmp [binary.MaxVarintLen64]byte
+	n := binary.PutVarint(tmp[:], x)
+	for i := 0; i < n; i++ {
+		p.rawByte(tmp[i])
+	}
+}
+
+// rawStringln should only be used to emit the initial version string.
+func (p *exporter) rawStringln(s string) {
+	for i := 0; i < len(s); i++ {
+		p.rawByte(s[i])
+	}
+	p.rawByte('\n')
+}
+
+// rawByte is the bottleneck interface to write to p.out.
+// rawByte escapes b as follows (any encoding does that
+// hides '$'):
+//
+//	'$'  => '|' 'S'
+//	'|'  => '|' '|'
+//
+// Necessary so other tools can find the end of the
+// export data by searching for "$$".
+// rawByte should only be used by low-level encoders.
+func (p *exporter) rawByte(b byte) {
+	switch b {
+	case '$':
+		// write '$' as '|' 'S'
+		b = 'S'
+		fallthrough
+	case '|':
+		// write '|' as '|' '|'
+		p.out.WriteByte('|')
+		p.written++
+	}
+	p.out.WriteByte(b)
+	p.written++
+}
+
+// tracef is like fmt.Printf but it rewrites the format string
+// to take care of indentation.
+func (p *exporter) tracef(format string, args ...interface{}) {
+	if strings.ContainsAny(format, "<>\n") {
+		var buf bytes.Buffer
+		for i := 0; i < len(format); i++ {
+			// no need to deal with runes
+			ch := format[i]
+			switch ch {
+			case '>':
+				p.indent++
+				continue
+			case '<':
+				p.indent--
+				continue
+			}
+			buf.WriteByte(ch)
+			if ch == '\n' {
+				for j := p.indent; j > 0; j-- {
+					buf.WriteString(".  ")
+				}
+			}
+		}
+		format = buf.String()
+	}
+	fmt.Printf(format, args...)
+}
+
+// Debugging support.
+// (tagString is only used when tracing is enabled)
+var tagString = [...]string{
+	// Packages
+	-packageTag: "package",
+
+	// Types
+	-namedTag:     "named type",
+	-arrayTag:     "array",
+	-sliceTag:     "slice",
+	-dddTag:       "ddd",
+	-structTag:    "struct",
+	-pointerTag:   "pointer",
+	-signatureTag: "signature",
+	-interfaceTag: "interface",
+	-mapTag:       "map",
+	-chanTag:      "chan",
+
+	// Values
+	-falseTag:    "false",
+	-trueTag:     "true",
+	-int64Tag:    "int64",
+	-floatTag:    "float",
+	-fractionTag: "fraction",
+	-complexTag:  "complex",
+	-stringTag:   "string",
+	-unknownTag:  "unknown",
+
+	// Type aliases
+	-aliasTag: "alias",
+}

vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go πŸ”—

@@ -0,0 +1,1028 @@
+// 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.
+
+// This file is a copy of $GOROOT/src/go/internal/gcimporter/bimport.go.
+
+package gcimporter
+
+import (
+	"encoding/binary"
+	"fmt"
+	"go/constant"
+	"go/token"
+	"go/types"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"unicode"
+	"unicode/utf8"
+)
+
+type importer struct {
+	imports    map[string]*types.Package
+	data       []byte
+	importpath string
+	buf        []byte // for reading strings
+	version    int    // export format version
+
+	// object lists
+	strList       []string           // in order of appearance
+	pathList      []string           // in order of appearance
+	pkgList       []*types.Package   // in order of appearance
+	typList       []types.Type       // in order of appearance
+	interfaceList []*types.Interface // for delayed completion only
+	trackAllTypes bool
+
+	// position encoding
+	posInfoFormat bool
+	prevFile      string
+	prevLine      int
+	fake          fakeFileSet
+
+	// debugging support
+	debugFormat bool
+	read        int // bytes read
+}
+
+// BImportData imports a package from the serialized package data
+// and returns the number of bytes consumed and a reference to the package.
+// If the export data version is not recognized or the format is otherwise
+// compromised, an error is returned.
+func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
+	// catch panics and return them as errors
+	const currentVersion = 6
+	version := -1 // unknown version
+	defer func() {
+		if e := recover(); e != nil {
+			// Return a (possibly nil or incomplete) package unchanged (see #16088).
+			if version > currentVersion {
+				err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
+			} else {
+				err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
+			}
+		}
+	}()
+
+	p := importer{
+		imports:    imports,
+		data:       data,
+		importpath: path,
+		version:    version,
+		strList:    []string{""}, // empty string is mapped to 0
+		pathList:   []string{""}, // empty string is mapped to 0
+		fake: fakeFileSet{
+			fset:  fset,
+			files: make(map[string]*token.File),
+		},
+	}
+
+	// read version info
+	var versionstr string
+	if b := p.rawByte(); b == 'c' || b == 'd' {
+		// Go1.7 encoding; first byte encodes low-level
+		// encoding format (compact vs debug).
+		// For backward-compatibility only (avoid problems with
+		// old installed packages). Newly compiled packages use
+		// the extensible format string.
+		// TODO(gri) Remove this support eventually; after Go1.8.
+		if b == 'd' {
+			p.debugFormat = true
+		}
+		p.trackAllTypes = p.rawByte() == 'a'
+		p.posInfoFormat = p.int() != 0
+		versionstr = p.string()
+		if versionstr == "v1" {
+			version = 0
+		}
+	} else {
+		// Go1.8 extensible encoding
+		// read version string and extract version number (ignore anything after the version number)
+		versionstr = p.rawStringln(b)
+		if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" {
+			if v, err := strconv.Atoi(s[1]); err == nil && v > 0 {
+				version = v
+			}
+		}
+	}
+	p.version = version
+
+	// read version specific flags - extend as necessary
+	switch p.version {
+	// case currentVersion:
+	// 	...
+	//	fallthrough
+	case currentVersion, 5, 4, 3, 2, 1:
+		p.debugFormat = p.rawStringln(p.rawByte()) == "debug"
+		p.trackAllTypes = p.int() != 0
+		p.posInfoFormat = p.int() != 0
+	case 0:
+		// Go1.7 encoding format - nothing to do here
+	default:
+		errorf("unknown bexport format version %d (%q)", p.version, versionstr)
+	}
+
+	// --- generic export data ---
+
+	// populate typList with predeclared "known" types
+	p.typList = append(p.typList, predeclared...)
+
+	// read package data
+	pkg = p.pkg()
+
+	// read objects of phase 1 only (see cmd/compile/internal/gc/bexport.go)
+	objcount := 0
+	for {
+		tag := p.tagOrIndex()
+		if tag == endTag {
+			break
+		}
+		p.obj(tag)
+		objcount++
+	}
+
+	// self-verification
+	if count := p.int(); count != objcount {
+		errorf("got %d objects; want %d", objcount, count)
+	}
+
+	// ignore compiler-specific import data
+
+	// complete interfaces
+	// TODO(gri) re-investigate if we still need to do this in a delayed fashion
+	for _, typ := range p.interfaceList {
+		typ.Complete()
+	}
+
+	// record all referenced packages as imports
+	list := append(([]*types.Package)(nil), p.pkgList[1:]...)
+	sort.Sort(byPath(list))
+	pkg.SetImports(list)
+
+	// package was imported completely and without errors
+	pkg.MarkComplete()
+
+	return p.read, pkg, nil
+}
+
+func errorf(format string, args ...interface{}) {
+	panic(fmt.Sprintf(format, args...))
+}
+
+func (p *importer) pkg() *types.Package {
+	// if the package was seen before, i is its index (>= 0)
+	i := p.tagOrIndex()
+	if i >= 0 {
+		return p.pkgList[i]
+	}
+
+	// otherwise, i is the package tag (< 0)
+	if i != packageTag {
+		errorf("unexpected package tag %d version %d", i, p.version)
+	}
+
+	// read package data
+	name := p.string()
+	var path string
+	if p.version >= 5 {
+		path = p.path()
+	} else {
+		path = p.string()
+	}
+	if p.version >= 6 {
+		p.int() // package height; unused by go/types
+	}
+
+	// we should never see an empty package name
+	if name == "" {
+		errorf("empty package name in import")
+	}
+
+	// an empty path denotes the package we are currently importing;
+	// it must be the first package we see
+	if (path == "") != (len(p.pkgList) == 0) {
+		errorf("package path %q for pkg index %d", path, len(p.pkgList))
+	}
+
+	// if the package was imported before, use that one; otherwise create a new one
+	if path == "" {
+		path = p.importpath
+	}
+	pkg := p.imports[path]
+	if pkg == nil {
+		pkg = types.NewPackage(path, name)
+		p.imports[path] = pkg
+	} else if pkg.Name() != name {
+		errorf("conflicting names %s and %s for package %q", pkg.Name(), name, path)
+	}
+	p.pkgList = append(p.pkgList, pkg)
+
+	return pkg
+}
+
+// objTag returns the tag value for each object kind.
+func objTag(obj types.Object) int {
+	switch obj.(type) {
+	case *types.Const:
+		return constTag
+	case *types.TypeName:
+		return typeTag
+	case *types.Var:
+		return varTag
+	case *types.Func:
+		return funcTag
+	default:
+		errorf("unexpected object: %v (%T)", obj, obj) // panics
+		panic("unreachable")
+	}
+}
+
+func sameObj(a, b types.Object) bool {
+	// Because unnamed types are not canonicalized, we cannot simply compare types for
+	// (pointer) identity.
+	// Ideally we'd check equality of constant values as well, but this is good enough.
+	return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type())
+}
+
+func (p *importer) declare(obj types.Object) {
+	pkg := obj.Pkg()
+	if alt := pkg.Scope().Insert(obj); alt != nil {
+		// This can only trigger if we import a (non-type) object a second time.
+		// Excluding type aliases, this cannot happen because 1) we only import a package
+		// once; and b) we ignore compiler-specific export data which may contain
+		// functions whose inlined function bodies refer to other functions that
+		// were already imported.
+		// However, type aliases require reexporting the original type, so we need
+		// to allow it (see also the comment in cmd/compile/internal/gc/bimport.go,
+		// method importer.obj, switch case importing functions).
+		// TODO(gri) review/update this comment once the gc compiler handles type aliases.
+		if !sameObj(obj, alt) {
+			errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt)
+		}
+	}
+}
+
+func (p *importer) obj(tag int) {
+	switch tag {
+	case constTag:
+		pos := p.pos()
+		pkg, name := p.qualifiedName()
+		typ := p.typ(nil, nil)
+		val := p.value()
+		p.declare(types.NewConst(pos, pkg, name, typ, val))
+
+	case aliasTag:
+		// TODO(gri) verify type alias hookup is correct
+		pos := p.pos()
+		pkg, name := p.qualifiedName()
+		typ := p.typ(nil, nil)
+		p.declare(types.NewTypeName(pos, pkg, name, typ))
+
+	case typeTag:
+		p.typ(nil, nil)
+
+	case varTag:
+		pos := p.pos()
+		pkg, name := p.qualifiedName()
+		typ := p.typ(nil, nil)
+		p.declare(types.NewVar(pos, pkg, name, typ))
+
+	case funcTag:
+		pos := p.pos()
+		pkg, name := p.qualifiedName()
+		params, isddd := p.paramList()
+		result, _ := p.paramList()
+		sig := types.NewSignature(nil, params, result, isddd)
+		p.declare(types.NewFunc(pos, pkg, name, sig))
+
+	default:
+		errorf("unexpected object tag %d", tag)
+	}
+}
+
+const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go
+
+func (p *importer) pos() token.Pos {
+	if !p.posInfoFormat {
+		return token.NoPos
+	}
+
+	file := p.prevFile
+	line := p.prevLine
+	delta := p.int()
+	line += delta
+	if p.version >= 5 {
+		if delta == deltaNewFile {
+			if n := p.int(); n >= 0 {
+				// file changed
+				file = p.path()
+				line = n
+			}
+		}
+	} else {
+		if delta == 0 {
+			if n := p.int(); n >= 0 {
+				// file changed
+				file = p.prevFile[:n] + p.string()
+				line = p.int()
+			}
+		}
+	}
+	p.prevFile = file
+	p.prevLine = line
+
+	return p.fake.pos(file, line)
+}
+
+// Synthesize a token.Pos
+type fakeFileSet struct {
+	fset  *token.FileSet
+	files map[string]*token.File
+}
+
+func (s *fakeFileSet) pos(file string, line int) token.Pos {
+	// Since we don't know the set of needed file positions, we
+	// reserve maxlines positions per file.
+	const maxlines = 64 * 1024
+	f := s.files[file]
+	if f == nil {
+		f = s.fset.AddFile(file, -1, maxlines)
+		s.files[file] = f
+		// Allocate the fake linebreak indices on first use.
+		// TODO(adonovan): opt: save ~512KB using a more complex scheme?
+		fakeLinesOnce.Do(func() {
+			fakeLines = make([]int, maxlines)
+			for i := range fakeLines {
+				fakeLines[i] = i
+			}
+		})
+		f.SetLines(fakeLines)
+	}
+
+	if line > maxlines {
+		line = 1
+	}
+
+	// Treat the file as if it contained only newlines
+	// and column=1: use the line number as the offset.
+	return f.Pos(line - 1)
+}
+
+var (
+	fakeLines     []int
+	fakeLinesOnce sync.Once
+)
+
+func (p *importer) qualifiedName() (pkg *types.Package, name string) {
+	name = p.string()
+	pkg = p.pkg()
+	return
+}
+
+func (p *importer) record(t types.Type) {
+	p.typList = append(p.typList, t)
+}
+
+// A dddSlice is a types.Type representing ...T parameters.
+// It only appears for parameter types and does not escape
+// the importer.
+type dddSlice struct {
+	elem types.Type
+}
+
+func (t *dddSlice) Underlying() types.Type { return t }
+func (t *dddSlice) String() string         { return "..." + t.elem.String() }
+
+// parent is the package which declared the type; parent == nil means
+// the package currently imported. The parent package is needed for
+// exported struct fields and interface methods which don't contain
+// explicit package information in the export data.
+//
+// A non-nil tname is used as the "owner" of the result type; i.e.,
+// the result type is the underlying type of tname. tname is used
+// to give interface methods a named receiver type where possible.
+func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type {
+	// if the type was seen before, i is its index (>= 0)
+	i := p.tagOrIndex()
+	if i >= 0 {
+		return p.typList[i]
+	}
+
+	// otherwise, i is the type tag (< 0)
+	switch i {
+	case namedTag:
+		// read type object
+		pos := p.pos()
+		parent, name := p.qualifiedName()
+		scope := parent.Scope()
+		obj := scope.Lookup(name)
+
+		// if the object doesn't exist yet, create and insert it
+		if obj == nil {
+			obj = types.NewTypeName(pos, parent, name, nil)
+			scope.Insert(obj)
+		}
+
+		if _, ok := obj.(*types.TypeName); !ok {
+			errorf("pkg = %s, name = %s => %s", parent, name, obj)
+		}
+
+		// associate new named type with obj if it doesn't exist yet
+		t0 := types.NewNamed(obj.(*types.TypeName), nil, nil)
+
+		// but record the existing type, if any
+		tname := obj.Type().(*types.Named) // tname is either t0 or the existing type
+		p.record(tname)
+
+		// read underlying type
+		t0.SetUnderlying(p.typ(parent, t0))
+
+		// interfaces don't have associated methods
+		if types.IsInterface(t0) {
+			return tname
+		}
+
+		// read associated methods
+		for i := p.int(); i > 0; i-- {
+			// TODO(gri) replace this with something closer to fieldName
+			pos := p.pos()
+			name := p.string()
+			if !exported(name) {
+				p.pkg()
+			}
+
+			recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver?
+			params, isddd := p.paramList()
+			result, _ := p.paramList()
+			p.int() // go:nointerface pragma - discarded
+
+			sig := types.NewSignature(recv.At(0), params, result, isddd)
+			t0.AddMethod(types.NewFunc(pos, parent, name, sig))
+		}
+
+		return tname
+
+	case arrayTag:
+		t := new(types.Array)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		n := p.int64()
+		*t = *types.NewArray(p.typ(parent, nil), n)
+		return t
+
+	case sliceTag:
+		t := new(types.Slice)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		*t = *types.NewSlice(p.typ(parent, nil))
+		return t
+
+	case dddTag:
+		t := new(dddSlice)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		t.elem = p.typ(parent, nil)
+		return t
+
+	case structTag:
+		t := new(types.Struct)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		*t = *types.NewStruct(p.fieldList(parent))
+		return t
+
+	case pointerTag:
+		t := new(types.Pointer)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		*t = *types.NewPointer(p.typ(parent, nil))
+		return t
+
+	case signatureTag:
+		t := new(types.Signature)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		params, isddd := p.paramList()
+		result, _ := p.paramList()
+		*t = *types.NewSignature(nil, params, result, isddd)
+		return t
+
+	case interfaceTag:
+		// Create a dummy entry in the type list. This is safe because we
+		// cannot expect the interface type to appear in a cycle, as any
+		// such cycle must contain a named type which would have been
+		// first defined earlier.
+		// TODO(gri) Is this still true now that we have type aliases?
+		// See issue #23225.
+		n := len(p.typList)
+		if p.trackAllTypes {
+			p.record(nil)
+		}
+
+		var embeddeds []types.Type
+		for n := p.int(); n > 0; n-- {
+			p.pos()
+			embeddeds = append(embeddeds, p.typ(parent, nil))
+		}
+
+		t := newInterface(p.methodList(parent, tname), embeddeds)
+		p.interfaceList = append(p.interfaceList, t)
+		if p.trackAllTypes {
+			p.typList[n] = t
+		}
+		return t
+
+	case mapTag:
+		t := new(types.Map)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		key := p.typ(parent, nil)
+		val := p.typ(parent, nil)
+		*t = *types.NewMap(key, val)
+		return t
+
+	case chanTag:
+		t := new(types.Chan)
+		if p.trackAllTypes {
+			p.record(t)
+		}
+
+		dir := chanDir(p.int())
+		val := p.typ(parent, nil)
+		*t = *types.NewChan(dir, val)
+		return t
+
+	default:
+		errorf("unexpected type tag %d", i) // panics
+		panic("unreachable")
+	}
+}
+
+func chanDir(d int) types.ChanDir {
+	// tag values must match the constants in cmd/compile/internal/gc/go.go
+	switch d {
+	case 1 /* Crecv */ :
+		return types.RecvOnly
+	case 2 /* Csend */ :
+		return types.SendOnly
+	case 3 /* Cboth */ :
+		return types.SendRecv
+	default:
+		errorf("unexpected channel dir %d", d)
+		return 0
+	}
+}
+
+func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) {
+	if n := p.int(); n > 0 {
+		fields = make([]*types.Var, n)
+		tags = make([]string, n)
+		for i := range fields {
+			fields[i], tags[i] = p.field(parent)
+		}
+	}
+	return
+}
+
+func (p *importer) field(parent *types.Package) (*types.Var, string) {
+	pos := p.pos()
+	pkg, name, alias := p.fieldName(parent)
+	typ := p.typ(parent, nil)
+	tag := p.string()
+
+	anonymous := false
+	if name == "" {
+		// anonymous field - typ must be T or *T and T must be a type name
+		switch typ := deref(typ).(type) {
+		case *types.Basic: // basic types are named types
+			pkg = nil // // objects defined in Universe scope have no package
+			name = typ.Name()
+		case *types.Named:
+			name = typ.Obj().Name()
+		default:
+			errorf("named base type expected")
+		}
+		anonymous = true
+	} else if alias {
+		// anonymous field: we have an explicit name because it's an alias
+		anonymous = true
+	}
+
+	return types.NewField(pos, pkg, name, typ, anonymous), tag
+}
+
+func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) {
+	if n := p.int(); n > 0 {
+		methods = make([]*types.Func, n)
+		for i := range methods {
+			methods[i] = p.method(parent, baseType)
+		}
+	}
+	return
+}
+
+func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func {
+	pos := p.pos()
+	pkg, name, _ := p.fieldName(parent)
+	// If we don't have a baseType, use a nil receiver.
+	// A receiver using the actual interface type (which
+	// we don't know yet) will be filled in when we call
+	// types.Interface.Complete.
+	var recv *types.Var
+	if baseType != nil {
+		recv = types.NewVar(token.NoPos, parent, "", baseType)
+	}
+	params, isddd := p.paramList()
+	result, _ := p.paramList()
+	sig := types.NewSignature(recv, params, result, isddd)
+	return types.NewFunc(pos, pkg, name, sig)
+}
+
+func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) {
+	name = p.string()
+	pkg = parent
+	if pkg == nil {
+		// use the imported package instead
+		pkg = p.pkgList[0]
+	}
+	if p.version == 0 && name == "_" {
+		// version 0 didn't export a package for _ fields
+		return
+	}
+	switch name {
+	case "":
+		// 1) field name matches base type name and is exported: nothing to do
+	case "?":
+		// 2) field name matches base type name and is not exported: need package
+		name = ""
+		pkg = p.pkg()
+	case "@":
+		// 3) field name doesn't match type name (alias)
+		name = p.string()
+		alias = true
+		fallthrough
+	default:
+		if !exported(name) {
+			pkg = p.pkg()
+		}
+	}
+	return
+}
+
+func (p *importer) paramList() (*types.Tuple, bool) {
+	n := p.int()
+	if n == 0 {
+		return nil, false
+	}
+	// negative length indicates unnamed parameters
+	named := true
+	if n < 0 {
+		n = -n
+		named = false
+	}
+	// n > 0
+	params := make([]*types.Var, n)
+	isddd := false
+	for i := range params {
+		params[i], isddd = p.param(named)
+	}
+	return types.NewTuple(params...), isddd
+}
+
+func (p *importer) param(named bool) (*types.Var, bool) {
+	t := p.typ(nil, nil)
+	td, isddd := t.(*dddSlice)
+	if isddd {
+		t = types.NewSlice(td.elem)
+	}
+
+	var pkg *types.Package
+	var name string
+	if named {
+		name = p.string()
+		if name == "" {
+			errorf("expected named parameter")
+		}
+		if name != "_" {
+			pkg = p.pkg()
+		}
+		if i := strings.Index(name, "Β·"); i > 0 {
+			name = name[:i] // cut off gc-specific parameter numbering
+		}
+	}
+
+	// read and discard compiler-specific info
+	p.string()
+
+	return types.NewVar(token.NoPos, pkg, name, t), isddd
+}
+
+func exported(name string) bool {
+	ch, _ := utf8.DecodeRuneInString(name)
+	return unicode.IsUpper(ch)
+}
+
+func (p *importer) value() constant.Value {
+	switch tag := p.tagOrIndex(); tag {
+	case falseTag:
+		return constant.MakeBool(false)
+	case trueTag:
+		return constant.MakeBool(true)
+	case int64Tag:
+		return constant.MakeInt64(p.int64())
+	case floatTag:
+		return p.float()
+	case complexTag:
+		re := p.float()
+		im := p.float()
+		return constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+	case stringTag:
+		return constant.MakeString(p.string())
+	case unknownTag:
+		return constant.MakeUnknown()
+	default:
+		errorf("unexpected value tag %d", tag) // panics
+		panic("unreachable")
+	}
+}
+
+func (p *importer) float() constant.Value {
+	sign := p.int()
+	if sign == 0 {
+		return constant.MakeInt64(0)
+	}
+
+	exp := p.int()
+	mant := []byte(p.string()) // big endian
+
+	// remove leading 0's if any
+	for len(mant) > 0 && mant[0] == 0 {
+		mant = mant[1:]
+	}
+
+	// convert to little endian
+	// TODO(gri) go/constant should have a more direct conversion function
+	//           (e.g., once it supports a big.Float based implementation)
+	for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 {
+		mant[i], mant[j] = mant[j], mant[i]
+	}
+
+	// adjust exponent (constant.MakeFromBytes creates an integer value,
+	// but mant represents the mantissa bits such that 0.5 <= mant < 1.0)
+	exp -= len(mant) << 3
+	if len(mant) > 0 {
+		for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 {
+			exp++
+		}
+	}
+
+	x := constant.MakeFromBytes(mant)
+	switch {
+	case exp < 0:
+		d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
+		x = constant.BinaryOp(x, token.QUO, d)
+	case exp > 0:
+		x = constant.Shift(x, token.SHL, uint(exp))
+	}
+
+	if sign < 0 {
+		x = constant.UnaryOp(token.SUB, x, 0)
+	}
+	return x
+}
+
+// ----------------------------------------------------------------------------
+// Low-level decoders
+
+func (p *importer) tagOrIndex() int {
+	if p.debugFormat {
+		p.marker('t')
+	}
+
+	return int(p.rawInt64())
+}
+
+func (p *importer) int() int {
+	x := p.int64()
+	if int64(int(x)) != x {
+		errorf("exported integer too large")
+	}
+	return int(x)
+}
+
+func (p *importer) int64() int64 {
+	if p.debugFormat {
+		p.marker('i')
+	}
+
+	return p.rawInt64()
+}
+
+func (p *importer) path() string {
+	if p.debugFormat {
+		p.marker('p')
+	}
+	// if the path was seen before, i is its index (>= 0)
+	// (the empty string is at index 0)
+	i := p.rawInt64()
+	if i >= 0 {
+		return p.pathList[i]
+	}
+	// otherwise, i is the negative path length (< 0)
+	a := make([]string, -i)
+	for n := range a {
+		a[n] = p.string()
+	}
+	s := strings.Join(a, "/")
+	p.pathList = append(p.pathList, s)
+	return s
+}
+
+func (p *importer) string() string {
+	if p.debugFormat {
+		p.marker('s')
+	}
+	// if the string was seen before, i is its index (>= 0)
+	// (the empty string is at index 0)
+	i := p.rawInt64()
+	if i >= 0 {
+		return p.strList[i]
+	}
+	// otherwise, i is the negative string length (< 0)
+	if n := int(-i); n <= cap(p.buf) {
+		p.buf = p.buf[:n]
+	} else {
+		p.buf = make([]byte, n)
+	}
+	for i := range p.buf {
+		p.buf[i] = p.rawByte()
+	}
+	s := string(p.buf)
+	p.strList = append(p.strList, s)
+	return s
+}
+
+func (p *importer) marker(want byte) {
+	if got := p.rawByte(); got != want {
+		errorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read)
+	}
+
+	pos := p.read
+	if n := int(p.rawInt64()); n != pos {
+		errorf("incorrect position: got %d; want %d", n, pos)
+	}
+}
+
+// rawInt64 should only be used by low-level decoders.
+func (p *importer) rawInt64() int64 {
+	i, err := binary.ReadVarint(p)
+	if err != nil {
+		errorf("read error: %v", err)
+	}
+	return i
+}
+
+// rawStringln should only be used to read the initial version string.
+func (p *importer) rawStringln(b byte) string {
+	p.buf = p.buf[:0]
+	for b != '\n' {
+		p.buf = append(p.buf, b)
+		b = p.rawByte()
+	}
+	return string(p.buf)
+}
+
+// needed for binary.ReadVarint in rawInt64
+func (p *importer) ReadByte() (byte, error) {
+	return p.rawByte(), nil
+}
+
+// byte is the bottleneck interface for reading p.data.
+// It unescapes '|' 'S' to '$' and '|' '|' to '|'.
+// rawByte should only be used by low-level decoders.
+func (p *importer) rawByte() byte {
+	b := p.data[0]
+	r := 1
+	if b == '|' {
+		b = p.data[1]
+		r = 2
+		switch b {
+		case 'S':
+			b = '$'
+		case '|':
+			// nothing to do
+		default:
+			errorf("unexpected escape sequence in export data")
+		}
+	}
+	p.data = p.data[r:]
+	p.read += r
+	return b
+
+}
+
+// ----------------------------------------------------------------------------
+// Export format
+
+// Tags. Must be < 0.
+const (
+	// Objects
+	packageTag = -(iota + 1)
+	constTag
+	typeTag
+	varTag
+	funcTag
+	endTag
+
+	// Types
+	namedTag
+	arrayTag
+	sliceTag
+	dddTag
+	structTag
+	pointerTag
+	signatureTag
+	interfaceTag
+	mapTag
+	chanTag
+
+	// Values
+	falseTag
+	trueTag
+	int64Tag
+	floatTag
+	fractionTag // not used by gc
+	complexTag
+	stringTag
+	nilTag     // only used by gc (appears in exported inlined function bodies)
+	unknownTag // not used by gc (only appears in packages with errors)
+
+	// Type aliases
+	aliasTag
+)
+
+var predeclared = []types.Type{
+	// basic types
+	types.Typ[types.Bool],
+	types.Typ[types.Int],
+	types.Typ[types.Int8],
+	types.Typ[types.Int16],
+	types.Typ[types.Int32],
+	types.Typ[types.Int64],
+	types.Typ[types.Uint],
+	types.Typ[types.Uint8],
+	types.Typ[types.Uint16],
+	types.Typ[types.Uint32],
+	types.Typ[types.Uint64],
+	types.Typ[types.Uintptr],
+	types.Typ[types.Float32],
+	types.Typ[types.Float64],
+	types.Typ[types.Complex64],
+	types.Typ[types.Complex128],
+	types.Typ[types.String],
+
+	// basic type aliases
+	types.Universe.Lookup("byte").Type(),
+	types.Universe.Lookup("rune").Type(),
+
+	// error
+	types.Universe.Lookup("error").Type(),
+
+	// untyped types
+	types.Typ[types.UntypedBool],
+	types.Typ[types.UntypedInt],
+	types.Typ[types.UntypedRune],
+	types.Typ[types.UntypedFloat],
+	types.Typ[types.UntypedComplex],
+	types.Typ[types.UntypedString],
+	types.Typ[types.UntypedNil],
+
+	// package unsafe
+	types.Typ[types.UnsafePointer],
+
+	// invalid type
+	types.Typ[types.Invalid], // only appears in packages with errors
+
+	// used internally by gc; never used by this package or in .a files
+	anyType{},
+}
+
+type anyType struct{}
+
+func (t anyType) Underlying() types.Type { return t }
+func (t anyType) String() string         { return "any" }

vendor/golang.org/x/tools/go/internal/gcimporter/exportdata.go πŸ”—

@@ -0,0 +1,93 @@
+// 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.
+
+// This file is a copy of $GOROOT/src/go/internal/gcimporter/exportdata.go.
+
+// This file implements FindExportData.
+
+package gcimporter
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"strconv"
+	"strings"
+)
+
+func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
+	// See $GOROOT/include/ar.h.
+	hdr := make([]byte, 16+12+6+6+8+10+2)
+	_, err = io.ReadFull(r, hdr)
+	if err != nil {
+		return
+	}
+	// leave for debugging
+	if false {
+		fmt.Printf("header: %s", hdr)
+	}
+	s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
+	size, err = strconv.Atoi(s)
+	if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
+		err = fmt.Errorf("invalid archive header")
+		return
+	}
+	name = strings.TrimSpace(string(hdr[:16]))
+	return
+}
+
+// FindExportData positions the reader r at the beginning of the
+// export data section of an underlying GC-created object/archive
+// file by reading from it. The reader must be positioned at the
+// start of the file before calling this function. The hdr result
+// is the string before the export data, either "$$" or "$$B".
+//
+func FindExportData(r *bufio.Reader) (hdr string, err error) {
+	// Read first line to make sure this is an object file.
+	line, err := r.ReadSlice('\n')
+	if err != nil {
+		err = fmt.Errorf("can't find export data (%v)", err)
+		return
+	}
+
+	if string(line) == "!<arch>\n" {
+		// Archive file. Scan to __.PKGDEF.
+		var name string
+		if name, _, err = readGopackHeader(r); err != nil {
+			return
+		}
+
+		// First entry should be __.PKGDEF.
+		if name != "__.PKGDEF" {
+			err = fmt.Errorf("go archive is missing __.PKGDEF")
+			return
+		}
+
+		// Read first line of __.PKGDEF data, so that line
+		// is once again the first line of the input.
+		if line, err = r.ReadSlice('\n'); err != nil {
+			err = fmt.Errorf("can't find export data (%v)", err)
+			return
+		}
+	}
+
+	// Now at __.PKGDEF in archive or still at beginning of file.
+	// Either way, line should begin with "go object ".
+	if !strings.HasPrefix(string(line), "go object ") {
+		err = fmt.Errorf("not a Go object file")
+		return
+	}
+
+	// Skip over object header to export data.
+	// Begins after first line starting with $$.
+	for line[0] != '$' {
+		if line, err = r.ReadSlice('\n'); err != nil {
+			err = fmt.Errorf("can't find export data (%v)", err)
+			return
+		}
+	}
+	hdr = string(line)
+
+	return
+}

vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go πŸ”—

@@ -0,0 +1,1051 @@
+// 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.
+
+// This file is a modified copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go,
+// but it also contains the original source-based importer code for Go1.6.
+// Once we stop supporting 1.6, we can remove that code.
+
+// Package gcimporter provides various functions for reading
+// gc-generated object files that can be used to implement the
+// Importer interface defined by the Go 1.5 standard library package.
+package gcimporter // import "golang.org/x/tools/go/internal/gcimporter"
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"go/build"
+	"go/constant"
+	"go/token"
+	"go/types"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strconv"
+	"strings"
+	"text/scanner"
+)
+
+// debugging/development support
+const debug = false
+
+var pkgExts = [...]string{".a", ".o"}
+
+// FindPkg returns the filename and unique package id for an import
+// path based on package information provided by build.Import (using
+// the build.Default build.Context). A relative srcDir is interpreted
+// relative to the current working directory.
+// If no file was found, an empty filename is returned.
+//
+func FindPkg(path, srcDir string) (filename, id string) {
+	if path == "" {
+		return
+	}
+
+	var noext string
+	switch {
+	default:
+		// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
+		// Don't require the source files to be present.
+		if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
+			srcDir = abs
+		}
+		bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
+		if bp.PkgObj == "" {
+			id = path // make sure we have an id to print in error message
+			return
+		}
+		noext = strings.TrimSuffix(bp.PkgObj, ".a")
+		id = bp.ImportPath
+
+	case build.IsLocalImport(path):
+		// "./x" -> "/this/directory/x.ext", "/this/directory/x"
+		noext = filepath.Join(srcDir, path)
+		id = noext
+
+	case filepath.IsAbs(path):
+		// for completeness only - go/build.Import
+		// does not support absolute imports
+		// "/x" -> "/x.ext", "/x"
+		noext = path
+		id = path
+	}
+
+	if false { // for debugging
+		if path != id {
+			fmt.Printf("%s -> %s\n", path, id)
+		}
+	}
+
+	// try extensions
+	for _, ext := range pkgExts {
+		filename = noext + ext
+		if f, err := os.Stat(filename); err == nil && !f.IsDir() {
+			return
+		}
+	}
+
+	filename = "" // not found
+	return
+}
+
+// ImportData imports a package by reading the gc-generated export data,
+// adds the corresponding package object to the packages map indexed by id,
+// and returns the object.
+//
+// The packages map must contains all packages already imported. The data
+// reader position must be the beginning of the export data section. The
+// filename is only used in error messages.
+//
+// If packages[id] contains the completely imported package, that package
+// can be used directly, and there is no need to call this function (but
+// there is also no harm but for extra time used).
+//
+func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) {
+	// support for parser error handling
+	defer func() {
+		switch r := recover().(type) {
+		case nil:
+			// nothing to do
+		case importError:
+			err = r
+		default:
+			panic(r) // internal error
+		}
+	}()
+
+	var p parser
+	p.init(filename, id, data, packages)
+	pkg = p.parseExport()
+
+	return
+}
+
+// Import imports a gc-generated package given its import path and srcDir, adds
+// the corresponding package object to the packages map, and returns the object.
+// The packages map must contain all packages already imported.
+//
+func Import(packages map[string]*types.Package, path, srcDir string) (pkg *types.Package, err error) {
+	filename, id := FindPkg(path, srcDir)
+	if filename == "" {
+		if path == "unsafe" {
+			return types.Unsafe, nil
+		}
+		err = fmt.Errorf("can't find import: %q", id)
+		return
+	}
+
+	// no need to re-import if the package was imported completely before
+	if pkg = packages[id]; pkg != nil && pkg.Complete() {
+		return
+	}
+
+	// open file
+	f, err := os.Open(filename)
+	if err != nil {
+		return
+	}
+	defer func() {
+		f.Close()
+		if err != nil {
+			// add file name to error
+			err = fmt.Errorf("reading export data: %s: %v", filename, err)
+		}
+	}()
+
+	var hdr string
+	buf := bufio.NewReader(f)
+	if hdr, err = FindExportData(buf); err != nil {
+		return
+	}
+
+	switch hdr {
+	case "$$\n":
+		return ImportData(packages, filename, id, buf)
+
+	case "$$B\n":
+		var data []byte
+		data, err = ioutil.ReadAll(buf)
+		if err != nil {
+			break
+		}
+
+		// TODO(gri): allow clients of go/importer to provide a FileSet.
+		// Or, define a new standard go/types/gcexportdata package.
+		fset := token.NewFileSet()
+
+		// The indexed export format starts with an 'i'; the older
+		// binary export format starts with a 'c', 'd', or 'v'
+		// (from "version"). Select appropriate importer.
+		if len(data) > 0 && data[0] == 'i' {
+			_, pkg, err = IImportData(fset, packages, data[1:], id)
+		} else {
+			_, pkg, err = BImportData(fset, packages, data, id)
+		}
+
+	default:
+		err = fmt.Errorf("unknown export data header: %q", hdr)
+	}
+
+	return
+}
+
+// ----------------------------------------------------------------------------
+// Parser
+
+// TODO(gri) Imported objects don't have position information.
+//           Ideally use the debug table line info; alternatively
+//           create some fake position (or the position of the
+//           import). That way error messages referring to imported
+//           objects can print meaningful information.
+
+// parser parses the exports inside a gc compiler-produced
+// object/archive file and populates its scope with the results.
+type parser struct {
+	scanner    scanner.Scanner
+	tok        rune                      // current token
+	lit        string                    // literal string; only valid for Ident, Int, String tokens
+	id         string                    // package id of imported package
+	sharedPkgs map[string]*types.Package // package id -> package object (across importer)
+	localPkgs  map[string]*types.Package // package id -> package object (just this package)
+}
+
+func (p *parser) init(filename, id string, src io.Reader, packages map[string]*types.Package) {
+	p.scanner.Init(src)
+	p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
+	p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
+	p.scanner.Whitespace = 1<<'\t' | 1<<' '
+	p.scanner.Filename = filename // for good error messages
+	p.next()
+	p.id = id
+	p.sharedPkgs = packages
+	if debug {
+		// check consistency of packages map
+		for _, pkg := range packages {
+			if pkg.Name() == "" {
+				fmt.Printf("no package name for %s\n", pkg.Path())
+			}
+		}
+	}
+}
+
+func (p *parser) next() {
+	p.tok = p.scanner.Scan()
+	switch p.tok {
+	case scanner.Ident, scanner.Int, scanner.Char, scanner.String, 'Β·':
+		p.lit = p.scanner.TokenText()
+	default:
+		p.lit = ""
+	}
+	if debug {
+		fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
+	}
+}
+
+func declTypeName(pkg *types.Package, name string) *types.TypeName {
+	scope := pkg.Scope()
+	if obj := scope.Lookup(name); obj != nil {
+		return obj.(*types.TypeName)
+	}
+	obj := types.NewTypeName(token.NoPos, pkg, name, nil)
+	// a named type may be referred to before the underlying type
+	// is known - set it up
+	types.NewNamed(obj, nil, nil)
+	scope.Insert(obj)
+	return obj
+}
+
+// ----------------------------------------------------------------------------
+// Error handling
+
+// Internal errors are boxed as importErrors.
+type importError struct {
+	pos scanner.Position
+	err error
+}
+
+func (e importError) Error() string {
+	return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
+}
+
+func (p *parser) error(err interface{}) {
+	if s, ok := err.(string); ok {
+		err = errors.New(s)
+	}
+	// panic with a runtime.Error if err is not an error
+	panic(importError{p.scanner.Pos(), err.(error)})
+}
+
+func (p *parser) errorf(format string, args ...interface{}) {
+	p.error(fmt.Sprintf(format, args...))
+}
+
+func (p *parser) expect(tok rune) string {
+	lit := p.lit
+	if p.tok != tok {
+		p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
+	}
+	p.next()
+	return lit
+}
+
+func (p *parser) expectSpecial(tok string) {
+	sep := 'x' // not white space
+	i := 0
+	for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' {
+		sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+		p.next()
+		i++
+	}
+	if i < len(tok) {
+		p.errorf("expected %q, got %q", tok, tok[0:i])
+	}
+}
+
+func (p *parser) expectKeyword(keyword string) {
+	lit := p.expect(scanner.Ident)
+	if lit != keyword {
+		p.errorf("expected keyword %s, got %q", keyword, lit)
+	}
+}
+
+// ----------------------------------------------------------------------------
+// Qualified and unqualified names
+
+// PackageId = string_lit .
+//
+func (p *parser) parsePackageId() string {
+	id, err := strconv.Unquote(p.expect(scanner.String))
+	if err != nil {
+		p.error(err)
+	}
+	// id == "" stands for the imported package id
+	// (only known at time of package installation)
+	if id == "" {
+		id = p.id
+	}
+	return id
+}
+
+// PackageName = ident .
+//
+func (p *parser) parsePackageName() string {
+	return p.expect(scanner.Ident)
+}
+
+// dotIdentifier = ( ident | 'Β·' ) { ident | int | 'Β·' } .
+func (p *parser) parseDotIdent() string {
+	ident := ""
+	if p.tok != scanner.Int {
+		sep := 'x' // not white space
+		for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == 'Β·') && sep > ' ' {
+			ident += p.lit
+			sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+			p.next()
+		}
+	}
+	if ident == "" {
+		p.expect(scanner.Ident) // use expect() for error handling
+	}
+	return ident
+}
+
+// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) .
+//
+func (p *parser) parseQualifiedName() (id, name string) {
+	p.expect('@')
+	id = p.parsePackageId()
+	p.expect('.')
+	// Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields.
+	if p.tok == '?' {
+		p.next()
+	} else {
+		name = p.parseDotIdent()
+	}
+	return
+}
+
+// getPkg returns the package for a given id. If the package is
+// not found, create the package and add it to the p.localPkgs
+// and p.sharedPkgs maps. name is the (expected) name of the
+// package. If name == "", the package name is expected to be
+// set later via an import clause in the export data.
+//
+// id identifies a package, usually by a canonical package path like
+// "encoding/json" but possibly by a non-canonical import path like
+// "./json".
+//
+func (p *parser) getPkg(id, name string) *types.Package {
+	// package unsafe is not in the packages maps - handle explicitly
+	if id == "unsafe" {
+		return types.Unsafe
+	}
+
+	pkg := p.localPkgs[id]
+	if pkg == nil {
+		// first import of id from this package
+		pkg = p.sharedPkgs[id]
+		if pkg == nil {
+			// first import of id by this importer;
+			// add (possibly unnamed) pkg to shared packages
+			pkg = types.NewPackage(id, name)
+			p.sharedPkgs[id] = pkg
+		}
+		// add (possibly unnamed) pkg to local packages
+		if p.localPkgs == nil {
+			p.localPkgs = make(map[string]*types.Package)
+		}
+		p.localPkgs[id] = pkg
+	} else if name != "" {
+		// package exists already and we have an expected package name;
+		// make sure names match or set package name if necessary
+		if pname := pkg.Name(); pname == "" {
+			pkg.SetName(name)
+		} else if pname != name {
+			p.errorf("%s package name mismatch: %s (given) vs %s (expected)", id, pname, name)
+		}
+	}
+	return pkg
+}
+
+// parseExportedName is like parseQualifiedName, but
+// the package id is resolved to an imported *types.Package.
+//
+func (p *parser) parseExportedName() (pkg *types.Package, name string) {
+	id, name := p.parseQualifiedName()
+	pkg = p.getPkg(id, "")
+	return
+}
+
+// ----------------------------------------------------------------------------
+// Types
+
+// BasicType = identifier .
+//
+func (p *parser) parseBasicType() types.Type {
+	id := p.expect(scanner.Ident)
+	obj := types.Universe.Lookup(id)
+	if obj, ok := obj.(*types.TypeName); ok {
+		return obj.Type()
+	}
+	p.errorf("not a basic type: %s", id)
+	return nil
+}
+
+// ArrayType = "[" int_lit "]" Type .
+//
+func (p *parser) parseArrayType(parent *types.Package) types.Type {
+	// "[" already consumed and lookahead known not to be "]"
+	lit := p.expect(scanner.Int)
+	p.expect(']')
+	elem := p.parseType(parent)
+	n, err := strconv.ParseInt(lit, 10, 64)
+	if err != nil {
+		p.error(err)
+	}
+	return types.NewArray(elem, n)
+}
+
+// MapType = "map" "[" Type "]" Type .
+//
+func (p *parser) parseMapType(parent *types.Package) types.Type {
+	p.expectKeyword("map")
+	p.expect('[')
+	key := p.parseType(parent)
+	p.expect(']')
+	elem := p.parseType(parent)
+	return types.NewMap(key, elem)
+}
+
+// Name = identifier | "?" | QualifiedName .
+//
+// For unqualified and anonymous names, the returned package is the parent
+// package unless parent == nil, in which case the returned package is the
+// package being imported. (The parent package is not nil if the the name
+// is an unqualified struct field or interface method name belonging to a
+// type declared in another package.)
+//
+// For qualified names, the returned package is nil (and not created if
+// it doesn't exist yet) unless materializePkg is set (which creates an
+// unnamed package with valid package path). In the latter case, a
+// subsequent import clause is expected to provide a name for the package.
+//
+func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) {
+	pkg = parent
+	if pkg == nil {
+		pkg = p.sharedPkgs[p.id]
+	}
+	switch p.tok {
+	case scanner.Ident:
+		name = p.lit
+		p.next()
+	case '?':
+		// anonymous
+		p.next()
+	case '@':
+		// exported name prefixed with package path
+		pkg = nil
+		var id string
+		id, name = p.parseQualifiedName()
+		if materializePkg {
+			pkg = p.getPkg(id, "")
+		}
+	default:
+		p.error("name expected")
+	}
+	return
+}
+
+func deref(typ types.Type) types.Type {
+	if p, _ := typ.(*types.Pointer); p != nil {
+		return p.Elem()
+	}
+	return typ
+}
+
+// Field = Name Type [ string_lit ] .
+//
+func (p *parser) parseField(parent *types.Package) (*types.Var, string) {
+	pkg, name := p.parseName(parent, true)
+
+	if name == "_" {
+		// Blank fields should be package-qualified because they
+		// are unexported identifiers, but gc does not qualify them.
+		// Assuming that the ident belongs to the current package
+		// causes types to change during re-exporting, leading
+		// to spurious "can't assign A to B" errors from go/types.
+		// As a workaround, pretend all blank fields belong
+		// to the same unique dummy package.
+		const blankpkg = "<_>"
+		pkg = p.getPkg(blankpkg, blankpkg)
+	}
+
+	typ := p.parseType(parent)
+	anonymous := false
+	if name == "" {
+		// anonymous field - typ must be T or *T and T must be a type name
+		switch typ := deref(typ).(type) {
+		case *types.Basic: // basic types are named types
+			pkg = nil // objects defined in Universe scope have no package
+			name = typ.Name()
+		case *types.Named:
+			name = typ.Obj().Name()
+		default:
+			p.errorf("anonymous field expected")
+		}
+		anonymous = true
+	}
+	tag := ""
+	if p.tok == scanner.String {
+		s := p.expect(scanner.String)
+		var err error
+		tag, err = strconv.Unquote(s)
+		if err != nil {
+			p.errorf("invalid struct tag %s: %s", s, err)
+		}
+	}
+	return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag
+}
+
+// StructType = "struct" "{" [ FieldList ] "}" .
+// FieldList  = Field { ";" Field } .
+//
+func (p *parser) parseStructType(parent *types.Package) types.Type {
+	var fields []*types.Var
+	var tags []string
+
+	p.expectKeyword("struct")
+	p.expect('{')
+	for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
+		if i > 0 {
+			p.expect(';')
+		}
+		fld, tag := p.parseField(parent)
+		if tag != "" && tags == nil {
+			tags = make([]string, i)
+		}
+		if tags != nil {
+			tags = append(tags, tag)
+		}
+		fields = append(fields, fld)
+	}
+	p.expect('}')
+
+	return types.NewStruct(fields, tags)
+}
+
+// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
+//
+func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
+	_, name := p.parseName(nil, false)
+	// remove gc-specific parameter numbering
+	if i := strings.Index(name, "Β·"); i >= 0 {
+		name = name[:i]
+	}
+	if p.tok == '.' {
+		p.expectSpecial("...")
+		isVariadic = true
+	}
+	typ := p.parseType(nil)
+	if isVariadic {
+		typ = types.NewSlice(typ)
+	}
+	// ignore argument tag (e.g. "noescape")
+	if p.tok == scanner.String {
+		p.next()
+	}
+	// TODO(gri) should we provide a package?
+	par = types.NewVar(token.NoPos, nil, name, typ)
+	return
+}
+
+// Parameters    = "(" [ ParameterList ] ")" .
+// ParameterList = { Parameter "," } Parameter .
+//
+func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) {
+	p.expect('(')
+	for p.tok != ')' && p.tok != scanner.EOF {
+		if len(list) > 0 {
+			p.expect(',')
+		}
+		par, variadic := p.parseParameter()
+		list = append(list, par)
+		if variadic {
+			if isVariadic {
+				p.error("... not on final argument")
+			}
+			isVariadic = true
+		}
+	}
+	p.expect(')')
+
+	return
+}
+
+// Signature = Parameters [ Result ] .
+// Result    = Type | Parameters .
+//
+func (p *parser) parseSignature(recv *types.Var) *types.Signature {
+	params, isVariadic := p.parseParameters()
+
+	// optional result type
+	var results []*types.Var
+	if p.tok == '(' {
+		var variadic bool
+		results, variadic = p.parseParameters()
+		if variadic {
+			p.error("... not permitted on result type")
+		}
+	}
+
+	return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic)
+}
+
+// InterfaceType = "interface" "{" [ MethodList ] "}" .
+// MethodList    = Method { ";" Method } .
+// Method        = Name Signature .
+//
+// The methods of embedded interfaces are always "inlined"
+// by the compiler and thus embedded interfaces are never
+// visible in the export data.
+//
+func (p *parser) parseInterfaceType(parent *types.Package) types.Type {
+	var methods []*types.Func
+
+	p.expectKeyword("interface")
+	p.expect('{')
+	for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
+		if i > 0 {
+			p.expect(';')
+		}
+		pkg, name := p.parseName(parent, true)
+		sig := p.parseSignature(nil)
+		methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
+	}
+	p.expect('}')
+
+	// Complete requires the type's embedded interfaces to be fully defined,
+	// but we do not define any
+	return types.NewInterface(methods, nil).Complete()
+}
+
+// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
+//
+func (p *parser) parseChanType(parent *types.Package) types.Type {
+	dir := types.SendRecv
+	if p.tok == scanner.Ident {
+		p.expectKeyword("chan")
+		if p.tok == '<' {
+			p.expectSpecial("<-")
+			dir = types.SendOnly
+		}
+	} else {
+		p.expectSpecial("<-")
+		p.expectKeyword("chan")
+		dir = types.RecvOnly
+	}
+	elem := p.parseType(parent)
+	return types.NewChan(dir, elem)
+}
+
+// Type =
+//	BasicType | TypeName | ArrayType | SliceType | StructType |
+//      PointerType | FuncType | InterfaceType | MapType | ChanType |
+//      "(" Type ")" .
+//
+// BasicType   = ident .
+// TypeName    = ExportedName .
+// SliceType   = "[" "]" Type .
+// PointerType = "*" Type .
+// FuncType    = "func" Signature .
+//
+func (p *parser) parseType(parent *types.Package) types.Type {
+	switch p.tok {
+	case scanner.Ident:
+		switch p.lit {
+		default:
+			return p.parseBasicType()
+		case "struct":
+			return p.parseStructType(parent)
+		case "func":
+			// FuncType
+			p.next()
+			return p.parseSignature(nil)
+		case "interface":
+			return p.parseInterfaceType(parent)
+		case "map":
+			return p.parseMapType(parent)
+		case "chan":
+			return p.parseChanType(parent)
+		}
+	case '@':
+		// TypeName
+		pkg, name := p.parseExportedName()
+		return declTypeName(pkg, name).Type()
+	case '[':
+		p.next() // look ahead
+		if p.tok == ']' {
+			// SliceType
+			p.next()
+			return types.NewSlice(p.parseType(parent))
+		}
+		return p.parseArrayType(parent)
+	case '*':
+		// PointerType
+		p.next()
+		return types.NewPointer(p.parseType(parent))
+	case '<':
+		return p.parseChanType(parent)
+	case '(':
+		// "(" Type ")"
+		p.next()
+		typ := p.parseType(parent)
+		p.expect(')')
+		return typ
+	}
+	p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+	return nil
+}
+
+// ----------------------------------------------------------------------------
+// Declarations
+
+// ImportDecl = "import" PackageName PackageId .
+//
+func (p *parser) parseImportDecl() {
+	p.expectKeyword("import")
+	name := p.parsePackageName()
+	p.getPkg(p.parsePackageId(), name)
+}
+
+// int_lit = [ "+" | "-" ] { "0" ... "9" } .
+//
+func (p *parser) parseInt() string {
+	s := ""
+	switch p.tok {
+	case '-':
+		s = "-"
+		p.next()
+	case '+':
+		p.next()
+	}
+	return s + p.expect(scanner.Int)
+}
+
+// number = int_lit [ "p" int_lit ] .
+//
+func (p *parser) parseNumber() (typ *types.Basic, val constant.Value) {
+	// mantissa
+	mant := constant.MakeFromLiteral(p.parseInt(), token.INT, 0)
+	if mant == nil {
+		panic("invalid mantissa")
+	}
+
+	if p.lit == "p" {
+		// exponent (base 2)
+		p.next()
+		exp, err := strconv.ParseInt(p.parseInt(), 10, 0)
+		if err != nil {
+			p.error(err)
+		}
+		if exp < 0 {
+			denom := constant.MakeInt64(1)
+			denom = constant.Shift(denom, token.SHL, uint(-exp))
+			typ = types.Typ[types.UntypedFloat]
+			val = constant.BinaryOp(mant, token.QUO, denom)
+			return
+		}
+		if exp > 0 {
+			mant = constant.Shift(mant, token.SHL, uint(exp))
+		}
+		typ = types.Typ[types.UntypedFloat]
+		val = mant
+		return
+	}
+
+	typ = types.Typ[types.UntypedInt]
+	val = mant
+	return
+}
+
+// ConstDecl   = "const" ExportedName [ Type ] "=" Literal .
+// Literal     = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit .
+// bool_lit    = "true" | "false" .
+// complex_lit = "(" float_lit "+" float_lit "i" ")" .
+// rune_lit    = "(" int_lit "+" int_lit ")" .
+// string_lit  = `"` { unicode_char } `"` .
+//
+func (p *parser) parseConstDecl() {
+	p.expectKeyword("const")
+	pkg, name := p.parseExportedName()
+
+	var typ0 types.Type
+	if p.tok != '=' {
+		// constant types are never structured - no need for parent type
+		typ0 = p.parseType(nil)
+	}
+
+	p.expect('=')
+	var typ types.Type
+	var val constant.Value
+	switch p.tok {
+	case scanner.Ident:
+		// bool_lit
+		if p.lit != "true" && p.lit != "false" {
+			p.error("expected true or false")
+		}
+		typ = types.Typ[types.UntypedBool]
+		val = constant.MakeBool(p.lit == "true")
+		p.next()
+
+	case '-', scanner.Int:
+		// int_lit
+		typ, val = p.parseNumber()
+
+	case '(':
+		// complex_lit or rune_lit
+		p.next()
+		if p.tok == scanner.Char {
+			p.next()
+			p.expect('+')
+			typ = types.Typ[types.UntypedRune]
+			_, val = p.parseNumber()
+			p.expect(')')
+			break
+		}
+		_, re := p.parseNumber()
+		p.expect('+')
+		_, im := p.parseNumber()
+		p.expectKeyword("i")
+		p.expect(')')
+		typ = types.Typ[types.UntypedComplex]
+		val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+
+	case scanner.Char:
+		// rune_lit
+		typ = types.Typ[types.UntypedRune]
+		val = constant.MakeFromLiteral(p.lit, token.CHAR, 0)
+		p.next()
+
+	case scanner.String:
+		// string_lit
+		typ = types.Typ[types.UntypedString]
+		val = constant.MakeFromLiteral(p.lit, token.STRING, 0)
+		p.next()
+
+	default:
+		p.errorf("expected literal got %s", scanner.TokenString(p.tok))
+	}
+
+	if typ0 == nil {
+		typ0 = typ
+	}
+
+	pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val))
+}
+
+// TypeDecl = "type" ExportedName Type .
+//
+func (p *parser) parseTypeDecl() {
+	p.expectKeyword("type")
+	pkg, name := p.parseExportedName()
+	obj := declTypeName(pkg, name)
+
+	// The type object may have been imported before and thus already
+	// have a type associated with it. We still need to parse the type
+	// structure, but throw it away if the object already has a type.
+	// This ensures that all imports refer to the same type object for
+	// a given type declaration.
+	typ := p.parseType(pkg)
+
+	if name := obj.Type().(*types.Named); name.Underlying() == nil {
+		name.SetUnderlying(typ)
+	}
+}
+
+// VarDecl = "var" ExportedName Type .
+//
+func (p *parser) parseVarDecl() {
+	p.expectKeyword("var")
+	pkg, name := p.parseExportedName()
+	typ := p.parseType(pkg)
+	pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
+}
+
+// Func = Signature [ Body ] .
+// Body = "{" ... "}" .
+//
+func (p *parser) parseFunc(recv *types.Var) *types.Signature {
+	sig := p.parseSignature(recv)
+	if p.tok == '{' {
+		p.next()
+		for i := 1; i > 0; p.next() {
+			switch p.tok {
+			case '{':
+				i++
+			case '}':
+				i--
+			}
+		}
+	}
+	return sig
+}
+
+// MethodDecl = "func" Receiver Name Func .
+// Receiver   = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
+//
+func (p *parser) parseMethodDecl() {
+	// "func" already consumed
+	p.expect('(')
+	recv, _ := p.parseParameter() // receiver
+	p.expect(')')
+
+	// determine receiver base type object
+	base := deref(recv.Type()).(*types.Named)
+
+	// parse method name, signature, and possibly inlined body
+	_, name := p.parseName(nil, false)
+	sig := p.parseFunc(recv)
+
+	// methods always belong to the same package as the base type object
+	pkg := base.Obj().Pkg()
+
+	// add method to type unless type was imported before
+	// and method exists already
+	// TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small.
+	base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
+}
+
+// FuncDecl = "func" ExportedName Func .
+//
+func (p *parser) parseFuncDecl() {
+	// "func" already consumed
+	pkg, name := p.parseExportedName()
+	typ := p.parseFunc(nil)
+	pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ))
+}
+
+// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
+//
+func (p *parser) parseDecl() {
+	if p.tok == scanner.Ident {
+		switch p.lit {
+		case "import":
+			p.parseImportDecl()
+		case "const":
+			p.parseConstDecl()
+		case "type":
+			p.parseTypeDecl()
+		case "var":
+			p.parseVarDecl()
+		case "func":
+			p.next() // look ahead
+			if p.tok == '(' {
+				p.parseMethodDecl()
+			} else {
+				p.parseFuncDecl()
+			}
+		}
+	}
+	p.expect('\n')
+}
+
+// ----------------------------------------------------------------------------
+// Export
+
+// Export        = "PackageClause { Decl } "$$" .
+// PackageClause = "package" PackageName [ "safe" ] "\n" .
+//
+func (p *parser) parseExport() *types.Package {
+	p.expectKeyword("package")
+	name := p.parsePackageName()
+	if p.tok == scanner.Ident && p.lit == "safe" {
+		// package was compiled with -u option - ignore
+		p.next()
+	}
+	p.expect('\n')
+
+	pkg := p.getPkg(p.id, name)
+
+	for p.tok != '$' && p.tok != scanner.EOF {
+		p.parseDecl()
+	}
+
+	if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
+		// don't call next()/expect() since reading past the
+		// export data may cause scanner errors (e.g. NUL chars)
+		p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
+	}
+
+	if n := p.scanner.ErrorCount; n != 0 {
+		p.errorf("expected no scanner errors, got %d", n)
+	}
+
+	// Record all locally referenced packages as imports.
+	var imports []*types.Package
+	for id, pkg2 := range p.localPkgs {
+		if pkg2.Name() == "" {
+			p.errorf("%s package has no name", id)
+		}
+		if id == p.id {
+			continue // avoid self-edge
+		}
+		imports = append(imports, pkg2)
+	}
+	sort.Sort(byPath(imports))
+	pkg.SetImports(imports)
+
+	// package was imported completely and without errors
+	pkg.MarkComplete()
+
+	return pkg
+}
+
+type byPath []*types.Package
+
+func (a byPath) Len() int           { return len(a) }
+func (a byPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }

vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go πŸ”—

@@ -0,0 +1,598 @@
+// Copyright 2018 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.
+
+// Indexed package import.
+// See cmd/compile/internal/gc/iexport.go for the export data format.
+
+// This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go.
+
+package gcimporter
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"go/constant"
+	"go/token"
+	"go/types"
+	"io"
+	"sort"
+)
+
+type intReader struct {
+	*bytes.Reader
+	path string
+}
+
+func (r *intReader) int64() int64 {
+	i, err := binary.ReadVarint(r.Reader)
+	if err != nil {
+		errorf("import %q: read varint error: %v", r.path, err)
+	}
+	return i
+}
+
+func (r *intReader) uint64() uint64 {
+	i, err := binary.ReadUvarint(r.Reader)
+	if err != nil {
+		errorf("import %q: read varint error: %v", r.path, err)
+	}
+	return i
+}
+
+const predeclReserved = 32
+
+type itag uint64
+
+const (
+	// Types
+	definedType itag = iota
+	pointerType
+	sliceType
+	arrayType
+	chanType
+	mapType
+	signatureType
+	structType
+	interfaceType
+)
+
+// IImportData imports a package from the serialized package data
+// and returns the number of bytes consumed and a reference to the package.
+// If the export data version is not recognized or the format is otherwise
+// compromised, an error is returned.
+func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
+	const currentVersion = 0
+	version := -1
+	defer func() {
+		if e := recover(); e != nil {
+			if version > currentVersion {
+				err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
+			} else {
+				err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
+			}
+		}
+	}()
+
+	r := &intReader{bytes.NewReader(data), path}
+
+	version = int(r.uint64())
+	switch version {
+	case currentVersion:
+	default:
+		errorf("unknown iexport format version %d", version)
+	}
+
+	sLen := int64(r.uint64())
+	dLen := int64(r.uint64())
+
+	whence, _ := r.Seek(0, io.SeekCurrent)
+	stringData := data[whence : whence+sLen]
+	declData := data[whence+sLen : whence+sLen+dLen]
+	r.Seek(sLen+dLen, io.SeekCurrent)
+
+	p := iimporter{
+		ipath: path,
+
+		stringData:  stringData,
+		stringCache: make(map[uint64]string),
+		pkgCache:    make(map[uint64]*types.Package),
+
+		declData: declData,
+		pkgIndex: make(map[*types.Package]map[string]uint64),
+		typCache: make(map[uint64]types.Type),
+
+		fake: fakeFileSet{
+			fset:  fset,
+			files: make(map[string]*token.File),
+		},
+	}
+
+	for i, pt := range predeclared {
+		p.typCache[uint64(i)] = pt
+	}
+
+	pkgList := make([]*types.Package, r.uint64())
+	for i := range pkgList {
+		pkgPathOff := r.uint64()
+		pkgPath := p.stringAt(pkgPathOff)
+		pkgName := p.stringAt(r.uint64())
+		_ = r.uint64() // package height; unused by go/types
+
+		if pkgPath == "" {
+			pkgPath = path
+		}
+		pkg := imports[pkgPath]
+		if pkg == nil {
+			pkg = types.NewPackage(pkgPath, pkgName)
+			imports[pkgPath] = pkg
+		} else if pkg.Name() != pkgName {
+			errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
+		}
+
+		p.pkgCache[pkgPathOff] = pkg
+
+		nameIndex := make(map[string]uint64)
+		for nSyms := r.uint64(); nSyms > 0; nSyms-- {
+			name := p.stringAt(r.uint64())
+			nameIndex[name] = r.uint64()
+		}
+
+		p.pkgIndex[pkg] = nameIndex
+		pkgList[i] = pkg
+	}
+
+	localpkg := pkgList[0]
+
+	names := make([]string, 0, len(p.pkgIndex[localpkg]))
+	for name := range p.pkgIndex[localpkg] {
+		names = append(names, name)
+	}
+	sort.Strings(names)
+	for _, name := range names {
+		p.doDecl(localpkg, name)
+	}
+
+	for _, typ := range p.interfaceList {
+		typ.Complete()
+	}
+
+	// record all referenced packages as imports
+	list := append(([]*types.Package)(nil), pkgList[1:]...)
+	sort.Sort(byPath(list))
+	localpkg.SetImports(list)
+
+	// package was imported completely and without errors
+	localpkg.MarkComplete()
+
+	consumed, _ := r.Seek(0, io.SeekCurrent)
+	return int(consumed), localpkg, nil
+}
+
+type iimporter struct {
+	ipath string
+
+	stringData  []byte
+	stringCache map[uint64]string
+	pkgCache    map[uint64]*types.Package
+
+	declData []byte
+	pkgIndex map[*types.Package]map[string]uint64
+	typCache map[uint64]types.Type
+
+	fake          fakeFileSet
+	interfaceList []*types.Interface
+}
+
+func (p *iimporter) doDecl(pkg *types.Package, name string) {
+	// See if we've already imported this declaration.
+	if obj := pkg.Scope().Lookup(name); obj != nil {
+		return
+	}
+
+	off, ok := p.pkgIndex[pkg][name]
+	if !ok {
+		errorf("%v.%v not in index", pkg, name)
+	}
+
+	r := &importReader{p: p, currPkg: pkg}
+	r.declReader.Reset(p.declData[off:])
+
+	r.obj(name)
+}
+
+func (p *iimporter) stringAt(off uint64) string {
+	if s, ok := p.stringCache[off]; ok {
+		return s
+	}
+
+	slen, n := binary.Uvarint(p.stringData[off:])
+	if n <= 0 {
+		errorf("varint failed")
+	}
+	spos := off + uint64(n)
+	s := string(p.stringData[spos : spos+slen])
+	p.stringCache[off] = s
+	return s
+}
+
+func (p *iimporter) pkgAt(off uint64) *types.Package {
+	if pkg, ok := p.pkgCache[off]; ok {
+		return pkg
+	}
+	path := p.stringAt(off)
+	errorf("missing package %q in %q", path, p.ipath)
+	return nil
+}
+
+func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
+	if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
+		return t
+	}
+
+	if off < predeclReserved {
+		errorf("predeclared type missing from cache: %v", off)
+	}
+
+	r := &importReader{p: p}
+	r.declReader.Reset(p.declData[off-predeclReserved:])
+	t := r.doType(base)
+
+	if base == nil || !isInterface(t) {
+		p.typCache[off] = t
+	}
+	return t
+}
+
+type importReader struct {
+	p          *iimporter
+	declReader bytes.Reader
+	currPkg    *types.Package
+	prevFile   string
+	prevLine   int64
+}
+
+func (r *importReader) obj(name string) {
+	tag := r.byte()
+	pos := r.pos()
+
+	switch tag {
+	case 'A':
+		typ := r.typ()
+
+		r.declare(types.NewTypeName(pos, r.currPkg, name, typ))
+
+	case 'C':
+		typ, val := r.value()
+
+		r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
+
+	case 'F':
+		sig := r.signature(nil)
+
+		r.declare(types.NewFunc(pos, r.currPkg, name, sig))
+
+	case 'T':
+		// Types can be recursive. We need to setup a stub
+		// declaration before recursing.
+		obj := types.NewTypeName(pos, r.currPkg, name, nil)
+		named := types.NewNamed(obj, nil, nil)
+		r.declare(obj)
+
+		underlying := r.p.typAt(r.uint64(), named).Underlying()
+		named.SetUnderlying(underlying)
+
+		if !isInterface(underlying) {
+			for n := r.uint64(); n > 0; n-- {
+				mpos := r.pos()
+				mname := r.ident()
+				recv := r.param()
+				msig := r.signature(recv)
+
+				named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig))
+			}
+		}
+
+	case 'V':
+		typ := r.typ()
+
+		r.declare(types.NewVar(pos, r.currPkg, name, typ))
+
+	default:
+		errorf("unexpected tag: %v", tag)
+	}
+}
+
+func (r *importReader) declare(obj types.Object) {
+	obj.Pkg().Scope().Insert(obj)
+}
+
+func (r *importReader) value() (typ types.Type, val constant.Value) {
+	typ = r.typ()
+
+	switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
+	case types.IsBoolean:
+		val = constant.MakeBool(r.bool())
+
+	case types.IsString:
+		val = constant.MakeString(r.string())
+
+	case types.IsInteger:
+		val = r.mpint(b)
+
+	case types.IsFloat:
+		val = r.mpfloat(b)
+
+	case types.IsComplex:
+		re := r.mpfloat(b)
+		im := r.mpfloat(b)
+		val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+
+	default:
+		errorf("unexpected type %v", typ) // panics
+		panic("unreachable")
+	}
+
+	return
+}
+
+func intSize(b *types.Basic) (signed bool, maxBytes uint) {
+	if (b.Info() & types.IsUntyped) != 0 {
+		return true, 64
+	}
+
+	switch b.Kind() {
+	case types.Float32, types.Complex64:
+		return true, 3
+	case types.Float64, types.Complex128:
+		return true, 7
+	}
+
+	signed = (b.Info() & types.IsUnsigned) == 0
+	switch b.Kind() {
+	case types.Int8, types.Uint8:
+		maxBytes = 1
+	case types.Int16, types.Uint16:
+		maxBytes = 2
+	case types.Int32, types.Uint32:
+		maxBytes = 4
+	default:
+		maxBytes = 8
+	}
+
+	return
+}
+
+func (r *importReader) mpint(b *types.Basic) constant.Value {
+	signed, maxBytes := intSize(b)
+
+	maxSmall := 256 - maxBytes
+	if signed {
+		maxSmall = 256 - 2*maxBytes
+	}
+	if maxBytes == 1 {
+		maxSmall = 256
+	}
+
+	n, _ := r.declReader.ReadByte()
+	if uint(n) < maxSmall {
+		v := int64(n)
+		if signed {
+			v >>= 1
+			if n&1 != 0 {
+				v = ^v
+			}
+		}
+		return constant.MakeInt64(v)
+	}
+
+	v := -n
+	if signed {
+		v = -(n &^ 1) >> 1
+	}
+	if v < 1 || uint(v) > maxBytes {
+		errorf("weird decoding: %v, %v => %v", n, signed, v)
+	}
+
+	buf := make([]byte, v)
+	io.ReadFull(&r.declReader, buf)
+
+	// convert to little endian
+	// TODO(gri) go/constant should have a more direct conversion function
+	//           (e.g., once it supports a big.Float based implementation)
+	for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
+		buf[i], buf[j] = buf[j], buf[i]
+	}
+
+	x := constant.MakeFromBytes(buf)
+	if signed && n&1 != 0 {
+		x = constant.UnaryOp(token.SUB, x, 0)
+	}
+	return x
+}
+
+func (r *importReader) mpfloat(b *types.Basic) constant.Value {
+	x := r.mpint(b)
+	if constant.Sign(x) == 0 {
+		return x
+	}
+
+	exp := r.int64()
+	switch {
+	case exp > 0:
+		x = constant.Shift(x, token.SHL, uint(exp))
+	case exp < 0:
+		d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
+		x = constant.BinaryOp(x, token.QUO, d)
+	}
+	return x
+}
+
+func (r *importReader) ident() string {
+	return r.string()
+}
+
+func (r *importReader) qualifiedIdent() (*types.Package, string) {
+	name := r.string()
+	pkg := r.pkg()
+	return pkg, name
+}
+
+func (r *importReader) pos() token.Pos {
+	delta := r.int64()
+	if delta != deltaNewFile {
+		r.prevLine += delta
+	} else if l := r.int64(); l == -1 {
+		r.prevLine += deltaNewFile
+	} else {
+		r.prevFile = r.string()
+		r.prevLine = l
+	}
+
+	if r.prevFile == "" && r.prevLine == 0 {
+		return token.NoPos
+	}
+
+	return r.p.fake.pos(r.prevFile, int(r.prevLine))
+}
+
+func (r *importReader) typ() types.Type {
+	return r.p.typAt(r.uint64(), nil)
+}
+
+func isInterface(t types.Type) bool {
+	_, ok := t.(*types.Interface)
+	return ok
+}
+
+func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) }
+func (r *importReader) string() string      { return r.p.stringAt(r.uint64()) }
+
+func (r *importReader) doType(base *types.Named) types.Type {
+	switch k := r.kind(); k {
+	default:
+		errorf("unexpected kind tag in %q: %v", r.p.ipath, k)
+		return nil
+
+	case definedType:
+		pkg, name := r.qualifiedIdent()
+		r.p.doDecl(pkg, name)
+		return pkg.Scope().Lookup(name).(*types.TypeName).Type()
+	case pointerType:
+		return types.NewPointer(r.typ())
+	case sliceType:
+		return types.NewSlice(r.typ())
+	case arrayType:
+		n := r.uint64()
+		return types.NewArray(r.typ(), int64(n))
+	case chanType:
+		dir := chanDir(int(r.uint64()))
+		return types.NewChan(dir, r.typ())
+	case mapType:
+		return types.NewMap(r.typ(), r.typ())
+	case signatureType:
+		r.currPkg = r.pkg()
+		return r.signature(nil)
+
+	case structType:
+		r.currPkg = r.pkg()
+
+		fields := make([]*types.Var, r.uint64())
+		tags := make([]string, len(fields))
+		for i := range fields {
+			fpos := r.pos()
+			fname := r.ident()
+			ftyp := r.typ()
+			emb := r.bool()
+			tag := r.string()
+
+			fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
+			tags[i] = tag
+		}
+		return types.NewStruct(fields, tags)
+
+	case interfaceType:
+		r.currPkg = r.pkg()
+
+		embeddeds := make([]types.Type, r.uint64())
+		for i := range embeddeds {
+			_ = r.pos()
+			embeddeds[i] = r.typ()
+		}
+
+		methods := make([]*types.Func, r.uint64())
+		for i := range methods {
+			mpos := r.pos()
+			mname := r.ident()
+
+			// TODO(mdempsky): Matches bimport.go, but I
+			// don't agree with this.
+			var recv *types.Var
+			if base != nil {
+				recv = types.NewVar(token.NoPos, r.currPkg, "", base)
+			}
+
+			msig := r.signature(recv)
+			methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
+		}
+
+		typ := newInterface(methods, embeddeds)
+		r.p.interfaceList = append(r.p.interfaceList, typ)
+		return typ
+	}
+}
+
+func (r *importReader) kind() itag {
+	return itag(r.uint64())
+}
+
+func (r *importReader) signature(recv *types.Var) *types.Signature {
+	params := r.paramList()
+	results := r.paramList()
+	variadic := params.Len() > 0 && r.bool()
+	return types.NewSignature(recv, params, results, variadic)
+}
+
+func (r *importReader) paramList() *types.Tuple {
+	xs := make([]*types.Var, r.uint64())
+	for i := range xs {
+		xs[i] = r.param()
+	}
+	return types.NewTuple(xs...)
+}
+
+func (r *importReader) param() *types.Var {
+	pos := r.pos()
+	name := r.ident()
+	typ := r.typ()
+	return types.NewParam(pos, r.currPkg, name, typ)
+}
+
+func (r *importReader) bool() bool {
+	return r.uint64() != 0
+}
+
+func (r *importReader) int64() int64 {
+	n, err := binary.ReadVarint(&r.declReader)
+	if err != nil {
+		errorf("readVarint: %v", err)
+	}
+	return n
+}
+
+func (r *importReader) uint64() uint64 {
+	n, err := binary.ReadUvarint(&r.declReader)
+	if err != nil {
+		errorf("readUvarint: %v", err)
+	}
+	return n
+}
+
+func (r *importReader) byte() byte {
+	x, err := r.declReader.ReadByte()
+	if err != nil {
+		errorf("declReader.ReadByte: %v", err)
+	}
+	return x
+}

vendor/golang.org/x/tools/go/internal/gcimporter/isAlias18.go πŸ”—

@@ -0,0 +1,13 @@
+// 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.
+
+// +build !go1.9
+
+package gcimporter
+
+import "go/types"
+
+func isAlias(obj *types.TypeName) bool {
+	return false // there are no type aliases before Go 1.9
+}

vendor/golang.org/x/tools/go/internal/gcimporter/isAlias19.go πŸ”—

@@ -0,0 +1,13 @@
+// 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.
+
+// +build go1.9
+
+package gcimporter
+
+import "go/types"
+
+func isAlias(obj *types.TypeName) bool {
+	return obj.IsAlias()
+}

vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go πŸ”—

@@ -0,0 +1,21 @@
+// Copyright 2018 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 !go1.11
+
+package gcimporter
+
+import "go/types"
+
+func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface {
+	named := make([]*types.Named, len(embeddeds))
+	for i, e := range embeddeds {
+		var ok bool
+		named[i], ok = e.(*types.Named)
+		if !ok {
+			panic("embedding of non-defined interfaces in interfaces is not supported before Go 1.11")
+		}
+	}
+	return types.NewInterface(methods, named)
+}

vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go πŸ”—

@@ -0,0 +1,13 @@
+// Copyright 2018 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 go1.11
+
+package gcimporter
+
+import "go/types"
+
+func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface {
+	return types.NewInterfaceType(methods, embeddeds)
+}

vendor/golang.org/x/tools/go/loader/doc.go πŸ”—

@@ -1,205 +0,0 @@
-// 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.
-//
-// THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE.
-//
-// 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.
-
-// TODO(adonovan): overhaul the package documentation.

vendor/golang.org/x/tools/go/loader/loader.go πŸ”—

@@ -1,1078 +0,0 @@
-// 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
-
-// See doc.go for package documentation and implementation notes.
-
-import (
-	"errors"
-	"fmt"
-	"go/ast"
-	"go/build"
-	"go/parser"
-	"go/token"
-	"go/types"
-	"os"
-	"path/filepath"
-	"sort"
-	"strings"
-	"sync"
-	"time"
-
-	"golang.org/x/tools/go/ast/astutil"
-	"golang.org/x/tools/go/internal/cgo"
-)
-
-var ignoreVendor build.ImportMode
-
-const trace = false // show timing info for type-checking
-
-// Config specifies the configuration for loading a whole program from
-// Go source code.
-// The zero value for Config is a ready-to-use default configuration.
-type Config struct {
-	// Fset is the file set for the parser to use when loading the
-	// program.  If nil, it may be lazily initialized by any
-	// method of Config.
-	Fset *token.FileSet
-
-	// ParserMode specifies the mode to be used by the parser when
-	// loading source packages.
-	ParserMode parser.Mode
-
-	// TypeChecker contains options relating to the type checker.
-	//
-	// The supplied IgnoreFuncBodies is not used; the effective
-	// value comes from the TypeCheckFuncBodies func below.
-	// The supplied Import function is not used either.
-	TypeChecker types.Config
-
-	// TypeCheckFuncBodies is a predicate over package paths.
-	// A package for which the predicate is false will
-	// have its package-level declarations type checked, but not
-	// its function bodies; this can be used to quickly load
-	// dependencies from source.  If nil, all func bodies are type
-	// checked.
-	TypeCheckFuncBodies func(path string) bool
-
-	// If Build is non-nil, it is used to locate source packages.
-	// Otherwise &build.Default is used.
-	//
-	// By default, cgo is invoked to preprocess Go files that
-	// import the fake package "C".  This behaviour can be
-	// disabled by setting CGO_ENABLED=0 in the environment prior
-	// to startup, or by setting Build.CgoEnabled=false.
-	Build *build.Context
-
-	// The current directory, used for resolving relative package
-	// references such as "./go/loader".  If empty, os.Getwd will be
-	// used instead.
-	Cwd string
-
-	// If DisplayPath is non-nil, it is used to transform each
-	// file name obtained from Build.Import().  This can be used
-	// to prevent a virtualized build.Config's file names from
-	// leaking into the user interface.
-	DisplayPath func(path string) string
-
-	// If AllowErrors is true, Load will return a Program even
-	// if some of the its packages contained I/O, parser or type
-	// errors; such errors are accessible via PackageInfo.Errors.  If
-	// false, Load will fail if any package had an error.
-	AllowErrors bool
-
-	// CreatePkgs specifies a list of non-importable initial
-	// packages to create.  The resulting packages will appear in
-	// the corresponding elements of the Program.Created slice.
-	CreatePkgs []PkgSpec
-
-	// ImportPkgs specifies a set of initial packages to load.
-	// The map keys are package paths.
-	//
-	// The map value indicates whether to load tests.  If true, Load
-	// will add and type-check two lists of files to the package:
-	// non-test files followed by in-package *_test.go files.  In
-	// addition, it will append the external test package (if any)
-	// to Program.Created.
-	ImportPkgs map[string]bool
-
-	// FindPackage is called during Load to create the build.Package
-	// for a given import path from a given directory.
-	// If FindPackage is nil, (*build.Context).Import is used.
-	// A client may use this hook to adapt to a proprietary build
-	// system that does not follow the "go build" layout
-	// conventions, for example.
-	//
-	// It must be safe to call concurrently from multiple goroutines.
-	FindPackage func(ctxt *build.Context, importPath, fromDir string, mode build.ImportMode) (*build.Package, error)
-
-	// AfterTypeCheck is called immediately after a list of files
-	// has been type-checked and appended to info.Files.
-	//
-	// This optional hook function is the earliest opportunity for
-	// the client to observe the output of the type checker,
-	// which may be useful to reduce analysis latency when loading
-	// a large program.
-	//
-	// The function is permitted to modify info.Info, for instance
-	// to clear data structures that are no longer needed, which can
-	// dramatically reduce peak memory consumption.
-	//
-	// The function may be called twice for the same PackageInfo:
-	// once for the files of the package and again for the
-	// in-package test files.
-	//
-	// It must be safe to call concurrently from multiple goroutines.
-	AfterTypeCheck func(info *PackageInfo, files []*ast.File)
-}
-
-// A PkgSpec specifies a non-importable package to be created by Load.
-// Files are processed first, but typically only one of Files and
-// Filenames is provided.  The path needn't be globally unique.
-//
-// For vendoring purposes, the package's directory is the one that
-// contains the first file.
-type PkgSpec struct {
-	Path      string      // package path ("" => use package declaration)
-	Files     []*ast.File // ASTs of already-parsed files
-	Filenames []string    // names of files to be parsed
-}
-
-// A Program is a Go program loaded from source as specified by a Config.
-type Program struct {
-	Fset *token.FileSet // the file set for this program
-
-	// Created[i] contains the initial package whose ASTs or
-	// filenames were supplied by Config.CreatePkgs[i], followed by
-	// the external test package, if any, of each package in
-	// Config.ImportPkgs ordered by ImportPath.
-	//
-	// NOTE: these files must not import "C".  Cgo preprocessing is
-	// only performed on imported packages, not ad hoc packages.
-	//
-	// TODO(adonovan): we need to copy and adapt the logic of
-	// goFilesPackage (from $GOROOT/src/cmd/go/build.go) and make
-	// Config.Import and Config.Create methods return the same kind
-	// of entity, essentially a build.Package.
-	// Perhaps we can even reuse that type directly.
-	Created []*PackageInfo
-
-	// Imported contains the initially imported packages,
-	// as specified by Config.ImportPkgs.
-	Imported map[string]*PackageInfo
-
-	// AllPackages contains the PackageInfo of every package
-	// encountered by Load: all initial packages and all
-	// dependencies, including incomplete ones.
-	AllPackages map[*types.Package]*PackageInfo
-
-	// importMap is the canonical mapping of package paths to
-	// packages.  It contains all Imported initial packages, but not
-	// Created ones, and all imported dependencies.
-	importMap map[string]*types.Package
-}
-
-// PackageInfo holds the ASTs and facts derived by the type-checker
-// for a single package.
-//
-// Not mutated once exposed via the API.
-//
-type PackageInfo struct {
-	Pkg                   *types.Package
-	Importable            bool        // true if 'import "Pkg.Path()"' would resolve to this
-	TransitivelyErrorFree bool        // true if Pkg and all its dependencies are free of errors
-	Files                 []*ast.File // syntax trees for the package's files
-	Errors                []error     // non-nil if the package had errors
-	types.Info                        // type-checker deductions.
-	dir                   string      // package directory
-
-	checker   *types.Checker // transient type-checker state
-	errorFunc func(error)
-}
-
-func (info *PackageInfo) String() string { return info.Pkg.Path() }
-
-func (info *PackageInfo) appendError(err error) {
-	if info.errorFunc != nil {
-		info.errorFunc(err)
-	} else {
-		fmt.Fprintln(os.Stderr, err)
-	}
-	info.Errors = append(info.Errors, err)
-}
-
-func (conf *Config) fset() *token.FileSet {
-	if conf.Fset == nil {
-		conf.Fset = token.NewFileSet()
-	}
-	return conf.Fset
-}
-
-// ParseFile is a convenience function (intended for testing) that invokes
-// the parser using the Config's FileSet, which is initialized if nil.
-//
-// src specifies the parser input as a string, []byte, or io.Reader, and
-// filename is its apparent name.  If src is nil, the contents of
-// filename are read from the file system.
-//
-func (conf *Config) ParseFile(filename string, src interface{}) (*ast.File, error) {
-	// TODO(adonovan): use conf.build() etc like parseFiles does.
-	return parser.ParseFile(conf.fset(), filename, src, conf.ParserMode)
-}
-
-// FromArgsUsage is a partial usage message that applications calling
-// FromArgs may wish to include in their -help output.
-const FromArgsUsage = `
-<args> is a list of arguments denoting a set of initial packages.
-It may take one of two forms:
-
-1. A list of *.go source files.
-
-   All of the specified files are loaded, parsed and type-checked
-   as a single package.  All the files must belong to the same directory.
-
-2. A list of import paths, each denoting a package.
-
-   The package's directory is found relative to the $GOROOT and
-   $GOPATH using similar logic to 'go build', and the *.go files in
-   that directory are loaded, parsed and type-checked as a single
-   package.
-
-   In addition, all *_test.go files in the directory are then loaded
-   and parsed.  Those files whose package declaration equals that of
-   the non-*_test.go files are included in the primary package.  Test
-   files whose package declaration ends with "_test" are type-checked
-   as another package, the 'external' test package, so that a single
-   import path may denote two packages.  (Whether this behaviour is
-   enabled is tool-specific, and may depend on additional flags.)
-
-A '--' argument terminates the list of packages.
-`
-
-// FromArgs interprets args as a set of initial packages to load from
-// source and updates the configuration.  It returns the list of
-// unconsumed arguments.
-//
-// It is intended for use in command-line interfaces that require a
-// set of initial packages to be specified; see FromArgsUsage message
-// for details.
-//
-// Only superficial errors are reported at this stage; errors dependent
-// on I/O are detected during Load.
-//
-func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) {
-	var rest []string
-	for i, arg := range args {
-		if arg == "--" {
-			rest = args[i+1:]
-			args = args[:i]
-			break // consume "--" and return the remaining args
-		}
-	}
-
-	if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
-		// Assume args is a list of a *.go files
-		// denoting a single ad hoc package.
-		for _, arg := range args {
-			if !strings.HasSuffix(arg, ".go") {
-				return nil, fmt.Errorf("named files must be .go files: %s", arg)
-			}
-		}
-		conf.CreateFromFilenames("", args...)
-	} else {
-		// Assume args are directories each denoting a
-		// package and (perhaps) an external test, iff xtest.
-		for _, arg := range args {
-			if xtest {
-				conf.ImportWithTests(arg)
-			} else {
-				conf.Import(arg)
-			}
-		}
-	}
-
-	return rest, nil
-}
-
-// CreateFromFilenames is a convenience function that adds
-// a conf.CreatePkgs entry to create a package of the specified *.go
-// files.
-//
-func (conf *Config) CreateFromFilenames(path string, filenames ...string) {
-	conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Filenames: filenames})
-}
-
-// CreateFromFiles is a convenience function that adds a conf.CreatePkgs
-// entry to create package of the specified path and parsed files.
-//
-func (conf *Config) CreateFromFiles(path string, files ...*ast.File) {
-	conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Files: files})
-}
-
-// ImportWithTests is a convenience function that adds path to
-// ImportPkgs, the set of initial source packages located relative to
-// $GOPATH.  The package will be augmented by any *_test.go files in
-// its directory that contain a "package x" (not "package x_test")
-// declaration.
-//
-// In addition, if any *_test.go files contain a "package x_test"
-// declaration, an additional package comprising just those files will
-// be added to CreatePkgs.
-//
-func (conf *Config) ImportWithTests(path string) { conf.addImport(path, true) }
-
-// Import is a convenience function that adds path to ImportPkgs, the
-// set of initial packages that will be imported from source.
-//
-func (conf *Config) Import(path string) { conf.addImport(path, false) }
-
-func (conf *Config) addImport(path string, tests bool) {
-	if path == "C" {
-		return // ignore; not a real package
-	}
-	if conf.ImportPkgs == nil {
-		conf.ImportPkgs = make(map[string]bool)
-	}
-	conf.ImportPkgs[path] = conf.ImportPkgs[path] || tests
-}
-
-// PathEnclosingInterval returns the PackageInfo and ast.Node that
-// contain source interval [start, end), and all the node's ancestors
-// up to the AST root.  It searches all ast.Files of all packages in prog.
-// exact is defined as for astutil.PathEnclosingInterval.
-//
-// The zero value is returned if not found.
-//
-func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) {
-	for _, info := range prog.AllPackages {
-		for _, f := range info.Files {
-			if f.Pos() == token.NoPos {
-				// This can happen if the parser saw
-				// too many errors and bailed out.
-				// (Use parser.AllErrors to prevent that.)
-				continue
-			}
-			if !tokenFileContainsPos(prog.Fset.File(f.Pos()), start) {
-				continue
-			}
-			if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil {
-				return info, path, exact
-			}
-		}
-	}
-	return nil, nil, false
-}
-
-// InitialPackages returns a new slice containing the set of initial
-// packages (Created + Imported) in unspecified order.
-//
-func (prog *Program) InitialPackages() []*PackageInfo {
-	infos := make([]*PackageInfo, 0, len(prog.Created)+len(prog.Imported))
-	infos = append(infos, prog.Created...)
-	for _, info := range prog.Imported {
-		infos = append(infos, info)
-	}
-	return infos
-}
-
-// Package returns the ASTs and results of type checking for the
-// specified package.
-func (prog *Program) Package(path string) *PackageInfo {
-	if info, ok := prog.AllPackages[prog.importMap[path]]; ok {
-		return info
-	}
-	for _, info := range prog.Created {
-		if path == info.Pkg.Path() {
-			return info
-		}
-	}
-	return nil
-}
-
-// ---------- Implementation ----------
-
-// importer holds the working state of the algorithm.
-type importer struct {
-	conf  *Config   // the client configuration
-	start time.Time // for logging
-
-	progMu sync.Mutex // guards prog
-	prog   *Program   // the resulting program
-
-	// findpkg is a memoization of FindPackage.
-	findpkgMu sync.Mutex // guards findpkg
-	findpkg   map[findpkgKey]*findpkgValue
-
-	importedMu sync.Mutex             // guards imported
-	imported   map[string]*importInfo // all imported packages (incl. failures) by import path
-
-	// import dependency graph: graph[x][y] => x imports y
-	//
-	// Since non-importable packages cannot be cyclic, we ignore
-	// their imports, thus we only need the subgraph over importable
-	// packages.  Nodes are identified by their import paths.
-	graphMu sync.Mutex
-	graph   map[string]map[string]bool
-}
-
-type findpkgKey struct {
-	importPath string
-	fromDir    string
-	mode       build.ImportMode
-}
-
-type findpkgValue struct {
-	ready chan struct{} // closed to broadcast readiness
-	bp    *build.Package
-	err   error
-}
-
-// importInfo tracks the success or failure of a single import.
-//
-// Upon completion, exactly one of info and err is non-nil:
-// info on successful creation of a package, err otherwise.
-// A successful package may still contain type errors.
-//
-type importInfo struct {
-	path     string        // import path
-	info     *PackageInfo  // results of typechecking (including errors)
-	complete chan struct{} // closed to broadcast that info is set.
-}
-
-// awaitCompletion blocks until ii is complete,
-// i.e. the info field is safe to inspect.
-func (ii *importInfo) awaitCompletion() {
-	<-ii.complete // wait for close
-}
-
-// Complete marks ii as complete.
-// Its info and err fields will not be subsequently updated.
-func (ii *importInfo) Complete(info *PackageInfo) {
-	if info == nil {
-		panic("info == nil")
-	}
-	ii.info = info
-	close(ii.complete)
-}
-
-type importError struct {
-	path string // import path
-	err  error  // reason for failure to create a package
-}
-
-// Load creates the initial packages specified by conf.{Create,Import}Pkgs,
-// loading their dependencies packages as needed.
-//
-// On success, Load returns a Program containing a PackageInfo for
-// each package.  On failure, it returns an error.
-//
-// If AllowErrors is true, Load will return a Program even if some
-// packages contained I/O, parser or type errors, or if dependencies
-// were missing.  (Such errors are accessible via PackageInfo.Errors.  If
-// false, Load will fail if any package had an error.
-//
-// It is an error if no packages were loaded.
-//
-func (conf *Config) Load() (*Program, error) {
-	// Create a simple default error handler for parse/type errors.
-	if conf.TypeChecker.Error == nil {
-		conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
-	}
-
-	// Set default working directory for relative package references.
-	if conf.Cwd == "" {
-		var err error
-		conf.Cwd, err = os.Getwd()
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	// Install default FindPackage hook using go/build logic.
-	if conf.FindPackage == nil {
-		conf.FindPackage = (*build.Context).Import
-	}
-
-	prog := &Program{
-		Fset:        conf.fset(),
-		Imported:    make(map[string]*PackageInfo),
-		importMap:   make(map[string]*types.Package),
-		AllPackages: make(map[*types.Package]*PackageInfo),
-	}
-
-	imp := importer{
-		conf:     conf,
-		prog:     prog,
-		findpkg:  make(map[findpkgKey]*findpkgValue),
-		imported: make(map[string]*importInfo),
-		start:    time.Now(),
-		graph:    make(map[string]map[string]bool),
-	}
-
-	// -- loading proper (concurrent phase) --------------------------------
-
-	var errpkgs []string // packages that contained errors
-
-	// Load the initially imported packages and their dependencies,
-	// in parallel.
-	// No vendor check on packages imported from the command line.
-	infos, importErrors := imp.importAll("", conf.Cwd, conf.ImportPkgs, ignoreVendor)
-	for _, ie := range importErrors {
-		conf.TypeChecker.Error(ie.err) // failed to create package
-		errpkgs = append(errpkgs, ie.path)
-	}
-	for _, info := range infos {
-		prog.Imported[info.Pkg.Path()] = info
-	}
-
-	// Augment the designated initial packages by their tests.
-	// Dependencies are loaded in parallel.
-	var xtestPkgs []*build.Package
-	for importPath, augment := range conf.ImportPkgs {
-		if !augment {
-			continue
-		}
-
-		// No vendor check on packages imported from command line.
-		bp, err := imp.findPackage(importPath, conf.Cwd, ignoreVendor)
-		if err != nil {
-			// Package not found, or can't even parse package declaration.
-			// Already reported by previous loop; ignore it.
-			continue
-		}
-
-		// Needs external test package?
-		if len(bp.XTestGoFiles) > 0 {
-			xtestPkgs = append(xtestPkgs, bp)
-		}
-
-		// Consult the cache using the canonical package path.
-		path := bp.ImportPath
-		imp.importedMu.Lock() // (unnecessary, we're sequential here)
-		ii, ok := imp.imported[path]
-		// Paranoid checks added due to issue #11012.
-		if !ok {
-			// Unreachable.
-			// The previous loop called importAll and thus
-			// startLoad for each path in ImportPkgs, which
-			// populates imp.imported[path] with a non-zero value.
-			panic(fmt.Sprintf("imported[%q] not found", path))
-		}
-		if ii == nil {
-			// Unreachable.
-			// The ii values in this loop are the same as in
-			// the previous loop, which enforced the invariant
-			// that at least one of ii.err and ii.info is non-nil.
-			panic(fmt.Sprintf("imported[%q] == nil", path))
-		}
-		if ii.info == nil {
-			// Unreachable.
-			// awaitCompletion has the postcondition
-			// ii.info != nil.
-			panic(fmt.Sprintf("imported[%q].info = nil", path))
-		}
-		info := ii.info
-		imp.importedMu.Unlock()
-
-		// Parse the in-package test files.
-		files, errs := imp.conf.parsePackageFiles(bp, 't')
-		for _, err := range errs {
-			info.appendError(err)
-		}
-
-		// The test files augmenting package P cannot be imported,
-		// but may import packages that import P,
-		// so we must disable the cycle check.
-		imp.addFiles(info, files, false)
-	}
-
-	createPkg := func(path, dir string, files []*ast.File, errs []error) {
-		info := imp.newPackageInfo(path, dir)
-		for _, err := range errs {
-			info.appendError(err)
-		}
-
-		// Ad hoc packages are non-importable,
-		// so no cycle check is needed.
-		// addFiles loads dependencies in parallel.
-		imp.addFiles(info, files, false)
-		prog.Created = append(prog.Created, info)
-	}
-
-	// Create packages specified by conf.CreatePkgs.
-	for _, cp := range conf.CreatePkgs {
-		files, errs := parseFiles(conf.fset(), conf.build(), nil, conf.Cwd, cp.Filenames, conf.ParserMode)
-		files = append(files, cp.Files...)
-
-		path := cp.Path
-		if path == "" {
-			if len(files) > 0 {
-				path = files[0].Name.Name
-			} else {
-				path = "(unnamed)"
-			}
-		}
-
-		dir := conf.Cwd
-		if len(files) > 0 && files[0].Pos().IsValid() {
-			dir = filepath.Dir(conf.fset().File(files[0].Pos()).Name())
-		}
-		createPkg(path, dir, files, errs)
-	}
-
-	// Create external test packages.
-	sort.Sort(byImportPath(xtestPkgs))
-	for _, bp := range xtestPkgs {
-		files, errs := imp.conf.parsePackageFiles(bp, 'x')
-		createPkg(bp.ImportPath+"_test", bp.Dir, files, errs)
-	}
-
-	// -- finishing up (sequential) ----------------------------------------
-
-	if len(prog.Imported)+len(prog.Created) == 0 {
-		return nil, errors.New("no initial packages were loaded")
-	}
-
-	// Create infos for indirectly imported packages.
-	// e.g. incomplete packages without syntax, loaded from export data.
-	for _, obj := range prog.importMap {
-		info := prog.AllPackages[obj]
-		if info == nil {
-			prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true}
-		} else {
-			// finished
-			info.checker = nil
-			info.errorFunc = nil
-		}
-	}
-
-	if !conf.AllowErrors {
-		// Report errors in indirectly imported packages.
-		for _, info := range prog.AllPackages {
-			if len(info.Errors) > 0 {
-				errpkgs = append(errpkgs, info.Pkg.Path())
-			}
-		}
-		if errpkgs != nil {
-			var more string
-			if len(errpkgs) > 3 {
-				more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
-				errpkgs = errpkgs[:3]
-			}
-			return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
-				strings.Join(errpkgs, ", "), more)
-		}
-	}
-
-	markErrorFreePackages(prog.AllPackages)
-
-	return prog, nil
-}
-
-type byImportPath []*build.Package
-
-func (b byImportPath) Len() int           { return len(b) }
-func (b byImportPath) Less(i, j int) bool { return b[i].ImportPath < b[j].ImportPath }
-func (b byImportPath) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
-
-// markErrorFreePackages sets the TransitivelyErrorFree flag on all
-// applicable packages.
-func markErrorFreePackages(allPackages map[*types.Package]*PackageInfo) {
-	// Build the transpose of the import graph.
-	importedBy := make(map[*types.Package]map[*types.Package]bool)
-	for P := range allPackages {
-		for _, Q := range P.Imports() {
-			clients, ok := importedBy[Q]
-			if !ok {
-				clients = make(map[*types.Package]bool)
-				importedBy[Q] = clients
-			}
-			clients[P] = true
-		}
-	}
-
-	// Find all packages reachable from some error package.
-	reachable := make(map[*types.Package]bool)
-	var visit func(*types.Package)
-	visit = func(p *types.Package) {
-		if !reachable[p] {
-			reachable[p] = true
-			for q := range importedBy[p] {
-				visit(q)
-			}
-		}
-	}
-	for _, info := range allPackages {
-		if len(info.Errors) > 0 {
-			visit(info.Pkg)
-		}
-	}
-
-	// Mark the others as "transitively error-free".
-	for _, info := range allPackages {
-		if !reachable[info.Pkg] {
-			info.TransitivelyErrorFree = true
-		}
-	}
-}
-
-// build returns the effective build context.
-func (conf *Config) build() *build.Context {
-	if conf.Build != nil {
-		return conf.Build
-	}
-	return &build.Default
-}
-
-// parsePackageFiles enumerates the files belonging to package path,
-// then loads, parses and returns them, plus a list of I/O or parse
-// errors that were encountered.
-//
-// 'which' indicates which files to include:
-//    'g': include non-test *.go source files (GoFiles + processed CgoFiles)
-//    't': include in-package *_test.go source files (TestGoFiles)
-//    'x': include external *_test.go source files. (XTestGoFiles)
-//
-func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) {
-	if bp.ImportPath == "unsafe" {
-		return nil, nil
-	}
-	var filenames []string
-	switch which {
-	case 'g':
-		filenames = bp.GoFiles
-	case 't':
-		filenames = bp.TestGoFiles
-	case 'x':
-		filenames = bp.XTestGoFiles
-	default:
-		panic(which)
-	}
-
-	files, errs := parseFiles(conf.fset(), conf.build(), conf.DisplayPath, bp.Dir, filenames, conf.ParserMode)
-
-	// Preprocess CgoFiles and parse the outputs (sequentially).
-	if which == 'g' && bp.CgoFiles != nil {
-		cgofiles, err := cgo.ProcessFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode)
-		if err != nil {
-			errs = append(errs, err)
-		} else {
-			files = append(files, cgofiles...)
-		}
-	}
-
-	return files, errs
-}
-
-// doImport imports the package denoted by path.
-// It implements the types.Importer signature.
-//
-// It returns an error if a package could not be created
-// (e.g. go/build or parse error), but type errors are reported via
-// the types.Config.Error callback (the first of which is also saved
-// in the package's PackageInfo).
-//
-// Idempotent.
-//
-func (imp *importer) doImport(from *PackageInfo, to string) (*types.Package, error) {
-	if to == "C" {
-		// This should be unreachable, but ad hoc packages are
-		// not currently subject to cgo preprocessing.
-		// See https://github.com/golang/go/issues/11627.
-		return nil, fmt.Errorf(`the loader doesn't cgo-process ad hoc packages like %q; see Go issue 11627`,
-			from.Pkg.Path())
-	}
-
-	bp, err := imp.findPackage(to, from.dir, 0)
-	if err != nil {
-		return nil, err
-	}
-
-	// The standard unsafe package is handled specially,
-	// and has no PackageInfo.
-	if bp.ImportPath == "unsafe" {
-		return types.Unsafe, nil
-	}
-
-	// Look for the package in the cache using its canonical path.
-	path := bp.ImportPath
-	imp.importedMu.Lock()
-	ii := imp.imported[path]
-	imp.importedMu.Unlock()
-	if ii == nil {
-		panic("internal error: unexpected import: " + path)
-	}
-	if ii.info != nil {
-		return ii.info.Pkg, nil
-	}
-
-	// Import of incomplete package: this indicates a cycle.
-	fromPath := from.Pkg.Path()
-	if cycle := imp.findPath(path, fromPath); cycle != nil {
-		cycle = append([]string{fromPath}, cycle...)
-		return nil, fmt.Errorf("import cycle: %s", strings.Join(cycle, " -> "))
-	}
-
-	panic("internal error: import of incomplete (yet acyclic) package: " + fromPath)
-}
-
-// findPackage locates the package denoted by the importPath in the
-// specified directory.
-func (imp *importer) findPackage(importPath, fromDir string, mode build.ImportMode) (*build.Package, error) {
-	// We use a non-blocking duplicate-suppressing cache (gopl.io Β§9.7)
-	// to avoid holding the lock around FindPackage.
-	key := findpkgKey{importPath, fromDir, mode}
-	imp.findpkgMu.Lock()
-	v, ok := imp.findpkg[key]
-	if ok {
-		// cache hit
-		imp.findpkgMu.Unlock()
-
-		<-v.ready // wait for entry to become ready
-	} else {
-		// Cache miss: this goroutine becomes responsible for
-		// populating the map entry and broadcasting its readiness.
-		v = &findpkgValue{ready: make(chan struct{})}
-		imp.findpkg[key] = v
-		imp.findpkgMu.Unlock()
-
-		ioLimit <- true
-		v.bp, v.err = imp.conf.FindPackage(imp.conf.build(), importPath, fromDir, mode)
-		<-ioLimit
-
-		if _, ok := v.err.(*build.NoGoError); ok {
-			v.err = nil // empty directory is not an error
-		}
-
-		close(v.ready) // broadcast ready condition
-	}
-	return v.bp, v.err
-}
-
-// importAll loads, parses, and type-checks the specified packages in
-// parallel and returns their completed importInfos in unspecified order.
-//
-// fromPath is the package path of the importing package, if it is
-// importable, "" otherwise.  It is used for cycle detection.
-//
-// fromDir is the directory containing the import declaration that
-// caused these imports.
-//
-func (imp *importer) importAll(fromPath, fromDir string, imports map[string]bool, mode build.ImportMode) (infos []*PackageInfo, errors []importError) {
-	// TODO(adonovan): opt: do the loop in parallel once
-	// findPackage is non-blocking.
-	var pending []*importInfo
-	for importPath := range imports {
-		bp, err := imp.findPackage(importPath, fromDir, mode)
-		if err != nil {
-			errors = append(errors, importError{
-				path: importPath,
-				err:  err,
-			})
-			continue
-		}
-		pending = append(pending, imp.startLoad(bp))
-	}
-
-	if fromPath != "" {
-		// We're loading a set of imports.
-		//
-		// We must record graph edges from the importing package
-		// to its dependencies, and check for cycles.
-		imp.graphMu.Lock()
-		deps, ok := imp.graph[fromPath]
-		if !ok {
-			deps = make(map[string]bool)
-			imp.graph[fromPath] = deps
-		}
-		for _, ii := range pending {
-			deps[ii.path] = true
-		}
-		imp.graphMu.Unlock()
-	}
-
-	for _, ii := range pending {
-		if fromPath != "" {
-			if cycle := imp.findPath(ii.path, fromPath); cycle != nil {
-				// Cycle-forming import: we must not await its
-				// completion since it would deadlock.
-				//
-				// We don't record the error in ii since
-				// the error is really associated with the
-				// cycle-forming edge, not the package itself.
-				// (Also it would complicate the
-				// invariants of importPath completion.)
-				if trace {
-					fmt.Fprintf(os.Stderr, "import cycle: %q\n", cycle)
-				}
-				continue
-			}
-		}
-		ii.awaitCompletion()
-		infos = append(infos, ii.info)
-	}
-
-	return infos, errors
-}
-
-// findPath returns an arbitrary path from 'from' to 'to' in the import
-// graph, or nil if there was none.
-func (imp *importer) findPath(from, to string) []string {
-	imp.graphMu.Lock()
-	defer imp.graphMu.Unlock()
-
-	seen := make(map[string]bool)
-	var search func(stack []string, importPath string) []string
-	search = func(stack []string, importPath string) []string {
-		if !seen[importPath] {
-			seen[importPath] = true
-			stack = append(stack, importPath)
-			if importPath == to {
-				return stack
-			}
-			for x := range imp.graph[importPath] {
-				if p := search(stack, x); p != nil {
-					return p
-				}
-			}
-		}
-		return nil
-	}
-	return search(make([]string, 0, 20), from)
-}
-
-// startLoad initiates the loading, parsing and type-checking of the
-// specified package and its dependencies, if it has not already begun.
-//
-// It returns an importInfo, not necessarily in a completed state.  The
-// caller must call awaitCompletion() before accessing its info field.
-//
-// startLoad is concurrency-safe and idempotent.
-//
-func (imp *importer) startLoad(bp *build.Package) *importInfo {
-	path := bp.ImportPath
-	imp.importedMu.Lock()
-	ii, ok := imp.imported[path]
-	if !ok {
-		ii = &importInfo{path: path, complete: make(chan struct{})}
-		imp.imported[path] = ii
-		go func() {
-			info := imp.load(bp)
-			ii.Complete(info)
-		}()
-	}
-	imp.importedMu.Unlock()
-
-	return ii
-}
-
-// load implements package loading by parsing Go source files
-// located by go/build.
-func (imp *importer) load(bp *build.Package) *PackageInfo {
-	info := imp.newPackageInfo(bp.ImportPath, bp.Dir)
-	info.Importable = true
-	files, errs := imp.conf.parsePackageFiles(bp, 'g')
-	for _, err := range errs {
-		info.appendError(err)
-	}
-
-	imp.addFiles(info, files, true)
-
-	imp.progMu.Lock()
-	imp.prog.importMap[bp.ImportPath] = info.Pkg
-	imp.progMu.Unlock()
-
-	return info
-}
-
-// addFiles adds and type-checks the specified files to info, loading
-// their dependencies if needed.  The order of files determines the
-// package initialization order.  It may be called multiple times on the
-// same package.  Errors are appended to the info.Errors field.
-//
-// cycleCheck determines whether the imports within files create
-// dependency edges that should be checked for potential cycles.
-//
-func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) {
-	// Ensure the dependencies are loaded, in parallel.
-	var fromPath string
-	if cycleCheck {
-		fromPath = info.Pkg.Path()
-	}
-	// TODO(adonovan): opt: make the caller do scanImports.
-	// Callers with a build.Package can skip it.
-	imp.importAll(fromPath, info.dir, scanImports(files), 0)
-
-	if trace {
-		fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n",
-			time.Since(imp.start), info.Pkg.Path(), len(files))
-	}
-
-	// Don't call checker.Files on Unsafe, even with zero files,
-	// because it would mutate the package, which is a global.
-	if info.Pkg == types.Unsafe {
-		if len(files) > 0 {
-			panic(`"unsafe" package contains unexpected files`)
-		}
-	} else {
-		// Ignore the returned (first) error since we
-		// already collect them all in the PackageInfo.
-		info.checker.Files(files)
-		info.Files = append(info.Files, files...)
-	}
-
-	if imp.conf.AfterTypeCheck != nil {
-		imp.conf.AfterTypeCheck(info, files)
-	}
-
-	if trace {
-		fmt.Fprintf(os.Stderr, "%s: stop %q\n",
-			time.Since(imp.start), info.Pkg.Path())
-	}
-}
-
-func (imp *importer) newPackageInfo(path, dir string) *PackageInfo {
-	var pkg *types.Package
-	if path == "unsafe" {
-		pkg = types.Unsafe
-	} else {
-		pkg = types.NewPackage(path, "")
-	}
-	info := &PackageInfo{
-		Pkg: pkg,
-		Info: types.Info{
-			Types:      make(map[ast.Expr]types.TypeAndValue),
-			Defs:       make(map[*ast.Ident]types.Object),
-			Uses:       make(map[*ast.Ident]types.Object),
-			Implicits:  make(map[ast.Node]types.Object),
-			Scopes:     make(map[ast.Node]*types.Scope),
-			Selections: make(map[*ast.SelectorExpr]*types.Selection),
-		},
-		errorFunc: imp.conf.TypeChecker.Error,
-		dir:       dir,
-	}
-
-	// Copy the types.Config so we can vary it across PackageInfos.
-	tc := imp.conf.TypeChecker
-	tc.IgnoreFuncBodies = false
-	if f := imp.conf.TypeCheckFuncBodies; f != nil {
-		tc.IgnoreFuncBodies = !f(path)
-	}
-	tc.Importer = closure{imp, info}
-	tc.Error = info.appendError // appendError wraps the user's Error function
-
-	info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info)
-	imp.progMu.Lock()
-	imp.prog.AllPackages[pkg] = info
-	imp.progMu.Unlock()
-	return info
-}
-
-type closure struct {
-	imp  *importer
-	info *PackageInfo
-}
-
-func (c closure) Import(to string) (*types.Package, error) { return c.imp.doImport(c.info, to) }

vendor/golang.org/x/tools/go/loader/util.go πŸ”—

@@ -1,124 +0,0 @@
-// 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()
-}

vendor/golang.org/x/tools/go/packages/doc.go πŸ”—

@@ -0,0 +1,269 @@
+// Copyright 2018 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 packages loads Go packages for inspection and analysis.
+
+NOTE: THIS PACKAGE IS NOT YET READY FOR WIDESPREAD USE:
+ - The interface is still being revised and is likely to change.
+ - The implementation depends on the Go 1.11 go command.
+ - We intend to finalize the API before Go 1.11 is released.
+
+The Load function takes as input a list of patterns and return a list of Package
+structs describing individual packages matched by those patterns.
+The LoadMode controls the amounts of detail about the loaded packages.
+
+The patterns are used as arguments to the underlying build tool,
+such as the go command or Bazel, and are interpreted according to
+that tool's conventions.
+
+The Package struct provides basic information about the package, including
+
+  - ID, a unique identifier for the package in the returned set;
+  - GoFiles, the names of the package's Go source files;
+  - Imports, a map from source import strings to the Packages they name;
+  - Types, the type information for the package's exported symbols;
+  - Syntax, the parsed syntax trees for the package's source code; and
+  - TypeInfo, the result of a complete type-check of the package syntax trees.
+
+(See the documentation for type Package for the complete list of fields
+and more detailed descriptions.)
+
+For example,
+
+	Load(nil, "bytes", "unicode...")
+
+returns four Package structs describing the standard library packages
+bytes, unicode, unicode/utf16, and unicode/utf8. Note that one pattern
+can match multiple packages and that a package might be matched by
+multiple patterns: in general it is not possible to determine which
+packages correspond to which patterns.
+
+Note that the list returned by Load (LoadAllSyntax in this case)
+only contains the packages matched by the patterns. Their dependencies
+can be found by walking the import graph using the Imports fields.
+
+The Load function can be configured by passing a non-nil Config struct as
+the first argument. If you pass nil for the Config Load will
+run in LoadAllSyntax mode, collecting the maximal amount of information
+it can.
+See the documentation for type Config for details.
+
+As noted earlier, the Config.Mode controls increasing amounts of detail
+about the loaded packages, with each mode returning all the data of the
+previous mode with some extra added. See the documentation for type LoadMode
+for details.
+
+Most tools should pass their command-line arguments (after any flags)
+uninterpreted to the loader, so that the loader can interpret them
+according to the conventions of the underlying build system.
+For example, this program prints the names of the source files
+for each package listed on the command line:
+
+	package main
+
+	import (
+		"flag"
+		"fmt"
+		"log"
+
+		"golang.org/x/tools/go/packages"
+	)
+
+	func main() {
+		flag.Parse()
+		pkgs, err := packages.Load(nil, flag.Args()...)
+		if err != nil {
+			log.Fatal(err)
+		}
+		for _, pkg := range pkgs {
+			fmt.Print(pkg.ID, pkg.GoFiles)
+		}
+	}
+*/
+package packages // import "golang.org/x/tools/go/packages"
+
+/*
+
+Motivation and design considerations
+
+The new package's design solves problems addressed by two existing
+packages: go/build, which locates and describes packages, and
+golang.org/x/tools/go/loader, which loads, parses and type-checks them.
+The go/build.Package structure encodes too much of the 'go build' way
+of organizing projects, leaving us in need of a data type that describes a
+package of Go source code independent of the underlying build system.
+We wanted something that works equally well with go build and vgo, and
+also other build systems such as Bazel and Blaze, making it possible to
+construct analysis tools that work in all these environments.
+Tools such as errcheck and staticcheck were essentially unavailable to
+the Go community at Google, and some of Google's internal tools for Go
+are unavailable externally.
+This new package provides a uniform way to obtain package metadata by
+querying each of these build systems, optionally supporting their
+preferred command-line notations for packages, so that tools integrate
+neatly with users' build environments. The Metadata query function
+executes an external query tool appropriate to the current workspace.
+
+Loading packages always returns the complete import graph "all the way down",
+even if all you want is information about a single package, because the query
+mechanisms of all the build systems we currently support ({go,vgo} list, and
+blaze/bazel aspect-based query) cannot provide detailed information
+about one package without visiting all its dependencies too, so there is
+no additional asymptotic cost to providing transitive information.
+(This property might not be true of a hypothetical 5th build system.)
+
+This package provides no parse-but-don't-typecheck operation because most tools
+that need only untyped syntax (such as gofmt, goimports, and golint)
+seem not to care about any files other than the ones they are directly
+instructed to look at.  Also, it is trivial for a client to supplement
+this functionality on top of a Metadata query.
+
+In calls to TypeCheck, all initial packages, and any package that
+transitively depends on one of them, must be loaded from source.
+Consider A->B->C->D->E: if A,C are initial, A,B,C must be loaded from
+source; D may be loaded from export data, and E may not be loaded at all
+(though it's possible that D's export data mentions it, so a
+types.Package may be created for it and exposed.)
+
+The old loader had a feature to suppress type-checking of function
+bodies on a per-package basis, primarily intended to reduce the work of
+obtaining type information for imported packages. Now that imports are
+satisfied by export data, the optimization no longer seems necessary.
+
+Despite some early attempts, the old loader did not exploit export data,
+instead always using the equivalent of WholeProgram mode. This was due
+to the complexity of mixing source and export data packages (now
+resolved by the upward traversal mentioned above), and because export data
+files were nearly always missing or stale. Now that 'go build' supports
+caching, all the underlying build systems can guarantee to produce
+export data in a reasonable (amortized) time.
+
+Test "main" packages synthesized by the build system are now reported as
+first-class packages, avoiding the need for clients (such as go/ssa) to
+reinvent this generation logic.
+
+One way in which go/packages is simpler than the old loader is in its
+treatment of in-package tests. In-package tests are packages that
+consist of all the files of the library under test, plus the test files.
+The old loader constructed in-package tests by a two-phase process of
+mutation called "augmentation": first it would construct and type check
+all the ordinary library packages and type-check the packages that
+depend on them; then it would add more (test) files to the package and
+type-check again. This two-phase approach had four major problems:
+1) in processing the tests, the loader modified the library package,
+   leaving no way for a client application to see both the test
+   package and the library package; one would mutate into the other.
+2) because test files can declare additional methods on types defined in
+   the library portion of the package, the dispatch of method calls in
+   the library portion was affected by the presence of the test files.
+   This should have been a clue that the packages were logically
+   different.
+3) this model of "augmentation" assumed at most one in-package test
+   per library package, which is true of projects using 'go build',
+   but not other build systems.
+4) because of the two-phase nature of test processing, all packages that
+   import the library package had to be processed before augmentation,
+   forcing a "one-shot" API and preventing the client from calling Load
+   in several times in sequence as is now possible in WholeProgram mode.
+   (TypeCheck mode has a similar one-shot restriction for a different reason.)
+
+Early drafts of this package supported "multi-shot" operation
+in the Metadata and WholeProgram modes, although this feature is not exposed
+through the API and will likely be removed.
+Although it allowed clients to make a sequence of calls (or concurrent
+calls) to Load, building up the graph of Packages incrementally,
+it was of marginal value: it complicated the API
+(since it allowed some options to vary across calls but not others),
+it complicated the implementation,
+it cannot be made to work in TypeCheck mode, as explained above,
+and it was less efficient than making one combined call (when this is possible).
+Among the clients we have inspected, none made multiple calls to load
+but could not be easily and satisfactorily modified to make only a single call.
+However, applications changes may be required.
+For example, the ssadump command loads the user-specified packages
+and in addition the runtime package.  It is tempting to simply append
+"runtime" to the user-provided list, but that does not work if the user
+specified an ad-hoc package such as [a.go b.go].
+Instead, ssadump no longer requests the runtime package,
+but seeks it among the dependencies of the user-specified packages,
+and emits an error if it is not found.
+
+Overlays: the ParseFile hook in the API permits clients to vary the way
+in which ASTs are obtained from filenames; the default implementation is
+based on parser.ParseFile. This features enables editor-integrated tools
+that analyze the contents of modified but unsaved buffers: rather than
+read from the file system, a tool can read from an archive of modified
+buffers provided by the editor.
+This approach has its limits. Because package metadata is obtained by
+fork/execing an external query command for each build system, we can
+fake only the file contents seen by the parser, type-checker, and
+application, but not by the metadata query, so, for example:
+- additional imports in the fake file will not be described by the
+  metadata, so the type checker will fail to load imports that create
+  new dependencies.
+- in TypeCheck mode, because export data is produced by the query
+  command, it will not reflect the fake file contents.
+- this mechanism cannot add files to a package without first saving them.
+
+Questions & Tasks
+
+- Add GOARCH/GOOS?
+  They are not portable concepts, but could be made portable.
+  Our goal has been to allow users to express themselves using the conventions
+  of the underlying build system: if the build system honors GOARCH
+  during a build and during a metadata query, then so should
+  applications built atop that query mechanism.
+  Conversely, if the target architecture of the build is determined by
+  command-line flags, the application can pass the relevant
+  flags through to the build system using a command such as:
+    myapp -query_flag="--cpu=amd64" -query_flag="--os=darwin"
+  However, this approach is low-level, unwieldy, and non-portable.
+  GOOS and GOARCH seem important enough to warrant a dedicated option.
+
+- How should we handle partial failures such as a mixture of good and
+  malformed patterns, existing and non-existent packages, successful and
+  failed builds, import failures, import cycles, and so on, in a call to
+  Load?
+
+- Do we need a GeneratedBy map that maps the name of each generated Go
+  source file in GoFiles to that of the original file, if known, or "" otherwise?
+  Or are //line directives and "Generated" comments in those files enough?
+
+- Support bazel, blaze, and go1.10 list, not just go1.11 list.
+
+- Handle (and test) various partial success cases, e.g.
+  a mixture of good packages and:
+  invalid patterns
+  nonexistent packages
+  empty packages
+  packages with malformed package or import declarations
+  unreadable files
+  import cycles
+  other parse errors
+  type errors
+  Make sure we record errors at the correct place in the graph.
+
+- Missing packages among initial arguments are not reported.
+  Return bogus packages for them, like golist does.
+
+- "undeclared name" errors (for example) are reported out of source file
+  order. I suspect this is due to the breadth-first resolution now used
+  by go/types. Is that a bug? Discuss with gri.
+
+- https://github.com/golang/go/issues/25980 causes these commands to crash:
+  $ GOPATH=/none ./gopackages -all all
+  due to:
+  $ GOPATH=/none go list -e -test -json all
+  and:
+  $ go list -e -test ./relative/path
+
+- Modify stringer to use go/packages, perhaps initially under flag control.
+
+- Bug: "gopackages fmt a.go" doesn't produce an error.
+
+- If necessary, add back an IsTest boolean or expose ForTests on the Package struct.
+  IsTest was removed because we couldn't agree on a useful definition.
+
+*/

vendor/golang.org/x/tools/go/packages/external.go πŸ”—

@@ -0,0 +1,68 @@
+// Copyright 2018 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 enables an external tool to intercept package requests.
+// If the tool is present then its results are used in preference to
+// the go list command.
+
+package packages
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"os/exec"
+	"strings"
+)
+
+// findExternalTool returns the file path of a tool that supplies supplies
+// the build system package structure, or "" if not found."
+// If GOPACKAGESDRIVER is set in the environment findExternalTool returns its
+// value, otherwise it searches for a binary named gopackagesdriver on the PATH.
+func findExternalDriver(cfg *Config) driver {
+	const toolPrefix = "GOPACKAGESDRIVER="
+	tool := ""
+	for _, env := range cfg.Env {
+		if val := strings.TrimPrefix(env, toolPrefix); val != env {
+			tool = val
+		}
+	}
+	if tool != "" && tool == "off" {
+		return nil
+	}
+	if tool == "" {
+		var err error
+		tool, err = exec.LookPath("gopackagesdriver")
+		if err != nil {
+			return nil
+		}
+	}
+	return func(cfg *Config, words ...string) (*driverResponse, error) {
+		buf := new(bytes.Buffer)
+		fullargs := []string{
+			"list",
+			fmt.Sprintf("-test=%t", cfg.Tests),
+			fmt.Sprintf("-export=%t", usesExportData(cfg)),
+			fmt.Sprintf("-deps=%t", cfg.Mode >= LoadImports),
+		}
+		for _, f := range cfg.Flags {
+			fullargs = append(fullargs, fmt.Sprintf("-flags=%v", f))
+		}
+		fullargs = append(fullargs, "--")
+		fullargs = append(fullargs, words...)
+		cmd := exec.CommandContext(cfg.Context, tool, fullargs...)
+		cmd.Env = cfg.Env
+		cmd.Dir = cfg.Dir
+		cmd.Stdout = buf
+		cmd.Stderr = new(bytes.Buffer)
+		if err := cmd.Run(); err != nil {
+			return nil, fmt.Errorf("%v: %v: %s", tool, err, cmd.Stderr)
+		}
+		var response driverResponse
+		if err := json.Unmarshal(buf.Bytes(), &response); err != nil {
+			return nil, err
+		}
+		return &response, nil
+	}
+}

vendor/golang.org/x/tools/go/packages/golist.go πŸ”—

@@ -0,0 +1,337 @@
+// Copyright 2018 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 packages
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+)
+
+// A goTooOldError reports that the go command
+// found by exec.LookPath is too old to use the new go list behavior.
+type goTooOldError struct {
+	error
+}
+
+// goListDriver uses the go list command to interpret the patterns and produce
+// the build system package structure.
+// See driver for more details.
+func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
+	// Determine files requested in contains patterns
+	var containFiles []string
+	restPatterns := make([]string, 0, len(patterns))
+	for _, pattern := range patterns {
+		if strings.HasPrefix(pattern, "contains:") {
+			containFile := strings.TrimPrefix(pattern, "contains:")
+			containFiles = append(containFiles, containFile)
+		} else {
+			restPatterns = append(restPatterns, pattern)
+		}
+	}
+	containFiles = absJoin(cfg.Dir, containFiles)
+	patterns = restPatterns
+
+	// TODO(matloob): Remove the definition of listfunc and just use golistPackages once go1.12 is released.
+	var listfunc driver
+	listfunc = func(cfg *Config, words ...string) (*driverResponse, error) {
+		response, err := golistDriverCurrent(cfg, patterns...)
+		if _, ok := err.(goTooOldError); ok {
+			listfunc = golistDriverFallback
+			return listfunc(cfg, patterns...)
+		}
+		listfunc = golistDriverCurrent
+		return response, err
+	}
+
+	var response *driverResponse
+	var err error
+
+	// see if we have any patterns to pass through to go list.
+	if len(patterns) > 0 {
+		response, err = listfunc(cfg, patterns...)
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		response = &driverResponse{}
+	}
+
+	// Run go list for contains: patterns.
+	seenPkgs := make(map[string]*Package) // for deduplication. different containing queries could produce same packages
+	if len(containFiles) > 0 {
+		for _, pkg := range response.Packages {
+			seenPkgs[pkg.ID] = pkg
+		}
+	}
+	for _, f := range containFiles {
+		// TODO(matloob): Do only one query per directory.
+		fdir := filepath.Dir(f)
+		cfg.Dir = fdir
+		dirResponse, err := listfunc(cfg, ".")
+		if err != nil {
+			return nil, err
+		}
+		isRoot := make(map[string]bool, len(dirResponse.Roots))
+		for _, root := range dirResponse.Roots {
+			isRoot[root] = true
+		}
+		for _, pkg := range dirResponse.Packages {
+			// Add any new packages to the main set
+			// We don't bother to filter packages that will be dropped by the changes of roots,
+			// that will happen anyway during graph construction outside this function.
+			// Over-reporting packages is not a problem.
+			if _, ok := seenPkgs[pkg.ID]; !ok {
+				// it is a new package, just add it
+				seenPkgs[pkg.ID] = pkg
+				response.Packages = append(response.Packages, pkg)
+			}
+			// if the package was not a root one, it cannot have the file
+			if !isRoot[pkg.ID] {
+				continue
+			}
+			for _, pkgFile := range pkg.GoFiles {
+				if filepath.Base(f) == filepath.Base(pkgFile) {
+					response.Roots = append(response.Roots, pkg.ID)
+					break
+				}
+			}
+		}
+	}
+	return response, nil
+}
+
+// Fields must match go list;
+// see $GOROOT/src/cmd/go/internal/load/pkg.go.
+type jsonPackage struct {
+	ImportPath      string
+	Dir             string
+	Name            string
+	Export          string
+	GoFiles         []string
+	CompiledGoFiles []string
+	CFiles          []string
+	CgoFiles        []string
+	CXXFiles        []string
+	MFiles          []string
+	HFiles          []string
+	FFiles          []string
+	SFiles          []string
+	SwigFiles       []string
+	SwigCXXFiles    []string
+	SysoFiles       []string
+	Imports         []string
+	ImportMap       map[string]string
+	Deps            []string
+	TestGoFiles     []string
+	TestImports     []string
+	XTestGoFiles    []string
+	XTestImports    []string
+	ForTest         string // q in a "p [q.test]" package, else ""
+	DepOnly         bool
+}
+
+func otherFiles(p *jsonPackage) [][]string {
+	return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
+}
+
+// golistDriverCurrent uses the "go list" command to expand the
+// pattern words and return metadata for the specified packages.
+// dir may be "" and env may be nil, as per os/exec.Command.
+func golistDriverCurrent(cfg *Config, words ...string) (*driverResponse, error) {
+	// go list uses the following identifiers in ImportPath and Imports:
+	//
+	// 	"p"			-- importable package or main (command)
+	//      "q.test"		-- q's test executable
+	// 	"p [q.test]"		-- variant of p as built for q's test executable
+	//	"q_test [q.test]"	-- q's external test package
+	//
+	// The packages p that are built differently for a test q.test
+	// are q itself, plus any helpers used by the external test q_test,
+	// typically including "testing" and all its dependencies.
+
+	// Run "go list" for complete
+	// information on the specified packages.
+	buf, err := golist(cfg, golistargs(cfg, words))
+	if err != nil {
+		return nil, err
+	}
+	// Decode the JSON and convert it to Package form.
+	var response driverResponse
+	for dec := json.NewDecoder(buf); dec.More(); {
+		p := new(jsonPackage)
+		if err := dec.Decode(p); err != nil {
+			return nil, fmt.Errorf("JSON decoding failed: %v", err)
+		}
+
+		// Bad package?
+		if p.Name == "" {
+			// This could be due to:
+			// - no such package
+			// - package directory contains no Go source files
+			// - all package declarations are mangled
+			// - and possibly other things.
+			//
+			// For now, we throw it away and let later
+			// stages rediscover the problem, but this
+			// discards the error message computed by go list
+			// and computes a new one---by different logic:
+			// if only one of the package declarations is
+			// bad, for example, should we report an error
+			// in Metadata mode?
+			// Unless we parse and typecheck, we might not
+			// notice there's a problem.
+			//
+			// Perhaps we should save a map of PackageID to
+			// errors for such cases.
+			continue
+		}
+
+		id := p.ImportPath
+
+		// Extract the PkgPath from the package's ID.
+		pkgpath := id
+		if i := strings.IndexByte(id, ' '); i >= 0 {
+			pkgpath = id[:i]
+		}
+
+		if pkgpath == "unsafe" {
+			p.GoFiles = nil // ignore fake unsafe.go file
+		}
+
+		// Assume go list emits only absolute paths for Dir.
+		if !filepath.IsAbs(p.Dir) {
+			log.Fatalf("internal error: go list returned non-absolute Package.Dir: %s", p.Dir)
+		}
+
+		export := p.Export
+		if export != "" && !filepath.IsAbs(export) {
+			export = filepath.Join(p.Dir, export)
+		}
+
+		// imports
+		//
+		// Imports contains the IDs of all imported packages.
+		// ImportsMap records (path, ID) only where they differ.
+		ids := make(map[string]bool)
+		for _, id := range p.Imports {
+			ids[id] = true
+		}
+		imports := make(map[string]*Package)
+		for path, id := range p.ImportMap {
+			imports[path] = &Package{ID: id} // non-identity import
+			delete(ids, id)
+		}
+		for id := range ids {
+			if id == "C" {
+				continue
+			}
+
+			imports[id] = &Package{ID: id} // identity import
+		}
+		if !p.DepOnly {
+			response.Roots = append(response.Roots, id)
+		}
+		pkg := &Package{
+			ID:              id,
+			Name:            p.Name,
+			PkgPath:         pkgpath,
+			GoFiles:         absJoin(p.Dir, p.GoFiles, p.CgoFiles),
+			CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
+			OtherFiles:      absJoin(p.Dir, otherFiles(p)...),
+			Imports:         imports,
+			ExportFile:      export,
+		}
+		// TODO(matloob): Temporary hack since CompiledGoFiles isn't always set.
+		if len(pkg.CompiledGoFiles) == 0 {
+			pkg.CompiledGoFiles = pkg.GoFiles
+		}
+		response.Packages = append(response.Packages, pkg)
+	}
+
+	return &response, nil
+}
+
+// absJoin absolutizes and flattens the lists of files.
+func absJoin(dir string, fileses ...[]string) (res []string) {
+	for _, files := range fileses {
+		for _, file := range files {
+			if !filepath.IsAbs(file) {
+				file = filepath.Join(dir, file)
+			}
+			res = append(res, file)
+		}
+	}
+	return res
+}
+
+func golistargs(cfg *Config, words []string) []string {
+	fullargs := []string{
+		"list", "-e", "-json", "-compiled",
+		fmt.Sprintf("-test=%t", cfg.Tests),
+		fmt.Sprintf("-export=%t", usesExportData(cfg)),
+		fmt.Sprintf("-deps=%t", cfg.Mode >= LoadImports),
+	}
+	fullargs = append(fullargs, cfg.Flags...)
+	fullargs = append(fullargs, "--")
+	fullargs = append(fullargs, words...)
+	return fullargs
+}
+
+// golist returns the JSON-encoded result of a "go list args..." query.
+func golist(cfg *Config, args []string) (*bytes.Buffer, error) {
+	out := new(bytes.Buffer)
+	cmd := exec.CommandContext(cfg.Context, "go", args...)
+	cmd.Env = cfg.Env
+	cmd.Dir = cfg.Dir
+	cmd.Stdout = out
+	cmd.Stderr = new(bytes.Buffer)
+	if err := cmd.Run(); err != nil {
+		exitErr, ok := err.(*exec.ExitError)
+		if !ok {
+			// Catastrophic error:
+			// - executable not found
+			// - context cancellation
+			return nil, fmt.Errorf("couldn't exec 'go list': %s %T", err, err)
+		}
+
+		// Old go list?
+		if strings.Contains(fmt.Sprint(cmd.Stderr), "flag provided but not defined") {
+			return nil, goTooOldError{fmt.Errorf("unsupported version of go list: %s: %s", exitErr, cmd.Stderr)}
+		}
+
+		// Export mode entails a build.
+		// If that build fails, errors appear on stderr
+		// (despite the -e flag) and the Export field is blank.
+		// Do not fail in that case.
+		if !usesExportData(cfg) {
+			return nil, fmt.Errorf("go list: %s: %s", exitErr, cmd.Stderr)
+		}
+	}
+
+	// Print standard error output from "go list".
+	// Due to the -e flag, this should be empty.
+	// However, in -export mode it contains build errors.
+	// Should go list save build errors in the Package.Error JSON field?
+	// See https://github.com/golang/go/issues/26319.
+	// If so, then we should continue to print stderr as go list
+	// will be silent unless something unexpected happened.
+	// If not, perhaps we should suppress it to reduce noise.
+	if stderr := fmt.Sprint(cmd.Stderr); stderr != "" {
+		fmt.Fprintf(os.Stderr, "go list stderr <<%s>>\n", stderr)
+	}
+
+	// debugging
+	if false {
+		fmt.Fprintln(os.Stderr, out)
+	}
+
+	return out, nil
+}

vendor/golang.org/x/tools/go/packages/golist_fallback.go πŸ”—

@@ -0,0 +1,282 @@
+// Copyright 2018 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 packages
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"go/build"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	"golang.org/x/tools/go/internal/cgo"
+)
+
+// TODO(matloob): Delete this file once Go 1.12 is released.
+
+// This file provides backwards compatibility support for
+// loading for versions of Go earlier than 1.10.4. This support is meant to
+// assist with migration to the Package API until there's
+// widespread adoption of these newer Go versions.
+// This support will be removed once Go 1.12 is released
+// in Q1 2019.
+
+func golistDriverFallback(cfg *Config, words ...string) (*driverResponse, error) {
+	original, deps, err := getDeps(cfg, words...)
+	if err != nil {
+		return nil, err
+	}
+
+	var tmpdir string // used for generated cgo files
+
+	var response driverResponse
+	addPackage := func(p *jsonPackage) {
+		if p.Name == "" {
+			return
+		}
+
+		id := p.ImportPath
+		isRoot := original[id] != nil
+		pkgpath := id
+
+		if pkgpath == "unsafe" {
+			p.GoFiles = nil // ignore fake unsafe.go file
+		}
+
+		importMap := func(importlist []string) map[string]*Package {
+			importMap := make(map[string]*Package)
+			for _, id := range importlist {
+
+				if id == "C" {
+					for _, path := range []string{"unsafe", "syscall", "runtime/cgo"} {
+						if pkgpath != path && importMap[path] == nil {
+							importMap[path] = &Package{ID: path}
+						}
+					}
+					continue
+				}
+				importMap[vendorlessPath(id)] = &Package{ID: id}
+			}
+			return importMap
+		}
+		compiledGoFiles := absJoin(p.Dir, p.GoFiles)
+		// Use a function to simplify control flow. It's just a bunch of gotos.
+		var cgoErrors []error
+		processCgo := func() bool {
+			// Suppress any cgo errors. Any relevant errors will show up in typechecking.
+			// TODO(matloob): Skip running cgo if Mode < LoadTypes.
+			if tmpdir == "" {
+				if tmpdir, err = ioutil.TempDir("", "gopackages"); err != nil {
+					cgoErrors = append(cgoErrors, err)
+					return false
+				}
+			}
+			outdir := filepath.Join(tmpdir, strings.Replace(p.ImportPath, "/", "_", -1))
+			if err := os.Mkdir(outdir, 0755); err != nil {
+				cgoErrors = append(cgoErrors, err)
+				return false
+			}
+			files, _, err := runCgo(p.Dir, outdir, cfg.Env)
+			if err != nil {
+				cgoErrors = append(cgoErrors, err)
+				return false
+			}
+			compiledGoFiles = append(compiledGoFiles, files...)
+			return true
+		}
+		if len(p.CgoFiles) == 0 || !processCgo() {
+			compiledGoFiles = append(compiledGoFiles, absJoin(p.Dir, p.CgoFiles)...) // Punt to typechecker.
+		}
+		if isRoot {
+			response.Roots = append(response.Roots, id)
+		}
+		response.Packages = append(response.Packages, &Package{
+			ID:              id,
+			Name:            p.Name,
+			GoFiles:         absJoin(p.Dir, p.GoFiles, p.CgoFiles),
+			CompiledGoFiles: compiledGoFiles,
+			OtherFiles:      absJoin(p.Dir, otherFiles(p)...),
+			PkgPath:         pkgpath,
+			Imports:         importMap(p.Imports),
+			// TODO(matloob): set errors on the Package to cgoErrors
+		})
+		if cfg.Tests {
+			testID := fmt.Sprintf("%s [%s.test]", id, id)
+			if len(p.TestGoFiles) > 0 || len(p.XTestGoFiles) > 0 {
+				if isRoot {
+					response.Roots = append(response.Roots, testID)
+				}
+				response.Packages = append(response.Packages, &Package{
+					ID:              testID,
+					Name:            p.Name,
+					GoFiles:         absJoin(p.Dir, p.GoFiles, p.CgoFiles, p.TestGoFiles),
+					CompiledGoFiles: append(compiledGoFiles, absJoin(p.Dir, p.TestGoFiles)...),
+					OtherFiles:      absJoin(p.Dir, otherFiles(p)...),
+					PkgPath:         pkgpath,
+					Imports:         importMap(append(p.Imports, p.TestImports...)),
+					// TODO(matloob): set errors on the Package to cgoErrors
+				})
+				if len(p.XTestGoFiles) > 0 {
+					xtestID := fmt.Sprintf("%s_test [%s.test]", id, id)
+					if isRoot {
+						response.Roots = append(response.Roots, xtestID)
+					}
+					for i, imp := range p.XTestImports {
+						if imp == p.ImportPath {
+							p.XTestImports[i] = testID
+							break
+						}
+					}
+					response.Packages = append(response.Packages, &Package{
+						ID:              xtestID,
+						Name:            p.Name + "_test",
+						GoFiles:         absJoin(p.Dir, p.XTestGoFiles),
+						CompiledGoFiles: absJoin(p.Dir, p.XTestGoFiles),
+						PkgPath:         pkgpath,
+						Imports:         importMap(p.XTestImports),
+					})
+				}
+			}
+		}
+	}
+
+	for _, pkg := range original {
+		addPackage(pkg)
+	}
+	if cfg.Mode < LoadImports || len(deps) == 0 {
+		return &response, nil
+	}
+
+	buf, err := golist(cfg, golistArgsFallback(cfg, deps))
+	if err != nil {
+		return nil, err
+	}
+
+	// Decode the JSON and convert it to Package form.
+	for dec := json.NewDecoder(buf); dec.More(); {
+		p := new(jsonPackage)
+		if err := dec.Decode(p); err != nil {
+			return nil, fmt.Errorf("JSON decoding failed: %v", err)
+		}
+
+		addPackage(p)
+	}
+
+	return &response, nil
+}
+
+// vendorlessPath returns the devendorized version of the import path ipath.
+// For example, VendorlessPath("foo/bar/vendor/a/b") returns "a/b".
+// Copied from golang.org/x/tools/imports/fix.go.
+func vendorlessPath(ipath string) string {
+	// Devendorize for use in import statement.
+	if i := strings.LastIndex(ipath, "/vendor/"); i >= 0 {
+		return ipath[i+len("/vendor/"):]
+	}
+	if strings.HasPrefix(ipath, "vendor/") {
+		return ipath[len("vendor/"):]
+	}
+	return ipath
+}
+
+// getDeps runs an initial go list to determine all the dependency packages.
+func getDeps(cfg *Config, words ...string) (originalSet map[string]*jsonPackage, deps []string, err error) {
+	buf, err := golist(cfg, golistArgsFallback(cfg, words))
+	if err != nil {
+		return nil, nil, err
+	}
+
+	depsSet := make(map[string]bool)
+	originalSet = make(map[string]*jsonPackage)
+	var testImports []string
+
+	// Extract deps from the JSON.
+	for dec := json.NewDecoder(buf); dec.More(); {
+		p := new(jsonPackage)
+		if err := dec.Decode(p); err != nil {
+			return nil, nil, fmt.Errorf("JSON decoding failed: %v", err)
+		}
+
+		originalSet[p.ImportPath] = p
+		for _, dep := range p.Deps {
+			depsSet[dep] = true
+		}
+		if cfg.Tests {
+			// collect the additional imports of the test packages.
+			pkgTestImports := append(p.TestImports, p.XTestImports...)
+			for _, imp := range pkgTestImports {
+				if depsSet[imp] {
+					continue
+				}
+				depsSet[imp] = true
+				testImports = append(testImports, imp)
+			}
+		}
+	}
+	// Get the deps of the packages imported by tests.
+	if len(testImports) > 0 {
+		buf, err = golist(cfg, golistArgsFallback(cfg, testImports))
+		if err != nil {
+			return nil, nil, err
+		}
+		// Extract deps from the JSON.
+		for dec := json.NewDecoder(buf); dec.More(); {
+			p := new(jsonPackage)
+			if err := dec.Decode(p); err != nil {
+				return nil, nil, fmt.Errorf("JSON decoding failed: %v", err)
+			}
+			for _, dep := range p.Deps {
+				depsSet[dep] = true
+			}
+		}
+	}
+
+	for orig := range originalSet {
+		delete(depsSet, orig)
+	}
+
+	deps = make([]string, 0, len(depsSet))
+	for dep := range depsSet {
+		deps = append(deps, dep)
+	}
+	sort.Strings(deps) // ensure output is deterministic
+	return originalSet, deps, nil
+}
+
+func golistArgsFallback(cfg *Config, words []string) []string {
+	fullargs := []string{"list", "-e", "-json"}
+	fullargs = append(fullargs, cfg.Flags...)
+	fullargs = append(fullargs, "--")
+	fullargs = append(fullargs, words...)
+	return fullargs
+}
+
+func runCgo(pkgdir, tmpdir string, env []string) (files, displayfiles []string, err error) {
+	// Use go/build to open cgo files and determine the cgo flags, etc, from them.
+	// This is tricky so it's best to avoid reimplementing as much as we can, and
+	// we plan to delete this support once Go 1.12 is released anyways.
+	// TODO(matloob): This isn't completely correct because we're using the Default
+	// context. Perhaps we should more accurately fill in the context.
+	bp, err := build.ImportDir(pkgdir, build.ImportMode(0))
+	if err != nil {
+		return nil, nil, err
+	}
+	for _, ev := range env {
+		if v := strings.TrimPrefix(ev, "CGO_CPPFLAGS"); v != ev {
+			bp.CgoCPPFLAGS = append(bp.CgoCPPFLAGS, strings.Fields(v)...)
+		} else if v := strings.TrimPrefix(ev, "CGO_CFLAGS"); v != ev {
+			bp.CgoCFLAGS = append(bp.CgoCFLAGS, strings.Fields(v)...)
+		} else if v := strings.TrimPrefix(ev, "CGO_CXXFLAGS"); v != ev {
+			bp.CgoCXXFLAGS = append(bp.CgoCXXFLAGS, strings.Fields(v)...)
+		} else if v := strings.TrimPrefix(ev, "CGO_LDFLAGS"); v != ev {
+			bp.CgoLDFLAGS = append(bp.CgoLDFLAGS, strings.Fields(v)...)
+		}
+	}
+	return cgo.Run(bp, pkgdir, tmpdir, true)
+}

vendor/golang.org/x/tools/go/packages/packages.go πŸ”—

@@ -0,0 +1,824 @@
+// Copyright 2018 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 packages
+
+// See doc.go for package documentation and implementation notes.
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"go/types"
+	"log"
+	"os"
+	"sync"
+
+	"golang.org/x/tools/go/gcexportdata"
+)
+
+// A LoadMode specifies the amount of detail to return when loading packages.
+// The modes are all strictly additive, as you go through the list it increases
+// the amount of information available to you, but may also increase the cost
+// of collecting the information.
+// Load is always allowed to return more information than requested.
+type LoadMode int
+
+const (
+	// LoadFiles finds the packages and computes their source file lists.
+	// Package fields: ID, Name, Errors, GoFiles, OtherFiles.
+	LoadFiles LoadMode = iota
+
+	// LoadImports adds import information for each package
+	// and its dependencies.
+	// Package fields added: Imports.
+	LoadImports
+
+	// LoadTypes adds type information for the package's exported symbols.
+	// Package fields added: Types, Fset, IllTyped.
+	// This will use the type information provided by the build system if
+	// possible, and the ExportFile field may be filled in.
+	LoadTypes
+
+	// LoadSyntax adds typed syntax trees for the packages matching the patterns.
+	// Package fields added: Syntax, TypesInfo, for direct pattern matches only.
+	LoadSyntax
+
+	// LoadAllSyntax adds typed syntax trees for the packages matching the patterns
+	// and all dependencies.
+	// Package fields added: Syntax, TypesInfo, for all packages in import graph.
+	LoadAllSyntax
+)
+
+// An Config specifies details about how packages should be loaded.
+// Calls to Load do not modify this struct.
+type Config struct {
+	// Mode controls the level of information returned for each package.
+	Mode LoadMode
+
+	// Context specifies the context for the load operation.
+	// If the context is cancelled, the loader may stop early
+	// and return an ErrCancelled error.
+	// If Context is nil, the load cannot be cancelled.
+	Context context.Context
+
+	// Dir is the directory in which to run the build system tool
+	// that provides information about the packages.
+	// If Dir is empty, the tool is run in the current directory.
+	Dir string
+
+	// Env is the environment to use when invoking the build system tool.
+	// If Env is nil, the current environment is used.
+	// Like in os/exec's Cmd, only the last value in the slice for
+	// each environment key is used. To specify the setting of only
+	// a few variables, append to the current environment, as in:
+	//
+	//	opt.Env = append(os.Environ(), "GOOS=plan9", "GOARCH=386")
+	//
+	Env []string
+
+	// Flags is a list of command-line flags to be passed through to
+	// the underlying query tool.
+	Flags []string
+
+	// Error is called for each error encountered during package loading.
+	// It must be safe to call Error simultaneously from multiple goroutines.
+	// In addition to calling Error, the loader will record each error
+	// in the corresponding Package's Errors list.
+	// If Error is nil, the loader will print errors to os.Stderr.
+	// To disable printing of errors, set opt.Error = func(error){}.
+	// TODO(rsc): What happens in the Metadata loader? Currently nothing.
+	Error func(error)
+
+	// Fset is the token.FileSet to use when parsing source files or
+	// type information provided by the build system.
+	// If Fset is nil, the loader will create one.
+	Fset *token.FileSet
+
+	// ParseFile is called to read and parse each file
+	// when preparing a package's type-checked syntax tree.
+	// It must be safe to call ParseFile simultaneously from multiple goroutines.
+	// If ParseFile is nil, the loader will uses parser.ParseFile.
+	//
+	// Setting ParseFile to a custom implementation can allow
+	// providing alternate file content in order to type-check
+	// unsaved text editor buffers, or to selectively eliminate
+	// unwanted function bodies to reduce the amount of work
+	// done by the type checker.
+	ParseFile func(fset *token.FileSet, filename string) (*ast.File, error)
+
+	// If Tests is set, the loader includes not just the packages
+	// matching a particular pattern but also any related test packages,
+	// including test-only variants of the package and the test executable.
+	//
+	// For example, when using the go command, loading "fmt" with Tests=true
+	// returns four packages, with IDs "fmt" (the standard package),
+	// "fmt [fmt.test]" (the package as compiled for the test),
+	// "fmt_test" (the test functions from source files in package fmt_test),
+	// and "fmt.test" (the test binary).
+	//
+	// In build systems with explicit names for tests,
+	// setting Tests may have no effect.
+	Tests bool
+
+	// TypeChecker provides additional configuration for type-checking syntax trees.
+	//
+	// It is used for all packages in LoadAllSyntax mode,
+	// and for the packages matching the patterns, but not their dependencies,
+	// in LoadSyntax mode.
+	//
+	// The TypeChecker.Error function is ignored:
+	// errors are reported using the Error function defined above.
+	//
+	// The TypeChecker.Importer function is ignored:
+	// the loader defines an appropriate importer.
+	//
+	// TODO(rsc): TypeChecker.Sizes should use the same sizes as the main build.
+	// Derive them from the runtime?
+	TypeChecker types.Config
+}
+
+// driver is the type for functions that query the build system for the
+// packages named by the patterns.
+type driver func(cfg *Config, patterns ...string) (*driverResponse, error)
+
+// driverResponse contains the results for a driver query.
+type driverResponse struct {
+	// Roots is the set of package IDs that make up the root packages.
+	// We have to encode this separately because when we encode a single package
+	// we cannot know if it is one of the roots as that requires knowledge of the
+	// graph it is part of.
+	Roots []string `json:",omitempty"`
+
+	// Packages is the full set of packages in the graph.
+	// The packages are not connected into a graph.
+	// The Imports if populated will be stubs that only have their ID set.
+	// Imports will be connected and then type and syntax information added in a
+	// later pass (see refine).
+	Packages []*Package
+}
+
+// Load loads and returns the Go packages named by the given patterns.
+//
+// Config specifies loading options;
+// nil behaves the same as an empty Config.
+//
+// Load returns an error if any of the patterns was invalid
+// as defined by the underlying build system.
+// It may return an empty list of packages without an error,
+// for instance for an empty expansion of a valid wildcard.
+func Load(cfg *Config, patterns ...string) ([]*Package, error) {
+	l := newLoader(cfg)
+	response, err := defaultDriver(&l.Config, patterns...)
+	if err != nil {
+		return nil, err
+	}
+	return l.refine(response.Roots, response.Packages...)
+}
+
+// defaultDriver is a driver that looks for an external driver binary, and if
+// it does not find it falls back to the built in go list driver.
+func defaultDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
+	driver := findExternalDriver(cfg)
+	if driver == nil {
+		driver = goListDriver
+	}
+	return driver(cfg, patterns...)
+}
+
+// A Package describes a single loaded Go package.
+type Package struct {
+	// ID is a unique identifier for a package,
+	// in a syntax provided by the underlying build system.
+	//
+	// Because the syntax varies based on the build system,
+	// clients should treat IDs as opaque and not attempt to
+	// interpret them.
+	ID string
+
+	// Name is the package name as it appears in the package source code.
+	Name string
+
+	// This is the package path as used by the types package.
+	// This is used to map entries in the type information back to the package
+	// they come from.
+	PkgPath string
+
+	// Errors lists any errors encountered while loading the package.
+	// TODO(rsc): Say something about the errors or at least their Strings,
+	// as far as file:line being at the beginning and so on.
+	Errors []error
+
+	// GoFiles lists the absolute file paths of the package's Go source files.
+	GoFiles []string
+
+	// CompiledGoFiles lists the absolute file paths of the package's source
+	// files that were presented to the compiler.
+	// This may differ from GoFiles if files are processed before compilation.
+	CompiledGoFiles []string
+
+	// OtherFiles lists the absolute file paths of the package's non-Go source files,
+	// including assembly, C, C++, Fortran, Objective-C, SWIG, and so on.
+	OtherFiles []string
+
+	// ExportFile is the absolute path to a file containing the type information
+	// provided by the build system.
+	ExportFile string
+
+	// Imports maps import paths appearing in the package's Go source files
+	// to corresponding loaded Packages.
+	Imports map[string]*Package
+
+	// Types is the type information for the package.
+	// Modes LoadTypes and above set this field for all packages.
+	//
+	// TODO(adonovan): all packages? In Types mode this entails
+	// asymptotically more export data processing than is required
+	// to load the requested packages. Is that what we want?
+	Types *types.Package
+
+	// Fset provides position information for Types, TypesInfo, and Syntax.
+	// Modes LoadTypes and above set this field for all packages.
+	Fset *token.FileSet
+
+	// IllTyped indicates whether the package has any type errors.
+	// Modes LoadTypes and above set this field for all packages.
+	IllTyped bool
+
+	// Syntax is the package's syntax trees, for the files listed in GoFiles.
+	//
+	// Mode LoadSyntax set this field for packages matching the patterns.
+	// Mode LoadSyntaxAll sets this field for all packages, including dependencies.
+	Syntax []*ast.File
+
+	// TypesInfo is the type-checking results for the package's syntax trees.
+	// It is set only when Syntax is set.
+	TypesInfo *types.Info
+}
+
+// packageError is used to serialize structured errors as much as possible.
+// This has members compatible with the golist error type, and possibly some
+// more if we need other error information to survive.
+type packageError struct {
+	Pos string // position of error
+	Err string // the error itself
+}
+
+func (e *packageError) Error() string {
+	return e.Pos + ": " + e.Err
+}
+
+// flatPackage is the JSON form of Package
+// It drops all the type and syntax fields, and transforms the Imports and Errors
+type flatPackage struct {
+	ID              string
+	Name            string            `json:",omitempty"`
+	PkgPath         string            `json:",omitempty"`
+	Errors          []*packageError   `json:",omitempty"`
+	GoFiles         []string          `json:",omitempty"`
+	CompiledGoFiles []string          `json:",omitempty"`
+	OtherFiles      []string          `json:",omitempty"`
+	ExportFile      string            `json:",omitempty"`
+	Imports         map[string]string `json:",omitempty"`
+}
+
+// MarshalJSON returns the Package in its JSON form.
+// For the most part, the structure fields are written out unmodified, and
+// the type and syntax fields are skipped.
+// The imports are written out as just a map of path to package id.
+// The errors are written using a custom type that tries to preserve the
+// structure of error types we know about.
+// This method exists to enable support for additional build systems.  It is
+// not intended for use by clients of the API and we may change the format.
+func (p *Package) MarshalJSON() ([]byte, error) {
+	flat := &flatPackage{
+		ID:              p.ID,
+		Name:            p.Name,
+		PkgPath:         p.PkgPath,
+		GoFiles:         p.GoFiles,
+		CompiledGoFiles: p.CompiledGoFiles,
+		OtherFiles:      p.OtherFiles,
+		ExportFile:      p.ExportFile,
+	}
+	if len(p.Errors) > 0 {
+		flat.Errors = make([]*packageError, len(p.Errors))
+		for i, err := range p.Errors {
+			//TODO: best effort mapping of errors to the serialized form
+			switch err := err.(type) {
+			case *packageError:
+				flat.Errors[i] = err
+			default:
+				flat.Errors[i] = &packageError{Err: err.Error()}
+			}
+		}
+	}
+	if len(p.Imports) > 0 {
+		flat.Imports = make(map[string]string, len(p.Imports))
+		for path, ipkg := range p.Imports {
+			flat.Imports[path] = ipkg.ID
+		}
+	}
+	return json.Marshal(flat)
+}
+
+// UnmarshalJSON reads in a Package from its JSON format.
+// See MarshalJSON for details about the format accepted.
+func (p *Package) UnmarshalJSON(b []byte) error {
+	flat := &flatPackage{}
+	if err := json.Unmarshal(b, &flat); err != nil {
+		return err
+	}
+	*p = Package{
+		ID:              flat.ID,
+		Name:            flat.Name,
+		PkgPath:         flat.PkgPath,
+		GoFiles:         flat.GoFiles,
+		CompiledGoFiles: flat.CompiledGoFiles,
+		OtherFiles:      flat.OtherFiles,
+		ExportFile:      flat.ExportFile,
+	}
+	if len(flat.Errors) > 0 {
+		p.Errors = make([]error, len(flat.Errors))
+		for i, err := range flat.Errors {
+			p.Errors[i] = err
+		}
+	}
+	if len(flat.Imports) > 0 {
+		p.Imports = make(map[string]*Package, len(flat.Imports))
+		for path, id := range flat.Imports {
+			p.Imports[path] = &Package{ID: id}
+		}
+	}
+	return nil
+}
+
+func (p *Package) String() string { return p.ID }
+
+// loaderPackage augments Package with state used during the loading phase
+type loaderPackage struct {
+	*Package
+	importErrors  map[string]error // maps each bad import to its error
+	loadOnce      sync.Once
+	color         uint8 // for cycle detection
+	mark, needsrc bool  // used when Mode >= LoadTypes
+}
+
+// loader holds the working state of a single call to load.
+type loader struct {
+	pkgs map[string]*loaderPackage
+	Config
+	exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
+}
+
+func newLoader(cfg *Config) *loader {
+	ld := &loader{}
+	if cfg != nil {
+		ld.Config = *cfg
+	}
+	if ld.Context == nil {
+		ld.Context = context.Background()
+	}
+	if ld.Dir == "" {
+		if dir, err := os.Getwd(); err == nil {
+			ld.Dir = dir
+		}
+	}
+
+	if ld.Mode >= LoadTypes {
+		if ld.Fset == nil {
+			ld.Fset = token.NewFileSet()
+		}
+
+		// Error and ParseFile are required even in LoadTypes mode
+		// because we load source if export data is missing.
+
+		if ld.Error == nil {
+			ld.Error = func(e error) {
+				fmt.Fprintln(os.Stderr, e)
+			}
+		}
+
+		if ld.ParseFile == nil {
+			ld.ParseFile = func(fset *token.FileSet, filename string) (*ast.File, error) {
+				const mode = parser.AllErrors | parser.ParseComments
+				return parser.ParseFile(fset, filename, nil, mode)
+			}
+		}
+	}
+	return ld
+}
+
+// refine connects the supplied packages into a graph and then adds type and
+// and syntax information as requested by the LoadMode.
+func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
+	if len(list) == 0 {
+		return nil, fmt.Errorf("packages not found")
+	}
+	isRoot := make(map[string]bool, len(roots))
+	for _, root := range roots {
+		isRoot[root] = true
+	}
+	ld.pkgs = make(map[string]*loaderPackage)
+	// first pass, fixup and build the map and roots
+	var initial []*loaderPackage
+	for _, pkg := range list {
+		lpkg := &loaderPackage{
+			Package: pkg,
+			needsrc: ld.Mode >= LoadAllSyntax ||
+				(ld.Mode >= LoadSyntax && isRoot[pkg.ID]) ||
+				(pkg.ExportFile == "" && pkg.PkgPath != "unsafe"),
+		}
+		ld.pkgs[lpkg.ID] = lpkg
+		if isRoot[lpkg.ID] {
+			initial = append(initial, lpkg)
+		}
+	}
+
+	// Materialize the import graph.
+
+	const (
+		white = 0 // new
+		grey  = 1 // in progress
+		black = 2 // complete
+	)
+
+	// visit traverses the import graph, depth-first,
+	// and materializes the graph as Packages.Imports.
+	//
+	// Valid imports are saved in the Packages.Import map.
+	// Invalid imports (cycles and missing nodes) are saved in the importErrors map.
+	// Thus, even in the presence of both kinds of errors, the Import graph remains a DAG.
+	//
+	// visit returns whether the package needs src or has a transitive
+	// dependency on a package that does. These are the only packages
+	// for which we load source code.
+	var stack []*loaderPackage
+	var visit func(lpkg *loaderPackage) bool
+	visit = func(lpkg *loaderPackage) bool {
+		switch lpkg.color {
+		case black:
+			return lpkg.needsrc
+		case grey:
+			panic("internal error: grey node")
+		}
+		lpkg.color = grey
+		stack = append(stack, lpkg) // push
+		stubs := lpkg.Imports       // the structure form has only stubs with the ID in the Imports
+		lpkg.Imports = make(map[string]*Package, len(stubs))
+		for importPath, ipkg := range stubs {
+			var importErr error
+			imp := ld.pkgs[ipkg.ID]
+			if imp == nil {
+				// (includes package "C" when DisableCgo)
+				importErr = fmt.Errorf("missing package: %q", ipkg.ID)
+			} else if imp.color == grey {
+				importErr = fmt.Errorf("import cycle: %s", stack)
+			}
+			if importErr != nil {
+				if lpkg.importErrors == nil {
+					lpkg.importErrors = make(map[string]error)
+				}
+				lpkg.importErrors[importPath] = importErr
+				continue
+			}
+
+			if visit(imp) {
+				lpkg.needsrc = true
+			}
+			lpkg.Imports[importPath] = imp.Package
+		}
+
+		stack = stack[:len(stack)-1] // pop
+		lpkg.color = black
+
+		return lpkg.needsrc
+	}
+
+	if ld.Mode < LoadImports {
+		//we do this to drop the stub import packages that we are not even going to try to resolve
+		for _, lpkg := range initial {
+			lpkg.Imports = nil
+		}
+	} else {
+		// For each initial package, create its import DAG.
+		for _, lpkg := range initial {
+			visit(lpkg)
+		}
+	}
+	// Load type data if needed, starting at
+	// the initial packages (roots of the import DAG).
+	if ld.Mode >= LoadTypes {
+		var wg sync.WaitGroup
+		for _, lpkg := range initial {
+			wg.Add(1)
+			go func(lpkg *loaderPackage) {
+				ld.loadRecursive(lpkg)
+				wg.Done()
+			}(lpkg)
+		}
+		wg.Wait()
+	}
+
+	result := make([]*Package, len(initial))
+	for i, lpkg := range initial {
+		result[i] = lpkg.Package
+	}
+	return result, nil
+}
+
+// loadRecursive loads the specified package and its dependencies,
+// recursively, in parallel, in topological order.
+// It is atomic and idempotent.
+// Precondition: ld.Mode >= LoadTypes.
+func (ld *loader) loadRecursive(lpkg *loaderPackage) {
+	lpkg.loadOnce.Do(func() {
+		// Load the direct dependencies, in parallel.
+		var wg sync.WaitGroup
+		for _, ipkg := range lpkg.Imports {
+			imp := ld.pkgs[ipkg.ID]
+			wg.Add(1)
+			go func(imp *loaderPackage) {
+				ld.loadRecursive(imp)
+				wg.Done()
+			}(imp)
+		}
+		wg.Wait()
+
+		ld.loadPackage(lpkg)
+	})
+}
+
+// loadPackage loads the specified package.
+// It must be called only once per Package,
+// after immediate dependencies are loaded.
+// Precondition: ld.Mode >= LoadTypes.
+func (ld *loader) loadPackage(lpkg *loaderPackage) {
+	if lpkg.PkgPath == "unsafe" {
+		// Fill in the blanks to avoid surprises.
+		lpkg.Types = types.Unsafe
+		lpkg.Fset = ld.Fset
+		lpkg.Syntax = []*ast.File{}
+		lpkg.TypesInfo = new(types.Info)
+		return
+	}
+
+	// Call NewPackage directly with explicit name.
+	// This avoids skew between golist and go/types when the files'
+	// package declarations are inconsistent.
+	lpkg.Types = types.NewPackage(lpkg.PkgPath, lpkg.Name)
+
+	if !lpkg.needsrc {
+		ld.loadFromExportData(lpkg)
+		return // not a source package, don't get syntax trees
+	}
+
+	hardErrors := false
+	appendError := func(err error) {
+		if terr, ok := err.(types.Error); ok && terr.Soft {
+			// Don't mark the package as bad.
+		} else {
+			hardErrors = true
+		}
+		ld.Error(err)
+		lpkg.Errors = append(lpkg.Errors, err)
+	}
+
+	files, errs := ld.parseFiles(lpkg.CompiledGoFiles)
+	for _, err := range errs {
+		appendError(err)
+	}
+
+	lpkg.Fset = ld.Fset
+	lpkg.Syntax = files
+
+	lpkg.TypesInfo = &types.Info{
+		Types:      make(map[ast.Expr]types.TypeAndValue),
+		Defs:       make(map[*ast.Ident]types.Object),
+		Uses:       make(map[*ast.Ident]types.Object),
+		Implicits:  make(map[ast.Node]types.Object),
+		Scopes:     make(map[ast.Node]*types.Scope),
+		Selections: make(map[*ast.SelectorExpr]*types.Selection),
+	}
+
+	// Copy the prototype types.Config as it must vary across Packages.
+	tc := ld.TypeChecker // copy
+	tc.Importer = importerFunc(func(path string) (*types.Package, error) {
+		if path == "unsafe" {
+			return types.Unsafe, nil
+		}
+
+		// The imports map is keyed by import path.
+		ipkg := lpkg.Imports[path]
+		if ipkg == nil {
+			if err := lpkg.importErrors[path]; err != nil {
+				return nil, err
+			}
+			// There was skew between the metadata and the
+			// import declarations, likely due to an edit
+			// race, or because the ParseFile feature was
+			// used to supply alternative file contents.
+			return nil, fmt.Errorf("no metadata for %s", path)
+		}
+
+		if ipkg.Types != nil && ipkg.Types.Complete() {
+			return ipkg.Types, nil
+		}
+		log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg)
+		panic("unreachable")
+	})
+	tc.Error = appendError
+
+	// type-check
+	types.NewChecker(&tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax)
+
+	lpkg.importErrors = nil // no longer needed
+
+	// If !Cgo, the type-checker uses FakeImportC mode, so
+	// it doesn't invoke the importer for import "C",
+	// nor report an error for the import,
+	// or for any undefined C.f reference.
+	// We must detect this explicitly and correctly
+	// mark the package as IllTyped (by reporting an error).
+	// TODO(adonovan): if these errors are annoying,
+	// we could just set IllTyped quietly.
+	if tc.FakeImportC {
+	outer:
+		for _, f := range lpkg.Syntax {
+			for _, imp := range f.Imports {
+				if imp.Path.Value == `"C"` {
+					appendError(fmt.Errorf(`%s: import "C" ignored`,
+						lpkg.Fset.Position(imp.Pos())))
+					break outer
+				}
+			}
+		}
+	}
+
+	// Record accumulated errors.
+	for _, imp := range lpkg.Imports {
+		if imp.IllTyped {
+			hardErrors = true
+			break
+		}
+	}
+
+	lpkg.IllTyped = hardErrors
+}
+
+// An importFunc is an implementation of the single-method
+// types.Importer interface based on a function value.
+type importerFunc func(path string) (*types.Package, error)
+
+func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
+
+// We use a counting semaphore to limit
+// the number of parallel I/O calls per process.
+var ioLimit = make(chan bool, 20)
+
+// parseFiles reads and parses the Go source files 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.
+//
+// Because files are scanned in parallel, the token.Pos
+// positions of the resulting ast.Files are not ordered.
+//
+func (ld *loader) parseFiles(filenames []string) ([]*ast.File, []error) {
+	var wg sync.WaitGroup
+	n := len(filenames)
+	parsed := make([]*ast.File, n)
+	errors := make([]error, n)
+	for i, file := range filenames {
+		wg.Add(1)
+		go func(i int, filename string) {
+			ioLimit <- true // wait
+			// ParseFile may return both an AST and an error.
+			parsed[i], errors[i] = ld.ParseFile(ld.Fset, filename)
+			<-ioLimit // signal
+			wg.Done()
+		}(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
+}
+
+// loadFromExportData returns type information for the specified
+// package, loading it from an export data file on the first request.
+func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error) {
+	if lpkg.PkgPath == "" {
+		log.Fatalf("internal error: Package %s has no PkgPath", lpkg)
+	}
+
+	// Because gcexportdata.Read has the potential to create or
+	// modify the types.Package for each node in the transitive
+	// closure of dependencies of lpkg, all exportdata operations
+	// must be sequential. (Finer-grained locking would require
+	// changes to the gcexportdata API.)
+	//
+	// The exportMu lock guards the Package.Pkg field and the
+	// types.Package it points to, for each Package in the graph.
+	//
+	// Not all accesses to Package.Pkg need to be protected by exportMu:
+	// graph ordering ensures that direct dependencies of source
+	// packages are fully loaded before the importer reads their Pkg field.
+	ld.exportMu.Lock()
+	defer ld.exportMu.Unlock()
+
+	if tpkg := lpkg.Types; tpkg != nil && tpkg.Complete() {
+		return tpkg, nil // cache hit
+	}
+
+	lpkg.IllTyped = true // fail safe
+
+	if lpkg.ExportFile == "" {
+		// Errors while building export data will have been printed to stderr.
+		return nil, fmt.Errorf("no export data file")
+	}
+	f, err := os.Open(lpkg.ExportFile)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+
+	// Read gc export data.
+	//
+	// We don't currently support gccgo export data because all
+	// underlying workspaces use the gc toolchain. (Even build
+	// systems that support gccgo don't use it for workspace
+	// queries.)
+	r, err := gcexportdata.NewReader(f)
+	if err != nil {
+		return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err)
+	}
+
+	// Build the view.
+	//
+	// The gcexportdata machinery has no concept of package ID.
+	// It identifies packages by their PkgPath, which although not
+	// globally unique is unique within the scope of one invocation
+	// of the linker, type-checker, or gcexportdata.
+	//
+	// So, we must build a PkgPath-keyed view of the global
+	// (conceptually ID-keyed) cache of packages and pass it to
+	// gcexportdata. The view must contain every existing
+	// package that might possibly be mentioned by the
+	// current package---its transitive closure.
+	//
+	// TODO(adonovan): it would be more simpler and more efficient
+	// if the export data machinery invoked a callback to
+	// get-or-create a package instead of a map.
+	//
+	view := make(map[string]*types.Package) // view seen by gcexportdata
+	seen := make(map[*loaderPackage]bool)   // all visited packages
+	var visit func(pkgs map[string]*Package)
+	visit = func(pkgs map[string]*Package) {
+		for _, p := range pkgs {
+			lpkg := ld.pkgs[p.ID]
+			if !seen[lpkg] {
+				seen[lpkg] = true
+				view[lpkg.PkgPath] = lpkg.Types
+				visit(lpkg.Imports)
+			}
+		}
+	}
+	visit(lpkg.Imports)
+
+	// Parse the export data.
+	// (May create/modify packages in view.)
+	tpkg, err := gcexportdata.Read(r, ld.Fset, view, lpkg.PkgPath)
+	if err != nil {
+		return nil, fmt.Errorf("reading %s: %v", lpkg.ExportFile, err)
+	}
+
+	lpkg.Types = tpkg
+	lpkg.IllTyped = false
+
+	return tpkg, nil
+}
+
+func usesExportData(cfg *Config) bool {
+	return LoadTypes <= cfg.Mode && cfg.Mode < LoadAllSyntax
+}

vendor/golang.org/x/tools/go/types/typeutil/imports.go πŸ”—

@@ -0,0 +1,31 @@
+// 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 typeutil
+
+import "go/types"
+
+// Dependencies returns all dependencies of the specified packages.
+//
+// Dependent packages appear in topological order: if package P imports
+// package Q, Q appears earlier than P in the result.
+// The algorithm follows import statements in the order they
+// appear in the source code, so the result is a total order.
+//
+func Dependencies(pkgs ...*types.Package) []*types.Package {
+	var result []*types.Package
+	seen := make(map[*types.Package]bool)
+	var visit func(pkgs []*types.Package)
+	visit = func(pkgs []*types.Package) {
+		for _, p := range pkgs {
+			if !seen[p] {
+				seen[p] = true
+				visit(p.Imports())
+				result = append(result, p)
+			}
+		}
+	}
+	visit(pkgs)
+	return result
+}

vendor/golang.org/x/tools/go/types/typeutil/map.go πŸ”—

@@ -0,0 +1,313 @@
+// 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 typeutil defines various utilities for types, such as Map,
+// a mapping from types.Type to interface{} values.
+package typeutil // import "golang.org/x/tools/go/types/typeutil"
+
+import (
+	"bytes"
+	"fmt"
+	"go/types"
+	"reflect"
+)
+
+// Map is a hash-table-based mapping from types (types.Type) to
+// arbitrary interface{} values.  The concrete types that implement
+// the Type interface are pointers.  Since they are not canonicalized,
+// == cannot be used to check for equivalence, and thus we cannot
+// simply use a Go map.
+//
+// Just as with map[K]V, a nil *Map is a valid empty map.
+//
+// Not thread-safe.
+//
+type Map struct {
+	hasher Hasher             // shared by many Maps
+	table  map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
+	length int                // number of map entries
+}
+
+// entry is an entry (key/value association) in a hash bucket.
+type entry struct {
+	key   types.Type
+	value interface{}
+}
+
+// SetHasher sets the hasher used by Map.
+//
+// All Hashers are functionally equivalent but contain internal state
+// used to cache the results of hashing previously seen types.
+//
+// A single Hasher created by MakeHasher() may be shared among many
+// Maps.  This is recommended if the instances have many keys in
+// common, as it will amortize the cost of hash computation.
+//
+// A Hasher may grow without bound as new types are seen.  Even when a
+// type is deleted from the map, the Hasher never shrinks, since other
+// types in the map may reference the deleted type indirectly.
+//
+// Hashers are not thread-safe, and read-only operations such as
+// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
+// read-lock) is require around all Map operations if a shared
+// hasher is accessed from multiple threads.
+//
+// If SetHasher is not called, the Map will create a private hasher at
+// the first call to Insert.
+//
+func (m *Map) SetHasher(hasher Hasher) {
+	m.hasher = hasher
+}
+
+// Delete removes the entry with the given key, if any.
+// It returns true if the entry was found.
+//
+func (m *Map) Delete(key types.Type) bool {
+	if m != nil && m.table != nil {
+		hash := m.hasher.Hash(key)
+		bucket := m.table[hash]
+		for i, e := range bucket {
+			if e.key != nil && types.Identical(key, e.key) {
+				// We can't compact the bucket as it
+				// would disturb iterators.
+				bucket[i] = entry{}
+				m.length--
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// At returns the map entry for the given key.
+// The result is nil if the entry is not present.
+//
+func (m *Map) At(key types.Type) interface{} {
+	if m != nil && m.table != nil {
+		for _, e := range m.table[m.hasher.Hash(key)] {
+			if e.key != nil && types.Identical(key, e.key) {
+				return e.value
+			}
+		}
+	}
+	return nil
+}
+
+// Set sets the map entry for key to val,
+// and returns the previous entry, if any.
+func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
+	if m.table != nil {
+		hash := m.hasher.Hash(key)
+		bucket := m.table[hash]
+		var hole *entry
+		for i, e := range bucket {
+			if e.key == nil {
+				hole = &bucket[i]
+			} else if types.Identical(key, e.key) {
+				prev = e.value
+				bucket[i].value = value
+				return
+			}
+		}
+
+		if hole != nil {
+			*hole = entry{key, value} // overwrite deleted entry
+		} else {
+			m.table[hash] = append(bucket, entry{key, value})
+		}
+	} else {
+		if m.hasher.memo == nil {
+			m.hasher = MakeHasher()
+		}
+		hash := m.hasher.Hash(key)
+		m.table = map[uint32][]entry{hash: {entry{key, value}}}
+	}
+
+	m.length++
+	return
+}
+
+// Len returns the number of map entries.
+func (m *Map) Len() int {
+	if m != nil {
+		return m.length
+	}
+	return 0
+}
+
+// Iterate calls function f on each entry in the map in unspecified order.
+//
+// If f should mutate the map, Iterate provides the same guarantees as
+// Go maps: if f deletes a map entry that Iterate has not yet reached,
+// f will not be invoked for it, but if f inserts a map entry that
+// Iterate has not yet reached, whether or not f will be invoked for
+// it is unspecified.
+//
+func (m *Map) Iterate(f func(key types.Type, value interface{})) {
+	if m != nil {
+		for _, bucket := range m.table {
+			for _, e := range bucket {
+				if e.key != nil {
+					f(e.key, e.value)
+				}
+			}
+		}
+	}
+}
+
+// Keys returns a new slice containing the set of map keys.
+// The order is unspecified.
+func (m *Map) Keys() []types.Type {
+	keys := make([]types.Type, 0, m.Len())
+	m.Iterate(func(key types.Type, _ interface{}) {
+		keys = append(keys, key)
+	})
+	return keys
+}
+
+func (m *Map) toString(values bool) string {
+	if m == nil {
+		return "{}"
+	}
+	var buf bytes.Buffer
+	fmt.Fprint(&buf, "{")
+	sep := ""
+	m.Iterate(func(key types.Type, value interface{}) {
+		fmt.Fprint(&buf, sep)
+		sep = ", "
+		fmt.Fprint(&buf, key)
+		if values {
+			fmt.Fprintf(&buf, ": %q", value)
+		}
+	})
+	fmt.Fprint(&buf, "}")
+	return buf.String()
+}
+
+// String returns a string representation of the map's entries.
+// Values are printed using fmt.Sprintf("%v", v).
+// Order is unspecified.
+//
+func (m *Map) String() string {
+	return m.toString(true)
+}
+
+// KeysString returns a string representation of the map's key set.
+// Order is unspecified.
+//
+func (m *Map) KeysString() string {
+	return m.toString(false)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Hasher
+
+// A Hasher maps each type to its hash value.
+// For efficiency, a hasher uses memoization; thus its memory
+// footprint grows monotonically over time.
+// Hashers are not thread-safe.
+// Hashers have reference semantics.
+// Call MakeHasher to create a Hasher.
+type Hasher struct {
+	memo map[types.Type]uint32
+}
+
+// MakeHasher returns a new Hasher instance.
+func MakeHasher() Hasher {
+	return Hasher{make(map[types.Type]uint32)}
+}
+
+// Hash computes a hash value for the given type t such that
+// Identical(t, t') => Hash(t) == Hash(t').
+func (h Hasher) Hash(t types.Type) uint32 {
+	hash, ok := h.memo[t]
+	if !ok {
+		hash = h.hashFor(t)
+		h.memo[t] = hash
+	}
+	return hash
+}
+
+// hashString computes the Fowler–Noll–Vo hash of s.
+func hashString(s string) uint32 {
+	var h uint32
+	for i := 0; i < len(s); i++ {
+		h ^= uint32(s[i])
+		h *= 16777619
+	}
+	return h
+}
+
+// hashFor computes the hash of t.
+func (h Hasher) hashFor(t types.Type) uint32 {
+	// See Identical for rationale.
+	switch t := t.(type) {
+	case *types.Basic:
+		return uint32(t.Kind())
+
+	case *types.Array:
+		return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
+
+	case *types.Slice:
+		return 9049 + 2*h.Hash(t.Elem())
+
+	case *types.Struct:
+		var hash uint32 = 9059
+		for i, n := 0, t.NumFields(); i < n; i++ {
+			f := t.Field(i)
+			if f.Anonymous() {
+				hash += 8861
+			}
+			hash += hashString(t.Tag(i))
+			hash += hashString(f.Name()) // (ignore f.Pkg)
+			hash += h.Hash(f.Type())
+		}
+		return hash
+
+	case *types.Pointer:
+		return 9067 + 2*h.Hash(t.Elem())
+
+	case *types.Signature:
+		var hash uint32 = 9091
+		if t.Variadic() {
+			hash *= 8863
+		}
+		return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
+
+	case *types.Interface:
+		var hash uint32 = 9103
+		for i, n := 0, t.NumMethods(); i < n; i++ {
+			// See go/types.identicalMethods for rationale.
+			// Method order is not significant.
+			// Ignore m.Pkg().
+			m := t.Method(i)
+			hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type())
+		}
+		return hash
+
+	case *types.Map:
+		return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
+
+	case *types.Chan:
+		return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
+
+	case *types.Named:
+		// Not safe with a copying GC; objects may move.
+		return uint32(reflect.ValueOf(t.Obj()).Pointer())
+
+	case *types.Tuple:
+		return h.hashTuple(t)
+	}
+	panic(t)
+}
+
+func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
+	// See go/types.identicalTypes for rationale.
+	n := tuple.Len()
+	var hash uint32 = 9137 + 2*uint32(n)
+	for i := 0; i < n; i++ {
+		hash += 3 * h.Hash(tuple.At(i).Type())
+	}
+	return hash
+}

vendor/golang.org/x/tools/go/types/typeutil/methodsetcache.go πŸ”—

@@ -0,0 +1,72 @@
+// 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 a cache of method sets.
+
+package typeutil
+
+import (
+	"go/types"
+	"sync"
+)
+
+// A MethodSetCache records the method set of each type T for which
+// MethodSet(T) is called so that repeat queries are fast.
+// The zero value is a ready-to-use cache instance.
+type MethodSetCache struct {
+	mu     sync.Mutex
+	named  map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
+	others map[types.Type]*types.MethodSet                            // all other types
+}
+
+// MethodSet returns the method set of type T.  It is thread-safe.
+//
+// If cache is nil, this function is equivalent to types.NewMethodSet(T).
+// Utility functions can thus expose an optional *MethodSetCache
+// parameter to clients that care about performance.
+//
+func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
+	if cache == nil {
+		return types.NewMethodSet(T)
+	}
+	cache.mu.Lock()
+	defer cache.mu.Unlock()
+
+	switch T := T.(type) {
+	case *types.Named:
+		return cache.lookupNamed(T).value
+
+	case *types.Pointer:
+		if N, ok := T.Elem().(*types.Named); ok {
+			return cache.lookupNamed(N).pointer
+		}
+	}
+
+	// all other types
+	// (The map uses pointer equivalence, not type identity.)
+	mset := cache.others[T]
+	if mset == nil {
+		mset = types.NewMethodSet(T)
+		if cache.others == nil {
+			cache.others = make(map[types.Type]*types.MethodSet)
+		}
+		cache.others[T] = mset
+	}
+	return mset
+}
+
+func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
+	if cache.named == nil {
+		cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
+	}
+	// Avoid recomputing mset(*T) for each distinct Pointer
+	// instance whose underlying type is a named type.
+	msets, ok := cache.named[named]
+	if !ok {
+		msets.value = types.NewMethodSet(named)
+		msets.pointer = types.NewMethodSet(types.NewPointer(named))
+		cache.named[named] = msets
+	}
+	return msets
+}

vendor/golang.org/x/tools/go/types/typeutil/ui.go πŸ”—

@@ -0,0 +1,52 @@
+// 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 typeutil
+
+// This file defines utilities for user interfaces that display types.
+
+import "go/types"
+
+// IntuitiveMethodSet returns the intuitive method set of a type T,
+// which is the set of methods you can call on an addressable value of
+// that type.
+//
+// The result always contains MethodSet(T), and is exactly MethodSet(T)
+// for interface types and for pointer-to-concrete types.
+// For all other concrete types T, the result additionally
+// contains each method belonging to *T if there is no identically
+// named method on T itself.
+//
+// This corresponds to user intuition about method sets;
+// this function is intended only for user interfaces.
+//
+// The order of the result is as for types.MethodSet(T).
+//
+func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
+	isPointerToConcrete := func(T types.Type) bool {
+		ptr, ok := T.(*types.Pointer)
+		return ok && !types.IsInterface(ptr.Elem())
+	}
+
+	var result []*types.Selection
+	mset := msets.MethodSet(T)
+	if types.IsInterface(T) || isPointerToConcrete(T) {
+		for i, n := 0, mset.Len(); i < n; i++ {
+			result = append(result, mset.At(i))
+		}
+	} else {
+		// T is some other concrete type.
+		// Report methods of T and *T, preferring those of T.
+		pmset := msets.MethodSet(types.NewPointer(T))
+		for i, n := 0, pmset.Len(); i < n; i++ {
+			meth := pmset.At(i)
+			if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
+				meth = m
+			}
+			result = append(result, meth)
+		}
+
+	}
+	return result
+}