diff --git a/compiler/ast.jou b/compiler/ast.jou index e4ed4207..bc7fd191 100644 --- a/compiler/ast.jou +++ b/compiler/ast.jou @@ -459,7 +459,6 @@ class AstCall: free(self->method_call_self) # Useful for formatting error messages, but not much else. - # TODO: use this def function_or_method(self) -> byte*: if self->method_call_self == NULL: return "function" diff --git a/compiler/parser.jou b/compiler/parser.jou index d10a09ed..c0765dba 100644 --- a/compiler/parser.jou +++ b/compiler/parser.jou @@ -318,10 +318,11 @@ class Parser: return result def parse_function_or_method_signature(self, is_method: bool) -> AstSignature: - # TODO: change error messages to say method, when it is a method (#243) - used_self: bool = False if self->tokens->kind != TokenKind.Name: - self->tokens->fail_expected_got("a function name") + if is_method: + self->tokens->fail_expected_got("a method name") + else: + self->tokens->fail_expected_got("a function name") result = AstSignature{ name_location = self->tokens->location, @@ -330,9 +331,10 @@ class Parser: self->tokens++ if not self->tokens->is_operator("("): - self->tokens->fail_expected_got("a '(' to denote the start of function arguments") + self->tokens->fail_expected_got("a '(' to denote the start of arguments") self->tokens++ + used_self = False while not self->tokens->is_operator(")"): if result.takes_varargs: fail(self->tokens->location, "if '...' is used, it must be the last parameter") @@ -389,7 +391,10 @@ class Parser: # Special case for common typo: def foo(): if self->tokens->is_operator(":"): - fail(self->tokens->location, "return type must be specified with '->', or with '-> None' if the function doesn't return anything") + if is_method: + fail(self->tokens->location, "return type must be specified with '->', or with '-> None' if the method doesn't return anything") + else: + fail(self->tokens->location, "return type must be specified with '->', or with '-> None' if the function doesn't return anything") if not self->tokens->is_operator("->"): self->tokens->fail_expected_got("a '->'") self->tokens++ @@ -423,7 +428,7 @@ class Parser: return result def parse_instantiation(self) -> AstInstantiation: - assert self->tokens->kind == TokenKind.Name # must be checked when calling this function + assert self->tokens->kind == TokenKind.Name result = AstInstantiation{class_name_location = self->tokens->location, class_name = self->tokens->short_string} self->tokens++ assert self->tokens->is_operator("{") diff --git a/compiler/typecheck/step2_populate_types.jou b/compiler/typecheck/step2_populate_types.jou index 7c49e007..19274e46 100644 --- a/compiler/typecheck/step2_populate_types.jou +++ b/compiler/typecheck/step2_populate_types.jou @@ -44,13 +44,15 @@ def handle_global_var(ft: FileTypes*, vardecl: AstNameTypeValue*, defined_here: def handle_signature(ft: FileTypes*, astsig: AstSignature*, self_class: Type*) -> Signature: + if self_class == NULL: + function_or_method = "function" + else: + function_or_method = "method" + msg: byte[500] if ft->find_function_or_method(self_class, astsig->name) != NULL: - if self_class != NULL: - snprintf(msg, sizeof(msg), "a method named '%s' already exists", astsig->name) - else: - snprintf(msg, sizeof(msg), "a function named '%s' already exists", astsig->name) + snprintf(msg, sizeof(msg), "a %s named '%s' already exists", function_or_method, astsig->name) fail(astsig->name_location, msg) sig = Signature{nargs = astsig->nargs, takes_varargs = astsig->takes_varargs} @@ -85,7 +87,12 @@ def handle_signature(ft: FileTypes*, astsig: AstSignature*, self_class: Type*) - if astsig->return_type.is_none() or astsig->return_type.is_noreturn(): sig.returntype = NULL elif astsig->return_type.is_void(): - fail(astsig->return_type.location, "void is not a valid return type, use '-> None' if the function does not return a value") + snprintf( + msg, sizeof(msg), + "void is not a valid return type, use '-> None' if the %s does not return a value", + function_or_method, + ) + fail(astsig->return_type.location, msg) else: sig.returntype = type_from_ast(ft, &astsig->return_type) diff --git a/compiler/typecheck/step3_function_and_method_bodies.jou b/compiler/typecheck/step3_function_and_method_bodies.jou index 6b13a926..84b42c8a 100644 --- a/compiler/typecheck/step3_function_and_method_bodies.jou +++ b/compiler/typecheck/step3_function_and_method_bodies.jou @@ -334,10 +334,7 @@ def typecheck_expression_not_void(ft: FileTypes*, expr: AstExpression*) -> Type* assert expr->kind == AstExpressionKind.Call msg: byte[500] - if expr->call.method_call_self == NULL: - snprintf(msg, sizeof(msg), "function '%s' does not return a value", expr->call.name) - else: - snprintf(msg, sizeof(msg), "method '%s' does not return a value", expr->call.name) + snprintf(msg, sizeof(msg), "%s '%s' does not return a value", expr->call.function_or_method(), expr->call.name) fail(expr->location, msg) @@ -1128,30 +1125,37 @@ def typecheck_statement(ft: FileTypes*, stmt: AstStatement*) -> None: fail(stmt->location, msg) elif stmt->kind == AstStatementKind.Return: + function_or_method = ft->current_fom_types->signature.function_or_method() + if ft->current_fom_types->signature.is_noreturn: - snprintf(msg, sizeof(msg), - "function '%s' cannot return because it was defined with '-> noreturn'", - ft->current_fom_types->signature.name) + snprintf( + msg, sizeof(msg), + "%s '%s' cannot return because it was defined with '-> noreturn'", + function_or_method, ft->current_fom_types->signature.name, + ) fail(stmt->location, msg) return_type = ft->current_fom_types->signature.returntype if stmt->return_value != NULL and return_type == NULL: - snprintf(msg, sizeof(msg), "function '%s' cannot return a value because it was defined with '-> None'", - ft->current_fom_types->signature.name) + snprintf( + msg, sizeof(msg), "%s '%s' cannot return a value because it was defined with '-> None'", + function_or_method, ft->current_fom_types->signature.name, + ) fail(stmt->location, msg) if return_type != NULL and stmt->return_value == NULL: snprintf(msg, sizeof(msg), - "a return value is needed, because the return type of function '%s' is %s", + "a return value is needed, because the return type of %s '%s' is %s", + function_or_method, ft->current_fom_types->signature.name, ft->current_fom_types->signature.returntype->name) fail(stmt->location, msg) if stmt->return_value != NULL: snprintf(msg, sizeof msg, - "attempting to return a value of type from function '%s' defined with '-> '", - ft->current_fom_types->signature.name) + "attempting to return a value of type from %s '%s' defined with '-> '", + function_or_method, ft->current_fom_types->signature.name) typecheck_expression_with_implicit_cast( ft, stmt->return_value, ft->find_local_var("return")->type, msg) diff --git a/compiler/types.jou b/compiler/types.jou index bb8ffec4..e4dd78eb 100644 --- a/compiler/types.jou +++ b/compiler/types.jou @@ -277,6 +277,13 @@ class Signature: assert False return NULL + # Useful for error messages, not much else. + def function_or_method(self) -> byte*: + if self->get_self_class() == NULL: + return "function" + else: + return "method" + def to_string(self, include_return_type: bool, include_self: bool) -> byte*: result = strdup(self->name) assert result != NULL diff --git a/tests/other_errors/missing_value_in_return.jou b/tests/other_errors/missing_value_in_return_function.jou similarity index 100% rename from tests/other_errors/missing_value_in_return.jou rename to tests/other_errors/missing_value_in_return_function.jou diff --git a/tests/other_errors/missing_value_in_return_method.jou b/tests/other_errors/missing_value_in_return_method.jou new file mode 100644 index 00000000..be3bd65b --- /dev/null +++ b/tests/other_errors/missing_value_in_return_method.jou @@ -0,0 +1,3 @@ +class Foo: + def bar(self) -> int: + return # Error: a return value is needed, because the return type of method 'bar' is int diff --git a/tests/other_errors/noreturn_but_return_with_value.jou b/tests/other_errors/noreturn_but_return_with_value_function.jou similarity index 100% rename from tests/other_errors/noreturn_but_return_with_value.jou rename to tests/other_errors/noreturn_but_return_with_value_function.jou diff --git a/tests/other_errors/noreturn_but_return_with_value_method.jou b/tests/other_errors/noreturn_but_return_with_value_method.jou new file mode 100644 index 00000000..6da15a2c --- /dev/null +++ b/tests/other_errors/noreturn_but_return_with_value_method.jou @@ -0,0 +1,3 @@ +class Foo: + def bar(self) -> noreturn: + return 1 # Error: method 'bar' cannot return because it was defined with '-> noreturn' diff --git a/tests/other_errors/return_void.jou b/tests/other_errors/return_void_function.jou similarity index 100% rename from tests/other_errors/return_void.jou rename to tests/other_errors/return_void_function.jou diff --git a/tests/other_errors/return_void_method.jou b/tests/other_errors/return_void_method.jou new file mode 100644 index 00000000..1684877a --- /dev/null +++ b/tests/other_errors/return_void_method.jou @@ -0,0 +1,3 @@ +class Foo: + def foo(self) -> void: # Error: void is not a valid return type, use '-> None' if the method does not return a value + pass diff --git a/tests/other_errors/unexpected_return_value_function.jou b/tests/other_errors/unexpected_return_value_function.jou new file mode 100644 index 00000000..62a954de --- /dev/null +++ b/tests/other_errors/unexpected_return_value_function.jou @@ -0,0 +1,2 @@ +def f() -> None: + return 123 # Error: function 'f' cannot return a value because it was defined with '-> None' diff --git a/tests/other_errors/unexpected_return_value_method.jou b/tests/other_errors/unexpected_return_value_method.jou new file mode 100644 index 00000000..e9e1eeae --- /dev/null +++ b/tests/other_errors/unexpected_return_value_method.jou @@ -0,0 +1,3 @@ +class Foo: + def f(self) -> None: + return 123 # Error: method 'f' cannot return a value because it was defined with '-> None' diff --git a/tests/syntax_error/bad_method_name_after_def.jou b/tests/syntax_error/bad_method_name_after_def.jou new file mode 100644 index 00000000..7d48c1e5 --- /dev/null +++ b/tests/syntax_error/bad_method_name_after_def.jou @@ -0,0 +1,3 @@ +class Foo: + def 69(self) -> None: # Error: expected a method name, got an integer + return diff --git a/tests/syntax_error/def_missing_args.jou b/tests/syntax_error/def_missing_args.jou deleted file mode 100644 index 1368954d..00000000 --- a/tests/syntax_error/def_missing_args.jou +++ /dev/null @@ -1,2 +0,0 @@ -def foo: # Error: expected a '(' to denote the start of function arguments, got ':' - return diff --git a/tests/syntax_error/missing_args_function.jou b/tests/syntax_error/missing_args_function.jou new file mode 100644 index 00000000..7daf9200 --- /dev/null +++ b/tests/syntax_error/missing_args_function.jou @@ -0,0 +1,2 @@ +def foo: # Error: expected a '(' to denote the start of arguments, got ':' + return diff --git a/tests/syntax_error/missing_args_method.jou b/tests/syntax_error/missing_args_method.jou new file mode 100644 index 00000000..943e0128 --- /dev/null +++ b/tests/syntax_error/missing_args_method.jou @@ -0,0 +1,3 @@ +class Foo: + def bar: # Error: expected a '(' to denote the start of arguments, got ':' + return diff --git a/tests/syntax_error/missing_return_type.jou b/tests/syntax_error/missing_return_type_function.jou similarity index 100% rename from tests/syntax_error/missing_return_type.jou rename to tests/syntax_error/missing_return_type_function.jou diff --git a/tests/syntax_error/missing_return_type_method.jou b/tests/syntax_error/missing_return_type_method.jou new file mode 100644 index 00000000..e9f98938 --- /dev/null +++ b/tests/syntax_error/missing_return_type_method.jou @@ -0,0 +1,3 @@ +class Foo: + def bar(self): # Error: return type must be specified with '->', or with '-> None' if the method doesn't return anything + return diff --git a/tests/wrong_type/return_value.jou b/tests/wrong_type/return_value_function.jou similarity index 100% rename from tests/wrong_type/return_value.jou rename to tests/wrong_type/return_value_function.jou diff --git a/tests/wrong_type/return_value_method.jou b/tests/wrong_type/return_value_method.jou new file mode 100644 index 00000000..8f31e07c --- /dev/null +++ b/tests/wrong_type/return_value_method.jou @@ -0,0 +1,3 @@ +class Foo: + def bar(self) -> byte: + return 1 # Error: attempting to return a value of type int from method 'bar' defined with '-> byte'