Add hint for identifiers renamed since Godot 3

This commit is contained in:
jordi 2022-03-23 12:54:41 -05:00
parent 9de0c73e45
commit bb9a00889a
7 changed files with 172 additions and 16 deletions

View file

@ -423,6 +423,9 @@
<member name="debug/gdscript/warnings/redundant_await" type="int" setter="" getter="" default="1">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a function that is not a coroutine is called with await.
</member>
<member name="debug/gdscript/warnings/renamed_in_godot_4_hint" type="bool" setter="" getter="" default="1">
When enabled, using a property, enum, or function that was renamed since Godot 3 will produce a hint if an error occurs.
</member>
<member name="debug/gdscript/warnings/return_value_discarded" type="int" setter="" getter="" default="0">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a function without using its return value (by assigning it to a variable or using it as a function argument). Such return values are sometimes used to denote possible errors using the [enum Error] enum.
</member>

View file

@ -32,10 +32,11 @@
#include "modules/modules_enabled.gen.h"
const int ERROR_CODE = 77;
#ifndef DISABLE_DEPRECATED
#ifdef MODULE_REGEX_ENABLED
const int ERROR_CODE = 77;
#include "modules/regex/regex.h"
#include "core/io/dir_access.h"
@ -44,7 +45,7 @@ const int ERROR_CODE = 77;
#include "core/templates/list.h"
#include "core/templates/local_vector.h"
static const char *enum_renames[][2] = {
const char *ProjectConverter3To4::enum_renames[][2] = {
//// constants
{ "TYPE_COLOR_ARRAY", "TYPE_PACKED_COLOR_ARRAY" },
{ "TYPE_FLOAT64_ARRAY", "TYPE_PACKED_FLOAT64_ARRAY" },
@ -164,7 +165,7 @@ static const char *enum_renames[][2] = {
{ nullptr, nullptr },
};
static const char *gdscript_function_renames[][2] = {
const char *ProjectConverter3To4::gdscript_function_renames[][2] = {
// { "_set_name", "get_tracker_name"}, // XRPositionalTracker - CameraFeed use this
// { "_unhandled_input", "_unhandled_key_input"}, // BaseButton, ViewportContainer broke Node, FileDialog,SubViewportContainer
// { "create_gizmo", "_create_gizmo"}, // EditorNode3DGizmoPlugin - may be used
@ -620,7 +621,7 @@ static const char *gdscript_function_renames[][2] = {
};
// gdscript_function_renames clone with CamelCase
static const char *csharp_function_renames[][2] = {
const char *ProjectConverter3To4::csharp_function_renames[][2] = {
// { "_SetName", "GetTrackerName"}, // XRPositionalTracker - CameraFeed use this
// { "_UnhandledInput", "_UnhandledKeyInput"}, // BaseButton, ViewportContainer broke Node, FileDialog,SubViewportContainer
// { "CreateGizmo", "_CreateGizmo"}, // EditorNode3DGizmoPlugin - may be used
@ -1057,7 +1058,7 @@ static const char *csharp_function_renames[][2] = {
};
// Some needs to be disabled, because users can use this names as variables
static const char *gdscript_properties_renames[][2] = {
const char *ProjectConverter3To4::gdscript_properties_renames[][2] = {
// // { "d", "distance" }, //WorldMarginShape2D - TODO, looks that polish letters ą ę are treaten as space, not as letter, so `będą` are renamed to `będistanceą`
// // {"alt","alt_pressed"}, // This may broke a lot of comments and user variables
// // {"command","command_pressed"},// This may broke a lot of comments and user variables
@ -1173,7 +1174,7 @@ static const char *gdscript_properties_renames[][2] = {
};
// Some needs to be disabled, because users can use this names as variables
static const char *csharp_properties_renames[][2] = {
const char *ProjectConverter3To4::csharp_properties_renames[][2] = {
// // { "D", "Distance" }, //WorldMarginShape2D - TODO, looks that polish letters ą ę are treaten as space, not as letter, so `będą` are renamed to `będistanceą`
// // {"Alt","AltPressed"}, // This may broke a lot of comments and user variables
// // {"Command","CommandPressed"},// This may broke a lot of comments and user variables
@ -1278,7 +1279,7 @@ static const char *csharp_properties_renames[][2] = {
{ nullptr, nullptr },
};
static const char *gdscript_signals_renames[][2] = {
const char *ProjectConverter3To4::gdscript_signals_renames[][2] = {
// {"instantiate","instance"}, // FileSystemDock
// { "hide", "hidden" }, // CanvasItem - function with same name exists
// { "tween_all_completed","loop_finished"}, // Tween - TODO, not sure
@ -1302,7 +1303,7 @@ static const char *gdscript_signals_renames[][2] = {
{ nullptr, nullptr },
};
static const char *csharp_signals_renames[][2] = {
const char *ProjectConverter3To4::csharp_signals_renames[][2] = {
// {"Instantiate","Instance"}, // FileSystemDock
// { "Hide", "Hidden" }, // CanvasItem - function with same name exists
// { "TweenAllCompleted","LoopFinished"}, // Tween - TODO, not sure
@ -1326,7 +1327,7 @@ static const char *csharp_signals_renames[][2] = {
};
static const char *project_settings_renames[][2] = {
const char *ProjectConverter3To4::project_settings_renames[][2] = {
{ "audio/channel_disable_threshold_db", "audio/buses/channel_disable_threshold_db" },
{ "audio/channel_disable_time", "audio/buses/channel_disable_time" },
{ "audio/default_bus_layout", "audio/buses/default_bus_layout" },
@ -1370,7 +1371,7 @@ static const char *project_settings_renames[][2] = {
{ nullptr, nullptr },
};
static const char *input_map_renames[][2] = {
const char *ProjectConverter3To4::input_map_renames[][2] = {
{ ",\"alt\":", ",\"alt_pressed\":" },
{ ",\"shift\":", ",\"shift_pressed\":" },
{ ",\"control\":", ",\"ctrl_pressed\":" },
@ -1382,7 +1383,7 @@ static const char *input_map_renames[][2] = {
{ nullptr, nullptr },
};
static const char *builtin_types_renames[][2] = {
const char *ProjectConverter3To4::builtin_types_renames[][2] = {
{ "PoolByteArray", "PackedByteArray" },
{ "PoolColorArray", "PackedColorArray" },
{ "PoolIntArray", "PackedInt32Array" },
@ -1396,7 +1397,7 @@ static const char *builtin_types_renames[][2] = {
{ nullptr, nullptr },
};
static const char *shaders_renames[][2] = {
const char *ProjectConverter3To4::shaders_renames[][2] = {
{ "ALPHA_SCISSOR", "ALPHA_SCISSOR_THRESHOLD" },
{ "CAMERA_MATRIX", "INV_VIEW_MATRIX" },
{ "INV_CAMERA_MATRIX", "VIEW_MATRIX" },
@ -1414,7 +1415,7 @@ static const char *shaders_renames[][2] = {
{ nullptr, nullptr },
};
static const char *class_renames[][2] = {
const char *ProjectConverter3To4::class_renames[][2] = {
// { "BulletPhysicsDirectBodyState", "BulletPhysicsDirectBodyState3D" }, // Class is not visible in ClassDB
// { "BulletPhysicsServer", "BulletPhysicsServer3D" }, // Class is not visible in ClassDB
// { "GDScriptFunctionState", "Node3D" }, // TODO - not sure to which should be changed
@ -1639,7 +1640,7 @@ static const char *class_renames[][2] = {
{ nullptr, nullptr },
};
static const char *color_renames[][2] = {
const char *ProjectConverter3To4::ProjectConverter3To4::color_renames[][2] = {
{ "aliceblue", "ALICE_BLUE" },
{ "antiquewhite", "ANTIQUE_WHITE" },
{ "aqua", "AQUA" },
@ -4348,3 +4349,4 @@ int ProjectConverter3To4::validate_conversion() {
}
#endif // MODULE_REGEX_ENABLED
#endif // DISABLE_DEPRECATED

View file

@ -29,6 +29,7 @@
/**************************************************************************/
#ifndef PROJECT_CONVERTER_3_TO_4_H
#ifndef DISABLE_DEPRECATED
#define PROJECT_CONVERTER_3_TO_4_H
#include "core/io/file_access.h"
@ -41,6 +42,19 @@ class RegEx;
class ProjectConverter3To4 {
public:
class RegExContainer;
static const char *enum_renames[][2];
static const char *gdscript_function_renames[][2];
static const char *csharp_function_renames[][2];
static const char *gdscript_properties_renames[][2];
static const char *csharp_properties_renames[][2];
static const char *gdscript_signals_renames[][2];
static const char *csharp_signals_renames[][2];
static const char *project_settings_renames[][2];
static const char *input_map_renames[][2];
static const char *builtin_types_renames[][2];
static const char *shaders_renames[][2];
static const char *class_renames[][2];
static const char *color_renames[][2];
private:
uint64_t maximum_file_size;
@ -97,4 +111,6 @@ public:
int convert();
};
#endif // DISABLE_DEPRECATED
#endif // PROJECT_CONVERTER_3_TO_4_H

View file

@ -2364,6 +2364,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.
@ -2776,7 +2832,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);
}
@ -2988,7 +3059,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) {
@ -3017,7 +3103,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
}
}
}
@ -3356,7 +3457,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;

View file

@ -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;
@ -129,6 +133,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();

View file

@ -159,6 +159,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
}
@ -180,6 +183,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");
}
@ -224,6 +230,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"INT_ASSIGNED_TO_ENUM",
"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.");

View file

@ -79,6 +79,7 @@ public:
INT_ASSIGNED_TO_ENUM, // An integer value was assigned to an enum-typed variable without casting.
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,
};