Merge pull request #84043 from dalexeev/gds-fix-unsafe-cast-warning

GDScript: Fix `UNSAFE_CAST` warning
This commit is contained in:
Rémi Verschelde 2024-04-09 22:24:55 +02:00
commit f8ca571efe
No known key found for this signature in database
GPG key ID: C3336907360768E1
22 changed files with 150 additions and 6 deletions

View file

@ -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.

View file

@ -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());
}
#endif
} else {
bool valid = false;

View file

@ -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]);

View file

@ -65,7 +65,7 @@ public:
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.

View file

@ -0,0 +1,3 @@
func test():
var integer := 1
print(integer as Array)

View file

@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Invalid cast. Cannot convert from "int" to "Array".

View file

@ -0,0 +1,3 @@
func test():
var integer := 1
print(integer as Node)

View file

@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Invalid cast. Cannot convert from "int" to "Node".

View file

@ -0,0 +1,3 @@
func test():
var object := RefCounted.new()
print(object as int)

View file

@ -0,0 +1,2 @@
GDTEST_ANALYZER_ERROR
Invalid cast. Cannot convert from "RefCounted" to "int".

View file

@ -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

View file

@ -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.

View file

@ -0,0 +1,4 @@
func test():
var node := Node.new()
node.free()
print(node as Node2D)

View file

@ -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.

View file

@ -0,0 +1,4 @@
func test():
var integer: Variant = 1
@warning_ignore("unsafe_cast")
print(integer as Array)

View file

@ -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'.

View file

@ -0,0 +1,4 @@
func test():
var integer: Variant = 1
@warning_ignore("unsafe_cast")
print(integer as Node)

View file

@ -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.

View file

@ -0,0 +1,4 @@
func test():
var object: Variant = RefCounted.new()
@warning_ignore("unsafe_cast")
print(object as int)

View file

@ -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'.

View file

@ -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)

View file

@ -0,0 +1,10 @@
GDTEST_OK
1
1
1.0
<Node>
<Node>
<Node>
null
null
null