diff options
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 503 |
1 files changed, 384 insertions, 119 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 7b64c0564e..7ed440929c 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-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 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 */ @@ -108,7 +108,7 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_native GDScriptParser::DataType type; type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; type.kind = GDScriptParser::DataType::ENUM; - type.builtin_type = Variant::OBJECT; + type.builtin_type = Variant::INT; type.is_constant = true; type.is_meta_type = true; @@ -196,7 +196,7 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C } if (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::NATIVE) { - if (current_data_type->native_type != StringName("")) { + if (current_data_type->native_type != StringName()) { return check_native_member_name_conflict( p_member_name, p_member_node, @@ -250,7 +250,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, if (!p_class->extends_used) { result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; result.kind = GDScriptParser::DataType::NATIVE; - result.native_type = "RefCounted"; + result.native_type = SNAME("RefCounted"); } else { result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -277,6 +277,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, base = 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); return ERR_PARSE_ERROR; } const StringName &name = p_class->extends[extends_index++]; @@ -438,7 +439,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type StringName first = p_type->type_chain[0]->name; - if (first == "Variant") { + if (first == SNAME("Variant")) { result.kind = GDScriptParser::DataType::VARIANT; if (p_type->type_chain.size() > 1) { push_error(R"("Variant" type don't contain nested types.)", p_type->type_chain[1]); @@ -447,9 +448,9 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type return result; } - if (first == "Object") { + if (first == SNAME("Object")) { result.kind = GDScriptParser::DataType::NATIVE; - result.native_type = "Object"; + result.native_type = SNAME("Object"); if (p_type->type_chain.size() > 1) { push_error(R"("Object" type don't contain nested types.)", p_type->type_chain[1]); return GDScriptParser::DataType(); @@ -646,13 +647,23 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } + // 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); + push_error(vformat(R"(Identifier "%s" must be declared above current variable.)", initializer_identifier->name), member.variable->initializer); + } else { + ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier."); + } + } + 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)) { + 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)) { + 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. @@ -849,6 +860,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; @@ -881,12 +895,23 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { for (int i = 0; i < p_class->members.size(); i++) { GDScriptParser::ClassNode::Member member = p_class->members[i]; if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) { - resolve_function_body(member.function); - // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.function->annotations) { E->apply(parser, member.function); } + +#ifdef DEBUG_ENABLED + 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); + } +#endif // DEBUG_ENABLED + + resolve_function_body(member.function); + +#ifdef DEBUG_ENABLED + parser->ignored_warning_codes = previously_ignored; +#endif // DEBUG_ENABLED } else if (member.type == GDScriptParser::ClassNode::Member::VARIABLE && member.variable->property != GDScriptParser::VariableNode::PROP_NONE) { if (member.variable->property == GDScriptParser::VariableNode::PROP_INLINE) { if (member.variable->getter != nullptr) { @@ -925,6 +950,10 @@ 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 + 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); + } if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) { parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name); } @@ -992,6 +1021,9 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) { push_error(vformat(R"(Getter with type "%s" cannot be used along with setter of type "%s".)", getter_function->datatype.to_string(), setter_function->parameters[0]->datatype.to_string()), member.variable); } } +#ifdef DEBUG_ENABLED + parser->ignored_warning_codes = previously_ignored; +#endif // DEBUG_ENABLED } } } @@ -1096,6 +1128,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * GDScriptParser::FunctionNode *previous_function = parser->current_function; parser->current_function = p_function; +#ifdef TOOLS_ENABLED + int default_value_count = 0; +#endif // TOOLS_ENABLED + for (int i = 0; i < p_function->parameters.size(); i++) { resolve_parameter(p_function->parameters[i]); #ifdef DEBUG_ENABLED @@ -1105,13 +1141,17 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * is_shadowing(p_function->parameters[i]->identifier, "function parameter"); #endif // DEBUG_ENABLED #ifdef TOOLS_ENABLED - if (p_function->parameters[i]->default_value && p_function->parameters[i]->default_value->is_constant) { - p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value); + if (p_function->parameters[i]->default_value) { + default_value_count++; + + if (p_function->parameters[i]->default_value->is_constant) { + p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value); + } } #endif // TOOLS_ENABLED } - if (p_function->identifier->name == "_init") { + if (p_function->identifier->name == GDScriptLanguage::get_singleton()->strings._init) { // Constructor. GDScriptParser::DataType return_type = parser->current_class->get_datatype(); return_type.is_meta_type = false; @@ -1123,8 +1163,67 @@ 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. + // Not for the constructor which can vary in signature. + GDScriptParser::DataType base_type = parser->current_class->base_type; + GDScriptParser::DataType parent_return_type; + List<GDScriptParser::DataType> parameters_types; + int default_par_count = 0; + bool is_static = false; + bool is_vararg = false; + if (get_function_signature(p_function, false, base_type, p_function->identifier->name, parent_return_type, parameters_types, default_par_count, is_static, is_vararg)) { + bool valid = p_function->is_static == is_static; + valid = valid && parent_return_type == p_function->get_datatype(); + + int par_count_diff = p_function->parameters.size() - parameters_types.size(); + valid = valid && par_count_diff >= 0; + valid = valid && default_value_count >= default_par_count + par_count_diff; + + int i = 0; + for (const GDScriptParser::DataType &par_type : parameters_types) { + valid = valid && par_type == p_function->parameters[i++]->get_datatype(); + } + + if (!valid) { + // Compute parent signature as a string to show in the error message. + String parent_signature = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant"; + if (parent_signature == "null") { + parent_signature = "void"; + } + parent_signature += " " + p_function->identifier->name.operator String() + "("; + int j = 0; + for (const GDScriptParser::DataType &par_type : parameters_types) { + if (j > 0) { + parent_signature += ", "; + } + String parameter = par_type.to_string(); + if (parameter == "null") { + parameter = "Variant"; + } + parent_signature += parameter; + if (j == parameters_types.size() - default_par_count) { + parent_signature += " = default"; + } + + j++; + } + parent_signature += ")"; + push_error(vformat(R"(The function signature doesn't match the parent. Parent signature is "%s".)", parent_signature), p_function); + } + } +#endif // TOOLS_ENABLED } parser->current_function = previous_function; @@ -1143,7 +1242,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()); @@ -1186,7 +1285,23 @@ void GDScriptAnalyzer::decide_suite_type(GDScriptParser::Node *p_suite, GDScript void GDScriptAnalyzer::resolve_suite(GDScriptParser::SuiteNode *p_suite) { for (int i = 0; i < p_suite->statements.size(); i++) { GDScriptParser::Node *stmt = p_suite->statements[i]; + for (GDScriptParser::AnnotationNode *&annotation : stmt->annotations) { + annotation->apply(parser, stmt); + } + +#ifdef DEBUG_ENABLED + HashSet<uint32_t> previously_ignored = parser->ignored_warning_codes; + for (uint32_t ignored_warning : stmt->ignored_warnings) { + parser->ignored_warning_codes.insert(ignored_warning); + } +#endif // DEBUG_ENABLED + resolve_node(stmt); + +#ifdef DEBUG_ENABLED + parser->ignored_warning_codes = previously_ignored; +#endif // DEBUG_ENABLED + decide_suite_type(p_suite, stmt); } } @@ -1207,7 +1322,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { bool list_resolved = false; // Optimize constant range() call to not allocate an array. - // Use int, Vector2, Vector3 instead, which also can be used as range iterators. + // Use int, Vector2i, Vector3i instead, which also can be used as range iterators. if (p_for->list && p_for->list->type == GDScriptParser::Node::CALL) { GDScriptParser::CallNode *call = static_cast<GDScriptParser::CallNode *>(p_for->list); GDScriptParser::Node::Type callee_type = call->get_callee_type(); @@ -1366,9 +1481,9 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable type.is_meta_type = false; if (p_variable->initializer != nullptr) { - if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true)) { + if (!is_type_compatible(type, p_variable->initializer->get_datatype(), true, p_variable->initializer)) { // Try reverse test since it can be a masked subtype. - if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true)) { + if (!is_type_compatible(p_variable->initializer->get_datatype(), type, true, p_variable->initializer)) { push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", p_variable->initializer->get_datatype().to_string(), type.to_string()), p_variable->initializer); } else { // TODO: Add warning. @@ -1380,7 +1495,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable parser->push_warning(p_variable->initializer, GDScriptWarning::NARROWING_CONVERSION); #endif } - if (p_variable->initializer->get_datatype().is_variant()) { + if (p_variable->initializer->get_datatype().is_variant() && !type.is_variant()) { // TODO: Warn unsafe assign. mark_node_unsafe(p_variable->initializer); p_variable->use_conversion_assign = true; @@ -1410,10 +1525,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)); } @@ -1432,8 +1559,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 @@ -1535,8 +1660,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; @@ -1552,7 +1677,7 @@ void GDScriptAnalyzer::resolve_match_pattern(GDScriptParser::PatternNode *p_matc if (p_match_pattern->dictionary[i].key) { reduce_expression(p_match_pattern->dictionary[i].key); if (!p_match_pattern->dictionary[i].key->is_constant) { - push_error(R"(Expression in dictionary pattern key must be a constant.)", p_match_pattern->expression); + push_error(R"(Expression in dictionary pattern key must be a constant.)", p_match_pattern->dictionary[i].key); } } @@ -1834,19 +1959,20 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig push_error("Cannot assign a new value to a constant.", p_assignment->assignee); } - if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) { - bool compatible = true; - GDScriptParser::DataType op_type = assigned_value_type; - if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { - op_type = get_operation_type(p_assignment->variant_op, assignee_type, assigned_value_type, compatible, p_assignment->assigned_value); - } + bool compatible = true; + GDScriptParser::DataType op_type = assigned_value_type; + if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) { + op_type = get_operation_type(p_assignment->variant_op, assignee_type, assigned_value_type, compatible, p_assignment->assigned_value); + } + p_assignment->set_datatype(op_type); + if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) { if (compatible) { - compatible = is_type_compatible(assignee_type, op_type, true); + compatible = is_type_compatible(assignee_type, op_type, true, p_assignment->assigned_value); if (!compatible) { if (assignee_type.is_hard_type()) { // Try reverse test since it can be a masked subtype. - if (!is_type_compatible(op_type, assignee_type, true)) { + if (!is_type_compatible(op_type, assignee_type, true, p_assignment->assigned_value)) { push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value); } else { // TODO: Add warning. @@ -1865,7 +1991,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig if (assignee_type.has_no_type() || assigned_value_type.is_variant()) { mark_node_unsafe(p_assignment); - if (assignee_type.is_hard_type()) { + if (assignee_type.is_hard_type() && !assignee_type.is_variant()) { p_assignment->use_conversion_assign = true; } } @@ -1952,7 +2078,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 @@ -2069,7 +2196,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) { @@ -2154,6 +2281,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; @@ -2198,7 +2326,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 @@ -2256,6 +2384,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: @@ -2298,6 +2427,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: @@ -2321,6 +2451,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; @@ -2371,7 +2505,9 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a GDScriptParser::DataType return_type; List<GDScriptParser::DataType> par_types; - if (get_function_signature(p_call, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) { + bool is_constructor = (base_type.is_meta_type || (p_call->callee && p_call->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_call->function_name == SNAME("new"); + + if (get_function_signature(p_call, is_constructor, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) { // If the function require typed arrays we must make literals be typed. for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) { int index = E.key; @@ -2381,19 +2517,30 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a } validate_call_arg(par_types, default_arg_count, is_vararg, p_call); + if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type) { + // Enum type is treated as a dictionary value for function calls. + base_type.is_meta_type = false; + } + 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(); } 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) { @@ -2423,11 +2570,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); @@ -2439,17 +2588,24 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) { GDScriptParser::DataType cast_type = resolve_datatype(p_cast->cast_type); if (!cast_type.is_set()) { + mark_node_unsafe(p_cast); return; } - cast_type.is_meta_type = false; // The casted value won't be a type name. + cast_type = type_from_metatype(cast_type); // The casted value won't be a type name. p_cast->set_datatype(cast_type); if (!cast_type.is_variant()) { GDScriptParser::DataType op_type = p_cast->operand->get_datatype(); if (!op_type.is_variant()) { bool valid = false; - if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) { + if (op_type.kind == GDScriptParser::DataType::ENUM && cast_type.kind == GDScriptParser::DataType::ENUM) { + // Enum types are compatible between each other, so it's a safe cast. + valid = true; + } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && op_type.builtin_type == Variant::INT && cast_type.kind == GDScriptParser::DataType::ENUM) { + // Convertint int to enum is always valid. + valid = true; + } else if (op_type.kind == GDScriptParser::DataType::BUILTIN && cast_type.kind == GDScriptParser::DataType::BUILTIN) { valid = Variant::can_convert(op_type.builtin_type, cast_type.builtin_type); } else if (op_type.kind != GDScriptParser::DataType::BUILTIN && cast_type.kind != GDScriptParser::DataType::BUILTIN) { valid = is_type_compatible(cast_type, op_type) || is_type_compatible(op_type, cast_type); @@ -2505,31 +2661,37 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node) GDScriptParser::DataType result; result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; result.kind = GDScriptParser::DataType::NATIVE; - result.native_type = "Node"; + result.native_type = SNAME("Node"); result.builtin_type = Variant::OBJECT; 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)); - Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); + 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); - GDScriptParser::DataType type; type.type_source = GDScriptParser::DataType::UNDETECTED; type.kind = GDScriptParser::DataType::VARIANT; return type; } - GDScriptParser::DataType type; type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; type.kind = GDScriptParser::DataType::CLASS; type.builtin_type = Variant::OBJECT; @@ -2551,6 +2713,34 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod const StringName &name = p_identifier->name; + if (base.kind == GDScriptParser::DataType::ENUM) { + if (base.is_meta_type) { + if (base.enum_values.has(name)) { + p_identifier->is_constant = true; + p_identifier->reduced_value = base.enum_values[name]; + + GDScriptParser::DataType result; + result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; + result.kind = GDScriptParser::DataType::ENUM; + result.is_constant = true; + result.builtin_type = Variant::INT; + result.native_type = base.native_type; + result.enum_type = base.enum_type; + p_identifier->set_datatype(result); + return; + } else { + // Consider as a Dictionary, so it can be anything. + // This will be evaluated in the next if block. + base.kind = GDScriptParser::DataType::BUILTIN; + base.builtin_type = Variant::DICTIONARY; + base.is_meta_type = false; + } + } else { + push_error(R"(Cannot get property from enum value.)", p_identifier); + return; + } + } + if (base.kind == GDScriptParser::DataType::BUILTIN) { if (base.is_meta_type) { bool valid = true; @@ -2597,31 +2787,6 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod return; } - if (base.kind == GDScriptParser::DataType::ENUM) { - if (base.is_meta_type) { - if (base.enum_values.has(name)) { - p_identifier->is_constant = true; - p_identifier->reduced_value = base.enum_values[name]; - - GDScriptParser::DataType result; - result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - result.kind = GDScriptParser::DataType::ENUM_VALUE; - result.builtin_type = base.builtin_type; - result.native_type = base.native_type; - result.enum_type = name; - p_identifier->set_datatype(result); - } else { - // Consider as a Dictionary - GDScriptParser::DataType dummy; - dummy.kind = GDScriptParser::DataType::VARIANT; - p_identifier->set_datatype(dummy); - } - } else { - push_error(R"(Cannot get property from enum value.)", p_identifier); - } - return; - } - GDScriptParser::ClassNode *base_class = base.class_type; // TODO: Switch current class/function/suite here to avoid misrepresenting identifiers (in recursive reduce calls). @@ -2719,29 +2884,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; } } @@ -2757,7 +2927,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident if (element.identifier->name == p_identifier->name) { GDScriptParser::DataType type; type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; - type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM_VALUE : GDScriptParser::DataType::BUILTIN; + type.kind = element.parent_enum->identifier ? GDScriptParser::DataType::ENUM : GDScriptParser::DataType::BUILTIN; type.builtin_type = Variant::INT; type.is_constant = true; if (element.parent_enum->identifier) { @@ -2792,7 +2962,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: @@ -2823,18 +2997,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; } @@ -3014,6 +3207,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) { @@ -3024,6 +3218,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; @@ -3129,8 +3329,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; @@ -3193,6 +3396,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. @@ -3200,6 +3404,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; @@ -3230,6 +3435,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: @@ -3341,6 +3547,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; @@ -3361,6 +3574,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; @@ -3457,6 +3677,9 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_metatype(const GDScriptPars GDScriptParser::DataType result = p_meta_type; result.is_meta_type = false; result.is_constant = false; + if (p_meta_type.kind == GDScriptParser::DataType::ENUM) { + result.builtin_type = Variant::INT; + } return result; } @@ -3471,7 +3694,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo result.builtin_type = p_property.type; if (p_property.type == Variant::OBJECT) { result.kind = GDScriptParser::DataType::NATIVE; - result.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name; + result.native_type = p_property.class_name == StringName() ? SNAME("Object") : p_property.class_name; } else { result.kind = GDScriptParser::DataType::BUILTIN; result.builtin_type = p_property.type; @@ -3507,12 +3730,24 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo return result; } -bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) { +bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bool p_is_constructor, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) { r_static = false; r_vararg = false; r_default_arg_count = 0; StringName function_name = p_function; + if (p_base_type.kind == GDScriptParser::DataType::ENUM) { + if (p_base_type.is_meta_type) { + // Enum type can be treated as a dictionary value. + p_base_type.kind = GDScriptParser::DataType::BUILTIN; + p_base_type.builtin_type = Variant::DICTIONARY; + p_base_type.is_meta_type = false; + } else { + push_error("Cannot call function on enum value.", p_source); + return false; + } + } + if (p_base_type.kind == GDScriptParser::DataType::BUILTIN) { // Construct a base type to get methods. Callable::CallError err; @@ -3535,8 +3770,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source return false; } - bool is_constructor = (p_base_type.is_meta_type || (p_source->callee && p_source->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_function == StaticCString::create("new"); - if (is_constructor) { + if (p_is_constructor) { function_name = "_init"; r_static = true; } @@ -3557,7 +3791,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source } if (found_function != nullptr) { - r_static = is_constructor || found_function->is_static; + r_static = p_is_constructor || found_function->is_static; for (int i = 0; i < found_function->parameters.size(); i++) { r_par_types.push_back(found_function->parameters[i]->get_datatype()); if (found_function->parameters[i]->default_value != nullptr) { @@ -3583,7 +3817,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source } // If the base is a script, it might be trying to access members of the Script class itself. - if (p_base_type.is_meta_type && !is_constructor && (p_base_type.kind == GDScriptParser::DataType::SCRIPT || p_base_type.kind == GDScriptParser::DataType::CLASS)) { + if (p_base_type.is_meta_type && !p_is_constructor && (p_base_type.kind == GDScriptParser::DataType::SCRIPT || p_base_type.kind == GDScriptParser::DataType::CLASS)) { MethodInfo info; StringName script_class = p_base_type.kind == GDScriptParser::DataType::SCRIPT ? p_base_type.script_type->get_class_name() : StringName(GDScript::get_class_static()); @@ -3603,7 +3837,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source } #endif - if (is_constructor) { + if (p_is_constructor) { // Native types always have a default constructor. r_return_type = p_base_type; r_return_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -3627,6 +3861,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)); @@ -3753,6 +3988,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator // Unary version. GDScriptParser::DataType nil_type; nil_type.builtin_type = Variant::NIL; + nil_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; return get_operation_type(p_operation, p_a, nil_type, r_valid, p_source); } @@ -3763,24 +3999,51 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator Variant::Type a_type = p_a.builtin_type; Variant::Type b_type = p_b.builtin_type; + if (p_a.kind == GDScriptParser::DataType::ENUM) { + if (p_a.is_meta_type) { + a_type = Variant::DICTIONARY; + } else { + a_type = Variant::INT; + } + } + if (p_b.kind == GDScriptParser::DataType::ENUM) { + if (p_b.is_meta_type) { + b_type = Variant::DICTIONARY; + } else { + b_type = Variant::INT; + } + } + Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type); - if (op_eval == nullptr) { + bool hard_operation = p_a.is_hard_type() && p_b.is_hard_type(); + bool validated = op_eval != nullptr; + + if (hard_operation && !validated) { r_valid = false; return result; + } else if (hard_operation && validated) { + r_valid = true; + result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type); + } else if (!hard_operation && !validated) { + r_valid = true; + result.type_source = GDScriptParser::DataType::UNDETECTED; + result.kind = GDScriptParser::DataType::VARIANT; + result.builtin_type = Variant::NIL; + } else if (!hard_operation && validated) { + r_valid = true; + result.type_source = GDScriptParser::DataType::INFERRED; + result.kind = GDScriptParser::DataType::BUILTIN; + result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type); } - r_valid = true; - result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; - - result.kind = GDScriptParser::DataType::BUILTIN; - result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type); - return result; } // TODO: Add safe/unsafe return variable (for variant cases) -bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion) const { +bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion, const GDScriptParser::Node *p_source_node) { // These return "true" so it doesn't affect users negatively. ERR_FAIL_COND_V_MSG(!p_target.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset target type"); ERR_FAIL_COND_V_MSG(!p_source.is_set(), true, "Parser bug (please report): Trying to check compatibility of unset value type"); @@ -3800,7 +4063,7 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ if (!valid && p_allow_implicit_conversion) { valid = Variant::can_convert_strict(p_source.builtin_type, p_target.builtin_type); } - if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM_VALUE) { + if (!valid && p_target.builtin_type == Variant::INT && p_source.kind == GDScriptParser::DataType::ENUM && !p_source.is_meta_type) { // Enum value is also integer. valid = true; } @@ -3821,6 +4084,11 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ if (p_target.kind == GDScriptParser::DataType::ENUM) { if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) { +#ifdef DEBUG_ENABLED + if (p_source_node) { + parser->push_warning(p_source_node, GDScriptWarning::INT_ASSIGNED_TO_ENUM); + } +#endif return true; } if (p_source.kind == GDScriptParser::DataType::ENUM) { @@ -3828,11 +4096,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ return true; } } - if (p_source.kind == GDScriptParser::DataType::ENUM_VALUE) { - if (p_source.native_type == p_target.native_type && p_target.enum_values.has(p_source.enum_type)) { - return true; - } - } return false; } @@ -3887,7 +4150,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ case GDScriptParser::DataType::VARIANT: case GDScriptParser::DataType::BUILTIN: case GDScriptParser::DataType::ENUM: - case GDScriptParser::DataType::ENUM_VALUE: case GDScriptParser::DataType::UNRESOLVED: break; // Already solved before. } @@ -3924,7 +4186,6 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ case GDScriptParser::DataType::VARIANT: case GDScriptParser::DataType::BUILTIN: case GDScriptParser::DataType::ENUM: - case GDScriptParser::DataType::ENUM_VALUE: case GDScriptParser::DataType::UNRESOLVED: break; // Already solved before. } @@ -3945,6 +4206,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); } @@ -3982,13 +4249,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; } |