Skip to content

Commit 0f43d1f

Browse files
committed
Expose file.Error type for better error handling
1 parent 546dfc9 commit 0f43d1f

File tree

7 files changed

+63
-26
lines changed

7 files changed

+63
-26
lines changed

checker/checker.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func Check(tree *parser.Tree, config *conf.Config) (reflect.Type, error) {
3838
}
3939

4040
if v.err != nil {
41-
return t, fmt.Errorf("%v", v.err.Format(tree.Source))
41+
return t, v.err.Bind(tree.Source)
4242
}
4343

4444
return t, nil

expr.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ func Compile(input string, ops ...Option) (*vm.Program, error) {
167167
err = optimizer.Optimize(&tree.Node, config)
168168
if err != nil {
169169
if fileError, ok := err.(*file.Error); ok {
170-
return nil, fmt.Errorf("%v", fileError.Format(tree.Source))
170+
return nil, fileError.Bind(tree.Source)
171171
}
172172
return nil, err
173173
}

expr_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package expr_test
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"github.com/antonmedv/expr/ast"
7+
"github.com/antonmedv/expr/file"
68
"reflect"
79
"strings"
810
"testing"
@@ -1017,6 +1019,41 @@ func TestPatch_length(t *testing.T) {
10171019
require.Equal(t, true, output)
10181020
}
10191021

1022+
func TestCompile_exposed_error(t *testing.T) {
1023+
_, err := expr.Compile(`1 == true`)
1024+
require.Error(t, err)
1025+
1026+
fileError, ok := err.(*file.Error)
1027+
require.True(t, ok, "error should be of type *file.Error")
1028+
require.Equal(t, "invalid operation: == (mismatched types int and bool) (1:3)\n | 1 == true\n | ..^", fileError.Error())
1029+
require.Equal(t, 2, fileError.Column)
1030+
require.Equal(t, 1, fileError.Line)
1031+
1032+
b, err := json.Marshal(err)
1033+
require.NoError(t, err)
1034+
require.Equal(t, `{"Line":1,"Column":2,"Message":"invalid operation: == (mismatched types int and bool)","Snippet":"\n | 1 == true\n | ..^"}`, string(b))
1035+
}
1036+
1037+
func TestAsBool_exposed_error_(t *testing.T) {
1038+
_, err := expr.Compile(`42`, expr.AsBool())
1039+
require.Error(t, err)
1040+
1041+
_, ok := err.(*file.Error)
1042+
require.False(t, ok, "error must not be of type *file.Error")
1043+
require.Equal(t, "expected bool, but got int", err.Error())
1044+
}
1045+
1046+
func TestEval_exposed_error(t *testing.T) {
1047+
_, err := expr.Eval(`1/0`, nil)
1048+
require.Error(t, err)
1049+
1050+
fileError, ok := err.(*file.Error)
1051+
require.True(t, ok, "error should be of type *file.Error")
1052+
require.Equal(t, "runtime error: integer divide by zero (1:2)\n | 1/0\n | .^", fileError.Error())
1053+
require.Equal(t, 1, fileError.Column)
1054+
require.Equal(t, 1, fileError.Line)
1055+
}
1056+
10201057
//
10211058
// Mock types
10221059
//

file/error.go

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,14 @@ import (
99
type Error struct {
1010
Location
1111
Message string
12+
Snippet string
1213
}
1314

14-
const (
15-
dot = "."
16-
ind = "^"
17-
)
18-
1915
func (e *Error) Error() string {
20-
return e.Message
16+
return e.format()
2117
}
2218

23-
func (e *Error) Format(source *Source) string {
24-
if e.Location.Empty() {
25-
return e.Message
26-
}
27-
var result = fmt.Sprintf(
28-
"%s (%d:%d)",
29-
e.Message,
30-
e.Location.Line,
31-
e.Location.Column+1, // add one to the 0-based column for display
32-
)
19+
func (e *Error) Bind(source *Source) *Error {
3320
if snippet, found := source.Snippet(e.Location.Line); found {
3421
snippet := strings.Replace(snippet, "\t", " ", -1)
3522
srcLine := "\n | " + snippet
@@ -41,18 +28,31 @@ func (e *Error) Format(source *Source) string {
4128
if sz > 1 {
4229
goto noind
4330
} else {
44-
indLine += dot
31+
indLine += "."
4532
}
4633
}
4734
if _, sz := utf8.DecodeRune(bytes); sz > 1 {
4835
goto noind
4936
} else {
50-
indLine += ind
37+
indLine += "^"
5138
}
5239
srcLine += indLine
5340

5441
noind:
55-
result += srcLine
42+
e.Snippet = srcLine
5643
}
57-
return result
44+
return e
45+
}
46+
47+
func (e *Error) format() string {
48+
if e.Location.Empty() {
49+
return e.Message
50+
}
51+
return fmt.Sprintf(
52+
"%s (%d:%d)%s",
53+
e.Message,
54+
e.Line,
55+
e.Column+1, // add one to the 0-based column for display
56+
e.Snippet,
57+
)
5858
}

parser/lexer/lexer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func Lex(source *file.Source) ([]Token, error) {
2323
}
2424

2525
if l.err != nil {
26-
return nil, fmt.Errorf("%v", l.err.Format(source))
26+
return nil, l.err.Bind(source)
2727
}
2828

2929
return l.tokens, nil

parser/parser.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func Parse(input string) (*Tree, error) {
105105
}
106106

107107
if p.err != nil {
108-
return nil, fmt.Errorf("%v", p.err.Format(source))
108+
return nil, p.err.Bind(source)
109109
}
110110

111111
return &Tree{

vm/vm.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ func Run(program *Program, env interface{}) (out interface{}, err error) {
2222

2323
defer func() {
2424
if r := recover(); r != nil {
25-
h := file.Error{
25+
f := &file.Error{
2626
Location: program.Locations[vm.pp],
2727
Message: fmt.Sprintf("%v", r),
2828
}
29-
err = fmt.Errorf("%v", h.Format(program.Source))
29+
err = f.Bind(program.Source)
3030
}
3131
}()
3232

0 commit comments

Comments
 (0)