From 9462bf90e4fe107c2a019c310cc34b80851a34e8 Mon Sep 17 00:00:00 2001 From: Jacek Olszak Date: Mon, 14 Aug 2023 16:48:12 +0200 Subject: [PATCH] [devtools] [repl] Do not print character number of error The character number reported by Yaegi (where the error occurred) is always wrong. It will be better to simply hide this information from the user. --- devtools/internal/interpreter/interpreter.go | 27 ++++++++++++++-- .../internal/interpreter/interpreter_test.go | 31 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) 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) {