summaryrefslogtreecommitdiff
path: root/modules/gdscript
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript')
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml2
-rw-r--r--modules/gdscript/gdscript.cpp66
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp72
-rw-r--r--modules/gdscript/gdscript_cache.cpp58
-rw-r--r--modules/gdscript/gdscript_cache.h3
-rw-r--r--modules/gdscript/gdscript_compiler.cpp21
-rw-r--r--modules/gdscript/gdscript_compiler.h1
-rw-r--r--modules/gdscript/gdscript_editor.cpp108
-rw-r--r--modules/gdscript/gdscript_parser.cpp23
-rw-r--r--modules/gdscript/gdscript_parser.h5
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp3
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp1
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp41
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd25
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out17
20 files changed, 277 insertions, 179 deletions
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index c8eda53a2d..4981750b7d 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -547,7 +547,7 @@
<return type="void" />
<param index="0" name="icon_path" type="String" />
<description>
- Add a custom icon to the current script. After loading an icon at [param icon_path], the icon is displayed in the Scene dock for every node that the script is attached to. For named classes, the icon is also displayed in various editor dialogs.
+ Add a custom icon to the current script. The script must be registered as a global class using the [code]class_name[/code] keyword for this to have a visible effect. The icon specified at [param icon_path] is displayed in the Scene dock for every node of that class, as well as in various editor dialogs.
[codeblock]
@icon("res://path/to/class/icon.svg")
[/codeblock]
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 60230257e0..c8195de640 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2019,6 +2019,42 @@ Error GDScriptLanguage::execute_file(const String &p_path) {
}
void GDScriptLanguage::finish() {
+ if (_call_stack) {
+ memdelete_arr(_call_stack);
+ _call_stack = nullptr;
+ }
+
+ // Clear the cache before parsing the script_list
+ GDScriptCache::clear();
+
+ // Clear dependencies between scripts, to ensure cyclic references are broken
+ // (to avoid leaks at exit).
+ SelfList<GDScript> *s = script_list.first();
+ while (s) {
+ // This ensures the current script is not released before we can check
+ // what's the next one in the list (we can't get the next upfront because we
+ // don't know if the reference breaking will cause it -or any other after
+ // it, for that matter- to be released so the next one is not the same as
+ // before).
+ Ref<GDScript> scr = s->self();
+ if (scr.is_valid()) {
+ for (KeyValue<StringName, GDScriptFunction *> &E : scr->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 (KeyValue<StringName, GDScript::MemberInfo> &E : scr->member_indices) {
+ E.value.data_type.script_type_ref = Ref<Script>();
+ }
+
+ // Clear backup for scripts that could slip out of the cyclic reference
+ // check
+ scr->clear();
+ }
+ s = s->next();
+ }
}
void GDScriptLanguage::profiling_start() {
@@ -2530,36 +2566,6 @@ GDScriptLanguage::GDScriptLanguage() {
}
GDScriptLanguage::~GDScriptLanguage() {
- if (_call_stack) {
- memdelete_arr(_call_stack);
- }
-
- // Clear dependencies between scripts, to ensure cyclic references are broken (to avoid leaks at exit).
- SelfList<GDScript> *s = script_list.first();
- while (s) {
- // This ensures the current script is not released before we can check what's the next one
- // in the list (we can't get the next upfront because we don't know if the reference breaking
- // will cause it -or any other after it, for that matter- to be released so the next one
- // is not the same as before).
- Ref<GDScript> scr = s->self();
- if (scr.is_valid()) {
- for (KeyValue<StringName, GDScriptFunction *> &E : scr->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 (KeyValue<StringName, GDScript::MemberInfo> &E : scr->member_indices) {
- E.value.data_type.script_type_ref = Ref<Script>();
- }
-
- // Clear backup for scripts that could slip out of the cyclic reference check
- scr->clear();
- }
- s = s->next();
- }
-
singleton = nullptr;
}
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 584bb74e4f..663d72038d 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -196,8 +196,11 @@ Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::C
while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) {
GDScriptParser::ClassNode *current_class_node = current_data_type->class_type;
if (has_member_name_conflict_in_script_class(p_member_name, current_class_node, p_member_node)) {
- push_error(vformat(R"(The member "%s" already exists in parent class %s.)", p_member_name, current_class_node->identifier->name),
- p_member_node);
+ String parent_class_name = current_class_node->fqcn;
+ if (current_class_node->identifier != nullptr) {
+ parent_class_name = current_class_node->identifier->name;
+ }
+ push_error(vformat(R"(The member "%s" already exists in parent class %s.)", p_member_name, parent_class_name), p_member_node);
return ERR_PARSE_ERROR;
}
current_data_type = &current_class_node->base_type;
@@ -494,8 +497,8 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result = ref->get_parser()->head->get_datatype();
} else {
result.kind = GDScriptParser::DataType::SCRIPT;
- result.native_type = ScriptServer::get_global_class_native_base(first);
result.script_type = ResourceLoader::load(path, "Script");
+ result.native_type = result.script_type->get_instance_base_type();
result.script_path = path;
result.is_constant = true;
result.is_meta_type = false;
@@ -1162,6 +1165,8 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
if (p_function->parameters[i]->default_value->is_constant) {
p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value);
+ } else {
+ p_function->default_arg_values.push_back(Variant()); // Prevent shift.
}
}
#endif // TOOLS_ENABLED
@@ -1214,11 +1219,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
if (!valid) {
// Compute parent signature as a string to show in the error message.
- String parent_signature = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant";
- if (parent_signature == "null") {
- parent_signature = "void";
- }
- parent_signature += " " + p_function->identifier->name.operator String() + "(";
+ String parent_signature = p_function->identifier->name.operator String() + "(";
int j = 0;
for (const GDScriptParser::DataType &par_type : parameters_types) {
if (j > 0) {
@@ -1235,7 +1236,15 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
j++;
}
- parent_signature += ")";
+ parent_signature += ") -> ";
+
+ const String return_type = parent_return_type.is_hard_type() ? parent_return_type.to_string() : "Variant";
+ if (return_type == "null") {
+ parent_signature += "void";
+ } else {
+ parent_signature += return_type;
+ }
+
push_error(vformat(R"(The function signature doesn't match the parent. Parent signature is "%s".)", parent_signature), p_function);
}
}
@@ -1725,7 +1734,6 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame
} else {
result.type_source = GDScriptParser::DataType::INFERRED;
}
- result.is_constant = false;
}
if (p_parameter->datatype_specifier != nullptr) {
@@ -1745,6 +1753,7 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame
push_error(vformat(R"(Could not infer the type of the variable "%s" because the initial value is "null".)", p_parameter->identifier->name), p_parameter->default_value);
}
+ result.is_constant = false;
p_parameter->set_datatype(result);
}
@@ -2563,8 +2572,14 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
parser->push_warning(p_call, GDScriptWarning::RETURN_VALUE_DISCARDED, p_call->function_name);
}
- if (is_static && !base_type.is_meta_type && !(callee_type != GDScriptParser::Node::SUBSCRIPT && parser->current_function != nullptr && parser->current_function->is_static)) {
- parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, base_type.to_string());
+ if (is_static && !base_type.is_meta_type && !(is_self && parser->current_function != nullptr && parser->current_function->is_static)) {
+ String caller_type = String(base_type.native_type);
+
+ if (caller_type.is_empty()) {
+ caller_type = base_type.to_string();
+ }
+
+ parser->push_warning(p_call, GDScriptWarning::STATIC_CALLED_ON_INSTANCE, p_call->function_name, caller_type);
}
#endif // DEBUG_ENABLED
@@ -2727,21 +2742,13 @@ GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const Str
return type;
}
- type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- type.kind = GDScriptParser::DataType::CLASS;
- type.builtin_type = Variant::OBJECT;
- type.native_type = ScriptServer::get_global_class_native_base(p_class_name);
- type.class_type = ref->get_parser()->head;
- type.script_path = ref->get_parser()->script_path;
- type.is_constant = true;
- type.is_meta_type = true;
- return type;
+ return ref->get_parser()->head->get_datatype();
} else {
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
type.kind = GDScriptParser::DataType::SCRIPT;
type.builtin_type = Variant::OBJECT;
- type.native_type = ScriptServer::get_global_class_native_base(p_class_name);
type.script_type = ResourceLoader::load(path, "Script");
+ type.native_type = type.script_type->get_instance_base_type();
type.script_path = path;
type.is_constant = true;
type.is_meta_type = true;
@@ -3125,14 +3132,19 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
}
} else if (ResourceLoader::get_resource_type(autoload.path) == "PackedScene") {
- Error err = OK;
- Ref<GDScript> scr = GDScriptCache::get_packed_scene_script(autoload.path, err);
- if (err == OK && scr.is_valid()) {
- Ref<GDScriptParserRef> singl_parser = get_parser_for(scr->get_path());
- if (singl_parser.is_valid()) {
- err = singl_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
- if (err == OK) {
- result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
+ if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(name)) {
+ Variant constant = GDScriptLanguage::get_singleton()->get_named_globals_map()[name];
+ Node *node = Object::cast_to<Node>(constant);
+ if (node != nullptr) {
+ Ref<Script> scr = node->get_script();
+ if (scr.is_valid()) {
+ Ref<GDScriptParserRef> singl_parser = get_parser_for(scr->get_path());
+ if (singl_parser.is_valid()) {
+ Error err = singl_parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ if (err == OK) {
+ result = type_from_metatype(singl_parser->get_parser()->head->get_datatype());
+ }
+ }
}
}
}
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index f35318e4c6..699ce67933 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -260,7 +260,7 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
Ref<GDScript> script;
r_error = OK;
if (singleton->full_gdscript_cache.has(p_path)) {
- script = Ref<GDScript>(singleton->full_gdscript_cache[p_path]);
+ script = singleton->full_gdscript_cache[p_path];
if (!p_update_from_disk) {
return script;
}
@@ -360,36 +360,10 @@ Ref<PackedScene> GDScriptCache::get_packed_scene(const String &p_path, Error &r_
singleton->packed_scene_cache[p_path] = scene;
singleton->packed_scene_dependencies[p_path].insert(p_owner);
- scene->recreate_state();
scene->reload_from_file();
return scene;
}
-Ref<GDScript> GDScriptCache::get_packed_scene_script(const String &p_path, Error &r_error) {
- r_error = OK;
- Ref<PackedScene> scene = get_packed_scene(p_path, r_error);
-
- if (r_error != OK) {
- return Ref<GDScript>();
- }
-
- int node_count = scene->get_state()->get_node_count();
- if (node_count == 0) {
- return Ref<GDScript>();
- }
-
- const int ROOT_NODE = 0;
- for (int i = 0; i < scene->get_state()->get_node_property_count(ROOT_NODE); i++) {
- if (scene->get_state()->get_node_property_name(ROOT_NODE, i) != SNAME("script")) {
- continue;
- }
-
- return scene->get_state()->get_node_property_value(ROOT_NODE, i);
- }
-
- return Ref<GDScript>();
-}
-
void GDScriptCache::clear_unreferenced_packed_scenes() {
if (singleton == nullptr) {
return;
@@ -407,15 +381,15 @@ void GDScriptCache::clear_unreferenced_packed_scenes() {
}
}
-GDScriptCache::GDScriptCache() {
- singleton = this;
-}
+void GDScriptCache::clear() {
+ if (singleton == nullptr) {
+ return;
+ }
-GDScriptCache::~GDScriptCache() {
- destructing = true;
+ MutexLock lock(singleton->mutex);
RBSet<Ref<GDScriptParserRef>> parser_map_refs;
- for (KeyValue<String, GDScriptParserRef *> &E : parser_map) {
+ for (KeyValue<String, GDScriptParserRef *> &E : singleton->parser_map) {
parser_map_refs.insert(E.value);
}
@@ -425,12 +399,20 @@ GDScriptCache::~GDScriptCache() {
}
parser_map_refs.clear();
- parser_map.clear();
- shallow_gdscript_cache.clear();
- full_gdscript_cache.clear();
+ singleton->parser_map.clear();
+ singleton->shallow_gdscript_cache.clear();
+ singleton->full_gdscript_cache.clear();
- packed_scene_cache.clear();
- packed_scene_dependencies.clear();
+ singleton->packed_scene_cache.clear();
+ singleton->packed_scene_dependencies.clear();
+}
+GDScriptCache::GDScriptCache() {
+ singleton = this;
+}
+
+GDScriptCache::~GDScriptCache() {
+ destructing = true;
+ clear();
singleton = nullptr;
}
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 0f9d87aa67..e7e1901d5d 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -102,7 +102,6 @@ public:
static Error finish_compiling(const String &p_owner);
static Ref<PackedScene> get_packed_scene(const String &p_path, Error &r_error, const String &p_owner = "");
- static Ref<GDScript> get_packed_scene_script(const String &p_path, Error &r_error);
static void clear_unreferenced_packed_scenes();
static bool is_destructing() {
@@ -112,6 +111,8 @@ public:
return singleton->destructing;
};
+ static void clear();
+
GDScriptCache();
~GDScriptCache();
};
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index f0ceb42f89..ea93e1ebfc 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -43,7 +43,7 @@ bool GDScriptCompiler::_is_class_member_property(CodeGen &codegen, const StringN
return false;
}
- if (codegen.parameters.has(p_name) || codegen.locals.has(p_name)) {
+ if (_is_local_or_parameter(codegen, p_name)) {
return false; //shadowed
}
@@ -65,6 +65,10 @@ bool GDScriptCompiler::_is_class_member_property(GDScript *owner, const StringNa
return ClassDB::has_property(nc->get_name(), p_name);
}
+bool GDScriptCompiler::_is_local_or_parameter(CodeGen &codegen, const StringName &p_name) {
+ return codegen.parameters.has(p_name) || codegen.locals.has(p_name);
+}
+
void GDScriptCompiler::_set_error(const String &p_error, const GDScriptParser::Node *p_node) {
if (!error.is_empty()) {
return;
@@ -920,7 +924,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
StringName var_name = identifier->name;
if (_is_class_member_property(codegen, var_name)) {
assign_class_member_property = var_name;
- } else if (!codegen.locals.has(var_name) && codegen.script->member_indices.has(var_name)) {
+ } else if (!_is_local_or_parameter(codegen, var_name) && codegen.script->member_indices.has(var_name)) {
is_member_property = true;
member_property_setter_function = codegen.script->member_indices[var_name].setter;
member_property_has_setter = member_property_setter_function != StringName();
@@ -1131,7 +1135,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
bool is_in_setter = false;
StringName setter_function;
StringName var_name = static_cast<const GDScriptParser::IdentifierNode *>(assignment->assignee)->name;
- if (!codegen.locals.has(var_name) && codegen.script->member_indices.has(var_name)) {
+ if (!_is_local_or_parameter(codegen, var_name) && codegen.script->member_indices.has(var_name)) {
is_member = true;
setter_function = codegen.script->member_indices[var_name].setter;
has_setter = setter_function != StringName();
@@ -1875,6 +1879,7 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name];
GDScriptDataType local_type = _gdtype_from_datatype(lv->get_datatype(), codegen.script);
+ bool initialized = false;
if (lv->initializer != nullptr) {
// For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
if (local_type.has_type && local_type.builtin_type == Variant::ARRAY) {
@@ -1896,15 +1901,23 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
+ initialized = true;
} else if (local_type.has_type) {
// Initialize with default for type.
if (local_type.has_container_element_type()) {
codegen.generator->write_construct_typed_array(local, local_type.get_container_element_type(), Vector<GDScriptCodeGenerator::Address>());
+ initialized = true;
} else if (local_type.kind == GDScriptDataType::BUILTIN) {
codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
+ initialized = true;
}
// The `else` branch is for objects, in such case we leave it as `null`.
}
+
+ // Assigns a null for the unassigned variables in loops.
+ if (!initialized && p_block->is_loop) {
+ codegen.generator->write_construct(local, Variant::NIL, Vector<GDScriptCodeGenerator::Address>());
+ }
} break;
case GDScriptParser::Node::CONSTANT: {
// Local constants.
@@ -2284,7 +2297,7 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
_set_error(vformat(R"(Could not find class "%s" in "%s".)", base->fully_qualified_name, base->path), nullptr);
return ERR_COMPILATION_FAILED;
}
- ERR_FAIL_COND_V(!base->is_valid(), ERR_BUG);
+ ERR_FAIL_COND_V(!base->is_valid() && !base->reloading, ERR_BUG);
}
p_script->base = base;
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 45ca4fe342..cba585e5a5 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -115,6 +115,7 @@ class GDScriptCompiler {
bool _is_class_member_property(CodeGen &codegen, const StringName &p_name);
bool _is_class_member_property(GDScript *owner, const StringName &p_name);
+ bool _is_local_or_parameter(CodeGen &codegen, const StringName &p_name);
void _set_error(const String &p_error, const GDScriptParser::Node *p_node);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 7628bffd22..6d5936ad2e 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -2512,50 +2512,62 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::SubscriptNode *p_subscript, GDScriptParser::DataType &r_base_type, Variant *r_base = nullptr) {
- if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER && p_context.base != nullptr) {
- const GDScriptParser::GetNodeNode *get_node = nullptr;
- const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base);
+ if (p_context.base == nullptr) {
+ return false;
+ }
+ const GDScriptParser::GetNodeNode *get_node = nullptr;
+
+ switch (p_subscript->base->type) {
+ case GDScriptParser::Node::GET_NODE: {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(p_subscript->base);
+ } break;
+
+ case GDScriptParser::Node::IDENTIFIER: {
+ const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base);
- switch (identifier_node->source) {
- case GDScriptParser::IdentifierNode::Source::MEMBER_VARIABLE: {
- if (p_context.current_class != nullptr) {
- const StringName &member_name = identifier_node->name;
- const GDScriptParser::ClassNode *current_class = p_context.current_class;
+ switch (identifier_node->source) {
+ case GDScriptParser::IdentifierNode::Source::MEMBER_VARIABLE: {
+ if (p_context.current_class != nullptr) {
+ const StringName &member_name = identifier_node->name;
+ const GDScriptParser::ClassNode *current_class = p_context.current_class;
- if (current_class->has_member(member_name)) {
- const GDScriptParser::ClassNode::Member &member = current_class->get_member(member_name);
+ if (current_class->has_member(member_name)) {
+ const GDScriptParser::ClassNode::Member &member = current_class->get_member(member_name);
- if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
- const GDScriptParser::VariableNode *variable = static_cast<GDScriptParser::VariableNode *>(member.variable);
+ if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {
+ const GDScriptParser::VariableNode *variable = static_cast<GDScriptParser::VariableNode *>(member.variable);
- if (variable->initializer && variable->initializer->type == GDScriptParser::Node::GET_NODE) {
- get_node = static_cast<GDScriptParser::GetNodeNode *>(variable->initializer);
+ if (variable->initializer && variable->initializer->type == GDScriptParser::Node::GET_NODE) {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(variable->initializer);
+ }
}
}
}
- }
- } break;
- case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: {
- if (identifier_node->next != nullptr && identifier_node->next->type == GDScriptParser::ClassNode::Node::GET_NODE) {
- get_node = static_cast<GDScriptParser::GetNodeNode *>(identifier_node->next);
- }
- } break;
- default:
- break;
- }
+ } break;
+ case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: {
+ if (identifier_node->next != nullptr && identifier_node->next->type == GDScriptParser::ClassNode::Node::GET_NODE) {
+ get_node = static_cast<GDScriptParser::GetNodeNode *>(identifier_node->next);
+ }
+ } break;
+ default: {
+ } break;
+ }
+ } break;
+ default: {
+ } break;
+ }
- if (get_node != nullptr) {
- const Object *node = p_context.base->call("get_node_or_null", NodePath(get_node->full_path));
- if (node != nullptr) {
- if (r_base != nullptr) {
- *r_base = node;
- }
- r_base_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- r_base_type.kind = GDScriptParser::DataType::NATIVE;
- r_base_type.native_type = node->get_class_name();
- r_base_type.builtin_type = Variant::OBJECT;
- return true;
+ if (get_node != nullptr) {
+ const Object *node = p_context.base->call("get_node_or_null", NodePath(get_node->full_path));
+ if (node != nullptr) {
+ if (r_base != nullptr) {
+ *r_base = node;
}
+ r_base_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ r_base_type.kind = GDScriptParser::DataType::NATIVE;
+ r_base_type.native_type = node->get_class_name();
+ r_base_type.builtin_type = Variant::OBJECT;
+ return true;
}
}
@@ -3265,15 +3277,6 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
}
- // Need special checks for assert and preload as they are technically
- // keywords, so are not registered in GDScriptUtilityFunctions.
- if (GDScriptUtilityFunctions::function_exists(p_symbol) || "assert" == p_symbol || "preload" == p_symbol) {
- r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;
- r_result.class_name = "@GDScript";
- r_result.class_member = p_symbol;
- return OK;
- }
-
if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;
r_result.class_name = "@GDScript";
@@ -3283,11 +3286,24 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
GDScriptParser parser;
parser.parse(p_code, p_path, true);
- GDScriptAnalyzer analyzer(&parser);
- analyzer.analyze();
GDScriptParser::CompletionContext context = parser.get_completion_context();
+ // Allows class functions with the names like built-ins to be handled properly.
+ if (context.type != GDScriptParser::COMPLETION_ATTRIBUTE) {
+ // Need special checks for assert and preload as they are technically
+ // keywords, so are not registered in GDScriptUtilityFunctions.
+ if (GDScriptUtilityFunctions::function_exists(p_symbol) || "assert" == p_symbol || "preload" == p_symbol) {
+ r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;
+ r_result.class_name = "@GDScript";
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ }
+
+ GDScriptAnalyzer analyzer(&parser);
+ analyzer.analyze();
+
if (context.current_class && context.current_class->extends.size() > 0) {
bool success = false;
ClassDB::get_integer_constant(context.current_class->extends[0], p_symbol, &success);
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index d24cba4c59..f2aafe9f0c 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -148,7 +148,9 @@ GDScriptParser::GDScriptParser() {
// Networking.
register_annotation(MethodInfo("@rpc", PropertyInfo(Variant::STRING, "mode"), PropertyInfo(Variant::STRING, "sync"), PropertyInfo(Variant::STRING, "transfer_mode"), PropertyInfo(Variant::INT, "transfer_channel")), AnnotationInfo::FUNCTION, &GDScriptParser::rpc_annotation, varray("", "", "", 0), true);
+#ifdef DEBUG_ENABLED
is_ignoring_warnings = !(bool)GLOBAL_GET("debug/gdscript/warnings/enable");
+#endif
}
GDScriptParser::~GDScriptParser() {
@@ -1833,9 +1835,9 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
}
suite->add_local(SuiteNode::Local(n_for->variable, current_function));
}
- suite->parent_for = n_for;
n_for->loop = parse_suite(R"("for" block)", suite);
+ n_for->loop->is_loop = true;
complete_extents(n_for);
// Reset break/continue state.
@@ -2167,6 +2169,7 @@ GDScriptParser::WhileNode *GDScriptParser::parse_while() {
is_continue_match = false;
n_while->loop = parse_suite(R"("while" block)");
+ n_while->loop->is_loop = true;
complete_extents(n_while);
// Reset break/continue state.
@@ -3738,6 +3741,12 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
// This is called after the analyzer is done finding the type, so this should be set here.
DataType export_type = variable->get_datatype();
+ if (p_annotation->name == SNAME("@export_range")) {
+ if (export_type.builtin_type == Variant::INT) {
+ variable->export_info.type = Variant::INT;
+ }
+ }
+
if (p_annotation->name == SNAME("@export")) {
if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
@@ -3777,15 +3786,14 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
break;
case GDScriptParser::DataType::CLASS:
- // Can assume type is a global GDScript class.
if (ClassDB::is_parent_class(export_type.native_type, SNAME("Resource"))) {
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
- variable->export_info.hint_string = export_type.class_type->identifier->name;
+ variable->export_info.hint_string = export_type.to_string();
} else if (ClassDB::is_parent_class(export_type.native_type, SNAME("Node"))) {
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_NODE_TYPE;
- variable->export_info.hint_string = export_type.class_type->identifier->name;
+ variable->export_info.hint_string = export_type.to_string();
} else {
push_error(R"(Export type can only be built-in, a resource, a node or an enum.)", variable);
return false;
@@ -3794,16 +3802,19 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
break;
case GDScriptParser::DataType::SCRIPT: {
StringName class_name;
- if (export_type.script_type != nullptr && export_type.script_type.is_valid()) {
+ StringName native_base;
+ if (export_type.script_type.is_valid()) {
class_name = export_type.script_type->get_language()->get_global_class_name(export_type.script_type->get_path());
+ native_base = export_type.script_type->get_instance_base_type();
}
if (class_name == StringName()) {
Ref<Script> script = ResourceLoader::load(export_type.script_path, SNAME("Script"));
if (script.is_valid()) {
class_name = script->get_language()->get_global_class_name(export_type.script_path);
+ native_base = script->get_instance_base_type();
}
}
- if (class_name != StringName() && ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(class_name), SNAME("Resource"))) {
+ if (class_name != StringName() && native_base != StringName() && ClassDB::is_parent_class(native_base, SNAME("Resource"))) {
variable->export_info.type = Variant::OBJECT;
variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
variable->export_info.hint_string = class_name;
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 7baa3ca3d9..d092a2a5e9 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1055,12 +1055,12 @@ public:
HashMap<StringName, int> locals_indices;
FunctionNode *parent_function = nullptr;
- ForNode *parent_for = nullptr;
IfNode *parent_if = nullptr;
bool has_return = false;
bool has_continue = false;
bool has_unreachable_code = false; // Just so warnings aren't given more than once per block.
+ bool is_loop = false;
bool has_local(const StringName &p_name) const;
const Local &get_local(const StringName &p_name) const;
@@ -1217,13 +1217,14 @@ private:
bool can_break = false;
bool can_continue = false;
bool is_continue_match = false; // Whether a `continue` will act on a `match`.
- bool is_ignoring_warnings = false;
List<bool> multiline_stack;
ClassNode *head = nullptr;
Node *list = nullptr;
List<ParserError> errors;
+
#ifdef DEBUG_ENABLED
+ bool is_ignoring_warnings = false;
List<GDScriptWarning> warnings;
HashSet<String> ignored_warnings;
HashSet<uint32_t> ignored_warning_codes;
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index de3becbaf8..e442bf8159 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -844,8 +844,9 @@ Error ExtendGDScriptParser::parse(const String &p_code, const String &p_path) {
lines = p_code.split("\n");
Error err = GDScriptParser::parse(p_code, p_path, false);
+ GDScriptAnalyzer analyzer(this);
+
if (err == OK) {
- GDScriptAnalyzer analyzer(this);
err = analyzer.analyze();
}
update_diagnostics();
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.cpp b/modules/gdscript/language_server/gdscript_language_protocol.cpp
index 39f4c976a4..551973140d 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "editor/doc_tools.h"
+#include "editor/editor_help.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
index 7f42643c8f..f59983ca90 100644
--- a/modules/gdscript/tests/gdscript_test_runner.cpp
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -71,27 +71,38 @@ void init_autoloads() {
continue;
}
- Ref<Resource> res = ResourceLoader::load(info.path);
- ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path);
Node *n = nullptr;
- Ref<PackedScene> scn = res;
- Ref<Script> script = res;
- if (scn.is_valid()) {
- n = scn->instantiate();
- } else if (script.is_valid()) {
- StringName ibt = script->get_instance_base_type();
- bool valid_type = ClassDB::is_parent_class(ibt, "Node");
- ERR_CONTINUE_MSG(!valid_type, "Script does not inherit from Node: " + info.path);
+ if (ResourceLoader::get_resource_type(info.path) == "PackedScene") {
+ // Cache the scene reference before loading it (for cyclic references)
+ Ref<PackedScene> scn;
+ scn.instantiate();
+ scn->set_path(info.path);
+ scn->reload_from_file();
+ ERR_CONTINUE_MSG(!scn.is_valid(), vformat("Can't autoload: %s.", info.path));
+
+ if (scn.is_valid()) {
+ n = scn->instantiate();
+ }
+ } else {
+ Ref<Resource> res = ResourceLoader::load(info.path);
+ ERR_CONTINUE_MSG(res.is_null(), vformat("Can't autoload: %s.", info.path));
- Object *obj = ClassDB::instantiate(ibt);
+ Ref<Script> scr = res;
+ if (scr.is_valid()) {
+ StringName ibt = scr->get_instance_base_type();
+ bool valid_type = ClassDB::is_parent_class(ibt, "Node");
+ ERR_CONTINUE_MSG(!valid_type, vformat("Script does not inherit from Node: %s.", info.path));
- ERR_CONTINUE_MSG(!obj, "Cannot instance script for autoload, expected 'Node' inheritance, got: " + String(ibt) + ".");
+ Object *obj = ClassDB::instantiate(ibt);
- n = Object::cast_to<Node>(obj);
- n->set_script(script);
+ ERR_CONTINUE_MSG(!obj, vformat("Cannot instance script for Autoload, expected 'Node' inheritance, got: %s.", ibt));
+
+ n = Object::cast_to<Node>(obj);
+ n->set_script(scr);
+ }
}
- ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path);
+ ERR_CONTINUE_MSG(!n, vformat("Path in autoload not a node or script: %s.", info.path));
n->set_name(info.name);
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out
index 3baeb17066..4ccd2da381 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_less.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int)".
+The function signature doesn't match the parent. Parent signature is "my_function(int) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out
index 3baeb17066..4ccd2da381 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_count_more.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int)".
+The function signature doesn't match the parent. Parent signature is "my_function(int) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out
index 665c229339..c70a1df10d 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_default_values.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int = default)".
+The function signature doesn't match the parent. Parent signature is "my_function(int = default) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out
index 3baeb17066..4ccd2da381 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_parameter_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function(int)".
+The function signature doesn't match the parent. Parent signature is "my_function(int) -> int".
diff --git a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out
index 5b22739a93..61004ff627 100644
--- a/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out
+++ b/modules/gdscript/tests/scripts/analyzer/errors/function_dont_match_parent_signature_return_type.out
@@ -1,2 +1,2 @@
GDTEST_ANALYZER_ERROR
-The function signature doesn't match the parent. Parent signature is "int my_function()".
+The function signature doesn't match the parent. Parent signature is "my_function() -> int".
diff --git a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
new file mode 100644
index 0000000000..f33ba7dffd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.gd
@@ -0,0 +1,25 @@
+# https://github.com/godotengine/godot/pull/69620
+
+var a: int = 1
+
+func shadow_regular_assignment(a: Variant, b: Variant) -> void:
+ print(a)
+ print(self.a)
+ a = b
+ print(a)
+ print(self.a)
+
+
+var v := Vector2(0.0, 0.0)
+
+func shadow_subscript_assignment(v: Vector2, x: float) -> void:
+ print(v)
+ print(self.v)
+ v.x += x
+ print(v)
+ print(self.v)
+
+
+func test():
+ shadow_regular_assignment('a', 'b')
+ shadow_subscript_assignment(Vector2(1.0, 1.0), 5.0)
diff --git a/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out
new file mode 100644
index 0000000000..5b981bc8bb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/parameter_shadowing.out
@@ -0,0 +1,17 @@
+GDTEST_OK
+>> WARNING
+>> Line: 5
+>> SHADOWED_VARIABLE
+>> The local function parameter "a" is shadowing an already-declared variable at line 3.
+>> WARNING
+>> Line: 15
+>> SHADOWED_VARIABLE
+>> The local function parameter "v" is shadowing an already-declared variable at line 13.
+a
+1
+b
+1
+(1, 1)
+(0, 0)
+(6, 1)
+(0, 0)