Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GDScript: Fix common mismatched external parser errors (reverted) #94617

Merged
merged 1 commit into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 109 additions & 35 deletions modules/gdscript/gdscript_analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,15 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
p_source = p_class;
}

Ref<GDScriptParserRef> parser_ref = ensure_cached_parser_for_class(p_class, nullptr, vformat(R"(Trying to resolve class inheritance of "%s" from "%s")", p_class->fqcn, parser->script_path), p_source);
Finally finally([&]() {
GDScriptParser::ClassNode *look_class = p_class;
do {
ensure_cached_parser_for_class(look_class->base_type.class_type, look_class, vformat(R"(Trying to resolve class inheritance of "%s" from "%s")", p_class->fqcn, parser->script_path), p_source);
look_class = look_class->base_type.class_type;
} while (look_class != nullptr);
});

if (p_class->base_type.is_resolving()) {
push_error(vformat(R"(Could not resolve class "%s": Cyclic reference.)", type_from_metatype(p_class->get_datatype()).to_string()), p_source);
return ERR_PARSE_ERROR;
Expand All @@ -323,21 +332,17 @@ Error GDScriptAnalyzer::resolve_class_inheritance(GDScriptParser::ClassNode *p_c
}

if (!parser->has_class(p_class)) {
String script_path = p_class->get_datatype().script_path;
Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path);
if (parser_ref.is_null()) {
push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
// Error already pushed.
return ERR_PARSE_ERROR;
}

Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
if (err) {
push_error(vformat(R"(Could not parse script "%s": %s.)", script_path, error_names[err]), p_source);
push_error(vformat(R"(Could not parse script "%s": %s.)", p_class->get_datatype().script_path, error_names[err]), p_source);
return ERR_PARSE_ERROR;
}

ERR_FAIL_COND_V_MSG(!parser_ref->get_parser()->has_class(p_class), ERR_PARSE_ERROR, R"(Parser bug: Mismatched external parser.)");

GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
GDScriptParser *other_parser = parser_ref->get_parser();

Expand Down Expand Up @@ -883,6 +888,11 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
p_source = member.get_source_node();
}

Ref<GDScriptParserRef> parser_ref = ensure_cached_parser_for_class(p_class, nullptr, vformat(R"(Trying to resolve class member "%s" of "%s" from "%s")", member.get_name(), p_class->fqcn, parser->script_path), p_source);
Finally finally([&]() {
ensure_cached_parser_for_class(member.get_datatype().class_type, p_class, vformat(R"(Trying to resolve class member "%s" of "%s" from "%s")", member.get_name(), p_class->fqcn, parser->script_path), p_source);
});

if (member.get_datatype().is_resolving()) {
push_error(vformat(R"(Could not resolve member "%s": Cyclic reference.)", member.get_name()), p_source);
return;
Expand All @@ -892,42 +902,39 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
return;
}

// If it's already resolving, that's ok.
if (!p_class->base_type.is_resolving()) {
Error err = resolve_class_inheritance(p_class);
if (err) {
return;
}
}

if (!parser->has_class(p_class)) {
String script_path = p_class->get_datatype().script_path;
Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path);
if (parser_ref.is_null()) {
push_error(vformat(R"(Could not find script "%s" (While resolving "%s").)", script_path, member.get_name()), p_source);
// Error already pushed.
return;
}

Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
if (err) {
push_error(vformat(R"(Could not resolve script "%s": %s (While resolving "%s").)", script_path, error_names[err], member.get_name()), p_source);
push_error(vformat(R"(Could not parse script "%s": %s (While resolving member "%s").)", p_class->get_datatype().script_path, error_names[err], member.get_name()), p_source);
return;
}

ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");

GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
GDScriptParser *other_parser = parser_ref->get_parser();

int error_count = other_parser->errors.size();
other_analyzer->resolve_class_member(p_class, p_index);
if (other_parser->errors.size() > error_count) {
push_error(vformat(R"(Could not resolve member "%s".)", member.get_name()), p_source);
return;
}

return;
}

// If it's already resolving, that's ok.
if (!p_class->base_type.is_resolving()) {
Error err = resolve_class_inheritance(p_class);
if (err) {
return;
}
}

GDScriptParser::ClassNode *previous_class = parser->current_class;
parser->current_class = p_class;

