Browse Source

Merge pull request #70700 from vonagam/fix-abstract-extends

GDScript: Fix extending abstract classes, forbid their construction
Rémi Verschelde 2 năm trước cách đây
mục cha
commit
def592114f

+ 5 - 0
editor/create_dialog.cpp

@@ -148,6 +148,11 @@ bool CreateDialog::_should_hide_type(const String &p_type) const {
 			return true;
 		}
 
+		StringName native_type = ScriptServer::get_global_class_native_base(p_type);
+		if (ClassDB::class_exists(native_type) && !ClassDB::can_instantiate(native_type)) {
+			return true;
+		}
+
 		String script_path = ScriptServer::get_global_class_path(p_type);
 		if (script_path.begins_with("res://addons/")) {
 			if (!EditorNode::get_singleton()->is_addon_plugin_enabled(script_path.get_slicec('/', 3))) {

+ 20 - 12
modules/gdscript/gdscript_analyzer.cpp

@@ -431,7 +431,7 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
 					return err;
 				}
 				base = info_parser->get_parser()->head->get_datatype();
-			} else if (class_exists(name) && ClassDB::can_instantiate(name)) {
+			} else if (class_exists(name)) {
 				base.kind = GDScriptParser::DataType::NATIVE;
 				base.native_type = name;
 			} else {
@@ -4010,6 +4010,25 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
 		return false;
 	}
 
+	StringName base_native = p_base_type.native_type;
+	if (base_native != StringName()) {
+		// Empty native class might happen in some Script implementations.
+		// Just ignore it.
+		if (!class_exists(base_native)) {
+			push_error(vformat("Native class %s used in script doesn't exist or isn't exposed.", base_native), p_source);
+			return false;
+		} else if (p_is_constructor && !ClassDB::can_instantiate(base_native)) {
+			if (p_base_type.kind == GDScriptParser::DataType::CLASS) {
+				push_error(vformat(R"(Class "%s" cannot be constructed as it is based on abstract native class "%s".)", p_base_type.class_type->fqcn.get_file(), base_native), p_source);
+			} else if (p_base_type.kind == GDScriptParser::DataType::SCRIPT) {
+				push_error(vformat(R"(Script "%s" cannot be constructed as it is based on abstract native class "%s".)", p_base_type.script_path.get_file(), base_native), p_source);
+			} else {
+				push_error(vformat(R"(Native class "%s" cannot be constructed as it is abstract.)", base_native), p_source);
+			}
+			return false;
+		}
+	}
+
 	if (p_is_constructor) {
 		function_name = "_init";
 		r_static = true;
@@ -4070,17 +4089,6 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
 		}
 	}
 
-	StringName base_native = p_base_type.native_type;
-#ifdef DEBUG_ENABLED
-	if (base_native != StringName()) {
-		// Empty native class might happen in some Script implementations.
-		// Just ignore it.
-		if (!class_exists(base_native)) {
-			ERR_FAIL_V_MSG(false, vformat("Native class %s used in script doesn't exist or isn't exposed.", base_native));
-		}
-	}
-#endif
-
 	if (p_is_constructor) {
 		// Native types always have a default constructor.
 		r_return_type = p_base_type;

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd

@@ -0,0 +1,2 @@
+func test():
+	CanvasItem.new()

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Native class "CanvasItem" cannot be constructed as it is abstract.

+ 9 - 0
modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd

@@ -0,0 +1,9 @@
+class A extends CanvasItem:
+	func _init():
+		print('no')
+
+class B extends A:
+	pass
+
+func test():
+	B.new()

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "CanvasItem".

+ 12 - 0
modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.gd

@@ -0,0 +1,12 @@
+class A extends CanvasItem:
+	func _init():
+		pass
+
+class B extends A:
+	pass
+
+class C extends CanvasItem:
+	pass
+
+func test():
+	print('ok')

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/features/extend_abstract_class.out

@@ -0,0 +1,2 @@
+GDTEST_OK
+ok