Skip to content

Commit

Permalink
Do not say "function" in method error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli committed Jan 14, 2025
1 parent 45721f3 commit fe1ab59
Show file tree
Hide file tree
Showing 21 changed files with 74 additions and 26 deletions.
1 change: 0 additions & 1 deletion compiler/ast.jou
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
17 changes: 11 additions & 6 deletions compiler/parser.jou
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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")
Expand Down Expand Up @@ -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++
Expand Down Expand Up @@ -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("{")
Expand Down
17 changes: 12 additions & 5 deletions compiler/typecheck/step2_populate_types.jou
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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)

Expand Down
28 changes: 16 additions & 12 deletions compiler/typecheck/step3_function_and_method_bodies.jou
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down Expand Up @@ -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> from function '%s' defined with '-> <to>'",
ft->current_fom_types->signature.name)
"attempting to return a value of type <from> from %s '%s' defined with '-> <to>'",
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)

Expand Down
7 changes: 7 additions & 0 deletions compiler/types.jou
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions tests/other_errors/missing_value_in_return_method.jou
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions tests/other_errors/noreturn_but_return_with_value_method.jou
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Foo:
def bar(self) -> noreturn:
return 1 # Error: method 'bar' cannot return because it was defined with '-> noreturn'
File renamed without changes.
3 changes: 3 additions & 0 deletions tests/other_errors/return_void_method.jou
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions tests/other_errors/unexpected_return_value_function.jou
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def f() -> None:
return 123 # Error: function 'f' cannot return a value because it was defined with '-> None'
3 changes: 3 additions & 0 deletions tests/other_errors/unexpected_return_value_method.jou
Original file line number Diff line number Diff line change
@@ -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'
3 changes: 3 additions & 0 deletions tests/syntax_error/bad_method_name_after_def.jou
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Foo:
def 69(self) -> None: # Error: expected a method name, got an integer
return
2 changes: 0 additions & 2 deletions tests/syntax_error/def_missing_args.jou

This file was deleted.

2 changes: 2 additions & 0 deletions tests/syntax_error/missing_args_function.jou
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def foo: # Error: expected a '(' to denote the start of arguments, got ':'
return
3 changes: 3 additions & 0 deletions tests/syntax_error/missing_args_method.jou
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Foo:
def bar: # Error: expected a '(' to denote the start of arguments, got ':'
return
File renamed without changes.
3 changes: 3 additions & 0 deletions tests/syntax_error/missing_return_type_method.jou
Original file line number Diff line number Diff line change
@@ -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
File renamed without changes.
3 changes: 3 additions & 0 deletions tests/wrong_type/return_value_method.jou
Original file line number Diff line number Diff line change
@@ -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'

0 comments on commit fe1ab59

Please sign in to comment.