From 5f4218459796bc332861c97cce66a1d862145510 Mon Sep 17 00:00:00 2001 From: Akuli Date: Wed, 15 Jan 2025 18:06:57 +0200 Subject: [PATCH] Improve error messages related to . and -> (#626) --- .../step3_function_and_method_bodies.jou | 44 ++++++++++++------- tests/404/method_on_class_ptr.jou | 7 --- tests/404/method_on_int.jou | 2 - tests/already_exists_error/bool.jou | 2 - .../method_on_ptr_called_on_class.jou | 11 ----- tests/wrong_type/arrow_instead_of_dot.jou | 7 +++ .../wrong_type/arrow_instead_of_dot_call.jou | 7 +++ tests/wrong_type/arrow_operator_int.jou | 3 ++ tests/wrong_type/arrow_operator_int_call.jou | 3 ++ tests/wrong_type/arrow_operator_intptr.jou | 4 ++ .../wrong_type/arrow_operator_intptr_call.jou | 4 ++ tests/wrong_type/arrow_operator_not_class.jou | 4 -- .../wrong_type/arrow_operator_not_pointer.jou | 3 -- .../arrow_operator_not_pointer_method.jou | 3 -- tests/wrong_type/dot_instead_of_arrow.jou | 6 +++ .../wrong_type/dot_instead_of_arrow_call.jou | 6 +++ tests/wrong_type/dot_operator.jou | 3 -- tests/wrong_type/dot_operator_int.jou | 3 ++ tests/wrong_type/dot_operator_int_call.jou | 3 ++ tests/wrong_type/dot_operator_intptr.jou | 4 ++ tests/wrong_type/dot_operator_intptr_call.jou | 4 ++ 21 files changed, 82 insertions(+), 51 deletions(-) delete mode 100644 tests/404/method_on_class_ptr.jou delete mode 100644 tests/404/method_on_int.jou delete mode 100644 tests/already_exists_error/bool.jou delete mode 100644 tests/other_errors/method_on_ptr_called_on_class.jou create mode 100644 tests/wrong_type/arrow_instead_of_dot.jou create mode 100644 tests/wrong_type/arrow_instead_of_dot_call.jou create mode 100644 tests/wrong_type/arrow_operator_int.jou create mode 100644 tests/wrong_type/arrow_operator_int_call.jou create mode 100644 tests/wrong_type/arrow_operator_intptr.jou create mode 100644 tests/wrong_type/arrow_operator_intptr_call.jou delete mode 100644 tests/wrong_type/arrow_operator_not_class.jou delete mode 100644 tests/wrong_type/arrow_operator_not_pointer.jou delete mode 100644 tests/wrong_type/arrow_operator_not_pointer_method.jou create mode 100644 tests/wrong_type/dot_instead_of_arrow.jou create mode 100644 tests/wrong_type/dot_instead_of_arrow_call.jou delete mode 100644 tests/wrong_type/dot_operator.jou create mode 100644 tests/wrong_type/dot_operator_int.jou create mode 100644 tests/wrong_type/dot_operator_int_call.jou create mode 100644 tests/wrong_type/dot_operator_intptr.jou create mode 100644 tests/wrong_type/dot_operator_intptr_call.jou diff --git a/compiler/typecheck/step3_function_and_method_bodies.jou b/compiler/typecheck/step3_function_and_method_bodies.jou index 904ffc67..595ea962 100644 --- a/compiler/typecheck/step3_function_and_method_bodies.jou +++ b/compiler/typecheck/step3_function_and_method_bodies.jou @@ -569,22 +569,12 @@ def typecheck_function_or_method_call(ft: FileTypes*, call: AstCall*, self_type: if sig == NULL: if self_type == NULL: snprintf(msg, sizeof(msg), "function '%s' not found", call->name) - elif self_type->kind == TypeKind.Class: + else: + assert self_type->kind == TypeKind.Class snprintf( msg, sizeof(msg), "class %s does not have a method named '%s'", self_type->name, call->name) - elif self_type->kind == TypeKind.Pointer and self_type->value_type->find_method(call->name) != NULL: - snprintf( - msg, sizeof(msg), - "the method '%s' is defined on class %s, not on the pointer type %s, so you need to dereference the pointer first (e.g. by using '->' instead of '.')", - call->name, self_type->value_type->name, self_type->name) - else: - snprintf( - msg, sizeof(msg), - "type %s does not have any methods because it is %s, not a class", - self_type->name, self_type->short_description()) - fail(location, msg) if self_type == NULL: @@ -884,8 +874,10 @@ def typecheck_expression(ft: FileTypes*, expr: AstExpression*) -> None: if temptype->kind != TypeKind.Pointer or temptype->value_type->kind != TypeKind.Class: snprintf( msg, sizeof(msg), - "left side of the '->' operator must be a pointer to a class, not %s", + "left side of '->' operator must be a pointer to an instance of a class, not %s", temptype->name) + if temptype->kind == TypeKind.Class and strlen(msg) + 50 < sizeof(msg): + strcat(msg, " (try . instead of ->)") fail(expr->location, msg) result = typecheck_class_field(temptype->value_type, expr->class_field.field_name, expr->location)->type else: @@ -893,8 +885,14 @@ def typecheck_expression(ft: FileTypes*, expr: AstExpression*) -> None: if temptype->kind != TypeKind.Class: snprintf( msg, sizeof(msg), - "left side of the '.' operator must be an instance of a class, not %s", + "left side of '.' operator must be an instance of a class, not %s", temptype->name) + if ( + temptype->kind == TypeKind.Pointer + and temptype->value_type->kind == TypeKind.Class + and strlen(msg) + 50 < sizeof(msg) + ): + strcat(msg, " (try -> instead of .)") fail(expr->location, msg) result = typecheck_class_field(temptype, expr->class_field.field_name, expr->location)->type @@ -903,14 +901,28 @@ def typecheck_expression(ft: FileTypes*, expr: AstExpression*) -> None: result = typecheck_function_or_method_call(ft, &expr->call, NULL, expr->location) elif expr->call.uses_arrow_operator: temptype = typecheck_expression_not_void(ft, expr->call.method_call_self) - if temptype->kind != TypeKind.Pointer: + if temptype->kind != TypeKind.Pointer or temptype->value_type->kind != TypeKind.Class: snprintf(msg, sizeof(msg), - "left side of the '->' operator must be a pointer, not %s", + "left side of '->' operator must be a pointer to an instance of a class, not %s", temptype->name) + if temptype->kind == TypeKind.Class and strlen(msg) + 50 < sizeof(msg): + strcat(msg, " (try . instead of ->)") fail(expr->location, msg) result = typecheck_function_or_method_call(ft, &expr->call, temptype->value_type, expr->location) else: temptype = typecheck_expression_not_void(ft, expr->call.method_call_self) + if temptype->kind != TypeKind.Class: + snprintf(msg, sizeof(msg), + "left side of '.' operator must be an instance of a class, not %s", + temptype->name) + if ( + temptype->kind == TypeKind.Pointer + and temptype->value_type->kind == TypeKind.Class + and strlen(msg) + 50 < sizeof(msg) + ): + strcat(msg, " (try -> instead of .)") + fail(expr->location, msg) + result = typecheck_function_or_method_call(ft, &expr->call, temptype, expr->location) # If self argument is passed by pointer, make sure we can create that pointer diff --git a/tests/404/method_on_class_ptr.jou b/tests/404/method_on_class_ptr.jou deleted file mode 100644 index 87c848f9..00000000 --- a/tests/404/method_on_class_ptr.jou +++ /dev/null @@ -1,7 +0,0 @@ -class Foo: - x: int - -def main() -> int: - f = Foo{x=1} - (&f).bar() # Error: type Foo* does not have any methods because it is a pointer type, not a class - return 0 diff --git a/tests/404/method_on_int.jou b/tests/404/method_on_int.jou deleted file mode 100644 index 9ad051d1..00000000 --- a/tests/404/method_on_int.jou +++ /dev/null @@ -1,2 +0,0 @@ -def foo() -> None: - (1 + 2).asdf() # Error: type int does not have any methods because it is a number type, not a class diff --git a/tests/already_exists_error/bool.jou b/tests/already_exists_error/bool.jou deleted file mode 100644 index 38f92a5d..00000000 --- a/tests/already_exists_error/bool.jou +++ /dev/null @@ -1,2 +0,0 @@ -def foo() -> None: - x = True.asdf() # Error: type bool does not have any methods because it is the built-in bool type, not a class diff --git a/tests/other_errors/method_on_ptr_called_on_class.jou b/tests/other_errors/method_on_ptr_called_on_class.jou deleted file mode 100644 index 4998d191..00000000 --- a/tests/other_errors/method_on_ptr_called_on_class.jou +++ /dev/null @@ -1,11 +0,0 @@ -class Foo: - x: int - - def bar(self) -> None: - return - -def asdf() -> None: - f = Foo{x=1} - f.bar() - (&f)->bar() - (&f).bar() # Error: the method 'bar' is defined on class Foo, not on the pointer type Foo*, so you need to dereference the pointer first (e.g. by using '->' instead of '.') diff --git a/tests/wrong_type/arrow_instead_of_dot.jou b/tests/wrong_type/arrow_instead_of_dot.jou new file mode 100644 index 00000000..d236a9df --- /dev/null +++ b/tests/wrong_type/arrow_instead_of_dot.jou @@ -0,0 +1,7 @@ +class Foo: + n: int + + +def blah() -> None: + f = Foo{} + f->n++ # Error: left side of '->' operator must be a pointer to an instance of a class, not Foo (try . instead of ->) diff --git a/tests/wrong_type/arrow_instead_of_dot_call.jou b/tests/wrong_type/arrow_instead_of_dot_call.jou new file mode 100644 index 00000000..59293d2c --- /dev/null +++ b/tests/wrong_type/arrow_instead_of_dot_call.jou @@ -0,0 +1,7 @@ +class Foo: + n: int + + +def blah() -> None: + f = Foo{} + f->do_stuff() # Error: left side of '->' operator must be a pointer to an instance of a class, not Foo (try . instead of ->) diff --git a/tests/wrong_type/arrow_operator_int.jou b/tests/wrong_type/arrow_operator_int.jou new file mode 100644 index 00000000..ac893504 --- /dev/null +++ b/tests/wrong_type/arrow_operator_int.jou @@ -0,0 +1,3 @@ +def foo() -> None: + num = 1 + x = num->lol # Error: left side of '->' operator must be a pointer to an instance of a class, not int diff --git a/tests/wrong_type/arrow_operator_int_call.jou b/tests/wrong_type/arrow_operator_int_call.jou new file mode 100644 index 00000000..e742e897 --- /dev/null +++ b/tests/wrong_type/arrow_operator_int_call.jou @@ -0,0 +1,3 @@ +def foo() -> None: + num = 1 + x = num->lol() # Error: left side of '->' operator must be a pointer to an instance of a class, not int diff --git a/tests/wrong_type/arrow_operator_intptr.jou b/tests/wrong_type/arrow_operator_intptr.jou new file mode 100644 index 00000000..986b235d --- /dev/null +++ b/tests/wrong_type/arrow_operator_intptr.jou @@ -0,0 +1,4 @@ +def foo() -> None: + num = 1 + pointer = &num + x = pointer->lol # Error: left side of '->' operator must be a pointer to an instance of a class, not int* diff --git a/tests/wrong_type/arrow_operator_intptr_call.jou b/tests/wrong_type/arrow_operator_intptr_call.jou new file mode 100644 index 00000000..5b541da3 --- /dev/null +++ b/tests/wrong_type/arrow_operator_intptr_call.jou @@ -0,0 +1,4 @@ +def foo() -> None: + num = 1 + pointer = &num + x = pointer->lol() # Error: left side of '->' operator must be a pointer to an instance of a class, not int* diff --git a/tests/wrong_type/arrow_operator_not_class.jou b/tests/wrong_type/arrow_operator_not_class.jou deleted file mode 100644 index d2e6fcdf..00000000 --- a/tests/wrong_type/arrow_operator_not_class.jou +++ /dev/null @@ -1,4 +0,0 @@ -def foo() -> None: - num = 1 - pointer = &num - x = pointer->lol # Error: left side of the '->' operator must be a pointer to a class, not int* diff --git a/tests/wrong_type/arrow_operator_not_pointer.jou b/tests/wrong_type/arrow_operator_not_pointer.jou deleted file mode 100644 index 79a816f7..00000000 --- a/tests/wrong_type/arrow_operator_not_pointer.jou +++ /dev/null @@ -1,3 +0,0 @@ -def foo() -> None: - num = 1 - x = num->lol # Error: left side of the '->' operator must be a pointer to a class, not int diff --git a/tests/wrong_type/arrow_operator_not_pointer_method.jou b/tests/wrong_type/arrow_operator_not_pointer_method.jou deleted file mode 100644 index b9b91ddf..00000000 --- a/tests/wrong_type/arrow_operator_not_pointer_method.jou +++ /dev/null @@ -1,3 +0,0 @@ -def foo() -> None: - num = 1 - x = num->lol() # Error: left side of the '->' operator must be a pointer, not int diff --git a/tests/wrong_type/dot_instead_of_arrow.jou b/tests/wrong_type/dot_instead_of_arrow.jou new file mode 100644 index 00000000..62be0aaa --- /dev/null +++ b/tests/wrong_type/dot_instead_of_arrow.jou @@ -0,0 +1,6 @@ +class Foo: + n: int + + +def blah(ptr: Foo*) -> None: + ptr.n++ # Error: left side of '.' operator must be an instance of a class, not Foo* (try -> instead of .) diff --git a/tests/wrong_type/dot_instead_of_arrow_call.jou b/tests/wrong_type/dot_instead_of_arrow_call.jou new file mode 100644 index 00000000..9c88f58f --- /dev/null +++ b/tests/wrong_type/dot_instead_of_arrow_call.jou @@ -0,0 +1,6 @@ +class Foo: + n: int + + +def blah(ptr: Foo*) -> None: + ptr.foo() # Error: left side of '.' operator must be an instance of a class, not Foo* (try -> instead of .) diff --git a/tests/wrong_type/dot_operator.jou b/tests/wrong_type/dot_operator.jou deleted file mode 100644 index 971fb8b9..00000000 --- a/tests/wrong_type/dot_operator.jou +++ /dev/null @@ -1,3 +0,0 @@ -def foo() -> None: - x = 123 - y = x.lolwat # Error: left side of the '.' operator must be an instance of a class, not int diff --git a/tests/wrong_type/dot_operator_int.jou b/tests/wrong_type/dot_operator_int.jou new file mode 100644 index 00000000..6e52940b --- /dev/null +++ b/tests/wrong_type/dot_operator_int.jou @@ -0,0 +1,3 @@ +def foo() -> None: + x = 123 + y = x.lolwat # Error: left side of '.' operator must be an instance of a class, not int diff --git a/tests/wrong_type/dot_operator_int_call.jou b/tests/wrong_type/dot_operator_int_call.jou new file mode 100644 index 00000000..6e0e953f --- /dev/null +++ b/tests/wrong_type/dot_operator_int_call.jou @@ -0,0 +1,3 @@ +def foo() -> None: + x = 123 + y = x.lolwat() # Error: left side of '.' operator must be an instance of a class, not int diff --git a/tests/wrong_type/dot_operator_intptr.jou b/tests/wrong_type/dot_operator_intptr.jou new file mode 100644 index 00000000..bfd3dc50 --- /dev/null +++ b/tests/wrong_type/dot_operator_intptr.jou @@ -0,0 +1,4 @@ +def foo() -> None: + x = 123 + y = &x + z = y.lolwat # Error: left side of '.' operator must be an instance of a class, not int* diff --git a/tests/wrong_type/dot_operator_intptr_call.jou b/tests/wrong_type/dot_operator_intptr_call.jou new file mode 100644 index 00000000..b94cf104 --- /dev/null +++ b/tests/wrong_type/dot_operator_intptr_call.jou @@ -0,0 +1,4 @@ +def foo() -> None: + x = 123 + y = &x + z = y.lolwat() # Error: left side of '.' operator must be an instance of a class, not int*