diff options
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 152 |
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() { |