Expand Down Expand Up @@ -1170,34 +1177,33 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
p_source = p_class;
}

Ref<GDScriptParserRef> parser_ref = ensure_cached_parser_for_class(p_class, nullptr, vformat(R"(Trying to resolve class interface of "%s" from "%s")", p_class->fqcn, parser->script_path), p_source);

if (!p_class->resolved_interface) {
#ifdef DEBUG_ENABLED
bool has_static_data = p_class->has_static_data;
#endif

if (!parser->has_class(p_class)) {
String script_path = p_class->get_datatype().script_path;
Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path);
if (parser_ref.is_null()) {
push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
// Error already pushed.
return;
}

Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
if (err) {
push_error(vformat(R"(Could not resolve script "%s": %s.)", script_path, error_names[err]), p_source);
push_error(vformat(R"(Could not parse script "%s": %s.)", p_class->get_datatype().script_path, error_names[err]), p_source);
return;
}

ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");

GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
GDScriptParser *other_parser = parser_ref->get_parser();

int error_count = other_parser->errors.size();
other_analyzer->resolve_class_interface(p_class);
if (other_parser->errors.size() > error_count) {
push_error(vformat(R"(Could not resolve class "%s".)", p_class->fqcn), p_source);
return;
}

return;
Expand Down Expand Up @@ -1261,33 +1267,32 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co
p_source = p_class;
}

Ref<GDScriptParserRef> parser_ref = ensure_cached_parser_for_class(p_class, nullptr, vformat(R"(Trying to resolve class body of "%s" from "%s")", p_class->fqcn, parser->script_path), p_source);

if (p_class->resolved_body) {
return;
}

if (!parser->has_class(p_class)) {
String script_path = p_class->get_datatype().script_path;
Ref<GDScriptParserRef> parser_ref = parser->get_depended_parser_for(script_path);
if (parser_ref.is_null()) {
push_error(vformat(R"(Could not find script "%s".)", script_path), p_source);
// Error already pushed.
return;
}

Error err = parser_ref->raise_status(GDScriptParserRef::PARSED);
if (err) {
push_error(vformat(R"(Could not resolve script "%s": %s.)", script_path, error_names[err]), p_source);
push_error(vformat(R"(Could not parse script "%s": %s.)", p_class->get_datatype().script_path, error_names[err]), p_source);
return;
}

ERR_FAIL_COND_MSG(!parser_ref->get_parser()->has_class(p_class), R"(Parser bug: Mismatched external parser.)");

GDScriptAnalyzer *other_analyzer = parser_ref->get_analyzer();
GDScriptParser *other_parser = parser_ref->get_parser();

int error_count = other_parser->errors.size();
other_analyzer->resolve_class_body(p_class);
if (other_parser->errors.size() > error_count) {
push_error(vformat(R"(Could not resolve class "%s".)", p_class->fqcn), p_source);
return;
}

return;
Expand Down Expand Up @@ -3645,12 +3650,81 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str
}
}

Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_parser_for_class(const GDScriptParser::ClassNode *p_class, const GDScriptParser::ClassNode *p_from_class, const String &p_context, const GDScriptParser::Node *p_source) {
if (p_class == nullptr) {
return nullptr;
}

if (parser->has_class(p_class)) {
return nullptr;
}

{
HashMap<const GDScriptParser::ClassNode *, Ref<GDScriptParserRef>>::Iterator E = external_class_parser_cache.find(p_class);
if (E) {
return E->value;
}
}

Ref<GDScriptParserRef> parser_ref;
Ref<GDScriptParserRef> dependant_parser_ref;
GDScriptParser *dependant_parser = parser;
do {
if (HashMap<const GDScriptParser::ClassNode *, Ref<GDScriptParserRef>>::Iterator E = external_class_parser_cache.find(p_from_class)) {
dependant_parser_ref = E->value;
dependant_parser = E->value->get_parser();

// Silently ensure it's parsed.
dependant_parser_ref->raise_status(GDScriptParserRef::PARSED);
}

if (dependant_parser == nullptr) {
continue;
}

if (dependant_parser_ref.is_valid() && dependant_parser->has_class(p_class)) {
external_class_parser_cache.insert(p_class, dependant_parser_ref);
parser_ref = dependant_parser_ref;
break;
}

String script_path = p_class->get_datatype().script_path;
HashMap<String, Ref<GDScriptParserRef>>::Iterator E = dependant_parser->depended_parsers.find(script_path);
if (E) {
// Silently ensure it's parsed.
E->value->raise_status(GDScriptParserRef::PARSED);
if (E->value->get_parser()->has_class(p_class)) {
external_class_parser_cache.insert(p_class, E->value);
parser_ref = E->value;
break;
}
}

dependant_parser_ref = Ref<GDScriptParserRef>();
dependant_parser = nullptr;
p_from_class = p_from_class->base_type.class_type;
} while (p_from_class != nullptr);

if (parser_ref.is_null()) {
push_error(vformat(R"(Parser bug: Could not find external parser. (%s))", p_context), p_source);
}

return parser_ref;
}

