From c2cfaf531b40d0a1fea5f8c3ada0476ec8efbf79 Mon Sep 17 00:00:00 2001 From: Adam Chidlow Date: Fri, 10 May 2024 14:49:21 +0800 Subject: [PATCH] fix: improve error messages when typing.Any type is encountered --- src/puya/awst_build/context.py | 30 +++++++++++++++++-- src/puya/awst_build/subroutine.py | 4 +-- .../test_expected_output/expected_errors.test | 4 +-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/puya/awst_build/context.py b/src/puya/awst_build/context.py index f717edffa5..c4d10e467f 100644 --- a/src/puya/awst_build/context.py +++ b/src/puya/awst_build/context.py @@ -186,9 +186,9 @@ def type_to_pytype( return pytypes.NoneType case mypy.types.UninhabitedType(): raise CodeError("Cannot resolve empty type", loc) - case mypy.types.AnyType(): - # TODO: look at type_of_any to improve error message - raise CodeError("Any type is not supported", loc) + case mypy.types.AnyType(type_of_any=type_of_any): + msg = _type_of_any_to_error_message(type_of_any, loc) + raise CodeError(msg, loc) case mypy.types.FunctionLike() as func_like: if func_like.is_type_obj(): msg = "References to type objects are not supported" @@ -219,3 +219,27 @@ def _parameterise_pytype( type_args_resolved.append(self.type_to_pytype(ta, source_location=loc)) result = generic.parameterise(type_args_resolved, loc) return result + + +def _type_of_any_to_error_message(type_of_any: int, source_location: SourceLocation) -> str: + from mypy.types import TypeOfAny + + match type_of_any: + case TypeOfAny.unannotated: + msg = "type annotation is required at this location" + case TypeOfAny.explicit | TypeOfAny.from_another_any: + msg = "Any type is not supported" + case TypeOfAny.from_unimported_type: + msg = "unknown type from import" + case TypeOfAny.from_omitted_generics: + msg = "type parameters are required at this location" + case TypeOfAny.from_error: + msg = "typing error prevents type resolution" + case TypeOfAny.special_form: + msg = "unsupported type form" + case TypeOfAny.implementation_artifact | TypeOfAny.suggestion_engine: + msg = "mypy cannot handle this type form, try providing an explicit annotation" + case _: + logger.debug(f"Unknown TypeOfAny value: {type_of_any}", location=source_location) + msg = "Any type is not supported" + return msg diff --git a/src/puya/awst_build/subroutine.py b/src/puya/awst_build/subroutine.py index 33520ea8d4..676e96bd1d 100644 --- a/src/puya/awst_build/subroutine.py +++ b/src/puya/awst_build/subroutine.py @@ -368,8 +368,8 @@ def _handle_proxy_assignment( raise InternalError("Assignment to self outside of a contract class", stmt_loc) if self.func_def.name != "__init__": raise CodeError( - f"{rvalue_pytyp} can only be assigned to a member variable" - " in the __init__ method", + f"{rvalue_pytyp.generic or rvalue_pytyp}" + " can only be assigned to a member variable in the __init__ method", stmt_loc, ) cref = self.contract_method_info.cref diff --git a/tests/test_expected_output/expected_errors.test b/tests/test_expected_output/expected_errors.test index f03062f7cd..0fed0cab53 100644 --- a/tests/test_expected_output/expected_errors.test +++ b/tests/test_expected_output/expected_errors.test @@ -299,7 +299,7 @@ class Derived3(Base): class Derived4(Base): def __init__(self) -> None: super().__init__() - self.bar_p = GlobalState(Bytes(b"bbb")) # type: ignore[arg-type] ## E: some error message here about redefining proxies + self.bar_p = GlobalState(Bytes(b"bbb")) # type: ignore[arg-type] ## E: Assignment target type algopy.UInt64 differs from expression value type algopy.Bytes ## case: void_state @@ -309,7 +309,7 @@ class Con(Contract): x: None ## E: None is not supported as a value, only a return type def approval_program(self) -> bool: - self.x = log(b"") # type: ignore[func-returns-value] ## E: None indicates an empty return and cannot be assigned + self.x = log(b"") # type: ignore[func-returns-value] ## E: typing error prevents type resolution return True def clear_state_program(self) -> bool: