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.cpp152
1 files changed, 117 insertions, 35 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 1b76c7f967..a6138cc564 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,6 +37,7 @@
#include "core/os/file_access.h"
#include "core/templates/hash_map.h"
#include "gdscript.h"
+#include "gdscript_utility_functions.h"
// TODO: Move this to a central location (maybe core?).
static HashMap<StringName, StringName> underscore_map;
@@ -59,7 +60,7 @@ static const char *underscore_classes[] = {
nullptr,
};
static StringName get_real_class_name(const StringName &p_source) {
- if (underscore_map.empty()) {
+ if (underscore_map.is_empty()) {
const char **class_name = underscore_classes;
while (*class_name != nullptr) {
underscore_map[*class_name] = String("_") + *class_name;
@@ -72,6 +73,39 @@ static StringName get_real_class_name(const StringName &p_source) {
return p_source;
}
+static MethodInfo info_from_utility_func(const StringName &p_function) {
+ ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo());
+
+ MethodInfo info(p_function);
+
+ if (Variant::has_utility_function_return_value(p_function)) {
+ info.return_val.type = Variant::get_utility_function_return_type(p_function);
+ if (info.return_val.type == Variant::NIL) {
+ info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ }
+
+ if (Variant::is_utility_function_vararg(p_function)) {
+ info.flags |= METHOD_FLAG_VARARG;
+ } else {
+ for (int i = 0; i < Variant::get_utility_function_argument_count(p_function); i++) {
+ PropertyInfo pi;
+#ifdef DEBUG_METHODS_ENABLED
+ pi.name = Variant::get_utility_function_argument_name(p_function, i);
+#else
+ pi.name = "arg" + itos(i + 1);
+#endif
+ pi.type = Variant::get_utility_function_argument_type(p_function, i);
+ if (pi.type == Variant::NIL) {
+ pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ info.arguments.push_back(pi);
+ }
+ }
+
+ return info;
+}
+
void GDScriptAnalyzer::cleanup() {
underscore_map.clear();
}
@@ -175,7 +209,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
int extends_index = 0;
- if (!p_class->extends_path.empty()) {
+ if (!p_class->extends_path.is_empty()) {
Ref<GDScriptParserRef> parser = get_parser_for(p_class->extends_path);
if (parser.is_null()) {
push_error(vformat(R"(Could not resolve super class path "%s".)", p_class->extends_path), p_class);
@@ -190,7 +224,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
base = parser->get_parser()->head->get_datatype();
} else {
- if (p_class->extends.empty()) {
+ if (p_class->extends.is_empty()) {
return ERR_PARSE_ERROR;
}
const StringName &name = p_class->extends[extends_index++];
@@ -342,7 +376,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result.type_source = result.ANNOTATED_EXPLICIT;
result.builtin_type = Variant::OBJECT;
- if (p_type->type_chain.empty()) {
+ if (p_type->type_chain.is_empty()) {
// void.
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::NIL;
@@ -1129,24 +1163,26 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) {
GDScriptParser::DataType type;
- reduce_expression(p_constant->initializer);
- if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
- const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer));
- } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
- const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
- }
+ 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));
+ } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
+ }
- if (!p_constant->initializer->is_constant) {
- push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
- }
+ if (!p_constant->initializer->is_constant) {
+ push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
+ }
- type = p_constant->initializer->get_datatype();
+ type = p_constant->initializer->get_datatype();
#ifdef DEBUG_ENABLED
- if (p_constant->initializer->type == GDScriptParser::Node::CALL && 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);
- }
+ if (p_constant->initializer->type == GDScriptParser::Node::CALL && 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) {
GDScriptParser::DataType explicit_type = resolve_datatype(p_constant->datatype_specifier);
@@ -1181,7 +1217,10 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
reduce_expression(p_assert->condition);
if (p_assert->message != nullptr) {
- reduce_literal(p_assert->message);
+ 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);
+ }
}
p_assert->set_datatype(p_assert->condition->get_datatype());
@@ -1701,7 +1740,6 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
// Call to name directly.
StringName function_name = p_call->function_name;
Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
- GDScriptFunctions::Function builtin_function = GDScriptParser::get_builtin_function(function_name);
if (builtin_type < Variant::VARIANT_MAX) {
// Is a builtin constructor.
@@ -1719,6 +1757,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
// Those are stored by reference so not suited for compile-time construction.
// Because in this case they would be the same reference in all constructed values.
case Variant::OBJECT:
+ case Variant::DICTIONARY:
+ case Variant::ARRAY:
case Variant::PACKED_BYTE_ARRAY:
case Variant::PACKED_INT32_ARRAY:
case Variant::PACKED_INT64_ARRAY:
@@ -1843,10 +1883,52 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
}
p_call->set_datatype(call_type);
return;
- } else if (builtin_function < GDScriptFunctions::FUNC_MAX) {
- MethodInfo function_info = GDScriptFunctions::get_info(builtin_function);
+ } else if (GDScriptUtilityFunctions::function_exists(function_name)) {
+ MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name);
+
+ if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) {
+ // Can call on compilation.
+ Vector<const Variant *> args;
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ args.push_back(&(p_call->arguments[i]->reduced_value));
+ }
+
+ Variant value;
+ Callable::CallError err;
+ GDScriptUtilityFunctions::get_function(function_name)(&value, (const Variant **)args.ptr(), args.size(), err);
+
+ switch (err.error) {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
+ PropertyInfo wrong_arg = function_info.arguments[err.argument];
+ 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()),
+ p_call->arguments[err.argument]);
+ } break;
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
+ push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
+ break;
+ 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_INSTANCE_IS_NULL:
+ break; // Can't happen in a builtin constructor.
+ case Callable::CallError::CALL_OK:
+ p_call->is_constant = true;
+ p_call->reduced_value = value;
+ break;
+ }
+ } else {
+ validate_call_arg(function_info, p_call);
+ }
+ p_call->set_datatype(type_from_property(function_info.return_val));
+ return;
+ } else if (Variant::has_utility_function(function_name)) {
+ MethodInfo function_info = info_from_utility_func(function_name);
- if (all_is_constant && GDScriptFunctions::is_deterministic(builtin_function)) {
+ if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) {
// Can call on compilation.
Vector<const Variant *> args;
for (int i = 0; i < p_call->arguments.size(); i++) {
@@ -1855,23 +1937,23 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
Variant value;
Callable::CallError err;
- GDScriptFunctions::call(builtin_function, (const Variant **)args.ptr(), args.size(), value, err);
+ Variant::call_utility_function(function_name, &value, (const Variant **)args.ptr(), args.size(), err);
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
PropertyInfo wrong_arg = function_info.arguments[err.argument];
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", GDScriptFunctions::get_func_name(builtin_function), err.argument + 1,
+ 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()),
p_call->arguments[err.argument]);
} break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
- push_error(vformat(R"(Invalid call for function "%s".)", GDScriptFunctions::get_func_name(builtin_function)), p_call);
+ push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
break;
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
- push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+ push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
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.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+ 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_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
@@ -1954,14 +2036,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
push_error(vformat(R"*(Name "%s" called as a function but is a "%s".)*", p_call->function_name, callee_datatype.to_string()), p_call->callee);
}
#ifdef DEBUG_ENABLED
- } else if (!is_self) {
+ } else if (!is_self && !(base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN)) {
parser->push_warning(p_call, GDScriptWarning::UNSAFE_METHOD_ACCESS, p_call->function_name, base_type.to_string());
mark_node_unsafe(p_call);
#endif
}
}
}
- if (!found && is_self) {
+ 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);
}
@@ -2385,7 +2467,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
// Not found.
// Check if it's a builtin function.
- if (parser->get_builtin_function(name) < GDScriptFunctions::FUNC_MAX) {
+ if (GDScriptUtilityFunctions::function_exists(name)) {
push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier);
} else {
push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
@@ -3327,12 +3409,12 @@ Error GDScriptAnalyzer::resolve_inheritance() {
Error GDScriptAnalyzer::resolve_interface() {
resolve_class_interface(parser->head);
- return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+ return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::resolve_body() {
resolve_class_body(parser->head);
- return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+ return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::resolve_program() {
@@ -3347,7 +3429,7 @@ Error GDScriptAnalyzer::resolve_program() {
}
depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED);
}
- return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+ return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::analyze() {