Просмотр исходного кода

Merge pull request #70000 from rune-scape/find-more-classes

GDScript: Fix built-in script `find_class` bugs
Rémi Verschelde 2 лет назад
Родитель
Сommit
8d52eea52b

+ 16 - 10
modules/gdscript/gdscript.cpp

@@ -1111,21 +1111,27 @@ bool GDScript::inherits_script(const Ref<Script> &p_script) const {
 GDScript *GDScript::find_class(const String &p_qualified_name) {
 	String first = p_qualified_name.get_slice("::", 0);
 
+	Vector<String> class_names;
 	GDScript *result = nullptr;
+	// Empty initial name means start here.
 	if (first.is_empty() || first == name) {
+		class_names = p_qualified_name.split("::");
 		result = this;
-	} else if (first == get_root_script()->path) {
+	} else if (p_qualified_name.begins_with(get_root_script()->path)) {
+		// Script path could have a class path separator("::") in it.
+		class_names = p_qualified_name.trim_prefix(get_root_script()->path).split("::");
 		result = get_root_script();
 	} else if (HashMap<StringName, Ref<GDScript>>::Iterator E = subclasses.find(first)) {
+		class_names = p_qualified_name.split("::");
 		result = E->value.ptr();
 	} else if (_owner != nullptr) {
 		// Check parent scope.
 		return _owner->find_class(p_qualified_name);
 	}
 
-	int name_count = p_qualified_name.get_slice_count("::");
-	for (int i = 1; result != nullptr && i < name_count; i++) {
-		String current_name = p_qualified_name.get_slice("::", i);
+	// Starts at index 1 because index 0 was handled above.
+	for (int i = 1; result != nullptr && i < class_names.size(); i++) {
+		String current_name = class_names[i];
 		if (HashMap<StringName, Ref<GDScript>>::Iterator E = result->subclasses.find(current_name)) {
 			result = E->value.ptr();
 		} else {
@@ -1137,11 +1143,12 @@ GDScript *GDScript::find_class(const String &p_qualified_name) {
 	return result;
 }
 
-bool GDScript::is_subclass(const GDScript *p_script) {
+bool GDScript::has_class(const GDScript *p_script) {
 	String fqn = p_script->fully_qualified_name;
-	if (!fqn.is_empty() && fqn != fully_qualified_name && fqn.begins_with(fully_qualified_name)) {
-		String fqn_rest = fqn.substr(fully_qualified_name.length());
-		return find_class(fqn_rest) == p_script;
+	if (fully_qualified_name.is_empty() && fqn.get_slice("::", 0).is_empty()) {
+		return p_script == this;
+	} else if (fqn.begins_with(fully_qualified_name)) {
+		return p_script == find_class(fqn.trim_prefix(fully_qualified_name));
 	}
 	return false;
 }
@@ -2592,8 +2599,7 @@ Ref<GDScript> GDScriptLanguage::get_script_by_fully_qualified_name(const String
 		SelfList<GDScript> *elem = script_list.first();
 		while (elem) {
 			GDScript *scr = elem->self();
-			scr = scr->find_class(p_name);
-			if (scr != nullptr) {
+			if (scr->fully_qualified_name == p_name) {
 				return scr;
 			}
 			elem = elem->next();

+ 1 - 1
modules/gdscript/gdscript.h

@@ -187,7 +187,7 @@ public:
 	bool inherits_script(const Ref<Script> &p_script) const override;
 
 	GDScript *find_class(const String &p_qualified_name);
-	bool is_subclass(const GDScript *p_script);
+	bool has_class(const GDScript *p_script);
 	GDScript *get_root_script();
 	bool is_root_script() const { return _owner == nullptr; }
 	String get_fully_qualified_name() const { return fully_qualified_name; }

+ 15 - 25
modules/gdscript/gdscript_analyzer.cpp

@@ -3996,37 +3996,27 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
 				result.kind = GDScriptParser::DataType::CLASS;
 				// This might be an inner class, so we want to get the parser for the root.
 				// But still get the inner class from that tree.
-				GDScript *current = gds.ptr();
-				List<StringName> class_chain;
-				while (current->_owner) {
-					// Push to front so it's in reverse.
-					class_chain.push_front(current->name);
-					current = current->_owner;
-				}
-
-				Ref<GDScriptParserRef> ref = get_parser_for(current->get_path());
+				String script_path = gds->get_script_path();
+				Ref<GDScriptParserRef> ref = get_parser_for(script_path);
 				if (ref.is_null()) {
-					push_error("Could not find script in path.", p_source);
+					push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
 					GDScriptParser::DataType error_type;
 					error_type.kind = GDScriptParser::DataType::VARIANT;
 					return error_type;
 				}
-				ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
-
-				GDScriptParser::ClassNode *found = ref->get_parser()->head;
-
-				for (const StringName &E : class_chain) {
-					if (!found->has_member(E)) {
-						return GDScriptParser::DataType();
-					}
-
-					if (found->get_member(E).type != GDScriptParser::ClassNode::Member::CLASS) {
-						return GDScriptParser::DataType();
+				Error err = ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED);
+				GDScriptParser::ClassNode *found = nullptr;
+				if (err == OK) {
+					found = ref->get_parser()->find_class(gds->fully_qualified_name);
+					if (found != nullptr) {
+						err = resolve_class_inheritance(found, p_source);
 					}
-
-					resolve_class_member(found, E, p_source);
-
-					found = found->get_member(E).m_class;
+				}
+				if (err || found == nullptr) {
+					push_error(vformat(R"(Could not resolve script "%s".)", script_path), p_source);
+					GDScriptParser::DataType error_type;
+					error_type.kind = GDScriptParser::DataType::VARIANT;
+					return error_type;
 				}
 
 				result.class_type = found;

+ 2 - 3
modules/gdscript/gdscript_compiler.cpp

@@ -117,8 +117,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
 			result.builtin_type = p_datatype.builtin_type;
 			result.native_type = p_datatype.native_type;
 
-			String root_name = p_datatype.class_type->fqcn.get_slice("::", 0);
-			bool is_local_class = !root_name.is_empty() && root_name == main_script->fully_qualified_name;
+			bool is_local_class = parser->has_class(p_datatype.class_type);
 
 			Ref<GDScript> script;
 			if (is_local_class) {
@@ -2300,7 +2299,7 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
 				return ERR_COMPILATION_FAILED;
 			}
 
-			if (base.ptr() == main_script || main_script->is_subclass(base.ptr())) {
+			if (main_script->has_class(base.ptr())) {
 				Error err = _populate_class_members(base.ptr(), p_class->base_type.class_type, p_keep_state);
 				if (err) {
 					return err;