summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/gdscript/gdscript.cpp96
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd2
2 files changed, 84 insertions, 14 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index d9b8a540c0..8b5a08f25a 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/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();
}
diff --git a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd b/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
index 0085b3f367..2470fe978e 100644
--- a/modules/gdscript/tests/scripts/parser/errors/class_name_after_annotation.gd
+++ b/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():