Path: blob/master/modules/gdscript/gdscript_editor.cpp
20875 views
/**************************************************************************/1/* gdscript_editor.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "gdscript.h"3132#include "gdscript_analyzer.h"33#include "gdscript_parser.h"34#include "gdscript_tokenizer.h"35#include "gdscript_utility_functions.h"3637#ifdef TOOLS_ENABLED38#include "editor/gdscript_docgen.h"39#include "editor/script_templates/templates.gen.h"40#endif4142#include "core/config/engine.h"43#include "core/core_constants.h"44#include "core/io/file_access.h"45#include "core/math/expression.h"46#include "core/variant/container_type_validate.h"4748#ifdef TOOLS_ENABLED49#include "core/config/project_settings.h"50#include "editor/editor_node.h"51#include "editor/editor_string_names.h"52#include "editor/file_system/editor_file_system.h"53#include "editor/settings/editor_settings.h"54#endif5556Vector<String> GDScriptLanguage::get_comment_delimiters() const {57static const Vector<String> delimiters = { "#" };58return delimiters;59}6061Vector<String> GDScriptLanguage::get_doc_comment_delimiters() const {62static const Vector<String> delimiters = { "##" };63return delimiters;64}6566Vector<String> GDScriptLanguage::get_string_delimiters() const {67static const Vector<String> delimiters = {68"\" \"",69"' '",70"\"\"\" \"\"\"",71"''' '''",72};73// NOTE: StringName, NodePath and r-strings are not listed here.74return delimiters;75}7677bool GDScriptLanguage::is_using_templates() {78return true;79}8081Ref<Script> GDScriptLanguage::make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const {82Ref<GDScript> scr;83scr.instantiate();8485String processed_template = p_template;8687#ifdef TOOLS_ENABLED88const bool type_hints = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");89#else90const bool type_hints = true;91#endif9293if (!type_hints) {94processed_template = processed_template.replace(": int", "")95.replace(": Shader.Mode", "")96.replace(": VisualShader.Type", "")97.replace(": float", "")98.replace(": String", "")99.replace(": Array[String]", "")100.replace(": Node", "")101.replace(": CharFXTransform", "")102.replace(":=", "=")103.replace(" -> void", "")104.replace(" -> bool", "")105.replace(" -> int", "")106.replace(" -> PortType", "")107.replace(" -> String", "")108.replace(" -> Object", "");109}110111processed_template = processed_template.replace("_BASE_", p_base_class_name)112.replace("_CLASS_SNAKE_CASE_", p_class_name.to_snake_case().validate_unicode_identifier())113.replace("_CLASS_", p_class_name.to_pascal_case().validate_unicode_identifier())114.replace("_TS_", _get_indentation());115scr->set_source_code(processed_template);116117return scr;118}119120Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(const StringName &p_object) {121Vector<ScriptLanguage::ScriptTemplate> templates;122#ifdef TOOLS_ENABLED123for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) {124if (TEMPLATES[i].inherit == p_object) {125templates.append(TEMPLATES[i]);126}127}128#endif129return templates;130}131132static void get_function_names_recursively(const GDScriptParser::ClassNode *p_class, const String &p_prefix, HashMap<int, String> &r_funcs) {133for (int i = 0; i < p_class->members.size(); i++) {134if (p_class->members[i].type == GDScriptParser::ClassNode::Member::FUNCTION) {135const GDScriptParser::FunctionNode *function = p_class->members[i].function;136r_funcs[function->start_line] = p_prefix.is_empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name);137} else if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) {138String new_prefix = p_class->members[i].m_class->identifier->name;139get_function_names_recursively(p_class->members[i].m_class, p_prefix.is_empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs);140}141}142}143144bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, HashSet<int> *r_safe_lines) const {145GDScriptParser parser;146GDScriptAnalyzer analyzer(&parser);147148Error err = parser.parse(p_script, p_path, false);149if (err == OK) {150err = analyzer.analyze();151}152#ifdef DEBUG_ENABLED153if (r_warnings) {154for (const GDScriptWarning &E : parser.get_warnings()) {155const GDScriptWarning &warn = E;156ScriptLanguage::Warning w;157w.start_line = warn.start_line;158w.end_line = warn.end_line;159w.code = (int)warn.code;160w.string_code = GDScriptWarning::get_name_from_code(warn.code);161w.message = warn.get_message();162r_warnings->push_back(w);163}164}165#endif166if (err) {167if (r_errors) {168for (const GDScriptParser::ParserError &pe : parser.get_errors()) {169ScriptLanguage::ScriptError e;170e.path = p_path;171e.line = pe.start_line;172e.column = pe.start_column;173e.message = pe.message;174r_errors->push_back(e);175}176177for (KeyValue<String, Ref<GDScriptParserRef>> E : parser.get_depended_parsers()) {178GDScriptParser *depended_parser = E.value->get_parser();179for (const GDScriptParser::ParserError &pe : depended_parser->get_errors()) {180ScriptLanguage::ScriptError e;181e.path = E.key;182e.line = pe.start_line;183e.column = pe.start_column;184e.message = pe.message;185r_errors->push_back(e);186}187}188}189return false;190} else if (r_functions) {191const GDScriptParser::ClassNode *cl = parser.get_tree();192HashMap<int, String> funcs;193194get_function_names_recursively(cl, "", funcs);195196for (const KeyValue<int, String> &E : funcs) {197r_functions->push_back(E.value + ":" + itos(E.key));198}199}200201#ifdef DEBUG_ENABLED202if (r_safe_lines) {203const HashSet<int> &unsafe_lines = parser.get_unsafe_lines();204for (int i = 1; i <= parser.get_last_line_number(); i++) {205if (!unsafe_lines.has(i)) {206r_safe_lines->insert(i);207}208}209}210#endif211212return true;213}214215bool GDScriptLanguage::supports_builtin_mode() const {216return true;217}218219bool GDScriptLanguage::supports_documentation() const {220return true;221}222223int GDScriptLanguage::find_function(const String &p_function, const String &p_code) const {224GDScriptTokenizerText tokenizer;225tokenizer.set_source_code(p_code);226int indent = 0;227GDScriptTokenizer::Token current = tokenizer.scan();228while (current.type != GDScriptTokenizer::Token::TK_EOF && current.type != GDScriptTokenizer::Token::ERROR) {229if (current.type == GDScriptTokenizer::Token::INDENT) {230indent++;231} else if (current.type == GDScriptTokenizer::Token::DEDENT) {232indent--;233}234if (indent == 0 && current.type == GDScriptTokenizer::Token::FUNC) {235current = tokenizer.scan();236if (current.is_identifier()) {237String identifier = current.get_identifier();238if (identifier == p_function) {239return current.start_line;240}241}242}243current = tokenizer.scan();244}245return -1;246}247248/* DEBUGGER FUNCTIONS */249250thread_local int GDScriptLanguage::_debug_parse_err_line = -1;251thread_local String GDScriptLanguage::_debug_parse_err_file;252thread_local String GDScriptLanguage::_debug_error;253254bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {255// break because of parse error256257if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {258_debug_parse_err_line = p_line;259_debug_parse_err_file = p_file;260_debug_error = p_error;261EngineDebugger::get_script_debugger()->debug(this, false, true);262// Because this is thread local, clear the memory afterwards.263_debug_parse_err_file = String();264_debug_error = String();265return true;266} else {267return false;268}269}270271bool GDScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) {272if (EngineDebugger::is_active()) {273_debug_parse_err_line = -1;274_debug_parse_err_file = "";275_debug_error = p_error;276bool is_error_breakpoint = p_error != "Breakpoint";277EngineDebugger::get_script_debugger()->debug(this, p_allow_continue, is_error_breakpoint);278// Because this is thread local, clear the memory afterwards.279_debug_parse_err_file = String();280_debug_error = String();281return true;282} else {283return false;284}285}286287String GDScriptLanguage::debug_get_error() const {288return _debug_error;289}290291int GDScriptLanguage::debug_get_stack_level_count() const {292if (_debug_parse_err_line >= 0) {293return 1;294}295296return _call_stack_size;297}298299int GDScriptLanguage::debug_get_stack_level_line(int p_level) const {300if (_debug_parse_err_line >= 0) {301return _debug_parse_err_line;302}303304ERR_FAIL_INDEX_V(p_level, (int)_call_stack_size, -1);305306return *(_get_stack_level(p_level)->line);307}308309String GDScriptLanguage::debug_get_stack_level_function(int p_level) const {310if (_debug_parse_err_line >= 0) {311return "";312}313314ERR_FAIL_INDEX_V(p_level, (int)_call_stack_size, "");315GDScriptFunction *func = _get_stack_level(p_level)->function;316return func ? func->get_name().operator String() : "";317}318319String GDScriptLanguage::debug_get_stack_level_source(int p_level) const {320if (_debug_parse_err_line >= 0) {321return _debug_parse_err_file;322}323324ERR_FAIL_INDEX_V(p_level, (int)_call_stack_size, "");325return _get_stack_level(p_level)->function->get_source();326}327328void GDScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {329if (_debug_parse_err_line >= 0) {330return;331}332333ERR_FAIL_INDEX(p_level, (int)_call_stack_size);334335CallLevel *cl = _get_stack_level(p_level);336GDScriptFunction *f = cl->function;337338List<Pair<StringName, int>> locals;339340f->debug_get_stack_member_state(*cl->line, &locals);341for (const Pair<StringName, int> &E : locals) {342p_locals->push_back(E.first);343344if (f->constant_map.has(E.first)) {345p_values->push_back(f->constant_map[E.first]);346} else {347p_values->push_back(cl->stack[E.second]);348}349}350}351352void GDScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {353if (_debug_parse_err_line >= 0) {354return;355}356357ERR_FAIL_INDEX(p_level, (int)_call_stack_size);358359CallLevel *cl = _get_stack_level(p_level);360GDScriptInstance *instance = cl->instance;361362if (!instance) {363return;364}365366Ref<GDScript> scr = instance->get_script();367ERR_FAIL_COND(scr.is_null());368369const HashMap<StringName, GDScript::MemberInfo> &mi = scr->debug_get_member_indices();370371for (const KeyValue<StringName, GDScript::MemberInfo> &E : mi) {372p_members->push_back(E.key);373p_values->push_back(instance->debug_get_member_by_index(E.value.index));374}375}376377ScriptInstance *GDScriptLanguage::debug_get_stack_level_instance(int p_level) {378if (_debug_parse_err_line >= 0) {379return nullptr;380}381382ERR_FAIL_INDEX_V(p_level, (int)_call_stack_size, nullptr);383384return _get_stack_level(p_level)->instance;385}386387void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {388const HashMap<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map();389const Variant *gl_array = GDScriptLanguage::get_singleton()->get_global_array();390391List<Pair<String, Variant>> cinfo;392get_public_constants(&cinfo);393394for (const KeyValue<StringName, int> &E : name_idx) {395if (GDScriptAnalyzer::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) {396continue;397}398399bool is_script_constant = false;400for (List<Pair<String, Variant>>::Element *CE = cinfo.front(); CE; CE = CE->next()) {401if (CE->get().first == E.key) {402is_script_constant = true;403break;404}405}406if (is_script_constant) {407continue;408}409410const Variant &var = gl_array[E.value];411bool freed = false;412const Object *obj = var.get_validated_object_with_check(freed);413if (obj && !freed) {414if (Object::cast_to<GDScriptNativeClass>(obj)) {415continue;416}417}418419bool skip = false;420for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {421if (E.key == CoreConstants::get_global_constant_name(i)) {422skip = true;423break;424}425}426if (skip) {427continue;428}429430p_globals->push_back(E.key);431p_values->push_back(var);432}433}434435String GDScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) {436List<String> names;437List<Variant> values;438debug_get_stack_level_locals(p_level, &names, &values, p_max_subitems, p_max_depth);439440Vector<String> name_vector;441for (const String &name : names) {442name_vector.push_back(name);443}444445Array value_array;446for (const Variant &value : values) {447value_array.push_back(value);448}449450Expression expression;451if (expression.parse(p_expression, name_vector) == OK) {452ScriptInstance *instance = debug_get_stack_level_instance(p_level);453if (instance) {454Variant return_val = expression.execute(value_array, instance->get_owner());455return return_val.get_construct_string();456}457}458459return String();460}461462void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) const {463p_extensions->push_back("gd");464}465466void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {467List<StringName> functions;468GDScriptUtilityFunctions::get_function_list(&functions);469470for (const StringName &E : functions) {471p_functions->push_back(GDScriptUtilityFunctions::get_function_info(E));472}473474// Not really "functions", but show in documentation.475{476MethodInfo mi;477mi.name = "preload";478mi.arguments.push_back(PropertyInfo(Variant::STRING, "path"));479mi.return_val = PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, Resource::get_class_static());480p_functions->push_back(mi);481}482{483MethodInfo mi;484mi.name = "assert";485mi.return_val.type = Variant::NIL;486mi.arguments.push_back(PropertyInfo(Variant::BOOL, "condition"));487mi.arguments.push_back(PropertyInfo(Variant::STRING, "message"));488mi.default_arguments.push_back(String());489p_functions->push_back(mi);490}491}492493void GDScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_constants) const {494Pair<String, Variant> pi;495pi.first = "PI";496pi.second = Math::PI;497p_constants->push_back(pi);498499Pair<String, Variant> tau;500tau.first = "TAU";501tau.second = Math::TAU;502p_constants->push_back(tau);503504Pair<String, Variant> infinity;505infinity.first = "INF";506infinity.second = Math::INF;507p_constants->push_back(infinity);508509Pair<String, Variant> nan;510nan.first = "NAN";511nan.second = Math::NaN;512p_constants->push_back(nan);513}514515void GDScriptLanguage::get_public_annotations(List<MethodInfo> *p_annotations) const {516GDScriptParser parser;517List<MethodInfo> annotations;518parser.get_annotation_list(&annotations);519520for (const MethodInfo &E : annotations) {521p_annotations->push_back(E);522}523}524525String GDScriptLanguage::make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const {526#ifdef TOOLS_ENABLED527const bool type_hints = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");528#else529const bool type_hints = true;530#endif531532String result = "func " + p_name + "(";533if (p_args.size()) {534for (int i = 0; i < p_args.size(); i++) {535if (i > 0) {536result += ", ";537}538539const String name_unstripped = p_args[i].get_slicec(':', 0);540result += name_unstripped.strip_edges();541542if (type_hints) {543const String type_stripped = p_args[i].substr(name_unstripped.length() + 1).strip_edges();544if (!type_stripped.is_empty()) {545result += ": " + type_stripped;546}547}548}549}550result += String(")") + (type_hints ? " -> void" : "") + ":\n" +551_get_indentation() + "pass # Replace with function body.\n";552553return result;554}555556//////// COMPLETION //////////557558#ifdef TOOLS_ENABLED559560#define COMPLETION_RECURSION_LIMIT 200561562struct GDScriptCompletionIdentifier {563GDScriptParser::DataType type;564String enumeration;565Variant value;566const GDScriptParser::ExpressionNode *assigned_expression = nullptr;567};568569// LOCATION METHODS570// These methods are used to populate the `CodeCompletionOption::location` integer.571// For these methods, the location is based on the depth in the inheritance chain that the property572// appears. For example, if you are completing code in a class that inherits Node2D, a property found on Node2D573// will have a "better" (lower) location "score" than a property that is found on CanvasItem.574575static int _get_property_location(const StringName &p_class, const StringName &p_property) {576if (!ClassDB::has_property(p_class, p_property)) {577return ScriptLanguage::LOCATION_OTHER;578}579580int depth = 0;581StringName class_test = p_class;582while (class_test && !ClassDB::has_property(class_test, p_property, true)) {583class_test = ClassDB::get_parent_class(class_test);584depth++;585}586587return depth | ScriptLanguage::LOCATION_PARENT_MASK;588}589590static int _get_property_location(Ref<Script> p_script, const StringName &p_property) {591int depth = 0;592Ref<Script> scr = p_script;593while (scr.is_valid()) {594if (scr->get_member_line(p_property) != -1) {595return depth | ScriptLanguage::LOCATION_PARENT_MASK;596}597depth++;598scr = scr->get_base_script();599}600return depth + _get_property_location(p_script->get_instance_base_type(), p_property);601}602603static int _get_constant_location(const StringName &p_class, const StringName &p_constant) {604if (!ClassDB::has_integer_constant(p_class, p_constant)) {605return ScriptLanguage::LOCATION_OTHER;606}607608int depth = 0;609StringName class_test = p_class;610while (class_test && !ClassDB::has_integer_constant(class_test, p_constant, true)) {611class_test = ClassDB::get_parent_class(class_test);612depth++;613}614615return depth | ScriptLanguage::LOCATION_PARENT_MASK;616}617618static int _get_constant_location(Ref<Script> p_script, const StringName &p_constant) {619int depth = 0;620Ref<Script> scr = p_script;621while (scr.is_valid()) {622if (scr->get_member_line(p_constant) != -1) {623return depth | ScriptLanguage::LOCATION_PARENT_MASK;624}625depth++;626scr = scr->get_base_script();627}628return depth + _get_constant_location(p_script->get_instance_base_type(), p_constant);629}630631static int _get_signal_location(const StringName &p_class, const StringName &p_signal) {632if (!ClassDB::has_signal(p_class, p_signal)) {633return ScriptLanguage::LOCATION_OTHER;634}635636int depth = 0;637StringName class_test = p_class;638while (class_test && !ClassDB::has_signal(class_test, p_signal, true)) {639class_test = ClassDB::get_parent_class(class_test);640depth++;641}642643return depth | ScriptLanguage::LOCATION_PARENT_MASK;644}645646static int _get_signal_location(Ref<Script> p_script, const StringName &p_signal) {647int depth = 0;648Ref<Script> scr = p_script;649while (scr.is_valid()) {650if (scr->get_member_line(p_signal) != -1) {651return depth | ScriptLanguage::LOCATION_PARENT_MASK;652}653depth++;654scr = scr->get_base_script();655}656return depth + _get_signal_location(p_script->get_instance_base_type(), p_signal);657}658659static int _get_method_location(const StringName &p_class, const StringName &p_method) {660if (!ClassDB::has_method(p_class, p_method)) {661return ScriptLanguage::LOCATION_OTHER;662}663664int depth = 0;665StringName class_test = p_class;666while (class_test && !ClassDB::has_method(class_test, p_method, true)) {667class_test = ClassDB::get_parent_class(class_test);668depth++;669}670671return depth | ScriptLanguage::LOCATION_PARENT_MASK;672}673674static int _get_enum_constant_location(const StringName &p_class, const StringName &p_enum_constant) {675if (!ClassDB::get_integer_constant_enum(p_class, p_enum_constant)) {676return ScriptLanguage::LOCATION_OTHER;677}678679int depth = 0;680StringName class_test = p_class;681while (class_test && !ClassDB::get_integer_constant_enum(class_test, p_enum_constant, true)) {682class_test = ClassDB::get_parent_class(class_test);683depth++;684}685686return depth | ScriptLanguage::LOCATION_PARENT_MASK;687}688689static int _get_enum_location(const StringName &p_class, const StringName &p_enum) {690if (!ClassDB::has_enum(p_class, p_enum)) {691return ScriptLanguage::LOCATION_OTHER;692}693694int depth = 0;695StringName class_test = p_class;696while (class_test && !ClassDB::has_enum(class_test, p_enum, true)) {697class_test = ClassDB::get_parent_class(class_test);698depth++;699}700701return depth | ScriptLanguage::LOCATION_PARENT_MASK;702}703704// END LOCATION METHODS705706static String _trim_parent_class(const String &p_class, const String &p_base_class) {707if (p_base_class.is_empty()) {708return p_class;709}710Vector<String> names = p_class.split(".", false, 1);711if (names.size() == 2) {712const String &first = names[0];713if (GDScriptAnalyzer::class_exists(p_base_class) && GDScriptAnalyzer::class_exists(first) && ClassDB::is_parent_class(p_base_class, first)) {714const String &rest = names[1];715return rest;716}717}718return p_class;719}720721static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg, const String &p_base_class = "") {722String class_name = p_info.class_name;723bool is_enum = p_info.type == Variant::INT && p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM;724// PROPERTY_USAGE_CLASS_IS_BITFIELD: BitField[T] isn't supported (yet?), use plain int.725726if ((p_info.type == Variant::OBJECT || is_enum) && !class_name.is_empty()) {727if (is_enum && CoreConstants::is_global_enum(p_info.class_name)) {728return class_name;729}730return _trim_parent_class(class_name, p_base_class);731} else if (p_info.type == Variant::ARRAY && p_info.hint == PROPERTY_HINT_ARRAY_TYPE && !p_info.hint_string.is_empty()) {732return "Array[" + _trim_parent_class(p_info.hint_string, p_base_class) + "]";733} else if (p_info.type == Variant::DICTIONARY && p_info.hint == PROPERTY_HINT_DICTIONARY_TYPE && !p_info.hint_string.is_empty()) {734const String key = p_info.hint_string.get_slicec(';', 0);735const String value = p_info.hint_string.get_slicec(';', 1);736return "Dictionary[" + _trim_parent_class(key, p_base_class) + ", " + _trim_parent_class(value, p_base_class) + "]";737} else if (p_info.type == Variant::NIL) {738if (p_is_arg || (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {739return "Variant";740} else {741return "void";742}743}744745return Variant::get_type_name(p_info.type);746}747748static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool p_is_annotation = false) {749String arghint;750if (!p_is_annotation) {751arghint += _get_visual_datatype(p_info.return_val, false) + " ";752}753arghint += p_info.name + "(";754755int def_args = p_info.arguments.size() - p_info.default_arguments.size();756int i = 0;757for (const PropertyInfo &E : p_info.arguments) {758if (i > 0) {759arghint += ", ";760}761762if (i == p_arg_idx) {763arghint += String::chr(0xFFFF);764}765arghint += E.name + ": " + _get_visual_datatype(E, true);766767if (i - def_args >= 0) {768arghint += String(" = ") + p_info.default_arguments[i - def_args].get_construct_string();769}770771if (i == p_arg_idx) {772arghint += String::chr(0xFFFF);773}774775i++;776}777778if (p_info.flags & METHOD_FLAG_VARARG) {779if (p_info.arguments.size() > 0) {780arghint += ", ";781}782if (p_arg_idx >= p_info.arguments.size()) {783arghint += String::chr(0xFFFF);784}785arghint += "...args: Array"; // `MethodInfo` does not support the rest parameter name.786if (p_arg_idx >= p_info.arguments.size()) {787arghint += String::chr(0xFFFF);788}789}790791arghint += ")";792793return arghint;794}795796static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_function, int p_arg_idx, bool p_just_args = false) {797String arghint;798799if (p_just_args) {800arghint = "(";801} else {802if (p_function->get_datatype().builtin_type == Variant::NIL) {803arghint = "void " + p_function->identifier->name + "(";804} else {805arghint = p_function->get_datatype().to_string() + " " + p_function->identifier->name + "(";806}807}808809for (int i = 0; i < p_function->parameters.size(); i++) {810if (i > 0) {811arghint += ", ";812}813814if (i == p_arg_idx) {815arghint += String::chr(0xFFFF);816}817const GDScriptParser::ParameterNode *par = p_function->parameters[i];818if (!par->get_datatype().is_hard_type()) {819arghint += par->identifier->name.operator String() + ": Variant";820} else {821arghint += par->identifier->name.operator String() + ": " + par->get_datatype().to_string();822}823824if (par->initializer) {825String def_val = "<unknown>";826switch (par->initializer->type) {827case GDScriptParser::Node::LITERAL: {828const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->initializer);829def_val = literal->value.get_construct_string();830} break;831case GDScriptParser::Node::IDENTIFIER: {832const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->initializer);833def_val = id->name.operator String();834} break;835case GDScriptParser::Node::CALL: {836const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->initializer);837if (call->is_constant && call->reduced) {838def_val = call->reduced_value.get_construct_string();839} else if (call->get_callee_type() == GDScriptParser::Node::IDENTIFIER) {840def_val = call->function_name.operator String() + (call->arguments.is_empty() ? "()" : "(...)");841}842} break;843case GDScriptParser::Node::ARRAY: {844const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->initializer);845if (arr->is_constant && arr->reduced) {846def_val = arr->reduced_value.get_construct_string();847} else {848def_val = arr->elements.is_empty() ? "[]" : "[...]";849}850} break;851case GDScriptParser::Node::DICTIONARY: {852const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->initializer);853if (dict->is_constant && dict->reduced) {854def_val = dict->reduced_value.get_construct_string();855} else {856def_val = dict->elements.is_empty() ? "{}" : "{...}";857}858} break;859case GDScriptParser::Node::SUBSCRIPT: {860const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->initializer);861if (sub->is_attribute && sub->datatype.kind == GDScriptParser::DataType::ENUM && !sub->datatype.is_meta_type) {862def_val = sub->get_datatype().to_string() + "." + sub->attribute->name;863} else if (sub->is_constant && sub->reduced) {864def_val = sub->reduced_value.get_construct_string();865}866} break;867default:868break;869}870arghint += " = " + def_val;871}872if (i == p_arg_idx) {873arghint += String::chr(0xFFFF);874}875}876877if (p_function->is_vararg()) {878if (!p_function->parameters.is_empty()) {879arghint += ", ";880}881if (p_arg_idx >= p_function->parameters.size()) {882arghint += String::chr(0xFFFF);883}884const GDScriptParser::ParameterNode *rest_param = p_function->rest_parameter;885arghint += "..." + rest_param->identifier->name + ": " + rest_param->get_datatype().to_string();886if (p_arg_idx >= p_function->parameters.size()) {887arghint += String::chr(0xFFFF);888}889}890891arghint += ")";892893return arghint;894}895896static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_list, const StringName &p_required_type = StringName()) {897const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";898const bool requires_type = !p_required_type.is_empty();899900for (int i = 0; i < p_dir->get_file_count(); i++) {901if (requires_type && !ClassDB::is_parent_class(p_dir->get_file_type(i), p_required_type)) {902continue;903}904ScriptLanguage::CodeCompletionOption option(p_dir->get_file_path(i).quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH);905r_list.insert(option.display, option);906}907908for (int i = 0; i < p_dir->get_subdir_count(); i++) {909_get_directory_contents(p_dir->get_subdir(i), r_list, p_required_type);910}911}912913static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_annotation, int p_argument, const String p_quote_style, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) {914ERR_FAIL_NULL(p_annotation);915916if (p_annotation->info != nullptr) {917r_arghint = _make_arguments_hint(p_annotation->info->info, p_argument, true);918}919if (p_annotation->name == SNAME("@export_range")) {920if (p_argument == 3 || p_argument == 4 || p_argument == 5) {921// Slider hint.922ScriptLanguage::CodeCompletionOption slider1("or_greater", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);923slider1.insert_text = slider1.display.quote(p_quote_style);924r_result.insert(slider1.display, slider1);925ScriptLanguage::CodeCompletionOption slider2("or_less", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);926slider2.insert_text = slider2.display.quote(p_quote_style);927r_result.insert(slider2.display, slider2);928ScriptLanguage::CodeCompletionOption slider3("prefer_slider", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);929slider3.insert_text = slider3.display.quote(p_quote_style);930r_result.insert(slider3.display, slider3);931ScriptLanguage::CodeCompletionOption slider4("hide_control", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);932slider4.insert_text = slider4.display.quote(p_quote_style);933r_result.insert(slider4.display, slider4);934}935} else if (p_annotation->name == SNAME("@export_exp_easing")) {936if (p_argument == 0 || p_argument == 1) {937// Easing hint.938ScriptLanguage::CodeCompletionOption hint1("attenuation", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);939hint1.insert_text = hint1.display.quote(p_quote_style);940r_result.insert(hint1.display, hint1);941ScriptLanguage::CodeCompletionOption hint2("inout", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);942hint2.insert_text = hint2.display.quote(p_quote_style);943r_result.insert(hint2.display, hint2);944}945} else if (p_annotation->name == SNAME("@export_node_path")) {946ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);947node.insert_text = node.display.quote(p_quote_style);948r_result.insert(node.display, node);949950LocalVector<StringName> native_classes;951ClassDB::get_inheriters_from_class("Node", native_classes);952for (const StringName &E : native_classes) {953if (!ClassDB::is_class_exposed(E)) {954continue;955}956ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);957option.insert_text = option.display.quote(p_quote_style);958r_result.insert(option.display, option);959}960961LocalVector<StringName> global_script_classes;962ScriptServer::get_global_class_list(global_script_classes);963for (const StringName &class_name : global_script_classes) {964if (!ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(class_name), "Node")) {965continue;966}967ScriptLanguage::CodeCompletionOption option(class_name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);968option.insert_text = option.display.quote(p_quote_style);969r_result.insert(option.display, option);970}971} else if (p_annotation->name == SNAME("@export_tool_button")) {972if (p_argument == 1) {973const Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();974if (theme.is_valid()) {975List<StringName> icon_list;976theme->get_icon_list(EditorStringName(EditorIcons), &icon_list);977for (const StringName &E : icon_list) {978ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);979option.insert_text = option.display.quote(p_quote_style);980r_result.insert(option.display, option);981}982}983}984} else if (p_annotation->name == SNAME("@export_custom")) {985switch (p_argument) {986case 0: {987static HashMap<StringName, int64_t> items;988if (unlikely(items.is_empty())) {989CoreConstants::get_enum_values(SNAME("PropertyHint"), &items);990}991for (const KeyValue<StringName, int64_t> &item : items) {992ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);993r_result.insert(option.display, option);994}995} break;996case 2: {997static HashMap<StringName, int64_t> items;998if (unlikely(items.is_empty())) {999CoreConstants::get_enum_values(SNAME("PropertyUsageFlags"), &items);1000}1001for (const KeyValue<StringName, int64_t> &item : items) {1002ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);1003r_result.insert(option.display, option);1004}1005} break;1006}1007} else if (p_annotation->name == SNAME("@warning_ignore") || p_annotation->name == SNAME("@warning_ignore_start") || p_annotation->name == SNAME("@warning_ignore_restore")) {1008for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {1009#ifndef DISABLE_DEPRECATED1010if (warning_code >= GDScriptWarning::FIRST_DEPRECATED_WARNING) {1011break; // Don't suggest deprecated warnings as they are never produced.1012}1013#endif // DISABLE_DEPRECATED1014ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1015warning.insert_text = warning.display.quote(p_quote_style);1016r_result.insert(warning.display, warning);1017}1018} else if (p_annotation->name == SNAME("@rpc")) {1019if (p_argument == 0 || p_argument == 1 || p_argument == 2) {1020static const char *options[7] = { "call_local", "call_remote", "any_peer", "authority", "reliable", "unreliable", "unreliable_ordered" };1021for (int i = 0; i < 7; i++) {1022ScriptLanguage::CodeCompletionOption option(options[i], ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1023option.insert_text = option.display.quote(p_quote_style);1024r_result.insert(option.display, option);1025}1026}1027}1028}10291030static void _find_built_in_variants(HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {1031for (int i = 0; i < Variant::VARIANT_MAX; i++) {1032if (Variant::Type(i) == Variant::Type::NIL) {1033continue;1034}1035ScriptLanguage::CodeCompletionOption option(Variant::get_type_name(Variant::Type(i)), ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1036r_result.insert(option.display, option);1037}1038}10391040static void _find_global_enums(HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {1041List<StringName> global_enums;1042CoreConstants::get_global_enums(&global_enums);1043for (const StringName &enum_name : global_enums) {1044ScriptLanguage::CodeCompletionOption option(enum_name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_OTHER);1045r_result.insert(option.display, option);1046}1047}10481049static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {1050// Built-in Variant Types1051_find_built_in_variants(r_result);10521053// Variant meta-type1054if (!p_inherit_only) {1055ScriptLanguage::CodeCompletionOption variant_option("Variant", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1056r_result.insert(variant_option.display, variant_option);1057}10581059LocalVector<StringName> native_types;1060ClassDB::get_class_list(native_types);1061for (const StringName &type : native_types) {1062if (ClassDB::is_class_exposed(type) && !Engine::get_singleton()->has_singleton(type)) {1063ScriptLanguage::CodeCompletionOption option(type, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1064r_result.insert(option.display, option);1065}1066}10671068// TODO: Unify with _find_identifiers_in_class.1069if (p_context.current_class) {1070if (!p_inherit_only && p_context.current_class->base_type.is_set()) {1071// Native enums from base class1072List<StringName> enums;1073ClassDB::get_enum_list(p_context.current_class->base_type.native_type, &enums);1074for (const StringName &E : enums) {1075ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM);1076r_result.insert(option.display, option);1077}1078}1079// Check current class for potential types.1080// TODO: Also check classes the current class inherits from.1081const GDScriptParser::ClassNode *current = p_context.current_class;1082int location_offset = 0;1083while (current) {1084for (int i = 0; i < current->members.size(); i++) {1085const GDScriptParser::ClassNode::Member &member = current->members[i];1086switch (member.type) {1087case GDScriptParser::ClassNode::Member::CLASS: {1088ScriptLanguage::CodeCompletionOption option(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL + location_offset);1089r_result.insert(option.display, option);1090} break;1091case GDScriptParser::ClassNode::Member::ENUM: {1092if (!p_inherit_only) {1093ScriptLanguage::CodeCompletionOption option(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_LOCAL + location_offset);1094r_result.insert(option.display, option);1095}1096} break;1097case GDScriptParser::ClassNode::Member::CONSTANT: {1098if (member.constant->get_datatype().is_meta_type) {1099ScriptLanguage::CodeCompletionOption option(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL + location_offset);1100r_result.insert(option.display, option);1101}1102} break;1103default:1104break;1105}1106}1107location_offset += 1;1108current = current->outer;1109}1110}11111112// Global scripts1113LocalVector<StringName> global_classes;1114ScriptServer::get_global_class_list(global_classes);1115for (const StringName &class_name : global_classes) {1116ScriptLanguage::CodeCompletionOption option(class_name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);1117r_result.insert(option.display, option);1118}11191120// Global enums1121if (!p_inherit_only) {1122_find_global_enums(r_result);1123}11241125// Autoload singletons1126HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads(ProjectSettings::get_singleton()->get_autoload_list());11271128for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {1129const ProjectSettings::AutoloadInfo &info = E.value;1130if (!info.is_singleton || !info.path.has_extension("gd")) {1131continue;1132}1133ScriptLanguage::CodeCompletionOption option(info.name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);1134r_result.insert(option.display, option);1135}1136}11371138static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth = 0) {1139for (int i = 0; i < p_suite->locals.size(); i++) {1140ScriptLanguage::CodeCompletionOption option;1141int location = p_recursion_depth == 0 ? ScriptLanguage::LOCATION_LOCAL : (p_recursion_depth | ScriptLanguage::LOCATION_PARENT_MASK);1142if (p_suite->locals[i].type == GDScriptParser::SuiteNode::Local::CONSTANT) {1143option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1144option.default_value = p_suite->locals[i].constant->initializer->reduced_value;1145} else {1146option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE, location);1147}1148r_result.insert(option.display, option);1149}1150if (p_suite->parent_block) {1151_find_identifiers_in_suite(p_suite->parent_block, r_result, p_recursion_depth + 1);1152}1153}11541155static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, bool p_types_only, bool p_add_braces, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth);11561157static void _find_identifiers_in_class(const GDScriptParser::ClassNode *p_class, bool p_only_functions, bool p_types_only, bool p_static, bool p_parent_only, bool p_add_braces, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {1158ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);11591160if (!p_parent_only) {1161bool outer = false;1162const GDScriptParser::ClassNode *clss = p_class;1163int classes_processed = 0;1164while (clss) {1165for (int i = 0; i < clss->members.size(); i++) {1166const int location = p_recursion_depth == 0 ? classes_processed : (p_recursion_depth | ScriptLanguage::LOCATION_PARENT_MASK);1167const GDScriptParser::ClassNode::Member &member = clss->members[i];1168ScriptLanguage::CodeCompletionOption option;1169switch (member.type) {1170case GDScriptParser::ClassNode::Member::VARIABLE:1171if (p_types_only || p_only_functions || outer || (p_static && !member.variable->is_static)) {1172continue;1173}1174option = ScriptLanguage::CodeCompletionOption(member.variable->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1175break;1176case GDScriptParser::ClassNode::Member::CONSTANT:1177if ((p_types_only && !member.constant->datatype.is_meta_type) || p_only_functions) {1178continue;1179}1180if (r_result.has(member.constant->identifier->name)) {1181continue;1182}1183option = ScriptLanguage::CodeCompletionOption(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1184if (member.constant->initializer) {1185option.default_value = member.constant->initializer->reduced_value;1186}1187break;1188case GDScriptParser::ClassNode::Member::CLASS:1189if (p_only_functions) {1190continue;1191}1192option = ScriptLanguage::CodeCompletionOption(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, location);1193break;1194case GDScriptParser::ClassNode::Member::ENUM_VALUE:1195if (p_types_only || p_only_functions) {1196continue;1197}1198option = ScriptLanguage::CodeCompletionOption(member.enum_value.identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1199break;1200case GDScriptParser::ClassNode::Member::ENUM:1201if (p_only_functions) {1202continue;1203}1204option = ScriptLanguage::CodeCompletionOption(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);1205break;1206case GDScriptParser::ClassNode::Member::FUNCTION:1207if (p_types_only || outer || (p_static && !member.function->is_static) || member.function->identifier->name.operator String().begins_with("@")) {1208continue;1209}1210option = ScriptLanguage::CodeCompletionOption(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1211if (p_add_braces) {1212if (member.function->parameters.size() > 0 || (member.function->info.flags & METHOD_FLAG_VARARG)) {1213option.insert_text += "(";1214option.display += U"(\u2026)";1215} else {1216option.insert_text += "()";1217option.display += "()";1218}1219}1220break;1221case GDScriptParser::ClassNode::Member::SIGNAL:1222if (p_types_only || p_only_functions || outer || p_static) {1223continue;1224}1225option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);1226break;1227case GDScriptParser::ClassNode::Member::GROUP:1228break; // No-op, but silences warnings.1229case GDScriptParser::ClassNode::Member::UNDEFINED:1230break;1231}1232r_result.insert(option.display, option);1233}1234if (p_types_only) {1235break; // Otherwise, it will fill the results with types from the outer class (which is undesired for that case).1236}12371238outer = true;1239clss = clss->outer;1240classes_processed++;1241}1242}12431244// Parents.1245GDScriptCompletionIdentifier base_type;1246base_type.type = p_class->base_type;1247base_type.type.is_meta_type = p_static;12481249_find_identifiers_in_base(base_type, p_only_functions, p_types_only, p_add_braces, r_result, p_recursion_depth + 1);1250}12511252static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base, bool p_only_functions, bool p_types_only, bool p_add_braces, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {1253ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);12541255GDScriptParser::DataType base_type = p_base.type;12561257if (!p_types_only && base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) {1258ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL);1259if (p_add_braces) {1260option.insert_text += "(";1261option.display += U"(\u2026)";1262}1263r_result.insert(option.display, option);1264}12651266while (!base_type.has_no_type()) {1267switch (base_type.kind) {1268case GDScriptParser::DataType::CLASS: {1269_find_identifiers_in_class(base_type.class_type, p_only_functions, p_types_only, base_type.is_meta_type, false, p_add_braces, r_result, p_recursion_depth);1270// This already finds all parent identifiers, so we are done.1271base_type = GDScriptParser::DataType();1272} break;1273case GDScriptParser::DataType::SCRIPT: {1274Ref<Script> scr = base_type.script_type;1275if (scr.is_valid()) {1276if (p_types_only) {1277// TODO: Need to implement Script::get_script_enum_list and retrieve the enum list from a script.1278} else if (!p_only_functions) {1279if (!base_type.is_meta_type) {1280List<PropertyInfo> members;1281scr->get_script_property_list(&members);1282for (const PropertyInfo &E : members) {1283if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {1284continue;1285}1286if (E.name.contains_char('/')) {1287continue;1288}1289int location = p_recursion_depth + _get_property_location(scr, E.name);1290ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1291r_result.insert(option.display, option);1292}12931294List<MethodInfo> signals;1295scr->get_script_signal_list(&signals);1296for (const MethodInfo &E : signals) {1297if (E.name.begins_with("_")) {1298continue;1299}1300int location = p_recursion_depth + _get_signal_location(scr, E.name);1301ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);1302r_result.insert(option.display, option);1303}1304}1305HashMap<StringName, Variant> constants;1306scr->get_constants(&constants);1307for (const KeyValue<StringName, Variant> &E : constants) {1308int location = p_recursion_depth + _get_constant_location(scr, E.key);1309ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1310r_result.insert(option.display, option);1311}1312}13131314if (!p_types_only) {1315List<MethodInfo> methods;1316scr->get_script_method_list(&methods);1317for (const MethodInfo &E : methods) {1318if (E.name.begins_with("@")) {1319continue;1320}1321int location = p_recursion_depth + _get_method_location(scr->get_class_name(), E.name);1322ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1323if (p_add_braces) {1324if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {1325option.insert_text += "(";1326option.display += U"(\u2026)";1327} else {1328option.insert_text += "()";1329option.display += "()";1330}1331}1332r_result.insert(option.display, option);1333}1334}13351336Ref<Script> base_script = scr->get_base_script();1337if (base_script.is_valid()) {1338base_type.script_type = base_script;1339} else {1340base_type.kind = GDScriptParser::DataType::NATIVE;1341base_type.builtin_type = Variant::OBJECT;1342base_type.native_type = scr->get_instance_base_type();1343}1344} else {1345return;1346}1347} break;1348case GDScriptParser::DataType::NATIVE: {1349StringName type = base_type.native_type;1350if (!GDScriptAnalyzer::class_exists(type)) {1351return;1352}13531354List<StringName> enums;1355ClassDB::get_enum_list(type, &enums);1356for (const StringName &E : enums) {1357int location = p_recursion_depth + _get_enum_location(type, E);1358ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);1359r_result.insert(option.display, option);1360}13611362if (p_types_only) {1363return;1364}13651366if (!p_only_functions) {1367List<String> constants;1368ClassDB::get_integer_constant_list(type, &constants);1369for (const String &E : constants) {1370int location = p_recursion_depth + _get_constant_location(type, StringName(E));1371ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1372r_result.insert(option.display, option);1373}13741375if (!base_type.is_meta_type || Engine::get_singleton()->has_singleton(type)) {1376List<PropertyInfo> pinfo;1377ClassDB::get_property_list(type, &pinfo);1378for (const PropertyInfo &E : pinfo) {1379if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {1380continue;1381}1382if (E.name.contains_char('/')) {1383continue;1384}1385int location = p_recursion_depth + _get_property_location(type, E.name);1386ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1387r_result.insert(option.display, option);1388}13891390List<MethodInfo> signals;1391ClassDB::get_signal_list(type, &signals);1392for (const MethodInfo &E : signals) {1393if (E.name.begins_with("_")) {1394continue;1395}1396int location = p_recursion_depth + _get_signal_location(type, StringName(E.name));1397ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);1398r_result.insert(option.display, option);1399}1400}1401}14021403bool only_static = base_type.is_meta_type && !Engine::get_singleton()->has_singleton(type);14041405List<MethodInfo> methods;1406ClassDB::get_method_list(type, &methods, false, true);1407for (const MethodInfo &E : methods) {1408if (only_static && (E.flags & METHOD_FLAG_STATIC) == 0) {1409continue;1410}1411if (E.name.begins_with("_")) {1412continue;1413}1414int location = p_recursion_depth + _get_method_location(type, E.name);1415ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1416if (p_add_braces) {1417if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {1418option.insert_text += "(";1419option.display += U"(\u2026)";1420} else {1421option.insert_text += "()";1422option.display += "()";1423}1424}1425r_result.insert(option.display, option);1426}1427return;1428} break;1429case GDScriptParser::DataType::ENUM: {1430if (p_types_only) {1431return;1432}14331434String type_str = base_type.native_type;14351436if (type_str.contains_char('.')) {1437StringName type = type_str.get_slicec('.', 0);1438StringName type_enum = base_type.enum_type;14391440List<StringName> enum_values;14411442ClassDB::get_enum_constants(type, type_enum, &enum_values);14431444for (const StringName &E : enum_values) {1445int location = p_recursion_depth + _get_enum_constant_location(type, E);1446ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1447r_result.insert(option.display, option);1448}1449} else if (CoreConstants::is_global_enum(base_type.enum_type)) {1450HashMap<StringName, int64_t> enum_values;1451CoreConstants::get_enum_values(base_type.enum_type, &enum_values);14521453for (const KeyValue<StringName, int64_t> &enum_value : enum_values) {1454int location = p_recursion_depth + ScriptLanguage::LOCATION_OTHER;1455ScriptLanguage::CodeCompletionOption option(enum_value.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1456r_result.insert(option.display, option);1457}1458}1459}1460[[fallthrough]];1461case GDScriptParser::DataType::BUILTIN: {1462if (p_types_only) {1463return;1464}14651466Callable::CallError err;1467Variant tmp;1468Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);1469if (err.error != Callable::CallError::CALL_OK) {1470return;1471}14721473int location = ScriptLanguage::LOCATION_OTHER;14741475if (!p_only_functions) {1476List<PropertyInfo> members;1477if (p_base.value.get_type() != Variant::NIL) {1478p_base.value.get_property_list(&members);1479} else {1480tmp.get_property_list(&members);1481}14821483for (const PropertyInfo &E : members) {1484if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {1485continue;1486}1487if (!String(E.name).contains_char('/')) {1488ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1489if (base_type.kind == GDScriptParser::DataType::ENUM) {1490// Sort enum members in their declaration order.1491location += 1;1492}1493if (GDScriptParser::theme_color_names.has(E.name)) {1494option.theme_color_name = GDScriptParser::theme_color_names[E.name];1495}1496r_result.insert(option.display, option);1497}1498}1499}15001501List<MethodInfo> methods;1502tmp.get_method_list(&methods);1503for (const MethodInfo &E : methods) {1504if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type && !(E.flags & METHOD_FLAG_CONST)) {1505// Enum types are static and cannot change, therefore we skip non-const dictionary methods.1506continue;1507}1508ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1509if (p_add_braces) {1510if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {1511option.insert_text += "(";1512option.display += U"(\u2026)";1513} else {1514option.insert_text += "()";1515option.display += "()";1516}1517}1518r_result.insert(option.display, option);1519}15201521return;1522} break;1523default: {1524return;1525} break;1526}1527}1528}15291530static void _find_identifiers(const GDScriptParser::CompletionContext &p_context, bool p_only_functions, bool p_add_braces, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth) {1531if (!p_only_functions && p_context.current_suite) {1532// This includes function parameters, since they are also locals.1533_find_identifiers_in_suite(p_context.current_suite, r_result);1534}15351536if (p_context.current_class) {1537_find_identifiers_in_class(p_context.current_class, p_only_functions, false, (!p_context.current_function || p_context.current_function->is_static), false, p_add_braces, r_result, p_recursion_depth);1538}15391540List<StringName> functions;1541GDScriptUtilityFunctions::get_function_list(&functions);15421543for (const StringName &E : functions) {1544MethodInfo function = GDScriptUtilityFunctions::get_function_info(E);1545ScriptLanguage::CodeCompletionOption option(String(E), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);1546if (p_add_braces) {1547if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {1548option.insert_text += "(";1549option.display += U"(\u2026)";1550} else {1551option.insert_text += "()";1552option.display += "()";1553}1554}1555r_result.insert(option.display, option);1556}15571558if (p_only_functions) {1559return;1560}15611562_find_built_in_variants(r_result);15631564static const char *_keywords[] = {1565"true", "false", "PI", "TAU", "INF", "NAN", "null", "self", "super",1566"break", "breakpoint", "continue", "pass", "return",1567nullptr1568};15691570const char **kw = _keywords;1571while (*kw) {1572ScriptLanguage::CodeCompletionOption option(*kw, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1573r_result.insert(option.display, option);1574kw++;1575}15761577static const char *_keywords_with_space[] = {1578"and", "not", "or", "in", "as", "class", "class_name", "extends", "is", "func", "signal", "await",1579"const", "enum", "static", "var", "if", "elif", "else", "for", "match", "when", "while",1580nullptr1581};15821583const char **kws = _keywords_with_space;1584while (*kws) {1585ScriptLanguage::CodeCompletionOption option(*kws, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1586option.insert_text += " ";1587r_result.insert(option.display, option);1588kws++;1589}15901591static const char *_keywords_with_args[] = {1592"assert", "preload",1593nullptr1594};15951596const char **kwa = _keywords_with_args;1597while (*kwa) {1598ScriptLanguage::CodeCompletionOption option(*kwa, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);1599if (p_add_braces) {1600option.insert_text += "(";1601option.display += U"(\u2026)";1602}1603r_result.insert(option.display, option);1604kwa++;1605}16061607List<StringName> utility_func_names;1608Variant::get_utility_function_list(&utility_func_names);16091610for (const StringName &util_func_name : utility_func_names) {1611ScriptLanguage::CodeCompletionOption option(util_func_name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);1612if (p_add_braces) {1613option.insert_text += "(";1614option.display += U"(\u2026)"; // As all utility functions contain an argument or more, this is hardcoded here.1615}1616r_result.insert(option.display, option);1617}16181619for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {1620if (!E.value.is_singleton) {1621continue;1622}1623ScriptLanguage::CodeCompletionOption option(E.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);1624r_result.insert(option.display, option);1625}16261627// Native classes and global constants.1628for (const KeyValue<StringName, int> &E : GDScriptLanguage::get_singleton()->get_global_map()) {1629ScriptLanguage::CodeCompletionOption option;1630if (GDScriptAnalyzer::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) {1631option = ScriptLanguage::CodeCompletionOption(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1632} else {1633option = ScriptLanguage::CodeCompletionOption(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);1634}1635r_result.insert(option.display, option);1636}16371638// Global enums1639_find_global_enums(r_result);16401641// Global classes1642LocalVector<StringName> global_classes;1643ScriptServer::get_global_class_list(global_classes);1644for (const StringName &class_name : global_classes) {1645ScriptLanguage::CodeCompletionOption option(class_name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);1646r_result.insert(option.display, option);1647}1648}16491650static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value, GDScriptParser::CompletionContext &p_context) {1651GDScriptCompletionIdentifier ci;1652ci.value = p_value;1653ci.type.is_constant = true;1654ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1655ci.type.kind = GDScriptParser::DataType::BUILTIN;1656ci.type.builtin_type = p_value.get_type();16571658if (ci.type.builtin_type == Variant::OBJECT) {1659Object *obj = p_value.operator Object *();1660if (!obj) {1661return ci;1662}1663ci.type.native_type = obj->get_class_name();1664Ref<Script> scr = p_value;1665if (scr.is_valid()) {1666ci.type.is_meta_type = true;1667} else {1668ci.type.is_meta_type = false;1669scr = obj->get_script();1670}1671if (scr.is_valid()) {1672ci.type.script_path = scr->get_path();1673ci.type.script_type = scr;1674ci.type.native_type = scr->get_instance_base_type();1675ci.type.kind = GDScriptParser::DataType::SCRIPT;16761677if (scr->get_path().ends_with(".gd")) {1678Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(scr->get_path());1679if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) {1680ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1681ci.type.class_type = parser->get_parser()->get_tree();1682ci.type.kind = GDScriptParser::DataType::CLASS;1683return ci;1684}1685}1686} else {1687ci.type.kind = GDScriptParser::DataType::NATIVE;1688}1689}16901691return ci;1692}16931694static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_property) {1695GDScriptCompletionIdentifier ci;16961697if (p_property.type == Variant::NIL) {1698// Variant1699ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1700ci.type.kind = GDScriptParser::DataType::VARIANT;1701return ci;1702}17031704if (p_property.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {1705ci.enumeration = p_property.class_name;1706}17071708ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1709ci.type.builtin_type = p_property.type;1710if (p_property.type == Variant::OBJECT) {1711if (ScriptServer::is_global_class(p_property.class_name)) {1712ci.type.kind = GDScriptParser::DataType::SCRIPT;1713ci.type.script_path = ScriptServer::get_global_class_path(p_property.class_name);1714ci.type.native_type = ScriptServer::get_global_class_native_base(p_property.class_name);17151716Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_property.class_name));1717if (scr.is_valid()) {1718ci.type.script_type = scr;1719}1720} else {1721ci.type.kind = GDScriptParser::DataType::NATIVE;1722ci.type.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;1723}1724} else {1725ci.type.kind = GDScriptParser::DataType::BUILTIN;1726}1727return ci;1728}17291730static GDScriptCompletionIdentifier _callable_type_from_method_info(const MethodInfo &p_method) {1731GDScriptCompletionIdentifier ci;1732ci.type.kind = GDScriptParser::DataType::BUILTIN;1733ci.type.builtin_type = Variant::CALLABLE;1734ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1735ci.type.is_constant = true;1736ci.type.method_info = p_method;1737return ci;1738}17391740#define MAX_COMPLETION_RECURSION 1001741struct RecursionCheck {1742int *counter;1743_FORCE_INLINE_ bool check() {1744return (*counter) > MAX_COMPLETION_RECURSION;1745}1746RecursionCheck(int *p_counter) :1747counter(p_counter) {1748(*counter)++;1749}1750~RecursionCheck() {1751(*counter)--;1752}1753};17541755static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type);1756static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);1757static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);17581759static bool _is_expression_named_identifier(const GDScriptParser::ExpressionNode *p_expression, const StringName &p_name) {1760if (p_expression) {1761switch (p_expression->type) {1762case GDScriptParser::Node::IDENTIFIER: {1763const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);1764if (id->name == p_name) {1765return true;1766}1767} break;1768case GDScriptParser::Node::CAST: {1769const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);1770return _is_expression_named_identifier(cn->operand, p_name);1771} break;1772default:1773break;1774}1775}17761777return false;1778}17791780// Creates a map of exemplary results for some functions that return a structured dictionary.1781// Setting this example as value allows autocompletion to suggest the specific keys in some cases.1782static HashMap<String, Dictionary> make_structure_samples() {1783HashMap<String, Dictionary> res;1784const Array arr;17851786{1787Dictionary d;1788d.set("major", 0);1789d.set("minor", 0);1790d.set("patch", 0);1791d.set("hex", 0);1792d.set("status", String());1793d.set("build", String());1794d.set("hash", String());1795d.set("timestamp", 0);1796d.set("string", String());1797res["Engine::get_version_info"] = d;1798}17991800{1801Dictionary d;1802d.set("lead_developers", arr);1803d.set("founders", arr);1804d.set("project_managers", arr);1805d.set("developers", arr);1806res["Engine::get_author_info"] = d;1807}18081809{1810Dictionary d;1811d.set("platinum_sponsors", arr);1812d.set("gold_sponsors", arr);1813d.set("silver_sponsors", arr);1814d.set("bronze_sponsors", arr);1815d.set("mini_sponsors", arr);1816d.set("gold_donors", arr);1817d.set("silver_donors", arr);1818d.set("bronze_donors", arr);1819res["Engine::get_donor_info"] = d;1820}18211822{1823Dictionary d;1824d.set("physical", -1);1825d.set("free", -1);1826d.set("available", -1);1827d.set("stack", -1);1828res["OS::get_memory_info"] = d;1829}18301831{1832Dictionary d;1833d.set("year", 0);1834d.set("month", 0);1835d.set("day", 0);1836d.set("weekday", 0);1837d.set("hour", 0);1838d.set("minute", 0);1839d.set("second", 0);1840d.set("dst", 0);1841res["Time::get_datetime_dict_from_system"] = d;1842}18431844{1845Dictionary d;1846d.set("year", 0);1847d.set("month", 0);1848d.set("day", 0);1849d.set("weekday", 0);1850d.set("hour", 0);1851d.set("minute", 0);1852d.set("second", 0);1853res["Time::get_datetime_dict_from_unix_time"] = d;1854}18551856{1857Dictionary d;1858d.set("year", 0);1859d.set("month", 0);1860d.set("day", 0);1861d.set("weekday", 0);1862res["Time::get_date_dict_from_system"] = d;1863res["Time::get_date_dict_from_unix_time"] = d;1864}18651866{1867Dictionary d;1868d.set("hour", 0);1869d.set("minute", 0);1870d.set("second", 0);1871res["Time::get_time_dict_from_system"] = d;1872res["Time::get_time_dict_from_unix_time"] = d;1873}18741875{1876Dictionary d;1877d.set("bias", 0);1878d.set("name", String());1879res["Time::get_time_zone_from_system"] = d;1880}18811882return res;1883}18841885static const HashMap<String, Dictionary> structure_examples = make_structure_samples();18861887static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) {1888bool found = false;18891890if (p_expression == nullptr) {1891return false;1892}18931894static int recursion_depth = 0;1895RecursionCheck recursion(&recursion_depth);1896if (unlikely(recursion.check())) {1897ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");1898}18991900if (p_expression->is_constant) {1901// Already has a value, so just use that.1902r_type = _type_from_variant(p_expression->reduced_value, p_context);1903switch (p_expression->get_datatype().kind) {1904case GDScriptParser::DataType::ENUM:1905case GDScriptParser::DataType::CLASS:1906r_type.type = p_expression->get_datatype();1907break;1908default:1909break;1910}1911found = true;1912} else {1913switch (p_expression->type) {1914case GDScriptParser::Node::IDENTIFIER: {1915const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);1916found = _guess_identifier_type(p_context, id, r_type);1917} break;1918case GDScriptParser::Node::DICTIONARY: {1919// Try to recreate the dictionary.1920const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);1921Dictionary d;1922bool full = true;1923for (int i = 0; i < dn->elements.size(); i++) {1924GDScriptCompletionIdentifier key;1925if (_guess_expression_type(p_context, dn->elements[i].key, key)) {1926if (!key.type.is_constant) {1927full = false;1928break;1929}1930GDScriptCompletionIdentifier value;1931if (_guess_expression_type(p_context, dn->elements[i].value, value)) {1932if (!value.type.is_constant) {1933full = false;1934break;1935}1936d[key.value] = value.value;1937} else {1938full = false;1939break;1940}1941} else {1942full = false;1943break;1944}1945}1946if (full) {1947r_type.value = d;1948r_type.type.is_constant = true;1949}1950r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1951r_type.type.kind = GDScriptParser::DataType::BUILTIN;1952r_type.type.builtin_type = Variant::DICTIONARY;1953found = true;1954} break;1955case GDScriptParser::Node::ARRAY: {1956// Try to recreate the array1957const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);1958Array a;1959bool full = true;1960a.resize(an->elements.size());1961for (int i = 0; i < an->elements.size(); i++) {1962GDScriptCompletionIdentifier value;1963if (_guess_expression_type(p_context, an->elements[i], value)) {1964if (value.type.is_constant) {1965a[i] = value.value;1966} else {1967full = false;1968break;1969}1970} else {1971full = false;1972break;1973}1974}1975if (full) {1976// If not fully constant, setting this value is detrimental to the inference.1977r_type.value = a;1978r_type.type.is_constant = true;1979}1980r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1981r_type.type.kind = GDScriptParser::DataType::BUILTIN;1982r_type.type.builtin_type = Variant::ARRAY;1983found = true;1984} break;1985case GDScriptParser::Node::CAST: {1986const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);1987GDScriptCompletionIdentifier value;1988if (_guess_expression_type(p_context, cn->operand, r_type)) {1989r_type.type = cn->get_datatype();1990found = true;1991}1992} break;1993case GDScriptParser::Node::CALL: {1994const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);1995GDScriptParser::CompletionContext c = p_context;1996c.current_line = call->start_line;19971998GDScriptParser::Node::Type callee_type = call->get_callee_type();19992000GDScriptCompletionIdentifier base;2001if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super) {2002// Simple call, so base is 'self'.2003if (p_context.current_class) {2004if (call->is_super) {2005base.type = p_context.current_class->base_type;2006base.value = p_context.base;2007} else {2008base.type.kind = GDScriptParser::DataType::CLASS;2009base.type.type_source = GDScriptParser::DataType::INFERRED;2010base.type.is_constant = true;2011base.type.class_type = p_context.current_class;2012base.value = p_context.base;2013}2014} else {2015break;2016}2017} else if (callee_type == GDScriptParser::Node::SUBSCRIPT && static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->is_attribute) {2018if (!_guess_expression_type(c, static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->base, base)) {2019found = false;2020break;2021}2022} else {2023break;2024}20252026// Apply additional behavior aware inference that the analyzer can't do.2027if (base.type.is_set()) {2028// Maintain type for duplicate methods.2029if (call->function_name == SNAME("duplicate")) {2030if (base.type.builtin_type == Variant::OBJECT && (ClassDB::is_parent_class(base.type.native_type, SNAME("Resource")) || ClassDB::is_parent_class(base.type.native_type, SNAME("Node")))) {2031r_type.type = base.type;2032found = true;2033break;2034}2035}20362037// Simulate generics for some typed array methods.2038if (base.type.builtin_type == Variant::ARRAY && base.type.has_container_element_types() && (call->function_name == SNAME("back") || call->function_name == SNAME("front") || call->function_name == SNAME("get") || call->function_name == SNAME("max") || call->function_name == SNAME("min") || call->function_name == SNAME("pick_random") || call->function_name == SNAME("pop_at") || call->function_name == SNAME("pop_back") || call->function_name == SNAME("pop_front"))) {2039r_type.type = base.type.get_container_element_type(0);2040found = true;2041break;2042}20432044// Insert example values for functions which a structured dictionary response.2045if (!base.type.is_meta_type) {2046const Dictionary *example = structure_examples.getptr(base.type.native_type.operator String() + "::" + call->function_name);2047if (example != nullptr) {2048r_type = _type_from_variant(*example, p_context);2049found = true;2050break;2051}2052}2053}20542055if (!found) {2056found = _guess_method_return_type_from_base(c, base, call->function_name, r_type);2057}2058} break;2059case GDScriptParser::Node::SUBSCRIPT: {2060const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);2061if (subscript->is_attribute) {2062GDScriptParser::CompletionContext c = p_context;2063c.current_line = subscript->start_line;20642065GDScriptCompletionIdentifier base;2066if (!_guess_expression_type(c, subscript->base, base)) {2067found = false;2068break;2069}20702071if (base.value.get_type() == Variant::DICTIONARY && base.value.operator Dictionary().has(String(subscript->attribute->name))) {2072Variant value = base.value.operator Dictionary()[String(subscript->attribute->name)];2073r_type = _type_from_variant(value, p_context);2074found = true;2075break;2076}20772078const GDScriptParser::DictionaryNode *dn = nullptr;2079if (subscript->base->type == GDScriptParser::Node::DICTIONARY) {2080dn = static_cast<const GDScriptParser::DictionaryNode *>(subscript->base);2081} else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::DICTIONARY) {2082dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);2083}20842085if (dn) {2086for (int i = 0; i < dn->elements.size(); i++) {2087GDScriptCompletionIdentifier key;2088if (!_guess_expression_type(c, dn->elements[i].key, key)) {2089continue;2090}2091if (key.value == String(subscript->attribute->name)) {2092r_type.assigned_expression = dn->elements[i].value;2093found = _guess_expression_type(c, dn->elements[i].value, r_type);2094break;2095}2096}2097}20982099if (!found) {2100found = _guess_identifier_type_from_base(c, base, subscript->attribute->name, r_type);2101}2102} else {2103if (subscript->index == nullptr) {2104found = false;2105break;2106}21072108GDScriptParser::CompletionContext c = p_context;2109c.current_line = subscript->start_line;21102111GDScriptCompletionIdentifier base;2112if (!_guess_expression_type(c, subscript->base, base)) {2113found = false;2114break;2115}21162117GDScriptCompletionIdentifier index;2118if (!_guess_expression_type(c, subscript->index, index)) {2119found = false;2120break;2121}21222123if (base.type.is_constant && index.type.is_constant) {2124if (base.value.get_type() == Variant::DICTIONARY) {2125Dictionary base_dict = base.value.operator Dictionary();2126if (base_dict.get_key_validator().test_validate(index.value) && base_dict.has(index.value)) {2127r_type = _type_from_variant(base_dict[index.value], p_context);2128found = true;2129break;2130}2131} else {2132bool valid;2133Variant value = base.value.get(index.value, &valid);2134if (valid) {2135r_type = _type_from_variant(value, p_context);2136found = true;2137break;2138}2139}2140}21412142// Look if it is a dictionary node.2143const GDScriptParser::DictionaryNode *dn = nullptr;2144if (subscript->base->type == GDScriptParser::Node::DICTIONARY) {2145dn = static_cast<const GDScriptParser::DictionaryNode *>(subscript->base);2146} else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::DICTIONARY) {2147dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);2148}21492150if (dn) {2151for (int i = 0; i < dn->elements.size(); i++) {2152GDScriptCompletionIdentifier key;2153if (!_guess_expression_type(c, dn->elements[i].key, key)) {2154continue;2155}2156if (key.value == index.value) {2157r_type.assigned_expression = dn->elements[i].value;2158found = _guess_expression_type(p_context, dn->elements[i].value, r_type);2159break;2160}2161}2162}21632164// Look if it is an array node.2165if (!found && index.value.is_num()) {2166int idx = index.value;2167const GDScriptParser::ArrayNode *an = nullptr;2168if (subscript->base->type == GDScriptParser::Node::ARRAY) {2169an = static_cast<const GDScriptParser::ArrayNode *>(subscript->base);2170} else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::ARRAY) {2171an = static_cast<const GDScriptParser::ArrayNode *>(base.assigned_expression);2172}21732174if (an && idx >= 0 && an->elements.size() > idx) {2175r_type.assigned_expression = an->elements[idx];2176found = _guess_expression_type(c, an->elements[idx], r_type);2177break;2178}2179}21802181// Look for valid indexing in other types2182if (!found && (index.value.is_string() || index.value.get_type() == Variant::NODE_PATH)) {2183StringName id = index.value;2184found = _guess_identifier_type_from_base(c, base, id, r_type);2185} else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) {2186Callable::CallError err;2187Variant base_val;2188Variant::construct(base.type.builtin_type, base_val, nullptr, 0, err);2189bool valid = false;2190Variant res = base_val.get(index.value, &valid);2191if (valid) {2192r_type = _type_from_variant(res, p_context);2193r_type.value = Variant();2194r_type.type.is_constant = false;2195found = true;2196}2197}2198}2199} break;2200case GDScriptParser::Node::BINARY_OPERATOR: {2201const GDScriptParser::BinaryOpNode *op = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);22022203if (op->variant_op == Variant::OP_MAX) {2204break;2205}22062207GDScriptParser::CompletionContext context = p_context;2208context.current_line = op->start_line;22092210GDScriptCompletionIdentifier p1;2211GDScriptCompletionIdentifier p2;22122213if (!_guess_expression_type(context, op->left_operand, p1)) {2214found = false;2215break;2216}22172218if (!_guess_expression_type(context, op->right_operand, p2)) {2219found = false;2220break;2221}22222223Callable::CallError ce;2224bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;2225Variant d1;2226Variant::construct(p1.type.builtin_type, d1, nullptr, 0, ce);2227Variant d2;2228Variant::construct(p2.type.builtin_type, d2, nullptr, 0, ce);22292230Variant v1 = (v1_use_value) ? p1.value : d1;2231bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;2232Variant v2 = (v2_use_value) ? p2.value : d2;2233// avoid potential invalid ops2234if ((op->variant_op == Variant::OP_DIVIDE || op->variant_op == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {2235v2 = 1;2236v2_use_value = false;2237}2238if (op->variant_op == Variant::OP_DIVIDE && v2.get_type() == Variant::FLOAT) {2239v2 = 1.0;2240v2_use_value = false;2241}22422243Variant res;2244bool valid;2245Variant::evaluate(op->variant_op, v1, v2, res, valid);2246if (!valid) {2247found = false;2248break;2249}2250r_type = _type_from_variant(res, p_context);2251if (!v1_use_value || !v2_use_value) {2252r_type.value = Variant();2253r_type.type.is_constant = false;2254}22552256found = true;2257} break;2258default:2259break;2260}2261}22622263// It may have found a null, but that's never useful2264if (found && r_type.type.kind == GDScriptParser::DataType::BUILTIN && r_type.type.builtin_type == Variant::NIL) {2265found = false;2266}22672268// If the found type was not fully analyzed we analyze it now.2269if (found && r_type.type.kind == GDScriptParser::DataType::CLASS && !r_type.type.class_type->resolved_body) {2270Error err;2271Ref<GDScriptParserRef> r = GDScriptCache::get_parser(r_type.type.script_path, GDScriptParserRef::FULLY_SOLVED, err);2272}22732274// Check type hint last. For collections we want chance to get the actual value first2275// This way we can detect types from the content of dictionaries and arrays2276if (!found && p_expression->get_datatype().is_hard_type()) {2277r_type.type = p_expression->get_datatype();2278if (!r_type.assigned_expression) {2279r_type.assigned_expression = p_expression;2280}2281found = true;2282}22832284return found;2285}22862287static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type) {2288static int recursion_depth = 0;2289RecursionCheck recursion(&recursion_depth);2290if (unlikely(recursion.check())) {2291ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");2292}22932294// Look in blocks first.2295int last_assign_line = -1;2296const GDScriptParser::ExpressionNode *last_assigned_expression = nullptr;2297GDScriptCompletionIdentifier id_type;2298GDScriptParser::SuiteNode *suite = p_context.current_suite;2299bool is_function_parameter = false;23002301bool can_be_local = true;2302switch (p_identifier->source) {2303case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:2304case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:2305case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:2306case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:2307case GDScriptParser::IdentifierNode::MEMBER_CLASS:2308case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:2309case GDScriptParser::IdentifierNode::STATIC_VARIABLE:2310case GDScriptParser::IdentifierNode::NATIVE_CLASS:2311can_be_local = false;2312break;2313default:2314break;2315}23162317if (can_be_local && suite && suite->has_local(p_identifier->name)) {2318const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name);23192320id_type.type = local.get_datatype();23212322// Check initializer as the first assignment.2323switch (local.type) {2324case GDScriptParser::SuiteNode::Local::VARIABLE:2325if (local.variable->initializer) {2326last_assign_line = local.variable->initializer->end_line;2327last_assigned_expression = local.variable->initializer;2328}2329break;2330case GDScriptParser::SuiteNode::Local::CONSTANT:2331if (local.constant->initializer) {2332last_assign_line = local.constant->initializer->end_line;2333last_assigned_expression = local.constant->initializer;2334}2335break;2336case GDScriptParser::SuiteNode::Local::PARAMETER:2337if (local.parameter->initializer) {2338last_assign_line = local.parameter->initializer->end_line;2339last_assigned_expression = local.parameter->initializer;2340}2341is_function_parameter = true;2342break;2343default:2344break;2345}2346} else {2347if (p_context.current_class) {2348GDScriptCompletionIdentifier base_identifier;23492350GDScriptCompletionIdentifier base;2351base.value = p_context.base;2352base.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2353base.type.kind = GDScriptParser::DataType::CLASS;2354base.type.class_type = p_context.current_class;2355base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static;23562357if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, base_identifier)) {2358id_type = base_identifier;2359}2360}2361}23622363while (suite) {2364for (int i = 0; i < suite->statements.size(); i++) {2365if (suite->statements[i]->end_line >= p_context.current_line) {2366break;2367}23682369switch (suite->statements[i]->type) {2370case GDScriptParser::Node::ASSIGNMENT: {2371const GDScriptParser::AssignmentNode *assign = static_cast<const GDScriptParser::AssignmentNode *>(suite->statements[i]);2372if (assign->end_line > last_assign_line && assign->assignee && assign->assigned_value && assign->assignee->type == GDScriptParser::Node::IDENTIFIER) {2373const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->assignee);2374if (id->name == p_identifier->name && id->source == p_identifier->source) {2375last_assign_line = assign->assigned_value->end_line;2376last_assigned_expression = assign->assigned_value;2377}2378}2379} break;2380default:2381// TODO: Check sub blocks (control flow statements) as they might also reassign stuff.2382break;2383}2384}23852386if (suite->parent_if && suite->parent_if->condition && suite->parent_if->condition->type == GDScriptParser::Node::TYPE_TEST) {2387// Operator `is` used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common..2388// Super dirty hack, but very useful.2389// Credit: Zylann.2390// TODO: this could be hacked to detect AND-ed conditions too...2391const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(suite->parent_if->condition);2392if (type_test->operand && type_test->test_type && type_test->operand->type == GDScriptParser::Node::IDENTIFIER && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->name == p_identifier->name && static_cast<const GDScriptParser::IdentifierNode *>(type_test->operand)->source == p_identifier->source) {2393// Bingo.2394GDScriptParser::CompletionContext c = p_context;2395c.current_line = type_test->operand->start_line;2396c.current_suite = suite;2397if (type_test->test_datatype.is_hard_type()) {2398id_type.type = type_test->test_datatype;2399if (last_assign_line < c.current_line) {2400// Override last assignment.2401last_assign_line = c.current_line;2402last_assigned_expression = nullptr;2403}2404}2405}2406}24072408suite = suite->parent_block;2409}24102411if (last_assigned_expression && last_assign_line < p_context.current_line) {2412GDScriptParser::CompletionContext c = p_context;2413c.current_line = last_assign_line;2414GDScriptCompletionIdentifier assigned_type;2415if (_guess_expression_type(c, last_assigned_expression, assigned_type)) {2416if (id_type.type.is_set() && (assigned_type.type.kind == GDScriptParser::DataType::VARIANT || (assigned_type.type.is_set() && !GDScriptAnalyzer::check_type_compatibility(id_type.type, assigned_type.type)))) {2417// The assigned type is incompatible. The annotated type takes priority.2418r_type = id_type;2419r_type.assigned_expression = last_assigned_expression;2420} else {2421r_type = assigned_type;2422}2423return true;2424}2425}24262427if (is_function_parameter && p_context.current_function && p_context.current_function->source_lambda == nullptr && p_context.current_class) {2428// Check if it's override of native function, then we can assume the type from the signature.2429GDScriptParser::DataType base_type = p_context.current_class->base_type;2430while (base_type.is_set()) {2431switch (base_type.kind) {2432case GDScriptParser::DataType::CLASS:2433if (base_type.class_type->has_function(p_context.current_function->identifier->name)) {2434GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;2435if (parent_function->parameters_indices.has(p_identifier->name)) {2436const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier->name]];2437if ((!id_type.type.is_set() || id_type.type.is_variant()) && parameter->get_datatype().is_hard_type()) {2438id_type.type = parameter->get_datatype();2439}2440if (parameter->initializer) {2441GDScriptParser::CompletionContext c = p_context;2442c.current_function = parent_function;2443c.current_class = base_type.class_type;2444c.base = nullptr;2445if (_guess_expression_type(c, parameter->initializer, r_type)) {2446return true;2447}2448}2449}2450}2451base_type = base_type.class_type->base_type;2452break;2453case GDScriptParser::DataType::NATIVE: {2454if (id_type.type.is_set() && !id_type.type.is_variant()) {2455base_type = GDScriptParser::DataType();2456break;2457}2458MethodInfo info;2459if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) {2460for (const PropertyInfo &E : info.arguments) {2461if (E.name == p_identifier->name) {2462r_type = _type_from_property(E);2463return true;2464}2465}2466}2467base_type = GDScriptParser::DataType();2468} break;2469default:2470break;2471}2472}2473}24742475if (id_type.type.is_set() && !id_type.type.is_variant()) {2476r_type = id_type;2477return true;2478}24792480// Check global scripts.2481if (ScriptServer::is_global_class(p_identifier->name)) {2482String script = ScriptServer::get_global_class_path(p_identifier->name);2483if (script.to_lower().ends_with(".gd")) {2484Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(script);2485if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) {2486r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2487r_type.type.script_path = script;2488r_type.type.class_type = parser->get_parser()->get_tree();2489r_type.type.is_meta_type = true;2490r_type.type.is_constant = false;2491r_type.type.kind = GDScriptParser::DataType::CLASS;2492r_type.value = Variant();2493return true;2494}2495} else {2496Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier->name));2497if (scr.is_valid()) {2498r_type = _type_from_variant(scr, p_context);2499r_type.type.is_meta_type = true;2500return true;2501}2502}2503return false;2504}25052506// Check global variables (including autoloads).2507if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier->name)) {2508r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name], p_context);2509return true;2510}25112512// Check ClassDB.2513if (GDScriptAnalyzer::class_exists(p_identifier->name)) {2514r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2515r_type.type.kind = GDScriptParser::DataType::NATIVE;2516r_type.type.builtin_type = Variant::OBJECT;2517r_type.type.native_type = p_identifier->name;2518r_type.type.is_constant = true;2519if (Engine::get_singleton()->has_singleton(p_identifier->name)) {2520r_type.type.is_meta_type = false;2521r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier->name);2522} else {2523r_type.type.is_meta_type = true;2524r_type.value = Variant();2525}2526return true;2527}25282529return false;2530}25312532static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {2533static int recursion_depth = 0;2534RecursionCheck recursion(&recursion_depth);2535if (unlikely(recursion.check())) {2536ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");2537}25382539GDScriptParser::DataType base_type = p_base.type;2540bool is_static = base_type.is_meta_type;2541while (base_type.is_set()) {2542switch (base_type.kind) {2543case GDScriptParser::DataType::CLASS:2544if (base_type.class_type->has_member(p_identifier)) {2545const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(p_identifier);2546switch (member.type) {2547case GDScriptParser::ClassNode::Member::CONSTANT:2548r_type.type = member.constant->get_datatype();2549if (member.constant->initializer && member.constant->initializer->is_constant) {2550r_type.value = member.constant->initializer->reduced_value;2551}2552return true;2553case GDScriptParser::ClassNode::Member::VARIABLE:2554if (!is_static || member.variable->is_static) {2555if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {2556r_type.type = member.variable->get_datatype();2557return true;2558} else if (member.variable->initializer) {2559const GDScriptParser::ExpressionNode *init = member.variable->initializer;2560if (init->is_constant) {2561r_type.value = init->reduced_value;2562r_type = _type_from_variant(init->reduced_value, p_context);2563return true;2564} else if (init->start_line == p_context.current_line) {2565return false;2566// Detects if variable is assigned to itself2567} else if (_is_expression_named_identifier(init, member.variable->identifier->name)) {2568if (member.variable->initializer->get_datatype().is_set()) {2569r_type.type = member.variable->initializer->get_datatype();2570} else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {2571r_type.type = member.variable->get_datatype();2572}2573return true;2574} else if (_guess_expression_type(p_context, init, r_type)) {2575return true;2576} else if (init->get_datatype().is_set() && !init->get_datatype().is_variant()) {2577r_type.type = init->get_datatype();2578return true;2579}2580}2581}2582// TODO: Check assignments in constructor.2583return false;2584case GDScriptParser::ClassNode::Member::ENUM:2585r_type.type = member.m_enum->get_datatype();2586r_type.enumeration = member.m_enum->identifier->name;2587return true;2588case GDScriptParser::ClassNode::Member::ENUM_VALUE:2589r_type = _type_from_variant(member.enum_value.value, p_context);2590return true;2591case GDScriptParser::ClassNode::Member::SIGNAL:2592r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2593r_type.type.kind = GDScriptParser::DataType::BUILTIN;2594r_type.type.builtin_type = Variant::SIGNAL;2595r_type.type.method_info = member.signal->method_info;2596return true;2597case GDScriptParser::ClassNode::Member::FUNCTION:2598if (is_static && !member.function->is_static) {2599return false;2600}2601r_type = _callable_type_from_method_info(member.function->info);2602return true;2603case GDScriptParser::ClassNode::Member::CLASS:2604r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2605r_type.type.kind = GDScriptParser::DataType::CLASS;2606r_type.type.class_type = member.m_class;2607r_type.type.is_meta_type = true;2608return true;2609case GDScriptParser::ClassNode::Member::GROUP:2610return false; // No-op, but silences warnings.2611case GDScriptParser::ClassNode::Member::UNDEFINED:2612return false; // Unreachable.2613}2614return false;2615}2616base_type = base_type.class_type->base_type;2617break;2618case GDScriptParser::DataType::SCRIPT: {2619Ref<Script> scr = base_type.script_type;2620if (scr.is_valid()) {2621HashMap<StringName, Variant> constants;2622scr->get_constants(&constants);2623if (constants.has(p_identifier)) {2624r_type = _type_from_variant(constants[p_identifier], p_context);2625return true;2626}26272628List<PropertyInfo> members;2629if (is_static) {2630scr->get_property_list(&members);2631} else {2632scr->get_script_property_list(&members);2633}2634for (const PropertyInfo &prop : members) {2635if (prop.name == p_identifier) {2636r_type = _type_from_property(prop);2637return true;2638}2639}26402641if (scr->has_method(p_identifier)) {2642MethodInfo mi = scr->get_method_info(p_identifier);2643r_type = _callable_type_from_method_info(mi);2644return true;2645}26462647Ref<Script> parent = scr->get_base_script();2648if (parent.is_valid()) {2649base_type.script_type = parent;2650} else {2651base_type.kind = GDScriptParser::DataType::NATIVE;2652base_type.builtin_type = Variant::OBJECT;2653base_type.native_type = scr->get_instance_base_type();2654}2655} else {2656return false;2657}2658} break;2659case GDScriptParser::DataType::NATIVE: {2660StringName class_name = base_type.native_type;2661if (!GDScriptAnalyzer::class_exists(class_name)) {2662return false;2663}26642665// Skip constants since they're all integers. Type does not matter because int has no members.26662667PropertyInfo prop;2668if (ClassDB::get_property_info(class_name, p_identifier, &prop)) {2669StringName getter = ClassDB::get_property_getter(class_name, p_identifier);2670if (getter != StringName()) {2671MethodBind *g = ClassDB::get_method(class_name, getter);2672if (g) {2673r_type = _type_from_property(g->get_return_info());2674return true;2675}2676} else {2677r_type = _type_from_property(prop);2678return true;2679}2680}26812682MethodInfo method;2683if (ClassDB::get_method_info(class_name, p_identifier, &method)) {2684r_type = _callable_type_from_method_info(method);2685return true;2686}26872688if (ClassDB::has_enum(class_name, p_identifier)) {2689r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2690r_type.type.kind = GDScriptParser::DataType::ENUM;2691r_type.type.enum_type = p_identifier;2692r_type.type.is_constant = true;2693r_type.type.is_meta_type = true;2694r_type.type.native_type = String(class_name) + "." + p_identifier;2695return true;2696}26972698return false;2699} break;2700case GDScriptParser::DataType::BUILTIN: {2701if (Variant::has_builtin_method(base_type.builtin_type, p_identifier)) {2702r_type = _callable_type_from_method_info(Variant::get_builtin_method_info(base_type.builtin_type, p_identifier));2703return true;2704} else {2705Callable::CallError err;2706Variant tmp;2707Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);27082709if (err.error != Callable::CallError::CALL_OK) {2710return false;2711}2712bool valid = false;2713Variant res = tmp.get(p_identifier, &valid);2714if (valid) {2715r_type = _type_from_variant(res, p_context);2716r_type.value = Variant();2717r_type.type.is_constant = false;2718return true;2719}2720}2721return false;2722} break;2723default: {2724return false;2725} break;2726}2727}2728return false;2729}27302731static void _find_last_return_in_block(GDScriptParser::CompletionContext &p_context, int &r_last_return_line, const GDScriptParser::ExpressionNode **r_last_returned_value) {2732if (!p_context.current_suite) {2733return;2734}27352736for (int i = 0; i < p_context.current_suite->statements.size(); i++) {2737if (p_context.current_suite->statements[i]->start_line < r_last_return_line) {2738break;2739}27402741GDScriptParser::CompletionContext c = p_context;2742switch (p_context.current_suite->statements[i]->type) {2743case GDScriptParser::Node::FOR:2744c.current_suite = static_cast<const GDScriptParser::ForNode *>(p_context.current_suite->statements[i])->loop;2745_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2746break;2747case GDScriptParser::Node::WHILE:2748c.current_suite = static_cast<const GDScriptParser::WhileNode *>(p_context.current_suite->statements[i])->loop;2749_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2750break;2751case GDScriptParser::Node::IF: {2752const GDScriptParser::IfNode *_if = static_cast<const GDScriptParser::IfNode *>(p_context.current_suite->statements[i]);2753c.current_suite = _if->true_block;2754_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2755if (_if->false_block) {2756c.current_suite = _if->false_block;2757_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2758}2759} break;2760case GDScriptParser::Node::MATCH: {2761const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(p_context.current_suite->statements[i]);2762for (int j = 0; j < match->branches.size(); j++) {2763c.current_suite = match->branches[j]->block;2764_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2765}2766} break;2767case GDScriptParser::Node::RETURN: {2768const GDScriptParser::ReturnNode *ret = static_cast<const GDScriptParser::ReturnNode *>(p_context.current_suite->statements[i]);2769if (ret->return_value) {2770if (ret->start_line > r_last_return_line) {2771r_last_return_line = ret->start_line;2772*r_last_returned_value = ret->return_value;2773}2774}2775} break;2776default:2777break;2778}2779}2780}27812782static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) {2783static int recursion_depth = 0;2784RecursionCheck recursion(&recursion_depth);2785if (unlikely(recursion.check())) {2786ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");2787}27882789GDScriptParser::DataType base_type = p_base.type;2790bool is_static = base_type.is_meta_type;27912792if (is_static && p_method == SNAME("new")) {2793r_type.type = base_type;2794r_type.type.is_meta_type = false;2795r_type.type.is_constant = false;2796return true;2797}27982799while (base_type.is_set() && !base_type.is_variant()) {2800switch (base_type.kind) {2801case GDScriptParser::DataType::CLASS:2802if (base_type.class_type->has_function(p_method)) {2803GDScriptParser::FunctionNode *method = base_type.class_type->get_member(p_method).function;2804if (!is_static || method->is_static) {2805if (method->get_datatype().is_set() && !method->get_datatype().is_variant()) {2806r_type.type = method->get_datatype();2807return true;2808}28092810int last_return_line = -1;2811const GDScriptParser::ExpressionNode *last_returned_value = nullptr;2812GDScriptParser::CompletionContext c = p_context;2813c.current_class = base_type.class_type;2814c.current_function = method;2815c.current_suite = method->body;28162817_find_last_return_in_block(c, last_return_line, &last_returned_value);2818if (last_returned_value) {2819c.current_line = c.current_suite->end_line;2820if (_guess_expression_type(c, last_returned_value, r_type)) {2821return true;2822}2823}2824}2825}2826base_type = base_type.class_type->base_type;2827break;2828case GDScriptParser::DataType::SCRIPT: {2829Ref<Script> scr = base_type.script_type;2830if (scr.is_valid()) {2831List<MethodInfo> methods;2832scr->get_script_method_list(&methods);2833for (const MethodInfo &mi : methods) {2834if (mi.name == p_method) {2835r_type = _type_from_property(mi.return_val);2836return true;2837}2838}2839Ref<Script> base_script = scr->get_base_script();2840if (base_script.is_valid()) {2841base_type.script_type = base_script;2842} else {2843base_type.kind = GDScriptParser::DataType::NATIVE;2844base_type.builtin_type = Variant::OBJECT;2845base_type.native_type = scr->get_instance_base_type();2846}2847} else {2848return false;2849}2850} break;2851case GDScriptParser::DataType::NATIVE: {2852if (!GDScriptAnalyzer::class_exists(base_type.native_type)) {2853return false;2854}2855MethodBind *mb = ClassDB::get_method(base_type.native_type, p_method);2856if (mb) {2857r_type = _type_from_property(mb->get_return_info());2858return true;2859}2860return false;2861} break;2862case GDScriptParser::DataType::BUILTIN: {2863Callable::CallError err;2864Variant tmp;2865Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);2866if (err.error != Callable::CallError::CALL_OK) {2867return false;2868}28692870List<MethodInfo> methods;2871tmp.get_method_list(&methods);28722873for (const MethodInfo &mi : methods) {2874if (mi.name == p_method) {2875r_type = _type_from_property(mi.return_val);2876return true;2877}2878}2879return false;2880} break;2881default: {2882return false;2883}2884}2885}28862887return false;2888}28892890static bool _guess_expecting_callable(GDScriptParser::CompletionContext &p_context) {2891if (p_context.call.call != nullptr && p_context.call.call->type == GDScriptParser::Node::CALL) {2892GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_context.call.call);2893GDScriptCompletionIdentifier ci;2894if (_guess_expression_type(p_context, call_node->callee, ci)) {2895if (ci.type.kind == GDScriptParser::DataType::BUILTIN && ci.type.builtin_type == Variant::CALLABLE) {2896if (p_context.call.argument >= 0 && p_context.call.argument < ci.type.method_info.arguments.size()) {2897return ci.type.method_info.arguments.get(p_context.call.argument).type == Variant::CALLABLE;2898}2899}2900}2901}29022903return false;2904}29052906static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {2907if (!p_enum_hint.contains_char('.')) {2908// Global constant or in the current class.2909StringName current_enum = p_enum_hint;2910if (p_context.current_class && p_context.current_class->has_member(current_enum) && p_context.current_class->get_member(current_enum).type == GDScriptParser::ClassNode::Member::ENUM) {2911const GDScriptParser::EnumNode *_enum = p_context.current_class->get_member(current_enum).m_enum;2912for (int i = 0; i < _enum->values.size(); i++) {2913ScriptLanguage::CodeCompletionOption option(_enum->values[i].identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM);2914r_result.insert(option.display, option);2915}2916} else {2917for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {2918if (CoreConstants::get_global_constant_enum(i) == current_enum) {2919ScriptLanguage::CodeCompletionOption option(CoreConstants::get_global_constant_name(i), ScriptLanguage::CODE_COMPLETION_KIND_ENUM);2920r_result.insert(option.display, option);2921}2922}2923}2924} else {2925String class_name = p_enum_hint.get_slicec('.', 0);2926String enum_name = p_enum_hint.get_slicec('.', 1);29272928if (!GDScriptAnalyzer::class_exists(class_name)) {2929return;2930}29312932List<StringName> enum_constants;2933ClassDB::get_enum_constants(class_name, enum_name, &enum_constants);2934for (const StringName &E : enum_constants) {2935String candidate = class_name + "." + E;2936int location = _get_enum_constant_location(class_name, E);2937ScriptLanguage::CodeCompletionOption option(candidate, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);2938r_result.insert(option.display, option);2939}2940}2941}29422943static void _list_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const GDScriptParser::CallNode *p_call, int p_argidx, bool p_static, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, String &r_arghint) {2944Variant base = p_base.value;2945GDScriptParser::DataType base_type = p_base.type;2946const StringName &method = p_call->function_name;29472948const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";2949const bool use_string_names = EDITOR_GET("text_editor/completion/add_string_name_literals");2950const bool use_node_paths = EDITOR_GET("text_editor/completion/add_node_path_literals");29512952while (base_type.is_set() && !base_type.is_variant()) {2953switch (base_type.kind) {2954case GDScriptParser::DataType::CLASS: {2955if (base_type.is_meta_type && method == SNAME("new")) {2956const GDScriptParser::ClassNode *current = base_type.class_type;29572958do {2959if (current->has_member("_init")) {2960const GDScriptParser::ClassNode::Member &member = current->get_member("_init");29612962if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {2963r_arghint = base_type.class_type->get_datatype().to_string() + " new" + _make_arguments_hint(member.function, p_argidx, true);2964return;2965}2966}2967current = current->base_type.class_type;2968} while (current != nullptr);29692970r_arghint = base_type.class_type->get_datatype().to_string() + " new()";2971return;2972}29732974if (base_type.class_type->has_member(method)) {2975const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(method);29762977if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {2978r_arghint = _make_arguments_hint(member.function, p_argidx);2979return;2980}2981}29822983base_type = base_type.class_type->base_type;2984} break;2985case GDScriptParser::DataType::SCRIPT: {2986if (base_type.script_type->is_valid() && base_type.script_type->has_method(method)) {2987r_arghint = _make_arguments_hint(base_type.script_type->get_method_info(method), p_argidx);2988return;2989}2990Ref<Script> base_script = base_type.script_type->get_base_script();2991if (base_script.is_valid()) {2992base_type.script_type = base_script;2993} else {2994base_type.kind = GDScriptParser::DataType::NATIVE;2995base_type.builtin_type = Variant::OBJECT;2996base_type.native_type = base_type.script_type->get_instance_base_type();2997}2998} break;2999case GDScriptParser::DataType::NATIVE: {3000StringName class_name = base_type.native_type;3001if (!GDScriptAnalyzer::class_exists(class_name)) {3002base_type.kind = GDScriptParser::DataType::UNRESOLVED;3003break;3004}30053006MethodInfo info;3007int method_args = 0;30083009if (ClassDB::get_method_info(class_name, method, &info)) {3010method_args = info.arguments.size();3011if (base.get_type() == Variant::OBJECT) {3012Object *obj = base.operator Object *();3013if (obj) {3014List<String> options;3015obj->get_argument_options(method, p_argidx, &options);3016for (String &opt : options) {3017// Handle user preference.3018if (opt.is_quoted()) {3019opt = opt.unquote().quote(quote_style);3020if (use_string_names && info.arguments[p_argidx].type == Variant::STRING_NAME) {3021if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3022GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3023if (literal->value.get_type() == Variant::STRING) {3024opt = "&" + opt;3025}3026} else {3027opt = "&" + opt;3028}3029} else if (use_node_paths && info.arguments[p_argidx].type == Variant::NODE_PATH) {3030if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3031GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3032if (literal->value.get_type() == Variant::STRING) {3033opt = "^" + opt;3034}3035} else {3036opt = "^" + opt;3037}3038}3039}3040ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3041r_result.insert(option.display, option);3042}3043}3044}30453046if (p_argidx < method_args) {3047const PropertyInfo &arg_info = info.arguments[p_argidx];3048if (arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {3049_find_enumeration_candidates(p_context, arg_info.class_name, r_result);3050}3051}30523053r_arghint = _make_arguments_hint(info, p_argidx);3054}30553056if (p_argidx == 1 && p_call && ClassDB::is_parent_class(class_name, SNAME("Tween")) && method == SNAME("tween_property")) {3057// Get tweened objects properties.3058if (p_call->arguments.is_empty()) {3059base_type.kind = GDScriptParser::DataType::UNRESOLVED;3060break;3061}3062GDScriptParser::ExpressionNode *tweened_object = p_call->arguments[0];3063if (!tweened_object) {3064base_type.kind = GDScriptParser::DataType::UNRESOLVED;3065break;3066}3067StringName native_type = tweened_object->datatype.native_type;3068switch (tweened_object->datatype.kind) {3069case GDScriptParser::DataType::SCRIPT: {3070Ref<Script> script = tweened_object->datatype.script_type;3071native_type = script->get_instance_base_type();3072int n = 0;3073while (script.is_valid()) {3074List<PropertyInfo> properties;3075script->get_script_property_list(&properties);3076for (const PropertyInfo &E : properties) {3077if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) {3078continue;3079}3080String name = E.name.quote(quote_style);3081if (use_node_paths) {3082if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3083GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3084if (literal->value.get_type() == Variant::STRING) {3085name = "^" + name;3086}3087} else {3088name = "^" + name;3089}3090}3091ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);3092r_result.insert(option.display, option);3093}3094script = script->get_base_script();3095n++;3096}3097} break;3098case GDScriptParser::DataType::CLASS: {3099GDScriptParser::ClassNode *clss = tweened_object->datatype.class_type;3100native_type = clss->base_type.native_type;3101int n = 0;3102while (clss) {3103for (GDScriptParser::ClassNode::Member member : clss->members) {3104if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {3105String name = member.get_name().quote(quote_style);3106if (use_node_paths) {3107if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3108GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3109if (literal->value.get_type() == Variant::STRING) {3110name = "^" + name;3111}3112} else {3113name = "^" + name;3114}3115}3116ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);3117r_result.insert(option.display, option);3118}3119}3120if (clss->base_type.kind == GDScriptParser::DataType::Kind::CLASS) {3121clss = clss->base_type.class_type;3122n++;3123} else {3124native_type = clss->base_type.native_type;3125clss = nullptr;3126}3127}3128} break;3129default:3130break;3131}31323133List<PropertyInfo> properties;3134ClassDB::get_property_list(native_type, &properties);3135for (const PropertyInfo &E : properties) {3136if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) {3137continue;3138}3139String name = E.name.quote(quote_style);3140if (use_node_paths) {3141if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3142GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3143if (literal->value.get_type() == Variant::STRING) {3144name = "^" + name;3145}3146} else {3147name = "^" + name;3148}3149}3150ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER);3151r_result.insert(option.display, option);3152}3153}31543155if (p_argidx == 0 && ClassDB::is_parent_class(class_name, SNAME("Node")) && (method == SNAME("get_node") || method == SNAME("has_node"))) {3156// Get autoloads3157List<PropertyInfo> props;3158ProjectSettings::get_singleton()->get_property_list(&props);31593160for (const PropertyInfo &E : props) {3161String s = E.name;3162if (!s.begins_with("autoload/")) {3163continue;3164}3165String name = s.get_slicec('/', 1);3166String path = ("/root/" + name).quote(quote_style);3167if (use_node_paths) {3168if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3169GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3170if (literal->value.get_type() == Variant::STRING) {3171path = "^" + path;3172}3173} else {3174path = "^" + path;3175}3176}3177ScriptLanguage::CodeCompletionOption option(path, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);3178r_result.insert(option.display, option);3179}3180}31813182if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, SNAME("InputEvent")) && method.operator String().contains("action")) {3183// Get input actions3184List<PropertyInfo> props;3185ProjectSettings::get_singleton()->get_property_list(&props);3186for (const PropertyInfo &E : props) {3187String s = E.name;3188if (!s.begins_with("input/")) {3189continue;3190}3191String name = s.get_slicec('/', 1).quote(quote_style);3192if (use_string_names) {3193if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3194GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3195if (literal->value.get_type() == Variant::STRING) {3196name = "&" + name;3197}3198} else {3199name = "&" + name;3200}3201}3202ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);3203r_result.insert(option.display, option);3204}3205}3206if (EDITOR_GET("text_editor/completion/complete_file_paths")) {3207if (p_argidx == 0 && method == SNAME("change_scene_to_file") && ClassDB::is_parent_class(class_name, SNAME("SceneTree"))) {3208HashMap<String, ScriptLanguage::CodeCompletionOption> list;3209_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), list, SNAME("PackedScene"));3210for (const KeyValue<String, ScriptLanguage::CodeCompletionOption> &key_value_pair : list) {3211ScriptLanguage::CodeCompletionOption option = key_value_pair.value;3212r_result.insert(option.display, option);3213}3214}3215}32163217base_type.kind = GDScriptParser::DataType::UNRESOLVED;3218} break;3219case GDScriptParser::DataType::BUILTIN: {3220if (base.get_type() == Variant::NIL) {3221Callable::CallError err;3222Variant::construct(base_type.builtin_type, base, nullptr, 0, err);3223if (err.error != Callable::CallError::CALL_OK) {3224return;3225}3226}32273228List<MethodInfo> methods;3229base.get_method_list(&methods);3230for (const MethodInfo &E : methods) {3231if (E.name == method) {3232r_arghint = _make_arguments_hint(E, p_argidx);3233return;3234}3235}32363237base_type.kind = GDScriptParser::DataType::UNRESOLVED;3238} break;3239default: {3240base_type.kind = GDScriptParser::DataType::UNRESOLVED;3241} break;3242}3243}3244}32453246static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::SubscriptNode *p_subscript, GDScriptParser::DataType &r_base_type, Variant *r_base = nullptr) {3247if (p_context.base == nullptr) {3248return false;3249}32503251const GDScriptParser::GetNodeNode *get_node = nullptr;32523253switch (p_subscript->base->type) {3254case GDScriptParser::Node::GET_NODE: {3255get_node = static_cast<GDScriptParser::GetNodeNode *>(p_subscript->base);3256} break;32573258case GDScriptParser::Node::IDENTIFIER: {3259const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base);32603261switch (identifier_node->source) {3262case GDScriptParser::IdentifierNode::Source::MEMBER_VARIABLE: {3263if (p_context.current_class != nullptr) {3264const StringName &member_name = identifier_node->name;3265const GDScriptParser::ClassNode *current_class = p_context.current_class;32663267if (current_class->has_member(member_name)) {3268const GDScriptParser::ClassNode::Member &member = current_class->get_member(member_name);32693270if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {3271const GDScriptParser::VariableNode *variable = static_cast<GDScriptParser::VariableNode *>(member.variable);32723273if (variable->initializer && variable->initializer->type == GDScriptParser::Node::GET_NODE) {3274get_node = static_cast<GDScriptParser::GetNodeNode *>(variable->initializer);3275}3276}3277}3278}3279} break;3280case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: {3281// TODO: Do basic assignment flow analysis like in `_guess_expression_type`.3282const GDScriptParser::SuiteNode::Local local = identifier_node->suite->get_local(identifier_node->name);3283switch (local.type) {3284case GDScriptParser::SuiteNode::Local::CONSTANT: {3285if (local.constant->initializer && local.constant->initializer->type == GDScriptParser::Node::GET_NODE) {3286get_node = static_cast<GDScriptParser::GetNodeNode *>(local.constant->initializer);3287}3288} break;3289case GDScriptParser::SuiteNode::Local::VARIABLE: {3290if (local.variable->initializer && local.variable->initializer->type == GDScriptParser::Node::GET_NODE) {3291get_node = static_cast<GDScriptParser::GetNodeNode *>(local.variable->initializer);3292}3293} break;3294default: {3295} break;3296}3297} break;3298default: {3299} break;3300}3301} break;3302default: {3303} break;3304}33053306if (get_node != nullptr) {3307const Object *node = p_context.base->call("get_node_or_null", NodePath(get_node->full_path));3308if (node != nullptr) {3309GDScriptParser::DataType assigned_type = _type_from_variant(node, p_context).type;3310GDScriptParser::DataType base_type = p_subscript->base->datatype;33113312if (p_subscript->base->type == GDScriptParser::Node::IDENTIFIER && base_type.type_source == GDScriptParser::DataType::ANNOTATED_EXPLICIT && (assigned_type.kind != base_type.kind || assigned_type.script_path != base_type.script_path || assigned_type.native_type != base_type.native_type)) {3313// Annotated type takes precedence.3314return false;3315}33163317if (r_base != nullptr) {3318*r_base = node;3319}33203321r_base_type.type_source = GDScriptParser::DataType::INFERRED;3322r_base_type.builtin_type = Variant::OBJECT;3323r_base_type.native_type = node->get_class_name();33243325Ref<Script> scr = node->get_script();3326if (scr.is_null()) {3327r_base_type.kind = GDScriptParser::DataType::NATIVE;3328} else {3329r_base_type.kind = GDScriptParser::DataType::SCRIPT;3330r_base_type.script_type = scr;3331}33323333return true;3334}3335}33363337return false;3338}33393340static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) {3341if (p_call->type == GDScriptParser::Node::PRELOAD) {3342if (p_argidx == 0 && bool(EDITOR_GET("text_editor/completion/complete_file_paths"))) {3343_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);3344}33453346MethodInfo mi(PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, Resource::get_class_static()), "preload", PropertyInfo(Variant::STRING, "path"));3347r_arghint = _make_arguments_hint(mi, p_argidx);3348return;3349} else if (p_call->type != GDScriptParser::Node::CALL) {3350return;3351}33523353Variant base;3354GDScriptParser::DataType base_type;3355bool _static = false;3356const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call);3357GDScriptParser::Node::Type callee_type = call->get_callee_type();33583359if (callee_type == GDScriptParser::Node::SUBSCRIPT) {3360const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);33613362if (subscript->base != nullptr && subscript->base->type == GDScriptParser::Node::IDENTIFIER) {3363const GDScriptParser::IdentifierNode *base_identifier = static_cast<const GDScriptParser::IdentifierNode *>(subscript->base);33643365Variant::Type method_type = GDScriptParser::get_builtin_type(base_identifier->name);3366if (method_type < Variant::VARIANT_MAX) {3367Variant v;3368Callable::CallError err;3369Variant::construct(method_type, v, nullptr, 0, err);3370if (err.error != Callable::CallError::CALL_OK) {3371return;3372}3373List<MethodInfo> methods;3374v.get_method_list(&methods);33753376for (MethodInfo &E : methods) {3377if (p_argidx >= E.arguments.size()) {3378continue;3379}3380if (E.name == call->function_name) {3381r_arghint += _make_arguments_hint(E, p_argidx);3382return;3383}3384}3385}3386}33873388if (subscript->is_attribute) {3389bool found_type = _get_subscript_type(p_context, subscript, base_type, &base);33903391if (!found_type) {3392GDScriptCompletionIdentifier ci;3393if (_guess_expression_type(p_context, subscript->base, ci)) {3394base_type = ci.type;3395base = ci.value;3396} else {3397return;3398}3399}34003401_static = base_type.is_meta_type;3402}3403} else if (Variant::has_utility_function(call->function_name)) {3404MethodInfo info = Variant::get_utility_function_info(call->function_name);3405r_arghint = _make_arguments_hint(info, p_argidx);3406return;3407} else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {3408MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);3409r_arghint = _make_arguments_hint(info, p_argidx);3410return;3411} else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {3412// Complete constructor.3413List<MethodInfo> constructors;3414Variant::get_constructor_list(GDScriptParser::get_builtin_type(call->function_name), &constructors);34153416int i = 0;3417for (const MethodInfo &E : constructors) {3418if (p_argidx >= E.arguments.size()) {3419continue;3420}3421if (i > 0) {3422r_arghint += "\n";3423}3424r_arghint += _make_arguments_hint(E, p_argidx);3425i++;3426}3427return;3428} else if (call->is_super || callee_type == GDScriptParser::Node::IDENTIFIER) {3429base = p_context.base;34303431if (p_context.current_class) {3432base_type = p_context.current_class->get_datatype();3433_static = !p_context.current_function || p_context.current_function->is_static;3434}3435} else {3436return;3437}34383439GDScriptCompletionIdentifier ci;3440ci.type = base_type;3441ci.value = base;3442_list_call_arguments(p_context, ci, call, p_argidx, _static, r_result, r_arghint);34433444r_forced = r_result.size() > 0;3445}34463447::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptLanguage::CodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) {3448const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";34493450GDScriptParser parser;3451GDScriptAnalyzer analyzer(&parser);34523453parser.parse(p_code, p_path, true);3454analyzer.analyze();34553456r_forced = false;3457HashMap<String, ScriptLanguage::CodeCompletionOption> options;34583459GDScriptParser::CompletionContext completion_context = parser.get_completion_context();3460if (completion_context.current_class != nullptr && completion_context.current_class->outer == nullptr) {3461completion_context.base = p_owner;3462}3463bool is_function = false;34643465switch (completion_context.type) {3466case GDScriptParser::COMPLETION_NONE:3467break;3468case GDScriptParser::COMPLETION_ANNOTATION: {3469List<MethodInfo> annotations;3470parser.get_annotation_list(&annotations);3471for (const MethodInfo &E : annotations) {3472ScriptLanguage::CodeCompletionOption option(E.name.substr(1), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3473if (E.arguments.size() > 0) {3474option.insert_text += "(";3475}3476options.insert(option.display, option);3477}3478r_forced = true;3479} break;3480case GDScriptParser::COMPLETION_ANNOTATION_ARGUMENTS: {3481if (completion_context.node == nullptr || completion_context.node->type != GDScriptParser::Node::ANNOTATION) {3482break;3483}3484const GDScriptParser::AnnotationNode *annotation = static_cast<const GDScriptParser::AnnotationNode *>(completion_context.node);3485_find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options, r_call_hint);3486r_forced = true;3487} break;3488case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: {3489// Constants.3490{3491List<StringName> constants;3492Variant::get_constants_for_type(completion_context.builtin_type, &constants);3493for (const StringName &E : constants) {3494ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);3495bool valid = false;3496Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E, &valid);3497if (valid) {3498option.default_value = default_value;3499}3500options.insert(option.display, option);3501}3502}3503// Methods.3504{3505List<StringName> methods;3506Variant::get_builtin_method_list(completion_context.builtin_type, &methods);3507for (const StringName &E : methods) {3508if (Variant::is_builtin_method_static(completion_context.builtin_type, E)) {3509ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3510if (!_guess_expecting_callable(completion_context)) {3511if (Variant::get_builtin_method_argument_count(completion_context.builtin_type, E) > 0 || Variant::is_builtin_method_vararg(completion_context.builtin_type, E)) {3512option.insert_text += "(";3513} else {3514option.insert_text += "()";3515}3516}3517options.insert(option.display, option);3518}3519}3520}3521} break;3522case GDScriptParser::COMPLETION_INHERIT_TYPE: {3523_list_available_types(true, completion_context, options);3524r_forced = true;3525} break;3526case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID: {3527ScriptLanguage::CodeCompletionOption option("void", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3528options.insert(option.display, option);3529}3530[[fallthrough]];3531case GDScriptParser::COMPLETION_TYPE_NAME: {3532_list_available_types(false, completion_context, options);3533r_forced = true;3534} break;3535case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE: {3536_list_available_types(false, completion_context, options);3537ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3538options.insert(get.display, get);3539ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3540options.insert(set.display, set);3541r_forced = true;3542} break;3543case GDScriptParser::COMPLETION_PROPERTY_DECLARATION: {3544ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3545options.insert(get.display, get);3546ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3547options.insert(set.display, set);3548r_forced = true;3549} break;3550case GDScriptParser::COMPLETION_PROPERTY_METHOD: {3551if (!completion_context.current_class) {3552break;3553}3554for (int i = 0; i < completion_context.current_class->members.size(); i++) {3555const GDScriptParser::ClassNode::Member &member = completion_context.current_class->members[i];3556if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {3557continue;3558}3559if (member.function->is_static) {3560continue;3561}3562ScriptLanguage::CodeCompletionOption option(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3563options.insert(option.display, option);3564}3565r_forced = true;3566} break;3567case GDScriptParser::COMPLETION_ASSIGN: {3568GDScriptCompletionIdentifier type;3569if (!completion_context.node || completion_context.node->type != GDScriptParser::Node::ASSIGNMENT) {3570break;3571}3572if (!_guess_expression_type(completion_context, static_cast<const GDScriptParser::AssignmentNode *>(completion_context.node)->assignee, type)) {3573_find_identifiers(completion_context, false, true, options, 0);3574r_forced = true;3575break;3576}35773578if (!type.enumeration.is_empty()) {3579_find_enumeration_candidates(completion_context, type.enumeration, options);3580r_forced = options.size() > 0;3581} else {3582_find_identifiers(completion_context, false, true, options, 0);3583r_forced = true;3584}3585} break;3586case GDScriptParser::COMPLETION_METHOD:3587is_function = true;3588[[fallthrough]];3589case GDScriptParser::COMPLETION_IDENTIFIER: {3590_find_identifiers(completion_context, is_function, !_guess_expecting_callable(completion_context), options, 0);3591} break;3592case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD:3593is_function = true;3594[[fallthrough]];3595case GDScriptParser::COMPLETION_ATTRIBUTE: {3596r_forced = true;3597const GDScriptParser::SubscriptNode *attr = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);3598if (attr->base) {3599GDScriptCompletionIdentifier base;3600bool found_type = _get_subscript_type(completion_context, attr, base.type);3601if (!found_type && !_guess_expression_type(completion_context, attr->base, base)) {3602break;3603}36043605_find_identifiers_in_base(base, is_function, false, !_guess_expecting_callable(completion_context), options, 0);3606}3607} break;3608case GDScriptParser::COMPLETION_SUBSCRIPT: {3609const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);3610GDScriptCompletionIdentifier base;3611const bool res = _guess_expression_type(completion_context, subscript->base, base);36123613// If the type is not known, we assume it is BUILTIN, since indices on arrays is the most common use case.3614if (!subscript->is_attribute && (!res || base.type.kind == GDScriptParser::DataType::BUILTIN || base.type.is_variant())) {3615if (base.value.get_type() == Variant::DICTIONARY) {3616List<PropertyInfo> members;3617base.value.get_property_list(&members);36183619for (const PropertyInfo &E : members) {3620ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::LOCATION_LOCAL);3621options.insert(option.display, option);3622}3623}3624if (!subscript->index || subscript->index->type != GDScriptParser::Node::LITERAL) {3625_find_identifiers(completion_context, false, !_guess_expecting_callable(completion_context), options, 0);3626}3627} else if (res) {3628if (!subscript->is_attribute) {3629// Quote the options if they are not accessed as attribute.36303631HashMap<String, ScriptLanguage::CodeCompletionOption> opt;3632_find_identifiers_in_base(base, false, false, false, opt, 0);3633for (const KeyValue<String, CodeCompletionOption> &E : opt) {3634ScriptLanguage::CodeCompletionOption option(E.value.insert_text.quote(quote_style), E.value.kind, E.value.location);3635options.insert(option.display, option);3636}3637} else {3638_find_identifiers_in_base(base, false, false, !_guess_expecting_callable(completion_context), options, 0);3639}3640}3641} break;3642case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {3643if (!completion_context.current_class) {3644break;3645}36463647const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(completion_context.node);3648ERR_FAIL_INDEX_V_MSG(completion_context.type_chain_index - 1, type->type_chain.size(), Error::ERR_BUG, "Could not complete type argument with out of bounds type chain index.");36493650GDScriptCompletionIdentifier base;36513652if (_guess_identifier_type(completion_context, type->type_chain[0], base)) {3653bool found = true;3654for (int i = 1; i < completion_context.type_chain_index; i++) {3655GDScriptCompletionIdentifier ci;3656found = _guess_identifier_type_from_base(completion_context, base, type->type_chain[i]->name, ci);3657base = ci;3658if (!found) {3659break;3660}3661}3662if (found) {3663_find_identifiers_in_base(base, false, true, true, options, 0);3664}3665}36663667r_forced = true;3668} break;3669case GDScriptParser::COMPLETION_RESOURCE_PATH: {3670if (EDITOR_GET("text_editor/completion/complete_file_paths")) {3671_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);3672r_forced = true;3673}3674} break;3675case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {3676if (!completion_context.node) {3677break;3678}3679_find_call_arguments(completion_context, completion_context.node, completion_context.current_argument, options, r_forced, r_call_hint);3680} break;3681case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {3682GDScriptParser::DataType native_type = completion_context.current_class->base_type;3683GDScriptParser::FunctionNode *function_node = static_cast<GDScriptParser::FunctionNode *>(completion_context.node);3684bool is_static = function_node != nullptr && function_node->is_static;3685while (native_type.is_set() && native_type.kind != GDScriptParser::DataType::NATIVE) {3686switch (native_type.kind) {3687case GDScriptParser::DataType::CLASS: {3688for (const GDScriptParser::ClassNode::Member &member : native_type.class_type->members) {3689if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {3690continue;3691}36923693if (options.has(member.function->identifier->name)) {3694continue;3695}36963697if (completion_context.current_class->has_function(member.get_name()) && completion_context.current_class->get_member(member.get_name()).function != function_node) {3698continue;3699}37003701if (is_static != member.function->is_static) {3702continue;3703}37043705String display_name = member.function->identifier->name;3706display_name += member.function->signature + ":";3707ScriptLanguage::CodeCompletionOption option(display_name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3708options.insert(member.function->identifier->name, option); // Insert name instead of display to track duplicates.3709}3710native_type = native_type.class_type->base_type;3711} break;3712default: {3713native_type.kind = GDScriptParser::DataType::UNRESOLVED;3714} break;3715}3716}37173718if (!native_type.is_set()) {3719break;3720}37213722StringName class_name = native_type.native_type;3723if (!GDScriptAnalyzer::class_exists(class_name)) {3724break;3725}37263727const bool type_hints = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");37283729List<MethodInfo> virtual_methods;3730if (is_static) {3731// Not truly a virtual method, but can also be "overridden".3732MethodInfo static_init("_static_init");3733static_init.return_val.type = Variant::NIL;3734static_init.flags |= METHOD_FLAG_STATIC | METHOD_FLAG_VIRTUAL;3735virtual_methods.push_back(static_init);3736} else {3737ClassDB::get_virtual_methods(class_name, &virtual_methods);3738}37393740for (const MethodInfo &mi : virtual_methods) {3741if (options.has(mi.name)) {3742continue;3743}3744if (completion_context.current_class->has_function(mi.name) && completion_context.current_class->get_member(mi.name).function != function_node) {3745continue;3746}3747String method_hint = mi.name;3748if (method_hint.contains_char(':')) {3749method_hint = method_hint.get_slicec(':', 0);3750}3751method_hint += "(";37523753for (int64_t i = 0; i < mi.arguments.size(); ++i) {3754if (i > 0) {3755method_hint += ", ";3756}3757String arg = mi.arguments[i].name;3758if (arg.contains_char(':')) {3759arg = arg.substr(0, arg.find_char(':'));3760}3761method_hint += arg;3762if (type_hints) {3763method_hint += ": " + _get_visual_datatype(mi.arguments[i], true, class_name);3764}3765}3766if (mi.flags & METHOD_FLAG_VARARG) {3767if (!mi.arguments.is_empty()) {3768method_hint += ", ";3769}3770method_hint += "...args"; // `MethodInfo` does not support the rest parameter name.3771if (type_hints) {3772method_hint += ": Array";3773}3774}3775method_hint += ")";3776if (type_hints) {3777method_hint += " -> " + _get_visual_datatype(mi.return_val, false, class_name);3778}3779method_hint += ":";37803781ScriptLanguage::CodeCompletionOption option(method_hint, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3782options.insert(option.display, option);3783}3784} break;3785case GDScriptParser::COMPLETION_GET_NODE: {3786// Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically.3787if (p_owner) {3788List<String> opts;3789p_owner->get_argument_options("get_node", 0, &opts);37903791bool for_unique_name = false;3792if (completion_context.node != nullptr && completion_context.node->type == GDScriptParser::Node::GET_NODE && !static_cast<GDScriptParser::GetNodeNode *>(completion_context.node)->use_dollar) {3793for_unique_name = true;3794}37953796for (const String &E : opts) {3797r_forced = true;3798String opt = E.strip_edges();3799if (opt.is_quoted()) {3800// Remove quotes so that we can handle user preferred quote style,3801// or handle NodePaths which are valid identifiers and don't need quotes.3802opt = opt.unquote();3803}38043805if (for_unique_name) {3806if (!opt.begins_with("%")) {3807continue;3808}3809opt = opt.substr(1);3810}38113812// The path needs quotes if at least one of its components (excluding `%` prefix and `/` separations)3813// is not a valid identifier.3814bool path_needs_quote = false;3815for (const String &part : opt.trim_prefix("%").split("/")) {3816if (!part.is_valid_ascii_identifier()) {3817path_needs_quote = true;3818break;3819}3820}38213822if (path_needs_quote) {3823// Ignore quote_style and just use double quotes for paths with apostrophes.3824// Double quotes don't need to be checked because they're not valid in node and property names.3825opt = opt.quote(opt.contains_char('\'') ? "\"" : quote_style); // Handle user preference.3826}3827ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);3828options.insert(option.display, option);3829}38303831if (!for_unique_name) {3832// Get autoloads.3833for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {3834String path = "/root/" + E.key;3835ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);3836options.insert(option.display, option);3837}3838}3839}3840} break;3841case GDScriptParser::COMPLETION_SUPER:3842break;3843case GDScriptParser::COMPLETION_SUPER_METHOD: {3844if (!completion_context.current_class) {3845break;3846}3847_find_identifiers_in_class(completion_context.current_class, true, false, false, true, !_guess_expecting_callable(completion_context), options, 0);3848} break;3849}38503851for (const KeyValue<String, ScriptLanguage::CodeCompletionOption> &E : options) {3852r_options->push_back(E.value);3853}38543855return OK;3856}38573858#else // !TOOLS_ENABLED38593860Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptLanguage::CodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) {3861return OK;3862}38633864#endif // TOOLS_ENABLED38653866//////// END COMPLETION //////////38673868String GDScriptLanguage::_get_indentation() const {3869#ifdef TOOLS_ENABLED3870if (Engine::get_singleton()->is_editor_hint()) {3871bool use_space_indentation = EDITOR_GET("text_editor/behavior/indent/type");38723873if (use_space_indentation) {3874int indent_size = EDITOR_GET("text_editor/behavior/indent/size");3875return String(" ").repeat(indent_size);3876}3877}3878#endif3879return "\t";3880}38813882void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {3883String indent = _get_indentation();38843885Vector<String> lines = p_code.split("\n");3886List<int> indent_stack;38873888for (int i = 0; i < lines.size(); i++) {3889String l = lines[i];3890int tc = 0;3891for (int j = 0; j < l.length(); j++) {3892if (l[j] == ' ' || l[j] == '\t') {3893tc++;3894} else {3895break;3896}3897}38983899String st = l.substr(tc).strip_edges();3900if (st.is_empty() || st.begins_with("#")) {3901continue; //ignore!3902}39033904int ilevel = 0;3905if (indent_stack.size()) {3906ilevel = indent_stack.back()->get();3907}39083909if (tc > ilevel) {3910indent_stack.push_back(tc);3911} else if (tc < ilevel) {3912while (indent_stack.size() && indent_stack.back()->get() > tc) {3913indent_stack.pop_back();3914}39153916if (indent_stack.size() && indent_stack.back()->get() != tc) {3917indent_stack.push_back(tc); // this is not right but gets the job done3918}3919}39203921if (i >= p_from_line) {3922l = indent.repeat(indent_stack.size()) + st;3923} else if (i > p_to_line) {3924break;3925}39263927lines.write[i] = l;3928}39293930p_code = "";3931for (int i = 0; i < lines.size(); i++) {3932if (i > 0) {3933p_code += "\n";3934}3935p_code += lines[i];3936}3937}39383939#ifdef TOOLS_ENABLED39403941static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, const String &p_symbol, GDScriptLanguage::LookupResult &r_result) {3942GDScriptParser::DataType base_type = p_base;39433944while (true) {3945switch (base_type.kind) {3946case GDScriptParser::DataType::CLASS: {3947ERR_FAIL_NULL_V(base_type.class_type, ERR_BUG);39483949String name = p_symbol;3950if (name == "new") {3951name = "_init";3952}39533954if (!base_type.class_type->has_member(name)) {3955base_type = base_type.class_type->base_type;3956break;3957}39583959const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(name);39603961switch (member.type) {3962case GDScriptParser::ClassNode::Member::UNDEFINED:3963case GDScriptParser::ClassNode::Member::GROUP:3964return ERR_BUG;3965case GDScriptParser::ClassNode::Member::CLASS: {3966String doc_type_name;3967String doc_enum_name;3968GDScriptDocGen::doctype_from_gdtype(GDScriptAnalyzer::type_from_metatype(member.get_datatype()), doc_type_name, doc_enum_name);39693970r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;3971r_result.class_name = doc_type_name;3972} break;3973case GDScriptParser::ClassNode::Member::CONSTANT:3974r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;3975break;3976case GDScriptParser::ClassNode::Member::FUNCTION:3977r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;3978break;3979case GDScriptParser::ClassNode::Member::SIGNAL:3980r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;3981break;3982case GDScriptParser::ClassNode::Member::VARIABLE:3983r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;3984break;3985case GDScriptParser::ClassNode::Member::ENUM:3986r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;3987break;3988case GDScriptParser::ClassNode::Member::ENUM_VALUE:3989r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;3990break;3991}39923993if (member.type != GDScriptParser::ClassNode::Member::CLASS) {3994String doc_type_name;3995String doc_enum_name;3996GDScriptDocGen::doctype_from_gdtype(GDScriptAnalyzer::type_from_metatype(base_type), doc_type_name, doc_enum_name);39973998r_result.class_name = doc_type_name;3999r_result.class_member = name;4000}40014002Error err = OK;4003r_result.script = GDScriptCache::get_shallow_script(base_type.script_path, err);4004r_result.script_path = base_type.script_path;4005r_result.location = member.get_line();4006return err;4007} break;4008case GDScriptParser::DataType::SCRIPT: {4009const Ref<Script> scr = base_type.script_type;40104011if (scr.is_null()) {4012return ERR_CANT_RESOLVE;4013}40144015String name = p_symbol;4016if (name == "new") {4017name = "_init";4018}40194020const int line = scr->get_member_line(name);4021if (line >= 0) {4022bool found_type = false;4023r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION;4024{4025List<PropertyInfo> properties;4026scr->get_script_property_list(&properties);4027for (const PropertyInfo &property : properties) {4028if (property.name == name && (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) {4029found_type = true;4030r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;4031r_result.class_name = scr->get_doc_class_name();4032r_result.class_member = name;4033break;4034}4035}4036}4037if (!found_type) {4038List<MethodInfo> methods;4039scr->get_script_method_list(&methods);4040for (const MethodInfo &method : methods) {4041if (method.name == name) {4042found_type = true;4043r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4044r_result.class_name = scr->get_doc_class_name();4045r_result.class_member = name;4046break;4047}4048}4049}4050if (!found_type) {4051List<MethodInfo> signals;4052scr->get_script_method_list(&signals);4053for (const MethodInfo &signal : signals) {4054if (signal.name == name) {4055found_type = true;4056r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;4057r_result.class_name = scr->get_doc_class_name();4058r_result.class_member = name;4059break;4060}4061}4062}4063if (!found_type) {4064const Ref<GDScript> gds = scr;4065if (gds.is_valid()) {4066const Ref<GDScript> *subclass = gds->get_subclasses().getptr(name);4067if (subclass != nullptr) {4068found_type = true;4069r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4070r_result.class_name = subclass->ptr()->get_doc_class_name();4071}4072// TODO: enums.4073}4074}4075if (!found_type) {4076HashMap<StringName, Variant> constants;4077scr->get_constants(&constants);4078if (constants.has(name)) {4079found_type = true;4080r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4081r_result.class_name = scr->get_doc_class_name();4082r_result.class_member = name;4083}4084}40854086r_result.script = scr;4087r_result.script_path = base_type.script_path;4088r_result.location = line;4089return OK;4090}40914092const Ref<Script> base_script = scr->get_base_script();4093if (base_script.is_valid()) {4094base_type.script_type = base_script;4095} else {4096base_type.kind = GDScriptParser::DataType::NATIVE;4097base_type.builtin_type = Variant::OBJECT;4098base_type.native_type = scr->get_instance_base_type();4099}4100} break;4101case GDScriptParser::DataType::NATIVE: {4102const StringName &class_name = base_type.native_type;41034104ERR_FAIL_COND_V(!GDScriptAnalyzer::class_exists(class_name), ERR_BUG);41054106if (ClassDB::has_method(class_name, p_symbol, true)) {4107r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4108r_result.class_name = class_name;4109r_result.class_member = p_symbol;4110return OK;4111}41124113List<MethodInfo> virtual_methods;4114ClassDB::get_virtual_methods(class_name, &virtual_methods, true);4115for (const MethodInfo &E : virtual_methods) {4116if (E.name == p_symbol) {4117r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4118r_result.class_name = class_name;4119r_result.class_member = p_symbol;4120return OK;4121}4122}41234124if (ClassDB::has_signal(class_name, p_symbol, true)) {4125r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;4126r_result.class_name = class_name;4127r_result.class_member = p_symbol;4128return OK;4129}41304131List<StringName> enums;4132ClassDB::get_enum_list(class_name, &enums);4133for (const StringName &E : enums) {4134if (E == p_symbol) {4135r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4136r_result.class_name = class_name;4137r_result.class_member = p_symbol;4138return OK;4139}4140}41414142if (!String(ClassDB::get_integer_constant_enum(class_name, p_symbol, true)).is_empty()) {4143r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4144r_result.class_name = class_name;4145r_result.class_member = p_symbol;4146return OK;4147}41484149List<String> constants;4150ClassDB::get_integer_constant_list(class_name, &constants, true);4151for (const String &E : constants) {4152if (E == p_symbol) {4153r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4154r_result.class_name = class_name;4155r_result.class_member = p_symbol;4156return OK;4157}4158}41594160if (ClassDB::has_property(class_name, p_symbol, true)) {4161PropertyInfo prop_info;4162ClassDB::get_property_info(class_name, p_symbol, &prop_info, true);4163if (prop_info.usage & PROPERTY_USAGE_INTERNAL) {4164return ERR_CANT_RESOLVE;4165}41664167r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;4168r_result.class_name = class_name;4169r_result.class_member = p_symbol;4170return OK;4171}41724173const StringName parent_class = ClassDB::get_parent_class(class_name);4174if (parent_class != StringName()) {4175base_type.native_type = parent_class;4176} else {4177return ERR_CANT_RESOLVE;4178}4179} break;4180case GDScriptParser::DataType::BUILTIN: {4181if (base_type.is_meta_type) {4182if (Variant::has_enum(base_type.builtin_type, p_symbol)) {4183r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4184r_result.class_name = Variant::get_type_name(base_type.builtin_type);4185r_result.class_member = p_symbol;4186return OK;4187}41884189if (Variant::has_constant(base_type.builtin_type, p_symbol)) {4190r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4191r_result.class_name = Variant::get_type_name(base_type.builtin_type);4192r_result.class_member = p_symbol;4193return OK;4194}4195} else {4196if (Variant::has_member(base_type.builtin_type, p_symbol)) {4197r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;4198r_result.class_name = Variant::get_type_name(base_type.builtin_type);4199r_result.class_member = p_symbol;4200return OK;4201}4202}42034204if (Variant::has_builtin_method(base_type.builtin_type, p_symbol)) {4205r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4206r_result.class_name = Variant::get_type_name(base_type.builtin_type);4207r_result.class_member = p_symbol;4208return OK;4209}42104211return ERR_CANT_RESOLVE;4212} break;4213case GDScriptParser::DataType::ENUM: {4214if (base_type.is_meta_type) {4215if (base_type.enum_values.has(p_symbol)) {4216String doc_type_name;4217String doc_enum_name;4218GDScriptDocGen::doctype_from_gdtype(GDScriptAnalyzer::type_from_metatype(base_type), doc_type_name, doc_enum_name);42194220if (CoreConstants::is_global_enum(doc_enum_name)) {4221r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4222r_result.class_name = "@GlobalScope";4223r_result.class_member = p_symbol;4224return OK;4225} else {4226const int dot_pos = doc_enum_name.rfind_char('.');4227if (dot_pos >= 0) {4228Error err = OK;4229r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4230if (base_type.class_type != nullptr) {4231// For script enums the value isn't accessible as class constant so we need the full enum name.4232r_result.class_name = doc_enum_name;4233r_result.class_member = p_symbol;4234r_result.script = GDScriptCache::get_shallow_script(base_type.script_path, err);4235r_result.script_path = base_type.script_path;4236const String enum_name = doc_enum_name.substr(dot_pos + 1);4237if (base_type.class_type->has_member(enum_name)) {4238const GDScriptParser::ClassNode::Member member = base_type.class_type->get_member(enum_name);4239if (member.type == GDScriptParser::ClassNode::Member::ENUM) {4240for (const GDScriptParser::EnumNode::Value &value : member.m_enum->values) {4241if (value.identifier->name == p_symbol) {4242r_result.location = value.line;4243break;4244}4245}4246}4247}4248} else if (base_type.script_type.is_valid()) {4249// For script enums the value isn't accessible as class constant so we need the full enum name.4250r_result.class_name = doc_enum_name;4251r_result.class_member = p_symbol;4252r_result.script = base_type.script_type;4253r_result.script_path = base_type.script_path;4254// TODO: Find a way to obtain enum value location for a script4255r_result.location = base_type.script_type->get_member_line(doc_enum_name.substr(dot_pos + 1));4256} else {4257r_result.class_name = doc_enum_name.left(dot_pos);4258r_result.class_member = p_symbol;4259}4260return err;4261}4262}4263} else if (Variant::has_builtin_method(Variant::DICTIONARY, p_symbol)) {4264r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4265r_result.class_name = "Dictionary";4266r_result.class_member = p_symbol;4267return OK;4268}4269}42704271return ERR_CANT_RESOLVE;4272} break;4273case GDScriptParser::DataType::VARIANT: {4274if (base_type.is_meta_type) {4275const String enum_name = "Variant." + p_symbol;4276if (CoreConstants::is_global_enum(enum_name)) {4277r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4278r_result.class_name = "@GlobalScope";4279r_result.class_member = enum_name;4280return OK;4281}4282}42834284return ERR_CANT_RESOLVE;4285} break;4286case GDScriptParser::DataType::RESOLVING:4287case GDScriptParser::DataType::UNRESOLVED: {4288return ERR_CANT_RESOLVE;4289} break;4290}4291}42924293return ERR_CANT_RESOLVE;4294}42954296::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) {4297// Before parsing, try the usual stuff.4298if (GDScriptAnalyzer::class_exists(p_symbol)) {4299r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4300r_result.class_name = p_symbol;4301return OK;4302}43034304if (Variant::get_type_by_name(p_symbol) < Variant::VARIANT_MAX) {4305r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4306r_result.class_name = p_symbol;4307return OK;4308}43094310if (p_symbol == "Variant") {4311r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4312r_result.class_name = "Variant";4313return OK;4314}43154316if (p_symbol == "PI" || p_symbol == "TAU" || p_symbol == "INF" || p_symbol == "NAN") {4317r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4318r_result.class_name = "@GDScript";4319r_result.class_member = p_symbol;4320return OK;4321}43224323GDScriptParser parser;4324parser.parse(p_code, p_path, true);43254326GDScriptParser::CompletionContext context = parser.get_completion_context();4327context.base = p_owner;43284329// Allows class functions with the names like built-ins to be handled properly.4330if (context.type != GDScriptParser::COMPLETION_ATTRIBUTE) {4331// Need special checks for `assert` and `preload` as they are technically4332// keywords, so are not registered in `GDScriptUtilityFunctions`.4333if (GDScriptUtilityFunctions::function_exists(p_symbol) || p_symbol == "assert" || p_symbol == "preload") {4334r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4335r_result.class_name = "@GDScript";4336r_result.class_member = p_symbol;4337return OK;4338}4339}43404341GDScriptAnalyzer analyzer(&parser);4342analyzer.analyze();43434344if (context.current_class && context.current_class->extends.size() > 0) {4345StringName class_name = context.current_class->extends[0]->name;43464347bool success = false;4348ClassDB::get_integer_constant(class_name, p_symbol, &success);4349if (success) {4350r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4351r_result.class_name = class_name;4352r_result.class_member = p_symbol;4353return OK;4354}4355do {4356List<StringName> enums;4357ClassDB::get_enum_list(class_name, &enums, true);4358for (const StringName &enum_name : enums) {4359if (enum_name == p_symbol) {4360r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4361r_result.class_name = class_name;4362r_result.class_member = p_symbol;4363return OK;4364}4365}4366class_name = ClassDB::get_parent_class_nocheck(class_name);4367} while (class_name != StringName());4368}43694370const GDScriptParser::TypeNode *type_node = dynamic_cast<const GDScriptParser::TypeNode *>(context.node);4371if (type_node != nullptr && !type_node->type_chain.is_empty()) {4372StringName class_name = type_node->type_chain[0]->name;4373if (ScriptServer::is_global_class(class_name)) {4374class_name = ScriptServer::get_global_class_native_base(class_name);4375}4376do {4377List<StringName> enums;4378ClassDB::get_enum_list(class_name, &enums, true);4379for (const StringName &enum_name : enums) {4380if (enum_name == p_symbol) {4381r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4382r_result.class_name = class_name;4383r_result.class_member = p_symbol;4384return OK;4385}4386}4387class_name = ClassDB::get_parent_class_nocheck(class_name);4388} while (class_name != StringName());4389}43904391bool is_function = false;43924393switch (context.type) {4394case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: {4395GDScriptParser::DataType base_type;4396base_type.kind = GDScriptParser::DataType::BUILTIN;4397base_type.builtin_type = context.builtin_type;4398base_type.is_meta_type = true;4399if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4400return OK;4401}4402} break;4403case GDScriptParser::COMPLETION_SUPER: {4404if (context.current_class && context.current_function) {4405if (_lookup_symbol_from_base(context.current_class->base_type, context.current_function->info.name, r_result) == OK) {4406return OK;4407}4408}4409} break;4410case GDScriptParser::COMPLETION_SUPER_METHOD:4411case GDScriptParser::COMPLETION_METHOD:4412case GDScriptParser::COMPLETION_ASSIGN:4413case GDScriptParser::COMPLETION_CALL_ARGUMENTS:4414case GDScriptParser::COMPLETION_IDENTIFIER:4415case GDScriptParser::COMPLETION_PROPERTY_METHOD:4416case GDScriptParser::COMPLETION_SUBSCRIPT: {4417GDScriptParser::DataType base_type;4418if (context.current_class) {4419if (context.type != GDScriptParser::COMPLETION_SUPER_METHOD) {4420base_type = context.current_class->get_datatype();4421} else {4422base_type = context.current_class->base_type;4423}4424} else {4425break;4426}44274428if (!is_function && context.current_suite) {4429// Lookup local variables.4430const GDScriptParser::SuiteNode *suite = context.current_suite;4431while (suite) {4432if (suite->has_local(p_symbol)) {4433const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_symbol);44344435switch (local.type) {4436case GDScriptParser::SuiteNode::Local::UNDEFINED:4437return ERR_BUG;4438case GDScriptParser::SuiteNode::Local::CONSTANT:4439r_result.type = ScriptLanguage::LOOKUP_RESULT_LOCAL_CONSTANT;4440r_result.description = local.constant->doc_data.description;4441r_result.is_deprecated = local.constant->doc_data.is_deprecated;4442r_result.deprecated_message = local.constant->doc_data.deprecated_message;4443r_result.is_experimental = local.constant->doc_data.is_experimental;4444r_result.experimental_message = local.constant->doc_data.experimental_message;4445if (local.constant->initializer != nullptr) {4446r_result.value = GDScriptDocGen::docvalue_from_expression(local.constant->initializer);4447}4448break;4449case GDScriptParser::SuiteNode::Local::VARIABLE:4450r_result.type = ScriptLanguage::LOOKUP_RESULT_LOCAL_VARIABLE;4451r_result.description = local.variable->doc_data.description;4452r_result.is_deprecated = local.variable->doc_data.is_deprecated;4453r_result.deprecated_message = local.variable->doc_data.deprecated_message;4454r_result.is_experimental = local.variable->doc_data.is_experimental;4455r_result.experimental_message = local.variable->doc_data.experimental_message;4456if (local.variable->initializer != nullptr) {4457r_result.value = GDScriptDocGen::docvalue_from_expression(local.variable->initializer);4458}4459break;4460case GDScriptParser::SuiteNode::Local::PARAMETER:4461case GDScriptParser::SuiteNode::Local::FOR_VARIABLE:4462case GDScriptParser::SuiteNode::Local::PATTERN_BIND:4463r_result.type = ScriptLanguage::LOOKUP_RESULT_LOCAL_VARIABLE;4464break;4465}44664467GDScriptDocGen::doctype_from_gdtype(local.get_datatype(), r_result.doc_type, r_result.enumeration);44684469Error err = OK;4470r_result.script = GDScriptCache::get_shallow_script(base_type.script_path, err);4471r_result.script_path = base_type.script_path;4472r_result.location = local.start_line;4473return err;4474}4475suite = suite->parent_block;4476}4477}44784479if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4480return OK;4481}44824483if (!is_function) {4484if (ProjectSettings::get_singleton()->has_autoload(p_symbol)) {4485const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(p_symbol);4486if (autoload.is_singleton) {4487String scr_path = autoload.path;4488if (!scr_path.ends_with(".gd")) {4489// Not a script, try find the script anyway, may have some success.4490scr_path = scr_path.get_basename() + ".gd";4491}44924493if (FileAccess::exists(scr_path)) {4494r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4495r_result.class_name = p_symbol;4496r_result.script = ResourceLoader::load(scr_path);4497r_result.script_path = scr_path;4498r_result.location = 0;4499return OK;4500}4501}4502}45034504if (ScriptServer::is_global_class(p_symbol)) {4505const String scr_path = ScriptServer::get_global_class_path(p_symbol);4506const Ref<Script> scr = ResourceLoader::load(scr_path);4507if (scr.is_null()) {4508return ERR_BUG;4509}4510r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4511r_result.class_name = scr->get_doc_class_name();4512r_result.script = scr;4513r_result.script_path = scr_path;4514r_result.location = 0;4515return OK;4516}45174518const HashMap<StringName, int> &global_map = GDScriptLanguage::get_singleton()->get_global_map();4519if (global_map.has(p_symbol)) {4520Variant value = GDScriptLanguage::get_singleton()->get_global_array()[global_map[p_symbol]];4521if (value.get_type() == Variant::OBJECT) {4522const Object *obj = value;4523if (obj) {4524if (Object::cast_to<GDScriptNativeClass>(obj)) {4525r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4526r_result.class_name = Object::cast_to<GDScriptNativeClass>(obj)->get_name();4527} else {4528r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4529r_result.class_name = obj->get_class();4530}4531return OK;4532}4533}4534}45354536if (CoreConstants::is_global_enum(p_symbol)) {4537r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4538r_result.class_name = "@GlobalScope";4539r_result.class_member = p_symbol;4540return OK;4541}45424543if (CoreConstants::is_global_constant(p_symbol)) {4544r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4545r_result.class_name = "@GlobalScope";4546r_result.class_member = p_symbol;4547return OK;4548}45494550if (Variant::has_utility_function(p_symbol)) {4551r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4552r_result.class_name = "@GlobalScope";4553r_result.class_member = p_symbol;4554return OK;4555}4556}4557} break;4558case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD:4559case GDScriptParser::COMPLETION_ATTRIBUTE: {4560if (context.node->type != GDScriptParser::Node::SUBSCRIPT) {4561break;4562}4563const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(context.node);4564if (!subscript->is_attribute) {4565break;4566}4567GDScriptCompletionIdentifier base;45684569bool found_type = _get_subscript_type(context, subscript, base.type);4570if (!found_type && !_guess_expression_type(context, subscript->base, base)) {4571break;4572}45734574if (_lookup_symbol_from_base(base.type, p_symbol, r_result) == OK) {4575return OK;4576}4577} break;4578case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {4579if (context.node == nullptr || context.node->type != GDScriptParser::Node::TYPE) {4580break;4581}4582const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(context.node);45834584GDScriptParser::DataType base_type;4585const GDScriptParser::IdentifierNode *prev = nullptr;4586for (const GDScriptParser::IdentifierNode *E : type->type_chain) {4587if (E->name == p_symbol && prev != nullptr) {4588base_type = prev->get_datatype();4589break;4590}4591prev = E;4592}4593if (base_type.kind != GDScriptParser::DataType::CLASS) {4594GDScriptCompletionIdentifier base;4595if (!_guess_expression_type(context, prev, base)) {4596break;4597}4598base_type = base.type;4599}46004601if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4602return OK;4603}4604} break;4605case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {4606GDScriptParser::DataType base_type = context.current_class->base_type;46074608if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4609return OK;4610}4611} break;4612case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE:4613case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID:4614case GDScriptParser::COMPLETION_TYPE_NAME: {4615GDScriptParser::DataType base_type = context.current_class->get_datatype();46164617if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4618return OK;4619}4620} break;4621case GDScriptParser::COMPLETION_ANNOTATION: {4622const String annotation_symbol = "@" + p_symbol;4623if (parser.annotation_exists(annotation_symbol)) {4624r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION;4625r_result.class_name = "@GDScript";4626r_result.class_member = annotation_symbol;4627return OK;4628}4629} break;4630default: {4631}4632}46334634return ERR_CANT_RESOLVE;4635}46364637#endif // TOOLS_ENABLED463846394640