From 058a46f1549b1dc549ba82509554313ac258aed3 Mon Sep 17 00:00:00 2001 From: Leonard Pop Date: Mon, 2 Feb 2026 17:05:40 +0200 Subject: [PATCH] Inherit parent method return types for untyped overrides --- modules/gdscript/gdscript_analyzer.cpp | 4 ++- ...typed_override_return_incompatible_type.gd | 14 +++++++++++ ...yped_override_return_incompatible_type.out | 6 +++++ .../function_return_type_covariance.gd | 6 ++--- ...untyped_override_return_compatible_type.gd | 25 +++++++++++++++++++ ...ntyped_override_return_compatible_type.out | 6 +++++ .../features/virtual_method_implemented.gd | 4 +-- .../features/virtual_method_implemented.out | 4 +-- .../features/warning_ignore_warnings.gd | 2 +- ...yped_override_return_incompatible_value.gd | 22 ++++++++++++++++ ...ped_override_return_incompatible_value.out | 9 +++++++ 11 files changed, 93 insertions(+), 9 deletions(-) create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/untyped_override_return_incompatible_type.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/untyped_override_return_incompatible_type.out create mode 100644 modules/gdscript/tests/scripts/analyzer/features/untyped_override_return_compatible_type.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/features/untyped_override_return_compatible_type.out create mode 100644 modules/gdscript/tests/scripts/runtime/errors/untyped_override_return_incompatible_value.gd create mode 100644 modules/gdscript/tests/scripts/runtime/errors/untyped_override_return_incompatible_value.out diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 23182d0a9b7..dbd5071bd29 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -1875,7 +1875,9 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * if (!p_is_lambda && get_function_signature(p_function, false, base_type, function_name, parent_return_type, parameters_types, default_par_count, method_flags, &native_base)) { bool valid = p_function->is_static == method_flags.has_flag(METHOD_FLAG_STATIC); - if (p_function->return_type != nullptr) { + if (p_function->return_type == nullptr) { + p_function->set_datatype(parent_return_type); + } else { // Check return type covariance. GDScriptParser::DataType return_type = p_function->get_datatype(); if (return_type.is_variant()) { diff --git a/modules/gdscript/tests/scripts/analyzer/errors/untyped_override_return_incompatible_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/untyped_override_return_incompatible_type.gd new file mode 100644 index 00000000000..bbc2bf0f20c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/untyped_override_return_incompatible_type.gd @@ -0,0 +1,14 @@ +class A: + func return_void() -> void: pass + func return_int() -> int: return 123 + func return_node(_resource: Resource) -> Node: return null + func return_variant() -> Variant: return null + +class B extends A: + func return_void(): return 1 + func return_int(): return "abc" + func return_node(resource: Resource): return resource + func return_variant(): pass + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/untyped_override_return_incompatible_type.out b/modules/gdscript/tests/scripts/analyzer/errors/untyped_override_return_incompatible_type.out new file mode 100644 index 00000000000..b8b685bdca0 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/untyped_override_return_incompatible_type.out @@ -0,0 +1,6 @@ +GDTEST_ANALYZER_ERROR +>> ERROR at line 8: A void function cannot return a value. +>> ERROR at line 9: Cannot return a value of type "String" as "int". +>> ERROR at line 9: Cannot return value of type "String" because the function return type is "int". +>> ERROR at line 10: Cannot return value of type "Resource" because the function return type is "Node". +>> ERROR at line 11: Not all code paths return a value. diff --git a/modules/gdscript/tests/scripts/analyzer/features/function_return_type_covariance.gd b/modules/gdscript/tests/scripts/analyzer/features/function_return_type_covariance.gd index 4de50b67316..84bcdf2cf22 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/function_return_type_covariance.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/function_return_type_covariance.gd @@ -24,9 +24,9 @@ class B extends A: func untyped_to_node() -> Node: return null func void_to_untyped(): pass - func variant_to_untyped(): pass - func int_to_untyped(): pass - func node_to_untyped(): pass + func variant_to_untyped(): return null + func int_to_untyped(): return 0 + func node_to_untyped(): return null func test(): pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/untyped_override_return_compatible_type.gd b/modules/gdscript/tests/scripts/analyzer/features/untyped_override_return_compatible_type.gd new file mode 100644 index 00000000000..ebccc92c255 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/untyped_override_return_compatible_type.gd @@ -0,0 +1,25 @@ +class A: + func return_float() -> float: return 1.0 + func return_int_as_float(_x: int) -> float: return 1.0 + func return_variant_as_float(_x: Variant) -> float: return 1.0 + func return_float_array() -> Array[float]: return [1.0] + func return_float_dict() -> Dictionary[float, float]: return {1.0: 1.0} + +class B extends A: + func return_float(): return 2 + func return_int_as_float(x: int): return x + func return_variant_as_float(x: Variant): return x + func return_float_array(): return [2] + func return_float_dict(): return {2: 2} + +func output(value: Variant) -> void: + print(var_to_str(value).replace("\n", "")) + +func test(): + var b := B.new() + + output(b.return_float()) + output(b.return_int_as_float(2)) + output(b.return_variant_as_float(2)) + output(b.return_float_array()) + output(b.return_float_dict()) diff --git a/modules/gdscript/tests/scripts/analyzer/features/untyped_override_return_compatible_type.out b/modules/gdscript/tests/scripts/analyzer/features/untyped_override_return_compatible_type.out new file mode 100644 index 00000000000..90e60450a05 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/untyped_override_return_compatible_type.out @@ -0,0 +1,6 @@ +GDTEST_OK +2.0 +2.0 +2.0 +Array[float]([2.0]) +Dictionary[float, float]({2.0: 2.0}) diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd index a8641e4f3b8..aac33440b31 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd @@ -1,6 +1,6 @@ class BaseClass: func _get_property_list(): - return {"property" : "definition"} + return [{"property" : "definition"}] class SuperClassMethodsRecognized extends BaseClass: func _init(): @@ -11,7 +11,7 @@ class SuperMethodsRecognized extends BaseClass: func _get_property_list(): # Recognizes super method. var result = super() - result["new"] = "new" + result[0]["new"] = "new" return result func test(): diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out index ccff6601175..1b4da2cd571 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out +++ b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out @@ -1,3 +1,3 @@ GDTEST_OK -{ "property": "definition" } -{ "property": "definition", "new": "new" } +[{ "property": "definition" }] +[{ "property": "definition", "new": "new" }] diff --git a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd index 333950d64e9..eb751c4db76 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/warning_ignore_warnings.gd @@ -149,7 +149,7 @@ func test_unsafe_void_return() -> void: @warning_ignore("native_method_override") func get_class(): - pass + return "" # We don't want to execute it because of errors, just analyze. func test(): diff --git a/modules/gdscript/tests/scripts/runtime/errors/untyped_override_return_incompatible_value.gd b/modules/gdscript/tests/scripts/runtime/errors/untyped_override_return_incompatible_value.gd new file mode 100644 index 00000000000..c16699a2522 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/untyped_override_return_incompatible_value.gd @@ -0,0 +1,22 @@ +class A: + func return_int(_variant: Variant) -> int: return 123 + func return_int_array(_variant: Variant) -> Array[int]: return [1] + func return_int_dict(_variant: Variant) -> Dictionary[int, int]: return {1: 1} + func return_node(_variant: Variant) -> Node: return null + +class B extends A: + func return_int(variant: Variant): return variant + func return_int_array(variant: Variant): return variant + func return_int_dict(variant: Variant): return variant + func return_node(variant: Variant): return variant + +func output(value: Variant) -> void: + print(var_to_str(value).replace("\n", "")) + +func test(): + var b := B.new() + + output(b.return_int("abc")) + output(b.return_int_array("abc")) + output(b.return_int_dict("abc")) + output(b.return_node("abc")) diff --git a/modules/gdscript/tests/scripts/runtime/errors/untyped_override_return_incompatible_value.out b/modules/gdscript/tests/scripts/runtime/errors/untyped_override_return_incompatible_value.out new file mode 100644 index 00000000000..4bb6c4c0467 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/errors/untyped_override_return_incompatible_value.out @@ -0,0 +1,9 @@ +GDTEST_RUNTIME_ERROR +>> SCRIPT ERROR at runtime/errors/untyped_override_return_incompatible_value.gd:8 on B.return_int(): Trying to return value of type "String" from a function whose return type is "int". +0 +>> SCRIPT ERROR at runtime/errors/untyped_override_return_incompatible_value.gd:9 on B.return_int_array(): Trying to return value of type "String" from a function whose return type is "Array[int]". +Array[int]([]) +>> SCRIPT ERROR at runtime/errors/untyped_override_return_incompatible_value.gd:10 on B.return_int_dict(): Trying to return a value of type "String" where expected return type is "Dictionary[int, int]". +Dictionary[int, int]({}) +>> SCRIPT ERROR at runtime/errors/untyped_override_return_incompatible_value.gd:11 on B.return_node(): Trying to return value of type "String" from a function whose return type is "Node". +null