Skip to content

Commit

Permalink
Merge pull request #84043 from dalexeev/gds-fix-unsafe-cast-warning
Browse files Browse the repository at this point in the history
GDScript: Fix `UNSAFE_CAST` warning
  • Loading branch information
akien-mga committed Apr 9, 2024
2 parents a7b8602 + 6e996a5 commit f8ca571
Show file tree
Hide file tree
Showing 22 changed files with 150 additions and 6 deletions.
2 changes: 1 addition & 1 deletion doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when using an expression whose type may not be compatible with the function parameter expected.
</member>
<member name="debug/gdscript/warnings/unsafe_cast" type="int" setter="" getter="" default="0">
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when performing an unsafe cast.
When set to [code]warn[/code] or [code]error[/code], produces a warning or an error respectively when a [Variant] value is cast to a non-Variant.
</member>
<member name="debug/gdscript/warnings/unsafe_method_access" 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 method whose presence is not guaranteed at compile-time in the class.
Expand Down
4 changes: 1 addition & 3 deletions modules/gdscript/gdscript_analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3439,9 +3439,7 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
if (op_type.is_variant() || !op_type.is_hard_type()) {
mark_node_unsafe(p_cast);
#ifdef DEBUG_ENABLED
if (op_type.is_variant() && !op_type.is_hard_type()) {
parser->push_warning(p_cast, GDScriptWarning::UNSAFE_CAST, cast_type.to_string());
}
parser->push_warning(p_cast, GDScriptWarning::UNSAFE_CAST, cast_type.to_string());
#endif
} else {
bool valid = false;
Expand Down
2 changes: 1 addition & 1 deletion modules/gdscript/gdscript_warning.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ String GDScriptWarning::get_message() const {
return vformat(R"*(The method "%s()" is not present on the inferred type "%s" (but may be present on a subtype).)*", symbols[0], symbols[1]);
case UNSAFE_CAST:
CHECK_SYMBOLS(1);
return vformat(R"(The value is cast to "%s" but has an unknown type.)", symbols[0]);
return vformat(R"(Casting "Variant" to "%s" is unsafe.)", symbols[0]);
case UNSAFE_CALL_ARGUMENT:
CHECK_SYMBOLS(5);
return vformat(R"*(The argument %s of the %s "%s()" requires the subtype "%s" but the supertype "%s" was provided.)*", symbols[0], symbols[1], symbols[2], symbols[3], symbols[4]);
Expand Down
2 changes: 1 addition & 1 deletion modules/gdscript/gdscript_warning.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class GDScriptWarning {
INFERRED_DECLARATION, // Variable/constant/parameter has an implicitly inferred static type.
UNSAFE_PROPERTY_ACCESS, // Property not found in the detected type (but can be in subtypes).
UNSAFE_METHOD_ACCESS, // Function not found in the detected type (but can be in subtypes).
UNSAFE_CAST, // Cast used in an unknown type.
UNSAFE_CAST, // Casting a `Variant` value to non-`Variant`.
UNSAFE_CALL_ARGUMENT, // Function call argument is of a supertype of the required type.
UNSAFE_VOID_RETURN, // Function returns void but returned a call to a function that can't be type checked.
RETURN_VALUE_DISCARDED, // Function call returns something but the value isn't used.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
func test():
var integer := 1
print(integer as Array)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Invalid cast. Cannot convert from "int" to "Array".
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
func test():
var integer := 1
print(integer as Node)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Invalid cast. Cannot convert from "int" to "Node".
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
func test():
var object := RefCounted.new()
print(object as int)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Invalid cast. Cannot convert from "RefCounted" to "int".
24 changes: 24 additions & 0 deletions modules/gdscript/tests/scripts/analyzer/warnings/unsafe_cast.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# We don't want to execute it because of errors, just analyze.
func no_exec_test():
var weak_int = 1
print(weak_int as Variant) # No warning.
print(weak_int as int)
print(weak_int as Node)

var weak_node = Node.new()
print(weak_node as Variant) # No warning.
print(weak_node as int)
print(weak_node as Node)

var weak_variant = null
print(weak_variant as Variant) # No warning.
print(weak_variant as int)
print(weak_variant as Node)

var hard_variant: Variant = null
print(hard_variant as Variant) # No warning.
print(hard_variant as int)
print(hard_variant as Node)

func test():
pass
33 changes: 33 additions & 0 deletions modules/gdscript/tests/scripts/analyzer/warnings/unsafe_cast.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
GDTEST_OK
>> WARNING
>> Line: 5
>> UNSAFE_CAST
>> Casting "Variant" to "int" is unsafe.
>> WARNING
>> Line: 6
>> UNSAFE_CAST
>> Casting "Variant" to "Node" is unsafe.
>> WARNING
>> Line: 10
>> UNSAFE_CAST
>> Casting "Variant" to "int" is unsafe.
>> WARNING
>> Line: 11
>> UNSAFE_CAST
>> Casting "Variant" to "Node" is unsafe.
>> WARNING
>> Line: 15
>> UNSAFE_CAST
>> Casting "Variant" to "int" is unsafe.
>> WARNING
>> Line: 16
>> UNSAFE_CAST
>> Casting "Variant" to "Node" is unsafe.
>> WARNING
>> Line: 20
>> UNSAFE_CAST
>> Casting "Variant" to "int" is unsafe.
>> WARNING
>> Line: 21
>> UNSAFE_CAST
>> Casting "Variant" to "Node" is unsafe.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
func test():
var node := Node.new()
node.free()
print(node as Node2D)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/cast_freed_object.gd
>> 4
>> Trying to cast a freed object.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
func test():
var integer: Variant = 1
@warning_ignore("unsafe_cast")
print(integer as Array)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/cast_int_to_array.gd
>> 4
>> Invalid cast: could not convert value to 'Array'.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
func test():
var integer: Variant = 1
@warning_ignore("unsafe_cast")
print(integer as Node)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/cast_int_to_object.gd
>> 4
>> Invalid cast: can't convert a non-object value to an object type.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
func test():
var object: Variant = RefCounted.new()
@warning_ignore("unsafe_cast")
print(object as int)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GDTEST_RUNTIME_ERROR
>> SCRIPT ERROR
>> on function: test()
>> runtime/errors/cast_object_to_int.gd
>> 4
>> Invalid cast: could not convert value to 'int'.
24 changes: 24 additions & 0 deletions modules/gdscript/tests/scripts/runtime/features/type_casting.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
func print_value(value: Variant) -> void:
if value is Object:
@warning_ignore("unsafe_method_access")
print("<%s>" % value.get_class())
else:
print(var_to_str(value))

func test():
var int_value := 1
print_value(int_value as Variant)
print_value(int_value as int)
print_value(int_value as float)

var node_value := Node.new()
print_value(node_value as Variant)
print_value(node_value as Object)
print_value(node_value as Node)
print_value(node_value as Node2D)
node_value.free()

var null_value = null
print_value(null_value as Variant)
@warning_ignore("unsafe_cast")
print_value(null_value as Node)
10 changes: 10 additions & 0 deletions modules/gdscript/tests/scripts/runtime/features/type_casting.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
GDTEST_OK
1
1
1.0
<Node>
<Node>
<Node>
null
null
null

0 comments on commit f8ca571

Please sign in to comment.