Ref<GDScript> GDScriptAnalyzer::get_depended_shallow_script(const String &p_path, Error &r_error) {
// To keep a local cache of the parser for resolving external nodes later.
parser->get_depended_parser_for(p_path);
Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_path, r_error, parser->script_path);
return scr;
}

void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype) {
ERR_FAIL_NULL(p_identifier);

p_identifier->set_datatype(p_identifier_datatype);
Error err = OK;
Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err, parser->script_path);
Ref<GDScript> scr = get_depended_shallow_script(p_identifier_datatype.script_path, err);
if (err) {
push_error(vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path), p_identifier);
return;
Expand Down Expand Up @@ -4340,7 +4414,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
const String &res_type = ResourceLoader::get_resource_type(p_preload->resolved_path);
if (res_type == "GDScript") {
Error err = OK;
Ref<GDScript> res = GDScriptCache::get_shallow_script(p_preload->resolved_path, err, parser->script_path);
Ref<GDScript> res = get_depended_shallow_script(p_preload->resolved_path, err);
p_preload->resource = res;
if (err != OK) {
push_error(vformat(R"(Could not preload resource script "%s".)", p_preload->resolved_path), p_preload->path);
Expand Down Expand Up @@ -4916,7 +4990,7 @@ Array GDScriptAnalyzer::make_array_from_element_datatype(const GDScriptParser::D
Ref<Script> script_type = p_element_datatype.script_type;
if (p_element_datatype.kind == GDScriptParser::DataType::CLASS && script_type.is_null()) {
Error err = OK;
Ref<GDScript> scr = GDScriptCache::get_shallow_script(p_element_datatype.script_path, err, parser->script_path);
Ref<GDScript> scr = get_depended_shallow_script(p_element_datatype.script_path, err);
if (err) {
push_error(vformat(R"(Error while getting cache for script "%s".)", p_element_datatype.script_path), p_source_node);
return array;
Expand Down
15 changes: 15 additions & 0 deletions modules/gdscript/gdscript_analyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,22 @@
class GDScriptAnalyzer {
GDScriptParser *parser = nullptr;

template <typename Fn>
class Finally {
Fn fn;

public:
Finally(Fn p_fn) :
fn(p_fn) {}
~Finally() {
fn();
}
};

const GDScriptParser::EnumNode *current_enum = nullptr;
GDScriptParser::LambdaNode *current_lambda = nullptr;
List<GDScriptParser::LambdaNode *> pending_body_resolution_lambdas;
HashMap<const GDScriptParser::ClassNode *, Ref<GDScriptParserRef>> external_class_parser_cache;
bool static_context = false;

// Tests for detecting invalid overloading of script members
Expand Down Expand Up @@ -132,6 +145,8 @@ class GDScriptAnalyzer {
void resolve_pending_lambda_bodies();
bool class_exists(const StringName &p_class) const;
void reduce_identifier_from_base_set_class(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType p_identifier_datatype);
Ref<GDScriptParserRef> ensure_cached_parser_for_class(const GDScriptParser::ClassNode *p_class, const GDScriptParser::ClassNode *p_from_class, const String &p_context, const GDScriptParser::Node *p_source);
Ref<GDScript> get_depended_shallow_script(const String &p_path, Error &r_error);
#ifdef DEBUG_ENABLED
void is_shadowing(GDScriptParser::IdentifierNode *p_identifier, const String &p_context, const bool p_in_local_scope);
#endif
Expand Down
Loading