summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml4
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp30
-rw-r--r--modules/gdscript/gdscript_editor.cpp45
-rw-r--r--modules/gdscript/gdscript_parser.cpp61
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/await_signal_no_infer.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node_inner_class.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.gd6
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/await_type_inference.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/await_type_inference.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.gd7
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/onready_on_inner_class_with_non_node_outer.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.gd15
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_usage.gd6
21 files changed, 179 insertions, 48 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 026b603683..e05b17168d 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -576,10 +576,10 @@
@rpc
func fn(): pass
- @rpc(any_peer, unreliable_ordered)
+ @rpc("any_peer", "unreliable_ordered")
func fn_update_pos(): pass
- @rpc(authority, call_remote, unreliable, 0) # Equivalent to @rpc
+ @rpc("authority", "call_remote", "unreliable", 0) # Equivalent to @rpc
func fn_default(): pass
[/codeblock]
</description>
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index e84c79d681..602d07d9a7 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -1576,11 +1576,8 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
resolve_suite(p_function->body);
- GDScriptParser::DataType return_type = p_function->body->get_datatype();
-
- if (!p_function->get_datatype().is_hard_type() && return_type.is_set()) {
+ if (!p_function->get_datatype().is_hard_type() && p_function->body->get_datatype().is_set()) {
// Use the suite inferred type if return isn't explicitly set.
- return_type.type_source = GDScriptParser::DataType::INFERRED;
p_function->set_datatype(p_function->body->get_datatype());
} else if (p_function->get_datatype().is_hard_type() && (p_function->get_datatype().kind != GDScriptParser::DataType::BUILTIN || p_function->get_datatype().builtin_type != Variant::NIL)) {
if (!p_function->body->has_return && (p_is_lambda || p_function->identifier->name != GDScriptLanguage::get_singleton()->strings._init)) {
@@ -2471,30 +2468,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
@@ -4113,7 +4107,6 @@ void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternar
if (!is_type_compatible(true_type, false_type)) {
result = false_type;
if (!is_type_compatible(false_type, true_type)) {
- result.type_source = GDScriptParser::DataType::UNDETECTED;
result.kind = GDScriptParser::DataType::VARIANT;
#ifdef DEBUG_ENABLED
parser->push_warning(p_ternary_op, GDScriptWarning::INCOMPATIBLE_TERNARY);
@@ -4121,6 +4114,7 @@ void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternar
}
}
}
+ result.type_source = true_type.is_hard_type() && false_type.is_hard_type() ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
p_ternary_op->set_datatype(result);
}
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 61ed88a9ca..3543c0a79f 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -800,6 +800,15 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
warning.insert_text = warning.display.quote(p_quote_style);
r_result.insert(warning.display, warning);
}
+ } else if (p_annotation->name == SNAME("@rpc")) {
+ if (p_argument == 0 || p_argument == 1 || p_argument == 2) {
+ static const char *options[7] = { "call_local", "call_remote", "any_peer", "authority", "reliable", "unreliable", "unreliable_ordered" };
+ for (int i = 0; i < 7; i++) {
+ ScriptLanguage::CodeCompletionOption option(options[i], ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);
+ option.insert_text = option.display.quote(p_quote_style);
+ r_result.insert(option.display, option);
+ }
+ }
}
}
@@ -968,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);
@@ -1024,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);
@@ -1032,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;
@@ -1084,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);
@@ -1106,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);
+ }
}
}
@@ -2022,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();
@@ -2133,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 d99a2b86a2..0a1a64cb59 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -1833,10 +1833,18 @@ GDScriptParser::IfNode *GDScriptParser::parse_if(const String &p_token) {
if (match(GDScriptTokenizer::Token::ELIF)) {
SuiteNode *else_block = alloc_node<SuiteNode>();
+ else_block->parent_function = current_function;
+ else_block->parent_block = current_suite;
+
+ SuiteNode *previous_suite = current_suite;
+ current_suite = else_block;
+
IfNode *elif = parse_if("elif");
else_block->statements.push_back(elif);
complete_extents(else_block);
n_if->false_block = else_block;
+
+ current_suite = previous_suite;
} else if (match(GDScriptTokenizer::Token::ELSE)) {
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "else".)");
n_if->false_block = parse_suite(R"("else" block)");
@@ -3611,6 +3619,10 @@ 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 (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);
+ }
+
VariableNode *variable = static_cast<VariableNode *>(p_node);
if (variable->onready) {
push_error(R"("@onready" annotation can only be used once per variable.)");
@@ -3685,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;
}
}
@@ -3902,26 +3921,46 @@ bool GDScriptParser::rpc_annotation(const AnnotationNode *p_annotation, Node *p_
push_error(R"(Invalid RPC arguments. At most 4 arguments are allowed, where only the last argument can be an integer to specify the channel.')", p_annotation);
return false;
}
+
+ unsigned char locality_args = 0;
+ unsigned char permission_args = 0;
+ unsigned char transfer_mode_args = 0;
+
for (int i = last; i >= 0; i--) {
- String mode = p_annotation->resolved_arguments[i].operator String();
- if (mode == "any_peer") {
- rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_ANY_PEER;
- } else if (mode == "authority") {
- rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_AUTHORITY;
- } else if (mode == "call_local") {
+ String arg = p_annotation->resolved_arguments[i].operator String();
+ if (arg == "call_local") {
+ locality_args++;
rpc_config["call_local"] = true;
- } else if (mode == "call_remote") {
+ } else if (arg == "call_remote") {
+ locality_args++;
rpc_config["call_local"] = false;
- } else if (mode == "reliable") {
+ } else if (arg == "any_peer") {
+ permission_args++;
+ rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_ANY_PEER;
+ } else if (arg == "authority") {
+ permission_args++;
+ rpc_config["rpc_mode"] = MultiplayerAPI::RPC_MODE_AUTHORITY;
+ } else if (arg == "reliable") {
+ transfer_mode_args++;
rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_RELIABLE;
- } else if (mode == "unreliable") {
+ } else if (arg == "unreliable") {
+ transfer_mode_args++;
rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE;
- } else if (mode == "unreliable_ordered") {
+ } else if (arg == "unreliable_ordered") {
+ transfer_mode_args++;
rpc_config["transfer_mode"] = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE_ORDERED;
} else {
- push_error(R"(Invalid RPC argument. Must be one of: 'call_local'/'call_remote' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation);
+ push_error(R"(Invalid RPC argument. Must be one of: "call_local"/"call_remote" (local calls), "any_peer"/"authority" (permission), "reliable"/"unreliable"/"unreliable_ordered" (transfer mode).)", p_annotation);
}
}
+
+ if (locality_args > 1) {
+ push_error(R"(Invalid RPC config. The locality ("call_local"/"call_remote") must be specified no more than once.)", p_annotation);
+ } else if (permission_args > 1) {
+ push_error(R"(Invalid RPC config. The permission ("any_peer"/"authority") must be specified no more than once.)", p_annotation);
+ } else if (transfer_mode_args > 1) {
+ push_error(R"(Invalid RPC config. The transfer mode ("reliable"/"unreliable"/"unreliable_ordered") must be specified no more than once.)", p_annotation);
+ }
}
function->rpc_config = rpc_config;
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/get_node_shorthand_within_non_node.gd b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.gd
new file mode 100644
index 0000000000..91f5071fa9
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.gd
@@ -0,0 +1,5 @@
+extends RefCounted
+
+func test():
+ var nope := $Node
+ print("Cannot use $ without a Node base")
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.out b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.out
new file mode 100644
index 0000000000..33365908bf
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/get_node_shorthand_within_non_node.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.gd b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.gd
new file mode 100644
index 0000000000..e781315266
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.gd
@@ -0,0 +1,6 @@
+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.out b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.out
new file mode 100644
index 0000000000..8088d28329
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/onready_within_non_node.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/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/errors/ternary_weak_infer.gd b/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.gd
new file mode 100644
index 0000000000..fac0e8756c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.gd
@@ -0,0 +1,6 @@
+func test():
+ var left_hard_int := 1
+ var right_weak_int = 2
+ var result_hm_int := left_hard_int if true else right_weak_int
+
+ print('not ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.out b/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.out
new file mode 100644
index 0000000000..71d1e2f8ae
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/ternary_weak_infer.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot infer the type of "result_hm_int" variable because the value doesn't have a set type.
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/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/ternary_hard_infer.gd b/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.gd
new file mode 100644
index 0000000000..44ca5f4dd0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.gd
@@ -0,0 +1,15 @@
+func test():
+ var left_hard_int := 1
+ var right_hard_int := 2
+ var result_hard_int := left_hard_int if true else right_hard_int
+ assert(result_hard_int == 1)
+
+ @warning_ignore("inference_on_variant")
+ var left_hard_variant := 1 as Variant
+ @warning_ignore("inference_on_variant")
+ var right_hard_variant := 2.0 as Variant
+ @warning_ignore("inference_on_variant")
+ var result_hard_variant := left_hard_variant if true else right_hard_variant
+ assert(result_hard_variant == 1)
+
+ print('ok')
diff --git a/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.out b/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.out
new file mode 100644
index 0000000000..1b47ed10dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/ternary_hard_infer.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')