summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml17
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml2
-rw-r--r--modules/gdscript/gdscript.cpp148
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp232
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp62
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h6
-rw-r--r--modules/gdscript/gdscript_cache.cpp8
-rw-r--r--modules/gdscript/gdscript_compiler.cpp140
-rw-r--r--modules/gdscript/gdscript_editor.cpp83
-rw-r--r--modules/gdscript/gdscript_function.cpp8
-rw-r--r--modules/gdscript/gdscript_parser.cpp81
-rw-r--r--modules/gdscript/gdscript_parser.h4
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp12
-rw-r--r--modules/gdscript/gdscript_vm.cpp15
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp9
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h2
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp25
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h3
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp7
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp91
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h4
-rw-r--r--modules/gdscript/language_server/lsp.hpp32
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp13
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.gd4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd19
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out4
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd14
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/property_functions.gd16
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/property_functions.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/property_inline.gd46
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/property_inline.out10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd8
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/subscript_self.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd10
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd32
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out6
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd13
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out5
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.gd19
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out3
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/lua_assign.gd4
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/lua_assign.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd11
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.out8
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/stringify.gd42
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/stringify.out34
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp4
69 files changed, 1049 insertions, 375 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 51b3452a3a..631ee4d895 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -176,7 +176,8 @@
<method name="range" qualifiers="vararg">
<return type="Array" />
<description>
- Returns an array with the given range. Range can be 1 argument N (0 to N-1), two arguments (initial, final-1) or three arguments (initial, final-1, increment).
+ 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.
[codeblock]
print(range(4))
print(range(2, 5))
@@ -188,6 +189,20 @@
[2, 3, 4]
[0, 2, 4]
[/codeblock]
+ To iterate over an [Array] backwards, use:
+ [codeblock]
+ var array = [3, 6, 9]
+ var i := array.size() - 1
+ while i &gt;= 0:
+ print(array[i])
+ i -= 1
+ [/codeblock]
+ Output:
+ [codeblock]
+ 9
+ 6
+ 3
+ [/codeblock]
</description>
</method>
<method name="str" qualifiers="vararg">
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index d45202bd40..0a448ed88c 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -8,7 +8,7 @@
[method new] creates a new instance of the script. [method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
</description>
<tutorials>
- <link title="GDScript tutorial index">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/index.html</link>
+ <link title="GDScript documentation index">https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/index.html</link>
</tutorials>
<methods>
<method name="get_as_byte_code" qualifiers="const">
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index bc8801b8b9..2bae838543 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -122,8 +122,8 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
instance->owner_id = p_owner->get_instance_id();
#ifdef DEBUG_ENABLED
//needed for hot reloading
- for (Map<StringName, MemberInfo>::Element *E = member_indices.front(); E; E = E->next()) {
- instance->member_indices_cache[E->key()] = E->get().index;
+ for (const KeyValue<StringName, MemberInfo> &E : member_indices) {
+ instance->member_indices_cache[E.key] = E.value.index;
}
#endif
instance->owner->set_script_instance(instance);
@@ -253,10 +253,10 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const {
const GDScript *current = this;
while (current) {
- for (const Map<StringName, GDScriptFunction *>::Element *E = current->member_functions.front(); E; E = E->next()) {
- GDScriptFunction *func = E->get();
+ for (const KeyValue<StringName, GDScriptFunction *> &E : current->member_functions) {
+ GDScriptFunction *func = E.value;
MethodInfo mi;
- mi.name = E->key();
+ mi.name = E.key;
for (int i = 0; i < func->get_argument_count(); i++) {
PropertyInfo arginfo = func->get_argument_type(i);
#ifdef TOOLS_ENABLED
@@ -286,11 +286,11 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl
while (sptr) {
Vector<_GDScriptMemberSort> msort;
- for (Map<StringName, PropertyInfo>::Element *E = sptr->member_info.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, PropertyInfo> &E : sptr->member_info) {
_GDScriptMemberSort ms;
- ERR_CONTINUE(!sptr->member_indices.has(E->key()));
- ms.index = sptr->member_indices[E->key()].index;
- ms.name = E->key();
+ ERR_CONTINUE(!sptr->member_indices.has(E.key));
+ ms.index = sptr->member_indices[E.key].index;
+ ms.name = E.key;
msort.push_back(ms);
}
@@ -412,8 +412,8 @@ void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<Pro
base_cache->_update_exports_values(values, propnames);
}
- for (Map<StringName, Variant>::Element *E = member_default_values_cache.front(); E; E = E->next()) {
- values[E->key()] = E->get();
+ for (const KeyValue<StringName, Variant> &E : member_default_values_cache) {
+ values[E.key] = E.value;
}
for (const PropertyInfo &E : members_cache) {
@@ -471,9 +471,9 @@ void GDScript::_update_doc() {
doc.description = doc_description;
doc.tutorials = doc_tutorials;
- for (Map<String, DocData::EnumDoc>::Element *E = doc_enums.front(); E; E = E->next()) {
- if (E->value().description != "") {
- doc.enums[E->key()] = E->value().description;
+ for (const KeyValue<String, DocData::EnumDoc> &E : doc_enums) {
+ if (E.value.description != "") {
+ doc.enums[E.key] = E.value.description;
}
}
@@ -552,29 +552,29 @@ void GDScript::_update_doc() {
doc.signals.push_back(signal_doc);
}
- for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
- if (subclasses.has(E->key())) {
+ for (const KeyValue<StringName, Variant> &E : constants) {
+ if (subclasses.has(E.key)) {
continue;
}
// Enums.
bool is_enum = false;
- if (E->value().get_type() == Variant::DICTIONARY) {
- if (doc_enums.has(E->key())) {
+ if (E.value.get_type() == Variant::DICTIONARY) {
+ if (doc_enums.has(E.key)) {
is_enum = true;
- for (int i = 0; i < doc_enums[E->key()].values.size(); i++) {
- doc_enums[E->key()].values.write[i].enumeration = E->key();
- doc.constants.push_back(doc_enums[E->key()].values[i]);
+ for (int i = 0; i < doc_enums[E.key].values.size(); i++) {
+ doc_enums[E.key].values.write[i].enumeration = E.key;
+ doc.constants.push_back(doc_enums[E.key].values[i]);
}
}
}
if (!is_enum && doc_enums.has("@unnamed_enums")) {
for (int i = 0; i < doc_enums["@unnamed_enums"].values.size(); i++) {
- if (E->key() == doc_enums["@unnamed_enums"].values[i].name) {
+ if (E.key == doc_enums["@unnamed_enums"].values[i].name) {
is_enum = true;
DocData::ConstantDoc constant_doc;
constant_doc.enumeration = "@unnamed_enums";
- DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_enums["@unnamed_enums"].values[i].description);
+ DocData::constant_doc_from_variant(constant_doc, E.key, E.value, doc_enums["@unnamed_enums"].values[i].description);
doc.constants.push_back(constant_doc);
break;
}
@@ -583,16 +583,16 @@ void GDScript::_update_doc() {
if (!is_enum) {
DocData::ConstantDoc constant_doc;
String doc_description;
- if (doc_constants.has(E->key())) {
- doc_description = doc_constants[E->key()];
+ if (doc_constants.has(E.key)) {
+ doc_description = doc_constants[E.key];
}
- DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_description);
+ DocData::constant_doc_from_variant(constant_doc, E.key, E.value, doc_description);
doc.constants.push_back(constant_doc);
}
}
- for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) {
- E->get()->_update_doc();
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ E.value->_update_doc();
}
_add_doc(doc);
@@ -784,8 +784,8 @@ void GDScript::update_exports() {
void GDScript::_set_subclass_path(Ref<GDScript> &p_sc, const String &p_path) {
p_sc->path = p_path;
- for (Map<StringName, Ref<GDScript>>::Element *E = p_sc->subclasses.front(); E; E = E->next()) {
- _set_subclass_path(E->get(), p_path);
+ for (KeyValue<StringName, Ref<GDScript>> &E : p_sc->subclasses) {
+ _set_subclass_path(E.value, p_path);
}
}
@@ -886,8 +886,8 @@ Error GDScript::reload(bool p_keep_state) {
valid = true;
- for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) {
- _set_subclass_path(E->get(), path);
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ _set_subclass_path(E.value, path);
}
_init_rpc_methods_properties();
@@ -901,8 +901,8 @@ ScriptLanguage *GDScript::get_language() const {
void GDScript::get_constants(Map<StringName, Variant> *p_constants) {
if (p_constants) {
- for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
- (*p_constants)[E->key()] = E->value();
+ for (const KeyValue<StringName, Variant> &E : constants) {
+ (*p_constants)[E.key] = E.value;
}
}
}
@@ -1032,9 +1032,9 @@ const Map<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions(
}
StringName GDScript::debug_get_member_by_index(int p_idx) const {
- for (const Map<StringName, MemberInfo>::Element *E = member_indices.front(); E; E = E->next()) {
- if (E->get().index == p_idx) {
- return E->key();
+ for (const KeyValue<StringName, MemberInfo> &E : member_indices) {
+ if (E.value.index == p_idx) {
+ return E.key;
}
}
@@ -1079,12 +1079,12 @@ bool GDScript::has_script_signal(const StringName &p_signal) const {
}
void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const {
- for (const Map<StringName, Vector<StringName>>::Element *E = _signals.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, Vector<StringName>> &E : _signals) {
MethodInfo mi;
- mi.name = E->key();
- for (int i = 0; i < E->get().size(); i++) {
+ mi.name = E.key;
+ for (int i = 0; i < E.value.size(); i++) {
PropertyInfo arg;
- arg.name = E->get()[i];
+ arg.name = E.value[i];
mi.arguments.push_back(arg);
}
r_list->push_back(mi);
@@ -1142,11 +1142,11 @@ void GDScript::_save_orphaned_subclasses() {
};
Vector<ClassRefWithName> weak_subclasses;
// collect subclasses ObjectID and name
- for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) {
- E->get()->_owner = nullptr; //bye, you are no longer owned cause I died
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ E.value->_owner = nullptr; //bye, you are no longer owned cause I died
ClassRefWithName subclass;
- subclass.id = E->get()->get_instance_id();
- subclass.fully_qualified_name = E->get()->fully_qualified_name;
+ subclass.id = E.value->get_instance_id();
+ subclass.fully_qualified_name = E.value->fully_qualified_name;
weak_subclasses.push_back(subclass);
}
@@ -1178,10 +1178,10 @@ void GDScript::_init_rpc_methods_properties() {
Map<StringName, Ref<GDScript>>::Element *sub_E = subclasses.front();
while (cscript) {
// RPC Methods
- for (Map<StringName, GDScriptFunction *>::Element *E = cscript->member_functions.front(); E; E = E->next()) {
- Multiplayer::RPCConfig config = E->get()->get_rpc_config();
+ for (KeyValue<StringName, GDScriptFunction *> &E : cscript->member_functions) {
+ Multiplayer::RPCConfig config = E.value->get_rpc_config();
if (config.rpc_mode != Multiplayer::RPC_MODE_DISABLED) {
- config.name = E->get()->get_name();
+ config.name = E.value->get_name();
if (rpc_functions.find(config) == -1) {
rpc_functions.push_back(config);
}
@@ -1215,8 +1215,8 @@ GDScript::~GDScript() {
}
}
- for (Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
+ memdelete(E.value);
}
if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown.
@@ -1442,11 +1442,11 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
//instance a fake script for editing the values
Vector<_GDScriptMemberSort> msort;
- for (Map<StringName, PropertyInfo>::Element *F = sptr->member_info.front(); F; F = F->next()) {
+ for (const KeyValue<StringName, PropertyInfo> &F : sptr->member_info) {
_GDScriptMemberSort ms;
- ERR_CONTINUE(!sptr->member_indices.has(F->key()));
- ms.index = sptr->member_indices[F->key()].index;
- ms.name = F->key();
+ ERR_CONTINUE(!sptr->member_indices.has(F.key));
+ ms.index = sptr->member_indices[F.key].index;
+ ms.name = F.key;
msort.push_back(ms);
}
@@ -1467,11 +1467,11 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
const GDScript *sptr = script.ptr();
while (sptr) {
- for (Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, GDScriptFunction *> &E : sptr->member_functions) {
MethodInfo mi;
- mi.name = E->key();
+ mi.name = E.key;
mi.flags |= METHOD_FLAG_FROM_SCRIPT;
- for (int i = 0; i < E->get()->get_argument_count(); i++) {
+ for (int i = 0; i < E.value->get_argument_count(); i++) {
mi.arguments.push_back(PropertyInfo(Variant::NIL, "arg" + itos(i)));
}
p_list->push_back(mi);
@@ -1569,10 +1569,10 @@ void GDScriptInstance::reload_members() {
new_members.resize(script->member_indices.size());
//pass the values to the new indices
- for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) {
- if (member_indices_cache.has(E->key())) {
- Variant value = members[member_indices_cache[E->key()]];
- new_members.write[E->get().index] = value;
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : script->member_indices) {
+ if (member_indices_cache.has(E.key)) {
+ Variant value = members[member_indices_cache[E.key]];
+ new_members.write[E.value.index] = value;
}
}
@@ -1581,8 +1581,8 @@ void GDScriptInstance::reload_members() {
//pass the values to the new indices
member_indices_cache.clear();
- for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) {
- member_indices_cache[E->key()] = E->get().index;
+ for (const KeyValue<StringName, GDScript::MemberInfo> &E : script->member_indices) {
+ member_indices_cache[E.key] = E.value.index;
}
#endif
@@ -1890,21 +1890,21 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
#endif
- for (Map<ObjectID, List<Pair<StringName, Variant>>>::Element *F = script->pending_reload_state.front(); F; F = F->next()) {
- map[F->key()] = F->get(); //pending to reload, use this one instead
+ for (const KeyValue<ObjectID, List<Pair<StringName, Variant>>> &F : script->pending_reload_state) {
+ map[F.key] = F.value; //pending to reload, use this one instead
}
}
}
- for (Map<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>>::Element *E = to_reload.front(); E; E = E->next()) {
- Ref<GDScript> scr = E->key();
+ for (KeyValue<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) {
+ Ref<GDScript> scr = E.key;
scr->reload(p_soft_reload);
//restore state if saved
- for (Map<ObjectID, List<Pair<StringName, Variant>>>::Element *F = E->get().front(); F; F = F->next()) {
- List<Pair<StringName, Variant>> &saved_state = F->get();
+ for (KeyValue<ObjectID, List<Pair<StringName, Variant>>> &F : E.value) {
+ List<Pair<StringName, Variant>> &saved_state = F.value;
- Object *obj = ObjectDB::get_instance(F->key());
+ Object *obj = ObjectDB::get_instance(F.key);
if (!obj) {
continue;
}
@@ -2210,15 +2210,15 @@ GDScriptLanguage::~GDScriptLanguage() {
// is not the same as before).
script->reference();
- for (Map<StringName, GDScriptFunction *>::Element *E = script->member_functions.front(); E; E = E->next()) {
- GDScriptFunction *func = E->get();
+ for (KeyValue<StringName, GDScriptFunction *> &E : script->member_functions) {
+ GDScriptFunction *func = E.value;
for (int i = 0; i < func->argument_types.size(); i++) {
func->argument_types.write[i].script_type_ref = Ref<Script>();
}
func->return_type.script_type_ref = Ref<Script>();
}
- for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) {
- E->get().data_type.script_type_ref = Ref<Script>();
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : script->member_indices) {
+ E.value.data_type.script_type_ref = Ref<Script>();
}
s = s->next();
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 23e88ae059..6b57784b1c 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -175,6 +175,11 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me
return ERR_PARSE_ERROR;
}
+ if (GDScriptParser::get_builtin_type(p_member_name) != Variant::VARIANT_MAX) {
+ push_error(vformat(R"(The member "%s" cannot have the same name as a builtin type.)", p_member_name), p_member_node);
+ return ERR_PARSE_ERROR;
+ }
+
return OK;
}
@@ -239,6 +244,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
class_type.kind = GDScriptParser::DataType::CLASS;
class_type.class_type = p_class;
class_type.script_path = parser->script_path;
+ class_type.builtin_type = Variant::OBJECT;
p_class->set_datatype(class_type);
if (!p_class->extends_used) {
@@ -464,6 +470,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
GDScriptParser::DataType container_type = resolve_datatype(p_type->container_type);
if (container_type.kind != GDScriptParser::DataType::VARIANT) {
+ container_type.is_meta_type = false;
result.set_container_element_type(container_type);
}
}
@@ -869,18 +876,34 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
GDScriptParser::ClassNode *previous_class = parser->current_class;
parser->current_class = p_class;
- // Do functions now.
+ // Do functions and properties now.
for (int i = 0; i < p_class->members.size(); i++) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
- if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {
- continue;
- }
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {
+ resolve_function_body(member.function);
- resolve_function_body(member.function);
+ // Apply annotations.
+ for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
+ E->apply(parser, member.function);
+ }
+ } else if (member.type == GDScriptParser::ClassNode::Member::VARIABLE && member.variable->property != GDScriptParser::VariableNode::PROP_NONE) {
+ if (member.variable->property == GDScriptParser::VariableNode::PROP_INLINE) {
+ if (member.variable->getter != nullptr) {
+ member.variable->getter->set_datatype(member.variable->datatype);
- // Apply annotations.
- for (GDScriptParser::AnnotationNode *&E : member.function->annotations) {
- E->apply(parser, member.function);
+ resolve_function_body(member.variable->getter);
+ }
+ if (member.variable->setter != nullptr) {
+ resolve_function_signature(member.variable->setter);
+
+ if (member.variable->setter->parameters.size() > 0) {
+ member.variable->setter->parameters[0]->datatype_specifier = member.variable->datatype_specifier;
+ member.variable->setter->parameters[0]->set_datatype(member.get_datatype());
+ }
+
+ resolve_function_body(member.variable->setter);
+ }
+ }
}
}
@@ -896,17 +919,80 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
resolve_class_body(member.m_class);
}
- // Check unused variables.
+ // Check unused variables and datatypes of property getters and setters.
for (int i = 0; i < p_class->members.size(); i++) {
GDScriptParser::ClassNode::Member member = p_class->members[i];
- if (member.type != GDScriptParser::ClassNode::Member::VARIABLE) {
- continue;
- }
+ if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
#ifdef DEBUG_ENABLED
- if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) {
- parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name);
- }
+ if (member.variable->usages == 0 && String(member.variable->identifier->name).begins_with("_")) {
+ parser->push_warning(member.variable->identifier, GDScriptWarning::UNUSED_PRIVATE_CLASS_VARIABLE, member.variable->identifier->name);
+ }
+#endif
+
+ if (member.variable->property == GDScriptParser::VariableNode::PROP_SETGET) {
+ GDScriptParser::FunctionNode *getter_function = nullptr;
+ GDScriptParser::FunctionNode *setter_function = nullptr;
+
+ bool has_valid_getter = false;
+ bool has_valid_setter = false;
+
+ if (member.variable->getter_pointer != nullptr) {
+ if (p_class->has_function(member.variable->getter_pointer->name)) {
+ getter_function = p_class->get_member(member.variable->getter_pointer->name).function;
+ }
+
+ if (getter_function == nullptr) {
+ push_error(vformat(R"(Getter "%s" not found.)", member.variable->getter_pointer->name), member.variable);
+
+ } else if (getter_function->parameters.size() != 0 || getter_function->datatype.has_no_type()) {
+ push_error(vformat(R"(Function "%s" cannot be used as getter because of its signature.)", getter_function->identifier->name), member.variable);
+
+ } else if (!is_type_compatible(member.variable->datatype, getter_function->datatype, true)) {
+ push_error(vformat(R"(Function with return type "%s" cannot be used as getter for a property of type "%s".)", getter_function->datatype.to_string(), member.variable->datatype.to_string()), member.variable);
+
+ } else {
+ has_valid_getter = true;
+
+#ifdef DEBUG_ENABLED
+ if (member.variable->datatype.builtin_type == Variant::INT && getter_function->datatype.builtin_type == Variant::FLOAT) {
+ parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION);
+ }
+#endif
+ }
+ }
+
+ if (member.variable->setter_pointer != nullptr) {
+ if (p_class->has_function(member.variable->setter_pointer->name)) {
+ setter_function = p_class->get_member(member.variable->setter_pointer->name).function;
+ }
+
+ if (setter_function == nullptr) {
+ push_error(vformat(R"(Setter "%s" not found.)", member.variable->setter_pointer->name), member.variable);
+
+ } else if (setter_function->parameters.size() != 1) {
+ push_error(vformat(R"(Function "%s" cannot be used as setter because of its signature.)", setter_function->identifier->name), member.variable);
+
+ } else if (!is_type_compatible(member.variable->datatype, setter_function->parameters[0]->datatype, true)) {
+ push_error(vformat(R"(Function with argument type "%s" cannot be used as setter for a property of type "%s".)", setter_function->parameters[0]->datatype.to_string(), member.variable->datatype.to_string()), member.variable);
+
+ } else {
+ has_valid_setter = true;
+
+#ifdef DEBUG_ENABLED
+ if (member.variable->datatype.builtin_type == Variant::INT && setter_function->return_type->datatype.builtin_type == Variant::FLOAT) {
+ parser->push_warning(member.variable, GDScriptWarning::NARROWING_CONVERSION);
+ }
#endif
+ }
+ }
+
+ if (member.variable->datatype.is_variant() && has_valid_getter && has_valid_setter) {
+ if (!is_type_compatible(getter_function->datatype, setter_function->parameters[0]->datatype, true)) {
+ push_error(vformat(R"(Getter with type "%s" cannot be used along with setter of type "%s".)", getter_function->datatype.to_string(), setter_function->parameters[0]->datatype.to_string()), member.variable);
+ }
+ }
+ }
+ }
}
}
@@ -1030,7 +1116,10 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
return_type.is_meta_type = false;
p_function->set_datatype(return_type);
if (p_function->return_type) {
- push_error("Constructor cannot have an explicit return type.", p_function->return_type);
+ GDScriptParser::DataType declared_return = resolve_datatype(p_function->return_type);
+ if (declared_return.kind != GDScriptParser::DataType::BUILTIN || declared_return.builtin_type != Variant::NIL) {
+ push_error("Constructor cannot have an explicit return type.", p_function->return_type);
+ }
}
} else {
GDScriptParser::DataType return_type = resolve_datatype(p_function->return_type);
@@ -1193,7 +1282,7 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
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);
- } else {
+ } else if (p_for->list) {
resolve_node(p_for->list);
if (p_for->list->datatype.has_container_element_type()) {
variable_type = p_for->list->datatype.get_container_element_type();
@@ -1208,7 +1297,9 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
variable_type.kind = GDScriptParser::DataType::VARIANT;
}
}
- p_for->variable->set_datatype(variable_type);
+ if (p_for->variable) {
+ p_for->variable->set_datatype(variable_type);
+ }
resolve_suite(p_for->loop);
p_for->set_datatype(p_for->loop->get_datatype());
@@ -1518,12 +1609,20 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame
void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
GDScriptParser::DataType result;
+ GDScriptParser::DataType expected_type;
+ bool has_expected_type = false;
+
+ if (parser->current_function != nullptr) {
+ expected_type = parser->current_function->get_datatype();
+ has_expected_type = true;
+ }
+
if (p_return->return_value != nullptr) {
reduce_expression(p_return->return_value);
if (p_return->return_value->type == GDScriptParser::Node::ARRAY) {
// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
- if (parser->current_function->get_datatype().has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) {
- update_array_literal_element_type(parser->current_function->get_datatype(), static_cast<GDScriptParser::ArrayNode *>(p_return->return_value));
+ if (has_expected_type && expected_type.has_container_element_type() && p_return->return_value->type == GDScriptParser::Node::ARRAY) {
+ update_array_literal_element_type(expected_type, static_cast<GDScriptParser::ArrayNode *>(p_return->return_value));
}
}
result = p_return->return_value->get_datatype();
@@ -1535,23 +1634,24 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
result.is_constant = true;
}
- GDScriptParser::DataType function_type = parser->current_function->get_datatype();
- function_type.is_meta_type = false;
- if (function_type.is_hard_type()) {
- if (!is_type_compatible(function_type, result)) {
- // Try other way. Okay but not safe.
- if (!is_type_compatible(result, function_type)) {
- push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), function_type.to_string()), p_return);
- } else {
- // TODO: Add warning.
- mark_node_unsafe(p_return);
- }
+ if (has_expected_type) {
+ expected_type.is_meta_type = false;
+ if (expected_type.is_hard_type()) {
+ if (!is_type_compatible(expected_type, result)) {
+ // Try other way. Okay but not safe.
+ if (!is_type_compatible(result, expected_type)) {
+ push_error(vformat(R"(Cannot return value of type "%s" because the function return type is "%s".)", result.to_string(), expected_type.to_string()), p_return);
+ } else {
+ // TODO: Add warning.
+ mark_node_unsafe(p_return);
+ }
#ifdef DEBUG_ENABLED
- } else if (function_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) {
- parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION);
- } else if (result.is_variant()) {
- mark_node_unsafe(p_return);
+ } else if (expected_type.builtin_type == Variant::INT && result.builtin_type == Variant::FLOAT) {
+ parser->push_warning(p_return, GDScriptWarning::NARROWING_CONVERSION);
+ } else if (result.is_variant()) {
+ mark_node_unsafe(p_return);
#endif
+ }
}
}
@@ -1732,7 +1832,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
}
- if (!assignee_type.is_variant() && !assigned_value_type.is_variant()) {
+ if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) {
bool compatible = true;
GDScriptParser::DataType op_type = assigned_value_type;
if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
@@ -1784,27 +1884,24 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER: {
GDScriptParser::DataType id_type = identifier->parameter_source->get_datatype();
if (!id_type.is_hard_type()) {
- id_type = assigned_value_type;
- id_type.type_source = GDScriptParser::DataType::INFERRED;
- id_type.is_constant = false;
+ id_type.kind = GDScriptParser::DataType::VARIANT;
+ id_type.type_source = GDScriptParser::DataType::UNDETECTED;
identifier->parameter_source->set_datatype(id_type);
}
} break;
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE: {
GDScriptParser::DataType id_type = identifier->variable_source->get_datatype();
if (!id_type.is_hard_type()) {
- id_type = assigned_value_type;
- id_type.type_source = GDScriptParser::DataType::INFERRED;
- id_type.is_constant = false;
+ id_type.kind = GDScriptParser::DataType::VARIANT;
+ id_type.type_source = GDScriptParser::DataType::UNDETECTED;
identifier->variable_source->set_datatype(id_type);
}
} break;
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR: {
GDScriptParser::DataType id_type = identifier->bind_source->get_datatype();
if (!id_type.is_hard_type()) {
- id_type = assigned_value_type;
- id_type.type_source = GDScriptParser::DataType::INFERRED;
- id_type.is_constant = false;
+ id_type.kind = GDScriptParser::DataType::VARIANT;
+ id_type.type_source = GDScriptParser::DataType::UNDETECTED;
identifier->variable_source->set_datatype(id_type);
}
} break;
@@ -2265,10 +2362,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
if (get_function_signature(p_call, base_type, p_call->function_name, return_type, par_types, default_arg_count, is_static, is_vararg)) {
// If the function require typed arrays we must make literals be typed.
- for (Map<int, GDScriptParser::ArrayNode *>::Element *E = arrays.front(); E; E = E->next()) {
- int index = E->key();
+ for (const KeyValue<int, GDScriptParser::ArrayNode *> &E : arrays) {
+ int index = E.key;
if (index < par_types.size() && par_types[index].has_container_element_type()) {
- update_array_literal_element_type(par_types[index], E->get());
+ update_array_literal_element_type(par_types[index], E.value);
}
}
validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
@@ -2503,7 +2600,10 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
result.enum_type = name;
p_identifier->set_datatype(result);
} else {
- push_error(vformat(R"(Cannot find value "%s" in "%s".)", name, base.to_string()), p_identifier);
+ // Consider as a Dictionary
+ GDScriptParser::DataType dummy;
+ dummy.kind = GDScriptParser::DataType::VARIANT;
+ p_identifier->set_datatype(dummy);
}
} else {
push_error(R"(Cannot get property from enum value.)", p_identifier);
@@ -2583,6 +2683,11 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
p_identifier->is_constant = false;
return;
} break;
+ case GDScriptParser::ClassNode::Member::CLASS: {
+ resolve_class_interface(member.m_class);
+ p_identifier->set_datatype(member.m_class->get_datatype());
+ return;
+ } break;
default:
break;
}
@@ -2597,10 +2702,13 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
const StringName &native = base.native_type;
if (class_exists(native)) {
- PropertyInfo prop_info;
MethodInfo method_info;
- if (ClassDB::get_property_info(native, name, &prop_info)) {
- p_identifier->set_datatype(type_from_property(prop_info));
+ if (ClassDB::has_property(native, name)) {
+ StringName getter_name = ClassDB::get_property_getter(native, name);
+ MethodBind *getter = ClassDB::get_method(native, getter_name);
+ if (getter != nullptr) {
+ p_identifier->set_datatype(type_from_property(getter->get_return_info()));
+ }
return;
}
if (ClassDB::get_method_info(native, name, &method_info)) {
@@ -2928,7 +3036,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
} else {
GDScriptParser::DataType base_type = p_subscript->base->get_datatype();
- if (base_type.is_variant()) {
+ if (base_type.is_variant() || !base_type.is_hard_type()) {
result_type.kind = GDScriptParser::DataType::VARIANT;
mark_node_unsafe(p_subscript);
} else {
@@ -3051,6 +3159,9 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
result_type.kind = GDScriptParser::DataType::BUILTIN;
result_type.type_source = base_type.is_hard_type() ? GDScriptParser::DataType::ANNOTATED_INFERRED : GDScriptParser::DataType::INFERRED;
+ if (base_type.kind != GDScriptParser::DataType::BUILTIN) {
+ base_type.builtin_type = Variant::OBJECT;
+ }
switch (base_type.builtin_type) {
// Can't index at all.
case Variant::RID:
@@ -3111,6 +3222,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::PLANE:
case Variant::COLOR:
case Variant::DICTIONARY:
+ case Variant::OBJECT:
result_type.kind = GDScriptParser::DataType::VARIANT;
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
break;
@@ -3125,7 +3237,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
}
break;
// Here for completeness.
- case Variant::OBJECT:
case Variant::VARIANT_MAX:
break;
}
@@ -3292,7 +3403,13 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
current = current->_owner;
}
- Ref<GDScriptParserRef> ref = get_parser_for(current->path);
+ Ref<GDScriptParserRef> ref = get_parser_for(current->get_path());
+ if (ref.is_null()) {
+ push_error("Could not find script in path.", p_source);
+ GDScriptParser::DataType error_type;
+ error_type.kind = GDScriptParser::DataType::VARIANT;
+ return error_type;
+ }
ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
GDScriptParser::ClassNode *found = ref->get_parser()->head;
@@ -3676,6 +3793,11 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
if (p_source.kind == GDScriptParser::DataType::BUILTIN && p_source.builtin_type == Variant::INT) {
return true;
}
+ if (p_source.kind == GDScriptParser::DataType::ENUM) {
+ if (p_source.native_type == p_target.native_type) {
+ return true;
+ }
+ }
if (p_source.kind == GDScriptParser::DataType::ENUM_VALUE) {
if (p_source.native_type == p_target.native_type && p_target.enum_values.has(p_source.enum_type)) {
return true;
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 1127488db8..6a7e4278d2 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -209,8 +209,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
if (name_map.size()) {
function->global_names.resize(name_map.size());
function->_global_names_ptr = &function->global_names[0];
- for (Map<StringName, int>::Element *E = name_map.front(); E; E = E->next()) {
- function->global_names.write[E->get()] = E->key();
+ for (const KeyValue<StringName, int> &E : name_map) {
+ function->global_names.write[E.value] = E.key;
}
function->_global_names_count = function->global_names.size();
@@ -241,8 +241,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->operator_funcs.resize(operator_func_map.size());
function->_operator_funcs_count = function->operator_funcs.size();
function->_operator_funcs_ptr = function->operator_funcs.ptr();
- for (const Map<Variant::ValidatedOperatorEvaluator, int>::Element *E = operator_func_map.front(); E; E = E->next()) {
- function->operator_funcs.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedOperatorEvaluator, int> &E : operator_func_map) {
+ function->operator_funcs.write[E.value] = E.key;
}
} else {
function->_operator_funcs_count = 0;
@@ -253,8 +253,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->setters.resize(setters_map.size());
function->_setters_count = function->setters.size();
function->_setters_ptr = function->setters.ptr();
- for (const Map<Variant::ValidatedSetter, int>::Element *E = setters_map.front(); E; E = E->next()) {
- function->setters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedSetter, int> &E : setters_map) {
+ function->setters.write[E.value] = E.key;
}
} else {
function->_setters_count = 0;
@@ -265,8 +265,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->getters.resize(getters_map.size());
function->_getters_count = function->getters.size();
function->_getters_ptr = function->getters.ptr();
- for (const Map<Variant::ValidatedGetter, int>::Element *E = getters_map.front(); E; E = E->next()) {
- function->getters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedGetter, int> &E : getters_map) {
+ function->getters.write[E.value] = E.key;
}
} else {
function->_getters_count = 0;
@@ -277,8 +277,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->keyed_setters.resize(keyed_setters_map.size());
function->_keyed_setters_count = function->keyed_setters.size();
function->_keyed_setters_ptr = function->keyed_setters.ptr();
- for (const Map<Variant::ValidatedKeyedSetter, int>::Element *E = keyed_setters_map.front(); E; E = E->next()) {
- function->keyed_setters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedKeyedSetter, int> &E : keyed_setters_map) {
+ function->keyed_setters.write[E.value] = E.key;
}
} else {
function->_keyed_setters_count = 0;
@@ -289,8 +289,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->keyed_getters.resize(keyed_getters_map.size());
function->_keyed_getters_count = function->keyed_getters.size();
function->_keyed_getters_ptr = function->keyed_getters.ptr();
- for (const Map<Variant::ValidatedKeyedGetter, int>::Element *E = keyed_getters_map.front(); E; E = E->next()) {
- function->keyed_getters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedKeyedGetter, int> &E : keyed_getters_map) {
+ function->keyed_getters.write[E.value] = E.key;
}
} else {
function->_keyed_getters_count = 0;
@@ -301,8 +301,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->indexed_setters.resize(indexed_setters_map.size());
function->_indexed_setters_count = function->indexed_setters.size();
function->_indexed_setters_ptr = function->indexed_setters.ptr();
- for (const Map<Variant::ValidatedIndexedSetter, int>::Element *E = indexed_setters_map.front(); E; E = E->next()) {
- function->indexed_setters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedIndexedSetter, int> &E : indexed_setters_map) {
+ function->indexed_setters.write[E.value] = E.key;
}
} else {
function->_indexed_setters_count = 0;
@@ -313,8 +313,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->indexed_getters.resize(indexed_getters_map.size());
function->_indexed_getters_count = function->indexed_getters.size();
function->_indexed_getters_ptr = function->indexed_getters.ptr();
- for (const Map<Variant::ValidatedIndexedGetter, int>::Element *E = indexed_getters_map.front(); E; E = E->next()) {
- function->indexed_getters.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedIndexedGetter, int> &E : indexed_getters_map) {
+ function->indexed_getters.write[E.value] = E.key;
}
} else {
function->_indexed_getters_count = 0;
@@ -325,8 +325,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->builtin_methods.resize(builtin_method_map.size());
function->_builtin_methods_ptr = function->builtin_methods.ptr();
function->_builtin_methods_count = builtin_method_map.size();
- for (const Map<Variant::ValidatedBuiltInMethod, int>::Element *E = builtin_method_map.front(); E; E = E->next()) {
- function->builtin_methods.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedBuiltInMethod, int> &E : builtin_method_map) {
+ function->builtin_methods.write[E.value] = E.key;
}
} else {
function->_builtin_methods_ptr = nullptr;
@@ -337,8 +337,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->constructors.resize(constructors_map.size());
function->_constructors_ptr = function->constructors.ptr();
function->_constructors_count = constructors_map.size();
- for (const Map<Variant::ValidatedConstructor, int>::Element *E = constructors_map.front(); E; E = E->next()) {
- function->constructors.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedConstructor, int> &E : constructors_map) {
+ function->constructors.write[E.value] = E.key;
}
} else {
function->_constructors_ptr = nullptr;
@@ -349,8 +349,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->utilities.resize(utilities_map.size());
function->_utilities_ptr = function->utilities.ptr();
function->_utilities_count = utilities_map.size();
- for (const Map<Variant::ValidatedUtilityFunction, int>::Element *E = utilities_map.front(); E; E = E->next()) {
- function->utilities.write[E->get()] = E->key();
+ for (const KeyValue<Variant::ValidatedUtilityFunction, int> &E : utilities_map) {
+ function->utilities.write[E.value] = E.key;
}
} else {
function->_utilities_ptr = nullptr;
@@ -361,8 +361,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->gds_utilities.resize(gds_utilities_map.size());
function->_gds_utilities_ptr = function->gds_utilities.ptr();
function->_gds_utilities_count = gds_utilities_map.size();
- for (const Map<GDScriptUtilityFunctions::FunctionPtr, int>::Element *E = gds_utilities_map.front(); E; E = E->next()) {
- function->gds_utilities.write[E->get()] = E->key();
+ for (const KeyValue<GDScriptUtilityFunctions::FunctionPtr, int> &E : gds_utilities_map) {
+ function->gds_utilities.write[E.value] = E.key;
}
} else {
function->_gds_utilities_ptr = nullptr;
@@ -373,8 +373,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->methods.resize(method_bind_map.size());
function->_methods_ptr = function->methods.ptrw();
function->_methods_count = method_bind_map.size();
- for (const Map<MethodBind *, int>::Element *E = method_bind_map.front(); E; E = E->next()) {
- function->methods.write[E->get()] = E->key();
+ for (const KeyValue<MethodBind *, int> &E : method_bind_map) {
+ function->methods.write[E.value] = E.key;
}
} else {
function->_methods_ptr = nullptr;
@@ -385,8 +385,8 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
function->lambdas.resize(lambdas_map.size());
function->_lambdas_ptr = function->lambdas.ptrw();
function->_lambdas_count = lambdas_map.size();
- for (const Map<GDScriptFunction *, int>::Element *E = lambdas_map.front(); E; E = E->next()) {
- function->lambdas.write[E->get()] = E->key();
+ for (const KeyValue<GDScriptFunction *, int> &E : lambdas_map) {
+ function->lambdas.write[E.value] = E.key;
}
} else {
function->_lambdas_ptr = nullptr;
@@ -692,7 +692,8 @@ void GDScriptByteCodeGenerator::write_end_ternary() {
void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) {
if (HAS_BUILTIN_TYPE(p_target)) {
- if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_setter(p_target.type.builtin_type)) {
+ if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_setter(p_target.type.builtin_type) &&
+ IS_BUILTIN_TYPE(p_source, Variant::get_indexed_element_type(p_target.type.builtin_type))) {
// Use indexed setter instead.
Variant::ValidatedIndexedSetter setter = Variant::get_member_validated_indexed_setter(p_target.type.builtin_type);
append(GDScriptFunction::OPCODE_SET_INDEXED_VALIDATED, 3);
@@ -746,7 +747,8 @@ void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address
}
void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
- if (HAS_BUILTIN_TYPE(p_target) && Variant::get_member_validated_setter(p_target.type.builtin_type, p_name)) {
+ if (HAS_BUILTIN_TYPE(p_target) && Variant::get_member_validated_setter(p_target.type.builtin_type, p_name) &&
+ IS_BUILTIN_TYPE(p_source, Variant::get_member_type(p_target.type.builtin_type, p_name))) {
Variant::ValidatedSetter setter = Variant::get_member_validated_setter(p_target.type.builtin_type, p_name);
append(GDScriptFunction::OPCODE_SET_NAMED_VALIDATED, 2);
append(p_target);
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index dcc11ebdce..fbbf5802fd 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -153,12 +153,12 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
#endif
locals.resize(current_locals);
if (debug_stack) {
- for (Map<StringName, int>::Element *E = block_identifiers.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, int> &E : block_identifiers) {
GDScriptFunction::StackDebug sd;
sd.added = false;
- sd.identifier = E->key();
+ sd.identifier = E.key;
sd.line = current_line;
- sd.pos = E->get();
+ sd.pos = E.value;
stack_debug.push_back(sd);
}
block_identifiers = block_identifier_stack.back()->get();
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 8121053245..bb0d9e9e9b 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -58,27 +58,27 @@ Error GDScriptParserRef::raise_status(Status p_new_status) {
while (p_new_status > status) {
switch (status) {
case EMPTY:
- result = parser->parse(GDScriptCache::get_source_code(path), path, false);
status = PARSED;
+ result = parser->parse(GDScriptCache::get_source_code(path), path, false);
break;
case PARSED: {
analyzer = memnew(GDScriptAnalyzer(parser));
- Error inheritance_result = analyzer->resolve_inheritance();
status = INHERITANCE_SOLVED;
+ Error inheritance_result = analyzer->resolve_inheritance();
if (result == OK) {
result = inheritance_result;
}
} break;
case INHERITANCE_SOLVED: {
- Error interface_result = analyzer->resolve_interface();
status = INTERFACE_SOLVED;
+ Error interface_result = analyzer->resolve_interface();
if (result == OK) {
result = interface_result;
}
} break;
case INTERFACE_SOLVED: {
- Error body_result = analyzer->resolve_body();
status = FULLY_SOLVED;
+ Error body_result = analyzer->resolve_body();
if (result == OK) {
result = body_result;
}
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index a8aef84db3..1f9aad40af 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -109,7 +109,9 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
// Locate class by constructing the path to it and following that path
GDScriptParser::ClassNode *class_type = p_datatype.class_type;
if (class_type) {
- if ((!main_script->path.is_empty() && class_type->fqcn.begins_with(main_script->path)) || (!main_script->name.is_empty() && class_type->fqcn.begins_with(main_script->name))) {
+ const bool is_inner_by_path = (!main_script->path.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->path);
+ const bool is_inner_by_name = (!main_script->name.is_empty()) && (class_type->fqcn.split("::")[0] == main_script->name);
+ if (is_inner_by_path || is_inner_by_name) {
// Local class.
List<StringName> names;
while (class_type->outer) {
@@ -1019,25 +1021,32 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
} else if (assignment->assignee->type == GDScriptParser::Node::IDENTIFIER && _is_class_member_property(codegen, static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name)) {
// Assignment to member property.
- GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
+ GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value);
if (r_error) {
return GDScriptCodeGenerator::Address();
}
- GDScriptCodeGenerator::Address assign_temp = assigned;
+
+ GDScriptCodeGenerator::Address to_assign = assigned_value;
+ bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE;
StringName name = static_cast<GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ if (has_operation) {
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary();
GDScriptCodeGenerator::Address member = codegen.add_temporary();
gen->write_get_member(member, name);
- gen->write_binary_operator(assigned, assignment->variant_op, member, assigned);
- gen->pop_temporary();
+ gen->write_binary_operator(op_result, assignment->variant_op, member, assigned_value);
+ gen->pop_temporary(); // Pop member temp.
+ to_assign = op_result;
}
- gen->write_set_member(assigned, name);
+ gen->write_set_member(to_assign, name);
- if (assign_temp.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary(); // Pop the assigned expression or the temp result if it has operation.
+ }
+ if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary(); // Pop the assigned expression if not done before.
}
} else {
// Regular assignment.
@@ -1071,19 +1080,25 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
}
- GDScriptCodeGenerator::Address assigned = _parse_expression(codegen, r_error, assignment->assigned_value);
- GDScriptCodeGenerator::Address op_result;
+ GDScriptCodeGenerator::Address assigned_value = _parse_expression(codegen, r_error, assignment->assigned_value);
if (r_error) {
return GDScriptCodeGenerator::Address();
}
- if (assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
+ GDScriptCodeGenerator::Address to_assign;
+ bool has_operation = assignment->operation != GDScriptParser::AssignmentNode::OP_NONE;
+ if (has_operation) {
// Perform operation.
- op_result = codegen.add_temporary();
- gen->write_binary_operator(op_result, assignment->variant_op, target, assigned);
+ GDScriptCodeGenerator::Address op_result = codegen.add_temporary();
+ GDScriptCodeGenerator::Address og_value = _parse_expression(codegen, r_error, assignment->assignee);
+ gen->write_binary_operator(op_result, assignment->variant_op, og_value, assigned_value);
+ to_assign = op_result;
+
+ if (og_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
} else {
- op_result = assigned;
- assigned = GDScriptCodeGenerator::Address();
+ to_assign = assigned_value;
}
GDScriptDataType assign_type = _gdtype_from_datatype(assignment->assignee->get_datatype());
@@ -1091,25 +1106,25 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (has_setter && !is_in_setter) {
// Call setter.
Vector<GDScriptCodeGenerator::Address> args;
- args.push_back(op_result);
+ args.push_back(to_assign);
gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), setter_function, args);
} else {
// Just assign.
if (assignment->use_conversion_assign) {
- gen->write_assign_with_conversion(target, op_result);
+ gen->write_assign_with_conversion(target, to_assign);
} else {
- gen->write_assign(target, op_result);
+ gen->write_assign(target, to_assign);
}
}
- if (op_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ if (to_assign.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary(); // Pop assigned value or temp operation result.
}
- if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ if (has_operation && assigned_value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary(); // Pop assigned value if not done before.
}
if (target.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ gen->pop_temporary(); // Pop the target to assignment.
}
}
return GDScriptCodeGenerator::Address(); // Assignment does not return a value.
@@ -2074,77 +2089,18 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
Error GDScriptCompiler::_parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter) {
Error error = OK;
- CodeGen codegen;
- codegen.generator = memnew(GDScriptByteCodeGenerator);
-
- codegen.class_node = p_class;
- codegen.script = p_script;
- StringName func_name;
+ GDScriptParser::FunctionNode *function;
if (p_is_setter) {
- func_name = "@" + p_variable->identifier->name + "_setter";
+ function = p_variable->setter;
} else {
- func_name = "@" + p_variable->identifier->name + "_getter";
- }
-
- codegen.function_name = func_name;
-
- GDScriptDataType return_type;
- if (p_is_setter) {
- return_type.has_type = true;
- return_type.kind = GDScriptDataType::BUILTIN;
- return_type.builtin_type = Variant::NIL;
- } else {
- return_type = _gdtype_from_datatype(p_variable->get_datatype(), p_script);
- }
-
- codegen.generator->write_start(p_script, func_name, false, Multiplayer::RPCConfig(), return_type);
-
- if (p_is_setter) {
- uint32_t par_addr = codegen.generator->add_parameter(p_variable->setter_parameter->name, false, _gdtype_from_datatype(p_variable->get_datatype()));
- codegen.parameters[p_variable->setter_parameter->name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::FUNCTION_PARAMETER, par_addr, _gdtype_from_datatype(p_variable->get_datatype()));
- }
-
- error = _parse_block(codegen, p_is_setter ? p_variable->setter : p_variable->getter);
- if (error) {
- memdelete(codegen.generator);
- return error;
- }
-
- GDScriptFunction *gd_function = codegen.generator->write_end();
-
- p_script->member_functions[func_name] = gd_function;
-
-#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active()) {
- String signature;
- //path
- if (p_script->get_path() != String()) {
- signature += p_script->get_path();
- }
- //loc
- signature += "::" + itos(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line);
-
- //function and class
-
- if (p_class->identifier) {
- signature += "::" + String(p_class->identifier->name) + "." + String(func_name);
- } else {
- signature += "::" + String(func_name);
- }
-
- codegen.generator->set_signature(signature);
+ function = p_variable->getter;
}
-#endif
- codegen.generator->set_initial_line(p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line);
-#ifdef TOOLS_ENABLED
- p_script->member_lines[func_name] = p_is_setter ? p_variable->setter->start_line : p_variable->getter->start_line;
-#endif
- memdelete(codegen.generator);
+ _parse_function(error, p_script, p_class, function);
- return OK;
+ return error;
}
Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
@@ -2187,8 +2143,8 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->_base = nullptr;
p_script->members.clear();
p_script->constants.clear();
- for (Map<StringName, GDScriptFunction *>::Element *E = p_script->member_functions.front(); E; E = E->next()) {
- memdelete(E->get());
+ for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) {
+ memdelete(E.value);
}
p_script->member_functions.clear();
p_script->member_indices.clear();
@@ -2513,8 +2469,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
instance->owner = E->get();
//needed for hot reloading
- for (Map<StringName, GDScript::MemberInfo>::Element *F = p_script->member_indices.front(); F; F = F->next()) {
- instance->member_indices_cache[F->key()] = F->get().index;
+ for (const KeyValue<StringName, GDScript::MemberInfo> &F : p_script->member_indices) {
+ instance->member_indices_cache[F.key] = F.value.index;
}
instance->owner->set_script_instance(instance);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 2f8a054b2a..71d2699c2e 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -173,8 +173,8 @@ bool GDScriptLanguage::validate(const String &p_script, const String &p_path, Li
get_function_names_recursively(cl, "", funcs);
- for (Map<int, String>::Element *E = funcs.front(); E; E = E->next()) {
- r_functions->push_back(E->get() + ":" + itos(E->key()));
+ for (const KeyValue<int, String> &E : funcs) {
+ r_functions->push_back(E.value + ":" + itos(E.key));
}
}
@@ -344,9 +344,9 @@ void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *
const Map<StringName, GDScript::MemberInfo> &mi = script->debug_get_member_indices();
- for (const Map<StringName, GDScript::MemberInfo>::Element *E = mi.front(); E; E = E->next()) {
- p_members->push_back(E->key());
- p_values->push_back(instance->debug_get_member_by_index(E->get().index));
+ for (const KeyValue<StringName, GDScript::MemberInfo> &E : mi) {
+ p_members->push_back(E.key);
+ p_values->push_back(instance->debug_get_member_by_index(E.value.index));
}
}
@@ -370,14 +370,14 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
List<Pair<String, Variant>> cinfo;
get_public_constants(&cinfo);
- for (const Map<StringName, int>::Element *E = name_idx.front(); E; E = E->next()) {
- if (ClassDB::class_exists(E->key()) || Engine::get_singleton()->has_singleton(E->key())) {
+ for (const KeyValue<StringName, int> &E : name_idx) {
+ if (ClassDB::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) {
continue;
}
bool is_script_constant = false;
for (List<Pair<String, Variant>>::Element *CE = cinfo.front(); CE; CE = CE->next()) {
- if (CE->get().first == E->key()) {
+ if (CE->get().first == E.key) {
is_script_constant = true;
break;
}
@@ -386,7 +386,7 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
continue;
}
- const Variant &var = globals[E->value()];
+ const Variant &var = globals[E.value];
if (Object *obj = var) {
if (Object::cast_to<GDScriptNativeClass>(obj)) {
continue;
@@ -395,7 +395,7 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
bool skip = false;
for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
- if (E->key() == CoreConstants::get_global_constant_name(i)) {
+ if (E.key == CoreConstants::get_global_constant_name(i)) {
skip = true;
break;
}
@@ -404,7 +404,7 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
continue;
}
- p_globals->push_back(E->key());
+ p_globals->push_back(E.key);
p_values->push_back(var);
}
}
@@ -637,7 +637,7 @@ static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String
}
static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, Map<String, ScriptCodeCompletionOption> &r_result) {
- if (p_annotation->name == "@export_range" || p_annotation->name == "@export_exp_range") {
+ if (p_annotation->name == "@export_range") {
if (p_argument == 3 || p_argument == 4) {
// Slider hint.
ScriptCodeCompletionOption slider1("or_greater", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
@@ -875,8 +875,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
Map<StringName, Variant> constants;
scr->get_constants(&constants);
- for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ for (const KeyValue<StringName, Variant> &E : constants) {
+ ScriptCodeCompletionOption option(E.key.operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
r_result.insert(option.display, option);
}
@@ -1089,6 +1089,15 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
kwa++;
}
+ List<StringName> utility_func_names;
+ Variant::get_utility_function_list(&utility_func_names);
+
+ for (List<StringName>::Element *E = utility_func_names.front(); E; E = E->next()) {
+ ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_FUNCTION);
+ option.insert_text += "(";
+ r_result.insert(option.display, option);
+ }
+
OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
if (!E.value().is_singleton) {
@@ -1099,12 +1108,12 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
}
// Native classes and global constants.
- for (const Map<StringName, int>::Element *E = GDScriptLanguage::get_singleton()->get_global_map().front(); E; E = E->next()) {
+ for (const KeyValue<StringName, int> &E : GDScriptLanguage::get_singleton()->get_global_map()) {
ScriptCodeCompletionOption option;
- if (ClassDB::class_exists(E->key()) || Engine::get_singleton()->has_singleton(E->key())) {
- option = ScriptCodeCompletionOption(E->key().operator String(), ScriptCodeCompletionOption::KIND_CLASS);
+ if (ClassDB::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) {
+ option = ScriptCodeCompletionOption(E.key.operator String(), ScriptCodeCompletionOption::KIND_CLASS);
} else {
- option = ScriptCodeCompletionOption(E->key().operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ option = ScriptCodeCompletionOption(E.key.operator String(), ScriptCodeCompletionOption::KIND_CONSTANT);
}
r_result.insert(option.display, option);
}
@@ -2222,8 +2231,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
if (obj) {
List<String> options;
obj->get_argument_options(p_method, p_argidx, &options);
- for (const String &F : options) {
- ScriptCodeCompletionOption option(F, ScriptCodeCompletionOption::KIND_FUNCTION);
+ for (String &opt : options) {
+ if (opt.is_quoted()) {
+ opt = opt.unquote().quote(quote_style); // Handle user preference.
+ }
+ ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_FUNCTION);
r_result.insert(option.display, option);
}
}
@@ -2322,7 +2334,11 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
GDScriptCompletionIdentifier connect_base;
- if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ if (Variant::has_utility_function(call->function_name)) {
+ MethodInfo info = Variant::get_utility_function_info(call->function_name);
+ r_arghint = _make_arguments_hint(info, p_argidx);
+ return;
+ } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);
r_arghint = _make_arguments_hint(info, p_argidx);
return;
@@ -2643,23 +2659,26 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
} break;
case GDScriptParser::COMPLETION_GET_NODE: {
+ // Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically.
if (p_owner) {
List<String> opts;
p_owner->get_argument_options("get_node", 0, &opts);
for (const String &E : opts) {
+ r_forced = true;
String opt = E.strip_edges();
if (opt.is_quoted()) {
- r_forced = true;
- String idopt = opt.unquote();
- if (idopt.replace("/", "_").is_valid_identifier()) {
- ScriptCodeCompletionOption option(idopt, ScriptCodeCompletionOption::KIND_NODE_PATH);
- options.insert(option.display, option);
- } else {
- ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH);
- options.insert(option.display, option);
- }
+ // Remove quotes so that we can handle user preferred quote style,
+ // or handle NodePaths which are valid identifiers and don't need quotes.
+ opt = opt.unquote();
+ }
+ // The path needs quotes if it's not a valid identifier (with an exception
+ // for "/" as path separator, which also doesn't require quotes).
+ if (!opt.replace("/", "_").is_valid_identifier()) {
+ opt = opt.quote(quote_style); // Handle user preference.
}
+ ScriptCodeCompletionOption option(opt, ScriptCodeCompletionOption::KIND_NODE_PATH);
+ options.insert(option.display, option);
}
// Get autoloads.
@@ -2680,8 +2699,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
} break;
}
- for (Map<String, ScriptCodeCompletionOption>::Element *E = options.front(); E; E = E->next()) {
- r_options->push_back(E->get());
+ for (const KeyValue<String, ScriptCodeCompletionOption> &E : options) {
+ r_options->push_back(E.value);
}
return OK;
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index 876c508689..a3f0c7dfef 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -120,11 +120,11 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String
}
List<_GDFKCS> stackpositions;
- for (Map<StringName, _GDFKC>::Element *E = sdmap.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, _GDFKC> &E : sdmap) {
_GDFKCS spp;
- spp.id = E->key();
- spp.order = E->get().order;
- spp.pos = E->get().pos.back()->get();
+ spp.id = E.key;
+ spp.order = E.value.order;
+ spp.pos = E.value.pos.back()->get();
stackpositions.push_back(spp);
}
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 025accf4ba..40be5cb324 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -582,9 +582,9 @@ void GDScriptParser::parse_program() {
parse_class_body(true);
#ifdef TOOLS_ENABLED
- for (Map<int, GDScriptTokenizer::CommentData>::Element *E = tokenizer.get_comments().front(); E; E = E->next()) {
- if (E->get().new_line && E->get().comment.begins_with("##")) {
- class_doc_line = MIN(class_doc_line, E->key());
+ for (const KeyValue<int, GDScriptTokenizer::CommentData> &E : tokenizer.get_comments()) {
+ if (E.value.new_line && E.value.comment.begins_with("##")) {
+ class_doc_line = MIN(class_doc_line, E.key);
}
}
if (has_comment(class_doc_line)) {
@@ -947,7 +947,7 @@ GDScriptParser::VariableNode *GDScriptParser::parse_property(VariableNode *p_var
void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
switch (p_variable->property) {
- case VariableNode::PROP_INLINE:
+ case VariableNode::PROP_INLINE: {
consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected "(" after "set".)");
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected parameter name after "(".)")) {
p_variable->setter_parameter = parse_identifier();
@@ -955,9 +955,30 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected ")" after parameter name.)*");
consume(GDScriptTokenizer::Token::COLON, R"*(Expected ":" after ")".)*");
- p_variable->setter = parse_suite("setter definition");
- break;
+ IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ identifier->name = "@" + p_variable->identifier->name + "_setter";
+
+ FunctionNode *function = alloc_node<FunctionNode>();
+ function->identifier = identifier;
+
+ FunctionNode *previous_function = current_function;
+ current_function = function;
+
+ ParameterNode *parameter = alloc_node<ParameterNode>();
+ parameter->identifier = p_variable->setter_parameter;
+ function->parameters_indices[parameter->identifier->name] = 0;
+ function->parameters.push_back(parameter);
+
+ SuiteNode *body = alloc_node<SuiteNode>();
+ body->add_local(parameter, function);
+
+ function->body = parse_suite("setter declaration", body);
+
+ p_variable->setter = function;
+ current_function = previous_function;
+ break;
+ }
case VariableNode::PROP_SETGET:
consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "set")");
make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable);
@@ -972,11 +993,25 @@ void GDScriptParser::parse_property_setter(VariableNode *p_variable) {
void GDScriptParser::parse_property_getter(VariableNode *p_variable) {
switch (p_variable->property) {
- case VariableNode::PROP_INLINE:
+ case VariableNode::PROP_INLINE: {
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "get".)");
- p_variable->getter = parse_suite("getter definition");
+ IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ identifier->name = "@" + p_variable->identifier->name + "_getter";
+
+ FunctionNode *function = alloc_node<FunctionNode>();
+ function->identifier = identifier;
+
+ FunctionNode *previous_function = current_function;
+ current_function = function;
+
+ SuiteNode *body = alloc_node<SuiteNode>();
+ function->body = parse_suite("getter declaration", body);
+
+ p_variable->getter = function;
+ current_function = previous_function;
break;
+ }
case VariableNode::PROP_SETGET:
consume(GDScriptTokenizer::Token::EQUAL, R"(Expected "=" after "get")");
make_completion_context(COMPLETION_PROPERTY_METHOD, p_variable);
@@ -1620,6 +1655,10 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
n_for->list = parse_expression(false);
+ if (!n_for->list) {
+ push_error(R"(Expected a list or range after "in".)");
+ }
+
consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after "for" condition.)");
// Save break/continue state.
@@ -3419,7 +3458,7 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
String enum_hint_string;
for (const Map<StringName, int>::Element *E = export_type.enum_values.front(); E; E = E->next()) {
- enum_hint_string += E->key().operator String().camelcase_to_underscore(true).capitalize().xml_escape();
+ enum_hint_string += E->key().operator String().capitalize().xml_escape();
enum_hint_string += ":";
enum_hint_string += String::num_int64(E->get()).xml_escape();
@@ -3481,22 +3520,22 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod
}
for (int i = last; i >= 0; i--) {
String mode = p_annotation->resolved_arguments[i].operator String();
- if (mode == "any") {
- rpc_config.rpc_mode = Multiplayer::RPC_MODE_ANY;
- } else if (mode == "auth") {
+ if (mode == "any_peer") {
+ rpc_config.rpc_mode = Multiplayer::RPC_MODE_ANY_PEER;
+ } else if (mode == "authority") {
rpc_config.rpc_mode = Multiplayer::RPC_MODE_AUTHORITY;
- } else if (mode == "sync") {
- rpc_config.sync = true;
- } else if (mode == "nosync") {
- rpc_config.sync = false;
+ } else if (mode == "call_local") {
+ rpc_config.call_local = true;
+ } else if (mode == "call_remote") {
+ rpc_config.call_local = false;
} else if (mode == "reliable") {
rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
} else if (mode == "unreliable") {
rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE;
- } else if (mode == "ordered") {
- rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_ORDERED;
+ } else if (mode == "unreliable_ordered") {
+ rpc_config.transfer_mode = Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED;
} else {
- push_error(R"(Invalid RPC argument. Must be one of: 'sync'/'nosync' (local calls), 'any'/'auth' (permission), 'reliable'/'unreliable'/'ordered' (transfer mode).)", p_annotation);
+ push_error(R"(Invalid RPC argument. Must be one of: 'call_local'/'call_remote' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation);
}
}
}
@@ -4433,7 +4472,7 @@ void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) {
if (p_variable->property == VariableNode::PROP_INLINE) {
push_line(":");
increase_indent();
- print_suite(p_variable->getter);
+ print_suite(p_variable->getter->body);
decrease_indent();
} else {
push_line(" =");
@@ -4453,7 +4492,7 @@ void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) {
}
push_line("):");
increase_indent();
- print_suite(p_variable->setter);
+ print_suite(p_variable->setter->body);
decrease_indent();
} else {
push_line(" =");
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 593fb0cc5e..af9b973ada 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1109,12 +1109,12 @@ public:
PropertyStyle property = PROP_NONE;
union {
- SuiteNode *setter = nullptr;
+ FunctionNode *setter = nullptr;
IdentifierNode *setter_pointer;
};
IdentifierNode *setter_parameter = nullptr;
union {
- SuiteNode *getter = nullptr;
+ FunctionNode *getter = nullptr;
IdentifierNode *getter_pointer;
};
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
index 62531473c3..f1b0079536 100644
--- a/modules/gdscript/gdscript_utility_functions.cpp
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -317,9 +317,9 @@ struct GDScriptUtilityFunctionsDefinitions {
d["@subpath"] = cp;
d["@path"] = p->get_path();
- for (Map<StringName, GDScript::MemberInfo>::Element *E = base->member_indices.front(); E; E = E->next()) {
- if (!d.has(E->key())) {
- d[E->key()] = ins->members[E->get().index];
+ for (const KeyValue<StringName, GDScript::MemberInfo> &E : base->member_indices) {
+ if (!d.has(E.key)) {
+ d[E.key] = ins->members[E.value.index];
}
}
*r_ret = d;
@@ -396,9 +396,9 @@ struct GDScriptUtilityFunctionsDefinitions {
GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
Ref<GDScript> gd_ref = ins->get_script();
- for (Map<StringName, GDScript::MemberInfo>::Element *E = gd_ref->member_indices.front(); E; E = E->next()) {
- if (d.has(E->key())) {
- ins->members.write[E->get().index] = d[E->key()];
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : gd_ref->member_indices) {
+ if (d.has(E.key)) {
+ ins->members.write[E.value.index] = d[E.key];
}
}
}
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 9ea9fc61de..1bc7ae086f 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -88,9 +88,9 @@ static String _get_var_type(const Variant *p_var) {
Object *bobj = p_var->get_validated_object_with_check(was_freed);
if (!bobj) {
if (was_freed) {
- basestr = "null instance";
- } else {
basestr = "previously freed";
+ } else {
+ basestr = "null instance";
}
} else {
basestr = bobj->get_class();
@@ -531,8 +531,8 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script));
- for (const Map<int, Variant::Type>::Element *E = temporary_slots.front(); E; E = E->next()) {
- type_init_function_table[E->get()](&stack[E->key()]);
+ for (const KeyValue<int, Variant::Type> &E : temporary_slots) {
+ type_init_function_table[E.value](&stack[E.key]);
}
String err_text;
@@ -1233,7 +1233,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
#ifdef DEBUG_ENABLED
- if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) {
+ if (src->operator Object *() && !src->get_validated_object()) {
err_text = "Trying to cast a freed object.";
OPCODE_BREAK;
}
@@ -1263,7 +1263,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(!nc);
#ifdef DEBUG_ENABLED
- if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) {
+ if (src->operator Object *() && !src->get_validated_object()) {
err_text = "Trying to cast a freed object.";
OPCODE_BREAK;
}
@@ -1295,7 +1295,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GD_ERR_BREAK(!base_type);
#ifdef DEBUG_ENABLED
- if (src->get_type() == Variant::OBJECT && !src->operator ObjectID().is_ref_counted() && ObjectDB::get_instance(src->operator ObjectID()) == nullptr) {
+ if (src->operator Object *() && !src->get_validated_object()) {
err_text = "Trying to cast a freed object.";
OPCODE_BREAK;
}
@@ -1719,6 +1719,7 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
#define OPCODE_CALL_PTR(m_type) \
OPCODE(OPCODE_CALL_PTRCALL_##m_type) { \
CHECK_SPACE(3 + instr_arg_count); \
+ ip += instr_arg_count; \
int argc = _code_ptr[ip + 1]; \
GET_INSTRUCTION_ARG(base, argc); \
MethodBind *method = _methods_ptr[_code_ptr[ip + 2]]; \
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index d106b3b541..f4c0c4d9bb 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -165,7 +165,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
case ClassNode::Member::VARIABLE: {
lsp::DocumentSymbol symbol;
symbol.name = m.variable->identifier->name;
- symbol.kind = m.variable->property == VariableNode::PropertyStyle::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property;
+ symbol.kind = m.variable->property == VariableNode::PROP_NONE ? lsp::SymbolKind::Variable : lsp::SymbolKind::Property;
symbol.deprecated = false;
symbol.range.start.line = LINE_NUMBER_TO_INDEX(m.variable->start_line);
symbol.range.start.character = LINE_NUMBER_TO_INDEX(m.variable->start_column);
@@ -491,7 +491,7 @@ String ExtendGDScriptParser::get_text_for_completion(const lsp::Position &p_curs
return longthing;
}
-String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol, bool p_func_requred) const {
+String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol, bool p_func_required) const {
String longthing;
int len = lines.size();
for (int i = 0; i < len; i++) {
@@ -513,7 +513,7 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c
longthing += first_part;
longthing += String::chr(0xFFFF); //not unicode, represents the cursor
- if (p_func_requred) {
+ if (p_func_required) {
longthing += "("; // tell the parser this is a function call
}
longthing += last_part;
@@ -532,6 +532,9 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c
String ExtendGDScriptParser::get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const {
ERR_FAIL_INDEX_V(p_position.line, lines.size(), "");
String line = lines[p_position.line];
+ if (line.is_empty()) {
+ return "";
+ }
ERR_FAIL_INDEX_V(p_position.character, line.size(), "");
int start_pos = p_position.character;
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h
index 28b9b3c82a..5d7b16765b 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -85,7 +85,7 @@ public:
Error get_left_function_call(const lsp::Position &p_position, lsp::Position &r_func_pos, int &r_arg_index) const;
String get_text_for_completion(const lsp::Position &p_cursor) const;
- String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_requred = false) const;
+ String get_text_for_lookup_symbol(const lsp::Position &p_cursor, const String &p_symbol = "", bool p_func_required = false) const;
String get_identifier_under_position(const lsp::Position &p_position, Vector2i &p_offset) const;
String get_uri() const;
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index b6c48468f5..5cf1e0fc5f 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -212,11 +212,11 @@ void GDScriptLanguageProtocol::initialized(const Variant &p_params) {
lsp::GodotCapabilities capabilities;
DocTools *doc = EditorHelp::get_doc_data();
- for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {
+ for (const KeyValue<String, DocData::ClassDoc> &E : doc->class_list) {
lsp::GodotNativeClassInfo gdclass;
- gdclass.name = E->get().name;
- gdclass.class_doc = &(E->get());
- if (ClassDB::ClassInfo *ptr = ClassDB::classes.getptr(StringName(E->get().name))) {
+ gdclass.name = E.value.name;
+ gdclass.class_doc = &(E.value);
+ if (ClassDB::ClassInfo *ptr = ClassDB::classes.getptr(StringName(E.value.name))) {
gdclass.class_info = ptr;
}
capabilities.native_classes.push_back(gdclass);
@@ -284,6 +284,23 @@ void GDScriptLanguageProtocol::notify_client(const String &p_method, const Varia
peer->res_queue.push_back(msg.utf8());
}
+void GDScriptLanguageProtocol::request_client(const String &p_method, const Variant &p_params, int p_client_id) {
+ if (p_client_id == -1) {
+ ERR_FAIL_COND_MSG(latest_client_id == -1,
+ "GDScript LSP: Can't notify client as none was connected.");
+ p_client_id = latest_client_id;
+ }
+ ERR_FAIL_COND(!clients.has(p_client_id));
+ Ref<LSPeer> peer = clients.get(p_client_id);
+ ERR_FAIL_COND(peer == nullptr);
+
+ Dictionary message = make_request(p_method, p_params, next_server_id);
+ next_server_id++;
+ String msg = Variant(message).to_json_string();
+ msg = format_output(msg);
+ peer->res_queue.push_back(msg.utf8());
+}
+
bool GDScriptLanguageProtocol::is_smart_resolve_enabled() const {
return bool(_EDITOR_GET("network/language_server/enable_smart_resolve"));
}
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index 5a2dd55c46..899446fb42 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -79,6 +79,8 @@ private:
int latest_client_id = 0;
int next_client_id = 0;
+ int next_server_id = 0;
+
Ref<GDScriptTextDocument> text_document;
Ref<GDScriptWorkspace> workspace;
@@ -107,6 +109,7 @@ public:
void stop();
void notify_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1);
+ void request_client(const String &p_method, const Variant &p_params = Variant(), int p_client_id = -1);
bool is_smart_resolve_enabled() const;
bool is_goto_native_symbols_enabled() const;
diff --git a/modules/gdscript/language_server/gdscript_text_document.cpp b/modules/gdscript/language_server/gdscript_text_document.cpp
index 03b1e3fa44..92ce71f395 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -217,8 +217,8 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
arr = native_member_completions.duplicate();
- for (Map<String, ExtendGDScriptParser *>::Element *E = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.front(); E; E = E->next()) {
- ExtendGDScriptParser *script = E->get();
+ for (KeyValue<String, ExtendGDScriptParser *> &E : GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts) {
+ ExtendGDScriptParser *script = E.value;
const Array &items = script->get_member_completions();
const int start_size = arr.size();
@@ -428,6 +428,9 @@ GDScriptTextDocument::~GDScriptTextDocument() {
void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) {
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path);
+ if (!path.begins_with("res://")) {
+ return;
+ }
GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content);
EditorFileSystem::get_singleton()->update_file(path);
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 1512b4bb89..371e3de419 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -42,6 +42,7 @@
#include "scene/resources/packed_scene.h"
void GDScriptWorkspace::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("apply_new_signal"), &GDScriptWorkspace::apply_new_signal);
ClassDB::bind_method(D_METHOD("didDeleteFiles"), &GDScriptWorkspace::did_delete_files);
ClassDB::bind_method(D_METHOD("symbol"), &GDScriptWorkspace::symbol);
ClassDB::bind_method(D_METHOD("parse_script", "path", "content"), &GDScriptWorkspace::parse_script);
@@ -52,6 +53,54 @@ void GDScriptWorkspace::_bind_methods() {
ClassDB::bind_method(D_METHOD("generate_script_api", "path"), &GDScriptWorkspace::generate_script_api);
}
+void GDScriptWorkspace::apply_new_signal(Object *obj, String function, PackedStringArray args) {
+ String function_signature = "func " + function;
+ Ref<Script> script = obj->get_script();
+
+ String source = script->get_source_code();
+
+ if (source.find(function_signature) != -1) {
+ return;
+ }
+
+ int first_class = source.find("\nclass ");
+ int start_line = 0;
+ if (first_class != -1) {
+ start_line = source.substr(0, first_class).split("\n").size();
+ } else {
+ start_line = source.split("\n").size();
+ }
+
+ String function_body = "\n\n" + function_signature + "(";
+ for (int i = 0; i < args.size(); ++i) {
+ function_body += args[i];
+ if (i < args.size() - 1) {
+ function_body += ", ";
+ }
+ }
+ function_body += ")";
+ if (EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints")) {
+ function_body += " -> void";
+ }
+ function_body += ":\n\tpass # Replace with function body.\n";
+
+ lsp::TextEdit text_edit;
+
+ if (first_class != -1) {
+ function_body += "\n\n";
+ }
+ text_edit.range.end.line = text_edit.range.start.line = start_line;
+
+ text_edit.newText = function_body;
+
+ String uri = get_file_uri(script->get_path());
+
+ lsp::ApplyWorkspaceEditParams params;
+ params.edit.add_edit(uri, text_edit);
+
+ GDScriptLanguageProtocol::get_singleton()->request_client("workspace/applyEdit", params.to_json());
+}
+
void GDScriptWorkspace::did_delete_files(const Dictionary &p_params) {
Array files = p_params["files"];
for (int i = 0; i < files.size(); ++i) {
@@ -212,9 +261,9 @@ Array GDScriptWorkspace::symbol(const Dictionary &p_params) {
String query = p_params["query"];
Array arr;
if (!query.is_empty()) {
- for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {
+ for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
Vector<lsp::DocumentedSymbolInformation> script_symbols;
- E->get()->get_symbols().symbol_tree_as_list(E->key(), script_symbols);
+ E.value->get_symbols().symbol_tree_as_list(E.key, script_symbols);
for (int i = 0; i < script_symbols.size(); ++i) {
if (query.is_subsequence_ofi(script_symbols[i].name)) {
lsp::DocumentedSymbolInformation symbol = script_symbols[i];
@@ -233,10 +282,10 @@ Error GDScriptWorkspace::initialize() {
}
DocTools *doc = EditorHelp::get_doc_data();
- for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {
- const DocData::ClassDoc &class_data = E->value();
+ for (const KeyValue<String, DocData::ClassDoc> &E : doc->class_list) {
+ const DocData::ClassDoc &class_data = E.value;
lsp::DocumentSymbol class_symbol;
- String class_name = E->key();
+ String class_name = E.key;
class_symbol.name = class_name;
class_symbol.native_class = class_name;
class_symbol.kind = lsp::SymbolKind::Class;
@@ -344,22 +393,25 @@ Error GDScriptWorkspace::initialize() {
reload_all_workspace_scripts();
if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
- for (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, lsp::DocumentSymbol> &E : native_symbols) {
ClassMembers members;
- const lsp::DocumentSymbol &class_symbol = E->get();
+ const lsp::DocumentSymbol &class_symbol = E.value;
for (int i = 0; i < class_symbol.children.size(); i++) {
const lsp::DocumentSymbol &symbol = class_symbol.children[i];
members.set(symbol.name, &symbol);
}
- native_members.set(E->key(), members);
+ native_members.set(E.key, members);
}
// cache member completions
- for (Map<String, ExtendGDScriptParser *>::Element *S = scripts.front(); S; S = S->next()) {
- S->get()->get_member_completions();
+ for (const KeyValue<String, ExtendGDScriptParser *> &S : scripts) {
+ S.value->get_member_completions();
}
}
+ EditorNode *editor_node = EditorNode::get_singleton();
+ editor_node->connect("script_add_function_request", callable_mp(this, &GDScriptWorkspace::apply_new_signal));
+
return OK;
}
@@ -551,7 +603,7 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
}
}
-const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name, bool p_func_requred) {
+const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name, bool p_func_required) {
const lsp::DocumentSymbol *symbol = nullptr;
String path = get_file_path(p_doc_pos.textDocument.uri);
@@ -576,7 +628,10 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
} else {
ScriptLanguage::LookupResult ret;
- if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_requred), symbol_identifier, path, nullptr, ret)) {
+ if (symbol_identifier == "new" && parser->get_lines()[p_doc_pos.position.line].replace(" ", "").replace("\t", "").find("new(") > -1) {
+ symbol_identifier = "_init";
+ }
+ if (OK == GDScriptLanguage::get_singleton()->lookup_code(parser->get_text_for_lookup_symbol(pos, symbol_identifier, p_func_required), symbol_identifier, path, nullptr, ret)) {
if (ret.type == ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION) {
String target_script_path = path;
if (!ret.script.is_null()) {
@@ -630,8 +685,8 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
class_ptr = native_members.next(class_ptr);
}
- for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {
- const ExtendGDScriptParser *script = E->get();
+ for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
+ const ExtendGDScriptParser *script = E.value;
const ClassMembers &members = script->get_members();
if (const lsp::DocumentSymbol *const *symbol = members.getptr(symbol_identifier)) {
r_list.push_back(*symbol);
@@ -731,12 +786,12 @@ GDScriptWorkspace::GDScriptWorkspace() {
GDScriptWorkspace::~GDScriptWorkspace() {
Set<String> cached_parsers;
- for (Map<String, ExtendGDScriptParser *>::Element *E = parse_results.front(); E; E = E->next()) {
- cached_parsers.insert(E->key());
+ for (const KeyValue<String, ExtendGDScriptParser *> &E : parse_results) {
+ cached_parsers.insert(E.key);
}
- for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {
- cached_parsers.insert(E->key());
+ for (const KeyValue<String, ExtendGDScriptParser *> &E : scripts) {
+ cached_parsers.insert(E.key);
}
for (Set<String>::Element *E = cached_parsers.front(); E; E = E->next()) {
diff --git a/modules/gdscript/language_server/gdscript_workspace.h b/modules/gdscript/language_server/gdscript_workspace.h
index 9496677449..6f5600b5cf 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -62,6 +62,8 @@ protected:
void list_script_files(const String &p_root_dir, List<String> &r_files);
+ void apply_new_signal(Object *obj, String function, PackedStringArray args);
+
public:
String root;
String root_uri;
@@ -85,7 +87,7 @@ public:
void publish_diagnostics(const String &p_path);
void completion(const lsp::CompletionParams &p_params, List<ScriptCodeCompletionOption> *r_options);
- const lsp::DocumentSymbol *resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name = "", bool p_func_requred = false);
+ const lsp::DocumentSymbol *resolve_symbol(const lsp::TextDocumentPositionParams &p_doc_pos, const String &p_symbol_name = "", bool p_func_required = false);
void resolve_related_symbols(const lsp::TextDocumentPositionParams &p_doc_pos, List<const lsp::DocumentSymbol *> &r_list);
const lsp::DocumentSymbol *resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params);
void resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list);
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index 9ac6c6bd4e..3710a84a28 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -263,19 +263,29 @@ struct WorkspaceEdit {
*/
Map<String, Vector<TextEdit>> changes;
+ _FORCE_INLINE_ void add_edit(const String &uri, const TextEdit &edit) {
+ if (changes.has(uri)) {
+ changes[uri].push_back(edit);
+ } else {
+ Vector<TextEdit> edits;
+ edits.push_back(edit);
+ changes[uri] = edits;
+ }
+ }
+
_FORCE_INLINE_ Dictionary to_json() const {
Dictionary dict;
Dictionary out_changes;
- for (Map<String, Vector<TextEdit>>::Element *E = changes.front(); E; E = E->next()) {
+ for (const KeyValue<String, Vector<TextEdit>> &E : changes) {
Array edits;
- for (int i = 0; i < E->get().size(); ++i) {
+ for (int i = 0; i < E.value.size(); ++i) {
Dictionary text_edit;
- text_edit["range"] = E->get()[i].range.to_json();
- text_edit["newText"] = E->get()[i].newText;
+ text_edit["range"] = E.value[i].range.to_json();
+ text_edit["newText"] = E.value[i].newText;
edits.push_back(text_edit);
}
- out_changes[E->key()] = edits;
+ out_changes[E.key] = edits;
}
dict["changes"] = out_changes;
@@ -1322,6 +1332,18 @@ struct DocumentSymbol {
}
};
+struct ApplyWorkspaceEditParams {
+ WorkspaceEdit edit;
+
+ Dictionary to_json() {
+ Dictionary dict;
+
+ dict["edit"] = edit.to_json();
+
+ return dict;
+ }
+};
+
struct NativeSymbolInspectParams {
String native_class;
String symbol_name;
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index c383830c82..50c1f68440 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -133,13 +133,12 @@ GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_l
if (do_init_languages) {
init_language(p_source_dir);
-
- // Enable all warnings for GDScript, so we can test them.
- ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
- for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
- String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
- ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true);
- }
+ }
+ // Enable all warnings for GDScript, so we can test them.
+ ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/enable", true);
+ for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
+ String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower();
+ ProjectSettings::get_singleton()->set_setting("debug/gdscript/warnings/" + warning, true);
}
// Enable printing to show results
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.gd
new file mode 100644
index 0000000000..b84ccdce81
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.gd
@@ -0,0 +1,5 @@
+class Vector2:
+ pass
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out
new file mode 100644
index 0000000000..87863baf75
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/class_name_shadows_builtin_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "Vector2" cannot have the same name as a builtin type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.gd
new file mode 100644
index 0000000000..a7c0a29a69
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.gd
@@ -0,0 +1,4 @@
+const Vector2 = 0
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.out
new file mode 100644
index 0000000000..87863baf75
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/constant_name_shadows_builtin_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "Vector2" cannot have the same name as a builtin type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.gd
new file mode 100644
index 0000000000..930f91b389
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.gd
@@ -0,0 +1,4 @@
+enum Vector2 { A, B }
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.out
new file mode 100644
index 0000000000..87863baf75
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/enum_name_shadows_builtin_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "Vector2" cannot have the same name as a builtin type.
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd
new file mode 100644
index 0000000000..f1be6aaa0c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.gd
@@ -0,0 +1,11 @@
+var _prop : int
+
+# Getter function has wrong return type.
+var prop : String:
+ get = get_prop
+
+func get_prop():
+ return _prop
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out
new file mode 100644
index 0000000000..29eec51ef2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_get_type_error.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Function with return type "int" cannot be used as getter for a property of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd
new file mode 100644
index 0000000000..dd190157a1
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.gd
@@ -0,0 +1,11 @@
+var _prop : int
+
+# Setter function has wrong argument type.
+var prop : String:
+ set = set_prop
+
+func set_prop(value : int):
+ _prop = value
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out
new file mode 100644
index 0000000000..7a25280d55
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_function_set_type_error.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Function with argument type "int" cannot be used as setter for a property of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd
new file mode 100644
index 0000000000..7f2b29222a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.gd
@@ -0,0 +1,9 @@
+var _prop : int
+
+# Inline getter returns int instead of String.
+var prop : String:
+ get:
+ return _prop
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out
new file mode 100644
index 0000000000..e0adef1bf8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_get_type_error.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot return value of type "int" because the function return type is "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd
new file mode 100644
index 0000000000..0ce239dbbd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.gd
@@ -0,0 +1,9 @@
+var _prop : int
+
+# Inline setter assigns String to int.
+var prop : String:
+ set(value):
+ _prop = value
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out
new file mode 100644
index 0000000000..bbadf1ce27
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/property_inline_set_type_error.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Cannot assign a value of type "String" to a target of type "int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd
new file mode 100644
index 0000000000..3bbee5f5f7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.gd
@@ -0,0 +1,8 @@
+var with_setter := 0:
+ set(val):
+ var x: String = val
+ with_setter = val
+
+func test():
+ with_setter = 1
+ print(with_setter)
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out
new file mode 100644
index 0000000000..9eb2a42ccd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/setter_parameter_uses_property_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Value of type "int" cannot be assigned to a variable of type "String".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.gd b/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.gd
new file mode 100644
index 0000000000..7cba29884c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.gd
@@ -0,0 +1,4 @@
+var Vector2
+
+func test():
+ pass
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.out b/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.out
new file mode 100644
index 0000000000..87863baf75
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/variable_name_shadows_builtin_type.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+The member "Vector2" cannot have the same name as a builtin type.
diff --git a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out
index 0e9f482af4..481016138a 100644
--- a/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out
+++ b/modules/gdscript/tests/scripts/analyzer/features/auto_inferred_type_dont_error.out
@@ -1,2 +1,6 @@
GDTEST_OK
+>> WARNING
+>> Line: 6
+>> UNSAFE_METHOD_ACCESS
+>> The method 'free' is not present on the inferred type 'Variant' (but may be present on a subtype).
Ok
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
new file mode 100644
index 0000000000..30e7deb05a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.gd
@@ -0,0 +1,19 @@
+class A:
+ var x = 3
+
+class B:
+ var x = 4
+
+class C:
+ var x = 5
+
+class Test:
+ var a = A.new()
+ var b: B = B.new()
+ var c := C.new()
+
+func test():
+ var test_instance := Test.new()
+ prints(test_instance.a.x)
+ prints(test_instance.b.x)
+ prints(test_instance.c.x)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out
new file mode 100644
index 0000000000..a078e62cc7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_from_parent.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+3
+4
+5
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd
new file mode 100644
index 0000000000..630b20c282
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.gd
@@ -0,0 +1,11 @@
+# https://github.com/godotengine/godot/issues/43503
+
+var test_var = null
+
+
+func test():
+ print(test_var.x)
+
+
+func _init():
+ test_var = Vector3()
diff --git a/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out
new file mode 100644
index 0000000000..94e2ec2af8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/class_inference_is_weak.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+0
diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd
new file mode 100644
index 0000000000..b3784dffa3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.gd
@@ -0,0 +1,14 @@
+# https://github.com/godotengine/godot/issues/41064
+var x = true
+
+func test():
+ var int_var: int = 0
+ var dyn_var = 2
+
+ if x:
+ dyn_var = 5
+ else:
+ dyn_var = Node.new()
+
+ int_var = dyn_var
+ print(int_var)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out
new file mode 100644
index 0000000000..952029f665
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/local_inference_is_weak.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+5
diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd b/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd
new file mode 100644
index 0000000000..1706087f82
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/property_functions.gd
@@ -0,0 +1,16 @@
+var _prop = 1
+var prop:
+ get = get_prop, set = set_prop
+
+func get_prop():
+ return _prop
+
+func set_prop(value):
+ _prop = value
+
+func test():
+ print(prop)
+
+ prop = 2
+
+ print(prop)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_functions.out b/modules/gdscript/tests/scripts/analyzer/features/property_functions.out
new file mode 100644
index 0000000000..f1253ca57e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/property_functions.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+1
+2
diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd b/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd
new file mode 100644
index 0000000000..23eb011b23
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.gd
@@ -0,0 +1,46 @@
+# Untyped inline property
+var prop1:
+ get:
+ return prop1
+ set(value):
+ prop1 = value
+
+# Typed inline property
+var prop2 : int:
+ get:
+ return prop2
+ set(value):
+ prop2 = value
+
+# Typed inline property with default value
+var prop3 : int = 1:
+ get:
+ return prop3
+ set(value):
+ prop3 = value
+
+# Typed inline property with backing variable
+var _prop4 : int = 2
+var prop4: int:
+ get:
+ return _prop4
+ set(value):
+ _prop4 = value
+
+func test():
+ print(prop1)
+ print(prop2)
+ print(prop3)
+ print(prop4)
+
+ print()
+
+ prop1 = 1
+ prop2 = 2
+ prop3 = 3
+ prop4 = 4
+
+ print(prop1)
+ print(prop2)
+ print(prop3)
+ print(prop4)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/property_inline.out b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out
new file mode 100644
index 0000000000..5482592e90
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/property_inline.out
@@ -0,0 +1,10 @@
+GDTEST_OK
+null
+0
+1
+2
+
+1
+2
+3
+4
diff --git a/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd
new file mode 100644
index 0000000000..f9a8b23b92
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.gd
@@ -0,0 +1,8 @@
+# https://github.com/godotengine/godot/issues/43221
+extends Node
+
+func test():
+ name = "Node"
+ print(self["name"])
+ self["name"] = "Changed"
+ print(name)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out
new file mode 100644
index 0000000000..6417f4f8da
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/subscript_self.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+Node
+Changed
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd
new file mode 100644
index 0000000000..9502f6e196
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.gd
@@ -0,0 +1,10 @@
+class Inner:
+ var prop = "Inner"
+
+
+var array: Array[Inner] = [Inner.new()]
+
+
+func test():
+ var element: Inner = array[0]
+ print(element.prop)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out
new file mode 100644
index 0000000000..8f250d2632
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/typed_array_with_custom_class.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+Inner
diff --git a/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd
new file mode 100644
index 0000000000..18174eae67
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.gd
@@ -0,0 +1,32 @@
+# https://github.com/godotengine/godot/issues/48121
+
+func test():
+ var x := []
+ var y := []
+ x.push_back(y)
+ print("TEST ARRAY ADD TO SELF: " + str(len(y)))
+ x.clear()
+
+ x = Array()
+ y = Array()
+ x.push_back(y)
+ print("TEST ARRAY ADD TO SELF: " + str(len(y)))
+ x.clear()
+
+ x = Array().duplicate()
+ y = Array().duplicate()
+ x.push_back(y)
+ print("TEST ARRAY ADD TO SELF: " + str(len(y)))
+ x.clear()
+
+ x = [].duplicate()
+ y = [].duplicate()
+ x.push_back(y)
+ print("TEST ARRAY ADD TO SELF: " + str(len(y)))
+ x.clear()
+
+ x = Array()
+ y = Array()
+ x.push_back(y)
+ print("TEST ARRAY ADD TO SELF: " + str(len(y)))
+ x.clear()
diff --git a/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out
new file mode 100644
index 0000000000..f6b7d3cc39
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/arrays_arent_shared.out
@@ -0,0 +1,6 @@
+GDTEST_OK
+TEST ARRAY ADD TO SELF: 0
+TEST ARRAY ADD TO SELF: 0
+TEST ARRAY ADD TO SELF: 0
+TEST ARRAY ADD TO SELF: 0
+TEST ARRAY ADD TO SELF: 0
diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd
new file mode 100644
index 0000000000..f6526aefb4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.gd
@@ -0,0 +1,13 @@
+extends Node
+
+func test():
+ process_priority = 10
+ var change = 20
+
+ print(process_priority)
+ print(change)
+
+ process_priority += change
+
+ print(process_priority)
+ print(change)
diff --git a/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out
new file mode 100644
index 0000000000..c9e6b34c77
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/assign_member_with_operation.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+10
+20
+30
+20
diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.gd b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.gd
new file mode 100644
index 0000000000..d5a5f8de64
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.gd
@@ -0,0 +1,19 @@
+# https://github.com/godotengine/godot/issues/48121
+
+func test():
+ var x := Dictionary()
+ var y := Dictionary()
+ y[0]=1
+ y[1]=1
+ y[2]=1
+ print("TEST OTHER DICTIONARY: " + str(len(x)))
+ x.clear()
+
+ x = Dictionary().duplicate()
+ y = Dictionary().duplicate()
+ y[0]=1
+ y[1]=1
+ y[2]=1
+ print("TEST OTHER DICTIONARY: " + str(len(x)))
+ x.clear()
+ return
diff --git a/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out
new file mode 100644
index 0000000000..0bf49f5934
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/dictionaries_arent_shared.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+TEST OTHER DICTIONARY: 0
+TEST OTHER DICTIONARY: 0
diff --git a/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd b/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd
new file mode 100644
index 0000000000..c9b5f8481e
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/lua_assign.gd
@@ -0,0 +1,4 @@
+func test():
+ var dict = {}
+ dict.test = 1
+ print(dict.test)
diff --git a/modules/gdscript/tests/scripts/runtime/features/lua_assign.out b/modules/gdscript/tests/scripts/runtime/features/lua_assign.out
new file mode 100644
index 0000000000..a7f1357bb2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/lua_assign.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+1
diff --git a/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd b/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd
new file mode 100644
index 0000000000..3eb02816ed
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.gd
@@ -0,0 +1,11 @@
+#GDTEST_OK
+var prop : int = 0:
+ get:
+ return prop
+ set(value):
+ prop = value % 7
+
+func test():
+ for i in 7:
+ prop += 1
+ print(prop)
diff --git a/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.out b/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.out
new file mode 100644
index 0000000000..76157853f2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/property_with_operator_assignment.out
@@ -0,0 +1,8 @@
+GDTEST_OK
+1
+2
+3
+4
+5
+6
+0
diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.gd b/modules/gdscript/tests/scripts/runtime/features/stringify.gd
new file mode 100644
index 0000000000..de269b92b3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/stringify.gd
@@ -0,0 +1,42 @@
+func test():
+ print(true, false)
+ print(-1, 0, 1)
+ print(-1.25, 0.25, 1.25)
+ print("hello world")
+
+ print(Vector2(0.25, 0.25))
+ print(Vector2i(0, 0))
+
+ print(Rect2(0.25, 0.25, 0.5, 0.5))
+ print(Rect2i(0, 0, 0, 0))
+
+ print(Vector3(0.25, 0.25, 0.25))
+ print(Vector3i(0, 0, 0))
+
+ print(Transform2D.IDENTITY)
+ print(Plane(1, 2, 3, 4))
+ print(Quaternion(1, 2, 3, 4))
+ print(AABB(Vector3.ZERO, Vector3.ONE))
+ print(Basis(Vector3(0, 0, 0)))
+ print(Transform3D.IDENTITY)
+
+ print(Color(1, 2, 3, 4))
+ print(StringName("hello"))
+ print(NodePath("hello/world"))
+ var node := Node.new()
+ print(RID(node))
+ print(node.get_name)
+ print(node.property_list_changed)
+ node.free()
+ print({"hello":123})
+ print(["hello", 123])
+
+ print(PackedByteArray([-1, 0, 1]))
+ print(PackedInt32Array([-1, 0, 1]))
+ print(PackedInt64Array([-1, 0, 1]))
+ print(PackedFloat32Array([-1, 0, 1]))
+ print(PackedFloat64Array([-1, 0, 1]))
+ print(PackedStringArray(["hello", "world"]))
+ print(PackedVector2Array([Vector2.ONE, Vector2.ZERO]))
+ print(PackedVector3Array([Vector3.ONE, Vector3.ZERO]))
+ print(PackedColorArray([Color.RED, Color.BLUE, Color.GREEN]))
diff --git a/modules/gdscript/tests/scripts/runtime/features/stringify.out b/modules/gdscript/tests/scripts/runtime/features/stringify.out
new file mode 100644
index 0000000000..7670fc0128
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/stringify.out
@@ -0,0 +1,34 @@
+GDTEST_OK
+truefalse
+-101
+-1.250.251.25
+hello world
+(0.25, 0.25)
+(0, 0)
+[P: (0.25, 0.25), S: (0.5, 0.5)]
+[P: (0, 0), S: (0, 0)]
+(0.25, 0.25, 0.25)
+(0, 0, 0)
+[X: (1, 0), Y: (0, 1), O: (0, 0)]
+[N: (1, 2, 3), D: 4]
+(1, 2, 3, 4)
+[P: (0, 0, 0), S: (1, 1, 1)]
+[X: (1, 0, 0), Y: (0, 1, 0), Z: (0, 0, 1)]
+[X: (1, 0, 0), Y: (0, 1, 0), Z: (0, 0, 1), O: (0, 0, 0)]
+(1, 2, 3, 4)
+hello
+hello/world
+RID(0)
+Node::get_name
+Node::[signal]property_list_changed
+{hello:123}
+[hello, 123]
+[255, 0, 1]
+[-1, 0, 1]
+[-1, 0, 1]
+[-1, 0, 1]
+[-1, 0, 1]
+[hello, world]
+[(1, 1), (0, 0)]
+[(1, 1, 1), (0, 0, 0)]
+[(1, 0, 0, 1), (0, 0, 1, 1), (0, 1, 0, 1)]
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index e54f055f2b..80eabc1596 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -172,8 +172,8 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
return;
}
- for (const Map<StringName, GDScriptFunction *>::Element *E = script->get_member_functions().front(); E; E = E->next()) {
- const GDScriptFunction *func = E->value();
+ for (const KeyValue<StringName, GDScriptFunction *> &E : script->get_member_functions()) {
+ const GDScriptFunction *func = E.value;
String signature = "Disassembling " + func->get_name().operator String() + "(";
for (int i = 0; i < func->get_argument_count(); i++) {