diff options
Diffstat (limited to 'modules/gdscript')
29 files changed, 240 insertions, 68 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 08c8763493..124c8be3e0 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.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="@GDScript" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> Built-in GDScript functions. </brief_description> @@ -126,6 +126,20 @@ <param index="0" name="value" type="Variant" /> <param index="1" name="type" type="Variant" /> <description> + Returns [code]true[/code] if [param value] is an instance of [param type]. The [param type] value must be one of the following: + - A constant from the [enum Variant.Type] enumeration, for example [constant TYPE_INT]. + - An [Object]-derived class which exists in [ClassDB], for example [Node]. + - A [Script] (you can use any class, including inner one). + Unlike the right operand of the [code]is[/code] operator, [param type] can be a non-constant value. The [code]is[/code] operator supports more features (such as typed arrays) and is more performant. Use the operator instead of this method if you do not need dynamic type checking. + Examples: + [codeblock] + print(is_instance_of(a, TYPE_INT)) + print(is_instance_of(a, Node)) + print(is_instance_of(a, MyClass)) + print(is_instance_of(a, MyClass.InnerClass)) + [/codeblock] + [b]Note:[/b] If [param value] and/or [param type] are freed objects (see [method @GlobalScope.is_instance_valid]), or [param type] is not one of the above options, this method will raise an runtime error. + See also [method @GlobalScope.typeof], [method type_exists], [method Array.is_same_typed] (and other [Array] methods). </description> </method> <method name="len"> diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml index 1a102bd16f..556f747eb3 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" inherits="Script" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> +<class name="GDScript" inherits="Script" version="4.1" 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> diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 00f8d2817a..b6caefbdb5 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -2027,11 +2027,6 @@ String GDScriptLanguage::get_extension() const { return "gd"; } -Error GDScriptLanguage::execute_file(const String &p_path) { - // ?? - return OK; -} - void GDScriptLanguage::finish() { if (_call_stack) { memdelete_arr(_call_stack); diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 82d04f641c..0117ed40ab 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -473,7 +473,6 @@ public: virtual void init() override; virtual String get_type() const override; virtual String get_extension() const override; - virtual Error execute_file(const String &p_path) override; virtual void finish() override; /* EDITOR FUNCTIONS */ diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 38f9163f70..38d5ae6b77 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -657,6 +657,10 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } else if (ProjectSettings::get_singleton()->has_autoload(first) && ProjectSettings::get_singleton()->get_autoload(first).is_singleton) { const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(first); Ref<GDScriptParserRef> ref = get_parser_for(autoload.path); + if (ref.is_null()) { + push_error(vformat(R"(The referenced autoload "%s" (from "%s") could not be loaded.)", first, autoload.path), p_type); + return bad_type; + } if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) { push_error(vformat(R"(Could not parse singleton "%s" from "%s".)", first, autoload.path), p_type); return bad_type; @@ -727,7 +731,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type } } if (!result.is_set()) { - push_error(vformat(R"("%s" was not found in the current scope.)", first), p_type); + push_error(vformat(R"(Could not find type "%s" in the current scope.)", first), p_type); return bad_type; } @@ -1534,6 +1538,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * // Check if the function signature matches the parent. If not it's an error since it breaks polymorphism. // Not for the constructor which can vary in signature. GDScriptParser::DataType base_type = parser->current_class->base_type; + base_type.is_meta_type = false; GDScriptParser::DataType parent_return_type; List<GDScriptParser::DataType> parameters_types; int default_par_count = 0; @@ -1852,33 +1857,40 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { push_error(vformat(R"*(Invalid call for "range()" function. Expected at most 3 arguments, %d given.)*", call->arguments.size()), call->callee); } else { // Now we can optimize it. - bool all_is_constant = true; + bool can_reduce = true; Vector<Variant> args; args.resize(call->arguments.size()); for (int i = 0; i < call->arguments.size(); i++) { - reduce_expression(call->arguments[i]); - - if (!call->arguments[i]->is_constant) { - all_is_constant = false; - } else if (all_is_constant) { - args.write[i] = call->arguments[i]->reduced_value; - } + GDScriptParser::ExpressionNode *argument = call->arguments[i]; + reduce_expression(argument); - GDScriptParser::DataType arg_type = call->arguments[i]->get_datatype(); - if (!arg_type.is_variant()) { - if (arg_type.kind != GDScriptParser::DataType::BUILTIN) { - all_is_constant = false; - push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]); - } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) { - all_is_constant = false; - push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]); + if (argument->is_constant) { + if (argument->reduced_value.get_type() != Variant::INT && argument->reduced_value.get_type() != Variant::FLOAT) { + can_reduce = false; + push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, Variant::get_type_name(argument->reduced_value.get_type())), argument); + } + if (can_reduce) { + args.write[i] = argument->reduced_value; + } + } else { + can_reduce = false; + GDScriptParser::DataType argument_type = argument->get_datatype(); + if (argument_type.is_variant() || !argument_type.is_hard_type()) { + mark_node_unsafe(argument); + } + if (!argument_type.is_variant() && (argument_type.builtin_type != Variant::INT && argument_type.builtin_type != Variant::FLOAT)) { + if (!argument_type.is_hard_type()) { + downgrade_node_type_source(argument); + } else { + push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, argument_type.to_string()), argument); + } } } } Variant reduced; - if (all_is_constant) { + if (can_reduce) { switch (args.size()) { case 1: reduced = (int32_t)args[0]; @@ -2606,7 +2618,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o result = get_operation_type(p_binary_op->variant_op, left_type, right_type, valid, p_binary_op); if (!valid) { push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", left_type.to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op); - } else if (result.type_source != GDScriptParser::DataType::ANNOTATED_EXPLICIT) { + } else if (!result.is_hard_type()) { mark_node_unsafe(p_binary_op); } } else { diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index a13bf8009f..d6f21d297a 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -581,7 +581,8 @@ void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Va } void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) { - if (HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand)) { + // Avoid validated evaluator for modulo and division when operands are int, since there's no check for division by zero. + if (HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand) && ((p_operator != Variant::OP_DIVIDE && p_operator != Variant::OP_MODULE) || p_left_operand.type.builtin_type != Variant::INT || p_right_operand.type.builtin_type != Variant::INT)) { if (p_target.mode == Address::TEMPORARY) { Variant::Type result_type = Variant::get_operator_return_type(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type); Variant::Type temp_type = temporaries[p_target.address].type; @@ -1057,7 +1058,7 @@ void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_tar void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) { bool is_validated = true; if (Variant::is_utility_function_vararg(p_function)) { - is_validated = true; // Vararg works fine with any argument, since they can be any type. + is_validated = false; // Vararg needs runtime checks, can't use validated call. } else if (p_arguments.size() == Variant::get_utility_function_argument_count(p_function)) { bool all_types_exact = true; for (int i = 0; i < p_arguments.size(); i++) { @@ -1106,7 +1107,7 @@ void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, // Check if all types are correct. if (Variant::is_builtin_method_vararg(p_type, p_method)) { - is_validated = true; // Vararg works fine with any argument, since they can be any type. + is_validated = false; // Vararg needs runtime checks, can't use validated call. } else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) { bool all_types_exact = true; for (int i = 0; i < p_arguments.size(); i++) { diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index efa75528fc..d0c2cb43a6 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -85,7 +85,7 @@ void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::N } GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::DataType &p_datatype, GDScript *p_owner) { - if (!p_datatype.is_set() || !p_datatype.is_hard_type()) { + if (!p_datatype.is_set() || !p_datatype.is_hard_type() || p_datatype.is_coroutine) { return GDScriptDataType(); } @@ -238,7 +238,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code // Try class members. if (_is_class_member_property(codegen, identifier)) { // Get property. - GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Could get the type of the class member here. + GDScriptCodeGenerator::Address temp = codegen.add_temporary(_gdtype_from_datatype(p_expression->get_datatype(), codegen.script)); gen->write_get_member(temp, identifier); return temp; } @@ -2018,6 +2018,32 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ bool is_initializer = p_func && !p_for_lambda && p_func->identifier->name == GDScriptLanguage::get_singleton()->strings._init; bool is_implicit_ready = !p_func && p_for_ready; + if (!p_for_lambda && is_implicit_initializer) { + // Initialize the default values for type variables before anything. + // This avoids crashes if they are accessed with validated calls before being properly initialized. + // It may happen with out-of-order access or with `@onready` variables. + for (const GDScriptParser::ClassNode::Member &member : p_class->members) { + if (member.type != GDScriptParser::ClassNode::Member::VARIABLE) { + continue; + } + + const GDScriptParser::VariableNode *field = member.variable; + GDScriptDataType field_type = _gdtype_from_datatype(field->get_datatype(), codegen.script); + GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, field_type); + + if (field_type.has_type) { + codegen.generator->write_newline(field->start_line); + + if (field_type.has_container_element_type()) { + codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); + } else if (field_type.kind == GDScriptDataType::BUILTIN) { + codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); + } + // The `else` branch is for objects, in such case we leave it as `null`. + } + } + } + if (!p_for_lambda && (is_implicit_initializer || is_implicit_ready)) { // Initialize class fields. for (int i = 0; i < p_class->members.size(); i++) { @@ -2051,16 +2077,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_ if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) { codegen.generator->pop_temporary(); } - } else if (field_type.has_type) { - codegen.generator->write_newline(field->start_line); - - // Initialize with default for type. - if (field_type.has_container_element_type()) { - codegen.generator->write_construct_typed_array(dst_address, field_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>()); - } else if (field_type.kind == GDScriptDataType::BUILTIN) { - codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>()); - } - // The `else` branch is for objects, in such case we leave it as `null`. } } } @@ -2265,13 +2281,15 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type, p_script); + int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type]; + p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx]; + ERR_FAIL_COND_V(p_script->native.is_null(), ERR_BUG); + // Inheritance switch (base_type.kind) { - case GDScriptDataType::NATIVE: { - int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type]; - p_script->native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx]; - ERR_FAIL_COND_V(p_script->native.is_null(), ERR_BUG); - } break; + case GDScriptDataType::NATIVE: + // Nothing more to do. + break; case GDScriptDataType::GDSCRIPT: { Ref<GDScript> base = Ref<GDScript>(base_type.script_type); if (base.is_null()) { @@ -2303,7 +2321,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri p_script->base = base; p_script->_base = base.ptr(); p_script->member_indices = base->member_indices; - p_script->native = base->native; } break; default: { _set_error("Parser bug: invalid inheritance.", nullptr); diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index b32313dad4..c402b63f7b 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3138,6 +3138,14 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p bool previous_in_lambda = in_lambda; in_lambda = true; + // Save break/continue state. + bool could_break = can_break; + bool could_continue = can_continue; + + // Disallow break/continue. + can_break = false; + can_continue = false; + function->body = parse_suite("lambda declaration", body, true); complete_extents(function); complete_extents(lambda); @@ -3155,6 +3163,11 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_p current_function = previous_function; in_lambda = previous_in_lambda; lambda->function = function; + + // Reset break/continue state. + can_break = could_break; + can_continue = could_continue; + return lambda; } diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp index ba400b8e15..83d2ed6010 100644 --- a/modules/gdscript/gdscript_vm.cpp +++ b/modules/gdscript/gdscript_vm.cpp @@ -1326,28 +1326,30 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a OPCODE_BREAK; } - ScriptInstance *scr_inst = val_obj->get_script_instance(); - if (!scr_inst) { - err_text = "Trying to assign value of type '" + val_obj->get_class_name() + - "' to a variable of type '" + base_type->get_path().get_file() + "'."; - OPCODE_BREAK; - } + if (val_obj) { // src is not null + ScriptInstance *scr_inst = val_obj->get_script_instance(); + if (!scr_inst) { + err_text = "Trying to assign value of type '" + val_obj->get_class_name() + + "' to a variable of type '" + base_type->get_path().get_file() + "'."; + OPCODE_BREAK; + } - Script *src_type = val_obj->get_script_instance()->get_script().ptr(); - bool valid = false; + Script *src_type = scr_inst->get_script().ptr(); + bool valid = false; - while (src_type) { - if (src_type == base_type) { - valid = true; - break; + while (src_type) { + if (src_type == base_type) { + valid = true; + break; + } + src_type = src_type->get_base_script().ptr(); } - src_type = src_type->get_base_script().ptr(); - } - if (!valid) { - err_text = "Trying to assign value of type '" + val_obj->get_script_instance()->get_script()->get_path().get_file() + - "' to a variable of type '" + base_type->get_path().get_file() + "'."; - OPCODE_BREAK; + if (!valid) { + err_text = "Trying to assign value of type '" + val_obj->get_script_instance()->get_script()->get_path().get_file() + + "' to a variable of type '" + base_type->get_path().get_file() + "'."; + OPCODE_BREAK; + } } } #endif // DEBUG_ENABLED diff --git a/modules/gdscript/tests/scripts/analyzer/errors/not_found_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/not_found_type.gd new file mode 100644 index 0000000000..1644295b38 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/not_found_type.gd @@ -0,0 +1,3 @@ +func test(): + var foo: Foo + print('not ok') diff --git a/modules/gdscript/tests/scripts/analyzer/errors/not_found_type.out b/modules/gdscript/tests/scripts/analyzer/errors/not_found_type.out new file mode 100644 index 0000000000..3f6c2d868d --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/not_found_type.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Could not find type "Foo" in the current scope. diff --git a/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.gd b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.gd new file mode 100644 index 0000000000..d2d9d04508 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.gd @@ -0,0 +1,9 @@ +enum E { E0 = 0, E3 = 3 } + +func test(): + var total := 0 + for value in range(E.E0, E.E3): + var inferable := value + total += inferable + assert(total == 0 + 1 + 2) + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.out b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/enums_in_range_call.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.gd new file mode 100644 index 0000000000..4a7f10f1ee --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.gd @@ -0,0 +1,7 @@ +func test(): + var array := [3, 6, 9] + var result := '' + for i in range(array.size(), 0, -1): + result += str(array[i - 1]) + assert(result == '963') + print('ok') diff --git a/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.out b/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/for_range_usage.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/inheritance_signature_check_no_meta.gd b/modules/gdscript/tests/scripts/analyzer/features/inheritance_signature_check_no_meta.gd new file mode 100644 index 0000000000..6c056530f6 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/inheritance_signature_check_no_meta.gd @@ -0,0 +1,10 @@ +func test(): + print("ok") + +# https://github.com/godotengine/godot/issues/71994 +class A: + extends RefCounted +class B: + extends A + func duplicate(): + pass diff --git a/modules/gdscript/tests/scripts/analyzer/features/inheritance_signature_check_no_meta.out b/modules/gdscript/tests/scripts/analyzer/features/inheritance_signature_check_no_meta.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/inheritance_signature_check_no_meta.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/script_typed_assign_null.gd b/modules/gdscript/tests/scripts/analyzer/features/script_typed_assign_null.gd new file mode 100644 index 0000000000..1b47680a7b --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/script_typed_assign_null.gd @@ -0,0 +1,18 @@ +extends Node + +class LocalClass extends Node: + pass + +func test(): + var typed: LocalClass = get_node_or_null("does_not_exist") + var untyped = null + var node_1: LocalClass = typed + var node_2: LocalClass = untyped + var node_3 = typed + var node_4 = untyped + print(typed) + print(untyped) + print(node_1) + print(node_2) + print(node_3) + print(node_4) diff --git a/modules/gdscript/tests/scripts/analyzer/features/script_typed_assign_null.out b/modules/gdscript/tests/scripts/analyzer/features/script_typed_assign_null.out new file mode 100644 index 0000000000..d66b72f5c3 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/script_typed_assign_null.out @@ -0,0 +1,7 @@ +GDTEST_OK +<Object#null> +<null> +<Object#null> +<null> +<Object#null> +<null> diff --git a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.gd b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.gd index 6fc90ea29c..1e5c10b7d5 100644 --- a/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.gd +++ b/modules/gdscript/tests/scripts/analyzer/warnings/lambda_unused_arg.gd @@ -1,4 +1,4 @@ func test(): var lambda := func(unused: Variant) -> void: pass - lambda.call() + lambda.call("something") diff --git a/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.gd b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.gd new file mode 100644 index 0000000000..319c1801c0 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.gd @@ -0,0 +1,5 @@ +func test(): + for index in range(0, 1): + var lambda := func(): + continue + print('not ok') diff --git a/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.out b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.out new file mode 100644 index 0000000000..262dfbc09b --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/errors/bad_continue_in_lambda.out @@ -0,0 +1,2 @@ +GDTEST_PARSER_ERROR +Cannot use "continue" outside of a loop. diff --git a/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.gd b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.gd new file mode 100644 index 0000000000..2fa45c1d7d --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.gd @@ -0,0 +1,13 @@ +func test(): + var i_string := '' + for i in 3: + if i == 1: continue + var lambda := func(): + var j_string := '' + for j in 3: + if j == 1: continue + j_string += str(j) + return j_string + i_string += lambda.call() + assert(i_string == '0202') + print('ok') diff --git a/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.out b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/good_continue_in_lambda.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/parser/features/string_formatting.gd b/modules/gdscript/tests/scripts/parser/features/string_formatting.gd index a91837145d..0815915f04 100644 --- a/modules/gdscript/tests/scripts/parser/features/string_formatting.gd +++ b/modules/gdscript/tests/scripts/parser/features/string_formatting.gd @@ -13,6 +13,6 @@ func test(): print("hello %.02f" % 0.123456 == "hello 0.12") # Dynamic padding: - # <https://docs.godotengine.org/en/stable/getting_started/scripting/gdscript/gdscript_format_string.html#dynamic-padding> + # https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/gdscript_format_string.html#dynamic-padding print("hello %*.*f" % [7, 3, 0.123456] == "hello 0.123") print("hello %0*.*f" % [7, 3, 0.123456] == "hello 000.123") diff --git a/modules/gdscript/tests/scripts/runtime/features/conversions_from_native_members.gd b/modules/gdscript/tests/scripts/runtime/features/conversions_from_native_members.gd new file mode 100644 index 0000000000..a778fb1a94 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/conversions_from_native_members.gd @@ -0,0 +1,11 @@ +class Foo extends Node: + func _init(): + name = 'f' + var string: String = name + assert(typeof(string) == TYPE_STRING) + assert(string == 'f') + print('ok') + +func test(): + var foo := Foo.new() + foo.free() diff --git a/modules/gdscript/tests/scripts/runtime/features/conversions_from_native_members.out b/modules/gdscript/tests/scripts/runtime/features/conversions_from_native_members.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/conversions_from_native_members.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/runtime/features/default_set_beforehand.gd b/modules/gdscript/tests/scripts/runtime/features/default_set_beforehand.gd new file mode 100644 index 0000000000..03278e453f --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/default_set_beforehand.gd @@ -0,0 +1,20 @@ +extends Node + +@onready var later_inferred := [1] +@onready var later_static : Array +@onready var later_static_with_init : Array = [1] +@onready var later_untyped = [1] + +func test(): + assert(typeof(later_inferred) == TYPE_ARRAY) + assert(later_inferred.size() == 0) + + assert(typeof(later_static) == TYPE_ARRAY) + assert(later_static.size() == 0) + + assert(typeof(later_static_with_init) == TYPE_ARRAY) + assert(later_static_with_init.size() == 0) + + assert(typeof(later_untyped) == TYPE_NIL) + + print("ok") diff --git a/modules/gdscript/tests/scripts/runtime/features/default_set_beforehand.out b/modules/gdscript/tests/scripts/runtime/features/default_set_beforehand.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/default_set_beforehand.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok |
