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:
committed by
Ludovic Fernandez
parent
015bff1599
commit
2ef097e334
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user