Merge pull request #72444 from reduz/fix-global-class-parsing

Fix global script class parsing.
This commit is contained in:
Rémi Verschelde 2023-01-31 13:20:00 +01:00
commit e768e02b78
No known key found for this signature in database
GPG key ID: C3336907360768E1
2 changed files with 83 additions and 13 deletions

View file

@ -2437,22 +2437,26 @@ 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()) {
*r_icon_path = c->icon_path.simplify_path();
@ -2460,7 +2464,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();
}

View file

@ -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():