diff options
Diffstat (limited to 'modules/gdscript')
24 files changed, 149 insertions, 44 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index cafc7328e0..78e437b42a 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2471,30 +2471,27 @@ void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) { return; } - GDScriptParser::DataType awaiting_type; - if (p_await->to_await->type == GDScriptParser::Node::CALL) { reduce_call(static_cast<GDScriptParser::CallNode *>(p_await->to_await), true); - awaiting_type = p_await->to_await->get_datatype(); } else { reduce_expression(p_await->to_await); } - if (p_await->to_await->is_constant) { + GDScriptParser::DataType await_type = p_await->to_await->get_datatype(); + // We cannot infer the type of the result of waiting for a signal. + if (await_type.is_hard_type() && await_type.kind == GDScriptParser::DataType::BUILTIN && await_type.builtin_type == Variant::SIGNAL) { + await_type.kind = GDScriptParser::DataType::VARIANT; + await_type.type_source = GDScriptParser::DataType::UNDETECTED; + } else if (p_await->to_await->is_constant) { p_await->is_constant = p_await->to_await->is_constant; p_await->reduced_value = p_await->to_await->reduced_value; - - awaiting_type = p_await->to_await->get_datatype(); - } else { - awaiting_type.kind = GDScriptParser::DataType::VARIANT; - awaiting_type.type_source = GDScriptParser::DataType::UNDETECTED; } - - p_await->set_datatype(awaiting_type); + await_type.is_coroutine = false; + p_await->set_datatype(await_type); #ifdef DEBUG_ENABLED - awaiting_type = p_await->to_await->get_datatype(); - if (!(awaiting_type.has_no_type() || awaiting_type.is_coroutine || awaiting_type.builtin_type == Variant::SIGNAL)) { + GDScriptParser::DataType to_await_type = p_await->to_await->get_datatype(); + if (!(to_await_type.has_no_type() || to_await_type.is_coroutine || to_await_type.builtin_type == Variant::SIGNAL)) { parser->push_warning(p_await, GDScriptWarning::REDUNDANT_AWAIT); } #endif @@ -3020,7 +3017,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parent_function->identifier->name), p_call); } else if (!is_self && base_type.is_meta_type && !is_static) { base_type.is_meta_type = false; // For `to_string()`. - push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call); + push_error(vformat(R"*(Cannot call non-static function "%s()" on a class directly. Make an instance instead.)*", p_call->function_name), p_call); } else if (is_self && !is_static) { mark_lambda_use_self(); } @@ -4567,16 +4564,6 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo base_script = base_script->get_base_script(); } - // If the base is a script, it might be trying to access members of the Script class itself. - if (p_base_type.is_meta_type && !p_is_constructor && (p_base_type.kind == GDScriptParser::DataType::SCRIPT || p_base_type.kind == GDScriptParser::DataType::CLASS)) { - MethodInfo info; - StringName script_class = p_base_type.kind == GDScriptParser::DataType::SCRIPT ? p_base_type.script_type->get_class_name() : StringName(GDScript::get_class_static()); - - if (ClassDB::get_method_info(script_class, function_name, &info)) { - return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg); - } - } - if (p_is_constructor) { // Native types always have a default constructor. r_return_type = p_base_type; diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index 46cd4b0d55..210550a674 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -591,7 +591,6 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } } else if (callee->type == GDScriptParser::Node::SUBSCRIPT) { const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee); - if (subscript->is_attribute) { // 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) { @@ -615,7 +614,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code } else { class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type; } - if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) { + if (!subscript->base->is_constant && ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) { MethodBind *method = ClassDB::get_method(class_name, call->function_name); if (_can_use_ptrcall(method, arguments)) { // Exact arguments, use ptrcall. diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 4e7d278aab..8cfd48b52b 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -977,7 +977,7 @@ static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, } break; case GDScriptParser::ClassNode::Member::SIGNAL: - if (p_only_functions || outer) { + if (p_only_functions || outer || p_static) { continue; } option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); @@ -1033,6 +1033,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); r_result.insert(option.display, option); } + + List<MethodInfo> signals; + scr->get_script_signal_list(&signals); + for (const MethodInfo &E : signals) { + int location = p_recursion_depth + _get_signal_location(scr->get_class_name(), E.name); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); + r_result.insert(option.display, option); + } } HashMap<StringName, Variant> constants; scr->get_constants(&constants); @@ -1041,14 +1049,6 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location); r_result.insert(option.display, option); } - - List<MethodInfo> signals; - scr->get_script_signal_list(&signals); - for (const MethodInfo &E : signals) { - int location = p_recursion_depth + _get_signal_location(scr->get_class_name(), E.name); - ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); - r_result.insert(option.display, option); - } } List<MethodInfo> methods; @@ -1093,14 +1093,6 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base r_result.insert(option.display, option); } - List<MethodInfo> signals; - ClassDB::get_signal_list(type, &signals); - for (const MethodInfo &E : signals) { - int location = p_recursion_depth + _get_signal_location(type, StringName(E.name)); - ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); - r_result.insert(option.display, option); - } - if (!base_type.is_meta_type || Engine::get_singleton()->has_singleton(type)) { List<PropertyInfo> pinfo; ClassDB::get_property_list(type, &pinfo); @@ -1115,6 +1107,14 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location); r_result.insert(option.display, option); } + + List<MethodInfo> signals; + ClassDB::get_signal_list(type, &signals); + for (const MethodInfo &E : signals) { + int location = p_recursion_depth + _get_signal_location(type, StringName(E.name)); + ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location); + r_result.insert(option.display, option); + } } } @@ -2031,6 +2031,7 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; r_type.type.script_path = script; r_type.type.class_type = parser->get_parser()->get_tree(); + r_type.type.is_meta_type = true; r_type.type.is_constant = false; r_type.type.kind = GDScriptParser::DataType::CLASS; r_type.value = Variant(); @@ -2142,6 +2143,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext & r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; r_type.type.kind = GDScriptParser::DataType::CLASS; r_type.type.class_type = member.m_class; + r_type.type.is_meta_type = true; return true; case GDScriptParser::ClassNode::Member::GROUP: return false; // No-op, but silences warnings. diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index 2ec7f77201..0a1a64cb59 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -3619,7 +3619,7 @@ bool GDScriptParser::icon_annotation(const AnnotationNode *p_annotation, Node *p bool GDScriptParser::onready_annotation(const AnnotationNode *p_annotation, Node *p_node) { ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, R"("@onready" annotation can only be applied to class variables.)"); - if (head && !ClassDB::is_parent_class(head->get_datatype().native_type, SNAME("Node"))) { + if (current_class && !ClassDB::is_parent_class(current_class->get_datatype().native_type, SNAME("Node"))) { push_error(R"("@onready" can only be used in classes that inherit "Node".)", p_annotation); } @@ -3697,6 +3697,13 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node variable->export_info.type = Variant::DICTIONARY; return true; + } else if (export_type.builtin_type == Variant::PACKED_STRING_ARRAY) { + String hint_prefix = itos(Variant::STRING) + "/" + itos(variable->export_info.hint); + variable->export_info.hint = PROPERTY_HINT_TYPE_STRING; + variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string; + variable->export_info.type = Variant::PACKED_STRING_ARRAY; + + return true; } } diff --git a/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.gd b/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.gd new file mode 100644 index 0000000000..c787d9e50e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.gd @@ -0,0 +1,4 @@ +signal my_signal() + +func test(): + var _a := await my_signal diff --git a/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.out b/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.out new file mode 100644 index 0000000000..8f8744ad7e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot infer the type of "_a" variable because the value doesn't have a set type. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.gd b/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.gd new file mode 100644 index 0000000000..966d2b0aa2 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.gd @@ -0,0 +1,5 @@ +const TestClass = preload("gdscript_duplicate_class.notest.gd") + +func test(): + # (TestClass as GDScript).duplicate() exists + TestClass.duplicate() diff --git a/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.out b/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.out new file mode 100644 index 0000000000..b2c7fec86e --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +Cannot call non-static function "duplicate()" on a class directly. Make an instance instead. diff --git a/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate_class.notest.gd b/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate_class.notest.gd new file mode 100644 index 0000000000..61510e14cd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/gdscript_duplicate_class.notest.gd @@ -0,0 +1 @@ +extends Node diff --git a/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.gd b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.gd new file mode 100644 index 0000000000..1639bbbd52 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.gd @@ -0,0 +1,7 @@ +extends Node + +class Inner extends RefCounted: + @onready var nope = 0 + +func test(): + print("Cannot use @onready without a Node base") diff --git a/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.out b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.out new file mode 100644 index 0000000000..8088d28329 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.out @@ -0,0 +1,2 @@ +GDTEST_ANALYZER_ERROR +"@onready" can only be used in classes that inherit "Node". diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.gd b/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.gd new file mode 100644 index 0000000000..9d8cfc7f99 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.gd @@ -0,0 +1,15 @@ +func coroutine() -> int: + @warning_ignore("redundant_await") + await 0 + return 1 + +func not_coroutine() -> int: + return 2 + +func test(): + var a := await coroutine() + @warning_ignore("redundant_await") + var b := await not_coroutine() + @warning_ignore("redundant_await") + var c := await 3 + prints(a, b, c) diff --git a/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.out b/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.out new file mode 100644 index 0000000000..2920e2ce9c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/await_type_inference.out @@ -0,0 +1,2 @@ +GDTEST_OK +1 2 3 diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.gd b/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.gd new file mode 100644 index 0000000000..030daf502c --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.gd @@ -0,0 +1,6 @@ +const TestClass = preload("gdscript_duplicate_class.notest.gd") + +func test(): + # TestClass.duplicate() fails + @warning_ignore("return_value_discarded") + (TestClass as GDScript).duplicate() diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.out b/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate_class.notest.gd b/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate_class.notest.gd new file mode 100644 index 0000000000..61510e14cd --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/gdscript_duplicate_class.notest.gd @@ -0,0 +1 @@ +extends Node diff --git a/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.gd b/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.gd new file mode 100644 index 0000000000..1ac03c2181 --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.gd @@ -0,0 +1,7 @@ +extends RefCounted + +func test(): + print("ok") + +class Inner extends Node: + @onready var okay = 0 diff --git a/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.out b/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.out new file mode 100644 index 0000000000..1b47ed10dc --- /dev/null +++ b/modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.out @@ -0,0 +1,2 @@ +GDTEST_OK +ok diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd index e1e6134fd4..092ae49d00 100644 --- a/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd +++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd @@ -201,4 +201,10 @@ func test(): assert(typed_enums.get_typed_builtin() == TYPE_INT) + var a := A.new() + var typed_natives: Array[RefCounted] = [a] + var typed_scripts = Array(typed_natives, TYPE_OBJECT, "RefCounted", A) + assert(typed_scripts[0] == a) + + print('ok') diff --git a/modules/gdscript/tests/scripts/parser/features/super_class_check.gd b/modules/gdscript/tests/scripts/parser/features/super_class_check.gd new file mode 100644 index 0000000000..edfc45a8d8 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/super_class_check.gd @@ -0,0 +1,13 @@ +# https://github.com/godotengine/godot/issues/71994 + +func test(): + pass + +class A extends RefCounted: + pass + +class B extends A: + # Parsing `duplicate()` here would throw this error: + # Parse Error: The function signature doesn't match the parent. Parent signature is "duplicate(bool = default) -> Resource". + func duplicate(): + pass diff --git a/modules/gdscript/tests/scripts/parser/features/super_class_check.out b/modules/gdscript/tests/scripts/parser/features/super_class_check.out new file mode 100644 index 0000000000..d73c5eb7cd --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/features/super_class_check.out @@ -0,0 +1 @@ +GDTEST_OK diff --git a/modules/gdscript/tests/scripts/runtime/features/static_duplicate.gd b/modules/gdscript/tests/scripts/runtime/features/static_duplicate.gd new file mode 100644 index 0000000000..418501dcc5 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/static_duplicate.gd @@ -0,0 +1,19 @@ +const PreloadClass = preload("static_duplicate_preload.notest.gd") +const PreloadClassAlias = PreloadClass + +func test(): + var dup_preload_one = PreloadClass.duplicate() + print(dup_preload_one == Vector2.ONE) + + var dup_preload_two = (PreloadClass as GDScript).duplicate() + print(dup_preload_two is GDScript) + + var dup_preload_alias_one = PreloadClassAlias.duplicate() + print(dup_preload_alias_one == Vector2.ONE) + + var dup_preload_alias_two = (PreloadClassAlias as GDScript).duplicate() + print(dup_preload_alias_two is GDScript) + + var PreloadClassAsGDScript = PreloadClass as GDScript + var dup_preload_class_as_gdscript_one = PreloadClassAsGDScript.duplicate() + print(dup_preload_class_as_gdscript_one is GDScript) diff --git a/modules/gdscript/tests/scripts/runtime/features/static_duplicate.out b/modules/gdscript/tests/scripts/runtime/features/static_duplicate.out new file mode 100644 index 0000000000..34cd5c7652 --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/static_duplicate.out @@ -0,0 +1,9 @@ +GDTEST_OK +preload duplicate +true +true +preload duplicate +true +true +preload duplicate +false diff --git a/modules/gdscript/tests/scripts/runtime/features/static_duplicate_preload.notest.gd b/modules/gdscript/tests/scripts/runtime/features/static_duplicate_preload.notest.gd new file mode 100644 index 0000000000..291ffc2c0b --- /dev/null +++ b/modules/gdscript/tests/scripts/runtime/features/static_duplicate_preload.notest.gd @@ -0,0 +1,5 @@ +extends RefCounted + +static func duplicate() -> Vector2: + print("preload duplicate") + return Vector2.ONE |