Merge pull request #69471 from rune-scape/rune-out-of-order
GDScript: Out of order member resolution
This commit is contained in:
commit
6debf86d51
File diff suppressed because it is too large
Load Diff
@ -52,18 +52,20 @@ class GDScriptAnalyzer {
|
|||||||
|
|
||||||
void get_class_node_current_scope_classes(GDScriptParser::ClassNode *p_node, List<GDScriptParser::ClassNode *> *p_list);
|
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_class_inheritance(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
|
||||||
|
Error resolve_class_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive);
|
||||||
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
|
GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type);
|
||||||
|
|
||||||
void decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement);
|
void decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement);
|
||||||
|
|
||||||
// This traverses the tree to resolve all TypeNodes.
|
|
||||||
Error resolve_program();
|
|
||||||
|
|
||||||
void resolve_annotation(GDScriptParser::AnnotationNode *p_annotation);
|
void resolve_annotation(GDScriptParser::AnnotationNode *p_annotation);
|
||||||
void resolve_class_interface(GDScriptParser::ClassNode *p_class);
|
void resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source = nullptr);
|
||||||
void resolve_class_body(GDScriptParser::ClassNode *p_class);
|
void resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source = nullptr);
|
||||||
void resolve_function_signature(GDScriptParser::FunctionNode *p_function);
|
void resolve_class_interface(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
|
||||||
|
void resolve_class_interface(GDScriptParser::ClassNode *p_class, bool p_recursive);
|
||||||
|
void resolve_class_body(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr);
|
||||||
|
void resolve_class_body(GDScriptParser::ClassNode *p_class, bool p_recursive);
|
||||||
|
void resolve_function_signature(GDScriptParser::FunctionNode *p_function, const GDScriptParser::Node *p_source = nullptr);
|
||||||
void resolve_function_body(GDScriptParser::FunctionNode *p_function);
|
void resolve_function_body(GDScriptParser::FunctionNode *p_function);
|
||||||
void resolve_node(GDScriptParser::Node *p_node, bool p_is_root = true);
|
void resolve_node(GDScriptParser::Node *p_node, bool p_is_root = true);
|
||||||
void resolve_suite(GDScriptParser::SuiteNode *p_suite);
|
void resolve_suite(GDScriptParser::SuiteNode *p_suite);
|
||||||
@ -115,7 +117,7 @@ class GDScriptAnalyzer {
|
|||||||
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
|
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
|
||||||
void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
|
void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
|
||||||
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
|
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false, const GDScriptParser::Node *p_source_node = nullptr);
|
||||||
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
|
void push_error(const String &p_message, const GDScriptParser::Node *p_origin = nullptr);
|
||||||
void mark_node_unsafe(const GDScriptParser::Node *p_node);
|
void mark_node_unsafe(const GDScriptParser::Node *p_node);
|
||||||
void mark_lambda_use_self();
|
void mark_lambda_use_self();
|
||||||
bool class_exists(const StringName &p_class) const;
|
bool class_exists(const StringName &p_class) const;
|
||||||
@ -128,6 +130,7 @@ public:
|
|||||||
Error resolve_inheritance();
|
Error resolve_inheritance();
|
||||||
Error resolve_interface();
|
Error resolve_interface();
|
||||||
Error resolve_body();
|
Error resolve_body();
|
||||||
|
Error resolve_dependencies();
|
||||||
Error analyze();
|
Error analyze();
|
||||||
|
|
||||||
GDScriptAnalyzer(GDScriptParser *p_parser);
|
GDScriptAnalyzer(GDScriptParser *p_parser);
|
||||||
|
@ -50,6 +50,13 @@ GDScriptParser *GDScriptParserRef::get_parser() const {
|
|||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GDScriptAnalyzer *GDScriptParserRef::get_analyzer() {
|
||||||
|
if (analyzer == nullptr) {
|
||||||
|
analyzer = memnew(GDScriptAnalyzer(parser));
|
||||||
|
}
|
||||||
|
return analyzer;
|
||||||
|
}
|
||||||
|
|
||||||
Error GDScriptParserRef::raise_status(Status p_new_status) {
|
Error GDScriptParserRef::raise_status(Status p_new_status) {
|
||||||
ERR_FAIL_COND_V(parser == nullptr, ERR_INVALID_DATA);
|
ERR_FAIL_COND_V(parser == nullptr, ERR_INVALID_DATA);
|
||||||
|
|
||||||
@ -64,23 +71,22 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
|
|||||||
result = parser->parse(GDScriptCache::get_source_code(path), path, false);
|
result = parser->parse(GDScriptCache::get_source_code(path), path, false);
|
||||||
break;
|
break;
|
||||||
case PARSED: {
|
case PARSED: {
|
||||||
analyzer = memnew(GDScriptAnalyzer(parser));
|
|
||||||
status = INHERITANCE_SOLVED;
|
status = INHERITANCE_SOLVED;
|
||||||
Error inheritance_result = analyzer->resolve_inheritance();
|
Error inheritance_result = get_analyzer()->resolve_inheritance();
|
||||||
if (result == OK) {
|
if (result == OK) {
|
||||||
result = inheritance_result;
|
result = inheritance_result;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case INHERITANCE_SOLVED: {
|
case INHERITANCE_SOLVED: {
|
||||||
status = INTERFACE_SOLVED;
|
status = INTERFACE_SOLVED;
|
||||||
Error interface_result = analyzer->resolve_interface();
|
Error interface_result = get_analyzer()->resolve_interface();
|
||||||
if (result == OK) {
|
if (result == OK) {
|
||||||
result = interface_result;
|
result = interface_result;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case INTERFACE_SOLVED: {
|
case INTERFACE_SOLVED: {
|
||||||
status = FULLY_SOLVED;
|
status = FULLY_SOLVED;
|
||||||
Error body_result = analyzer->resolve_body();
|
Error body_result = get_analyzer()->resolve_body();
|
||||||
if (result == OK) {
|
if (result == OK) {
|
||||||
result = body_result;
|
result = body_result;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@ public:
|
|||||||
bool is_valid() const;
|
bool is_valid() const;
|
||||||
Status get_status() const;
|
Status get_status() const;
|
||||||
GDScriptParser *get_parser() const;
|
GDScriptParser *get_parser() const;
|
||||||
|
GDScriptAnalyzer *get_analyzer();
|
||||||
Error raise_status(Status p_new_status);
|
Error raise_status(Status p_new_status);
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
@ -157,6 +157,7 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
|
|||||||
result.builtin_type = Variant::INT;
|
result.builtin_type = Variant::INT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case GDScriptParser::DataType::RESOLVING:
|
||||||
case GDScriptParser::DataType::UNRESOLVED: {
|
case GDScriptParser::DataType::UNRESOLVED: {
|
||||||
ERR_PRINT("Parser bug: converting unresolved type.");
|
ERR_PRINT("Parser bug: converting unresolved type.");
|
||||||
return GDScriptDataType();
|
return GDScriptDataType();
|
||||||
|
@ -642,6 +642,53 @@ void GDScriptParser::parse_program() {
|
|||||||
clear_unused_annotations();
|
clear_unused_annotations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GDScriptParser::ClassNode *GDScriptParser::find_class(const String &p_qualified_name) const {
|
||||||
|
String first = p_qualified_name.get_slice("::", 0);
|
||||||
|
|
||||||
|
Vector<String> class_names;
|
||||||
|
GDScriptParser::ClassNode *result = nullptr;
|
||||||
|
// Empty initial name means start at the head.
|
||||||
|
if (first.is_empty() || (head->identifier && first == head->identifier->name)) {
|
||||||
|
class_names = p_qualified_name.split("::");
|
||||||
|
result = head;
|
||||||
|
} else if (p_qualified_name.begins_with(script_path)) {
|
||||||
|
// Script path could have a class path separator("::") in it.
|
||||||
|
class_names = p_qualified_name.trim_prefix(script_path).split("::");
|
||||||
|
result = head;
|
||||||
|
} else if (head->has_member(first)) {
|
||||||
|
class_names = p_qualified_name.split("::");
|
||||||
|
GDScriptParser::ClassNode::Member member = head->get_member(first);
|
||||||
|
if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
|
||||||
|
result = member.m_class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starts at index 1 because index 0 was handled above.
|
||||||
|
for (int i = 1; result != nullptr && i < class_names.size(); i++) {
|
||||||
|
String current_name = class_names[i];
|
||||||
|
GDScriptParser::ClassNode *next = nullptr;
|
||||||
|
if (result->has_member(current_name)) {
|
||||||
|
GDScriptParser::ClassNode::Member member = result->get_member(current_name);
|
||||||
|
if (member.type == GDScriptParser::ClassNode::Member::CLASS) {
|
||||||
|
next = member.m_class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDScriptParser::has_class(const GDScriptParser::ClassNode *p_class) const {
|
||||||
|
if (head->fqcn.is_empty() && p_class->fqcn.get_slice("::", 0).is_empty()) {
|
||||||
|
return p_class == head;
|
||||||
|
} else if (p_class->fqcn.begins_with(head->fqcn)) {
|
||||||
|
return find_class(p_class->fqcn.trim_prefix(head->fqcn)) == p_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
GDScriptParser::ClassNode *GDScriptParser::parse_class() {
|
GDScriptParser::ClassNode *GDScriptParser::parse_class() {
|
||||||
ClassNode *n_class = alloc_node<ClassNode>();
|
ClassNode *n_class = alloc_node<ClassNode>();
|
||||||
|
|
||||||
@ -2240,7 +2287,7 @@ GDScriptParser::IdentifierNode *GDScriptParser::parse_identifier() {
|
|||||||
|
|
||||||
GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign) {
|
GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode *p_previous_operand, bool p_can_assign) {
|
||||||
if (!previous.is_identifier()) {
|
if (!previous.is_identifier()) {
|
||||||
ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing literal node without literal token.");
|
ERR_FAIL_V_MSG(nullptr, "Parser bug: parsing identifier node without identifier token.");
|
||||||
}
|
}
|
||||||
IdentifierNode *identifier = alloc_node<IdentifierNode>();
|
IdentifierNode *identifier = alloc_node<IdentifierNode>();
|
||||||
complete_extents(identifier);
|
complete_extents(identifier);
|
||||||
@ -4042,11 +4089,12 @@ String GDScriptParser::DataType::to_string() const {
|
|||||||
}
|
}
|
||||||
case ENUM:
|
case ENUM:
|
||||||
return enum_type.operator String() + " (enum)";
|
return enum_type.operator String() + " (enum)";
|
||||||
|
case RESOLVING:
|
||||||
case UNRESOLVED:
|
case UNRESOLVED:
|
||||||
return "<unresolved type>";
|
return "<unresolved type>";
|
||||||
}
|
}
|
||||||
|
|
||||||
ERR_FAIL_V_MSG("<unresolved type", "Kind set outside the enum range.");
|
ERR_FAIL_V_MSG("<unresolved type>", "Kind set outside the enum range.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_type) {
|
static Variant::Type _variant_type_to_typed_array_element_type(Variant::Type p_type) {
|
||||||
|
@ -107,6 +107,7 @@ public:
|
|||||||
CLASS, // GDScript.
|
CLASS, // GDScript.
|
||||||
ENUM, // Enumeration.
|
ENUM, // Enumeration.
|
||||||
VARIANT, // Can be any type.
|
VARIANT, // Can be any type.
|
||||||
|
RESOLVING, // Currently resolving.
|
||||||
UNRESOLVED,
|
UNRESOLVED,
|
||||||
};
|
};
|
||||||
Kind kind = UNRESOLVED;
|
Kind kind = UNRESOLVED;
|
||||||
@ -133,9 +134,10 @@ public:
|
|||||||
MethodInfo method_info; // For callable/signals.
|
MethodInfo method_info; // For callable/signals.
|
||||||
HashMap<StringName, int64_t> enum_values; // For enums.
|
HashMap<StringName, int64_t> enum_values; // For enums.
|
||||||
|
|
||||||
_FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; }
|
_FORCE_INLINE_ bool is_set() const { return kind != RESOLVING && kind != UNRESOLVED; }
|
||||||
|
_FORCE_INLINE_ bool is_resolving() const { return kind == RESOLVING; }
|
||||||
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
|
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
|
||||||
_FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == UNRESOLVED; }
|
_FORCE_INLINE_ bool is_variant() const { return kind == VARIANT || kind == RESOLVING || kind == UNRESOLVED; }
|
||||||
_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
|
_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
|
||||||
String to_string() const;
|
String to_string() const;
|
||||||
|
|
||||||
@ -188,6 +190,7 @@ public:
|
|||||||
return script_type == p_other.script_type;
|
return script_type == p_other.script_type;
|
||||||
case CLASS:
|
case CLASS:
|
||||||
return class_type == p_other.class_type;
|
return class_type == p_other.class_type;
|
||||||
|
case RESOLVING:
|
||||||
case UNRESOLVED:
|
case UNRESOLVED:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -516,6 +519,32 @@ public:
|
|||||||
};
|
};
|
||||||
EnumNode::Value enum_value;
|
EnumNode::Value enum_value;
|
||||||
|
|
||||||
|
String get_name() const {
|
||||||
|
switch (type) {
|
||||||
|
case UNDEFINED:
|
||||||
|
return "<undefined member>";
|
||||||
|
case CLASS:
|
||||||
|
// All class-type members have an id.
|
||||||
|
return m_class->identifier->name;
|
||||||
|
case CONSTANT:
|
||||||
|
return constant->identifier->name;
|
||||||
|
case FUNCTION:
|
||||||
|
return function->identifier->name;
|
||||||
|
case SIGNAL:
|
||||||
|
return signal->identifier->name;
|
||||||
|
case VARIABLE:
|
||||||
|
return variable->identifier->name;
|
||||||
|
case ENUM:
|
||||||
|
// All enum-type members have an id.
|
||||||
|
return m_enum->identifier->name;
|
||||||
|
case ENUM_VALUE:
|
||||||
|
return enum_value.identifier->name;
|
||||||
|
case GROUP:
|
||||||
|
return annotation->export_info.name;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
String get_type_name() const {
|
String get_type_name() const {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case UNDEFINED:
|
case UNDEFINED:
|
||||||
@ -576,31 +605,42 @@ public:
|
|||||||
return variable->get_datatype();
|
return variable->get_datatype();
|
||||||
case ENUM:
|
case ENUM:
|
||||||
return m_enum->get_datatype();
|
return m_enum->get_datatype();
|
||||||
case ENUM_VALUE: {
|
case ENUM_VALUE:
|
||||||
// Always integer.
|
return enum_value.identifier->get_datatype();
|
||||||
DataType out_type;
|
case SIGNAL:
|
||||||
out_type.type_source = DataType::ANNOTATED_EXPLICIT;
|
return signal->get_datatype();
|
||||||
out_type.kind = DataType::BUILTIN;
|
case GROUP:
|
||||||
out_type.builtin_type = Variant::INT;
|
|
||||||
return out_type;
|
|
||||||
}
|
|
||||||
case SIGNAL: {
|
|
||||||
DataType out_type;
|
|
||||||
out_type.type_source = DataType::ANNOTATED_EXPLICIT;
|
|
||||||
out_type.kind = DataType::BUILTIN;
|
|
||||||
out_type.builtin_type = Variant::SIGNAL;
|
|
||||||
// TODO: Add parameter info.
|
|
||||||
return out_type;
|
|
||||||
}
|
|
||||||
case GROUP: {
|
|
||||||
return DataType();
|
return DataType();
|
||||||
}
|
|
||||||
case UNDEFINED:
|
case UNDEFINED:
|
||||||
return DataType();
|
return DataType();
|
||||||
}
|
}
|
||||||
ERR_FAIL_V_MSG(DataType(), "Reaching unhandled type.");
|
ERR_FAIL_V_MSG(DataType(), "Reaching unhandled type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node *get_source_node() const {
|
||||||
|
switch (type) {
|
||||||
|
case CLASS:
|
||||||
|
return m_class;
|
||||||
|
case CONSTANT:
|
||||||
|
return constant;
|
||||||
|
case FUNCTION:
|
||||||
|
return function;
|
||||||
|
case VARIABLE:
|
||||||
|
return variable;
|
||||||
|
case ENUM:
|
||||||
|
return m_enum;
|
||||||
|
case ENUM_VALUE:
|
||||||
|
return enum_value.identifier;
|
||||||
|
case SIGNAL:
|
||||||
|
return signal;
|
||||||
|
case GROUP:
|
||||||
|
return annotation;
|
||||||
|
case UNDEFINED:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ERR_FAIL_V_MSG(nullptr, "Reaching unhandled type.");
|
||||||
|
}
|
||||||
|
|
||||||
Member() {}
|
Member() {}
|
||||||
|
|
||||||
Member(ClassNode *p_class) {
|
Member(ClassNode *p_class) {
|
||||||
@ -1430,6 +1470,8 @@ public:
|
|||||||
Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion);
|
Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion);
|
||||||
ClassNode *get_tree() const { return head; }
|
ClassNode *get_tree() const { return head; }
|
||||||
bool is_tool() const { return _is_tool; }
|
bool is_tool() const { return _is_tool; }
|
||||||
|
ClassNode *find_class(const String &p_qualified_name) const;
|
||||||
|
bool has_class(const GDScriptParser::ClassNode *p_class) const;
|
||||||
static Variant::Type get_builtin_type(const StringName &p_type);
|
static Variant::Type get_builtin_type(const StringName &p_type);
|
||||||
|
|
||||||
CompletionContext get_completion_context() const { return completion_context; }
|
CompletionContext get_completion_context() const { return completion_context; }
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
GDTEST_ANALYZER_ERROR
|
GDTEST_ANALYZER_ERROR
|
||||||
The member "Vector2" cannot have the same name as a builtin type.
|
Class "Vector2" hides a built-in type.
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
func test():
|
||||||
|
print(InnerA.new())
|
||||||
|
|
||||||
|
class InnerA extends InnerB:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class InnerB extends InnerA:
|
||||||
|
pass
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Cyclic inheritance.
|
@ -0,0 +1,5 @@
|
|||||||
|
func test():
|
||||||
|
print(c1)
|
||||||
|
|
||||||
|
const c1 = c2
|
||||||
|
const c2 = c1
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Could not resolve member "c1": Cyclic reference.
|
@ -0,0 +1,5 @@
|
|||||||
|
func test():
|
||||||
|
print(E1.V)
|
||||||
|
|
||||||
|
enum E1 {V = E2.V}
|
||||||
|
enum E2 {V = E1.V}
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Could not resolve member "E1": Cyclic reference.
|
@ -0,0 +1,5 @@
|
|||||||
|
func test():
|
||||||
|
print(EV1)
|
||||||
|
|
||||||
|
enum {EV1 = EV2}
|
||||||
|
enum {EV2 = EV1}
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Could not resolve member "EV1": Cyclic reference.
|
@ -0,0 +1,6 @@
|
|||||||
|
func test():
|
||||||
|
print(v)
|
||||||
|
|
||||||
|
var v = A.v
|
||||||
|
|
||||||
|
const A = preload("cyclic_ref_external_a.notest.gd")
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Could not resolve member "v".
|
@ -0,0 +1,3 @@
|
|||||||
|
const B = preload("cyclic_ref_external.gd")
|
||||||
|
|
||||||
|
var v = B.v
|
@ -0,0 +1,9 @@
|
|||||||
|
func test():
|
||||||
|
print(f1())
|
||||||
|
print(f2())
|
||||||
|
|
||||||
|
static func f1(p := f2()) -> int:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
static func f2(p := f1()) -> int:
|
||||||
|
return 2
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Could not resolve member "f1": Cyclic reference.
|
@ -0,0 +1,12 @@
|
|||||||
|
func test():
|
||||||
|
print(v)
|
||||||
|
|
||||||
|
var v := InnerA.new().f()
|
||||||
|
|
||||||
|
class InnerA:
|
||||||
|
func f(p := InnerB.new().f()) -> int:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
class InnerB extends InnerA:
|
||||||
|
func f(p := 1) -> int:
|
||||||
|
return super.f()
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Could not resolve member "f": Cyclic reference.
|
@ -0,0 +1,5 @@
|
|||||||
|
func test():
|
||||||
|
print(v1)
|
||||||
|
|
||||||
|
var v1 := v2
|
||||||
|
var v2 := v1
|
@ -0,0 +1,2 @@
|
|||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Could not resolve member "v1": Cyclic reference.
|
@ -0,0 +1,50 @@
|
|||||||
|
func test():
|
||||||
|
print("v1: ", v1)
|
||||||
|
print("v1 is String: ", v1 is String)
|
||||||
|
print("v2: ", v2)
|
||||||
|
print("v2 is bool: ", v2 is bool)
|
||||||
|
print("c1: ", c1)
|
||||||
|
print("c1 is int: ", c1 is int)
|
||||||
|
print("c2: ", c2)
|
||||||
|
print("c2 is int: ", c2 is int)
|
||||||
|
print("E1.V1: ", E1.V1)
|
||||||
|
print("E1.V2: ", E1.V2)
|
||||||
|
print("E2.V: ", E2.V)
|
||||||
|
print("EV1: ", EV1)
|
||||||
|
print("EV2: ", EV2)
|
||||||
|
print("EV3: ", EV3)
|
||||||
|
|
||||||
|
var v1 := InnerA.new().fn()
|
||||||
|
|
||||||
|
class InnerA extends InnerAB:
|
||||||
|
func fn(p2 := E1.V2) -> String:
|
||||||
|
return "%s, p2=%s" % [super.fn(), p2]
|
||||||
|
|
||||||
|
class InnerAB:
|
||||||
|
func fn(p1 := c1) -> String:
|
||||||
|
return "p1=%s" % p1
|
||||||
|
|
||||||
|
var v2 := f()
|
||||||
|
|
||||||
|
func f() -> bool:
|
||||||
|
return true
|
||||||
|
|
||||||
|
const c1 := E1.V1
|
||||||
|
|
||||||
|
enum E1 {
|
||||||
|
V1 = E2.V + 2,
|
||||||
|
V2 = V1 - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
enum E2 {V = 2}
|
||||||
|
|
||||||
|
const c2 := EV2
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EV1 = 42,
|
||||||
|
EV2 = EV3 + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EV3 = EV1 + 1
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
v1: p1=4, p2=3
|
||||||
|
v1 is String: true
|
||||||
|
v2: true
|
||||||
|
v2 is bool: true
|
||||||
|
c1: 4
|
||||||
|
c1 is int: true
|
||||||
|
c2: 44
|
||||||
|
c2 is int: true
|
||||||
|
E1.V1: 4
|
||||||
|
E1.V2: 3
|
||||||
|
E2.V: 2
|
||||||
|
EV1: 42
|
||||||
|
EV2: 44
|
||||||
|
EV3: 43
|
@ -0,0 +1,39 @@
|
|||||||
|
const B = preload("out_of_order_external_a.notest.gd")
|
||||||
|
|
||||||
|
func test():
|
||||||
|
print("v1: ", v1)
|
||||||
|
print("v1 is String: ", v1 is String)
|
||||||
|
print("v2: ", v2)
|
||||||
|
print("v2 is bool: ", v2 is bool)
|
||||||
|
print("c1: ", c1)
|
||||||
|
print("c1 is int: ", c1 is int)
|
||||||
|
print("c2: ", c2)
|
||||||
|
print("c2 is int: ", c2 is int)
|
||||||
|
print("E1.V1: ", E1.V1)
|
||||||
|
print("E1.V2: ", E1.V2)
|
||||||
|
print("B.E2.V: ", B.E2.V)
|
||||||
|
print("EV1: ", EV1)
|
||||||
|
print("EV2: ", EV2)
|
||||||
|
print("B.EV3: ", B.EV3)
|
||||||
|
|
||||||
|
var v1 := Inner.new().fn()
|
||||||
|
|
||||||
|
class Inner extends B.Inner:
|
||||||
|
func fn(p2 := E1.V2) -> String:
|
||||||
|
return "%s, p2=%s" % [super.fn(), p2]
|
||||||
|
|
||||||
|
var v2 := B.new().f()
|
||||||
|
|
||||||
|
const c1 := E1.V1
|
||||||
|
|
||||||
|
enum E1 {
|
||||||
|
V1 = B.E2.V + 2,
|
||||||
|
V2 = V1 - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const c2 := EV2
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EV1 = 42,
|
||||||
|
EV2 = B.EV3 + 1
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
GDTEST_OK
|
||||||
|
v1: p1=4, p2=3
|
||||||
|
v1 is String: true
|
||||||
|
v2: true
|
||||||
|
v2 is bool: true
|
||||||
|
c1: 4
|
||||||
|
c1 is int: true
|
||||||
|
c2: 44
|
||||||
|
c2 is int: true
|
||||||
|
E1.V1: 4
|
||||||
|
E1.V2: 3
|
||||||
|
B.E2.V: 2
|
||||||
|
EV1: 42
|
||||||
|
EV2: 44
|
||||||
|
B.EV3: 43
|
@ -0,0 +1,12 @@
|
|||||||
|
const A = preload("out_of_order_external.gd")
|
||||||
|
|
||||||
|
class Inner:
|
||||||
|
func fn(p1 := A.c1) -> String:
|
||||||
|
return "p1=%s" % p1
|
||||||
|
|
||||||
|
func f(p := A.c1) -> bool:
|
||||||
|
return p is int
|
||||||
|
|
||||||
|
enum E2 {V = 2}
|
||||||
|
|
||||||
|
enum {EV3 = A.EV1 + 1}
|
Loading…
Reference in New Issue
Block a user