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

Conversation

rune-scape
Copy link
Contributor

@rune-scape rune-scape commented Jul 22, 2024

tried to make this simple and thorough without fundamentally changing how GDScriptCache or GDScriptAnalyzer works

adds a cache of external class nodes that have been requested to be resolved, that map to the GDScriptParserRef that owns the class node

then the external resolve code can use it to find the parser that owns the node it wants to resolve
this is all needed to accommodates multiple different parser trees of the same script in the dependency tree

https://github.com/Zylann/godot_heightmap_plugin was excellent in demonstrating weird edge cases, i feel confident this PR solves the issue generally

also added much more descriptive errors

fixes #94244

@rune-scape rune-scape requested a review from a team as a code owner July 22, 2024 10:29
@akien-mga akien-mga added this to the 4.3 milestone Jul 22, 2024
@akien-mga
Copy link
Member

There's a failing unit test:

./modules/gdscript/tests/gdscript_test_runner_suite.h:43:
TEST SUITE: [Modules][GDScript]
TEST CASE:  Script compilation and runtime

modules/gdscript/tests/gdscript_test_runner.cpp:217: ERROR: CHECK( result.passed ) is NOT correct!
  values: CHECK( false )
  logged: /home/runner/work/godot/godot/modules/gdscript/tests/scripts/analyzer/features/base_outer_resolution.gd
          

./modules/gdscript/tests/gdscript_test_runner_suite.h:49: FATAL ERROR: REQUIRE( fail_count == 0 ) is NOT correct!
  values: REQUIRE( 1 == 0 )
  logged: Make sure `*.out` files have expected results.
          All GDScript tests should pass.

modules/gdscript/gdscript_analyzer.h Outdated Show resolved Hide resolved
modules/gdscript/gdscript_analyzer.cpp Outdated Show resolved Hide resolved
modules/gdscript/gdscript_analyzer.cpp Outdated Show resolved Hide resolved
modules/gdscript/gdscript_analyzer.cpp Outdated Show resolved Hide resolved
modules/gdscript/gdscript_analyzer.cpp Outdated Show resolved Hide resolved
modules/gdscript/gdscript_analyzer.cpp Outdated Show resolved Hide resolved
modules/gdscript/gdscript_analyzer.cpp Outdated Show resolved Hide resolved
modules/gdscript/gdscript_analyzer.cpp Outdated Show resolved Hide resolved
@rune-scape rune-scape force-pushed the fix-mismatched-parsers branch from 85d5dbf to 1ecfdde Compare July 23, 2024 11:16
@akien-mga
Copy link
Member

akien-mga commented Jul 23, 2024

I can't speak to the implementation, but I confirm it fixes the regression from #92616 identified in #94244.

The Finally/lambda pattern used here seems new in the codebase. It's local to GDScriptAnalyzer so I'm not too concerned, I'll let the team evaluate whether they like this kind of approach.

@rune-scape
Copy link
Contributor Author

the failing test helped me generalize it more, its much simpler and more robust now

@vnen
Copy link
Member

vnen commented Jul 23, 2024

It is a bit complex but it looks okay to me. I just really don't understand the need of Finally here. Is there a problem in executing the contents right away instead of postponing it to when the function returns?

@rune-scape
Copy link
Contributor Author

it searches the depended parsers of other parsers, so it has to be delayed until they actually resolve and call get_depended_parser_for or it might not yet depend on the parser we need

and the finally pattern is of course to make the code much cleaner

@rune-scape rune-scape force-pushed the fix-mismatched-parsers branch from a5437d7 to c75225f Compare July 23, 2024 19:25
Copy link
Member

@akien-mga akien-mga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving as I tested it fixes the bug, and I need it for 4.3 RC 1.
Discussion can continue post-merge on implementation details.

@akien-mga akien-mga merged commit e91725f into godotengine:master Jul 24, 2024
18 checks passed
@akien-mga
Copy link
Member

Thanks!

@ydeltastar
Copy link
Contributor

ydeltastar commented Jul 24, 2024

I'm getting a nullptr exception at the bellow line when I try opening a scene after this PR.

p_from_class = p_from_class->base_type.class_type;

Coming from here:

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);

I don't know how the analyzer system works but the second argument (which is p_from_class) is nullptr so this code path always throws an exception.

Call stack:

