summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp118
-rw-r--r--modules/gdscript/gdscript_analyzer.h11
-rw-r--r--modules/gdscript/gdscript_warning.cpp7
-rw-r--r--modules/gdscript/gdscript_warning.h1
4 files changed, 136 insertions, 1 deletions
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index f788236af3..b8702c57d0 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -2464,6 +2464,62 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
p_binary_op->set_datatype(result);
}
+#ifdef TOOLS_ENABLED
+#ifndef DISABLE_DEPRECATED
+const char *GDScriptAnalyzer::get_rename_from_map(const char *map[][2], String key) {
+ for (int index = 0; map[index][0]; index++) {
+ if (map[index][0] == key) {
+ return map[index][1];
+ }
+ }
+ return nullptr;
+}
+
+// Checks if an identifier/function name has been renamed in Godot 4, uses ProjectConverter3To4 for rename map.
+// Returns the new name if found, nullptr otherwise.
+const char *GDScriptAnalyzer::check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type) {
+ switch (type) {
+ case GDScriptParser::Node::IDENTIFIER: {
+ // Check properties
+ const char *result = get_rename_from_map(ProjectConverter3To4::gdscript_properties_renames, identifier);
+ if (result) {
+ return result;
+ }
+ // Check enum values
+ result = get_rename_from_map(ProjectConverter3To4::enum_renames, identifier);
+ if (result) {
+ return result;
+ }
+ // Check color constants
+ result = get_rename_from_map(ProjectConverter3To4::color_renames, identifier);
+ if (result) {
+ return result;
+ }
+ // Check type names
+ result = get_rename_from_map(ProjectConverter3To4::class_renames, identifier);
+ if (result) {
+ return result;
+ }
+ return get_rename_from_map(ProjectConverter3To4::builtin_types_renames, identifier);
+ }
+ case GDScriptParser::Node::CALL: {
+ const char *result = get_rename_from_map(ProjectConverter3To4::gdscript_function_renames, identifier);
+ if (result) {
+ return result;
+ }
+ // Built-in Types are mistaken for function calls when the built-in type is not found.
+ // Check built-in types if function rename not found
+ return get_rename_from_map(ProjectConverter3To4::builtin_types_renames, identifier);
+ }
+ // Signal references don't get parsed through the GDScriptAnalyzer. No support for signal rename hints.
+ default:
+ // No rename found, return null
+ return nullptr;
+ }
+}
+#endif // DISABLE_DEPRECATED
+#endif // TOOLS_ENABLED
+
void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_await, bool p_is_root) {
bool all_is_constant = true;
HashMap<int, GDScriptParser::ArrayNode *> arrays; // For array literal to potentially type when passing.
@@ -2881,7 +2937,22 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
}
if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) {
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
+#ifdef TOOLS_ENABLED
+#ifndef DISABLE_DEPRECATED
+ String rename_hint = String();
+ if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
+ const char *renamed_function_name = check_for_renamed_identifier(p_call->function_name, p_call->type);
+ if (renamed_function_name) {
+ rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", String(renamed_function_name) + "()");
+ }
+ }
+ push_error(vformat(R"*(Function "%s()" not found in base %s.%s)*", p_call->function_name, base_name, rename_hint), p_call->is_super ? p_call : p_call->callee);
+#else // !DISABLE_DEPRECATED
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
+#endif // DISABLE_DEPRECATED
+#else
+ push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
+#endif
} else if (!found && (!p_call->is_super && base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::NATIVE && base_type.is_meta_type)) {
push_error(vformat(R"*(Static function "%s()" not found in base "%s".)*", p_call->function_name, base_type.native_type), p_call);
}
@@ -3071,7 +3142,22 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
p_identifier->reduced_value = result;
p_identifier->set_datatype(type_from_variant(result, p_identifier));
} else if (base.is_hard_type()) {
- push_error(vformat(R"(Cannot find constant "%s" on type "%s".)", name, base.to_string()), p_identifier);
+#ifdef TOOLS_ENABLED
+#ifndef DISABLE_DEPRECATED
+ String rename_hint = String();
+ if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
+ const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
+ if (renamed_identifier_name) {
+ 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);
+#else // !DISABLE_DEPRECATED
+ push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier);
+#endif // DISABLE_DEPRECATED
+#else
+ push_error(vformat(R"(Cannot find constant "%s" on base "%s".)", name, base.to_string()), p_identifier);
+#endif
}
} else {
switch (base.builtin_type) {
@@ -3100,7 +3186,22 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
}
if (base.is_hard_type()) {
+#ifdef TOOLS_ENABLED
+#ifndef DISABLE_DEPRECATED
+ String rename_hint = String();
+ if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
+ const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
+ if (renamed_identifier_name) {
+ 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);
+#else // !DISABLE_DEPRECATED
+ push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
+#endif // DISABLE_DEPRECATED
+#else
push_error(vformat(R"(Cannot find property "%s" on base "%s".)", name, base.to_string()), p_identifier);
+#endif
}
}
}
@@ -3439,7 +3540,22 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
if (GDScriptUtilityFunctions::function_exists(name)) {
push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier);
} else {
+#ifdef TOOLS_ENABLED
+#ifndef DISABLE_DEPRECATED
+ String rename_hint = String();
+ if (GLOBAL_GET(GDScriptWarning::get_settings_path_from_code(GDScriptWarning::Code::RENAMED_IN_GD4_HINT)).booleanize()) {
+ const char *renamed_identifier_name = check_for_renamed_identifier(name, p_identifier->type);
+ if (renamed_identifier_name) {
+ rename_hint = " " + vformat(R"(Did you mean to use "%s"?)", renamed_identifier_name);
+ }
+ }
+ push_error(vformat(R"(Identifier "%s" not declared in the current scope.%s)", name, rename_hint), p_identifier);
+#else // !DISABLE_DEPRECATED
push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
+#endif // DISABLE_DEPRECATED
+#else
+ push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
+#endif
}
GDScriptParser::DataType dummy;
dummy.kind = GDScriptParser::DataType::VARIANT;
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index 613399fb40..b51564fb0a 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -37,6 +37,10 @@
#include "gdscript_cache.h"
#include "gdscript_parser.h"
+#ifdef TOOLS_ENABLED
+#include "editor/project_converter_3_to_4.h"
+#endif
+
class GDScriptAnalyzer {
GDScriptParser *parser = nullptr;
HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
@@ -133,6 +137,13 @@ class GDScriptAnalyzer {
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
#endif
+#ifdef TOOLS_ENABLED
+#ifndef DISABLE_DEPRECATED
+ const char *get_rename_from_map(const char *map[][2], String key);
+ const char *check_for_renamed_identifier(String identifier, GDScriptParser::Node::Type type);
+#endif // DISABLE_DEPRECATED
+#endif // TOOLS_ENABLED
+
public:
Error resolve_inheritance();
Error resolve_interface();
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 024fed8517..169450165e 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -163,6 +163,9 @@ String GDScriptWarning::get_message() const {
CHECK_SYMBOLS(1);
return vformat(R"(The identifier "%s" has misleading characters and might be confused with something else.)", symbols[0]);
}
+ case RENAMED_IN_GD4_HINT: {
+ break; // Renamed identifier hint is taken care of by the GDScriptAnalyzer. No message needed here.
+ }
case WARNING_MAX:
break; // Can't happen, but silences warning
}
@@ -184,6 +187,9 @@ int GDScriptWarning::get_default_value(Code p_code) {
PropertyInfo GDScriptWarning::get_property_info(Code p_code) {
// Making this a separate function in case a warning needs different PropertyInfo in the future.
+ if (p_code == Code::RENAMED_IN_GD4_HINT) {
+ return PropertyInfo(Variant::BOOL, get_settings_path_from_code(p_code));
+ }
return PropertyInfo(Variant::INT, get_settings_path_from_code(p_code), PROPERTY_HINT_ENUM, "Ignore,Warn,Error");
}
@@ -229,6 +235,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"INT_AS_ENUM_WITHOUT_MATCH",
"STATIC_CALLED_ON_INSTANCE",
"CONFUSABLE_IDENTIFIER",
+ "RENAMED_IN_GODOT_4_HINT"
};
static_assert((sizeof(names) / sizeof(*names)) == WARNING_MAX, "Amount of warning types don't match the amount of warning names.");
diff --git a/modules/gdscript/gdscript_warning.h b/modules/gdscript/gdscript_warning.h
index 7492972c1a..516d69f6ff 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -80,6 +80,7 @@ public:
INT_AS_ENUM_WITHOUT_MATCH, // An integer value was used as an enum value without matching enum member.
STATIC_CALLED_ON_INSTANCE, // A static method was called on an instance of a class instead of on the class itself.
CONFUSABLE_IDENTIFIER, // The identifier contains misleading characters that can be confused. E.g. "usеr" (has Cyrillic "е" instead of Latin "e").
+ RENAMED_IN_GD4_HINT, // A variable or function that could not be found has been renamed in Godot 4
WARNING_MAX,
};