diff --git a/devtools/internal/interpreter/interpreter.go b/devtools/internal/interpreter/interpreter.go index 0dd47af..7580b61 100644 --- a/devtools/internal/interpreter/interpreter.go +++ b/devtools/internal/interpreter/interpreter.go @@ -225,7 +225,7 @@ func (i Instance) runGoCode(source string) (r EvalResult, e error) { err := recover() if err != nil { r = GoCodeExecuted - e = fmt.Errorf("panic when running Yaegi: %s", err) + e = fmt.Errorf("panic when running Yaegi Go interpreter: %s", err) } }() @@ -244,7 +244,7 @@ func (i Instance) runGoCode(source string) (r EvalResult, e error) { return Continued, nil } - return GoCodeExecuted, err + return GoCodeExecuted, convertYaegiError(err) } printResult(res) @@ -252,6 +252,29 @@ func (i Instance) runGoCode(source string) (r EvalResult, e error) { return GoCodeExecuted, nil } +var ( + yaegiCfgErrorPattern = regexp.MustCompile(`^(\d+:)(\d+:)(.*)`) // control flow graph generation error + yaegiScannerErrorPattern = regexp.MustCompile(`^_\.go:(\d+:)(\d+:)(.*)`) +) + +// Yaegi reports wrong character number in error messages. It is better to remove this information completely. +func convertYaegiError(err error) error { + errStr := err.Error() + + switch { + case yaegiCfgErrorPattern.MatchString(errStr): + return fmt.Errorf( + yaegiCfgErrorPattern.ReplaceAllString(errStr, "$1$3"), + ) + case yaegiScannerErrorPattern.MatchString(errStr): + return fmt.Errorf( + yaegiScannerErrorPattern.ReplaceAllString(errStr, "$1$3"), + ) + default: + return err + } +} + // shouldContinueOnError returns true if the error can be safely ignored // to let the caller grab one more line before retrying to parse its input. func shouldContinueOnError(err error, source string) bool { diff --git a/devtools/internal/interpreter/interpreter_test.go b/devtools/internal/interpreter/interpreter_test.go index 6dcbf68..8601bba 100644 --- a/devtools/internal/interpreter/interpreter_test.go +++ b/devtools/internal/interpreter/interpreter_test.go @@ -249,6 +249,37 @@ func TestEval(t *testing.T) { require.NoError(t, err) assert.Equal(t, "interpreter_test.anotherType: {}\n", swapper.ReadOutput(t)) }) + + t.Run("should convert error message to exclude invalid character number", func(t *testing.T) { + tests := map[string]struct { + source string + expectedErrMessage string + }{ + "undefined": { + source: "undefinedVar", + expectedErrMessage: "1: undefined: undefinedVar", + }, + "undefined in second line": { + source: "{ \n undefinedVar \n }", + expectedErrMessage: "2: undefined: undefinedVar", + }, + "invalid number literal": { + source: "a := 123a123", + expectedErrMessage: "1: expected ';', found a123 (and 1 more errors)", + }, + "invalid number literal in second line": { + source: "{ \n a := 123a123 \n }", + expectedErrMessage: "2: expected ';', found a123 (and 1 more errors)", + }, + } + for name, testCase := range tests { + t.Run(name, func(t *testing.T) { + _, err := newInterpreterInstance(t).Eval(testCase.source) + require.Error(t, err) + assert.Equal(t, testCase.expectedErrMessage, err.Error()) + }) + } + }) } func TestExport(t *testing.T) {