summaryrefslogtreecommitdiff
path: root/modules/gdscript/gdscript_analyzer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp427
1 files changed, 289 insertions, 138 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index af3d65d4d6..704dda8045 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -260,21 +260,21 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
if (!p_class->extends_path.is_empty()) {
if (p_class->extends_path.is_relative_path()) {
- p_class->extends_path = class_type.script_path.get_base_dir().plus_file(p_class->extends_path).simplify_path();
+ p_class->extends_path = class_type.script_path.get_base_dir().path_join(p_class->extends_path).simplify_path();
}
- Ref<GDScriptParserRef> parser = get_parser_for(p_class->extends_path);
- if (parser.is_null()) {
+ Ref<GDScriptParserRef> ext_parser = get_parser_for(p_class->extends_path);
+ if (ext_parser.is_null()) {
push_error(vformat(R"(Could not resolve super class path "%s".)", p_class->extends_path), p_class);
return ERR_PARSE_ERROR;
}
- Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = ext_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", p_class->extends_path), p_class);
return err;
}
- base = parser->get_parser()->head->get_datatype();
+ base = ext_parser->get_parser()->head->get_datatype();
} else {
if (p_class->extends.is_empty()) {
push_error("Could not resolve an empty super class path.", p_class);
@@ -289,18 +289,18 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
if (base_path == parser->script_path) {
base = parser->head->get_datatype();
} else {
- Ref<GDScriptParserRef> parser = get_parser_for(base_path);
- if (parser.is_null()) {
+ Ref<GDScriptParserRef> base_parser = get_parser_for(base_path);
+ if (base_parser.is_null()) {
push_error(vformat(R"(Could not resolve super class "%s".)", name), p_class);
return ERR_PARSE_ERROR;
}
- Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = base_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
return err;
}
- base = parser->get_parser()->head->get_datatype();
+ base = base_parser->get_parser()->head->get_datatype();
}
} else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) {
const ProjectSettings::AutoloadInfo &info = ProjectSettings::get_singleton()->get_autoload(name);
@@ -309,13 +309,13 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
return ERR_PARSE_ERROR;
}
- Ref<GDScriptParserRef> parser = get_parser_for(info.path);
- if (parser.is_null()) {
+ Ref<GDScriptParserRef> info_parser = get_parser_for(info.path);
+ if (info_parser.is_null()) {
push_error(vformat(R"(Could not parse singleton from "%s".)", info.path), p_class);
return ERR_PARSE_ERROR;
}
- Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = info_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
if (err != OK) {
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
return err;
@@ -484,12 +484,23 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
if (parser->script_path == ScriptServer::get_global_class_path(first)) {
result = parser->head->get_datatype();
} else {
- Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(first));
- if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
- push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
- return GDScriptParser::DataType();
+ String path = ScriptServer::get_global_class_path(first);
+ String ext = path.get_extension();
+ if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
+ Ref<GDScriptParserRef> ref = get_parser_for(path);
+ if (!ref.is_valid() || ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
+ push_error(vformat(R"(Could not parse global class "%s" from "%s".)", first, ScriptServer::get_global_class_path(first)), p_type);
+ return GDScriptParser::DataType();
+ }
+ result = ref->get_parser()->head->get_datatype();
+ } else {
+ result.kind = GDScriptParser::DataType::SCRIPT;
+ result.native_type = ScriptServer::get_global_class_native_base(first);
+ result.script_type = ResourceLoader::load(path, "Script");
+ result.script_path = path;
+ result.is_constant = true;
+ result.is_meta_type = false;
}
- result = ref->get_parser()->head->get_datatype();
}
} 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);
@@ -540,12 +551,13 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result = ref->get_parser()->head->get_datatype();
result.is_meta_type = false;
} else {
- Ref<GDScript> script = member.constant->initializer->reduced_value;
+ Ref<Script> script = member.constant->initializer->reduced_value;
result.kind = GDScriptParser::DataType::SCRIPT;
result.builtin_type = Variant::OBJECT;
result.script_type = script;
result.script_path = script->get_path();
result.native_type = script->get_instance_base_type();
+ result.is_meta_type = false;
}
break;
}
@@ -647,7 +659,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
}
- // Check if initalizer is an unset identifier (ie: a variable within scope, but declared below)
+ // Check if initializer is an unset identifier (ie: a variable within scope, but declared below)
if (member.variable->initializer && !member.variable->initializer->get_datatype().is_set()) {
if (member.variable->initializer->type == GDScriptParser::Node::IDENTIFIER) {
GDScriptParser::IdentifierNode *initializer_identifier = static_cast<GDScriptParser::IdentifierNode *>(member.variable->initializer);
@@ -655,43 +667,43 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
} else {
ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier.");
}
- } else {
- if (member.variable->datatype_specifier != nullptr) {
- datatype = specified_type;
+ }
- if (member.variable->initializer != nullptr) {
- if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
- // Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
- push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(member.variable->initializer);
- member.variable->use_conversion_assign = true;
- }
- } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
-#ifdef DEBUG_ENABLED
- parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
- }
- if (member.variable->initializer->get_datatype().is_variant()) {
- // TODO: Warn unsafe assign.
+ if (member.variable->datatype_specifier != nullptr) {
+ datatype = specified_type;
+
+ if (member.variable->initializer != nullptr) {
+ if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
+ push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
+ } else {
+ // TODO: Add warning.
mark_node_unsafe(member.variable->initializer);
member.variable->use_conversion_assign = true;
}
+ } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
+#ifdef DEBUG_ENABLED
+ parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
+#endif
}
- } else if (member.variable->infer_datatype) {
- if (member.variable->initializer == nullptr) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
- } else if (!datatype.is_set() || datatype.has_no_type()) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
- } else if (datatype.is_variant()) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
- } else if (datatype.builtin_type == Variant::NIL) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
+ if (member.variable->initializer->get_datatype().is_variant()) {
+ // TODO: Warn unsafe assign.
+ mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
}
- datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
+ } else if (member.variable->infer_datatype) {
+ if (member.variable->initializer == nullptr) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
+ } else if (!datatype.is_set() || datatype.has_no_type()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.is_variant()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.builtin_type == Variant::NIL) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
+ }
+ datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
datatype.is_constant = false;
@@ -860,6 +872,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
case GDScriptParser::ClassNode::Member::CLASS:
check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class);
break;
+ case GDScriptParser::ClassNode::Member::GROUP:
+ // No-op, but needed to silence warnings.
+ break;
case GDScriptParser::ClassNode::Member::UNDEFINED:
ERR_PRINT("Trying to resolve undefined member.");
break;
@@ -898,7 +913,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
}
#ifdef DEBUG_ENABLED
- Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
+ HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes;
for (uint32_t ignored_warning : member.function->ignored_warnings) {
parser->ignored_warning_codes.insert(ignored_warning);
}
@@ -947,7 +962,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
#ifdef DEBUG_ENABLED
- Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
+ HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes;
for (uint32_t ignored_warning : member.function->ignored_warnings) {
parser->ignored_warning_codes.insert(ignored_warning);
}
@@ -970,21 +985,26 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
if (getter_function == nullptr) {
push_error(vformat(R"(Getter "%s" not found.)", member.variable->getter_pointer->name), member.variable);
-
- } else if (getter_function->parameters.size() != 0 || getter_function->datatype.has_no_type()) {
- push_error(vformat(R"(Function "%s" cannot be used as getter because of its signature.)", getter_function->identifier->name), member.variable);
-
- } else if (!is_type_compatible(member.variable->datatype, getter_function->datatype, true)) {
- push_error(vformat(R"(Function with return type "%s" cannot be used as getter for a property of type "%s".)", getter_function->datatype.to_string(), member.variable->datatype.to_string()), member.variable);
-
} else {
- has_valid_getter = true;
+ GDScriptParser::DataType return_datatype = getter_function->datatype;
+ if (getter_function->return_type != nullptr) {
+ return_datatype = getter_function->return_type->datatype;
+ return_datatype.is_meta_type = false;
+ }
+
+ if (getter_function->parameters.size() != 0 || return_datatype.has_no_type()) {
+ push_error(vformat(R"(Function "%s" cannot be used as getter because of its signature.)", getter_function->identifier->name), member.variable);
+ } else if (!is_type_compatible(member.variable->datatype, return_datatype, true)) {
+ push_error(vformat(R"(Function with return type "%s" cannot be used as getter for a property of type "%s".)", return_datatype.to_string(), member.variable->datatype.to_string()), member.variable);
+ } else {
+ has_valid_getter = true;
#ifdef DEBUG_ENABLED
- if (member.variable->datatype.builtin_type == Variant::INT && getter_function->datatype.builtin_type == Variant::FLOAT) {
- parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION);
- }
+ if (member.variable->datatype.builtin_type == Variant::INT && return_datatype.builtin_type == Variant::FLOAT) {
+ parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION);
+ }
#endif
+ }
}
}
@@ -1026,7 +1046,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
}
}
-void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
+void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root) {
ERR_FAIL_COND_MSG(p_node == nullptr, "Trying to resolve type of a null node.");
switch (p_node->type) {
@@ -1099,7 +1119,7 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
case GDScriptParser::Node::SUBSCRIPT:
case GDScriptParser::Node::TERNARY_OPERATOR:
case GDScriptParser::Node::UNARY_OPERATOR:
- reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node), true);
+ reduce_expression(static_cast<GDScriptParser::ExpressionNode *>(p_node), p_is_root);
break;
case GDScriptParser::Node::BREAK:
case GDScriptParser::Node::BREAKPOINT:
@@ -1160,8 +1180,16 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
}
} else {
- GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type);
- p_function->set_datatype(return_type);
+ if (p_function->return_type != nullptr) {
+ p_function->set_datatype(resolve_datatype(p_function->return_type));
+ } else {
+ // In case the function is not typed, we can safely assume it's a Variant, so it's okay to mark as "inferred" here.
+ // It's not "undetected" to not mix up with unknown functions.
+ GDScriptParser::DataType return_type;
+ return_type.type_source = GDScriptParser::DataType::INFERRED;
+ return_type.kind = GDScriptParser::DataType::VARIANT;
+ p_function->set_datatype(return_type);
+ }
#ifdef TOOLS_ENABLED
// Check if the function signature matches the parent. If not it's an error since it breaks polymorphism.
@@ -1231,7 +1259,7 @@ void GDScriptAnalyzer::resolve_function_body(GDScriptParser::FunctionNode *p_fun
GDScriptParser::DataType return_type = p_function->body->get_datatype();
- if (p_function->get_datatype().has_no_type() && return_type.is_set()) {
+ if (!p_function->get_datatype().is_hard_type() && return_type.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());
@@ -1279,7 +1307,7 @@ void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) {
}
#ifdef DEBUG_ENABLED
- Set<uint32_t> previously_ignored = parser->ignored_warning_codes;
+ HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes;
for (uint32_t ignored_warning : stmt->ignored_warnings) {
parser->ignored_warning_codes.insert(ignored_warning);
}
@@ -1354,7 +1382,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
if (all_is_constant) {
switch (args.size()) {
case 1:
- reduced = args[0];
+ reduced = (int32_t)args[0];
break;
case 2:
reduced = Vector2i(args[0], args[1]);
@@ -1388,7 +1416,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else?
p_for->variable->set_datatype(variable_type);
} else if (p_for->list) {
- resolve_node(p_for->list);
+ resolve_node(p_for->list, false);
if (p_for->list->datatype.has_container_element_type()) {
variable_type = p_for->list->datatype.get_container_element_type();
variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
@@ -1416,7 +1444,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
}
void GDScriptAnalyzer::resolve_while(GDScriptParser::WhileNode *p_while) {
- resolve_node(p_while->condition);
+ resolve_node(p_while->condition, false);
resolve_suite(p_while->loop);
p_while->set_datatype(p_while->loop->get_datatype());
@@ -1514,10 +1542,22 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) {
GDScriptParser::DataType type;
+ GDScriptParser::DataType explicit_type;
+ if (p_constant->datatype_specifier != nullptr) {
+ explicit_type = resolve_datatype(p_constant->datatype_specifier);
+ explicit_type.is_meta_type = false;
+ }
+
if (p_constant->initializer != nullptr) {
reduce_expression(p_constant->initializer);
if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer));
+ GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer);
+ const_fold_array(array);
+
+ // Can only infer typed array if it has elements.
+ if (array->elements.size() > 0 || (p_constant->datatype_specifier != nullptr && explicit_type.has_container_element_type())) {
+ update_array_literal_element_type(explicit_type, array);
+ }
} else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
}
@@ -1536,8 +1576,6 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
}
if (p_constant->datatype_specifier != nullptr) {
- GDScriptParser::DataType explicit_type = resolve_datatype(p_constant->datatype_specifier);
- explicit_type.is_meta_type = false;
if (!is_type_compatible(explicit_type, type)) {
push_error(vformat(R"(Assigned value for constant "%s" has type %s which is not compatible with defined type %s.)", p_constant->identifier->name, type.to_string(), explicit_type.to_string()), p_constant->initializer);
#ifdef DEBUG_ENABLED
@@ -1569,8 +1607,8 @@ void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
reduce_expression(p_assert->condition);
if (p_assert->message != nullptr) {
reduce_expression(p_assert->message);
- if (!p_assert->message->is_constant || p_assert->message->reduced_value.get_type() != Variant::STRING) {
- push_error(R"(Expected constant string for assert error message.)", p_assert->message);
+ if (!p_assert->message->get_datatype().has_no_type() && (p_assert->message->get_datatype().kind != GDScriptParser::DataType::BUILTIN || p_assert->message->get_datatype().builtin_type != Variant::STRING)) {
+ push_error(R"(Expected string for assert error message.)", p_assert->message);
}
}
@@ -1639,8 +1677,8 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc
p_match_pattern->bind->set_datatype(result);
#ifdef DEBUG_ENABLED
is_shadowing(p_match_pattern->bind, "pattern bind");
- if (p_match_pattern->bind->usages == 0) {
- parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNASSIGNED_VARIABLE, p_match_pattern->bind->name);
+ if (p_match_pattern->bind->usages == 0 && !String(p_match_pattern->bind->name).begins_with("_")) {
+ parser->push_warning(p_match_pattern->bind, GDScriptWarning::UNUSED_VARIABLE, p_match_pattern->bind->name);
}
#endif
break;
@@ -1791,7 +1829,7 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
reduce_binary_op(static_cast<GDScriptParser::BinaryOpNode *>(p_expression));
break;
case GDScriptParser::Node::CALL:
- reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression), p_is_root);
+ reduce_call(static_cast<GDScriptParser::CallNode *>(p_expression), false, p_is_root);
break;
case GDScriptParser::Node::CAST:
reduce_cast(static_cast<GDScriptParser::CastNode *>(p_expression));
@@ -2057,7 +2095,8 @@ void GDScriptAnalyzer::reduce_await(GDScriptParser::AwaitNode *p_await) {
p_await->set_datatype(awaiting_type);
#ifdef DEBUG_ENABLED
- if (!awaiting_type.is_coroutine && awaiting_type.builtin_type != Variant::SIGNAL) {
+ awaiting_type = p_await->to_await->get_datatype();
+ if (!(awaiting_type.has_no_type() || awaiting_type.is_coroutine || awaiting_type.builtin_type == Variant::SIGNAL)) {
parser->push_warning(p_await, GDScriptWarning::REDUNDANT_AWAIT);
}
#endif
@@ -2174,7 +2213,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) {
bool all_is_constant = true;
- Map<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
+ HashMap<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
for (int i = 0; i < p_call->arguments.size(); i++) {
reduce_expression(p_call->arguments[i]);
if (p_call->arguments[i]->type == GDScriptParser::Node::ARRAY) {
@@ -2259,6 +2298,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
push_error(vformat(R"(Too few arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
break;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
p_call->is_constant = true;
@@ -2303,7 +2343,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
break;
#ifdef DEBUG_ENABLED
} else {
- if (par_type.builtin_type == Variant::INT && p_call->arguments[i]->get_datatype().builtin_type == Variant::FLOAT) {
+ if (par_type.builtin_type == Variant::INT && p_call->arguments[i]->get_datatype().builtin_type == Variant::FLOAT && builtin_type != Variant::INT) {
parser->push_warning(p_call, GDScriptWarning::NARROWING_CONVERSION, p_call->function_name);
}
#endif
@@ -2361,6 +2401,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
@@ -2389,9 +2430,15 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
- PropertyInfo wrong_arg = function_info.arguments[err.argument];
+ String expected_type_name;
+ if (err.argument < function_info.arguments.size()) {
+ expected_type_name = type_from_property(function_info.arguments[err.argument]).to_string();
+ } else {
+ expected_type_name = Variant::get_type_name((Variant::Type)err.expected);
+ }
+
push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
- type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
+ expected_type_name, p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
} break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
@@ -2403,6 +2450,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
@@ -2426,6 +2474,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
base_type = parser->current_class->base_type;
base_type.is_meta_type = false;
is_self = true;
+
+ if (p_call->callee == nullptr && !lambda_stack.is_empty()) {
+ push_error("Cannot use `super()` inside a lambda.", p_call);
+ }
} else if (callee_type == GDScriptParser::Node::IDENTIFIER) {
base_type = parser->current_class->get_datatype();
base_type.is_meta_type = false;
@@ -2494,18 +2546,34 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
- push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
+ // Get the parent function above any lambda.
+ GDScriptParser::FunctionNode *parent_function = parser->current_function;
+ while (parent_function->source_lambda) {
+ parent_function = parent_function->source_lambda->parent_function;
+ }
+ 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->callee);
- } else if (is_self && !is_static && !lambda_stack.is_empty()) {
- push_error(vformat(R"*(Cannot call non-static function "%s()" from a lambda function.)*", p_call->function_name), p_call->callee);
+ 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);
+ } else if (is_self && !is_static) {
+ mark_lambda_use_self();
+ }
+
+#ifdef DEBUG_ENABLED
+ 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);
+ }
+
+ if (is_static && !base_type.is_meta_type && !(callee_type != GDScriptParser::Node::SUBSCRIPT && parser->current_function != nullptr && parser->current_function->is_static)) {
+ parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, base_type.to_string());
}
+#endif // DEBUG_ENABLED
call_type = return_type;
} else {
- // Check if the name exists as something else.
bool found = false;
+
+ // Check if the name exists as something else.
if (!p_call->is_super && callee_type != GDScriptParser::Node::NONE) {
GDScriptParser::IdentifierNode *callee_id;
if (callee_type == GDScriptParser::Node::IDENTIFIER) {
@@ -2535,11 +2603,13 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) {
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
+ } else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) {
+ push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type.operator String()), p_call);
}
}
if (call_type.is_coroutine && !p_is_await && !p_is_root) {
- push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call->callee);
+ push_error(vformat(R"*(Function "%s()" is a coroutine, so it must be called with "await".)*", p_call->function_name), p_call);
}
p_call->set_datatype(call_type);
@@ -2629,41 +2699,55 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) {
push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node);
- } else if (!lambda_stack.is_empty()) {
- push_error(R"*(Cannot use shorthand "get_node()" notation ("$") inside a lambda. Use a captured variable instead.)*", p_get_node);
}
+ mark_lambda_use_self();
+
p_get_node->set_datatype(result);
}
GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType type;
- Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(p_class_name));
- if (ref.is_null()) {
- push_error(vformat(R"(Could not find script for class "%s".)", p_class_name), p_source);
- type.type_source = GDScriptParser::DataType::UNDETECTED;
- type.kind = GDScriptParser::DataType::VARIANT;
+ String path = ScriptServer::get_global_class_path(p_class_name);
+ String ext = path.get_extension();
+ if (ext == GDScriptLanguage::get_singleton()->get_extension()) {
+ Ref<GDScriptParserRef> ref = get_parser_for(path);
+ if (ref.is_null()) {
+ push_error(vformat(R"(Could not find script for class "%s".)", p_class_name), p_source);
+ type.type_source = GDScriptParser::DataType::UNDETECTED;
+ type.kind = GDScriptParser::DataType::VARIANT;
+ return type;
+ }
+
+ Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (err) {
+ push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
+ type.type_source = GDScriptParser::DataType::UNDETECTED;
+ type.kind = GDScriptParser::DataType::VARIANT;
+ return type;
+ }
+
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::CLASS;
+ type.builtin_type = Variant::OBJECT;
+ type.native_type = ScriptServer::get_global_class_native_base(p_class_name);
+ type.class_type = ref->get_parser()->head;
+ type.script_path = ref->get_parser()->script_path;
+ type.is_constant = true;
+ type.is_meta_type = true;
return type;
- }
-
- Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
- if (err) {
- push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
- type.type_source = GDScriptParser::DataType::UNDETECTED;
- type.kind = GDScriptParser::DataType::VARIANT;
+ } else {
+ type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ type.kind = GDScriptParser::DataType::SCRIPT;
+ type.builtin_type = Variant::OBJECT;
+ type.native_type = ScriptServer::get_global_class_native_base(p_class_name);
+ type.script_type = ResourceLoader::load(path, "Script");
+ type.script_path = path;
+ type.is_constant = true;
+ type.is_meta_type = true;
return type;
}
-
- type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = GDScriptParser::DataType::CLASS;
- type.builtin_type = Variant::OBJECT;
- type.native_type = ScriptServer::get_global_class_native_base(p_class_name);
- type.class_type = ref->get_parser()->head;
- type.script_path = ref->get_parser()->script_path;
- type.is_constant = true;
- type.is_meta_type = true;
- return type;
}
void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base) {
@@ -2689,6 +2773,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
result.builtin_type = Variant::INT;
result.native_type = base.native_type;
result.enum_type = base.enum_type;
+ result.enum_values = base.enum_values;
p_identifier->set_datatype(result);
return;
} else {
@@ -2847,29 +2932,34 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
MethodBind *getter = ClassDB::get_method(native, getter_name);
if (getter != nullptr) {
p_identifier->set_datatype(type_from_property(getter->get_return_info()));
+ p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
}
return;
}
if (ClassDB::get_method_info(native, name, &method_info)) {
// Method is callable.
p_identifier->set_datatype(make_callable_type(method_info));
+ p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
return;
}
if (ClassDB::get_signal(native, name, &method_info)) {
// Signal is a type too.
p_identifier->set_datatype(make_signal_type(method_info));
+ p_identifier->source = GDScriptParser::IdentifierNode::INHERITED_VARIABLE;
return;
}
if (ClassDB::has_enum(native, name)) {
p_identifier->set_datatype(make_native_enum_type(native, name));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
return;
}
bool valid = false;
- int int_constant = ClassDB::get_integer_constant(native, name, &valid);
+ int64_t int_constant = ClassDB::get_integer_constant(native, name, &valid);
if (valid) {
p_identifier->is_constant = true;
p_identifier->reduced_value = int_constant;
p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
return;
}
}
@@ -2920,7 +3010,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
found_source = true;
break;
+ case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:
+ mark_lambda_use_self();
+ break;
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
+ mark_lambda_use_self();
p_identifier->variable_source->usages++;
[[fallthrough]];
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
@@ -2951,18 +3045,37 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
if (found_source) {
- // If the identifier is local, check if it's any kind of capture by comparing their source function.
- // Only capture locals and members and enum values. Constants are still accessible from the lambda using the script reference.
- if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT || lambda_stack.is_empty()) {
- return;
+ if ((p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) && parser->current_function && parser->current_function->is_static) {
+ // Get the parent function above any lambda.
+ GDScriptParser::FunctionNode *parent_function = parser->current_function;
+ while (parent_function->source_lambda) {
+ parent_function = parent_function->source_lambda->parent_function;
+ }
+ push_error(vformat(R"*(Cannot access instance variable "%s" from the static function "%s()".)*", p_identifier->name, parent_function->identifier->name), p_identifier);
}
- GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function;
- while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) {
- function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size();
- function_test->source_lambda->captures.push_back(p_identifier);
- function_test = function_test->source_lambda->parent_function;
+ if (!lambda_stack.is_empty()) {
+ // If the identifier is a member variable (including the native class properties), we consider the lambda to be using `self`, so we keep a reference to the current instance.
+ if (p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_VARIABLE || p_identifier->source == GDScriptParser::IdentifierNode::INHERITED_VARIABLE) {
+ mark_lambda_use_self();
+ return; // No need to capture.
+ }
+ // If the identifier is local, check if it's any kind of capture by comparing their source function.
+ // Only capture locals and enum values. Constants are still accessible from the lambda using the script reference. If not, this method is done.
+ if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT) {
+ return;
+ }
+
+ GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function;
+ // Make sure we aren't capturing variable in the same lambda.
+ // This also add captures for nested lambdas.
+ while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) {
+ function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size();
+ function_test->source_lambda->captures.push_back(p_identifier);
+ function_test = function_test->source_lambda->parent_function;
+ }
}
+
return;
}
@@ -3001,11 +3114,11 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
result.kind = GDScriptParser::DataType::NATIVE;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
if (autoload.path.to_lower().ends_with(GDScriptLanguage::get_singleton()->get_extension())) {
- Ref<GDScriptParserRef> parser = get_parser_for(autoload.path);
- if (parser.is_valid()) {
- Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Ref<GDScriptParserRef> singl_parser = get_parser_for(autoload.path);
+ if (singl_parser.is_valid()) {
+ Error err = singl_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
if (err == OK) {
- result = type_from_metatype(parser->get_parser()->head->get_datatype());
+ result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
}
}
}
@@ -3120,11 +3233,17 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
p_preload->resolved_path = p_preload->path->reduced_value;
// TODO: Save this as script dependency.
if (p_preload->resolved_path.is_relative_path()) {
- p_preload->resolved_path = parser->script_path.get_base_dir().plus_file(p_preload->resolved_path);
+ p_preload->resolved_path = parser->script_path.get_base_dir().path_join(p_preload->resolved_path);
}
p_preload->resolved_path = p_preload->resolved_path.simplify_path();
- if (!FileAccess::exists(p_preload->resolved_path)) {
- push_error(vformat(R"(Preload file "%s" does not exist.)", p_preload->resolved_path), p_preload->path);
+ if (!ResourceLoader::exists(p_preload->resolved_path)) {
+ Ref<FileAccess> file_check = FileAccess::create(FileAccess::ACCESS_RESOURCES);
+
+ if (file_check->file_exists(p_preload->resolved_path)) {
+ push_error(vformat(R"(Preload file "%s" has no resource loaders (unrecognized file extension).)", p_preload->resolved_path), p_preload->path);
+ } else {
+ push_error(vformat(R"(Preload file "%s" does not exist.)", p_preload->resolved_path), p_preload->path);
+ }
} else {
// TODO: Don't load if validating: use completion cache.
p_preload->resource = ResourceLoader::load(p_preload->resolved_path);
@@ -3142,6 +3261,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
p_self->is_constant = false;
p_self->set_datatype(type_from_metatype(parser->current_class->get_datatype()));
+ mark_lambda_use_self();
}
void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) {
@@ -3152,6 +3272,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true);
} else {
reduce_expression(p_subscript->base);
+
+ if (p_subscript->base->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_subscript->base));
+ } else if (p_subscript->base->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_subscript->base));
+ }
}
GDScriptParser::DataType result_type;
@@ -3166,12 +3292,12 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
if (!valid) {
push_error(vformat(R"(Cannot get member "%s" from "%s".)", p_subscript->attribute->name, p_subscript->base->reduced_value), p_subscript->index);
+ result_type.kind = GDScriptParser::DataType::VARIANT;
} else {
p_subscript->is_constant = true;
p_subscript->reduced_value = value;
result_type = type_from_variant(value, p_subscript);
}
- result_type.kind = GDScriptParser::DataType::VARIANT;
} else {
GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
@@ -3257,8 +3383,11 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::VECTOR2I:
case Variant::VECTOR3:
case Variant::VECTOR3I:
+ case Variant::VECTOR4:
+ case Variant::VECTOR4I:
case Variant::TRANSFORM2D:
case Variant::TRANSFORM3D:
+ case Variant::PROJECTION:
error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT &&
index_type.builtin_type != Variant::STRING;
break;
@@ -3321,6 +3450,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::PACKED_INT64_ARRAY:
case Variant::VECTOR2I:
case Variant::VECTOR3I:
+ case Variant::VECTOR4I:
result_type.builtin_type = Variant::INT;
break;
// Return float.
@@ -3328,6 +3458,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::PACKED_FLOAT64_ARRAY:
case Variant::VECTOR2:
case Variant::VECTOR3:
+ case Variant::VECTOR4:
case Variant::QUATERNION:
result_type.builtin_type = Variant::FLOAT;
break;
@@ -3358,6 +3489,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
break;
// Depends on the index.
case Variant::TRANSFORM3D:
+ case Variant::PROJECTION:
case Variant::PLANE:
case Variant::COLOR:
case Variant::DICTIONARY:
@@ -3469,6 +3601,13 @@ void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) {
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element = p_array->elements[i];
+
+ if (element->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element));
+ } else if (element->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element));
+ }
+
all_is_constant = all_is_constant && element->is_constant;
if (!all_is_constant) {
return;
@@ -3489,6 +3628,13 @@ void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_d
for (int i = 0; i < p_dictionary->elements.size(); i++) {
const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
+
+ if (element.value->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(element.value));
+ } else if (element.value->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(element.value));
+ }
+
all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
if (!all_is_constant) {
return;
@@ -3715,7 +3861,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
Ref<Script> base_script = p_base_type.script_type;
- while (base_script.is_valid() && base_script->is_valid()) {
+ while (base_script.is_valid() && base_script->has_method(function_name)) {
MethodInfo info = base_script->get_method_info(function_name);
if (!(info == MethodInfo())) {
@@ -3769,6 +3915,7 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
r_return_type = type_from_property(p_info.return_val);
r_default_arg_count = p_info.default_arguments.size();
r_vararg = (p_info.flags & METHOD_FLAG_VARARG) != 0;
+ r_static = (p_info.flags & METHOD_FLAG_STATIC) != 0;
for (const PropertyInfo &E : p_info.arguments) {
r_par_types.push_back(type_from_property(E));
@@ -4113,6 +4260,12 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
#endif
}
+void GDScriptAnalyzer::mark_lambda_use_self() {
+ for (GDScriptParser::LambdaNode *lambda : lambda_stack) {
+ lambda->use_self = true;
+ }
+}
+
bool GDScriptAnalyzer::class_exists(const StringName &p_class) const {
return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class);
}
@@ -4150,13 +4303,11 @@ Error GDScriptAnalyzer::resolve_program() {
resolve_class_interface(parser->head);
resolve_class_body(parser->head);
- List<String> parser_keys;
- depended_parsers.get_key_list(&parser_keys);
- for (const String &E : parser_keys) {
- if (depended_parsers[E].is_null()) {
+ for (KeyValue<String, Ref<GDScriptParserRef>> &K : depended_parsers) {
+ if (K.value.is_null()) {
return ERR_PARSE_ERROR;
}
- depended_parsers[E]->raise_status(GDScriptParserRef::FULLY_SOLVED);
+ K.value->raise_status(GDScriptParserRef::FULLY_SOLVED);
}
return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}