From 0a51bb4ca54fa9fd15b40dac28476eae6ff0c816 Mon Sep 17 00:00:00 2001 From: clayjohn Date: Thu, 13 Oct 2022 12:42:11 -0700 Subject: [PATCH] Add STATIC_CALLED_ON_INSTANCE warning to highlight when static functions are called directly from objects --- doc/classes/ProjectSettings.xml | 3 +++ modules/gdscript/gdscript_analyzer.cpp | 4 ++++ modules/gdscript/gdscript_warning.cpp | 5 +++++ modules/gdscript/gdscript_warning.h | 1 + .../parser/warnings/static_called_on_instance.gd | 11 +++++++++++ .../parser/warnings/static_called_on_instance.out | 7 +++++++ 6 files changed, 31 insertions(+) create mode 100644 modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd create mode 100644 modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 4b2bb3b6e911..ff66affeabd6 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -413,6 +413,9 @@ When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a ternary expression that has no effect on the surrounding code, such as writing [code]42 if active else 0[/code] as a statement. + + When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when calling a static method from an instance of a class instead of from the class directly. + If [code]true[/code], all warnings will be reported as if they are errors. diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index b3ca8ff00af3..6fbdec863fc1 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -2552,6 +2552,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a if (p_is_root && return_type.kind != GDScriptParser::DataType::UNRESOLVED && return_type.builtin_type != Variant::NIL) { parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name); } + + if (is_static && !base_type.is_meta_type && !(callee_type != GDScriptParser::Node::SUBSCRIPT && parser->current_function != nullptr && parser->current_function->is_static)) { + parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, base_type.to_string()); + } #endif // DEBUG_ENABLED call_type = return_type; diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp index 1cae7bdfac06..a0c107aa538b 100644 --- a/modules/gdscript/gdscript_warning.cpp +++ b/modules/gdscript/gdscript_warning.cpp @@ -155,6 +155,10 @@ String GDScriptWarning::get_message() const { case INT_ASSIGNED_TO_ENUM: { return "Integer used when an enum value is expected. If this is intended cast the integer to the enum type."; } + case STATIC_CALLED_ON_INSTANCE: { + CHECK_SYMBOLS(2); + return vformat(R"(The function '%s()' is a static function but was called from an instance. Instead, it should be directly called from the type: '%s.%s()'.)", symbols[0], symbols[1], symbols[0]); + } case WARNING_MAX: break; // Can't happen, but silences warning } @@ -215,6 +219,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) { "EMPTY_FILE", "SHADOWED_GLOBAL_IDENTIFIER", "INT_ASSIGNED_TO_ENUM", + "STATIC_CALLED_ON_INSTANCE", }; 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 a639e7b44e57..7e4e97551019 100644 --- a/modules/gdscript/gdscript_warning.h +++ b/modules/gdscript/gdscript_warning.h @@ -78,6 +78,7 @@ public: EMPTY_FILE, // A script file is empty. SHADOWED_GLOBAL_IDENTIFIER, // A global class or function has the same name as variable. 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. WARNING_MAX, }; diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd new file mode 100644 index 000000000000..29d8501b7894 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.gd @@ -0,0 +1,11 @@ +class Player: + var x = 3 + +func test(): + # These should not emit a warning. + var _player = Player.new() + print(String.num_uint64(8589934592)) # 2 ^ 33 + + # This should emit a warning. + var some_string = String() + print(some_string.num_uint64(8589934592)) # 2 ^ 33 diff --git a/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out new file mode 100644 index 000000000000..3933a35178d1 --- /dev/null +++ b/modules/gdscript/tests/scripts/parser/warnings/static_called_on_instance.out @@ -0,0 +1,7 @@ +GDTEST_OK +>> WARNING +>> Line: 11 +>> STATIC_CALLED_ON_INSTANCE +>> The function 'num_uint64()' is a static function but was called from an instance. Instead, it should be directly called from the type: 'String.num_uint64()'. +8589934592 +8589934592