mirror of
https://github.com/godotengine/godot
synced 2024-09-18 21:41:28 +00:00
Fix constant base typing in extended GDScript class
This commit is contained in:
parent
39fe0bfdc3
commit
65a49bad5a
|
@ -219,6 +219,22 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C
|
||||||
return OK;
|
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) {
|
Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) {
|
||||||
if (p_class->base_type.is_set()) {
|
if (p_class->base_type.is_set()) {
|
||||||
// Already resolved
|
// Already resolved
|
||||||
|
@ -327,9 +343,10 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
|
||||||
base.native_type = name;
|
base.native_type = name;
|
||||||
} else {
|
} else {
|
||||||
// Look for other classes in script.
|
// Look for other classes in script.
|
||||||
GDScriptParser::ClassNode *look_class = p_class;
|
|
||||||
bool found = false;
|
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->identifier && look_class->identifier->name == name) {
|
||||||
if (!look_class->get_datatype().is_set()) {
|
if (!look_class->get_datatype().is_set()) {
|
||||||
Error err = resolve_inheritance(look_class, false);
|
Error err = resolve_inheritance(look_class, false);
|
||||||
|
@ -353,7 +370,6 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
look_class = look_class->outer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
|
@ -517,12 +533,11 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
|
||||||
result = make_native_enum_type(parser->current_class->base_type.native_type, first);
|
result = make_native_enum_type(parser->current_class->base_type.native_type, first);
|
||||||
} else {
|
} else {
|
||||||
// Classes in current scope.
|
// Classes in current scope.
|
||||||
GDScriptParser::ClassNode *script_class = parser->current_class;
|
List<GDScriptParser::ClassNode *> script_classes;
|
||||||
bool found = false;
|
get_class_node_current_scope_classes(parser->current_class, &script_classes);
|
||||||
while (!found && script_class != nullptr) {
|
for (GDScriptParser::ClassNode *script_class : script_classes) {
|
||||||
if (script_class->identifier && script_class->identifier->name == first) {
|
if (script_class->identifier && script_class->identifier->name == first) {
|
||||||
result = script_class->get_datatype();
|
result = script_class->get_datatype();
|
||||||
found = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (script_class->members_indices.has(first)) {
|
if (script_class->members_indices.has(first)) {
|
||||||
|
@ -530,17 +545,14 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
|
||||||
switch (member.type) {
|
switch (member.type) {
|
||||||
case GDScriptParser::ClassNode::Member::CLASS:
|
case GDScriptParser::ClassNode::Member::CLASS:
|
||||||
result = member.m_class->get_datatype();
|
result = member.m_class->get_datatype();
|
||||||
found = true;
|
|
||||||
break;
|
break;
|
||||||
case GDScriptParser::ClassNode::Member::ENUM:
|
case GDScriptParser::ClassNode::Member::ENUM:
|
||||||
result = member.m_enum->get_datatype();
|
result = member.m_enum->get_datatype();
|
||||||
found = true;
|
|
||||||
break;
|
break;
|
||||||
case GDScriptParser::ClassNode::Member::CONSTANT:
|
case GDScriptParser::ClassNode::Member::CONSTANT:
|
||||||
if (member.constant->get_datatype().is_meta_type) {
|
if (member.constant->get_datatype().is_meta_type) {
|
||||||
result = member.constant->get_datatype();
|
result = member.constant->get_datatype();
|
||||||
result.is_meta_type = false;
|
result.is_meta_type = false;
|
||||||
found = true;
|
|
||||||
break;
|
break;
|
||||||
} else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) {
|
} else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) {
|
||||||
Ref<GDScript> gdscript = member.constant->initializer->reduced_value;
|
Ref<GDScript> gdscript = member.constant->initializer->reduced_value;
|
||||||
|
@ -569,7 +581,6 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
|
||||||
return GDScriptParser::DataType();
|
return GDScriptParser::DataType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
script_class = script_class->outer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!result.is_set()) {
|
if (!result.is_set()) {
|
||||||
|
@ -2891,41 +2902,43 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
|
||||||
}
|
}
|
||||||
// Check outer constants.
|
// Check outer constants.
|
||||||
// TODO: Allow outer static functions.
|
// TODO: Allow outer static functions.
|
||||||
GDScriptParser::ClassNode *outer = base_class->outer;
|
if (base_class->outer != nullptr) {
|
||||||
while (outer != nullptr) {
|
List<GDScriptParser::ClassNode *> script_classes;
|
||||||
if (outer->has_member(name)) {
|
get_class_node_current_scope_classes(parser->current_class, &script_classes);
|
||||||
const GDScriptParser::ClassNode::Member &member = outer->get_member(name);
|
for (GDScriptParser::ClassNode *script_class : script_classes) {
|
||||||
switch (member.type) {
|
if (script_class->has_member(name)) {
|
||||||
case GDScriptParser::ClassNode::Member::CONSTANT: {
|
const GDScriptParser::ClassNode::Member &member = script_class->get_member(name);
|
||||||
// TODO: Make sure loops won't cause problem. And make special error message for those.
|
switch (member.type) {
|
||||||
// For out-of-order resolution:
|
case GDScriptParser::ClassNode::Member::CONSTANT: {
|
||||||
reduce_expression(member.constant->initializer);
|
// TODO: Make sure loops won't cause problem. And make special error message for those.
|
||||||
p_identifier->set_datatype(member.get_datatype());
|
// For out-of-order resolution:
|
||||||
p_identifier->is_constant = true;
|
reduce_expression(member.constant->initializer);
|
||||||
p_identifier->reduced_value = member.constant->initializer->reduced_value;
|
p_identifier->set_datatype(member.get_datatype());
|
||||||
return;
|
p_identifier->is_constant = true;
|
||||||
} break;
|
p_identifier->reduced_value = member.constant->initializer->reduced_value;
|
||||||
case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
|
return;
|
||||||
p_identifier->set_datatype(member.get_datatype());
|
} break;
|
||||||
p_identifier->is_constant = true;
|
case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
|
||||||
p_identifier->reduced_value = member.enum_value.value;
|
p_identifier->set_datatype(member.get_datatype());
|
||||||
return;
|
p_identifier->is_constant = true;
|
||||||
} break;
|
p_identifier->reduced_value = member.enum_value.value;
|
||||||
case GDScriptParser::ClassNode::Member::ENUM: {
|
return;
|
||||||
p_identifier->set_datatype(member.get_datatype());
|
} break;
|
||||||
p_identifier->is_constant = false;
|
case GDScriptParser::ClassNode::Member::ENUM: {
|
||||||
return;
|
p_identifier->set_datatype(member.get_datatype());
|
||||||
} break;
|
p_identifier->is_constant = false;
|
||||||
case GDScriptParser::ClassNode::Member::CLASS: {
|
return;
|
||||||
resolve_class_interface(member.m_class);
|
} break;
|
||||||
p_identifier->set_datatype(member.m_class->get_datatype());
|
case GDScriptParser::ClassNode::Member::CLASS: {
|
||||||
return;
|
resolve_class_interface(member.m_class);
|
||||||
} break;
|
p_identifier->set_datatype(member.m_class->get_datatype());
|
||||||
default:
|
return;
|
||||||
break;
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
outer = outer->outer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base_class = base_class->base_type.class_type;
|
base_class = base_class->base_type.class_type;
|
||||||
|
|
|
@ -50,6 +50,8 @@ class GDScriptAnalyzer {
|
||||||
Error check_native_member_name_conflict(const StringName &p_member_name, const GDScriptParser::Node *p_member_node, const StringName &p_native_type_string);
|
Error check_native_member_name_conflict(const StringName &p_member_name, const GDScriptParser::Node *p_member_node, const StringName &p_native_type_string);
|
||||||
Error check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node);
|
Error check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node);
|
||||||
|
|
||||||
|
void get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list);
|
||||||
|
|
||||||
Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true);
|
Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true);
|
||||||
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
|
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
const A: = preload("base_outer_resolution_a.notest.gd")
|
||||||
|
const B: = preload("base_outer_resolution_b.notest.gd")
|
||||||
|
const C: = preload("base_outer_resolution_c.notest.gd")
|
||||||
|
|
||||||
|
const Extend: = preload("base_outer_resolution_extend.notest.gd")
|
||||||
|
|
||||||
|
func test() -> void:
|
||||||
|
Extend.test_a(A.new())
|
||||||
|
Extend.test_b(B.new())
|
||||||
|
Extend.InnerClass.test_c(C.new())
|
||||||
|
Extend.InnerClass.InnerInnerClass.test_a_b_c(A.new(), B.new(), C.new())
|
||||||
|
Extend.InnerClass.InnerInnerClass.test_enum(C.TestEnum.HELLO_WORLD)
|
||||||
|
Extend.InnerClass.InnerInnerClass.test_a_prime(A.APrime.new())
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
GDTEST_OK
|
||||||
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
|
true
|
||||||
|
true
|
|
@ -0,0 +1,2 @@
|
||||||
|
class APrime:
|
||||||
|
pass
|
|
@ -0,0 +1,4 @@
|
||||||
|
const A: = preload("base_outer_resolution_a.notest.gd")
|
||||||
|
|
||||||
|
class InnerClassInBase:
|
||||||
|
const C: = preload("base_outer_resolution_c.notest.gd")
|
|
@ -0,0 +1,3 @@
|
||||||
|
enum TestEnum {
|
||||||
|
HELLO_WORLD
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
extends "base_outer_resolution_base.notest.gd"
|
||||||
|
|
||||||
|
const B: = preload("base_outer_resolution_b.notest.gd")
|
||||||
|
|
||||||
|
static func test_a(a: A) -> void:
|
||||||
|
print(a is A)
|
||||||
|
|
||||||
|
static func test_b(b: B) -> void:
|
||||||
|
print(b is B)
|
||||||
|
|
||||||
|
class InnerClass extends InnerClassInBase:
|
||||||
|
static func test_c(c: C) -> void:
|
||||||
|
print(c is C)
|
||||||
|
|
||||||
|
class InnerInnerClass:
|
||||||
|
static func test_a_b_c(a: A, b: B, c: C) -> void:
|
||||||
|
print(a is A and b is B and c is C)
|
||||||
|
|
||||||
|
static func test_enum(test_enum: C.TestEnum) -> void:
|
||||||
|
print(test_enum == C.TestEnum.HELLO_WORLD)
|
||||||
|
|
||||||
|
static func test_a_prime(a_prime: A.APrime) -> void:
|
||||||
|
print(a_prime is A.APrime)
|
Loading…
Reference in a new issue