summaryrefslogtreecommitdiffstats
path: root/modules/gdscript/gdscript_analyzer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript_analyzer.cpp')
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp159
1 files changed, 117 insertions, 42 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 4a3a3a4b61..af92450835 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -169,7 +169,9 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_n
GDScriptParser::DataType type = make_enum_type(p_enum_name, native_base, p_meta);
if (p_meta) {
- type.builtin_type = Variant::NIL; // Native enum types are not Dictionaries.
+ // Native enum types are not dictionaries.
+ type.builtin_type = Variant::NIL;
+ type.is_pseudo_type = true;
}
List<StringName> enum_values;
@@ -182,10 +184,29 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_enum_n
return type;
}
+static GDScriptParser::DataType make_builtin_enum_type(const StringName &p_enum_name, Variant::Type p_type, bool p_meta = true) {
+ GDScriptParser::DataType type = make_enum_type(p_enum_name, Variant::get_type_name(p_type), p_meta);
+ if (p_meta) {
+ // Built-in enum types are not dictionaries.
+ type.builtin_type = Variant::NIL;
+ type.is_pseudo_type = true;
+ }
+
+ List<StringName> enum_values;
+ Variant::get_enumerations_for_enum(p_type, p_enum_name, &enum_values);
+
+ for (const StringName &E : enum_values) {
+ type.enum_values[E] = Variant::get_enum_value(p_type, p_enum_name, E);
+ }
+
+ return type;
+}
+
static GDScriptParser::DataType make_global_enum_type(const StringName &p_enum_name, const StringName &p_base, bool p_meta = true) {
GDScriptParser::DataType type = make_enum_type(p_enum_name, p_base, p_meta);
if (p_meta) {
- type.builtin_type = Variant::NIL; // Native enum types are not Dictionaries.
+ // Global enum types are not dictionaries.
+ type.builtin_type = Variant::NIL;
type.is_pseudo_type = true;
}
@@ -703,8 +724,8 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
if (first == SNAME("Variant")) {
if (p_type->type_chain.size() == 2) {
// May be nested enum.
- StringName enum_name = p_type->type_chain[1]->name;
- StringName qualified_name = String(first) + ENUM_SEPARATOR + String(p_type->type_chain[1]->name);
+ const StringName enum_name = p_type->type_chain[1]->name;
+ const StringName qualified_name = String(first) + ENUM_SEPARATOR + String(p_type->type_chain[1]->name);
if (CoreConstants::is_global_enum(qualified_name)) {
result = make_global_enum_type(enum_name, first, true);
return result;
@@ -719,21 +740,34 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result.kind = GDScriptParser::DataType::VARIANT;
} else if (GDScriptParser::get_builtin_type(first) < Variant::VARIANT_MAX) {
// Built-in types.
- if (p_type->type_chain.size() > 1) {
- push_error(R"(Built-in types don't contain nested types.)", p_type->type_chain[1]);
+ const Variant::Type builtin_type = GDScriptParser::get_builtin_type(first);
+
+ if (p_type->type_chain.size() == 2) {
+ // May be nested enum.
+ const StringName enum_name = p_type->type_chain[1]->name;
+ if (Variant::has_enum(builtin_type, enum_name)) {
+ result = make_builtin_enum_type(enum_name, builtin_type, true);
+ return result;
+ } else {
+ push_error(vformat(R"(Name "%s" is not a nested type of "%s".)", enum_name, first), p_type->type_chain[1]);
+ return bad_type;
+ }
+ } else if (p_type->type_chain.size() > 2) {
+ push_error(R"(Built-in types only contain enum types, which do not have nested types.)", p_type->type_chain[2]);
return bad_type;
}
+
result.kind = GDScriptParser::DataType::BUILTIN;
- result.builtin_type = GDScriptParser::get_builtin_type(first);
+ result.builtin_type = builtin_type;
- if (result.builtin_type == Variant::ARRAY) {
+ if (builtin_type == Variant::ARRAY) {
GDScriptParser::DataType container_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(0)));
if (container_type.kind != GDScriptParser::DataType::VARIANT) {
container_type.is_constant = false;
result.set_container_element_type(0, container_type);
}
}
- if (result.builtin_type == Variant::DICTIONARY) {
+ if (builtin_type == Variant::DICTIONARY) {
GDScriptParser::DataType key_type = type_from_metatype(resolve_datatype(p_type->get_container_type_or_null(0)));
if (key_type.kind != GDScriptParser::DataType::VARIANT) {
key_type.is_constant = false;
@@ -940,8 +974,8 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
Finally finally([&]() {
ensure_cached_external_parser_for_class(member.get_datatype().class_type, p_class, "Trying to resolve datatype of class member", p_source);
GDScriptParser::DataType member_type = member.get_datatype();
- if (member_type.has_container_element_type(0)) {
- ensure_cached_external_parser_for_class(member_type.get_container_element_type(0).class_type, p_class, "Trying to resolve datatype of class member", p_source);
+ for (int i = 0; i < member_type.get_container_element_type_count(); ++i) {
+ ensure_cached_external_parser_for_class(member_type.get_container_element_type(i).class_type, p_class, "Trying to resolve datatype of class member", p_source);
}
});
@@ -3816,6 +3850,12 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str
}
Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_external_parser_for_class(const GDScriptParser::ClassNode *p_class, const GDScriptParser::ClassNode *p_from_class, const char *p_context, const GDScriptParser::Node *p_source) {
+ // Delicate piece of code that intentionally doesn't use the GDScript cache or `get_depended_parser_for`.
+ // Search dependencies for the parser that owns `p_class` and make a cache entry for it.
+ // Required for how we store pointers to classes owned by other parser trees and need to call `resolve_class_member` and such on the same parser tree.
+ // Since https://github.com/godotengine/godot/pull/94871 there can technically be multiple parsers for the same script in the same parser tree.
+ // Even if unlikely, getting the wrong parser could lead to strange undefined behavior without errors.
+
if (p_class == nullptr) {
return nullptr;
}
@@ -3832,8 +3872,6 @@ Ref<GDScriptParserRef> GDScriptAnalyzer::ensure_cached_external_parser_for_class
p_from_class = parser->head;
}
- String script_path = p_class->get_datatype().script_path;
-
Ref<GDScriptParserRef> parser_ref;
for (const GDScriptParser::ClassNode *look_class = p_from_class; look_class != nullptr; look_class = look_class->base_type.class_type) {
if (parser->has_class(look_class)) {
@@ -3966,13 +4004,36 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if (base.kind == GDScriptParser::DataType::BUILTIN) {
if (base.is_meta_type) {
- bool valid = true;
- Variant result = Variant::get_constant_value(base.builtin_type, name, &valid);
- if (valid) {
+ bool valid = false;
+
+ if (Variant::has_constant(base.builtin_type, name)) {
+ valid = true;
+
+ const Variant constant_value = Variant::get_constant_value(base.builtin_type, name);
+
p_identifier->is_constant = true;
- p_identifier->reduced_value = result;
- p_identifier->set_datatype(type_from_variant(result, p_identifier));
- } else if (base.is_hard_type()) {
+ p_identifier->reduced_value = constant_value;
+ p_identifier->set_datatype(type_from_variant(constant_value, p_identifier));
+ }
+
+ if (!valid) {
+ const StringName enum_name = Variant::get_enum_for_enumeration(base.builtin_type, name);
+ if (enum_name != StringName()) {
+ valid = true;
+
+ p_identifier->is_constant = true;
+ p_identifier->reduced_value = Variant::get_enum_value(base.builtin_type, enum_name, name);
+ p_identifier->set_datatype(make_builtin_enum_type(enum_name, base.builtin_type, false));
+ }
+ }
+
+ if (!valid && Variant::has_enum(base.builtin_type, name)) {
+ valid = true;
+
+ p_identifier->set_datatype(make_builtin_enum_type(name, base.builtin_type, true));
+ }
+
+ if (!valid && base.is_hard_type()) {
#ifdef SUGGEST_GODOT4_RENAMES
String rename_hint;
if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::RENAMED_IN_GODOT_4_HINT)).booleanize()) {
@@ -3981,9 +4042,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
}
}
- push_error(vformat(R"(Cannot find constant "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
+ push_error(vformat(R"(Cannot find member "%s" in base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
#else
- push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier);
+ push_error(vformat(R"(Cannot find member "%s" in base "%s".)", name, base.to_string()), p_identifier);
#endif // SUGGEST_GODOT4_RENAMES
}
} else {
@@ -4025,9 +4086,9 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
}
}
- push_error(vformat(R"(Cannot find property "%s" on base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
+ push_error(vformat(R"(Cannot find member "%s" in base "%s".%s)", name, base.to_string(), rename_hint), p_identifier);
#else
- push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
+ push_error(vformat(R"(Cannot find member "%s" in base "%s".)", name, base.to_string()), p_identifier);
#endif // SUGGEST_GODOT4_RENAMES
}
}
@@ -5568,7 +5629,7 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
} else {
Vector<String> names = String(p_property.class_name).split(ENUM_SEPARATOR);
if (names.size() == 2) {
- result = make_native_enum_type(names[1], names[0], false);
+ result = make_enum_type(names[1], names[0], false);
result.is_constant = false;
}
}
@@ -5805,8 +5866,6 @@ void GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
#ifdef DEBUG_ENABLED
void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier, const String &p_context, const bool p_in_local_scope) {
const StringName &name = p_identifier->name;
- GDScriptParser::DataType base = parser->current_class->get_datatype();
- GDScriptParser::ClassNode *base_class = base.class_type;
{
List<MethodInfo> gdscript_funcs;
@@ -5834,40 +5893,56 @@ void GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_identifier
}
}
+ const GDScriptParser::DataType current_class_type = parser->current_class->get_datatype();
if (p_in_local_scope) {
- while (base_class != nullptr) {
+ GDScriptParser::ClassNode *base_class = current_class_type.class_type;
+
+ if (base_class != nullptr) {
if (base_class->has_member(name)) {
parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE, p_context, p_identifier->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()));
return;
}
base_class = base_class->base_type.class_type;
}
+
+ while (base_class != nullptr) {
+ if (base_class->has_member(name)) {
+ String base_class_name = base_class->get_global_name();
+ if (base_class_name.is_empty()) {
+ base_class_name = base_class->fqcn;
+ }
+
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, base_class->get_member(name).get_type_name(), itos(base_class->get_member(name).get_line()), base_class_name);
+ return;
+ }
+ base_class = base_class->base_type.class_type;
+ }
}
- StringName parent = base.native_type;
- while (parent != StringName()) {
- ERR_FAIL_COND_MSG(!class_exists(parent), "Non-existent native base class.");
+ StringName native_base_class = current_class_type.native_type;
+ while (native_base_class != StringName()) {
+ ERR_FAIL_COND_MSG(!class_exists(native_base_class), "Non-existent native base class.");
- if (ClassDB::has_method(parent, name, true)) {
- parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "method", parent);
+ if (ClassDB::has_method(native_base_class, name, true)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "method", native_base_class);
return;
- } else if (ClassDB::has_signal(parent, name, true)) {
- parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "signal", parent);
+ } else if (ClassDB::has_signal(native_base_class, name, true)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "signal", native_base_class);
return;
- } else if (ClassDB::has_property(parent, name, true)) {
- parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "property", parent);
+ } else if (ClassDB::has_property(native_base_class, name, true)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "property", native_base_class);
return;
- } else if (ClassDB::has_integer_constant(parent, name, true)) {
- parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "constant", parent);
+ } else if (ClassDB::has_integer_constant(native_base_class, name, true)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "constant", native_base_class);
return;
- } else if (ClassDB::has_enum(parent, name, true)) {
- parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "enum", parent);
+ } else if (ClassDB::has_enum(native_base_class, name, true)) {
+ parser->push_warning(p_identifier, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_identifier->name, "enum", native_base_class);
return;
}
- parent = ClassDB::get_parent_class(parent);
+ native_base_class = ClassDB::get_parent_class(native_base_class);
}
}
-#endif
+#endif // DEBUG_ENABLED
GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source) {
// Unary version.