Просмотр исходного кода

GDScript: Fix typing of iterator in for loop

Dmitrii Maganov 2 лет назад
Родитель
Сommit
40613ebd21
24 измененных файлов с 204 добавлено и 14 удалено
  1. 2 2
      core/variant/variant_setget.cpp
  2. 43 12
      modules/gdscript/gdscript_analyzer.cpp
  3. 6 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd
  4. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.out
  5. 6 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd
  6. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.out
  7. 6 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd
  8. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.out
  9. 6 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd
  10. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.out
  11. 6 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd
  12. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.out
  13. 14 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd
  14. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.out
  15. 6 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd
  16. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.out
  17. 3 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.gd
  18. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_bool.out
  19. 4 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd
  20. 2 0
      modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.out
  21. 15 0
      modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd
  22. 4 0
      modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out
  23. 51 0
      modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd
  24. 14 0
      modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out

+ 2 - 2
core/variant/variant_setget.cpp

@@ -1253,7 +1253,7 @@ bool Variant::iter_init(Variant &r_iter, bool &valid) const {
 			return _data._int > 0;
 		} break;
 		case FLOAT: {
-			r_iter = 0;
+			r_iter = 0.0;
 			return _data._float > 0.0;
 		} break;
 		case VECTOR2: {
@@ -1457,7 +1457,7 @@ bool Variant::iter_next(Variant &r_iter, bool &valid) const {
 			return true;
 		} break;
 		case FLOAT: {
-			int64_t idx = r_iter;
+			double idx = r_iter;
 			idx++;
 			if (idx >= _data._float) {
 				return false;

+ 43 - 12
modules/gdscript/gdscript_analyzer.cpp

@@ -1722,21 +1722,52 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
 	if (list_resolved) {
 		variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
 		variable_type.kind = GDScriptParser::DataType::BUILTIN;
-		variable_type.builtin_type = Variant::INT; // Can this ever be a float or something else?
-		p_for->variable->set_datatype(variable_type);
+		variable_type.builtin_type = Variant::INT;
 	} else if (p_for->list) {
 		resolve_node(p_for->list, false);
-		if (p_for->list->datatype.has_container_element_type()) {
-			variable_type = p_for->list->datatype.get_container_element_type();
-			variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
-		} else if (p_for->list->datatype.is_typed_container_type()) {
-			variable_type = p_for->list->datatype.get_typed_container_type();
-			variable_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
-		} else {
-			// Last resort
-			// TODO: Must other cases be handled? Must we mark as unsafe?
-			variable_type.type_source = GDScriptParser::DataType::UNDETECTED;
+		GDScriptParser::DataType list_type = p_for->list->get_datatype();
+		if (!list_type.is_hard_type()) {
+			mark_node_unsafe(p_for->list);
+		}
+		if (list_type.is_variant()) {
+			variable_type.kind = GDScriptParser::DataType::VARIANT;
+			mark_node_unsafe(p_for->list);
+		} else if (list_type.has_container_element_type()) {
+			variable_type = list_type.get_container_element_type();
+			variable_type.type_source = list_type.type_source;
+		} else if (list_type.is_typed_container_type()) {
+			variable_type = list_type.get_typed_container_type();
+			variable_type.type_source = list_type.type_source;
+		} else if (list_type.builtin_type == Variant::INT || list_type.builtin_type == Variant::FLOAT || list_type.builtin_type == Variant::STRING) {
+			variable_type.type_source = list_type.type_source;
+			variable_type.kind = GDScriptParser::DataType::BUILTIN;
+			variable_type.builtin_type = list_type.builtin_type;
+		} else if (list_type.builtin_type == Variant::VECTOR2I || list_type.builtin_type == Variant::VECTOR3I) {
+			variable_type.type_source = list_type.type_source;
+			variable_type.kind = GDScriptParser::DataType::BUILTIN;
+			variable_type.builtin_type = Variant::INT;
+		} else if (list_type.builtin_type == Variant::VECTOR2 || list_type.builtin_type == Variant::VECTOR3) {
+			variable_type.type_source = list_type.type_source;
+			variable_type.kind = GDScriptParser::DataType::BUILTIN;
+			variable_type.builtin_type = Variant::FLOAT;
+		} else if (list_type.builtin_type == Variant::OBJECT) {
+			GDScriptParser::DataType return_type;
+			List<GDScriptParser::DataType> par_types;
+			int default_arg_count = 0;
+			bool is_static = false;
+			bool is_vararg = false;
+			if (get_function_signature(p_for->list, false, list_type, CoreStringNames::get_singleton()->_iter_get, return_type, par_types, default_arg_count, is_static, is_vararg)) {
+				variable_type = return_type;
+				variable_type.type_source = list_type.type_source;
+			} else if (!list_type.is_hard_type()) {
+				variable_type.kind = GDScriptParser::DataType::VARIANT;
+			} else {
+				push_error(vformat(R"(Unable to iterate on object of type "%s".)", list_type.to_string()), p_for->list);
+			}
+		} else if (list_type.builtin_type == Variant::ARRAY || list_type.builtin_type == Variant::DICTIONARY || !list_type.is_hard_type()) {
 			variable_type.kind = GDScriptParser::DataType::VARIANT;
+		} else {
+			push_error(vformat(R"(Unable to iterate on value of type "%s".)", list_type.to_string()), p_for->list);
 		}
 	}
 	if (p_for->variable) {

+ 6 - 0
modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_float.gd

@@ -0,0 +1,6 @@
+const constant_float = 1.0
+
+func test():
+	for x in constant_float:
+		if x is String:
+			pass

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

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "float" so it can't be of type "String".

+ 6 - 0
modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_constant_int.gd

@@ -0,0 +1,6 @@
+const constant_int = 1
+
+func test():
+	for x in constant_int:
+		if x is String:
+			pass

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

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".

+ 6 - 0
modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_enum_value.gd

@@ -0,0 +1,6 @@
+enum { enum_value = 1 }
+
+func test():
+	for x in enum_value:
+		if x is String:
+			pass

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

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".

+ 6 - 0
modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_float.gd

@@ -0,0 +1,6 @@
+func test():
+	var hard_float := 1.0
+
+	for x in hard_float:
+		if x is String:
+			pass

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

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "float" so it can't be of type "String".

+ 6 - 0
modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_int.gd

@@ -0,0 +1,6 @@
+func test():
+	var hard_int := 1
+
+	for x in hard_int:
+		if x is String:
+			pass

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

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".

+ 14 - 0
modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_iterator.gd

@@ -0,0 +1,14 @@
+class Iterator:
+	func _iter_init(_count):
+		return true
+	func _iter_next(_count):
+		return false
+	func _iter_get(_count) -> StringName:
+		return &'custom'
+
+func test():
+	var hard_iterator := Iterator.new()
+
+	for x in hard_iterator:
+		if x is int:
+			pass

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

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "StringName" so it can't be of type "int".

+ 6 - 0
modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_hard_string.gd

@@ -0,0 +1,6 @@
+func test():
+	var hard_string := 'a'
+
+	for x in hard_string:
+		if x is int:
+			pass

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

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "String" so it can't be of type "int".

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

@@ -0,0 +1,3 @@
+func test():
+	for x in true:
+		pass

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

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Unable to iterate on value of type "bool".

+ 4 - 0
modules/gdscript/tests/scripts/analyzer/errors/for_loop_on_literal_int.gd

@@ -0,0 +1,4 @@
+func test():
+	for x in 1:
+		if x is String:
+			pass

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

@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Expression is of type "int" so it can't be of type "String".

+ 15 - 0
modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.gd

@@ -0,0 +1,15 @@
+func test():
+	var variant_int: Variant = 1
+	var weak_int = 1
+
+	for x in variant_int:
+		if x is String:
+			print('never')
+		print(x)
+
+	for x in weak_int:
+		if x is String:
+			print('never')
+		print(x)
+
+	print('ok')

+ 4 - 0
modules/gdscript/tests/scripts/analyzer/features/for_loop_on_variant.out

@@ -0,0 +1,4 @@
+GDTEST_OK
+0
+0
+ok

+ 51 - 0
modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.gd

@@ -0,0 +1,51 @@
+const constant_float = 1.0
+const constant_int = 1
+enum { enum_value = 1 }
+
+class Iterator:
+	func _iter_init(_count):
+		return true
+	func _iter_next(_count):
+		return false
+	func _iter_get(_count) -> StringName:
+		return &'custom'
+
+func test():
+	var hard_float := 1.0
+	var hard_int := 1
+	var hard_string := '0'
+	var hard_iterator := Iterator.new()
+
+	var variant_float: Variant = hard_float
+	var variant_int: Variant = hard_int
+	var variant_string: Variant = hard_string
+	var variant_iterator: Variant = hard_iterator
+
+	for i in 1.0:
+		print(typeof(i) == TYPE_FLOAT)
+	for i in 1:
+		print(typeof(i) == TYPE_INT)
+	for i in 'a':
+		print(typeof(i) == TYPE_STRING)
+	for i in Iterator.new():
+		print(typeof(i) == TYPE_STRING_NAME)
+
+	for i in hard_float:
+		print(typeof(i) == TYPE_FLOAT)
+	for i in hard_int:
+		print(typeof(i) == TYPE_INT)
+	for i in hard_string:
+		print(typeof(i) == TYPE_STRING)
+	for i in hard_iterator:
+		print(typeof(i) == TYPE_STRING_NAME)
+
+	for i in variant_float:
+		print(typeof(i) == TYPE_FLOAT)
+	for i in variant_int:
+		print(typeof(i) == TYPE_INT)
+	for i in variant_string:
+		print(typeof(i) == TYPE_STRING)
+	for i in variant_iterator:
+		print(typeof(i) == TYPE_STRING_NAME)
+
+	print('ok')

+ 14 - 0
modules/gdscript/tests/scripts/runtime/features/for_loop_iterator_types.out

@@ -0,0 +1,14 @@
+GDTEST_OK
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+true
+ok