Skip to content

Commit

Permalink
Merge pull request #883 from 99designs/handle-invalid-types
Browse files Browse the repository at this point in the history
Gracefully handle invalid types from invalid go packages
  • Loading branch information
vektah authored Sep 30, 2019
2 parents 046054d + 25b7027 commit 4c35356
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 10 deletions.
4 changes: 2 additions & 2 deletions api/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
40 changes: 35 additions & 5 deletions codegen/config/binder.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"bytes"
"fmt"
"go/token"
"go/types"
Expand All @@ -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) {
Expand All @@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 7 additions & 1 deletion codegen/config/binder_test.go
Original file line number Diff line number Diff line change
@@ -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{
Expand Down
10 changes: 10 additions & 0 deletions codegen/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
4 changes: 2 additions & 2 deletions codegen/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down

0 comments on commit 4c35356

Please sign in to comment.