summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd8
-rw-r--r--modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd12
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp66
-rw-r--r--modules/gdscript/gdscript_editor.cpp29
-rw-r--r--modules/gdscript/gdscript_function.h2
-rw-r--r--modules/gdscript/gdscript_vm.cpp39
6 files changed, 114 insertions, 42 deletions
diff --git a/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd b/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
index edaccae018..34b5ba45b7 100644
--- a/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
+++ b/modules/gdscript/editor_templates/CharacterBody2D/basic_movement.gd
@@ -12,18 +12,18 @@ var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")
func _physics_process(delta: float) -> void:
# Add the gravity.
if not is_on_floor():
- motion_velocity.y += gravity * delta
+ velocity.y += gravity * delta
# Handle Jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
- motion_velocity.y = JUMP_VELOCITY
+ velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
var direction := Input.get_axis("ui_left", "ui_right")
if direction:
- motion_velocity.x = direction * SPEED
+ velocity.x = direction * SPEED
else:
- motion_velocity.x = move_toward(motion_velocity.x, 0, SPEED)
+ velocity.x = move_toward(velocity.x, 0, SPEED)
move_and_slide()
diff --git a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd b/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
index e191e5451a..cbc9cf1064 100644
--- a/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
+++ b/modules/gdscript/editor_templates/CharacterBody3D/basic_movement.gd
@@ -12,21 +12,21 @@ var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
func _physics_process(delta: float) -> void:
# Add the gravity.
if not is_on_floor():
- motion_velocity.y -= gravity * delta
+ velocity.y -= gravity * delta
# Handle Jump.
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
- motion_velocity.y = JUMP_VELOCITY
+ velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
var input_dir := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction:
- motion_velocity.x = direction.x * SPEED
- motion_velocity.z = direction.z * SPEED
+ velocity.x = direction.x * SPEED
+ velocity.z = direction.z * SPEED
else:
- motion_velocity.x = move_toward(motion_velocity.x, 0, SPEED)
- motion_velocity.z = move_toward(motion_velocity.z, 0, SPEED)
+ velocity.x = move_toward(velocity.x, 0, SPEED)
+ velocity.z = move_toward(velocity.z, 0, SPEED)
move_and_slide()
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 94daba4bf6..9a79f3d016 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -646,41 +646,51 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
}
- if (member.variable->datatype_specifier != nullptr) {
- datatype = specified_type;
+ // Check if initalizer is an unset identifier (ie: a variable within scope, but declared below)
+ if (member.variable->initializer && !member.variable->initializer->get_datatype().is_set()) {
+ if (member.variable->initializer->type == GDScriptParser::Node::IDENTIFIER) {
+ GDScriptParser::IdentifierNode *initializer_identifier = static_cast<GDScriptParser::IdentifierNode *>(member.variable->initializer);
+ push_error(vformat(R"(Identifier "%s" must be declared above current variable.)", initializer_identifier->name), member.variable->initializer);
+ } else {
+ ERR_PRINT("Parser bug (please report): tried to assign unset node without an identifier.");
+ }
+ } else {
+ if (member.variable->datatype_specifier != nullptr) {
+ datatype = specified_type;
- if (member.variable->initializer != nullptr) {
- if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
- // Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
- push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
- } else {
- // TODO: Add warning.
+ if (member.variable->initializer != nullptr) {
+ if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true, member.variable->initializer)) {
+ // Try reverse test since it can be a masked subtype.
+ if (!is_type_compatible(member.variable->initializer->get_datatype(), datatype, true, member.variable->initializer)) {
+ push_error(vformat(R"(Value of type "%s" cannot be assigned to a variable of type "%s".)", member.variable->initializer->get_datatype().to_string(), datatype.to_string()), member.variable->initializer);
+ } else {
+ // TODO: Add warning.
+ mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
+ }
+ } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
+#ifdef DEBUG_ENABLED
+ parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
+#endif
+ }
+ if (member.variable->initializer->get_datatype().is_variant()) {
+ // TODO: Warn unsafe assign.
mark_node_unsafe(member.variable->initializer);
member.variable->use_conversion_assign = true;
}
- } else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
-#ifdef DEBUG_ENABLED
- parser->push_warning(member.variable->initializer, GDScriptWarning::NARROWING_CONVERSION);
-#endif
}
- if (member.variable->initializer->get_datatype().is_variant()) {
- // TODO: Warn unsafe assign.
- mark_node_unsafe(member.variable->initializer);
- member.variable->use_conversion_assign = true;
+ } else if (member.variable->infer_datatype) {
+ if (member.variable->initializer == nullptr) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
+ } else if (!datatype.is_set() || datatype.has_no_type()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.is_variant()) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
+ } else if (datatype.builtin_type == Variant::NIL) {
+ push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
}
+ datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
- } else if (member.variable->infer_datatype) {
- if (member.variable->initializer == nullptr) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because there's no default value.)", member.variable->identifier->name), member.variable->identifier);
- } else if (!datatype.is_set() || datatype.has_no_type()) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value doesn't have a set type.)", member.variable->identifier->name), member.variable->initializer);
- } else if (datatype.is_variant()) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is Variant. Use explicit "Variant" type if this is intended.)", member.variable->identifier->name), member.variable->initializer);
- } else if (datatype.builtin_type == Variant::NIL) {
- push_error(vformat(R"(Cannot infer the type of "%s" variable because the initial value is "null".)", member.variable->identifier->name), member.variable->initializer);
- }
- datatype.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
}
datatype.is_constant = false;
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index f0dc830ed8..6fb95d32ca 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -1198,6 +1198,27 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);
static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);
+static bool _is_expression_named_identifier(const GDScriptParser::ExpressionNode *p_expression, const StringName &p_name) {
+ if (p_expression) {
+ switch (p_expression->type) {
+ case GDScriptParser::Node::IDENTIFIER: {
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);
+ if (id->name == p_name) {
+ return true;
+ }
+ } break;
+ case GDScriptParser::Node::CAST: {
+ const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);
+ return _is_expression_named_identifier(cn->operand, p_name);
+ } break;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) {
bool found = false;
@@ -1904,6 +1925,14 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
return true;
} else if (init->start_line == p_context.current_line) {
return false;
+ // Detects if variable is assigned to itself
+ } else if (_is_expression_named_identifier(init, member.variable->identifier->name)) {
+ if (member.variable->initializer->get_datatype().is_set()) {
+ r_type.type = member.variable->initializer->get_datatype();
+ } else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {
+ r_type.type = member.variable->get_datatype();
+ }
+ return true;
} else if (_guess_expression_type(p_context, init, r_type)) {
return true;
} else if (init->get_datatype().is_set() && !init->get_datatype().is_variant()) {
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index db663ca48f..3ee664c76d 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -502,6 +502,8 @@ private:
List<StackDebug> stack_debug;
+ Variant _get_default_variant_for_data_type(const GDScriptDataType &p_data_type);
+
_FORCE_INLINE_ Variant *_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const;
_FORCE_INLINE_ String _get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const;
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 95122714f9..6964f27423 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -123,6 +123,34 @@ static String _get_var_type(const Variant *p_var) {
}
#endif // DEBUG_ENABLED
+Variant GDScriptFunction::_get_default_variant_for_data_type(const GDScriptDataType &p_data_type) {
+ if (p_data_type.kind == GDScriptDataType::BUILTIN) {
+ if (p_data_type.builtin_type == Variant::ARRAY) {
+ Array array;
+ // Typed array.
+ if (p_data_type.has_container_element_type()) {
+ const GDScriptDataType &element_type = p_data_type.get_container_element_type();
+ array.set_typed(
+ element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT,
+ element_type.native_type,
+ element_type.script_type);
+ }
+
+ return array;
+ } else {
+ Callable::CallError ce;
+ Variant variant;
+ Variant::construct(p_data_type.builtin_type, variant, nullptr, 0, ce);
+
+ ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, Variant());
+
+ return variant;
+ }
+ }
+
+ return Variant();
+}
+
String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const {
String err_text;
@@ -428,7 +456,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
OPCODES_TABLE;
if (!_code_ptr) {
- return Variant();
+ return _get_default_variant_for_data_type(return_type);
}
r_err.error = Callable::CallError::CALL_OK;
@@ -467,11 +495,11 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
r_err.argument = _argument_count;
- return Variant();
+ return _get_default_variant_for_data_type(return_type);
} else if (p_argcount < _argument_count - _default_arg_count) {
r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_err.argument = _argument_count - _default_arg_count;
- return Variant();
+ return _get_default_variant_for_data_type(return_type);
} else {
defarg = _argument_count - p_argcount;
}
@@ -498,7 +526,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_err.argument = i;
r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
- return Variant();
+ return _get_default_variant_for_data_type(return_type);
}
if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
Variant arg;
@@ -3324,6 +3352,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
#endif
+ // Get a default return type in case of failure
+ retvalue = _get_default_variant_for_data_type(return_type);
+
OPCODE_OUT;
}