diff options
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 154 |
1 files changed, 92 insertions, 62 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 95e577c140..2892ae3f4e 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -196,8 +196,11 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) { GDScriptParser::ClassNode *current_class_node = current_data_type->class_type; if (has_member_name_conflict_in_script_class(p_member_name, current_class_node, p_member_node)) { - push_error(vformat(R"(The member "%s" already exists in parent class %s.)", p_member_name, current_class_node->identifier->name), - p_member_node); + String parent_class_name = current_class_node->fqcn; + if (current_class_node->identifier != nullptr) { + parent_class_name = current_class_node->identifier->name; + } + push_error(vformat(R"(The member "%s" already exists in parent class %s.)", p_member_name, parent_class_name), p_member_node); return ERR_PARSE_ERROR; } current_data_type = ¤t_class_node->base_type; @@ -216,6 +219,22 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C return OK; } +void GDScriptAnalyzer::get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list) { + if (p_list->find(p_node) != nullptr) { + return; + } + p_list->push_back(p_node); + + // Prioritize node base type over its outer class + if (p_node->base_type.class_type != nullptr) { + get_class_node_current_scope_classes(p_node->base_type.class_type, p_list); + } + + if (p_node->outer != nullptr) { + get_class_node_current_scope_classes(p_node->outer, p_list); + } +} + Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) { if (p_class->base_type.is_set()) { // Already resolved @@ -324,9 +343,10 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, base.native_type = name; } else { // Look for other classes in script. - GDScriptParser::ClassNode *look_class = p_class; bool found = false; - while (look_class != nullptr) { + List<GDScriptParser::ClassNode *> script_classes; + get_class_node_current_scope_classes(p_class, &script_classes); + for (GDScriptParser::ClassNode *look_class : script_classes) { if (look_class->identifier && look_class->identifier->name == name) { if (!look_class->get_datatype().is_set()) { Error err = resolve_inheritance(look_class, false); @@ -350,7 +370,6 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, found = true; break; } - look_class = look_class->outer; } if (!found) { @@ -514,12 +533,11 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type result = make_native_enum_type(parser->current_class->base_type.native_type, first); } else { // Classes in current scope. - GDScriptParser::ClassNode *script_class = parser->current_class; - bool found = false; - while (!found && script_class != nullptr) { + List<GDScriptParser::ClassNode *> script_classes; + get_class_node_current_scope_classes(parser->current_class, &script_classes); + for (GDScriptParser::ClassNode *script_class : script_classes) { if (script_class->identifier && script_class->identifier->name == first) { result = script_class->get_datatype(); - found = true; break; } if (script_class->members_indices.has(first)) { @@ -527,24 +545,21 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type switch (member.type) { case GDScriptParser::ClassNode::Member::CLASS: result = member.m_class->get_datatype(); - found = true; break; case GDScriptParser::ClassNode::Member::ENUM: result = member.m_enum->get_datatype(); - found = true; break; case GDScriptParser::ClassNode::Member::CONSTANT: if (member.constant->get_datatype().is_meta_type) { result = member.constant->get_datatype(); result.is_meta_type = false; - found = true; break; } else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) { Ref<GDScript> gdscript = member.constant->initializer->reduced_value; if (gdscript.is_valid()) { - Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_path()); + Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path()); if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) { - push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_path()), p_type); + push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), p_type); return GDScriptParser::DataType(); } result = ref->get_parser()->head->get_datatype(); @@ -566,7 +581,6 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type return GDScriptParser::DataType(); } } - script_class = script_class->outer; } } if (!result.is_set()) { @@ -1162,6 +1176,8 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * if (p_function->parameters[i]->default_value->is_constant) { p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value); + } else { + p_function->default_arg_values.push_back(Variant()); // Prevent shift. } } #endif // TOOLS_ENABLED @@ -1214,11 +1230,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * 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() + "("; + String parent_signature = p_function->identifier->name.operator String() + "("; int j = 0; for (const GDScriptParser::DataType &par_type : parameters_types) { if (j > 0) { @@ -1235,7 +1247,15 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode * j++; } - parent_signature += ")"; + parent_signature += ") -> "; + + const String return_type = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant"; + if (return_type == "null") { + parent_signature += "void"; + } else { + parent_signature += return_type; + } + push_error(vformat(R"(The function signature doesn't match the parent. Parent signature is "%s".)", parent_signature), p_function); } } @@ -2667,7 +2687,7 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) { } void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary) { - HashMap<Variant, GDScriptParser::ExpressionNode *, VariantHasher, VariantComparator> elements; + HashMap<Variant, GDScriptParser::ExpressionNode *, VariantHasher, StringLikeVariantComparator> elements; for (int i = 0; i < p_dictionary->elements.size(); i++) { const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; @@ -2882,41 +2902,43 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod } // Check outer constants. // TODO: Allow outer static functions. - GDScriptParser::ClassNode *outer = base_class->outer; - while (outer != nullptr) { - if (outer->has_member(name)) { - const GDScriptParser::ClassNode::Member &member = outer->get_member(name); - 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; - case GDScriptParser::ClassNode::Member::CLASS: { - resolve_class_interface(member.m_class); - p_identifier->set_datatype(member.m_class->get_datatype()); - return; - } break; - default: - break; + if (base_class->outer != nullptr) { + List<GDScriptParser::ClassNode *> script_classes; + get_class_node_current_scope_classes(parser->current_class, &script_classes); + for (GDScriptParser::ClassNode *script_class : script_classes) { + if (script_class->has_member(name)) { + const GDScriptParser::ClassNode::Member &member = script_class->get_member(name); + 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; + case GDScriptParser::ClassNode::Member::CLASS: { + resolve_class_interface(member.m_class); + p_identifier->set_datatype(member.m_class->get_datatype()); + return; + } break; + default: + break; + } } } - outer = outer->outer; } base_class = base_class->base_type.class_type; @@ -3127,9 +3149,9 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name]; Node *node = Object::cast_to<Node>(constant); if (node != nullptr) { - Ref<Script> scr = node->get_script(); + Ref<GDScript> scr = node->get_script(); if (scr.is_valid()) { - Ref<GDScriptParserRef> singl_parser = get_parser_for(scr->get_path()); + Ref<GDScriptParserRef> singl_parser = get_parser_for(scr->get_script_path()); if (singl_parser.is_valid()) { Error err = singl_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED); if (err == OK) { @@ -3332,7 +3354,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri Ref<GDScript> gdscr = Ref<GDScript>(p_subscript->base->reduced_value); if (!valid && gdscr.is_valid()) { Error err = OK; - GDScriptCache::get_full_script(gdscr->get_path(), err); + GDScriptCache::get_full_script(gdscr->get_script_path(), err); if (err == OK) { value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid); } @@ -3423,7 +3445,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri case Variant::QUATERNION: case Variant::AABB: case Variant::OBJECT: - error = index_type.builtin_type != Variant::STRING; + error = index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME; break; // Expect String or number. case Variant::BASIS: @@ -3437,11 +3459,11 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri 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; + index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME; break; // Expect String or int. case Variant::COLOR: - error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING; + error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME; break; // Don't support indexing, but we will check it later. case Variant::RID: @@ -3705,7 +3727,13 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va result.builtin_type = p_value.get_type(); result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; // Constant has explicit type. - if (p_value.get_type() == Variant::OBJECT) { + if (p_value.get_type() == Variant::NIL) { + // A null value is a variant, not void. + result.kind = GDScriptParser::DataType::VARIANT; + } else if (p_value.get_type() == Variant::OBJECT) { + // Object is treated as a native type, not a builtin type. + result.kind = GDScriptParser::DataType::NATIVE; + Object *obj = p_value; if (!obj) { return GDScriptParser::DataType(); @@ -4155,6 +4183,8 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ if (p_target.kind == GDScriptParser::DataType::BUILTIN) { bool valid = p_source.kind == GDScriptParser::DataType::BUILTIN && p_target.builtin_type == p_source.builtin_type; + valid |= p_source.builtin_type == Variant::STRING && p_target.builtin_type == Variant::STRING_NAME; + valid |= p_source.builtin_type == Variant::STRING_NAME && p_target.builtin_type == Variant::STRING; if (!valid && p_allow_implicit_conversion) { valid = Variant::can_convert_strict(p_source.builtin_type, p_target.builtin_type); } |