Merge pull request #70702 from vnen/gdscript-error-on-assign-void

GDScript: Error when assigning return value of void function
This commit is contained in:
Rémi Verschelde 2023-01-03 12:23:00 +01:00
commit 4e360ac612
No known key found for this signature in database
GPG key ID: C3336907360768E1
26 changed files with 108 additions and 53 deletions

View file

@ -65,11 +65,13 @@ static _FORCE_INLINE_ void vc_method_call(R (T::*method)(P...) const, Variant *b
template <class T, class... P> template <class T, class... P>
static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) {
VariantInternal::clear(&r_ret);
call_with_variant_args_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals); call_with_variant_args_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals);
} }
template <class T, class... P> template <class T, class... P>
static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) {
VariantInternal::clear(&r_ret);
call_with_variant_argsc_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals); call_with_variant_argsc_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals);
} }

View file

@ -464,9 +464,6 @@
<member name="debug/gdscript/warnings/unused_variable" type="int" setter="" getter="" default="1"> <member name="debug/gdscript/warnings/unused_variable" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable is unused. When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a local variable is unused.
</member> </member>
<member name="debug/gdscript/warnings/void_assignment" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when assigning the result of a function that returns [code]void[/code] to a variable.
</member>
<member name="debug/settings/crash_handler/message" type="String" setter="" getter="" default="&quot;Please include this when reporting the bug to the project developer.&quot;"> <member name="debug/settings/crash_handler/message" type="String" setter="" getter="" default="&quot;Please include this when reporting the bug to the project developer.&quot;">
Message to be displayed before the backtrace when the engine crashes. By default, this message is only used in exported projects due to the editor-only override applied to this setting. Message to be displayed before the backtrace when the engine crashes. By default, this message is only used in exported projects due to the editor-only override applied to this setting.
</member> </member>

View file

@ -1751,12 +1751,6 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
type = p_variable->initializer->get_datatype(); type = p_variable->initializer->get_datatype();
#ifdef DEBUG_ENABLED
if (p_variable->initializer->type == GDScriptParser::Node::CALL && type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
parser->push_warning(p_variable->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_variable->initializer)->function_name);
}
#endif
if (p_variable->infer_datatype) { if (p_variable->infer_datatype) {
type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
@ -1846,12 +1840,6 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
} }
type = p_constant->initializer->get_datatype(); type = p_constant->initializer->get_datatype();
#ifdef DEBUG_ENABLED
if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.is_hard_type() && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name);
}
#endif
} }
if (p_constant->datatype_specifier != nullptr) { if (p_constant->datatype_specifier != nullptr) {
@ -2335,9 +2323,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
} }
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_value_type.is_hard_type() && assigned_value_type.kind == GDScriptParser::DataType::BUILTIN && assigned_value_type.builtin_type == Variant::NIL) { if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_assignment->assigned_value, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value)->function_name);
} else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION); parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION);
} }
#endif #endif
@ -2652,6 +2638,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else if (GDScriptUtilityFunctions::function_exists(function_name)) { } else if (GDScriptUtilityFunctions::function_exists(function_name)) {
MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name); MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name);
if (!p_is_root && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) {
push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call);
}
if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) { if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) {
// Can call on compilation. // Can call on compilation.
Vector<const Variant *> args; Vector<const Variant *> args;
@ -2695,6 +2685,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else if (Variant::has_utility_function(function_name)) { } else if (Variant::has_utility_function(function_name)) {
MethodInfo function_info = info_from_utility_func(function_name); MethodInfo function_info = info_from_utility_func(function_name);
if (!p_is_root && function_info.return_val.type == Variant::NIL && ((function_info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) == 0)) {
push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", function_name), p_call);
}
if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) { if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) {
// Can call on compilation. // Can call on compilation.
Vector<const Variant *> args; Vector<const Variant *> args;
@ -2837,6 +2831,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
mark_lambda_use_self(); mark_lambda_use_self();
} }
if (!p_is_root && return_type.is_hard_type() && return_type.kind == GDScriptParser::DataType::BUILTIN && return_type.builtin_type == Variant::NIL) {
push_error(vformat(R"*(Cannot get return value of call to "%s()" because it returns "void".)*", p_call->function_name), p_call);
}
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL) { if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL) {
parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name); parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name);

