diff options
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 140 |
1 files changed, 115 insertions, 25 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 1b488d560c..d7f6126207 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -802,6 +802,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) { + resolve_annotation(E); E->apply(parser, member.variable); } } break; @@ -812,6 +813,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) { + resolve_annotation(E); E->apply(parser, member.constant); } } break; @@ -835,6 +837,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) { + resolve_annotation(E); E->apply(parser, member.signal); } } break; @@ -882,6 +885,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) { + resolve_annotation(E); E->apply(parser, member.m_enum); } } break; @@ -1064,6 +1068,7 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class, co if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) { // Apply annotations. for (GDScriptParser::AnnotationNode *&E : member.function->annotations) { + resolve_annotation(E); E->apply(parser, member.function); } @@ -1290,7 +1295,55 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node, bool p_is_root } void GDScriptAnalyzer::resolve_annotation(GDScriptParser::AnnotationNode *p_annotation) { - // TODO: Add second validation function for annotations, so they can use checked types. + ERR_FAIL_COND_MSG(!parser->valid_annotations.has(p_annotation->name), vformat(R"(Annotation "%s" not found to validate.)", p_annotation->name)); + + const MethodInfo &annotation_info = parser->valid_annotations[p_annotation->name].info; + + const List<PropertyInfo>::Element *E = annotation_info.arguments.front(); + for (int i = 0; i < p_annotation->arguments.size(); i++) { + GDScriptParser::ExpressionNode *argument = p_annotation->arguments[i]; + const PropertyInfo &argument_info = E->get(); + + if (E->next() != nullptr) { + E = E->next(); + } + + reduce_expression(argument); + + if (!argument->is_constant) { + push_error(vformat(R"(Argument %d of annotation "%s" isn't a constant expression.)", i + 1, p_annotation->name), argument); + return; + } + + Variant value = argument->reduced_value; + + if (value.get_type() != argument_info.type) { +#ifdef DEBUG_ENABLED + if (argument_info.type == Variant::INT && value.get_type() == Variant::FLOAT) { + parser->push_warning(argument, GDScriptWarning::NARROWING_CONVERSION); + } +#endif + + if (!Variant::can_convert_strict(value.get_type(), argument_info.type)) { + push_error(vformat(R"(Invalid argument for annotation "%s": argument %d should be "%s" but is "%s".)", p_annotation->name, i + 1, Variant::get_type_name(argument_info.type), argument->get_datatype().to_string()), argument); + return; + } + + Variant converted_to; + const Variant *converted_from = &value; + Callable::CallError call_error; + Variant::construct(argument_info.type, converted_to, &converted_from, 1, call_error); + + if (call_error.error != Callable::CallError::CALL_OK) { + push_error(vformat(R"(Cannot convert argument %d of annotation "%s" from "%s" to "%s".)", i + 1, p_annotation->name, Variant::get_type_name(value.get_type()), Variant::get_type_name(argument_info.type)), argument); + return; + } + + value = converted_to; + } + + p_annotation->resolved_arguments.push_back(value); + } } void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source, bool p_is_lambda) { @@ -1486,8 +1539,10 @@ 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); + // Apply annotations. + for (GDScriptParser::AnnotationNode *&E : stmt->annotations) { + resolve_annotation(E); + E->apply(parser, stmt); } #ifdef DEBUG_ENABLED @@ -1544,10 +1599,8 @@ void GDScriptAnalyzer::resolve_assignable(GDScriptParser::AssignableNode *p_assi GDScriptParser::DataType initializer_type = p_assignable->initializer->get_datatype(); if (p_assignable->infer_datatype) { - if (!initializer_type.is_set() || initializer_type.has_no_type()) { + if (!initializer_type.is_set() || initializer_type.has_no_type() || !initializer_type.is_hard_type()) { push_error(vformat(R"(Cannot infer the type of "%s" %s because the value doesn't have a set type.)", p_assignable->identifier->name, p_kind), p_assignable->initializer); - } else if (initializer_type.is_variant() && !initializer_type.is_hard_type()) { - push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is Variant. Use explicit "Variant" type if this is intended.)", p_assignable->identifier->name, p_kind), p_assignable->initializer); } else if (initializer_type.kind == GDScriptParser::DataType::BUILTIN && initializer_type.builtin_type == Variant::NIL && !is_constant) { push_error(vformat(R"(Cannot infer the type of "%s" %s because the value is "null".)", p_assignable->identifier->name, p_kind), p_assignable->initializer); } @@ -1722,21 +1775,52 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) { 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); + variable_type.builtin_type = Variant::INT; } else if (p_for->list) { resolve_node(p_for->list, false); - 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; + GDScriptParser::DataType list_type = p_for->list->get_datatype(); + if (!list_type.is_hard_type()) { + mark_node_unsafe(p_for->list); + } + if (list_type.is_variant()) { + variable_type.kind = GDScriptParser::DataType::VARIANT; + mark_node_unsafe(p_for->list); + } else if (list_type.has_container_element_type()) { + variable_type = list_type.get_container_element_type(); + variable_type.type_source = list_type.type_source; + } else if (list_type.is_typed_container_type()) { + variable_type = list_type.get_typed_container_type(); + variable_type.type_source = list_type.type_source; + } else if (list_type.builtin_type == Variant::INT || list_type.builtin_type == Variant::FLOAT || list_type.builtin_type == Variant::STRING) { + variable_type.type_source = list_type.type_source; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = list_type.builtin_type; + } else if (list_type.builtin_type == Variant::VECTOR2I || list_type.builtin_type == Variant::VECTOR3I) { + variable_type.type_source = list_type.type_source; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = Variant::INT; + } else if (list_type.builtin_type == Variant::VECTOR2 || list_type.builtin_type == Variant::VECTOR3) { + variable_type.type_source = list_type.type_source; + variable_type.kind = GDScriptParser::DataType::BUILTIN; + variable_type.builtin_type = Variant::FLOAT; + } else if (list_type.builtin_type == Variant::OBJECT) { + GDScriptParser::DataType return_type; + List<GDScriptParser::DataType> par_types; + int default_arg_count = 0; + bool is_static = false; + bool is_vararg = false; + if (get_function_signature(p_for->list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, is_static, is_vararg)) { + variable_type = return_type; + variable_type.type_source = list_type.type_source; + } else if (!list_type.is_hard_type()) { + variable_type.kind = GDScriptParser::DataType::VARIANT; + } else { + push_error(vformat(R"(Unable to iterate on object of type "%s".)", list_type.to_string()), p_for->list); + } + } else if (list_type.builtin_type == Variant::ARRAY || list_type.builtin_type == Variant::DICTIONARY || !list_type.is_hard_type()) { variable_type.kind = GDScriptParser::DataType::VARIANT; + } else { + push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list); } } if (p_for->variable) { @@ -1983,7 +2067,7 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre reduce_subscript(static_cast<GDScriptParser::SubscriptNode *>(p_expression)); break; case GDScriptParser::Node::TERNARY_OPERATOR: - reduce_ternary_op(static_cast<GDScriptParser::TernaryOpNode *>(p_expression)); + reduce_ternary_op(static_cast<GDScriptParser::TernaryOpNode *>(p_expression), p_is_root); break; case GDScriptParser::Node::UNARY_OPERATOR: reduce_unary_op(static_cast<GDScriptParser::UnaryOpNode *>(p_expression)); @@ -2910,8 +2994,8 @@ void GDScriptAnalyzer::reduce_identifier_from_base_set_class(GDScriptParser::Ide p_identifier->set_datatype(p_identifier_datatype); Error err = OK; - GDScript *scr = GDScriptCache::get_full_script(p_identifier_datatype.script_path, err).ptr(); - ERR_FAIL_COND_MSG(err != OK, "Error while getting full script."); + GDScript *scr = GDScriptCache::get_shallow_script(p_identifier_datatype.script_path, err).ptr(); + ERR_FAIL_COND_MSG(err != OK, vformat(R"(Error while getting cache for script "%s".)", p_identifier_datatype.script_path)); scr = scr->find_class(p_identifier_datatype.class_type->fqcn); p_identifier->reduced_value = scr; p_identifier->is_constant = true; @@ -3140,7 +3224,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident for (int i = 0; i < current_enum->values.size(); i++) { const GDScriptParser::EnumNode::Value &element = current_enum->values[i]; if (element.identifier->name == p_identifier->name) { - StringName enum_name = current_enum->identifier->name ? current_enum->identifier->name : UNNAMED_ENUM; + StringName enum_name = current_enum->identifier ? current_enum->identifier->name : UNNAMED_ENUM; GDScriptParser::DataType type = make_enum_type(enum_name, parser->current_class->fqcn, false); if (element.parent_enum->identifier) { type.enum_type = element.parent_enum->identifier->name; @@ -3503,6 +3587,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } #endif result_type.kind = GDScriptParser::DataType::VARIANT; + mark_node_unsafe(p_subscript); } } if (!valid) { @@ -3704,10 +3789,10 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri p_subscript->set_datatype(result_type); } -void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op) { +void GDScriptAnalyzer::reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op, bool p_is_root) { reduce_expression(p_ternary_op->condition); - reduce_expression(p_ternary_op->true_expr); - reduce_expression(p_ternary_op->false_expr); + reduce_expression(p_ternary_op->true_expr, p_is_root); + reduce_expression(p_ternary_op->false_expr, p_is_root); GDScriptParser::DataType result; @@ -4521,6 +4606,11 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) { } Error GDScriptAnalyzer::resolve_inheritance() { + // Apply annotations. + for (GDScriptParser::AnnotationNode *&E : parser->head->annotations) { + resolve_annotation(E); + E->apply(parser, parser->head); + } return resolve_class_inheritance(parser->head, true); } |