stmt: propagate err checks to top-level function calls and assignment calls

This commit is contained in:
mappu 2020-04-07 23:25:43 +12:00
parent 52ccea5d65
commit e9aa553216
2 changed files with 81 additions and 32 deletions

View File

@ -6,7 +6,7 @@ function Foo(): int {
} }
try { try {
echo Foo(); Foo();
} catch (\BarException | \BazException $e) { } catch (\BarException | \BazException $e) {
echo "bar or baz exception " . $e->getMessage() . "\n"; echo "bar or baz exception " . $e->getMessage() . "\n";
} catch (\Exception $e) { } catch (\Exception $e) {

109
node.go
View File

@ -528,16 +528,35 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
} }
} }
// Regular case // Assignment expressions can take on better error-handling behaviour
// when we know the assignment is a top-level expression
if a, ok := n.Expr.(*assign.Assign); ok {
return this.convertAssignment(a, true)
}
// Non-assignment expression
child, err := this.convert(n.Expr) child, err := this.convert(n.Expr)
if err != nil { if err != nil {
return "", parseErr{n, err} return "", parseErr{n, err}
} }
ret := child + "\n" // standalone expression statement // If this is a simple expression (func call; indirect/method call; assignment of func/method call) then
// we need to propagate errors
switch n.Expr.(type) {
case *expr.FunctionCall, *expr.StaticCall, *expr.New:
ret := "_, err = " + child + "\n"
ret += "if err != nil {\n"
ret += this.currentErrHandler
ret += "}\n"
return ret, nil return ret, nil
default:
// Some other kind of general expression - no special error handling needed
ret := child + "\n" // standalone expression statement
return ret, nil
}
case *stmt.Echo: case *stmt.Echo:
// Convert into fmt.Print // Convert into fmt.Print
args := make([]string, 0, len(n.Exprs)) args := make([]string, 0, len(n.Exprs))
@ -699,34 +718,7 @@ func (this *conversionState) convertNoFreeFloating(n_ node.Node) (string, error)
// //
case *assign.Assign: case *assign.Assign:
return this.convertAssignment(n, false /* no reason to believe we are a top-level statement */)
rvalue, err := this.convert(n.Expression)
if err != nil {
return "", parseErr{n, err}
}
if dimf, ok := n.Variable.(*expr.ArrayDimFetch); ok && dimf.Dim == nil {
// Special handling for the case of foo[] = bar
// Transform into append()
arrayVar, err := this.convert(dimf.Variable)
if err != nil {
return "", parseErr{dimf, err}
}
return arrayVar + ` = append(` + arrayVar + `, ` + rvalue + `)`, nil
} else {
// Normal assignment
lvalue, err := this.convert(n.Variable) // might be a more complicated lvalue
if err != nil {
return "", parseErr{n, err}
}
// TODO this may need to use `:=`
return lvalue + " = " + rvalue, nil
}
// //
// expr // expr
@ -1152,6 +1144,63 @@ func (this *conversionState) resolveName(n node.Node) (string, error) {
return paramType, nil return paramType, nil
} }
func (this *conversionState) convertAssignment(n *assign.Assign, isTopLevelStatement bool) (string, error) {
rvalue, err := this.convert(n.Expression)
if err != nil {
return "", parseErr{n, err}
}
if dimf, ok := n.Variable.(*expr.ArrayDimFetch); ok && dimf.Dim == nil {
// Special handling for the case of foo[] = bar
// Transform into append()
arrayVar, err := this.convert(dimf.Variable)
if err != nil {
return "", parseErr{dimf, err}
}
ret := arrayVar + ` = append(` + arrayVar + `, ` + rvalue + `)`
if isTopLevelStatement {
ret += "\n"
}
return ret, nil
} else {
// Normal assignment
lvalue, err := this.convert(n.Variable) // might be a more complicated lvalue
if err != nil {
return "", parseErr{n, err}
}
// TODO this may need to use `:=`
if isTopLevelStatement {
// If this is a simple expression (func call; indirect/method call; assignment of func/method call) then
// we need to propagate errors
switch n.Expression.(type) {
case *expr.FunctionCall, *expr.StaticCall, *expr.New:
ret := "_, " + lvalue + " = " + rvalue + "\n"
ret += "if err != nil {\n"
ret += this.currentErrHandler
ret += "}\n"
return ret, nil
default:
ret := lvalue + " = " + rvalue + "\n" // Just the basic assignment - with trailing NL
return ret, nil
}
} else {
return lvalue + " = " + rvalue, nil // Just the basic assignment - without trailing NL
}
}
}
// convertToStmtList asserts that the node is either a StmtList or wraps it in a // convertToStmtList asserts that the node is either a StmtList or wraps it in a
// single-stmt StmtList if not. // single-stmt StmtList if not.
// Loop bodies may be a StmtList if it is wrapped in {}, or a single statement // Loop bodies may be a StmtList if it is wrapped in {}, or a single statement