summaryrefslogtreecommitdiffstats
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml10
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml4
-rw-r--r--modules/gdscript/editor/gdscript_docgen.cpp3
-rw-r--r--modules/gdscript/gdscript.cpp12
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp10
-rw-r--r--modules/gdscript/gdscript_cache.cpp5
-rw-r--r--modules/gdscript/gdscript_compiler.cpp48
-rw-r--r--modules/gdscript/gdscript_compiler.h5
-rw-r--r--modules/gdscript/gdscript_parser.cpp19
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/register_types.cpp2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.gd2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.out1
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.gd6
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd59
-rw-r--r--modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.out25
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.gd10
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.out (renamed from modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out)2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.gd28
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.out14
31 files changed, 226 insertions, 83 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index f2a65451a7..0c300eade4 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="@GDScript" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="@GDScript" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Built-in GDScript constants, functions, and annotations.
</brief_description>
@@ -620,10 +620,10 @@
<param index="3" name="transfer_channel" type="int" default="0" />
<description>
Mark the following method for remote procedure calls. See [url=$DOCS_URL/tutorials/networking/high_level_multiplayer.html]High-level multiplayer[/url].
- The accepted values:
- - for [param mode] are [code]"any_peer"[/code] or [code]"authority"[/code];
- - for [param sync] are [code]"call_remote"[/code] or [code]"call_local"[/code];
- - and for [param transfer_mode] are [code]"unreliable"[/code], [code]"unreliable_ordered"[/code], or [code]"reliable"[/code].
+ If [param mode] is set as [code]"any_peer"[/code], allows any peer to call this RPC function. Otherwise, only the authority peer is allowed to call it and [param mode] should be kept as [code]"authority"[/code]. When configuring functions as RPCs with [method Node.rpc_config], each of these modes respectively corresponds to the [constant MultiplayerAPI.RPC_MODE_AUTHORITY] and [constant MultiplayerAPI.RPC_MODE_ANY_PEER] RPC modes. See [enum MultiplayerAPI.RPCMode]. If a peer that is not the authority tries to call a function that is only allowed for the authority, the function will not be executed. If the error can be detected locally (when the RPC configuration is consistent between the local and the remote peer), an error message will be displayed on the sender peer. Otherwise, the remote peer will detect the error and print an error there.
+ If [param sync] is set as [code]"call_remote"[/code], the function will only be executed on the remote peer, but not locally. To run this function locally too, set [param sync] to [code]"call_local"[/code]. When configuring functions as RPCs with [method Node.rpc_config], this is equivalent to setting `call_local` to `true`.
+ The [param transfer_mode] accepted values are [code]"unreliable"[/code], [code]"unreliable_ordered"[/code], or [code]"reliable"[/code]. It sets the transfer mode of the underlying [MultiplayerPeer]. See [member MultiplayerPeer.transfer_mode].
+ The [param transfer_channel] defines the channel of the underlying [MultiplayerPeer]. See [member MultiplayerPeer.transfer_channel].
The order of [param mode], [param sync] and [param transfer_mode] does not matter, but values related to the same argument must not be used more than once. [param transfer_channel] always has to be the 4th argument (you must specify 3 preceding arguments).
[codeblock]
@rpc
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index f383eed480..5f7a7e2915 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GDScript" inherits="Script" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="GDScript" inherits="Script" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
A script implemented in the GDScript programming language.
</brief_description>
<description>
- A script implemented in the GDScript programming language. The script extends the functionality of all objects that instantiate it.
+ A script implemented in the GDScript programming language, saved with the [code].gd[/code] extension. The script extends the functionality of all objects that instantiate it.
Calling [method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
If you are looking for GDScript's built-in functions, see [@GDScript] instead.
</description>
diff --git a/modules/gdscript/editor/gdscript_docgen.cpp b/modules/gdscript/editor/gdscript_docgen.cpp
index df17581ad1..0d8453738d 100644
--- a/modules/gdscript/editor/gdscript_docgen.cpp
+++ b/modules/gdscript/editor/gdscript_docgen.cpp
@@ -236,6 +236,8 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
p_script->member_lines[name] = m_enum->start_line;
+ doc.enums[name] = m_enum->doc_description;
+
for (const GDP::EnumNode::Value &val : m_enum->values) {
DocData::ConstantDoc const_doc;
const_doc.name = val.identifier->name;
@@ -244,7 +246,6 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
const_doc.description = val.doc_description;
const_doc.enumeration = name;
- doc.enums[const_doc.name] = const_doc.description;
doc.constants.push_back(const_doc);
}
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 8f870368ce..7117337827 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -787,11 +787,11 @@ Error GDScript::reload(bool p_keep_state) {
err = compiler.compile(&parser, this, p_keep_state);
if (err) {
+ _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
if (can_run) {
if (EngineDebugger::is_active()) {
GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error());
}
- _err_print_error("GDScript::reload", path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT);
reloading = false;
return ERR_COMPILATION_FAILED;
} else {
@@ -1542,7 +1542,7 @@ GDScript::~GDScript() {
{
MutexLock lock(GDScriptLanguage::get_singleton()->mutex);
- GDScriptLanguage::get_singleton()->script_list.remove(&script_list);
+ script_list.remove_from_list();
}
}
@@ -1747,11 +1747,10 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
Dictionary d = arr[i];
ERR_CONTINUE(!d.has("name"));
ERR_CONTINUE(!d.has("type"));
+
PropertyInfo pinfo;
- pinfo.type = Variant::Type(d["type"].operator int());
- ERR_CONTINUE(pinfo.type < 0 || pinfo.type >= Variant::VARIANT_MAX);
pinfo.name = d["name"];
- ERR_CONTINUE(pinfo.name.is_empty());
+ pinfo.type = Variant::Type(d["type"].operator int());
if (d.has("hint")) {
pinfo.hint = PropertyHint(d["hint"].operator int());
}
@@ -1765,6 +1764,9 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
pinfo.class_name = d["class_name"];
}
+ ERR_CONTINUE(pinfo.name.is_empty() && (pinfo.usage & PROPERTY_USAGE_STORAGE));
+ ERR_CONTINUE(pinfo.type < 0 || pinfo.type >= Variant::VARIANT_MAX);
+
props.push_back(pinfo);
}
}
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 9d39cbf1a5..d3445b8cc0 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -3097,16 +3097,6 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
bool is_constructor = (base_type.is_meta_type || (p_call->callee && p_call->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_call->function_name == SNAME("new");
if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, method_flags)) {
- // If the method is implemented in the class hierarchy, the virtual flag will not be set for that MethodInfo and the search stops there.
- // MethodInfo's above the class that defines the method might still have the virtual flag set.
- if (method_flags.has_flag(METHOD_FLAG_VIRTUAL)) {
- if (p_call->is_super) {
- push_error(vformat(R"*(Cannot call the parent class' virtual function "%s()" because it hasn't been defined.)*", p_call->function_name), p_call);
- } else {
- push_error(vformat(R"*(Cannot call virtual function "%s()" because it hasn't been defined.)*", p_call->function_name), p_call);
- }
- }
-
// If the function require typed arrays we must make literals be typed.
for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) {
int index = E.key;
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index e5bb93e3c8..d191bd0224 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -294,8 +294,12 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
if (p_update_from_disk) {
r_error = script->load_source_code(p_path);
+ if (r_error) {
+ return script;
+ }
}
+ r_error = script->reload(true);
if (r_error) {
return script;
}
@@ -303,7 +307,6 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
singleton->full_gdscript_cache[p_path] = script;
singleton->shallow_gdscript_cache.erase(p_path);
- script->reload(true);
return script;
}
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 90ee56b9de..3f571602e8 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1767,25 +1767,39 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
ERR_FAIL_V_MSG(p_previous_test, "Reaching the end of pattern compilation without matching a pattern.");
}
-void GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) {
+List<GDScriptCodeGenerator::Address> GDScriptCompiler::_add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block) {
+ List<GDScriptCodeGenerator::Address> addresses;
for (int i = 0; i < p_block->locals.size(); i++) {
if (p_block->locals[i].type == GDScriptParser::SuiteNode::Local::PARAMETER || p_block->locals[i].type == GDScriptParser::SuiteNode::Local::FOR_VARIABLE) {
// Parameters are added directly from function and loop variables are declared explicitly.
continue;
}
- codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype(), codegen.script));
+ addresses.push_back(codegen.add_local(p_block->locals[i].name, _gdtype_from_datatype(p_block->locals[i].get_datatype(), codegen.script)));
}
+ return addresses;
}
-Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals) {
+// Avoid keeping in the stack long-lived references to objects, which may prevent RefCounted objects from being freed.
+void GDScriptCompiler::_clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses) {
+ for (const List<GDScriptCodeGenerator::Address>::Element *E = p_addresses.front(); E; E = E->next()) {
+ GDScriptDataType type = E->get().type;
+ // If not an object and cannot contain an object, no need to clear.
+ if (type.kind != GDScriptDataType::BUILTIN || type.builtin_type == Variant::ARRAY || type.builtin_type == Variant::DICTIONARY) {
+ codegen.generator->write_assign_false(E->get());
+ }
+ }
+}
+
+Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals, bool p_reset_locals) {
Error err = OK;
GDScriptCodeGenerator *gen = codegen.generator;
+ List<GDScriptCodeGenerator::Address> block_locals;
gen->clean_temporaries();
codegen.start_block();
if (p_add_locals) {
- _add_locals_in_block(codegen, p_block);
+ block_locals = _add_locals_in_block(codegen, p_block);
}
for (int i = 0; i < p_block->statements.size(); i++) {
@@ -1841,7 +1855,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
codegen.start_block(); // Create an extra block around for binds.
// Add locals in block before patterns, so temporaries don't use the stack address for binds.
- _add_locals_in_block(codegen, branch->block);
+ List<GDScriptCodeGenerator::Address> branch_locals = _add_locals_in_block(codegen, branch->block);
#ifdef DEBUG_ENABLED
// Add a newline before each branch, since the debugger needs those.
@@ -1868,6 +1882,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
return err;
}
+ _clear_addresses(codegen, branch_locals);
+
codegen.end_block(); // Get out of extra block.
}
@@ -2052,7 +2068,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
}
// Assigns a null for the unassigned variables in loops.
- if (!initialized && p_block->is_loop) {
+ if (!initialized && p_block->is_in_loop) {
codegen.generator->write_construct(local, Variant::NIL, Vector<GDScriptCodeGenerator::Address>());
}
} break;
@@ -2088,6 +2104,10 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
gen->clean_temporaries();
}
+ if (p_add_locals && p_reset_locals) {
+ _clear_addresses(codegen, block_locals);
+ }
+
codegen.end_block();
return OK;
}
@@ -2241,7 +2261,8 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
codegen.generator->end_parameters();
}
- r_error = _parse_block(codegen, p_func->body);
+ // No need to reset locals at the end of the function, the stack will be cleared anyway.
+ r_error = _parse_block(codegen, p_func->body, true, false);
if (r_error) {
memdelete(codegen.generator);
return nullptr;
@@ -2558,9 +2579,9 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
}
} else if (!base->is_valid()) {
Error err = OK;
- Ref<GDScript> base_root = GDScriptCache::get_full_script(base->path, err, p_script->path);
+ Ref<GDScript> base_root = GDScriptCache::get_shallow_script(base->path, err, p_script->path);
if (err) {
- _set_error(vformat(R"(Could not compile base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr);
+ _set_error(vformat(R"(Could not parse base class "%s" from "%s": %s)", base->fully_qualified_name, base->path, error_names[err]), nullptr);
return err;
}
if (base_root.is_valid()) {
@@ -2570,7 +2591,12 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
_set_error(vformat(R"(Could not find class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);
return ERR_COMPILATION_FAILED;
}
- ERR_FAIL_COND_V(!base->is_valid() && !base->reloading, ERR_BUG);
+
+ err = _populate_class_members(base.ptr(), p_class->base_type.class_type, p_keep_state);
+ if (err) {
+ _set_error(vformat(R"(Could not populate class members of base class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);
+ return err;
+ }
}
p_script->base = base;
@@ -2947,7 +2973,7 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
GDScriptCache::add_static_script(p_script);
}
- return GDScriptCache::finish_compiling(main_script->get_path());
+ return GDScriptCache::finish_compiling(main_script->path);
}
String GDScriptCompiler::get_error() const {
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 494eef41d9..2f522da4ea 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -129,8 +129,9 @@ class GDScriptCompiler {
GDScriptCodeGenerator::Address _parse_assign_right_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::AssignmentNode *p_assignmentint, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
GDScriptCodeGenerator::Address _parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root = false, bool p_initializer = false, const GDScriptCodeGenerator::Address &p_index_addr = GDScriptCodeGenerator::Address());
GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested);
- void _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block);
- Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true);
+ List<GDScriptCodeGenerator::Address> _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block);
+ void _clear_addresses(CodeGen &codegen, const List<GDScriptCodeGenerator::Address> &p_addresses);
+ Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true, bool p_reset_locals = true);
GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false);
GDScriptFunction *_make_static_initializer(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class);
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index cf750958ee..5c2d4a060c 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -1139,6 +1139,7 @@ GDScriptParser::ConstantNode *GDScriptParser::parse_constant(bool p_is_static) {
ConstantNode *constant = alloc_node<ConstantNode>();
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected constant name after "const".)")) {
+ complete_extents(constant);
return nullptr;
}
@@ -1536,6 +1537,11 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
suite->parent_function = current_function;
current_suite = suite;
+ if (!p_for_lambda && suite->parent_block != nullptr && suite->parent_block->is_in_loop) {
+ // Do not reset to false if true is set before calling parse_suite().
+ suite->is_in_loop = true;
+ }
+
bool multiline = false;
if (match(GDScriptTokenizer::Token::NEWLINE)) {
@@ -1871,9 +1877,8 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
}
suite->add_local(SuiteNode::Local(n_for->variable, current_function));
}
-
+ suite->is_in_loop = true;
n_for->loop = parse_suite(R"("for" block)", suite);
- n_for->loop->is_loop = true;
complete_extents(n_for);
// Reset break/continue state.
@@ -2143,6 +2148,7 @@ GDScriptParser::PatternNode *GDScriptParser::parse_match_pattern(PatternNode *p_
ExpressionNode *expression = parse_expression(false);
if (expression == nullptr) {
push_error(R"(Expected expression for match pattern.)");
+ complete_extents(pattern);
return nullptr;
} else {
if (expression->type == GDScriptParser::Node::LITERAL) {
@@ -2186,8 +2192,9 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() {
can_break = true;
can_continue = true;
- n_while->loop = parse_suite(R"("while" block)");
- n_while->loop->is_loop = true;
+ SuiteNode *suite = alloc_node<SuiteNode>();
+ suite->is_in_loop = true;
+ n_while->loop = parse_suite(R"("while" block)", suite);
complete_extents(n_while);
// Reset break/continue state.
@@ -2231,7 +2238,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
while (p_precedence <= get_rule(current.type)->precedence) {
- if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || (previous_operand->type == Node::LAMBDA && lambda_ended)) {
+ if (previous_operand == nullptr || (p_stop_on_assign && current.type == GDScriptTokenizer::Token::EQUAL) || lambda_ended) {
return previous_operand;
}
// Also switch multiline mode on here for infix operators.
@@ -3224,7 +3231,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_type_test(ExpressionNode *
}
GDScriptParser::ExpressionNode *GDScriptParser::parse_yield(ExpressionNode *p_previous_operand, bool p_can_assign) {
- push_error(R"("yield" was removed in Godot 4.0. Use "await" instead.)");
+ push_error(R"("yield" was removed in Godot 4. Use "await" instead.)");
return nullptr;
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index ad08c3bfd6..18757eb9fd 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1121,7 +1121,7 @@ public:
bool has_return = false;
bool has_continue = false;
bool has_unreachable_code = false; // Just so warnings aren't given more than once per block.
- bool is_loop = false;
+ bool is_in_loop = false; // The block is nested in a loop (directly or indirectly).
bool has_local(const StringName &p_name) const;
const Local &get_local(const StringName &p_name) const;
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index e23bd50b8b..605e82be6e 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -98,7 +98,7 @@ public:
return;
}
- virtual String _get_name() const override { return "GDScript"; }
+ virtual String get_name() const override { return "GDScript"; }
};
static void _editor_init() {
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
index 38c2faa859..8709b89b54 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.gd
@@ -1,2 +1,2 @@
func test():
- CanvasItem.new()
+ InstancePlaceholder.new()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
index 9eff912b59..36224c6b6f 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_class_instantiate.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Native class "CanvasItem" cannot be constructed as it is abstract.
+Native class "InstancePlaceholder" cannot be constructed as it is abstract.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
index 118e7e8a45..be67182efb 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.gd
@@ -1,4 +1,4 @@
-class A extends CanvasItem:
+class A extends InstancePlaceholder:
func _init():
print('no')
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
index 8b956f5974..260f062555 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/abstract_script_instantiate.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "CanvasItem".
+Class "abstract_script_instantiate.gd::B" cannot be constructed as it is based on abstract native class "InstancePlaceholder".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.gd b/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.gd
deleted file mode 100644
index c34d927035..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.gd
+++ /dev/null
@@ -1,2 +0,0 @@
-func test():
- _get_property_list()
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.out b/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.out
deleted file mode 100644
index ce2f49a5e5..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/errors/virtual_method_not_implemented.out
+++ /dev/null
@@ -1,2 +0,0 @@
-GDTEST_ANALYZER_ERROR
-Cannot call virtual function "_get_property_list()" because it hasn't been defined.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd
deleted file mode 100644
index 57dfffdbee..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.gd
+++ /dev/null
@@ -1,5 +0,0 @@
-func _init():
- super()
-
-func test():
- pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out b/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out
deleted file mode 100644
index e68759223c..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/errors/virtual_super_not_implemented.out
+++ /dev/null
@@ -1,2 +0,0 @@
-GDTEST_ANALYZER_ERROR
-Cannot call the parent class' virtual function "_init()" because it hasn't been defined.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd b/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd
deleted file mode 100644
index 1aacd1d11c..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.gd
+++ /dev/null
@@ -1,11 +0,0 @@
-class TestOne:
- func _get_property_list():
- return {}
-
-class TestTwo extends TestOne:
- func _init():
- var _x = _get_property_list()
-
-func test():
- var x = TestTwo.new()
- var _x = x._get_property_list()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.gd b/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.gd
deleted file mode 100644
index c447003619..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.gd
+++ /dev/null
@@ -1,10 +0,0 @@
-class TestOne:
- func _init():
- pass
-
-class TestTwo extends TestOne:
- func _init():
- super()
-
-func test():
- pass
diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.out b/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.out
deleted file mode 100644
index d73c5eb7cd..0000000000
--- a/modules/gdscript/tests/scripts/analyzer/features/virtual_super_implemented.out
+++ /dev/null
@@ -1 +0,0 @@
-GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.gd b/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.gd
new file mode 100644
index 0000000000..8c5fb46109
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.gd
@@ -0,0 +1,6 @@
+# https://github.com/godotengine/godot/issues/73273
+
+func not_called():
+ var v
+ v=func(): v=1
+ in v
diff --git a/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.out b/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.out
new file mode 100644
index 0000000000..539240f790
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/lambda_no_continue_on_new_line.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected statement, found "in" instead.
diff --git a/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out b/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out
index 36cb699e92..f68d76d101 100644
--- a/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out
+++ b/modules/gdscript/tests/scripts/parser/errors/yield_instead_of_await.out
@@ -1,2 +1,2 @@
GDTEST_PARSER_ERROR
-"yield" was removed in Godot 4.0. Use "await" instead.
+"yield" was removed in Godot 4. Use "await" instead.
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd
new file mode 100644
index 0000000000..df6001c7e2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.gd
@@ -0,0 +1,59 @@
+# https://github.com/godotengine/godot/issues/73273
+
+func other(callable : Callable):
+ callable.call()
+
+func four_parameters(_a, callable : Callable, b=func(): print(10)):
+ callable.call()
+ b.call()
+
+func test():
+ var v
+ v=func():v=1
+ if true: v=1
+ print(v)
+ print()
+
+ v=func(): print(2) if false else print(3)
+ @warning_ignore("unsafe_cast")
+ (v as Callable).call()
+ print()
+
+ v=func():
+ print(4)
+ print(5)
+ @warning_ignore("unsafe_cast")
+ if true: (v as Callable).call()
+ print()
+
+ other(v)
+ print()
+
+ other(func(): print(6))
+ print()
+
+ other(func():
+ print(7)
+ print(8)
+ )
+ print()
+
+ four_parameters(1,func():print(9))
+ four_parameters(1,func():print(9), func(): print(11))
+ four_parameters(1,func():
+ print(12)
+ print(13)
+ , func(): print(11))
+ print()
+
+ from_ticket()
+
+func from_ticket():
+ var _v
+ if true: _v = (func(): test())
+ if true: _v = (func(): test())
+ if true: _v = (func(): test())
+
+ if true: _v = func(): test()
+ if true: _v = func(): test()
+ print(14)
diff --git a/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.out b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.out
new file mode 100644
index 0000000000..4347310960
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/lambda_ends_with_new_line.out
@@ -0,0 +1,25 @@
+GDTEST_OK
+1
+
+3
+
+4
+5
+
+4
+5
+
+6
+
+7
+8
+
+9
+10
+9
+11
+12
+13
+11
+
+14
diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.gd b/modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.gd
new file mode 100644
index 0000000000..c774ebf83c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.gd
@@ -0,0 +1,10 @@
+# GH-77666
+
+func test():
+ var ref := RefCounted.new()
+ print(ref.get_reference_count())
+
+ if true:
+ var _temp := ref
+
+ print(ref.get_reference_count())
diff --git a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out b/modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.out
index d73c5eb7cd..04b4638adf 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/virtual_method_implemented.out
+++ b/modules/gdscript/tests/scripts/runtime/features/reset_local_var_on exit_block.out
@@ -1 +1,3 @@
GDTEST_OK
+1
+1
diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.gd b/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.gd
new file mode 100644
index 0000000000..c45f8dce48
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.gd
@@ -0,0 +1,28 @@
+# GH-56223, GH-76569
+
+func test():
+ for i in 3:
+ var a
+ if true:
+ var b
+ if true:
+ var c
+ prints("Begin:", i, a, b, c)
+ a = 1
+ b = 1
+ c = 1
+ prints("End:", i, a, b, c)
+ print("===")
+ var j := 0
+ while j < 3:
+ var a
+ if true:
+ var b
+ if true:
+ var c
+ prints("Begin:", j, a, b, c)
+ a = 1
+ b = 1
+ c = 1
+ prints("End:", j, a, b, c)
+ j += 1
diff --git a/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.out b/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.out
new file mode 100644
index 0000000000..7eddcbf903
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/reset_unassigned_variables_in_loops.out
@@ -0,0 +1,14 @@
+GDTEST_OK
+Begin: 0 <null> <null> <null>
+End: 0 1 1 1
+Begin: 1 <null> <null> <null>
+End: 1 1 1 1
+Begin: 2 <null> <null> <null>
+End: 2 1 1 1
+===
+Begin: 0 <null> <null> <null>
+End: 0 1 1 1
+Begin: 1 <null> <null> <null>
+End: 1 1 1 1
+Begin: 2 <null> <null> <null>
+End: 2 1 1 1