View file

@ -538,20 +538,20 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Construct a built-in type. // Construct a built-in type.
Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name); Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
gen->write_construct(result, vtype, arguments); gen->write_construct(return_addr, vtype, arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) { } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
// Variant utility function. // Variant utility function.
gen->write_call_utility(result, call->function_name, arguments); gen->write_call_utility(return_addr, call->function_name, arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) { } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {
// GDScript utility function. // GDScript utility function.
gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments); gen->write_call_gdscript_utility(return_addr, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
} else { } else {
// Regular function. // Regular function.
const GDScriptParser::ExpressionNode *callee = call->callee; const GDScriptParser::ExpressionNode *callee = call->callee;
if (call->is_super) { if (call->is_super) {
// Super call. // Super call.
gen->write_super_call(result, call->function_name, arguments); gen->write_super_call(return_addr, call->function_name, arguments);
} else { } else {
if (callee->type == GDScriptParser::Node::IDENTIFIER) { if (callee->type == GDScriptParser::Node::IDENTIFIER) {
// Self function call. // Self function call.
@ -563,22 +563,22 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (_have_exact_arguments(method, arguments)) { if (_have_exact_arguments(method, arguments)) {
// Exact arguments, use ptrcall. // Exact arguments, use ptrcall.
gen->write_call_ptrcall(result, self, method, arguments); gen->write_call_ptrcall(return_addr, self, method, arguments);
} else { } else {
// Not exact arguments, but still can use method bind call. // Not exact arguments, but still can use method bind call.
gen->write_call_method_bind(result, self, method, arguments); gen->write_call_method_bind(return_addr, self, method, arguments);
} }
} else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") { } else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
GDScriptCodeGenerator::Address self; GDScriptCodeGenerator::Address self;
self.mode = GDScriptCodeGenerator::Address::CLASS; self.mode = GDScriptCodeGenerator::Address::CLASS;
if (within_await) { if (within_await) {
gen->write_call_async(result, self, call->function_name, arguments); gen->write_call_async(return_addr, self, call->function_name, arguments);
} else { } else {
gen->write_call(return_addr, self, call->function_name, arguments); gen->write_call(return_addr, self, call->function_name, arguments);
} }
} else { } else {
if (within_await) { if (within_await) {
gen->write_call_self_async(result, call->function_name, arguments); gen->write_call_self_async(return_addr, call->function_name, arguments);
} else { } else {
gen->write_call_self(return_addr, call->function_name, arguments); gen->write_call_self(return_addr, call->function_name, arguments);
} }
@ -589,18 +589,18 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (subscript->is_attribute) { if (subscript->is_attribute) {
// May be static built-in method call. // May be static built-in method call.
if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) { if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) {
gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments); gen->write_call_builtin_type_static(return_addr, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);
} else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") && } else if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && call->function_name != SNAME("new") &&
ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) { ClassDB::class_exists(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) && !Engine::get_singleton()->has_singleton(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name)) {
// It's a static native method call. // It's a static native method call.
gen->write_call_native_static(result, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments); gen->write_call_native_static(return_addr, static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name, subscript->attribute->name, arguments);
} else { } else {
GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base); GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
if (r_error) { if (r_error) {
return GDScriptCodeGenerator::Address(); return GDScriptCodeGenerator::Address();
} }
if (within_await) { if (within_await) {
gen->write_call_async(result, base, call->function_name, arguments); gen->write_call_async(return_addr, base, call->function_name, arguments);
} else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) { } else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
// Native method, use faster path. // Native method, use faster path.
StringName class_name; StringName class_name;
@ -613,16 +613,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
MethodBind *method = ClassDB::get_method(class_name, call->function_name); MethodBind *method = ClassDB::get_method(class_name, call->function_name);
if (_have_exact_arguments(method, arguments)) { if (_have_exact_arguments(method, arguments)) {
// Exact arguments, use ptrcall. // Exact arguments, use ptrcall.
gen->write_call_ptrcall(result, base, method, arguments); gen->write_call_ptrcall(return_addr, base, method, arguments);
} else { } else {
// Not exact arguments, but still can use method bind call. // Not exact arguments, but still can use method bind call.
gen->write_call_method_bind(result, base, method, arguments); gen->write_call_method_bind(return_addr, base, method, arguments);
} }
} else { } else {
gen->write_call(return_addr, base, call->function_name, arguments); gen->write_call(return_addr, base, call->function_name, arguments);
} }
} else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) { } else if (base.type.has_type && base.type.kind == GDScriptDataType::BUILTIN) {
gen->write_call_builtin_type(result, base, base.type.builtin_type, call->function_name, arguments); gen->write_call_builtin_type(return_addr, base, base.type.builtin_type, call->function_name, arguments);
} else { } else {
gen->write_call(return_addr, base, call->function_name, arguments); gen->write_call(return_addr, base, call->function_name, arguments);
} }

