summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml12
-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.cpp2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp80
-rw-r--r--modules/gdscript/gdscript_editor.cpp29
-rw-r--r--modules/gdscript/gdscript_function.h2
-rw-r--r--modules/gdscript/gdscript_parser.cpp3
-rw-r--r--modules/gdscript/gdscript_vm.cpp39
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp11
10 files changed, 146 insertions, 52 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 4d6320d8c3..d9fab01dce 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -186,6 +186,7 @@
<description>
Returns an array with the given range. Range can be 1 argument [code]N[/code] (0 to [code]N[/code] - 1), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). Returns an empty array if the range isn't valid (e.g. [code]range(2, 5, -1)[/code] or [code]range(5, 5, 1)[/code]).
Returns an array with the given range. [code]range()[/code] can have 1 argument N ([code]0[/code] to [code]N - 1[/code]), two arguments ([code]initial[/code], [code]final - 1[/code]) or three arguments ([code]initial[/code], [code]final - 1[/code], [code]increment[/code]). [code]increment[/code] can be negative. If [code]increment[/code] is negative, [code]final - 1[/code] will become [code]final + 1[/code]. Also, the initial value must be greater than the final value for the loop to run.
+ [code]range()(/code] converts all arguments to [int] before processing.
[codeblock]
print(range(4))
print(range(2, 5))
@@ -211,6 +212,17 @@
6
3
[/codeblock]
+ To iterate over [float], convert them in the loop.
+ [codeblock]
+ for i in range (3, 0, -1):
+ print(i / 10.0)
+ [/codeblock]
+ Output:
+ [codeblock]
+ 0.3
+ 0.2
+ 0.1
+ [/codeblock]
</description>
</method>
<method name="str" qualifiers="vararg">
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.cpp b/modules/gdscript/gdscript.cpp
index 58a788e255..8bf5fd1eda 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -796,7 +796,7 @@ void GDScript::_set_subclass_path(Ref<GDScript> &p_sc, const String &p_path) {
String GDScript::_get_debug_path() const {
if (is_built_in() && !get_name().is_empty()) {
- return get_name() + " (" + get_path().get_slice("::", 0) + ")";
+ return get_name() + " (" + get_path() + ")";
} else {
return get_path();
}
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 94daba4bf6..1a841b2a0f 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;
@@ -1241,7 +1251,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
bool list_resolved = false;
// Optimize constant range() call to not allocate an array.
- // Use int, Vector2, Vector3 instead, which also can be used as range iterators.
+ // Use int, Vector2i, Vector3i instead, which also can be used as range iterators.
if (p_for->list && p_for->list->type == GDScriptParser::Node::CALL) {
GDScriptParser::CallNode *call = static_cast<GDScriptParser::CallNode *>(p_for->list);
GDScriptParser::Node::Type callee_type = call->get_callee_type();
@@ -2565,18 +2575,24 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
}
GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source) {
+ GDScriptParser::DataType type;
+
Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(p_class_name));
- Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (ref.is_null()) {
+ push_error(vformat(R"(Could not find script for class "%s".)", p_class_name), p_source);
+ type.type_source = GDScriptParser::DataType::UNDETECTED;
+ type.kind = GDScriptParser::DataType::VARIANT;
+ return type;
+ }
+ Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
if (err) {
push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
- GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::UNDETECTED;
type.kind = GDScriptParser::DataType::VARIANT;
return type;
}
- GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
type.kind = GDScriptParser::DataType::CLASS;
type.builtin_type = Variant::OBJECT;
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_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 8e4e457ec1..725b62f6d6 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -2692,12 +2692,13 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *
}
}
- attribute->is_attribute = true;
attribute->base = p_previous_operand;
if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier after "." for attribute access.)")) {
return attribute;
}
+
+ attribute->is_attribute = true;
attribute->attribute = parse_identifier();
return attribute;
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;
}
diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp
index 33c1c834f1..14337e87da 100644
--- a/modules/gdscript/language_server/gdscript_language_server.cpp
+++ b/modules/gdscript/language_server/gdscript_language_server.cpp
@@ -45,17 +45,20 @@ GDScriptLanguageServer::GDScriptLanguageServer() {
void GDScriptLanguageServer::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_ENTER_TREE: {
start();
- break;
- case NOTIFICATION_EXIT_TREE:
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
stop();
- break;
+ } break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
if (started && !use_thread) {
protocol.poll();
}
} break;
+
case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
String host = String(_EDITOR_GET("network/language_server/remote_host"));
int port = (int)_EDITOR_GET("network/language_server/remote_port");