diff options
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 140 |
1 files changed, 114 insertions, 26 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index f2b601dc2c..c07849bfa8 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -171,7 +171,12 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me } if (class_exists(p_member_name)) { - push_error(vformat(R"(The class "%s" shadows a native class.)", p_member_name), p_member_node); + push_error(vformat(R"(The member "%s" shadows a native class.)", p_member_name), p_member_node); + return ERR_PARSE_ERROR; + } + + if (GDScriptParser::get_builtin_type(p_member_name) != Variant::VARIANT_MAX) { + push_error(vformat(R"(The member "%s" cannot have the same name as a builtin type.)", p_member_name), p_member_node); return ERR_PARSE_ERROR; } @@ -218,6 +223,17 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, p_class->fqcn = p_class->outer->fqcn + "::" + String(p_class->identifier->name); } + if (p_class->identifier) { + StringName class_name = p_class->identifier->name; + if (class_exists(class_name)) { + push_error(vformat(R"(Class "%s" hides a native class.)", class_name), p_class->identifier); + } else if (ScriptServer::is_global_class(class_name) && (ScriptServer::get_global_class_path(class_name) != parser->script_path || p_class != parser->head)) { + push_error(vformat(R"(Class "%s" hides a global script class.)", class_name), p_class->identifier); + } else if (ProjectSettings::get_singleton()->has_autoload(class_name) && ProjectSettings::get_singleton()->get_autoload(class_name).is_singleton) { + push_error(vformat(R"(Class "%s" hides an autoload singleton.)", class_name), p_class->identifier); + } + } + GDScriptParser::DataType result; // Set datatype for class. @@ -242,6 +258,9 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, int extends_index = 0; 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(); + } 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); @@ -681,8 +700,9 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas specified_type.is_meta_type = false; } - GDScriptParser::DataType datatype = member.constant->get_datatype(); + GDScriptParser::DataType datatype; if (member.constant->initializer) { + datatype = member.constant->initializer->get_datatype(); if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) { GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer); const_fold_array(array); @@ -1015,7 +1035,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * return_type.is_meta_type = false; p_function->set_datatype(return_type); if (p_function->return_type) { - push_error("Constructor cannot have an explicit return type.", p_function->return_type); + GDScriptParser::DataType declared_return = resolve_datatype(p_function->return_type); + if (declared_return.kind != GDScriptParser::DataType::BUILTIN || declared_return.builtin_type != Variant::NIL) { + push_error("Constructor cannot have an explicit return type.", p_function->return_type); + } } } else { GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type); @@ -1172,12 +1195,30 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { } } - if (!list_resolved) { + GDScriptParser::DataType variable_type; + if (list_resolved) { + variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + 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); + 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; + } else if (p_for->list->datatype.is_typed_container_type()) { + variable_type = p_for->list->datatype.get_typed_container_type(); + variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED; + } else { + // Last resort + // TODO: Must other cases be handled? Must we mark as unsafe? + variable_type.type_source = GDScriptParser::DataType::UNDETECTED; + variable_type.kind = GDScriptParser::DataType::VARIANT; + } + } + if (p_for->variable) { + p_for->variable->set_datatype(variable_type); } - - // TODO: If list is a typed array, the variable should be an element. - // Also applicable for constant range() (so variable is int or float). resolve_suite(p_for->loop); p_for->set_datatype(p_for->loop->get_datatype()); @@ -1465,8 +1506,7 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame } if (p_parameter->datatype_specifier != nullptr) { - resolve_datatype(p_parameter->datatype_specifier); - result = p_parameter->datatype_specifier->get_datatype(); + result = resolve_datatype(p_parameter->datatype_specifier); result.is_meta_type = false; if (p_parameter->default_value != nullptr) { @@ -1478,6 +1518,10 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame } } + if (result.builtin_type == Variant::Type::NIL && result.type_source == GDScriptParser::DataType::ANNOTATED_INFERRED && p_parameter->datatype_specifier == nullptr) { + push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_parameter->identifier->name), p_parameter->default_value); + } + p_parameter->set_datatype(result); } @@ -1720,7 +1764,6 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig } else { // TODO: Warning in this case. mark_node_unsafe(p_assignment); - p_assignment->use_conversion_assign = true; } } } else { @@ -1748,6 +1791,15 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig identifier->variable_source->set_datatype(id_type); } } break; + case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: { + GDScriptParser::DataType id_type = identifier->parameter_source->get_datatype(); + if (!id_type.is_hard_type()) { + id_type = assigned_value_type; + id_type.type_source = GDScriptParser::DataType::INFERRED; + id_type.is_constant = false; + identifier->parameter_source->set_datatype(id_type); + } + } break; case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: { GDScriptParser::DataType id_type = identifier->variable_source->get_datatype(); if (!id_type.is_hard_type()) { @@ -2223,10 +2275,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa if (get_function_signature(p_call, 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 (Map<int, GDScriptParser::ArrayNode *>::Element *E = arrays.front(); E; E = E->next()) { - int index = E->key(); + for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) { + int index = E.key; if (index < par_types.size() && par_types[index].has_container_element_type()) { - update_array_literal_element_type(par_types[index], E->get()); + update_array_literal_element_type(par_types[index], E.value); } } validate_call_arg(par_types, default_arg_count, is_vararg, p_call); @@ -2409,13 +2461,15 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod p_identifier->is_constant = true; p_identifier->reduced_value = result; p_identifier->set_datatype(type_from_variant(result, p_identifier)); - } else { + } else if (base.is_hard_type()) { push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier); } } else { switch (base.builtin_type) { case Variant::NIL: { - push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier); + if (base.is_hard_type()) { + push_error(vformat(R"(Invalid get index "%s" on base Nil)", name), p_identifier); + } return; } case Variant::DICTIONARY: { @@ -2436,7 +2490,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod return; } } - push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); + if (base.is_hard_type()) { + push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier); + } } } } @@ -2457,7 +2513,10 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod result.enum_type = name; p_identifier->set_datatype(result); } else { - push_error(vformat(R"(Cannot find value "%s" in "%s".)", name, base.to_string()), p_identifier); + // 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); @@ -2516,14 +2575,29 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod while (outer != nullptr) { if (outer->has_member(name)) { const GDScriptParser::ClassNode::Member &member = outer->get_member(name); - if (member.type == GDScriptParser::ClassNode::Member::CONSTANT) { - // TODO: Make sure loops won't cause problem. And make special error message for those. - // For out-of-order resolution: - reduce_expression(member.constant->initializer); - p_identifier->set_datatype(member.get_datatype()); - p_identifier->is_constant = true; - p_identifier->reduced_value = member.constant->initializer->reduced_value; - return; + switch (member.type) { + case GDScriptParser::ClassNode::Member::CONSTANT: { + // TODO: Make sure loops won't cause problem. And make special error message for those. + // For out-of-order resolution: + reduce_expression(member.constant->initializer); + p_identifier->set_datatype(member.get_datatype()); + p_identifier->is_constant = true; + p_identifier->reduced_value = member.constant->initializer->reduced_value; + return; + } break; + case GDScriptParser::ClassNode::Member::ENUM_VALUE: { + p_identifier->set_datatype(member.get_datatype()); + p_identifier->is_constant = true; + p_identifier->reduced_value = member.enum_value.value; + return; + } break; + case GDScriptParser::ClassNode::Member::ENUM: { + p_identifier->set_datatype(member.get_datatype()); + p_identifier->is_constant = false; + return; + } break; + default: + break; } } outer = outer->outer; @@ -2837,6 +2911,9 @@ void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) { } void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscript) { + if (p_subscript->base == nullptr) { + return; + } if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER) { reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base), true); } else { @@ -3228,7 +3305,13 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va current = current->_owner; } - Ref<GDScriptParserRef> ref = get_parser_for(current->path); + Ref<GDScriptParserRef> ref = get_parser_for(current->get_path()); + if (ref.is_null()) { + push_error("Could not find script in path.", p_source); + GDScriptParser::DataType error_type; + error_type.kind = GDScriptParser::DataType::VARIANT; + return error_type; + } ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED); GDScriptParser::ClassNode *found = ref->get_parser()->head; @@ -3612,6 +3695,11 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) { return true; } + if (p_source.kind == GDScriptParser::DataType::ENUM) { + if (p_source.native_type == p_target.native_type) { + 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; |