godot.windows.editor.dev.x86_64.exe!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) Line 3705 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:3705)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode * p_class, int p_index, const GDScriptParser::Node * p_source) Line 891 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:891)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode * p_class, const StringName & p_name, const GDScriptParser::Node * p_source) Line 881 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:881)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNode * p_identifier, GDScriptParser::DataType * p_base) Line 3865 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:3865)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode * p_subscript, bool p_can_be_pseudo_type) Line 4526 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:4526)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode * p_subscript, bool p_can_be_pseudo_type) Line 4454 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:4454)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode * p_expression, bool p_is_root) Line 2500 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:2500)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode * p_assignment) Line 2673 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:2673)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode * p_expression, bool p_is_root) Line 2464 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:2464)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_node(GDScriptParser::Node * p_node, bool p_is_root) Line 1540 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:1540)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode * p_suite) Line 1868 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:1868)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_if(GDScriptParser::IfNode * p_if) Line 2045 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:2045)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_node(GDScriptParser::Node * p_node, bool p_is_root) Line 1476 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:1476)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode * p_suite) Line 1868 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:1868)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode * p_function, bool p_is_lambda) Line 1818 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:1818)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode * p_class, const GDScriptParser::Node * p_source) Line 1339 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:1339)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode * p_class, bool p_recursive) Line 1445 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:1445)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::resolve_body() Line 5847 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:5847)
godot.windows.editor.dev.x86_64.exe!GDScriptAnalyzer::analyze() Line 5873 (c:\godot\godot\modules\gdscript\gdscript_analyzer.cpp:5873)
godot.windows.editor.dev.x86_64.exe!GDScript::reload(bool p_keep_state) Line 820 (c:\godot\godot\modules\gdscript\gdscript.cpp:820)
godot.windows.editor.dev.x86_64.exe!GDScriptCache::get_full_script(const String & p_path, Error & r_error, const String & p_owner, bool p_update_from_disk) Line 379 (c:\godot\godot\modules\gdscript\gdscript_cache.cpp:379)
godot.windows.editor.dev.x86_64.exe!ResourceFormatLoaderGDScript::load(const String & p_path, const String & p_original_path, Error * r_error, bool p_use_sub_threads, float * r_progress, ResourceFormatLoader::CacheMode p_cache_mode) Line 2932 (c:\godot\godot\modules\gdscript\gdscript.cpp:2932)
godot.windows.editor.dev.x86_64.exe!ResourceLoader::_load(const String & p_path, const String & p_original_path, const String & p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error * r_error, bool p_use_sub_threads, float * r_progress) Line 269 (c:\godot\godot\core\io\resource_loader.cpp:269)
godot.windows.editor.dev.x86_64.exe!ResourceLoader::_thread_load_function(void * p_userdata) Line 323 (c:\godot\godot\core\io\resource_loader.cpp:323)
godot.windows.editor.dev.x86_64.exe!ResourceLoader::_load_start(const String & p_path, const String & p_type_hint, ResourceLoader::LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode) Line 527 (c:\godot\godot\core\io\resource_loader.cpp:527)
godot.windows.editor.dev.x86_64.exe!ResourceLoaderText::load() Line 472 (c:\godot\godot\scene\resources\resource_format_text.cpp:472)
godot.windows.editor.dev.x86_64.exe!ResourceFormatLoaderText::load(const String & p_path, const String & p_original_path, Error * r_error, bool p_use_sub_threads, float * r_progress, ResourceFormatLoader::CacheMode p_cache_mode) Line 1392 (c:\godot\godot\scene\resources\resource_format_text.cpp:1392)
godot.windows.editor.dev.x86_64.exe!ResourceLoader::_load(const String & p_path, const String & p_original_path, const String & p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error * r_error, bool p_use_sub_threads, float * r_progress) Line 269 (c:\godot\godot\core\io\resource_loader.cpp:269)
godot.windows.editor.dev.x86_64.exe!ResourceLoader::_thread_load_function(void * p_userdata) Line 323 (c:\godot\godot\core\io\resource_loader.cpp:323)
godot.windows.editor.dev.x86_64.exe!ResourceLoader::_load_start(const String & p_path, const String & p_type_hint, ResourceLoader::LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode) Line 527 (c:\godot\godot\core\io\resource_loader.cpp:527)
godot.windows.editor.dev.x86_64.exe!ResourceLoaderText::load() Line 472 (c:\godot\godot\scene\resources\resource_format_text.cpp:472)
godot.windows.editor.dev.x86_64.exe!ResourceFormatLoaderText::load(const String & p_path, const String & p_original_path, Error * r_error, bool p_use_sub_threads, float * r_progress, ResourceFormatLoader::CacheMode p_cache_mode) Line 1392 (c:\godot\godot\scene\resources\resource_format_text.cpp:1392)
godot.windows.editor.dev.x86_64.exe!ResourceLoader::_load(const String & p_path, const String & p_original_path, const String & p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error * r_error, bool p_use_sub_threads, float * r_progress) Line 269 (c:\godot\godot\core\io\resource_loader.cpp:269)
godot.windows.editor.dev.x86_64.exe!ResourceLoader::_thread_load_function(void * p_userdata) Line 323 (c:\godot\godot\core\io\resource_loader.cpp:323)
godot.windows.editor.dev.x86_64.exe!ResourceLoader::_load_start(const String & p_path, const String & p_type_hint, ResourceLoader::LoadThreadMode p_thread_mode, ResourceFormatLoader::CacheMode p_cache_mode) Line 527 (c:\godot\godot\core\io\resource_loader.cpp:527)
godot.windows.editor.dev.x86_64.exe!ResourceLoader::load(const String & p_path, const String & p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error * r_error) Line 445 (c:\godot\godot\core\io\resource_loader.cpp:445)
godot.windows.editor.dev.x86_64.exe!EditorNode::load_scene(const String & p_scene, bool p_ignore_broken_deps, bool p_set_inherited, bool p_clear_errors, bool p_force_open_imported, bool p_silent_change_tab) Line 4021 (c:\godot\godot\editor\editor_node.cpp:4021)
godot.windows.editor.dev.x86_64.exe!EditorNode::open_request(const String & p_path) Line 4459 (c:\godot\godot\editor\editor_node.cpp:4459)
godot.windows.editor.dev.x86_64.exe!FileSystemDock::_select_file(const String & p_path, bool p_select_in_favorites) Line 1213 (c:\godot\godot\editor\filesystem_dock.cpp:1213)
godot.windows.editor.dev.x86_64.exe!FileSystemDock::_file_list_activate_file(int p_idx) Line 1259 (c:\godot\godot\editor\filesystem_dock.cpp:1259)
godot.windows.editor.dev.x86_64.exe!call_with_variant_args_helper<FileSystemDock,int,0>(FileSystemDock * p_instance, void(FileSystemDock::*)(int) p_method, const Variant * * p_args, Callable::CallError & r_error, IndexSequence<0> __formal) Line 309 (c:\godot\godot\core\variant\binder_common.h:309)
godot.windows.editor.dev.x86_64.exe!call_with_variant_args<FileSystemDock,int>(FileSystemDock * p_instance, void(FileSystemDock::*)(int) p_method, const Variant * * p_args, int p_argcount, Callable::CallError & r_error) Line 419 (c:\godot\godot\core\variant\binder_common.h:419)
godot.windows.editor.dev.x86_64.exe!CallableCustomMethodPointer<FileSystemDock,int>::call(const Variant * * p_arguments, int p_argcount, Variant & r_return_value, Callable::CallError & r_call_error) Line 104 (c:\godot\godot\core\object\callable_method_pointer.h:104)
godot.windows.editor.dev.x86_64.exe!Callable::callp(const Variant * * p_arguments, int p_argcount, Variant & r_return_value, Callable::CallError & r_call_error) Line 58 (c:\godot\godot\core\variant\callable.cpp:58)
godot.windows.editor.dev.x86_64.exe!Object::emit_signalp(const StringName & p_name, const Variant * * p_args, int p_argcount) Line 1188 (c:\godot\godot\core\object\object.cpp:1188)
godot.windows.editor.dev.x86_64.exe!Node::emit_signalp(const StringName & p_name, const Variant * * p_args, int p_argcount) Line 3896 (c:\godot\godot\scene\main\node.cpp:3896)
godot.windows.editor.dev.x86_64.exe!Object::emit_signal<int>(const StringName & p_name, int <p_args_0>) Line 936 (c:\godot\godot\core\object\object.h:936)
godot.windows.editor.dev.x86_64.exe!ItemList::gui_input(const Ref<InputEvent> & p_event) Line 749 (c:\godot\godot\scene\gui\item_list.cpp:749)
godot.windows.editor.dev.x86_64.exe!Control::_call_gui_input(const Ref<InputEvent> & p_event) Line 1831 (c:\godot\godot\scene\gui\control.cpp:1831)
godot.windows.editor.dev.x86_64.exe!Viewport::_gui_call_input(Control * p_control, const Ref<InputEvent> & p_input) Line 1570 (c:\godot\godot\scene\main\viewport.cpp:1570)
godot.windows.editor.dev.x86_64.exe!Viewport::_gui_input_event(Ref<InputEvent> p_event) Line 1799 (c:\godot\godot\scene\main\viewport.cpp:1799)
godot.windows.editor.dev.x86_64.exe!Viewport::push_input(const Ref<InputEvent> & p_event, bool p_local_coords) Line 3259 (c:\godot\godot\scene\main\viewport.cpp:3259)
godot.windows.editor.dev.x86_64.exe!Window::_window_input(const Ref<InputEvent> & p_ev) Line 1692 (c:\godot\godot\scene\main\window.cpp:1692)
godot.windows.editor.dev.x86_64.exe!call_with_variant_args_helper<Window,Ref<InputEvent> const &,0>(Window * p_instance, void(Window::*)(const Ref<InputEvent> &) p_method, const Variant * * p_args, Callable::CallError & r_error, IndexSequence<0> __formal) Line 304 (c:\godot\godot\core\variant\binder_common.h:304)
godot.windows.editor.dev.x86_64.exe!call_with_variant_args<Window,Ref<InputEvent> const &>(Window * p_instance, void(Window::*)(const Ref<InputEvent> &) p_method, const Variant * * p_args, int p_argcount, Callable::CallError & r_error) Line 419 (c:\godot\godot\core\variant\binder_common.h:419)
godot.windows.editor.dev.x86_64.exe!CallableCustomMethodPointer<Window,Ref<InputEvent> const &>::call(const Variant * * p_arguments, int p_argcount, Variant & r_return_value, Callable::CallError & r_call_error) Line 104 (c:\godot\godot\core\object\callable_method_pointer.h:104)
godot.windows.editor.dev.x86_64.exe!Callable::callp(const Variant * * p_arguments, int p_argcount, Variant & r_return_value, Callable::CallError & r_call_error) Line 58 (c:\godot\godot\core\variant\callable.cpp:58)
godot.windows.editor.dev.x86_64.exe!Callable::call<Ref<InputEvent>>(Ref<InputEvent> <p_args_0>) Line 876 (c:\godot\godot\core\variant\variant.h:876)
godot.windows.editor.dev.x86_64.exe!DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> & p_event) Line 3554 (c:\godot\godot\platform\windows\display_server_windows.cpp:3554)
godot.windows.editor.dev.x86_64.exe!DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> & p_event) Line 3525 (c:\godot\godot\platform\windows\display_server_windows.cpp:3525)
godot.windows.editor.dev.x86_64.exe!Input::_parse_input_event_impl(const Ref<InputEvent> & p_event, bool p_is_emulated) Line 775 (c:\godot\godot\core\input\input.cpp:775)
godot.windows.editor.dev.x86_64.exe!Input::flush_buffered_events() Line 1056 (c:\godot\godot\core\input\input.cpp:1056)
godot.windows.editor.dev.x86_64.exe!DisplayServerWindows::process_events() Line 3022 (c:\godot\godot\platform\windows\display_server_windows.cpp:3022)
godot.windows.editor.dev.x86_64.exe!OS_Windows::run() Line 1666 (c:\godot\godot\platform\windows\os_windows.cpp:1666)
godot.windows.editor.dev.x86_64.exe!widechar_main(int argc, wchar_t * * argv) Line 181 (c:\godot\godot\platform\windows\godot_windows.cpp:181)
godot.windows.editor.dev.x86_64.exe!_main() Line 206 (c:\godot\godot\platform\windows\godot_windows.cpp:206)
godot.windows.editor.dev.x86_64.exe!main(int argc, char * * argv) Line 220 (c:\godot\godot\platform\windows\godot_windows.cpp:220)
godot.windows.editor.dev.x86_64.exe!WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 234 (c:\godot\godot\platform\windows\godot_windows.cpp:234)
[Inline Frame] godot.windows.editor.dev.x86_64.exe!invoke_main() Line 102 (d:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:102)
godot.windows.editor.dev.x86_64.exe!__scrt_common_main_seh() Line 288 (d:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288)
kernel32.dll!00007ffc905026bd() (Unknown Source:0)
ntdll.dll!00007ffc916cdfb8() (Unknown Source:0)

