From 25b7027118f99c097255d0d11e7384898d65b471 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 30 Sep 2019 11:59:41 +1000 Subject: [PATCH] Gracefully handle invalid types from invalid go packages --- api/generate.go | 4 ++-- codegen/config/binder.go | 40 ++++++++++++++++++++++++++++++----- codegen/config/binder_test.go | 8 ++++++- codegen/data.go | 10 +++++++++ codegen/field.go | 4 ++-- 5 files changed, 56 insertions(+), 10 deletions(-) diff --git a/api/generate.go b/api/generate.go index 3256bdc3de1..e0238e5602d 100644 --- a/api/generate.go +++ b/api/generate.go @@ -38,11 +38,11 @@ func Generate(cfg *config.Config, option ...Option) error { // 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") + return errors.Wrap(err, "merging type systems failed") } if err = codegen.GenerateCode(data); err != nil { - return errors.Wrap(err, "generating core failed") + return errors.Wrap(err, "generating code failed") } for _, p := range plugins { diff --git a/codegen/config/binder.go b/codegen/config/binder.go index 1dfec6c19b0..2cf38af1008 100644 --- a/codegen/config/binder.go +++ b/codegen/config/binder.go @@ -1,6 +1,7 @@ package config import ( + "bytes" "fmt" "go/token" "go/types" @@ -18,6 +19,8 @@ type Binder struct { schema *ast.Schema cfg *Config References []*TypeReference + PkgErrors PkgErrors + SawInvalid bool } func (c *Config) NewBinder(s *ast.Schema) (*Binder, error) { @@ -27,22 +30,36 @@ func (c *Config) NewBinder(s *ast.Schema) (*Binder, error) { } mp := map[string]*packages.Package{} + var pkgErrs PkgErrors for _, p := range pkgs { populatePkg(mp, p) for _, e := range p.Errors { if e.Kind == packages.ListError { - return nil, p.Errors[0] + return nil, e } } + pkgErrs = append(pkgErrs, p.Errors...) } return &Binder{ - pkgs: mp, - schema: s, - cfg: c, + pkgs: mp, + schema: s, + cfg: c, + PkgErrors: pkgErrs, }, nil } +type PkgErrors []packages.Error + +func (p PkgErrors) Error() string { + var b bytes.Buffer + b.WriteString("packages.Load: ") + for _, e := range p { + b.WriteString(e.Error() + "\n") + } + return b.String() +} + func populatePkg(mp map[string]*packages.Package, p *packages.Package) { imp := code.NormalizeVendor(p.PkgPath) if _, ok := mp[imp]; ok { @@ -316,6 +333,11 @@ func isIntf(t types.Type) bool { } func (b *Binder) TypeReference(schemaType *ast.Type, bindTarget types.Type) (ret *TypeReference, err error) { + if !isValid(bindTarget) { + b.SawInvalid = true + return nil, fmt.Errorf("%s has an invalid type", schemaType.Name()) + } + var pkgName, typeName string def := b.schema.Types[schemaType.Name()] defer func() { @@ -402,7 +424,15 @@ func (b *Binder) TypeReference(schemaType *ast.Type, bindTarget types.Type) (ret return ref, nil } - return nil, fmt.Errorf("%s has type compatible with %s", schemaType.Name(), bindTarget.String()) + return nil, fmt.Errorf("%s is incompatible with %s", schemaType.Name(), bindTarget.String()) +} + +func isValid(t types.Type) bool { + basic, isBasic := t.(*types.Basic) + if !isBasic { + return true + } + return basic.Kind() != types.Invalid } func (b *Binder) CopyModifiersFromAst(t *ast.Type, base types.Type) types.Type { diff --git a/codegen/config/binder_test.go b/codegen/config/binder_test.go index 6b34bed1229..6a842a12da8 100644 --- a/codegen/config/binder_test.go +++ b/codegen/config/binder_test.go @@ -1,14 +1,20 @@ package config import ( + "go/types" "testing" "github.com/stretchr/testify/require" - "github.com/vektah/gqlparser" "github.com/vektah/gqlparser/ast" ) +func TestBindingToInvalid(t *testing.T) { + binder, schema := createBinder(Config{}) + _, err := binder.TypeReference(schema.Query.Fields.ForName("messages").Type, &types.Basic{}) + require.EqualError(t, err, "Message has an invalid type") +} + func TestSlicePointerBinding(t *testing.T) { t.Run("without OmitSliceElementPointers", func(t *testing.T) { binder, schema := createBinder(Config{ diff --git a/codegen/data.go b/codegen/data.go index f743dee3624..71206885be1 100644 --- a/codegen/data.go +++ b/codegen/data.go @@ -133,6 +133,16 @@ func BuildData(cfg *config.Config) (*Data, error) { return s.Inputs[i].Definition.Name < s.Inputs[j].Definition.Name }) + if b.Binder.SawInvalid { + // if we have a syntax error, show it + if len(b.Binder.PkgErrors) > 0 { + return nil, b.Binder.PkgErrors + } + + // otherwise show a generic error message + return nil, fmt.Errorf("invalid types were encountered while traversing the go source code, this probably means the invalid code generated isnt correct. add try adding -v to debug") + } + return &s, nil } diff --git a/codegen/field.go b/codegen/field.go index 25e0ee3fb24..9d9b60934fe 100644 --- a/codegen/field.go +++ b/codegen/field.go @@ -74,12 +74,12 @@ func (b *builder) buildField(obj *Object, field *ast.FieldDefinition) (*Field, e return &f, nil } -func (b *builder) bindField(obj *Object, f *Field) error { +func (b *builder) bindField(obj *Object, f *Field) (errret error) { defer func() { if f.TypeReference == nil { tr, err := b.Binder.TypeReference(f.Type, nil) if err != nil { - panic(err) + errret = err } f.TypeReference = tr }