mirror of
https://github.com/godotengine/godot.git
synced 2025-11-02 06:31:13 +00:00
GDScript: Allow use local constants as types
This commit is contained in:
parent
6758a7f8c0
commit
68a567bd13
12 changed files with 236 additions and 122 deletions
|
|
@ -613,8 +613,45 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
StringName first = p_type->type_chain[0]->name;
|
const GDScriptParser::IdentifierNode *first_id = p_type->type_chain[0];
|
||||||
|
StringName first = first_id->name;
|
||||||
|
bool type_found = false;
|
||||||
|
|
||||||
|
if (first_id->suite && first_id->suite->has_local(first)) {
|
||||||
|
const GDScriptParser::SuiteNode::Local &local = first_id->suite->get_local(first);
|
||||||
|
if (local.type == GDScriptParser::SuiteNode::Local::CONSTANT) {
|
||||||
|
result = local.get_datatype();
|
||||||
|
if (!result.is_set()) {
|
||||||
|
// Don't try to resolve it as the constant can be declared below.
|
||||||
|
push_error(vformat(R"(Local constant "%s" is not resolved at this point.)", first), first_id);
|
||||||
|
return bad_type;
|
||||||
|
}
|
||||||
|
if (result.is_meta_type) {
|
||||||
|
type_found = true;
|
||||||
|
} else if (Ref<Script>(local.constant->initializer->reduced_value).is_valid()) {
|
||||||
|
Ref<GDScript> gdscript = local.constant->initializer->reduced_value;
|
||||||
|
if (gdscript.is_valid()) {
|
||||||
|
Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_script_path());
|
||||||
|
if (ref->raise_status(GDScriptParserRef::INHERITANCE_SOLVED) != OK) {
|
||||||
|
push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_script_path()), first_id);
|
||||||
|
return bad_type;
|
||||||
|
}
|
||||||
|
result = ref->get_parser()->head->get_datatype();
|
||||||
|
} else {
|
||||||
|
result = make_script_meta_type(local.constant->initializer->reduced_value);
|
||||||
|
}
|
||||||
|
type_found = true;
|
||||||
|
} else {
|
||||||
|
push_error(vformat(R"(Local constant "%s" is not a valid type.)", first), first_id);
|
||||||
|
return bad_type;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
push_error(vformat(R"(Local %s "%s" cannot be used as a type.)", local.get_name(), first), first_id);
|
||||||
|
return bad_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!type_found) {
|
||||||
if (first == SNAME("Variant")) {
|
if (first == SNAME("Variant")) {
|
||||||
if (p_type->type_chain.size() == 2) {
|
if (p_type->type_chain.size() == 2) {
|
||||||
// May be nested enum.
|
// May be nested enum.
|
||||||
|
|
@ -751,6 +788,8 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!result.is_set()) {
|
if (!result.is_set()) {
|
||||||
push_error(vformat(R"(Could not find type "%s" in the current scope.)", first), p_type);
|
push_error(vformat(R"(Could not find type "%s" in the current scope.)", first), p_type);
|
||||||
return bad_type;
|
return bad_type;
|
||||||
|
|
|
||||||
|
|
@ -2291,9 +2291,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
|
||||||
IdentifierNode *identifier = alloc_node<IdentifierNode>();
|
IdentifierNode *identifier = alloc_node<IdentifierNode>();
|
||||||
complete_extents(identifier);
|
complete_extents(identifier);
|
||||||
identifier->name = previous.get_identifier();
|
identifier->name = previous.get_identifier();
|
||||||
#ifdef DEBUG_ENABLED
|
|
||||||
identifier->suite = current_suite;
|
identifier->suite = current_suite;
|
||||||
#endif
|
|
||||||
|
|
||||||
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
|
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
|
||||||
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
|
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
|
||||||
|
|
@ -4283,7 +4281,7 @@ String GDScriptParser::SuiteNode::Local::get_name() const {
|
||||||
case SuiteNode::Local::FOR_VARIABLE:
|
case SuiteNode::Local::FOR_VARIABLE:
|
||||||
return "for loop iterator";
|
return "for loop iterator";
|
||||||
case SuiteNode::Local::PATTERN_BIND:
|
case SuiteNode::Local::PATTERN_BIND:
|
||||||
return "pattern_bind";
|
return "pattern bind";
|
||||||
case SuiteNode::Local::UNDEFINED:
|
case SuiteNode::Local::UNDEFINED:
|
||||||
return "<undefined>";
|
return "<undefined>";
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -859,9 +859,7 @@ public:
|
||||||
|
|
||||||
struct IdentifierNode : public ExpressionNode {
|
struct IdentifierNode : public ExpressionNode {
|
||||||
StringName name;
|
StringName name;
|
||||||
#ifdef DEBUG_ENABLED
|
|
||||||
SuiteNode *suite = nullptr; // The block in which the identifier is used.
|
SuiteNode *suite = nullptr; // The block in which the identifier is used.
|
||||||
#endif
|
|
||||||
|
|
||||||
enum Source {
|
enum Source {
|
||||||
UNDEFINED_SOURCE,
|
UNDEFINED_SOURCE,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
enum MyEnum {}
|
||||||
|
|
||||||
|
func test():
|
||||||
|
var e: E
|
||||||
|
const E = MyEnum
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Local constant "E" is not resolved at this point.
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
enum MyEnum {}
|
||||||
|
|
||||||
|
func test():
|
||||||
|
var E = MyEnum
|
||||||
|
var e: E
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Local variable "E" cannot be used as a type.
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
enum MyEnum {A}
|
||||||
|
|
||||||
|
func test():
|
||||||
|
const E = MyEnum.A
|
||||||
|
var e: E
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
GDTEST_ANALYZER_ERROR
|
||||||
|
Local constant "E" is not a valid type.
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
class InnerClass:
|
||||||
|
enum InnerEnum {A = 2}
|
||||||
|
const INNER_CONST = "INNER_CONST"
|
||||||
|
|
||||||
|
enum Enum {A = 1}
|
||||||
|
|
||||||
|
const Other = preload("./local_const_as_type.notest.gd")
|
||||||
|
|
||||||
|
func test():
|
||||||
|
const IC = InnerClass
|
||||||
|
const IE = IC.InnerEnum
|
||||||
|
const E = Enum
|
||||||
|
# Doesn't work in CI, but works in the editor. Looks like an unrelated bug. TODO: Investigate it.
|
||||||
|
# Error: Invalid call. Nonexistent function 'new' in base 'GDScript'.
|
||||||
|
var a1: IC = null # IC.new()
|
||||||
|
var a2: IE = IE.A
|
||||||
|
var a3: IC.InnerEnum = IE.A
|
||||||
|
var a4: E = E.A
|
||||||
|
print(a1.INNER_CONST)
|
||||||
|
print(a2)
|
||||||
|
print(a3)
|
||||||
|
print(a4)
|
||||||
|
|
||||||
|
const O = Other
|
||||||
|
const OV: Variant = Other # Removes metatype.
|
||||||
|
const OIC = O.InnerClass
|
||||||
|
const OIE = OIC.InnerEnum
|
||||||
|
const OE = O.Enum
|
||||||
|
var b: O = O.new()
|
||||||
|
@warning_ignore("unsafe_method_access")
|
||||||
|
var bv: OV = OV.new()
|
||||||
|
var b1: OIC = OIC.new()
|
||||||
|
var b2: OIE = OIE.A
|
||||||
|
var b3: O.InnerClass.InnerEnum = OIE.A
|
||||||
|
var b4: OE = OE.A
|
||||||
|
print(b.CONST)
|
||||||
|
print(bv.CONST)
|
||||||
|
print(b1.INNER_CONST)
|
||||||
|
print(b2)
|
||||||
|
print(b3)
|
||||||
|
print(b4)
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
class InnerClass:
|
||||||
|
enum InnerEnum {A = 20}
|
||||||
|
const INNER_CONST = "OTHER_INNER_CONST"
|
||||||
|
|
||||||
|
enum Enum {A = 10}
|
||||||
|
const CONST = "OTHER_CONST"
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
GDTEST_OK
|
||||||
|
INNER_CONST
|
||||||
|
2
|
||||||
|
2
|
||||||
|
1
|
||||||
|
OTHER_CONST
|
||||||
|
OTHER_CONST
|
||||||
|
OTHER_INNER_CONST
|
||||||
|
20
|
||||||
|
20
|
||||||
|
10
|
||||||
Loading…
Add table
Add a link
Reference in a new issue