@akien-mga
Copy link
Member

akien-mga commented Jul 24, 2024

I don't know if that preserves the intention of this code, but this might fix the crash:

diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 990384bcaa..27e1a97b8e 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3669,7 +3669,7 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_parser_for_class(const GD
 	Ref<GDScriptParserRef> parser_ref;
 	Ref<GDScriptParserRef> dependant_parser_ref;
 	GDScriptParser *dependant_parser = parser;
-	do {
+	while (p_from_class != nullptr) {
 		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();
@@ -3703,7 +3703,7 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_parser_for_class(const GD
 		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);

(or move it somewhere else)

Could you try and confirm? I couldn't reproduce the crash in a couple projects I tested.

Edit: Well that might fix the crash but then it will raise the parser bug below:

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

@vnen
Copy link
Member

vnen commented Jul 24, 2024

I think we can just add a null check before dereferencing it:

diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 990384bcaa..3299aa44a2 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3702,7 +3702,9 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_parser_for_class(const GD
 
        dependant_parser_ref = Ref<GDScriptParserRef>();
        dependant_parser = nullptr;
-       p_from_class = p_from_class->base_type.class_type;
+       if (p_from_class != nullptr) {
+           p_from_class = p_from_class->base_type.class_type;
+       }
    } while (p_from_class != nullptr);
 
    if (parser_ref.is_null()) {

EDIT: Or simply break early:

diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 990384bcaa..2e29c141bd 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3702,6 +3702,9 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_parser_for_class(const GD
 
        dependant_parser_ref = Ref<GDScriptParserRef>();
        dependant_parser = nullptr;
+       if (p_from_class == nullptr) {
+           break;
+       }
        p_from_class = p_from_class->base_type.class_type;
    } while (p_from_class != nullptr);

@ydeltastar
Copy link
Contributor

Moving the while statement causes an infinite loop for scripts that didn't trigger the exception before.
Both changes by vnen fixes the crash but triggers the "Parser bug: Could not find external parser ..." error.

@mihe
Copy link
Contributor

mihe commented Jul 24, 2024

I can confirm that both suggestions by @vnen resolves the crash but instead results in the errors mentioned by @ydeltastar.

@mihe
Copy link
Contributor

mihe commented Jul 24, 2024

This maybe isn't the most minimal of MRPs, but it does reproduce the crash/regression mentioned above: cached-parser-crash.zip

It's essentially just a clean project with the Cheatsheet add-on added to it, but with one tiny modification done to it, which is that scripts/cheatsheet.gd now references scripts/argument.gd, specifically in its register function (although the location doesn't seem to matter):

func register(name: String, callback: Callable) -> Command:
	var command := db.register(name, callback)
+	if not command.args.is_empty():
+		print(command.args[0].name)
	commands_changed.emit()
	return command

That modification causes the crash, and without that modification the project loads just fine.

EDIT: There's a more minimal MRP here as well: #94697 (comment)

akien-mga added a commit to akien-mga/godot that referenced this pull request Jul 25, 2024
This reverts commit c75225f.

This caused a crashing regression for multiple users:
godotengine#94617 (comment)
@akien-mga akien-mga changed the title GDScript: Fix common mismatched external parser errors GDScript: Fix common mismatched external parser errors (reverted) Jul 25, 2024
@akien-mga
Copy link
Member

Reverted for now with #94723 to fix the new regression, which is more critical than the one fixed by this PR.

Luis-Wong pushed a commit to Luis-Wong/godot that referenced this pull request Jul 26, 2024
This reverts commit c75225f.

This caused a crashing regression for multiple users:
godotengine#94617 (comment)
MBCX pushed a commit to Sunfly-Studios/godot that referenced this pull request Jul 26, 2024
This reverts commit c75225f.

This caused a crashing regression for multiple users:
godotengine/godot#94617 (comment)
RadiantUwU pushed a commit to RadiantUwU/godot that referenced this pull request Jul 27, 2024
This reverts commit c75225f.

This caused a crashing regression for multiple users:
godotengine#94617 (comment)
kii-chan-reloaded pushed a commit to kii-chan-reloaded/godot that referenced this pull request Aug 3, 2024
This reverts commit c75225f.

This caused a crashing regression for multiple users:
godotengine#94617 (comment)
2nafish117 pushed a commit to 2nafish117/godot that referenced this pull request Aug 5, 2024
This reverts commit c75225f.

This caused a crashing regression for multiple users:
godotengine#94617 (comment)
chryan pushed a commit to chryan/godot that referenced this pull request Aug 6, 2024
This reverts commit c75225f.

This caused a crashing regression for multiple users:
godotengine#94617 (comment)
@rune-scape rune-scape deleted the fix-mismatched-parsers branch September 9, 2024 21:59
maidopi-usagi pushed a commit to maidopi-usagi/godot that referenced this pull request Sep 11, 2024
This reverts commit c75225f.

This caused a crashing regression for multiple users:
godotengine#94617 (comment)
Akeal pushed a commit to Akeal/godot that referenced this pull request Oct 16, 2024
This reverts commit c75225f.

This caused a crashing regression for multiple users:
godotengine#94617 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Immediate Blocker
Development

Successfully merging this pull request may close these issues.

4.3.beta3 GDScript errors on load of Zylann hterrain plugin: Parser bug: Mismatched external parser.
6 participants