diff options
Diffstat (limited to 'modules/gdscript')
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 = ¤t_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) |