|
@@ -213,6 +213,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
|
|
|
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
|
|
|
return err;
|
|
|
}
|
|
|
+ base = parser->get_parser()->head->get_datatype();
|
|
|
}
|
|
|
} else if (ProjectSettings::get_singleton()->has_autoload(name) && ProjectSettings::get_singleton()->get_autoload(name).is_singleton) {
|
|
|
const ProjectSettings::AutoloadInfo &info = ProjectSettings::get_singleton()->get_autoload(name);
|
|
@@ -1266,6 +1267,7 @@ void GDScriptAnalyzer::resolve_pararameter(GDScriptParser::ParameterNode *p_para
|
|
|
reduce_expression(p_parameter->default_value);
|
|
|
result = p_parameter->default_value->get_datatype();
|
|
|
result.type_source = GDScriptParser::DataType::INFERRED;
|
|
|
+ result.is_constant = false;
|
|
|
}
|
|
|
|
|
|
if (p_parameter->datatype_specifier != nullptr) {
|
|
@@ -1455,7 +1457,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
|
|
|
bool compatible = true;
|
|
|
GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype();
|
|
|
if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
|
|
|
- op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible);
|
|
|
+ op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible, p_assignment->assigned_value);
|
|
|
}
|
|
|
|
|
|
if (compatible) {
|
|
@@ -1618,7 +1620,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
|
|
|
ERR_PRINT("Parser bug: unknown binary operation.");
|
|
|
}
|
|
|
}
|
|
|
- p_binary_op->set_datatype(type_from_variant(p_binary_op->reduced_value));
|
|
|
+ p_binary_op->set_datatype(type_from_variant(p_binary_op->reduced_value, p_binary_op));
|
|
|
|
|
|
return;
|
|
|
}
|
|
@@ -1632,7 +1634,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
|
|
|
} else {
|
|
|
if (p_binary_op->variant_op < Variant::OP_MAX) {
|
|
|
bool valid = false;
|
|
|
- result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid);
|
|
|
+ result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid, p_binary_op);
|
|
|
|
|
|
if (!valid) {
|
|
|
push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", p_binary_op->left_operand->get_datatype().to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
|
|
@@ -2061,7 +2063,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|
|
if (valid) {
|
|
|
p_identifier->is_constant = true;
|
|
|
p_identifier->reduced_value = result;
|
|
|
- p_identifier->set_datatype(type_from_variant(result));
|
|
|
+ p_identifier->set_datatype(type_from_variant(result, p_identifier));
|
|
|
} else {
|
|
|
push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier);
|
|
|
}
|
|
@@ -2192,7 +2194,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
|
|
if (valid) {
|
|
|
p_identifier->is_constant = true;
|
|
|
p_identifier->reduced_value = int_constant;
|
|
|
- p_identifier->set_datatype(type_from_variant(int_constant));
|
|
|
+ p_identifier->set_datatype(type_from_variant(int_constant, p_identifier));
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
@@ -2289,10 +2291,34 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // Try singletons.
|
|
|
+ // Do this before globals because this might be a singleton loading another one before it's compiled.
|
|
|
+ if (ProjectSettings::get_singleton()->has_autoload(name)) {
|
|
|
+ const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(name);
|
|
|
+ if (autoload.is_singleton) {
|
|
|
+ // Singleton exists, so it's at least a Node.
|
|
|
+ GDScriptParser::DataType result;
|
|
|
+ result.kind = GDScriptParser::DataType::NATIVE;
|
|
|
+ result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
|
|
|
+ if (autoload.path.to_lower().ends_with(GDScriptLanguage::get_singleton()->get_extension())) {
|
|
|
+ Ref<GDScriptParserRef> parser = get_parser_for(autoload.path);
|
|
|
+ if (parser.is_valid()) {
|
|
|
+ Error err = parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
|
|
|
+ if (err == OK) {
|
|
|
+ result = type_from_metatype(parser->get_parser()->head->get_datatype());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result.is_constant = true;
|
|
|
+ p_identifier->set_datatype(result);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (GDScriptLanguage::get_singleton()->get_global_map().has(name)) {
|
|
|
int idx = GDScriptLanguage::get_singleton()->get_global_map()[name];
|
|
|
Variant constant = GDScriptLanguage::get_singleton()->get_global_array()[idx];
|
|
|
- p_identifier->set_datatype(type_from_variant(constant));
|
|
|
+ p_identifier->set_datatype(type_from_variant(constant, p_identifier));
|
|
|
p_identifier->is_constant = true;
|
|
|
p_identifier->reduced_value = constant;
|
|
|
return;
|
|
@@ -2300,7 +2326,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
|
|
|
|
|
|
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(name)) {
|
|
|
Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name];
|
|
|
- p_identifier->set_datatype(type_from_variant(constant));
|
|
|
+ p_identifier->set_datatype(type_from_variant(constant, p_identifier));
|
|
|
p_identifier->is_constant = true;
|
|
|
p_identifier->reduced_value = constant;
|
|
|
return;
|
|
@@ -2322,7 +2348,7 @@ void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
|
|
|
p_literal->reduced_value = p_literal->value;
|
|
|
p_literal->is_constant = true;
|
|
|
|
|
|
- p_literal->set_datatype(type_from_variant(p_literal->reduced_value));
|
|
|
+ p_literal->set_datatype(type_from_variant(p_literal->reduced_value, p_literal));
|
|
|
}
|
|
|
|
|
|
void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
|
|
@@ -2359,7 +2385,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) {
|
|
|
|
|
|
p_preload->is_constant = true;
|
|
|
p_preload->reduced_value = p_preload->resource;
|
|
|
- p_preload->set_datatype(type_from_variant(p_preload->reduced_value));
|
|
|
+ p_preload->set_datatype(type_from_variant(p_preload->reduced_value, p_preload));
|
|
|
}
|
|
|
|
|
|
void GDScriptAnalyzer::reduce_self(GDScriptParser::SelfNode *p_self) {
|
|
@@ -2408,7 +2434,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
|
|
|
} else {
|
|
|
p_subscript->is_constant = true;
|
|
|
p_subscript->reduced_value = value;
|
|
|
- result_type = type_from_variant(value);
|
|
|
+ result_type = type_from_variant(value, p_subscript);
|
|
|
}
|
|
|
result_type.kind = GDScriptParser::DataType::VARIANT;
|
|
|
} else {
|
|
@@ -2448,7 +2474,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
|
|
|
} else {
|
|
|
p_subscript->is_constant = true;
|
|
|
p_subscript->reduced_value = value;
|
|
|
- result_type = type_from_variant(value);
|
|
|
+ result_type = type_from_variant(value, p_subscript);
|
|
|
}
|
|
|
result_type.kind = GDScriptParser::DataType::VARIANT;
|
|
|
} else {
|
|
@@ -2672,13 +2698,13 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
|
|
|
if (p_unary_op->operand->is_constant) {
|
|
|
p_unary_op->is_constant = true;
|
|
|
p_unary_op->reduced_value = Variant::evaluate(p_unary_op->variant_op, p_unary_op->operand->reduced_value, Variant());
|
|
|
- result = type_from_variant(p_unary_op->reduced_value);
|
|
|
+ result = type_from_variant(p_unary_op->reduced_value, p_unary_op);
|
|
|
} else if (p_unary_op->operand->get_datatype().is_variant()) {
|
|
|
result.kind = GDScriptParser::DataType::VARIANT;
|
|
|
mark_node_unsafe(p_unary_op);
|
|
|
} else {
|
|
|
bool valid = false;
|
|
|
- result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid);
|
|
|
+ result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid, p_unary_op);
|
|
|
|
|
|
if (!valid) {
|
|
|
push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", p_unary_op->operand->get_datatype().to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op->operand);
|
|
@@ -2688,7 +2714,7 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
|
|
|
p_unary_op->set_datatype(result);
|
|
|
}
|
|
|
|
|
|
-GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value) {
|
|
|
+GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
|
|
|
GDScriptParser::DataType result;
|
|
|
result.is_constant = true;
|
|
|
result.kind = GDScriptParser::DataType::BUILTIN;
|
|
@@ -2710,37 +2736,44 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
|
|
|
scr = obj->get_script();
|
|
|
}
|
|
|
if (scr.is_valid()) {
|
|
|
- result.script_type = scr;
|
|
|
- result.script_path = scr->get_path();
|
|
|
- Ref<GDScript> gds = scr;
|
|
|
- if (gds.is_valid()) {
|
|
|
- result.kind = GDScriptParser::DataType::CLASS;
|
|
|
- // This might be an inner class, so we want to get the parser for the root.
|
|
|
- // But still get the inner class from that tree.
|
|
|
- GDScript *current = gds.ptr();
|
|
|
- List<StringName> class_chain;
|
|
|
- while (current->_owner) {
|
|
|
- // Push to front so it's in reverse.
|
|
|
- class_chain.push_front(current->name);
|
|
|
- current = current->_owner;
|
|
|
- }
|
|
|
+ if (scr->is_valid()) {
|
|
|
+ result.script_type = scr;
|
|
|
+ result.script_path = scr->get_path();
|
|
|
+ Ref<GDScript> gds = scr;
|
|
|
+ if (gds.is_valid()) {
|
|
|
+ result.kind = GDScriptParser::DataType::CLASS;
|
|
|
+ // This might be an inner class, so we want to get the parser for the root.
|
|
|
+ // But still get the inner class from that tree.
|
|
|
+ GDScript *current = gds.ptr();
|
|
|
+ List<StringName> class_chain;
|
|
|
+ while (current->_owner) {
|
|
|
+ // Push to front so it's in reverse.
|
|
|
+ class_chain.push_front(current->name);
|
|
|
+ current = current->_owner;
|
|
|
+ }
|
|
|
|
|
|
- Ref<GDScriptParserRef> ref = get_parser_for(current->path);
|
|
|
- ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
|
|
|
+ Ref<GDScriptParserRef> ref = get_parser_for(current->path);
|
|
|
+ ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
|
|
|
|
|
|
- GDScriptParser::ClassNode *found = ref->get_parser()->head;
|
|
|
+ GDScriptParser::ClassNode *found = ref->get_parser()->head;
|
|
|
|
|
|
- // It should be okay to assume this exists, since we have a complete script already.
|
|
|
- for (const List<StringName>::Element *E = class_chain.front(); E; E = E->next()) {
|
|
|
- found = found->get_member(E->get()).m_class;
|
|
|
- }
|
|
|
+ // It should be okay to assume this exists, since we have a complete script already.
|
|
|
+ for (const List<StringName>::Element *E = class_chain.front(); E; E = E->next()) {
|
|
|
+ found = found->get_member(E->get()).m_class;
|
|
|
+ }
|
|
|
|
|
|
- result.class_type = found;
|
|
|
- result.script_path = ref->get_parser()->script_path;
|
|
|
+ result.class_type = found;
|
|
|
+ result.script_path = ref->get_parser()->script_path;
|
|
|
+ } else {
|
|
|
+ result.kind = GDScriptParser::DataType::SCRIPT;
|
|
|
+ }
|
|
|
+ result.native_type = scr->get_instance_base_type();
|
|
|
} else {
|
|
|
- result.kind = GDScriptParser::DataType::SCRIPT;
|
|
|
+ push_error(vformat(R"(Constant value uses script from "%s" which is loaded but not compiled.)", scr->get_path()), p_source);
|
|
|
+ result.kind = GDScriptParser::DataType::VARIANT;
|
|
|
+ result.type_source = GDScriptParser::DataType::UNDETECTED;
|
|
|
+ result.is_meta_type = false;
|
|
|
}
|
|
|
- result.native_type = scr->get_instance_base_type();
|
|
|
} else {
|
|
|
result.kind = GDScriptParser::DataType::NATIVE;
|
|
|
if (result.native_type == GDScriptNativeClass::get_class_static()) {
|
|
@@ -2996,7 +3029,7 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid) {
|
|
|
+GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
|
|
|
// This function creates dummy variant values and apply the operation to those. Less error-prone than keeping a table of valid operations.
|
|
|
|
|
|
GDScriptParser::DataType result;
|
|
@@ -3069,7 +3102,7 @@ GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator
|
|
|
Variant::evaluate(p_operation, a, b, ret, r_valid);
|
|
|
|
|
|
if (r_valid) {
|
|
|
- return type_from_variant(ret);
|
|
|
+ return type_from_variant(ret, p_source);
|
|
|
}
|
|
|
|
|
|
return result;
|