2
0
Эх сурвалжийг харах

Fix global script class parsing.

* Broke with #72226
* Restored previous version of the code, made it even more error tolerant.
* Added a warning to **not** change the code.

Fixes #72226.
Juan Linietsky 2 жил өмнө
parent
commit
1bbe0a2b2f

+ 83 - 13
modules/gdscript/gdscript.cpp

@@ -2458,21 +2458,25 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
 
 	GDScriptParser parser;
 	err = parser.parse(source, p_path, false);
-	if (err) {
-		return String();
-	}
-
-	GDScriptAnalyzer analyzer(&parser);
-	err = analyzer.resolve_inheritance();
-	if (err) {
-		return String();
-	}
 
 	const GDScriptParser::ClassNode *c = parser.get_tree();
-
-	if (r_base_type) {
-		*r_base_type = c->get_datatype().native_type;
-	}
+	if (!c) {
+		return String(); // No class parsed.
+	}
+
+	/* **WARNING**
+	 *
+	 * This function is written with the goal to be *extremely* error tolerant, as such
+	 * it should meet the following requirements:
+	 *
+	 * - It must not rely on the analyzer (in fact, the analyzer must not be used here),
+	 *   because at the time global classes are parsed, the dependencies may not be present
+	 *   yet, hence the function will fail (which is unintended).
+	 * - It must not fail even if the parsing fails, because even if the file is broken,
+	 *   it should attempt its best to retrieve the inheritance metadata.
+	 *
+	 * Before changing this function, please ask the current maintainer of EditorFileSystem.
+	 */
 
 	if (r_icon_path) {
 		if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) {
@@ -2481,7 +2485,73 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
 			*r_icon_path = p_path.get_base_dir().path_join(c->icon_path).simplify_path();
 		}
 	}
+	if (r_base_type) {
+		const GDScriptParser::ClassNode *subclass = c;
+		String path = p_path;
+		GDScriptParser subparser;
+		while (subclass) {
+			if (subclass->extends_used) {
+				if (!subclass->extends_path.is_empty()) {
+					if (subclass->extends.size() == 0) {
+						get_global_class_name(subclass->extends_path, r_base_type);
+						subclass = nullptr;
+						break;
+					} else {
+						Vector<StringName> extend_classes = subclass->extends;
+
+						Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ);
+						if (subfile.is_null()) {
+							break;
+						}
+						String subsource = subfile->get_as_utf8_string();
+
+						if (subsource.is_empty()) {
+							break;
+						}
+						String subpath = subclass->extends_path;
+						if (subpath.is_relative_path()) {
+							subpath = path.get_base_dir().path_join(subpath).simplify_path();
+						}
 
+						if (OK != subparser.parse(subsource, subpath, false)) {
+							break;
+						}
+						path = subpath;
+						subclass = subparser.get_tree();
+
+						while (extend_classes.size() > 0) {
+							bool found = false;
+							for (int i = 0; i < subclass->members.size(); i++) {
+								if (subclass->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {
+									continue;
+								}
+
+								const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class;
+								if (inner_class->identifier->name == extend_classes[0]) {
+									extend_classes.remove_at(0);
+									found = true;
+									subclass = inner_class;
+									break;
+								}
+							}
+							if (!found) {
+								subclass = nullptr;
+								break;
+							}
+						}
+					}
+				} else if (subclass->extends.size() == 1) {
+					*r_base_type = subclass->extends[0];
+					subclass = nullptr;
+				} else {
+					break;
+				}
+			} else {
+				*r_base_type = "RefCounted";
+				subclass = nullptr;
+			}
+		}
+	}
 	return c->identifier != nullptr ? String(c->identifier->name) : String();
 }
 

+ 1 - 1
modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd

@@ -1,5 +1,5 @@
 # Error here. Annotations should be used before `class_name`, not after.
-class_name HelloWorld
+class_name WrongAnnotationPlace
 @icon("res://path/to/optional/icon.svg")
 
 func test():