From 2beec2b00fc7bf40b66107d52a4a43a06819fd80 Mon Sep 17 00:00:00 2001 From: "ocean (they/them)" Date: Sun, 17 Sep 2023 11:34:18 -0400 Subject: [PATCH] GDScript: Add check for `super()` methods not being implemented (cherry picked from commit 729c9b4d4b82c88de6adde34fc060fdb8727d116) --- modules/gdscript/gdscript_analyzer.cpp | 8 ++++++- .../errors/virtual_super_not_implemented.gd | 5 +++++ .../errors/virtual_super_not_implemented.out | 2 ++ .../features/virtual_method_implemented.gd | 21 +++++++++++++++++++ .../features/virtual_method_implemented.out | 3 +++ 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out create mode 100644 modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd create mode 100644 modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 55f0a4aa012..816eb09e631 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -3097,7 +3097,13 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a bool is_constructor = (base_type.is_meta_type || (p_call->callee && p_call->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_call->function_name == SNAME("new"); if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, method_flags)) { - // If the function require typed arrays we must make literals be typed. + // If the method is implemented in the class hierarchy, the virtual flag will not be set for that MethodInfo and the search stops there. + // Virtual check only possible for super() calls because class hierarchy is known. Node/Objects may have scripts attached we don't know of at compile-time. + if (p_call->is_super && method_flags.has_flag(METHOD_FLAG_VIRTUAL)) { + push_error(vformat(R"*(Cannot call the parent class' virtual function "%s()" because it hasn't been defined.)*", p_call->function_name), p_call); + } + + // If the function requires typed arrays we must make literals be typed. for (const KeyValue &E : arrays) { int index = E.key; if (index < par_types.size() && par_types[index].has_container_element_type()) { diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd new file mode 100644 index 00000000000..57dfffdbee7 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd @@ -0,0 +1,5 @@ +func _init(): + super() + +func test(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out new file mode 100644 index 00000000000..e68759223c5 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot call the parent class' virtual function "_init()" because it hasn't been defined. diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd new file mode 100644 index 00000000000..a8641e4f3b8 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd @@ -0,0 +1,21 @@ +class BaseClass: + func _get_property_list(): + return {"property" : "definition"} + +class SuperClassMethodsRecognized extends BaseClass: + func _init(): + # Recognizes super class methods. + var _x = _get_property_list() + +class SuperMethodsRecognized extends BaseClass: + func _get_property_list(): + # Recognizes super method. + var result = super() + result["new"] = "new" + return result + +func test(): + var test1 = SuperClassMethodsRecognized.new() + print(test1._get_property_list()) # Calls base class's method. + var test2 = SuperMethodsRecognized.new() + print(test2._get_property_list()) diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out new file mode 100644 index 00000000000..ccff6601175 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out @@ -0,0 +1,3 @@ +GDTEST_OK +{ "property": "definition" } +{ "property": "definition", "new": "new" }