Browse Source

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

GDScript: Fix `UNSAFE_CAST` warning
Rémi Verschelde 1 year ago
parent
commit
f8ca571efe
22 changed files with 150 additions and 6 deletions
  1. 1 1
      doc/classes/ProjectSettings.xml
  2. 1 3
      modules/gdscript/gdscript_analyzer.cpp
  3. 1 1
      modules/gdscript/gdscript_warning.cpp
  4. 1 1
      modules/gdscript/gdscript_warning.h
  5. 3 0
      modules/gdscript/tests/scripts/analyzer/errors/cast_int_to_array.gd
  6. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/cast_int_to_array.out
  7. 3 0
      modules/gdscript/tests/scripts/analyzer/errors/cast_int_to_object.gd
  8. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/cast_int_to_object.out
  9. 3 0
      modules/gdscript/tests/scripts/analyzer/errors/cast_object_to_int.gd
  10. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/cast_object_to_int.out
  11. 24 0
      modules/gdscript/tests/scripts/analyzer/warnings/unsafe_cast.gd
  12. 33 0
      modules/gdscript/tests/scripts/analyzer/warnings/unsafe_cast.out
  13. 4 0
      modules/gdscript/tests/scripts/runtime/errors/cast_freed_object.gd
  14. 6 0
      modules/gdscript/tests/scripts/runtime/errors/cast_freed_object.out
  15. 4 0
      modules/gdscript/tests/scripts/runtime/errors/cast_int_to_array.gd
  16. 6 0
      modules/gdscript/tests/scripts/runtime/errors/cast_int_to_array.out
  17. 4 0
      modules/gdscript/tests/scripts/runtime/errors/cast_int_to_object.gd
  18. 6 0
      modules/gdscript/tests/scripts/runtime/errors/cast_int_to_object.out
  19. 4 0
      modules/gdscript/tests/scripts/runtime/errors/cast_object_to_int.gd
  20. 6 0
      modules/gdscript/tests/scripts/runtime/errors/cast_object_to_int.out
  21. 24 0
      modules/gdscript/tests/scripts/runtime/features/type_casting.gd
  22. 10 0
      modules/gdscript/tests/scripts/runtime/features/type_casting.out

+ 1 - 1
doc/classes/ProjectSettings.xml

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

+ 1 - 3
modules/gdscript/gdscript_analyzer.cpp

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

+ 1 - 1
modules/gdscript/gdscript_warning.cpp

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

+ 1 - 1
modules/gdscript/gdscript_warning.h

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

+ 3 - 0
modules/gdscript/tests/scripts/analyzer/errors/cast_int_to_array.gd

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

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/errors/cast_int_to_array.out

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

+ 3 - 0
modules/gdscript/tests/scripts/analyzer/errors/cast_int_to_object.gd

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

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/errors/cast_int_to_object.out

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

+ 3 - 0
modules/gdscript/tests/scripts/analyzer/errors/cast_object_to_int.gd

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

+ 2 - 0
modules/gdscript/tests/scripts/analyzer/errors/cast_object_to_int.out

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

+ 24 - 0
modules/gdscript/tests/scripts/analyzer/warnings/unsafe_cast.gd

@@ -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 - 0
modules/gdscript/tests/scripts/analyzer/warnings/unsafe_cast.out

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

+ 4 - 0
modules/gdscript/tests/scripts/runtime/errors/cast_freed_object.gd

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

+ 6 - 0
modules/gdscript/tests/scripts/runtime/errors/cast_freed_object.out

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

+ 4 - 0
modules/gdscript/tests/scripts/runtime/errors/cast_int_to_array.gd

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

+ 6 - 0
modules/gdscript/tests/scripts/runtime/errors/cast_int_to_array.out

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

+ 4 - 0
modules/gdscript/tests/scripts/runtime/errors/cast_int_to_object.gd

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

+ 6 - 0
modules/gdscript/tests/scripts/runtime/errors/cast_int_to_object.out

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

+ 4 - 0
modules/gdscript/tests/scripts/runtime/errors/cast_object_to_int.gd

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

+ 6 - 0
modules/gdscript/tests/scripts/runtime/errors/cast_object_to_int.out

@@ -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 - 0
modules/gdscript/tests/scripts/runtime/features/type_casting.gd

@@ -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 - 0
modules/gdscript/tests/scripts/runtime/features/type_casting.out

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