View file

@ -1533,8 +1533,28 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
Callable::CallError err; Callable::CallError err;
if (call_ret) { if (call_ret) {
GET_INSTRUCTION_ARG(ret, argc + 1); GET_INSTRUCTION_ARG(ret, argc + 1);
#ifdef DEBUG_ENABLED
Variant::Type base_type = base->get_type();
Object *base_obj = base->get_validated_object();
StringName base_class = base_obj ? base_obj->get_class_name() : StringName();
#endif
base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err); base->callp(*methodname, (const Variant **)argptrs, argc, *ret, err);
#ifdef DEBUG_ENABLED #ifdef DEBUG_ENABLED
if (ret->get_type() == Variant::NIL) {
if (base_type == Variant::OBJECT) {
if (base_obj) {
MethodBind *method = ClassDB::get_method(base_class, *methodname);
if (*methodname == CoreStringNames::get_singleton()->_free || (method && !method->has_return())) {
err_text = R"(Trying to get a return value of a method that returns "void")";
OPCODE_BREAK;
}
}
} else if (Variant::has_builtin_method(base_type, *methodname) && !Variant::has_builtin_method_return_value(base_type, *methodname)) {
err_text = R"(Trying to get a return value of a method that returns "void")";
OPCODE_BREAK;
}
}
if (!call_async && ret->get_type() == Variant::OBJECT) { if (!call_async && ret->get_type() == Variant::OBJECT) {
// Check if getting a function state without await. // Check if getting a function state without await.
bool was_freed = false; bool was_freed = false;

View file

@ -80,10 +80,6 @@ String GDScriptWarning::get_message() const {
case STANDALONE_EXPRESSION: { case STANDALONE_EXPRESSION: {
return "Standalone expression (the line has no effect)."; return "Standalone expression (the line has no effect).";
} break; } break;
case VOID_ASSIGNMENT: {
CHECK_SYMBOLS(1);
return "Assignment operation, but the function '" + symbols[0] + "()' returns void.";
} break;
case NARROWING_CONVERSION: { case NARROWING_CONVERSION: {
return "Narrowing conversion (float is converted to int and loses precision)."; return "Narrowing conversion (float is converted to int and loses precision).";
} break; } break;
@ -202,7 +198,6 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"UNREACHABLE_CODE", "UNREACHABLE_CODE",
"UNREACHABLE_PATTERN", "UNREACHABLE_PATTERN",
"STANDALONE_EXPRESSION", "STANDALONE_EXPRESSION",
"VOID_ASSIGNMENT",
"NARROWING_CONVERSION", "NARROWING_CONVERSION",
"INCOMPATIBLE_TERNARY", "INCOMPATIBLE_TERNARY",
"UNUSED_SIGNAL", "UNUSED_SIGNAL",

View file

@ -57,7 +57,6 @@ public:
UNREACHABLE_CODE, // Code after a return statement. UNREACHABLE_CODE, // Code after a return statement.
UNREACHABLE_PATTERN, // Pattern in a match statement after a catch all pattern (wildcard or bind). UNREACHABLE_PATTERN, // Pattern in a match statement after a catch all pattern (wildcard or bind).
STANDALONE_EXPRESSION, // Expression not assigned to a variable. STANDALONE_EXPRESSION, // Expression not assigned to a variable.
VOID_ASSIGNMENT, // Function returns void but it's assigned to a variable.
NARROWING_CONVERSION, // Float value into an integer slot, precision is lost. NARROWING_CONVERSION, // Float value into an integer slot, precision is lost.
INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible. INCOMPATIBLE_TERNARY, // Possible values of a ternary if are not mutually compatible.
UNUSED_SIGNAL, // Signal is defined but never emitted. UNUSED_SIGNAL, // Signal is defined but never emitted.

View file

@ -0,0 +1,3 @@
func test():
var builtin := []
print(builtin.reverse()) # Built-in type method.

View file

@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot get return value of call to "reverse()" because it returns "void".

View file

@ -0,0 +1,5 @@
func foo() -> void:
pass
func test():
print(foo()) # Custom method.

View file

@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot get return value of call to "foo()" because it returns "void".

View file

@ -0,0 +1,2 @@
func test():
print(print_debug()) # GDScript utility function.

View file

@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot get return value of call to "print_debug()" because it returns "void".

View file

@ -0,0 +1,3 @@
func test():
var obj := Node.new()
print(obj.free()) # Native type method.

View file

@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot get return value of call to "free()" because it returns "void".

View file

@ -0,0 +1,2 @@
func test():
print(print()) # Built-in utility function.

View file

@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Cannot get return value of call to "print()" because it returns "void".

View file

@ -1,6 +0,0 @@
func i_return_void() -> void:
return
func test():
var __ = i_return_void()

View file

@ -1,5 +0,0 @@
GDTEST_OK
>> WARNING
>> Line: 6
>> VOID_ASSIGNMENT
>> Assignment operation, but the function 'i_return_void()' returns void.

View file

@ -0,0 +1,4 @@
func test():
var obj
obj = Node.new()
print(obj.free())

View file

@ -0,0 +1,6 @@
GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/use_return_value_of_free_call.gd
>> 4
>> Trying to get a return value of a method that returns "void"

View file

@ -0,0 +1,4 @@
func test():
var value
value = []
print(value.reverse())

View file

@ -0,0 +1,6 @@
GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/use_return_value_of_void_builtin_method_call.gd
>> 4
>> Trying to get a return value of a method that returns "void"

View file

@ -0,0 +1,4 @@
func test():
var obj
obj = RefCounted.new()
print(obj.notify_property_list_changed())

View file

@ -0,0 +1,6 @@
GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/use_return_value_of_void_native_method_call.gd
>> 4
>> Trying to get a return value of a method that returns "void"

View file

@ -15,10 +15,10 @@ func test():
var string_array: Array[String] = [] var string_array: Array[String] = []
var stringname_array: Array[StringName] = [] var stringname_array: Array[StringName] = []
assert(!string_array.push_back(&"abc")) string_array.push_back(&"abc")
print("Array[String] insert converted: ", typeof(string_array[0]) == TYPE_STRING) print("Array[String] insert converted: ", typeof(string_array[0]) == TYPE_STRING)
assert(!stringname_array.push_back("abc")) stringname_array.push_back("abc")
print("Array[StringName] insert converted: ", typeof(stringname_array[0]) == TYPE_STRING_NAME) print("Array[StringName] insert converted: ", typeof(stringname_array[0]) == TYPE_STRING_NAME)
print("StringName in Array[String]: ", &"abc" in string_array) print("StringName in Array[String]: ", &"abc" in string_array)
@ -28,8 +28,8 @@ func test():
assert(!packed_string_array.push_back("abc")) assert(!packed_string_array.push_back("abc"))
print("StringName in PackedStringArray: ", &"abc" in packed_string_array) print("StringName in PackedStringArray: ", &"abc" in packed_string_array)
assert(!string_array.push_back("abc")) string_array.push_back("abc")
print("StringName finds String in Array: ", string_array.find(&"abc")) print("StringName finds String in Array: ", string_array.find(&"abc"))
assert(!stringname_array.push_back(&"abc")) stringname_array.push_back(&"abc")
print("String finds StringName in Array: ", stringname_array.find("abc")) print("String finds StringName in Array: ", stringname_array.find("abc"))