feat: better error handling in type parsing (#24)

When parsing types, instead of panic, set proper error and diagnostic with source location.
Propagate errors correctly.
This commit is contained in:
Marc Vertes
2019-01-21 22:03:40 +01:00
committed by Ludovic Fernandez
parent 015bff1599
commit 2ef097e334
6 changed files with 122 additions and 62 deletions

View File

@@ -237,7 +237,7 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
return false
case ArrayType, BasicLit, ChanType, MapType, StructType:
n.typ = nodeType(interp, scope, n)
n.typ, err = nodeType(interp, scope, n)
return false
}
return true
@@ -429,7 +429,7 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
n.typ = scope.getType("int")
case "make":
if n.typ = scope.getType(n.child[1].ident); n.typ == nil {
n.typ = nodeType(interp, scope, n.child[1])
n.typ, err = nodeType(interp, scope, n.child[1])
}
n.child[1].val = n.typ
n.child[1].kind = BasicLit
@@ -484,7 +484,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
n.findex = scope.inc(interp)
}
if n.child[0].typ == nil {
n.child[0].typ = nodeType(interp, scope, n.child[0])
if n.child[0].typ, err = nodeType(interp, scope, n.child[0]); err != nil {
return
}
}
// TODO: Check that composite litteral expr matches corresponding type
n.typ = n.child[0].typ
@@ -498,7 +500,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
// Handle object assign from sparse key / values
if len(n.child) > 1 && n.child[1].kind == KeyValueExpr {
n.gen = compositeSparse
n.typ = nodeType(interp, scope, n.child[0])
if n.typ, err = nodeType(interp, scope, n.child[0]); err != nil {
return
}
for _, c := range n.child[1:] {
c.findex = n.typ.fieldIndex(c.child[0].ident)
}
@@ -514,7 +518,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
// Otherwise, just point to corresponding location in frame, resolved in
// ident child.
l := len(n.child) - 1
n.typ = nodeType(interp, scope.anc, n.child[l])
if n.typ, err = nodeType(interp, scope.anc, n.child[l]); err != nil {
return
}
if l == 0 {
if n.anc.anc.kind == FuncDecl {
// Receiver with implicit var decl
@@ -626,7 +632,9 @@ func (interp *Interpreter) Cfg(root *Node) ([]*Node, error) {
n.framepos = n.child[2].framepos
case FuncType:
n.typ = nodeType(interp, scope, n)
if n.typ, err = nodeType(interp, scope, n); err != nil {
return
}
// Store list of parameter frame indices in framepos
for _, c := range n.child[0].child {
for _, f := range c.child[:len(c.child)-1] {
@@ -942,13 +950,22 @@ func (n *Node) cfgError(format string, a ...interface{}) CfgError {
return CfgError(fmt.Errorf("%s: "+format, a...))
}
func genRun(n *Node) {
func genRun(n *Node) error {
var err CfgError
n.Walk(func(n *Node) bool {
if err != nil {
return false
}
switch n.kind {
case File:
n.types = frameTypes(n, n.fsize)
if n.types, err = frameTypes(n, n.fsize); err != nil {
return false
}
case FuncDecl, FuncLit:
n.types = frameTypes(n, n.flen)
if n.types, err = frameTypes(n, n.flen); err != nil {
return false
}
case FuncType:
if len(n.anc.child) == 4 {
setExec(n.anc.child[3].start)
@@ -959,6 +976,8 @@ func genRun(n *Node) {
}
return true
}, nil)
return err
}
// Find default case clause index of a switch statement, if any
@@ -1229,10 +1248,14 @@ func getReturnedType(n *Node) *Type {
}
// frameTypes returns a slice of frame types for FuncDecl or FuncLit nodes
func frameTypes(node *Node, size int) []reflect.Type {
func frameTypes(node *Node, size int) ([]reflect.Type, error) {
ft := make([]reflect.Type, size)
var err CfgError
node.Walk(func(n *Node) bool {
if err != nil {
return false
}
if n.kind == FuncDecl || n.kind == ImportDecl || n.kind == TypeDecl || n.kind == FuncLit {
return n == node // Do not dive in substree, except if this is the entry point
}
@@ -1241,7 +1264,9 @@ func frameTypes(node *Node, size int) []reflect.Type {
}
if ft[n.findex] == nil {
if n.typ.incomplete {
n.typ = n.typ.finalize()
if n.typ, err = n.typ.finalize(); err != nil {
return false
}
}
if n.typ.cat == FuncT {
ft[n.findex] = reflect.TypeOf(n)
@@ -1253,5 +1278,5 @@ func frameTypes(node *Node, size int) []reflect.Type {
return true
}, nil)
return ft
return ft, err
}

View File

@@ -14,14 +14,17 @@ func (interp *Interpreter) Gta(root *Node) error {
scope := interp.universe
root.Walk(func(n *Node) bool {
if err != nil {
return false
}
switch n.kind {
case Define:
varName := n.child[0].ident
scope.sym[varName] = &Symbol{kind: Var, global: true, index: scope.inc(interp)}
if len(n.child) > 1 {
scope.sym[varName].typ = nodeType(interp, scope, n.child[1])
scope.sym[varName].typ, err = nodeType(interp, scope, n.child[1])
} else {
scope.sym[varName].typ = nodeType(interp, scope, n.anc.child[0].child[1])
scope.sym[varName].typ, err = nodeType(interp, scope, n.anc.child[0].child[1])
}
return false
@@ -33,7 +36,9 @@ func (interp *Interpreter) Gta(root *Node) error {
scope = interp.scope[pkgName]
case FuncDecl:
n.typ = nodeType(interp, scope, n.child[2])
if n.typ, err = nodeType(interp, scope, n.child[2]); err != nil {
return false
}
scope.sym[n.child[1].ident] = &Symbol{kind: Func, typ: n.typ, node: n}
if len(n.child[0].child) > 0 {
// function is a method, add it to the related type
@@ -102,9 +107,14 @@ func (interp *Interpreter) Gta(root *Node) error {
case TypeSpec:
typeName := n.child[0].ident
if n.child[1].kind == Ident {
n.typ = &Type{cat: AliasT, val: nodeType(interp, scope, n.child[1])}
var typ *Type
typ, err = nodeType(interp, scope, n.child[1])
n.typ = &Type{cat: AliasT, val: typ}
} else {
n.typ = nodeType(interp, scope, n.child[1])
n.typ, err = nodeType(interp, scope, n.child[1])
}
if err != nil {
return false
}
// Type may already be declared for a receiver in a method function
if scope.sym[typeName] == nil {

View File

@@ -209,7 +209,7 @@ func (i *Interpreter) Eval(src string) (reflect.Value, error) {
initNodes = append(initNodes, sym.node)
}
} else {
root.types = frameTypes(root, i.fsize+1)
root.types, _ = frameTypes(root, i.fsize+1)
setExec(root.start)
}
@@ -219,7 +219,9 @@ func (i *Interpreter) Eval(src string) (reflect.Value, error) {
// Execute CFG
if !i.NoRun {
genRun(root)
if err = genRun(root); err != nil {
return res, err
}
i.fsize++
i.resizeFrame()
i.run(root, nil)

View File

@@ -664,7 +664,7 @@ func getFunc(n *Node) {
i := n.findex
next := getExec(n.tnext)
if len(n.types) == 0 {
n.types = frameTypes(n, n.flen)
n.types, _ = frameTypes(n, n.flen)
}
n.exec = func(f *Frame) Builtin {
@@ -982,7 +982,7 @@ func arrayLit(n *Node) {
value := valueGenerator(n, n.findex)
next := getExec(n.tnext)
child := n.child[1:]
a := n.typ.zero()
a, _ := n.typ.zero()
values := make([]func(*Frame) reflect.Value, len(child))
for i, c := range child {
convertLiteralValue(c, n.typ.val.TypeOf())
@@ -1039,7 +1039,7 @@ func compositeLit(n *Node) {
value := valueGenerator(n, n.findex)
next := getExec(n.tnext)
child := n.child[1:]
a := n.typ.zero()
a, _ := n.typ.zero()
values := make([]func(*Frame) reflect.Value, len(child))
for i, c := range child {
convertLiteralValue(c, n.typ.field[i].typ.TypeOf())
@@ -1061,7 +1061,7 @@ func compositeSparse(n *Node) {
next := getExec(n.tnext)
child := n.child[1:]
values := make(map[int]func(*Frame) reflect.Value)
a := n.typ.zero()
a, _ := n.typ.zero()
for _, c := range child {
convertLiteralValue(c.child[1], n.typ.field[c.findex].typ.TypeOf())
values[c.findex] = genValue(c.child[1])

View File

@@ -34,13 +34,12 @@ func (i *Interpreter) importSrcFile(path string) error {
}
name = filepath.Join(dir, name)
buf, err := ioutil.ReadFile(name)
if err != nil {
var buf []byte
if buf, err = ioutil.ReadFile(name); err != nil {
return err
}
_, root, err = i.ast(string(buf), name)
if err != nil {
if _, root, err = i.ast(string(buf), name); err != nil {
return err
}
rootNodes = append(rootNodes, root)
@@ -56,8 +55,8 @@ func (i *Interpreter) importSrcFile(path string) error {
// Generate control flow graphs
for _, root := range rootNodes {
nodes, err := i.Cfg(root)
if err != nil {
var nodes []*Node
if nodes, err = i.Cfg(root); err != nil {
return err
}
initNodes = append(initNodes, nodes...)
@@ -69,7 +68,9 @@ func (i *Interpreter) importSrcFile(path string) error {
// Once all package sources have been parsed, execute entry points then init functions
for _, n := range rootNodes {
genRun(n)
if err = genRun(n); err != nil {
return err
}
i.fsize++
i.resizeFrame()
i.run(n, nil)

View File

@@ -1,7 +1,6 @@
package interp
import (
"log"
"reflect"
"strconv"
)
@@ -117,9 +116,11 @@ type Type struct {
}
// nodeType returns a type definition for the corresponding AST subtree
func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
func nodeType(interp *Interpreter, scope *Scope, n *Node) (*Type, error) {
var err CfgError
if n.typ != nil && !n.typ.incomplete {
return n.typ
return n.typ, err
}
var t = &Type{node: n, scope: scope}
@@ -127,7 +128,7 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
switch n.kind {
case Address, StarExpr:
t.cat = PtrT
t.val = nodeType(interp, scope, n.child[0])
t.val, err = nodeType(interp, scope, n.child[0])
t.incomplete = t.val.incomplete
case ArrayType:
@@ -149,10 +150,10 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
t.incomplete = true
}
}
t.val = nodeType(interp, scope, n.child[1])
t.val, err = nodeType(interp, scope, n.child[1])
t.incomplete = t.incomplete || t.val.incomplete
} else {
t.val = nodeType(interp, scope, n.child[0])
t.val, err = nodeType(interp, scope, n.child[0])
t.incomplete = t.val.incomplete
}
@@ -171,30 +172,33 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
case string:
t.cat = StringT
default:
log.Panicf("Missing support for basic type %T, node %v\n", n.val, n.index)
err = n.cfgError("missign support for type %T", n.val)
}
case CallExpr, CompositeLitExpr:
t = nodeType(interp, scope, n.child[0])
t, err = nodeType(interp, scope, n.child[0])
case ChanType:
t.cat = ChanT
t.val = nodeType(interp, scope, n.child[0])
t.val, err = nodeType(interp, scope, n.child[0])
t.incomplete = t.val.incomplete
case Ellipsis:
t = nodeType(interp, scope, n.child[0])
t, err = nodeType(interp, scope, n.child[0])
t.variadic = true
case FuncLit:
t = nodeType(interp, scope, n.child[2])
t, err = nodeType(interp, scope, n.child[2])
case FuncType:
t.cat = FuncT
// Handle input parameters
for _, arg := range n.child[0].child {
cl := len(arg.child) - 1
typ := nodeType(interp, scope, arg.child[cl])
typ, err := nodeType(interp, scope, arg.child[cl])
if err != nil {
return nil, err
}
t.arg = append(t.arg, typ)
for i := 1; i < cl; i++ {
// Several arguments may be factorized on the same field type
@@ -206,7 +210,10 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
// Handle returned values
for _, ret := range n.child[1].child {
cl := len(ret.child) - 1
typ := nodeType(interp, scope, ret.child[cl])
typ, err := nodeType(interp, scope, ret.child[cl])
if err != nil {
return nil, err
}
t.ret = append(t.ret, typ)
for i := 1; i < cl; i++ {
// Several arguments may be factorized on the same field type
@@ -231,8 +238,12 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
case MapType:
t.cat = MapT
t.key = nodeType(interp, scope, n.child[0])
t.val = nodeType(interp, scope, n.child[1])
if t.key, err = nodeType(interp, scope, n.child[0]); err != nil {
return nil, err
}
if t.val, err = nodeType(interp, scope, n.child[1]); err != nil {
return nil, err
}
t.incomplete = t.key.incomplete || t.val.incomplete
case SelectorExpr:
@@ -248,7 +259,7 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
}
}
} else {
log.Panicln("unknown package", pkgName)
err = n.cfgError("undefined package: %s", pkgName)
}
// TODO: handle pkgsrc types
@@ -256,12 +267,18 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
t.cat = StructT
for _, c := range n.child[0].child {
if len(c.child) == 1 {
typ := nodeType(interp, scope, c.child[0])
typ, err := nodeType(interp, scope, c.child[0])
if err != nil {
return nil, err
}
t.field = append(t.field, StructField{name: c.child[0].ident, embed: true, typ: typ})
t.incomplete = t.incomplete || typ.incomplete
} else {
l := len(c.child)
typ := nodeType(interp, scope, c.child[l-1])
typ, err := nodeType(interp, scope, c.child[l-1])
if err != nil {
return nil, err
}
t.incomplete = t.incomplete || typ.incomplete
for _, d := range c.child[:l-1] {
t.field = append(t.field, StructField{name: d.ident, typ: typ})
@@ -270,10 +287,10 @@ func nodeType(interp *Interpreter, scope *Scope, n *Node) *Type {
}
default:
log.Panicln("type definition not implemented for node", n.index, n.kind)
err = n.cfgError("type definition not implemented: %s", n.kind)
}
return t
return t, err
}
var zeroValues [MaxT]reflect.Value
@@ -302,33 +319,37 @@ func init() {
}
// if type is incomplete, re-parse it.
func (t *Type) finalize() *Type {
func (t *Type) finalize() (*Type, error) {
var err CfgError
if t.incomplete {
t = nodeType(t.node.interp, t.scope, t.node)
t, err = nodeType(t.node.interp, t.scope, t.node)
t.node.typ = t
if t.incomplete {
log.Panicln("incomplete type", t.node.index)
if t.incomplete && err == nil {
err = t.node.cfgError("incomplete type")
}
}
return t
return t, err
}
// zero instantiates and return a zero value object for the given type during execution
func (t *Type) zero() reflect.Value {
t = t.finalize()
func (t *Type) zero() (v reflect.Value, err error) {
if t, err = t.finalize(); err != nil {
return v, err
}
switch t.cat {
case AliasT:
return t.val.zero()
v, err = t.val.zero()
case ArrayT, StructT:
return reflect.New(t.TypeOf()).Elem()
v = reflect.New(t.TypeOf()).Elem()
case ValueT:
return reflect.New(t.rtype).Elem()
v = reflect.New(t.rtype).Elem()
default:
return zeroValues[t.cat]
v = zeroValues[t.cat]
}
return v, err
}
// fieldIndex returns the field index from name in a struct, or -1 if not found
@@ -461,6 +482,7 @@ func (t *Type) TypeOf() reflect.Type {
return t.rtype
default:
return t.zero().Type()
z, _ := t.zero()
return z.Type()
}
}