From dc49b106f376992e63dd49e7f3ed504b225b00a2 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Wed, 24 Jun 2026 11:20:50 +0200 Subject: [PATCH] Improved error handling for invalid loop controls --- src/core/compileLoopControl.go | 11 +++++++++-- src/core/errors.go | 14 ++++++++++++++ src/core/errors_test.go | 2 ++ src/core/testdata/InvalidLoopControl.q | 5 +++++ src/core/testdata/InvalidLoopControl2.q | 5 +++++ 5 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 src/core/testdata/InvalidLoopControl.q create mode 100644 src/core/testdata/InvalidLoopControl2.q diff --git a/src/core/compileLoopControl.go b/src/core/compileLoopControl.go index 530dee8..fca34e9 100644 --- a/src/core/compileLoopControl.go +++ b/src/core/compileLoopControl.go @@ -19,8 +19,9 @@ func (f *Function) compileLoopControl(control *ast.LoopControl) error { } loop := f.loopStack.Current() + name := call.Children[0].Token.StringFrom(f.File.Bytes) - switch call.Children[0].Token.StringFrom(f.File.Bytes) { + switch name { case "next": f.loopNext(loop) case "restart": @@ -28,7 +29,13 @@ func (f *Function) compileLoopControl(control *ast.LoopControl) error { case "stop": f.jump(loop.Exit) default: - panic("invalid") + correctName := "" + + if name == "break" { + correctName = "stop" + } + + return errors.New(&InvalidLoopControl{Name: name, CorrectName: correctName}, f.File, call.Children[0].Source()) } return nil diff --git a/src/core/errors.go b/src/core/errors.go index a61efe3..8570281 100644 --- a/src/core/errors.go +++ b/src/core/errors.go @@ -76,6 +76,20 @@ func (err *IdenticalExpressions) Error() string { return fmt.Sprintf("Identical expressions to the left and right of the '%s' operator", err.Operator) } +// InvalidLoopControl represents invalid loop control statements. +type InvalidLoopControl struct { + Name string + CorrectName string +} + +func (err *InvalidLoopControl) Error() string { + if err.CorrectName != "" { + return fmt.Sprintf("Invalid loop control '%s', did you mean '%s'?", err.Name, err.CorrectName) + } + + return fmt.Sprintf("Invalid loop control '%s'", err.Name) +} + // NoMatchingFunction is created when a function is not defined for the given type. type NoMatchingFunction struct { Function string diff --git a/src/core/errors_test.go b/src/core/errors_test.go index dae0b2e..cc8019b 100644 --- a/src/core/errors_test.go +++ b/src/core/errors_test.go @@ -53,6 +53,8 @@ var errs = []struct { {"InvalidFieldInit.q", core.InvalidFieldInit}, {"InvalidFieldInit2.q", core.InvalidFieldInit}, {"InvalidLeftExpression.q", core.InvalidLeftExpression}, + {"InvalidLoopControl.q", &core.InvalidLoopControl{Name: "unknown"}}, + {"InvalidLoopControl2.q", &core.InvalidLoopControl{Name: "break", CorrectName: "stop"}}, {"InvalidLoopHeader.q", core.InvalidLoopHeader}, {"InvalidStructOperation.q", core.InvalidStructOperation}, {"InvalidStructOperation2.q", core.InvalidStructOperation}, diff --git a/src/core/testdata/InvalidLoopControl.q b/src/core/testdata/InvalidLoopControl.q new file mode 100644 index 0000000..8b48a32 --- /dev/null +++ b/src/core/testdata/InvalidLoopControl.q @@ -0,0 +1,5 @@ +main() { + loop { + loop.unknown() + } +} \ No newline at end of file diff --git a/src/core/testdata/InvalidLoopControl2.q b/src/core/testdata/InvalidLoopControl2.q new file mode 100644 index 0000000..ccf4dd1 --- /dev/null +++ b/src/core/testdata/InvalidLoopControl2.q @@ -0,0 +1,5 @@ +main() { + loop { + loop.break() + } +} \ No newline at end of file -- 2.51.2