From 43655b8a0c62d9bceda235a2001bd3e239d81f25 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Sat, 23 Aug 2025 20:23:57 +0200 Subject: [PATCH] Added an error message for missing function implementations --- src/core/errors.go | 9 +++++++++ src/core/errors_test.go | 1 + src/core/evaluateDot.go | 4 ++++ src/core/evaluateToken.go | 11 +++++++++-- src/core/testdata/NoMatchingFunction.q | 6 ++++++ 5 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 src/core/testdata/NoMatchingFunction.q diff --git a/src/core/errors.go b/src/core/errors.go index 3d22f0b..847f19c 100644 --- a/src/core/errors.go +++ b/src/core/errors.go @@ -34,6 +34,15 @@ func (err *DefinitionCountMismatch) Error() string { return fmt.Sprintf("Not enough variables for the return value of '%s'", err.Function) } +// NoMatchingFunction is created when a function is not defined for the given type. +type NoMatchingFunction struct { + Function string +} + +func (err *NoMatchingFunction) Error() string { + return fmt.Sprintf("No matching function for call to '%s'", err.Function) +} + // ParameterCountMismatch error is created when the number of provided parameters doesn't match the function signature. type ParameterCountMismatch struct { Function string diff --git a/src/core/errors_test.go b/src/core/errors_test.go index a4c6dc7..1220a76 100644 --- a/src/core/errors_test.go +++ b/src/core/errors_test.go @@ -29,6 +29,7 @@ var errs = []struct { {"InvalidStructOperation.q", core.InvalidStructOperation}, {"InvalidStructOperation2.q", core.InvalidStructOperation}, {"MissingOperand.q", core.MissingOperand}, + {"NoMatchingFunction.q", &core.NoMatchingFunction{Function: "main.f"}}, {"ParameterCountMismatch.q", &core.ParameterCountMismatch{Function: "main.f", Count: 0, ExpectedCount: 1}}, {"ParameterCountMismatch2.q", &core.ParameterCountMismatch{Function: "main.f", Count: 2, ExpectedCount: 1}}, {"ReturnCountMismatch.q", &core.ReturnCountMismatch{Count: 1, ExpectedCount: 0}}, diff --git a/src/core/evaluateDot.go b/src/core/evaluateDot.go index d424730..32c25c8 100644 --- a/src/core/evaluateDot.go +++ b/src/core/evaluateDot.go @@ -48,6 +48,10 @@ func (f *Function) evaluateDot(expr *expression.Expression) (ssa.Value, error) { return nil, err } + if fn == nil { + return nil, errors.New(&NoMatchingFunction{Function: fmt.Sprintf("%s.%s", pkg.Name, rightText)}, f.File, left.Token.Position) + } + if fn.IsExtern() { f.Assembler.Libraries.Append(fn.Package, fn.Name) } else { diff --git a/src/core/evaluateToken.go b/src/core/evaluateToken.go index b5a5f69..cfe679d 100644 --- a/src/core/evaluateToken.go +++ b/src/core/evaluateToken.go @@ -1,6 +1,8 @@ package core import ( + "fmt" + "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/ssa" @@ -62,7 +64,12 @@ func (f *Function) evaluateLeaf(expr *expression.Expression) (ssa.Value, error) } pkg := f.Env.Packages[f.File.Package] - variants := pkg.Functions[name] + variants, exists := pkg.Functions[name] + + if !exists { + return nil, errors.New(&UnknownIdentifier{Name: name}, f.File, expr.Token.Position) + } + inputExpressions := expr.Parent.Children[1:] fn, err := f.selectFunction(variants, inputExpressions, expr) @@ -84,7 +91,7 @@ func (f *Function) evaluateLeaf(expr *expression.Expression) (ssa.Value, error) return v, nil } - return nil, errors.New(&UnknownIdentifier{Name: name}, f.File, expr.Token.Position) + return nil, errors.New(&NoMatchingFunction{Function: fmt.Sprintf("%s.%s", pkg.Name, name)}, f.File, expr.Token.Position) case token.Number, token.Rune: number, err := toNumber(expr.Token, f.File) diff --git a/src/core/testdata/NoMatchingFunction.q b/src/core/testdata/NoMatchingFunction.q new file mode 100644 index 0000000..c031e15 --- /dev/null +++ b/src/core/testdata/NoMatchingFunction.q @@ -0,0 +1,6 @@ +main() { + f() +} + +f(_ string) {} +f(_ int) {} \ No newline at end of file -- 2.51.2