Path: blob/master/modules/gdscript/gdscript_editor.cpp
10277 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.line;172e.column = pe.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.line;183e.column = pe.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}247248Script *GDScriptLanguage::create_script() const {249return memnew(GDScript);250}251252/* DEBUGGER FUNCTIONS */253254thread_local int GDScriptLanguage::_debug_parse_err_line = -1;255thread_local String GDScriptLanguage::_debug_parse_err_file;256thread_local String GDScriptLanguage::_debug_error;257258bool GDScriptLanguage::debug_break_parse(const String &p_file, int p_line, const String &p_error) {259// break because of parse error260261if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {262_debug_parse_err_line = p_line;263_debug_parse_err_file = p_file;264_debug_error = p_error;265EngineDebugger::get_script_debugger()->debug(this, false, true);266// Because this is thread local, clear the memory afterwards.267_debug_parse_err_file = String();268_debug_error = String();269return true;270} else {271return false;272}273}274275bool GDScriptLanguage::debug_break(const String &p_error, bool p_allow_continue) {276if (EngineDebugger::is_active()) {277_debug_parse_err_line = -1;278_debug_parse_err_file = "";279_debug_error = p_error;280bool is_error_breakpoint = p_error != "Breakpoint";281EngineDebugger::get_script_debugger()->debug(this, p_allow_continue, is_error_breakpoint);282// Because this is thread local, clear the memory afterwards.283_debug_parse_err_file = String();284_debug_error = String();285return true;286} else {287return false;288}289}290291String GDScriptLanguage::debug_get_error() const {292return _debug_error;293}294295int GDScriptLanguage::debug_get_stack_level_count() const {296if (_debug_parse_err_line >= 0) {297return 1;298}299300return _call_stack_size;301}302303int GDScriptLanguage::debug_get_stack_level_line(int p_level) const {304if (_debug_parse_err_line >= 0) {305return _debug_parse_err_line;306}307308ERR_FAIL_INDEX_V(p_level, (int)_call_stack_size, -1);309310return *(_get_stack_level(p_level)->line);311}312313String GDScriptLanguage::debug_get_stack_level_function(int p_level) const {314if (_debug_parse_err_line >= 0) {315return "";316}317318ERR_FAIL_INDEX_V(p_level, (int)_call_stack_size, "");319GDScriptFunction *func = _get_stack_level(p_level)->function;320return func ? func->get_name().operator String() : "";321}322323String GDScriptLanguage::debug_get_stack_level_source(int p_level) const {324if (_debug_parse_err_line >= 0) {325return _debug_parse_err_file;326}327328ERR_FAIL_INDEX_V(p_level, (int)_call_stack_size, "");329return _get_stack_level(p_level)->function->get_source();330}331332void 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) {333if (_debug_parse_err_line >= 0) {334return;335}336337ERR_FAIL_INDEX(p_level, (int)_call_stack_size);338339CallLevel *cl = _get_stack_level(p_level);340GDScriptFunction *f = cl->function;341342List<Pair<StringName, int>> locals;343344f->debug_get_stack_member_state(*cl->line, &locals);345for (const Pair<StringName, int> &E : locals) {346p_locals->push_back(E.first);347p_values->push_back(cl->stack[E.second]);348}349}350351void 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) {352if (_debug_parse_err_line >= 0) {353return;354}355356ERR_FAIL_INDEX(p_level, (int)_call_stack_size);357358CallLevel *cl = _get_stack_level(p_level);359GDScriptInstance *instance = cl->instance;360361if (!instance) {362return;363}364365Ref<GDScript> scr = instance->get_script();366ERR_FAIL_COND(scr.is_null());367368const HashMap<StringName, GDScript::MemberInfo> &mi = scr->debug_get_member_indices();369370for (const KeyValue<StringName, GDScript::MemberInfo> &E : mi) {371p_members->push_back(E.key);372p_values->push_back(instance->debug_get_member_by_index(E.value.index));373}374}375376ScriptInstance *GDScriptLanguage::debug_get_stack_level_instance(int p_level) {377if (_debug_parse_err_line >= 0) {378return nullptr;379}380381ERR_FAIL_INDEX_V(p_level, (int)_call_stack_size, nullptr);382383return _get_stack_level(p_level)->instance;384}385386void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {387const HashMap<StringName, int> &name_idx = GDScriptLanguage::get_singleton()->get_global_map();388const Variant *gl_array = GDScriptLanguage::get_singleton()->get_global_array();389390List<Pair<String, Variant>> cinfo;391get_public_constants(&cinfo);392393for (const KeyValue<StringName, int> &E : name_idx) {394if (ClassDB::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) {395continue;396}397398bool is_script_constant = false;399for (List<Pair<String, Variant>>::Element *CE = cinfo.front(); CE; CE = CE->next()) {400if (CE->get().first == E.key) {401is_script_constant = true;402break;403}404}405if (is_script_constant) {406continue;407}408409const Variant &var = gl_array[E.value];410bool freed = false;411const Object *obj = var.get_validated_object_with_check(freed);412if (obj && !freed) {413if (Object::cast_to<GDScriptNativeClass>(obj)) {414continue;415}416}417418bool skip = false;419for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {420if (E.key == CoreConstants::get_global_constant_name(i)) {421skip = true;422break;423}424}425if (skip) {426continue;427}428429p_globals->push_back(E.key);430p_values->push_back(var);431}432}433434String GDScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) {435List<String> names;436List<Variant> values;437debug_get_stack_level_locals(p_level, &names, &values, p_max_subitems, p_max_depth);438439Vector<String> name_vector;440for (const String &name : names) {441name_vector.push_back(name);442}443444Array value_array;445for (const Variant &value : values) {446value_array.push_back(value);447}448449Expression expression;450if (expression.parse(p_expression, name_vector) == OK) {451ScriptInstance *instance = debug_get_stack_level_instance(p_level);452if (instance) {453Variant return_val = expression.execute(value_array, instance->get_owner());454return return_val.get_construct_string();455}456}457458return String();459}460461void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) const {462p_extensions->push_back("gd");463}464465void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {466List<StringName> functions;467GDScriptUtilityFunctions::get_function_list(&functions);468469for (const StringName &E : functions) {470p_functions->push_back(GDScriptUtilityFunctions::get_function_info(E));471}472473// Not really "functions", but show in documentation.474{475MethodInfo mi;476mi.name = "preload";477mi.arguments.push_back(PropertyInfo(Variant::STRING, "path"));478mi.return_val = PropertyInfo(Variant::OBJECT, "", PROPERTY_HINT_RESOURCE_TYPE, "Resource");479p_functions->push_back(mi);480}481{482MethodInfo mi;483mi.name = "assert";484mi.return_val.type = Variant::NIL;485mi.arguments.push_back(PropertyInfo(Variant::BOOL, "condition"));486mi.arguments.push_back(PropertyInfo(Variant::STRING, "message"));487mi.default_arguments.push_back(String());488p_functions->push_back(mi);489}490}491492void GDScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_constants) const {493Pair<String, Variant> pi;494pi.first = "PI";495pi.second = Math::PI;496p_constants->push_back(pi);497498Pair<String, Variant> tau;499tau.first = "TAU";500tau.second = Math::TAU;501p_constants->push_back(tau);502503Pair<String, Variant> infinity;504infinity.first = "INF";505infinity.second = Math::INF;506p_constants->push_back(infinity);507508Pair<String, Variant> nan;509nan.first = "NAN";510nan.second = Math::NaN;511p_constants->push_back(nan);512}513514void GDScriptLanguage::get_public_annotations(List<MethodInfo> *p_annotations) const {515GDScriptParser parser;516List<MethodInfo> annotations;517parser.get_annotation_list(&annotations);518519for (const MethodInfo &E : annotations) {520p_annotations->push_back(E);521}522}523524String GDScriptLanguage::make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const {525#ifdef TOOLS_ENABLED526const bool type_hints = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");527#else528const bool type_hints = true;529#endif530531String result = "func " + p_name + "(";532if (p_args.size()) {533for (int i = 0; i < p_args.size(); i++) {534if (i > 0) {535result += ", ";536}537538const String name_unstripped = p_args[i].get_slicec(':', 0);539result += name_unstripped.strip_edges();540541if (type_hints) {542const String type_stripped = p_args[i].substr(name_unstripped.length() + 1).strip_edges();543if (!type_stripped.is_empty()) {544result += ": " + type_stripped;545}546}547}548}549result += String(")") + (type_hints ? " -> void" : "") + ":\n" +550_get_indentation() + "pass # Replace with function body.\n";551552return result;553}554555//////// COMPLETION //////////556557#ifdef TOOLS_ENABLED558559#define COMPLETION_RECURSION_LIMIT 200560561struct GDScriptCompletionIdentifier {562GDScriptParser::DataType type;563String enumeration;564Variant value;565const GDScriptParser::ExpressionNode *assigned_expression = nullptr;566};567568// LOCATION METHODS569// These methods are used to populate the `CodeCompletionOption::location` integer.570// For these methods, the location is based on the depth in the inheritance chain that the property571// appears. For example, if you are completing code in a class that inherits Node2D, a property found on Node2D572// will have a "better" (lower) location "score" than a property that is found on CanvasItem.573574static int _get_property_location(const StringName &p_class, const StringName &p_property) {575if (!ClassDB::has_property(p_class, p_property)) {576return ScriptLanguage::LOCATION_OTHER;577}578579int depth = 0;580StringName class_test = p_class;581while (class_test && !ClassDB::has_property(class_test, p_property, true)) {582class_test = ClassDB::get_parent_class(class_test);583depth++;584}585586return depth | ScriptLanguage::LOCATION_PARENT_MASK;587}588589static int _get_property_location(Ref<Script> p_script, const StringName &p_property) {590int depth = 0;591Ref<Script> scr = p_script;592while (scr.is_valid()) {593if (scr->get_member_line(p_property) != -1) {594return depth | ScriptLanguage::LOCATION_PARENT_MASK;595}596depth++;597scr = scr->get_base_script();598}599return depth + _get_property_location(p_script->get_instance_base_type(), p_property);600}601602static int _get_constant_location(const StringName &p_class, const StringName &p_constant) {603if (!ClassDB::has_integer_constant(p_class, p_constant)) {604return ScriptLanguage::LOCATION_OTHER;605}606607int depth = 0;608StringName class_test = p_class;609while (class_test && !ClassDB::has_integer_constant(class_test, p_constant, true)) {610class_test = ClassDB::get_parent_class(class_test);611depth++;612}613614return depth | ScriptLanguage::LOCATION_PARENT_MASK;615}616617static int _get_constant_location(Ref<Script> p_script, const StringName &p_constant) {618int depth = 0;619Ref<Script> scr = p_script;620while (scr.is_valid()) {621if (scr->get_member_line(p_constant) != -1) {622return depth | ScriptLanguage::LOCATION_PARENT_MASK;623}624depth++;625scr = scr->get_base_script();626}627return depth + _get_constant_location(p_script->get_instance_base_type(), p_constant);628}629630static int _get_signal_location(const StringName &p_class, const StringName &p_signal) {631if (!ClassDB::has_signal(p_class, p_signal)) {632return ScriptLanguage::LOCATION_OTHER;633}634635int depth = 0;636StringName class_test = p_class;637while (class_test && !ClassDB::has_signal(class_test, p_signal, true)) {638class_test = ClassDB::get_parent_class(class_test);639depth++;640}641642return depth | ScriptLanguage::LOCATION_PARENT_MASK;643}644645static int _get_signal_location(Ref<Script> p_script, const StringName &p_signal) {646int depth = 0;647Ref<Script> scr = p_script;648while (scr.is_valid()) {649if (scr->get_member_line(p_signal) != -1) {650return depth | ScriptLanguage::LOCATION_PARENT_MASK;651}652depth++;653scr = scr->get_base_script();654}655return depth + _get_signal_location(p_script->get_instance_base_type(), p_signal);656}657658static int _get_method_location(const StringName &p_class, const StringName &p_method) {659if (!ClassDB::has_method(p_class, p_method)) {660return ScriptLanguage::LOCATION_OTHER;661}662663int depth = 0;664StringName class_test = p_class;665while (class_test && !ClassDB::has_method(class_test, p_method, true)) {666class_test = ClassDB::get_parent_class(class_test);667depth++;668}669670return depth | ScriptLanguage::LOCATION_PARENT_MASK;671}672673static int _get_enum_constant_location(const StringName &p_class, const StringName &p_enum_constant) {674if (!ClassDB::get_integer_constant_enum(p_class, p_enum_constant)) {675return ScriptLanguage::LOCATION_OTHER;676}677678int depth = 0;679StringName class_test = p_class;680while (class_test && !ClassDB::get_integer_constant_enum(class_test, p_enum_constant, true)) {681class_test = ClassDB::get_parent_class(class_test);682depth++;683}684685return depth | ScriptLanguage::LOCATION_PARENT_MASK;686}687688static int _get_enum_location(const StringName &p_class, const StringName &p_enum) {689if (!ClassDB::has_enum(p_class, p_enum)) {690return ScriptLanguage::LOCATION_OTHER;691}692693int depth = 0;694StringName class_test = p_class;695while (class_test && !ClassDB::has_enum(class_test, p_enum, true)) {696class_test = ClassDB::get_parent_class(class_test);697depth++;698}699700return depth | ScriptLanguage::LOCATION_PARENT_MASK;701}702703// END LOCATION METHODS704705static String _trim_parent_class(const String &p_class, const String &p_base_class) {706if (p_base_class.is_empty()) {707return p_class;708}709Vector<String> names = p_class.split(".", false, 1);710if (names.size() == 2) {711const String &first = names[0];712if (ClassDB::class_exists(p_base_class) && ClassDB::class_exists(first) && ClassDB::is_parent_class(p_base_class, first)) {713const String &rest = names[1];714return rest;715}716}717return p_class;718}719720static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg, const String &p_base_class = "") {721String class_name = p_info.class_name;722bool is_enum = p_info.type == Variant::INT && p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM;723// PROPERTY_USAGE_CLASS_IS_BITFIELD: BitField[T] isn't supported (yet?), use plain int.724725if ((p_info.type == Variant::OBJECT || is_enum) && !class_name.is_empty()) {726if (is_enum && CoreConstants::is_global_enum(p_info.class_name)) {727return class_name;728}729return _trim_parent_class(class_name, p_base_class);730} else if (p_info.type == Variant::ARRAY && p_info.hint == PROPERTY_HINT_ARRAY_TYPE && !p_info.hint_string.is_empty()) {731return "Array[" + _trim_parent_class(p_info.hint_string, p_base_class) + "]";732} else if (p_info.type == Variant::DICTIONARY && p_info.hint == PROPERTY_HINT_DICTIONARY_TYPE && !p_info.hint_string.is_empty()) {733const String key = p_info.hint_string.get_slicec(';', 0);734const String value = p_info.hint_string.get_slicec(';', 1);735return "Dictionary[" + _trim_parent_class(key, p_base_class) + ", " + _trim_parent_class(value, p_base_class) + "]";736} else if (p_info.type == Variant::NIL) {737if (p_is_arg || (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {738return "Variant";739} else {740return "void";741}742}743744return Variant::get_type_name(p_info.type);745}746747static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool p_is_annotation = false) {748String arghint;749if (!p_is_annotation) {750arghint += _get_visual_datatype(p_info.return_val, false) + " ";751}752arghint += p_info.name + "(";753754int def_args = p_info.arguments.size() - p_info.default_arguments.size();755int i = 0;756for (const PropertyInfo &E : p_info.arguments) {757if (i > 0) {758arghint += ", ";759}760761if (i == p_arg_idx) {762arghint += String::chr(0xFFFF);763}764arghint += E.name + ": " + _get_visual_datatype(E, true);765766if (i - def_args >= 0) {767arghint += String(" = ") + p_info.default_arguments[i - def_args].get_construct_string();768}769770if (i == p_arg_idx) {771arghint += String::chr(0xFFFF);772}773774i++;775}776777if (p_info.flags & METHOD_FLAG_VARARG) {778if (p_info.arguments.size() > 0) {779arghint += ", ";780}781if (p_arg_idx >= p_info.arguments.size()) {782arghint += String::chr(0xFFFF);783}784arghint += "...args: Array"; // `MethodInfo` does not support the rest parameter name.785if (p_arg_idx >= p_info.arguments.size()) {786arghint += String::chr(0xFFFF);787}788}789790arghint += ")";791792return arghint;793}794795static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_function, int p_arg_idx, bool p_just_args = false) {796String arghint;797798if (p_just_args) {799arghint = "(";800} else {801if (p_function->get_datatype().builtin_type == Variant::NIL) {802arghint = "void " + p_function->identifier->name + "(";803} else {804arghint = p_function->get_datatype().to_string() + " " + p_function->identifier->name + "(";805}806}807808for (int i = 0; i < p_function->parameters.size(); i++) {809if (i > 0) {810arghint += ", ";811}812813if (i == p_arg_idx) {814arghint += String::chr(0xFFFF);815}816const GDScriptParser::ParameterNode *par = p_function->parameters[i];817if (!par->get_datatype().is_hard_type()) {818arghint += par->identifier->name.operator String() + ": Variant";819} else {820arghint += par->identifier->name.operator String() + ": " + par->get_datatype().to_string();821}822823if (par->initializer) {824String def_val = "<unknown>";825switch (par->initializer->type) {826case GDScriptParser::Node::LITERAL: {827const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(par->initializer);828def_val = literal->value.get_construct_string();829} break;830case GDScriptParser::Node::IDENTIFIER: {831const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(par->initializer);832def_val = id->name.operator String();833} break;834case GDScriptParser::Node::CALL: {835const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(par->initializer);836if (call->is_constant && call->reduced) {837def_val = call->reduced_value.get_construct_string();838} else if (call->get_callee_type() == GDScriptParser::Node::IDENTIFIER) {839def_val = call->function_name.operator String() + (call->arguments.is_empty() ? "()" : "(...)");840}841} break;842case GDScriptParser::Node::ARRAY: {843const GDScriptParser::ArrayNode *arr = static_cast<const GDScriptParser::ArrayNode *>(par->initializer);844if (arr->is_constant && arr->reduced) {845def_val = arr->reduced_value.get_construct_string();846} else {847def_val = arr->elements.is_empty() ? "[]" : "[...]";848}849} break;850case GDScriptParser::Node::DICTIONARY: {851const GDScriptParser::DictionaryNode *dict = static_cast<const GDScriptParser::DictionaryNode *>(par->initializer);852if (dict->is_constant && dict->reduced) {853def_val = dict->reduced_value.get_construct_string();854} else {855def_val = dict->elements.is_empty() ? "{}" : "{...}";856}857} break;858case GDScriptParser::Node::SUBSCRIPT: {859const GDScriptParser::SubscriptNode *sub = static_cast<const GDScriptParser::SubscriptNode *>(par->initializer);860if (sub->is_attribute && sub->datatype.kind == GDScriptParser::DataType::ENUM && !sub->datatype.is_meta_type) {861def_val = sub->get_datatype().to_string() + "." + sub->attribute->name;862} else if (sub->is_constant && sub->reduced) {863def_val = sub->reduced_value.get_construct_string();864}865} break;866default:867break;868}869arghint += " = " + def_val;870}871if (i == p_arg_idx) {872arghint += String::chr(0xFFFF);873}874}875876if (p_function->is_vararg()) {877if (!p_function->parameters.is_empty()) {878arghint += ", ";879}880if (p_arg_idx >= p_function->parameters.size()) {881arghint += String::chr(0xFFFF);882}883const GDScriptParser::ParameterNode *rest_param = p_function->rest_parameter;884arghint += "..." + rest_param->identifier->name + ": " + rest_param->get_datatype().to_string();885if (p_arg_idx >= p_function->parameters.size()) {886arghint += String::chr(0xFFFF);887}888}889890arghint += ")";891892return arghint;893}894895static void _get_directory_contents(EditorFileSystemDirectory *p_dir, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_list, const StringName &p_required_type = StringName()) {896const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";897const bool requires_type = !p_required_type.is_empty();898899for (int i = 0; i < p_dir->get_file_count(); i++) {900if (requires_type && !ClassDB::is_parent_class(p_dir->get_file_type(i), p_required_type)) {901continue;902}903ScriptLanguage::CodeCompletionOption option(p_dir->get_file_path(i).quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH);904r_list.insert(option.display, option);905}906907for (int i = 0; i < p_dir->get_subdir_count(); i++) {908_get_directory_contents(p_dir->get_subdir(i), r_list, p_required_type);909}910}911912static 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) {913ERR_FAIL_NULL(p_annotation);914915if (p_annotation->info != nullptr) {916r_arghint = _make_arguments_hint(p_annotation->info->info, p_argument, true);917}918if (p_annotation->name == SNAME("@export_range")) {919if (p_argument == 3 || p_argument == 4 || p_argument == 5) {920// Slider hint.921ScriptLanguage::CodeCompletionOption slider1("or_greater", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);922slider1.insert_text = slider1.display.quote(p_quote_style);923r_result.insert(slider1.display, slider1);924ScriptLanguage::CodeCompletionOption slider2("or_less", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);925slider2.insert_text = slider2.display.quote(p_quote_style);926r_result.insert(slider2.display, slider2);927ScriptLanguage::CodeCompletionOption slider3("hide_slider", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);928slider3.insert_text = slider3.display.quote(p_quote_style);929r_result.insert(slider3.display, slider3);930}931} else if (p_annotation->name == SNAME("@export_exp_easing")) {932if (p_argument == 0 || p_argument == 1) {933// Easing hint.934ScriptLanguage::CodeCompletionOption hint1("attenuation", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);935hint1.insert_text = hint1.display.quote(p_quote_style);936r_result.insert(hint1.display, hint1);937ScriptLanguage::CodeCompletionOption hint2("inout", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);938hint2.insert_text = hint2.display.quote(p_quote_style);939r_result.insert(hint2.display, hint2);940}941} else if (p_annotation->name == SNAME("@export_node_path")) {942ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);943node.insert_text = node.display.quote(p_quote_style);944r_result.insert(node.display, node);945946LocalVector<StringName> native_classes;947ClassDB::get_inheriters_from_class("Node", native_classes);948for (const StringName &E : native_classes) {949if (!ClassDB::is_class_exposed(E)) {950continue;951}952ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);953option.insert_text = option.display.quote(p_quote_style);954r_result.insert(option.display, option);955}956957List<StringName> global_script_classes;958ScriptServer::get_global_class_list(&global_script_classes);959for (const StringName &E : global_script_classes) {960if (!ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(E), "Node")) {961continue;962}963ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);964option.insert_text = option.display.quote(p_quote_style);965r_result.insert(option.display, option);966}967} else if (p_annotation->name == SNAME("@export_tool_button")) {968if (p_argument == 1) {969const Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();970if (theme.is_valid()) {971List<StringName> icon_list;972theme->get_icon_list(EditorStringName(EditorIcons), &icon_list);973for (const StringName &E : icon_list) {974ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);975option.insert_text = option.display.quote(p_quote_style);976r_result.insert(option.display, option);977}978}979}980} else if (p_annotation->name == SNAME("@export_custom")) {981switch (p_argument) {982case 0: {983static HashMap<StringName, int64_t> items;984if (unlikely(items.is_empty())) {985CoreConstants::get_enum_values(SNAME("PropertyHint"), &items);986}987for (const KeyValue<StringName, int64_t> &item : items) {988ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);989r_result.insert(option.display, option);990}991} break;992case 2: {993static HashMap<StringName, int64_t> items;994if (unlikely(items.is_empty())) {995CoreConstants::get_enum_values(SNAME("PropertyUsageFlags"), &items);996}997for (const KeyValue<StringName, int64_t> &item : items) {998ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);999r_result.insert(option.display, option);1000}1001} break;1002}1003} else if (p_annotation->name == SNAME("@warning_ignore") || p_annotation->name == SNAME("@warning_ignore_start") || p_annotation->name == SNAME("@warning_ignore_restore")) {1004for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {1005#ifndef DISABLE_DEPRECATED1006if (warning_code >= GDScriptWarning::FIRST_DEPRECATED_WARNING) {1007break; // Don't suggest deprecated warnings as they are never produced.1008}1009#endif1010ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1011warning.insert_text = warning.display.quote(p_quote_style);1012r_result.insert(warning.display, warning);1013}1014} else if (p_annotation->name == SNAME("@rpc")) {1015if (p_argument == 0 || p_argument == 1 || p_argument == 2) {1016static const char *options[7] = { "call_local", "call_remote", "any_peer", "authority", "reliable", "unreliable", "unreliable_ordered" };1017for (int i = 0; i < 7; i++) {1018ScriptLanguage::CodeCompletionOption option(options[i], ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1019option.insert_text = option.display.quote(p_quote_style);1020r_result.insert(option.display, option);1021}1022}1023}1024}10251026static void _find_built_in_variants(HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool exclude_nil = false) {1027for (int i = 0; i < Variant::VARIANT_MAX; i++) {1028if (!exclude_nil && Variant::Type(i) == Variant::Type::NIL) {1029ScriptLanguage::CodeCompletionOption option("null", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1030r_result.insert(option.display, option);1031} else {1032ScriptLanguage::CodeCompletionOption option(Variant::get_type_name(Variant::Type(i)), ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1033r_result.insert(option.display, option);1034}1035}1036}10371038static void _find_global_enums(HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {1039List<StringName> global_enums;1040CoreConstants::get_global_enums(&global_enums);1041for (const StringName &enum_name : global_enums) {1042ScriptLanguage::CodeCompletionOption option(enum_name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_OTHER);1043r_result.insert(option.display, option);1044}1045}10461047static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {1048// Built-in Variant Types1049_find_built_in_variants(r_result, true);10501051List<StringName> native_types;1052ClassDB::get_class_list(&native_types);1053for (const StringName &E : native_types) {1054if (ClassDB::is_class_exposed(E) && !Engine::get_singleton()->has_singleton(E)) {1055ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1056r_result.insert(option.display, option);1057}1058}10591060// TODO: Unify with _find_identifiers_in_class.1061if (p_context.current_class) {1062if (!p_inherit_only && p_context.current_class->base_type.is_set()) {1063// Native enums from base class1064List<StringName> enums;1065ClassDB::get_enum_list(p_context.current_class->base_type.native_type, &enums);1066for (const StringName &E : enums) {1067ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM);1068r_result.insert(option.display, option);1069}1070}1071// Check current class for potential types.1072// TODO: Also check classes the current class inherits from.1073const GDScriptParser::ClassNode *current = p_context.current_class;1074int location_offset = 0;1075while (current) {1076for (int i = 0; i < current->members.size(); i++) {1077const GDScriptParser::ClassNode::Member &member = current->members[i];1078switch (member.type) {1079case GDScriptParser::ClassNode::Member::CLASS: {1080ScriptLanguage::CodeCompletionOption option(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL + location_offset);1081r_result.insert(option.display, option);1082} break;1083case GDScriptParser::ClassNode::Member::ENUM: {1084if (!p_inherit_only) {1085ScriptLanguage::CodeCompletionOption option(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_LOCAL + location_offset);1086r_result.insert(option.display, option);1087}1088} break;1089case GDScriptParser::ClassNode::Member::CONSTANT: {1090if (member.constant->get_datatype().is_meta_type) {1091ScriptLanguage::CodeCompletionOption option(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL + location_offset);1092r_result.insert(option.display, option);1093}1094} break;1095default:1096break;1097}1098}1099location_offset += 1;1100current = current->outer;1101}1102}11031104// Global scripts1105List<StringName> global_classes;1106ScriptServer::get_global_class_list(&global_classes);1107for (const StringName &E : global_classes) {1108ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);1109r_result.insert(option.display, option);1110}11111112// Global enums1113if (!p_inherit_only) {1114_find_global_enums(r_result);1115}11161117// Autoload singletons1118HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();11191120for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {1121const ProjectSettings::AutoloadInfo &info = E.value;1122if (!info.is_singleton || info.path.get_extension().to_lower() != "gd") {1123continue;1124}1125ScriptLanguage::CodeCompletionOption option(info.name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);1126r_result.insert(option.display, option);1127}1128}11291130static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth = 0) {1131for (int i = 0; i < p_suite->locals.size(); i++) {1132ScriptLanguage::CodeCompletionOption option;1133int location = p_recursion_depth == 0 ? ScriptLanguage::LOCATION_LOCAL : (p_recursion_depth | ScriptLanguage::LOCATION_PARENT_MASK);1134if (p_suite->locals[i].type == GDScriptParser::SuiteNode::Local::CONSTANT) {1135option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1136option.default_value = p_suite->locals[i].constant->initializer->reduced_value;1137} else {1138option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE, location);1139}1140r_result.insert(option.display, option);1141}1142if (p_suite->parent_block) {1143_find_identifiers_in_suite(p_suite->parent_block, r_result, p_recursion_depth + 1);1144}1145}11461147static 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);11481149static 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) {1150ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);11511152if (!p_parent_only) {1153bool outer = false;1154const GDScriptParser::ClassNode *clss = p_class;1155int classes_processed = 0;1156while (clss) {1157for (int i = 0; i < clss->members.size(); i++) {1158const int location = p_recursion_depth == 0 ? classes_processed : (p_recursion_depth | ScriptLanguage::LOCATION_PARENT_MASK);1159const GDScriptParser::ClassNode::Member &member = clss->members[i];1160ScriptLanguage::CodeCompletionOption option;1161switch (member.type) {1162case GDScriptParser::ClassNode::Member::VARIABLE:1163if (p_types_only || p_only_functions || outer || (p_static && !member.variable->is_static)) {1164continue;1165}1166option = ScriptLanguage::CodeCompletionOption(member.variable->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1167break;1168case GDScriptParser::ClassNode::Member::CONSTANT:1169if ((p_types_only && !member.constant->datatype.is_meta_type) || p_only_functions) {1170continue;1171}1172if (r_result.has(member.constant->identifier->name)) {1173continue;1174}1175option = ScriptLanguage::CodeCompletionOption(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1176if (member.constant->initializer) {1177option.default_value = member.constant->initializer->reduced_value;1178}1179break;1180case GDScriptParser::ClassNode::Member::CLASS:1181if (p_only_functions) {1182continue;1183}1184option = ScriptLanguage::CodeCompletionOption(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, location);1185break;1186case GDScriptParser::ClassNode::Member::ENUM_VALUE:1187if (p_types_only || p_only_functions) {1188continue;1189}1190option = ScriptLanguage::CodeCompletionOption(member.enum_value.identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1191break;1192case GDScriptParser::ClassNode::Member::ENUM:1193if (p_only_functions) {1194continue;1195}1196option = ScriptLanguage::CodeCompletionOption(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);1197break;1198case GDScriptParser::ClassNode::Member::FUNCTION:1199if (p_types_only || outer || (p_static && !member.function->is_static) || member.function->identifier->name.operator String().begins_with("@")) {1200continue;1201}1202option = ScriptLanguage::CodeCompletionOption(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1203if (p_add_braces) {1204if (member.function->parameters.size() > 0 || (member.function->info.flags & METHOD_FLAG_VARARG)) {1205option.insert_text += "(";1206option.display += U"(\u2026)";1207} else {1208option.insert_text += "()";1209option.display += "()";1210}1211}1212break;1213case GDScriptParser::ClassNode::Member::SIGNAL:1214if (p_types_only || p_only_functions || outer || p_static) {1215continue;1216}1217option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);1218break;1219case GDScriptParser::ClassNode::Member::GROUP:1220break; // No-op, but silences warnings.1221case GDScriptParser::ClassNode::Member::UNDEFINED:1222break;1223}1224r_result.insert(option.display, option);1225}1226if (p_types_only) {1227break; // Otherwise, it will fill the results with types from the outer class (which is undesired for that case).1228}12291230outer = true;1231clss = clss->outer;1232classes_processed++;1233}1234}12351236// Parents.1237GDScriptCompletionIdentifier base_type;1238base_type.type = p_class->base_type;1239base_type.type.is_meta_type = p_static;12401241_find_identifiers_in_base(base_type, p_only_functions, p_types_only, p_add_braces, r_result, p_recursion_depth + 1);1242}12431244static 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) {1245ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);12461247GDScriptParser::DataType base_type = p_base.type;12481249if (!p_types_only && base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) {1250ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL);1251if (p_add_braces) {1252option.insert_text += "(";1253option.display += U"(\u2026)";1254}1255r_result.insert(option.display, option);1256}12571258while (!base_type.has_no_type()) {1259switch (base_type.kind) {1260case GDScriptParser::DataType::CLASS: {1261_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);1262// This already finds all parent identifiers, so we are done.1263base_type = GDScriptParser::DataType();1264} break;1265case GDScriptParser::DataType::SCRIPT: {1266Ref<Script> scr = base_type.script_type;1267if (scr.is_valid()) {1268if (p_types_only) {1269// TODO: Need to implement Script::get_script_enum_list and retrieve the enum list from a script.1270} else if (!p_only_functions) {1271if (!base_type.is_meta_type) {1272List<PropertyInfo> members;1273scr->get_script_property_list(&members);1274for (const PropertyInfo &E : members) {1275if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {1276continue;1277}1278if (E.name.contains_char('/')) {1279continue;1280}1281int location = p_recursion_depth + _get_property_location(scr, E.name);1282ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1283r_result.insert(option.display, option);1284}12851286List<MethodInfo> signals;1287scr->get_script_signal_list(&signals);1288for (const MethodInfo &E : signals) {1289int location = p_recursion_depth + _get_signal_location(scr, E.name);1290ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);1291r_result.insert(option.display, option);1292}1293}1294HashMap<StringName, Variant> constants;1295scr->get_constants(&constants);1296for (const KeyValue<StringName, Variant> &E : constants) {1297int location = p_recursion_depth + _get_constant_location(scr, E.key);1298ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1299r_result.insert(option.display, option);1300}1301}13021303if (!p_types_only) {1304List<MethodInfo> methods;1305scr->get_script_method_list(&methods);1306for (const MethodInfo &E : methods) {1307if (E.name.begins_with("@")) {1308continue;1309}1310int location = p_recursion_depth + _get_method_location(scr->get_class_name(), E.name);1311ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1312if (p_add_braces) {1313if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {1314option.insert_text += "(";1315option.display += U"(\u2026)";1316} else {1317option.insert_text += "()";1318option.display += "()";1319}1320}1321r_result.insert(option.display, option);1322}1323}13241325Ref<Script> base_script = scr->get_base_script();1326if (base_script.is_valid()) {1327base_type.script_type = base_script;1328} else {1329base_type.kind = GDScriptParser::DataType::NATIVE;1330base_type.builtin_type = Variant::OBJECT;1331base_type.native_type = scr->get_instance_base_type();1332}1333} else {1334return;1335}1336} break;1337case GDScriptParser::DataType::NATIVE: {1338StringName type = base_type.native_type;1339if (!ClassDB::class_exists(type)) {1340return;1341}13421343List<StringName> enums;1344ClassDB::get_enum_list(type, &enums);1345for (const StringName &E : enums) {1346int location = p_recursion_depth + _get_enum_location(type, E);1347ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);1348r_result.insert(option.display, option);1349}13501351if (p_types_only) {1352return;1353}13541355if (!p_only_functions) {1356List<String> constants;1357ClassDB::get_integer_constant_list(type, &constants);1358for (const String &E : constants) {1359int location = p_recursion_depth + _get_constant_location(type, StringName(E));1360ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1361r_result.insert(option.display, option);1362}13631364if (!base_type.is_meta_type || Engine::get_singleton()->has_singleton(type)) {1365List<PropertyInfo> pinfo;1366ClassDB::get_property_list(type, &pinfo);1367for (const PropertyInfo &E : pinfo) {1368if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {1369continue;1370}1371if (E.name.contains_char('/')) {1372continue;1373}1374int location = p_recursion_depth + _get_property_location(type, E.name);1375ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1376r_result.insert(option.display, option);1377}13781379List<MethodInfo> signals;1380ClassDB::get_signal_list(type, &signals);1381for (const MethodInfo &E : signals) {1382int location = p_recursion_depth + _get_signal_location(type, StringName(E.name));1383ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);1384r_result.insert(option.display, option);1385}1386}1387}13881389bool only_static = base_type.is_meta_type && !Engine::get_singleton()->has_singleton(type);13901391List<MethodInfo> methods;1392ClassDB::get_method_list(type, &methods, false, true);1393for (const MethodInfo &E : methods) {1394if (only_static && (E.flags & METHOD_FLAG_STATIC) == 0) {1395continue;1396}1397if (E.name.begins_with("_")) {1398continue;1399}1400int location = p_recursion_depth + _get_method_location(type, E.name);1401ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1402if (p_add_braces) {1403if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {1404option.insert_text += "(";1405option.display += U"(\u2026)";1406} else {1407option.insert_text += "()";1408option.display += "()";1409}1410}1411r_result.insert(option.display, option);1412}1413return;1414} break;1415case GDScriptParser::DataType::ENUM: {1416if (p_types_only) {1417return;1418}14191420String type_str = base_type.native_type;14211422if (type_str.contains_char('.')) {1423StringName type = type_str.get_slicec('.', 0);1424StringName type_enum = base_type.enum_type;14251426List<StringName> enum_values;14271428ClassDB::get_enum_constants(type, type_enum, &enum_values);14291430for (const StringName &E : enum_values) {1431int location = p_recursion_depth + _get_enum_constant_location(type, E);1432ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1433r_result.insert(option.display, option);1434}1435} else if (CoreConstants::is_global_enum(base_type.enum_type)) {1436HashMap<StringName, int64_t> enum_values;1437CoreConstants::get_enum_values(base_type.enum_type, &enum_values);14381439for (const KeyValue<StringName, int64_t> &enum_value : enum_values) {1440int location = p_recursion_depth + ScriptLanguage::LOCATION_OTHER;1441ScriptLanguage::CodeCompletionOption option(enum_value.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1442r_result.insert(option.display, option);1443}1444}1445}1446[[fallthrough]];1447case GDScriptParser::DataType::BUILTIN: {1448if (p_types_only) {1449return;1450}14511452Callable::CallError err;1453Variant tmp;1454Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);1455if (err.error != Callable::CallError::CALL_OK) {1456return;1457}14581459int location = ScriptLanguage::LOCATION_OTHER;14601461if (!p_only_functions) {1462List<PropertyInfo> members;1463if (p_base.value.get_type() != Variant::NIL) {1464p_base.value.get_property_list(&members);1465} else {1466tmp.get_property_list(&members);1467}14681469for (const PropertyInfo &E : members) {1470if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {1471continue;1472}1473if (!String(E.name).contains_char('/')) {1474ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1475if (base_type.kind == GDScriptParser::DataType::ENUM) {1476// Sort enum members in their declaration order.1477location += 1;1478}1479if (GDScriptParser::theme_color_names.has(E.name)) {1480option.theme_color_name = GDScriptParser::theme_color_names[E.name];1481}1482r_result.insert(option.display, option);1483}1484}1485}14861487List<MethodInfo> methods;1488tmp.get_method_list(&methods);1489for (const MethodInfo &E : methods) {1490if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type && !(E.flags & METHOD_FLAG_CONST)) {1491// Enum types are static and cannot change, therefore we skip non-const dictionary methods.1492continue;1493}1494ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1495if (p_add_braces) {1496if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {1497option.insert_text += "(";1498option.display += U"(\u2026)";1499} else {1500option.insert_text += "()";1501option.display += "()";1502}1503}1504r_result.insert(option.display, option);1505}15061507return;1508} break;1509default: {1510return;1511} break;1512}1513}1514}15151516static 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) {1517if (!p_only_functions && p_context.current_suite) {1518// This includes function parameters, since they are also locals.1519_find_identifiers_in_suite(p_context.current_suite, r_result);1520}15211522if (p_context.current_class) {1523_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);1524}15251526List<StringName> functions;1527GDScriptUtilityFunctions::get_function_list(&functions);15281529for (const StringName &E : functions) {1530MethodInfo function = GDScriptUtilityFunctions::get_function_info(E);1531ScriptLanguage::CodeCompletionOption option(String(E), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);1532if (p_add_braces) {1533if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {1534option.insert_text += "(";1535option.display += U"(\u2026)";1536} else {1537option.insert_text += "()";1538option.display += "()";1539}1540}1541r_result.insert(option.display, option);1542}15431544if (p_only_functions) {1545return;1546}15471548_find_built_in_variants(r_result);15491550static const char *_keywords[] = {1551"true", "false", "PI", "TAU", "INF", "NAN", "null", "self", "super",1552"break", "breakpoint", "continue", "pass", "return",1553nullptr1554};15551556const char **kw = _keywords;1557while (*kw) {1558ScriptLanguage::CodeCompletionOption option(*kw, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1559r_result.insert(option.display, option);1560kw++;1561}15621563static const char *_keywords_with_space[] = {1564"and", "not", "or", "in", "as", "class", "class_name", "extends", "is", "func", "signal", "await",1565"const", "enum", "static", "var", "if", "elif", "else", "for", "match", "when", "while",1566nullptr1567};15681569const char **kws = _keywords_with_space;1570while (*kws) {1571ScriptLanguage::CodeCompletionOption option(*kws, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1572option.insert_text += " ";1573r_result.insert(option.display, option);1574kws++;1575}15761577static const char *_keywords_with_args[] = {1578"assert", "preload",1579nullptr1580};15811582const char **kwa = _keywords_with_args;1583while (*kwa) {1584ScriptLanguage::CodeCompletionOption option(*kwa, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);1585if (p_add_braces) {1586option.insert_text += "(";1587option.display += U"(\u2026)";1588}1589r_result.insert(option.display, option);1590kwa++;1591}15921593List<StringName> utility_func_names;1594Variant::get_utility_function_list(&utility_func_names);15951596for (const StringName &util_func_name : utility_func_names) {1597ScriptLanguage::CodeCompletionOption option(util_func_name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);1598if (p_add_braces) {1599option.insert_text += "(";1600option.display += U"(\u2026)"; // As all utility functions contain an argument or more, this is hardcoded here.1601}1602r_result.insert(option.display, option);1603}16041605for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {1606if (!E.value.is_singleton) {1607continue;1608}1609ScriptLanguage::CodeCompletionOption option(E.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);1610r_result.insert(option.display, option);1611}16121613// Native classes and global constants.1614for (const KeyValue<StringName, int> &E : GDScriptLanguage::get_singleton()->get_global_map()) {1615ScriptLanguage::CodeCompletionOption option;1616if (ClassDB::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) {1617option = ScriptLanguage::CodeCompletionOption(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1618} else {1619option = ScriptLanguage::CodeCompletionOption(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);1620}1621r_result.insert(option.display, option);1622}16231624// Global enums1625_find_global_enums(r_result);16261627// Global classes1628List<StringName> global_classes;1629ScriptServer::get_global_class_list(&global_classes);1630for (const StringName &E : global_classes) {1631ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);1632r_result.insert(option.display, option);1633}1634}16351636static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value, GDScriptParser::CompletionContext &p_context) {1637GDScriptCompletionIdentifier ci;1638ci.value = p_value;1639ci.type.is_constant = true;1640ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1641ci.type.kind = GDScriptParser::DataType::BUILTIN;1642ci.type.builtin_type = p_value.get_type();16431644if (ci.type.builtin_type == Variant::OBJECT) {1645Object *obj = p_value.operator Object *();1646if (!obj) {1647return ci;1648}1649ci.type.native_type = obj->get_class_name();1650Ref<Script> scr = p_value;1651if (scr.is_valid()) {1652ci.type.is_meta_type = true;1653} else {1654ci.type.is_meta_type = false;1655scr = obj->get_script();1656}1657if (scr.is_valid()) {1658ci.type.script_path = scr->get_path();1659ci.type.script_type = scr;1660ci.type.native_type = scr->get_instance_base_type();1661ci.type.kind = GDScriptParser::DataType::SCRIPT;16621663if (scr->get_path().ends_with(".gd")) {1664Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(scr->get_path());1665if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) {1666ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1667ci.type.class_type = parser->get_parser()->get_tree();1668ci.type.kind = GDScriptParser::DataType::CLASS;1669return ci;1670}1671}1672} else {1673ci.type.kind = GDScriptParser::DataType::NATIVE;1674}1675}16761677return ci;1678}16791680static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_property) {1681GDScriptCompletionIdentifier ci;16821683if (p_property.type == Variant::NIL) {1684// Variant1685ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1686ci.type.kind = GDScriptParser::DataType::VARIANT;1687return ci;1688}16891690if (p_property.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {1691ci.enumeration = p_property.class_name;1692}16931694ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1695ci.type.builtin_type = p_property.type;1696if (p_property.type == Variant::OBJECT) {1697if (ScriptServer::is_global_class(p_property.class_name)) {1698ci.type.kind = GDScriptParser::DataType::SCRIPT;1699ci.type.script_path = ScriptServer::get_global_class_path(p_property.class_name);1700ci.type.native_type = ScriptServer::get_global_class_native_base(p_property.class_name);17011702Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_property.class_name));1703if (scr.is_valid()) {1704ci.type.script_type = scr;1705}1706} else {1707ci.type.kind = GDScriptParser::DataType::NATIVE;1708ci.type.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;1709}1710} else {1711ci.type.kind = GDScriptParser::DataType::BUILTIN;1712}1713return ci;1714}17151716static GDScriptCompletionIdentifier _callable_type_from_method_info(const MethodInfo &p_method) {1717GDScriptCompletionIdentifier ci;1718ci.type.kind = GDScriptParser::DataType::BUILTIN;1719ci.type.builtin_type = Variant::CALLABLE;1720ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1721ci.type.is_constant = true;1722ci.type.method_info = p_method;1723return ci;1724}17251726#define MAX_COMPLETION_RECURSION 1001727struct RecursionCheck {1728int *counter;1729_FORCE_INLINE_ bool check() {1730return (*counter) > MAX_COMPLETION_RECURSION;1731}1732RecursionCheck(int *p_counter) :1733counter(p_counter) {1734(*counter)++;1735}1736~RecursionCheck() {1737(*counter)--;1738}1739};17401741static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type);1742static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);1743static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);17441745static bool _is_expression_named_identifier(const GDScriptParser::ExpressionNode *p_expression, const StringName &p_name) {1746if (p_expression) {1747switch (p_expression->type) {1748case GDScriptParser::Node::IDENTIFIER: {1749const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);1750if (id->name == p_name) {1751return true;1752}1753} break;1754case GDScriptParser::Node::CAST: {1755const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);1756return _is_expression_named_identifier(cn->operand, p_name);1757} break;1758default:1759break;1760}1761}17621763return false;1764}17651766// Creates a map of exemplary results for some functions that return a structured dictionary.1767// Setting this example as value allows autocompletion to suggest the specific keys in some cases.1768static HashMap<String, Dictionary> make_structure_samples() {1769HashMap<String, Dictionary> res;1770const Array arr;17711772{1773Dictionary d;1774d.set("major", 0);1775d.set("minor", 0);1776d.set("patch", 0);1777d.set("hex", 0);1778d.set("status", String());1779d.set("build", String());1780d.set("hash", String());1781d.set("timestamp", 0);1782d.set("string", String());1783res["Engine::get_version_info"] = d;1784}17851786{1787Dictionary d;1788d.set("lead_developers", arr);1789d.set("founders", arr);1790d.set("project_managers", arr);1791d.set("developers", arr);1792res["Engine::get_author_info"] = d;1793}17941795{1796Dictionary d;1797d.set("platinum_sponsors", arr);1798d.set("gold_sponsors", arr);1799d.set("silver_sponsors", arr);1800d.set("bronze_sponsors", arr);1801d.set("mini_sponsors", arr);1802d.set("gold_donors", arr);1803d.set("silver_donors", arr);1804d.set("bronze_donors", arr);1805res["Engine::get_donor_info"] = d;1806}18071808{1809Dictionary d;1810d.set("physical", -1);1811d.set("free", -1);1812d.set("available", -1);1813d.set("stack", -1);1814res["OS::get_memory_info"] = d;1815}18161817{1818Dictionary d;1819d.set("year", 0);1820d.set("month", 0);1821d.set("day", 0);1822d.set("weekday", 0);1823d.set("hour", 0);1824d.set("minute", 0);1825d.set("second", 0);1826d.set("dst", 0);1827res["Time::get_datetime_dict_from_system"] = d;1828}18291830{1831Dictionary d;1832d.set("year", 0);1833d.set("month", 0);1834d.set("day", 0);1835d.set("weekday", 0);1836d.set("hour", 0);1837d.set("minute", 0);1838d.set("second", 0);1839res["Time::get_datetime_dict_from_unix_time"] = d;1840}18411842{1843Dictionary d;1844d.set("year", 0);1845d.set("month", 0);1846d.set("day", 0);1847d.set("weekday", 0);1848res["Time::get_date_dict_from_system"] = d;1849res["Time::get_date_dict_from_unix_time"] = d;1850}18511852{1853Dictionary d;1854d.set("hour", 0);1855d.set("minute", 0);1856d.set("second", 0);1857res["Time::get_time_dict_from_system"] = d;1858res["Time::get_time_dict_from_unix_time"] = d;1859}18601861{1862Dictionary d;1863d.set("bias", 0);1864d.set("name", String());1865res["Time::get_time_zone_from_system"] = d;1866}18671868return res;1869}18701871static const HashMap<String, Dictionary> structure_examples = make_structure_samples();18721873static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) {1874bool found = false;18751876if (p_expression == nullptr) {1877return false;1878}18791880static int recursion_depth = 0;1881RecursionCheck recursion(&recursion_depth);1882if (unlikely(recursion.check())) {1883ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");1884}18851886if (p_expression->is_constant) {1887// Already has a value, so just use that.1888r_type = _type_from_variant(p_expression->reduced_value, p_context);1889switch (p_expression->get_datatype().kind) {1890case GDScriptParser::DataType::ENUM:1891case GDScriptParser::DataType::CLASS:1892r_type.type = p_expression->get_datatype();1893break;1894default:1895break;1896}1897found = true;1898} else {1899switch (p_expression->type) {1900case GDScriptParser::Node::LITERAL: {1901const GDScriptParser::LiteralNode *literal = static_cast<const GDScriptParser::LiteralNode *>(p_expression);1902r_type = _type_from_variant(literal->value, p_context);1903found = true;1904} break;1905case GDScriptParser::Node::SELF: {1906if (p_context.current_class) {1907r_type.type = p_context.current_class->get_datatype();1908r_type.type.is_meta_type = false;1909found = true;1910}1911} break;1912case GDScriptParser::Node::IDENTIFIER: {1913const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);1914found = _guess_identifier_type(p_context, id, r_type);1915} break;1916case GDScriptParser::Node::DICTIONARY: {1917// Try to recreate the dictionary.1918const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);1919Dictionary d;1920bool full = true;1921for (int i = 0; i < dn->elements.size(); i++) {1922GDScriptCompletionIdentifier key;1923if (_guess_expression_type(p_context, dn->elements[i].key, key)) {1924if (!key.type.is_constant) {1925full = false;1926break;1927}1928GDScriptCompletionIdentifier value;1929if (_guess_expression_type(p_context, dn->elements[i].value, value)) {1930if (!value.type.is_constant) {1931full = false;1932break;1933}1934d[key.value] = value.value;1935} else {1936full = false;1937break;1938}1939} else {1940full = false;1941break;1942}1943}1944if (full) {1945r_type.value = d;1946r_type.type.is_constant = true;1947}1948r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1949r_type.type.kind = GDScriptParser::DataType::BUILTIN;1950r_type.type.builtin_type = Variant::DICTIONARY;1951found = true;1952} break;1953case GDScriptParser::Node::ARRAY: {1954// Try to recreate the array1955const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);1956Array a;1957bool full = true;1958a.resize(an->elements.size());1959for (int i = 0; i < an->elements.size(); i++) {1960GDScriptCompletionIdentifier value;1961if (_guess_expression_type(p_context, an->elements[i], value)) {1962if (value.type.is_constant) {1963a[i] = value.value;1964} else {1965full = false;1966break;1967}1968} else {1969full = false;1970break;1971}1972}1973if (full) {1974// If not fully constant, setting this value is detrimental to the inference.1975r_type.value = a;1976r_type.type.is_constant = true;1977}1978r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1979r_type.type.kind = GDScriptParser::DataType::BUILTIN;1980r_type.type.builtin_type = Variant::ARRAY;1981found = true;1982} break;1983case GDScriptParser::Node::CAST: {1984const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);1985GDScriptCompletionIdentifier value;1986if (_guess_expression_type(p_context, cn->operand, r_type)) {1987r_type.type = cn->get_datatype();1988found = true;1989}1990} break;1991case GDScriptParser::Node::CALL: {1992const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);1993GDScriptParser::CompletionContext c = p_context;1994c.current_line = call->start_line;19951996GDScriptParser::Node::Type callee_type = call->get_callee_type();19971998GDScriptCompletionIdentifier base;1999if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super) {2000// Simple call, so base is 'self'.2001if (p_context.current_class) {2002if (call->is_super) {2003base.type = p_context.current_class->base_type;2004base.value = p_context.base;2005} else {2006base.type.kind = GDScriptParser::DataType::CLASS;2007base.type.type_source = GDScriptParser::DataType::INFERRED;2008base.type.is_constant = true;2009base.type.class_type = p_context.current_class;2010base.value = p_context.base;2011}2012} else {2013break;2014}2015} else if (callee_type == GDScriptParser::Node::SUBSCRIPT && static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->is_attribute) {2016if (!_guess_expression_type(c, static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->base, base)) {2017found = false;2018break;2019}2020} else {2021break;2022}20232024// Apply additional behavior aware inference that the analyzer can't do.2025if (base.type.is_set()) {2026// Maintain type for duplicate methods.2027if (call->function_name == SNAME("duplicate")) {2028if (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")))) {2029r_type.type = base.type;2030found = true;2031break;2032}2033}20342035// Simulate generics for some typed array methods.2036if (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"))) {2037r_type.type = base.type.get_container_element_type(0);2038found = true;2039break;2040}20412042// Insert example values for functions which a structured dictionary response.2043if (!base.type.is_meta_type) {2044const Dictionary *example = structure_examples.getptr(base.type.native_type.operator String() + "::" + call->function_name);2045if (example != nullptr) {2046r_type = _type_from_variant(*example, p_context);2047found = true;2048break;2049}2050}2051}20522053if (!found) {2054found = _guess_method_return_type_from_base(c, base, call->function_name, r_type);2055}2056} break;2057case GDScriptParser::Node::SUBSCRIPT: {2058const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);2059if (subscript->is_attribute) {2060GDScriptParser::CompletionContext c = p_context;2061c.current_line = subscript->start_line;20622063GDScriptCompletionIdentifier base;2064if (!_guess_expression_type(c, subscript->base, base)) {2065found = false;2066break;2067}20682069if (base.value.get_type() == Variant::DICTIONARY && base.value.operator Dictionary().has(String(subscript->attribute->name))) {2070Variant value = base.value.operator Dictionary()[String(subscript->attribute->name)];2071r_type = _type_from_variant(value, p_context);2072found = true;2073break;2074}20752076const GDScriptParser::DictionaryNode *dn = nullptr;2077if (subscript->base->type == GDScriptParser::Node::DICTIONARY) {2078dn = static_cast<const GDScriptParser::DictionaryNode *>(subscript->base);2079} else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::DICTIONARY) {2080dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);2081}20822083if (dn) {2084for (int i = 0; i < dn->elements.size(); i++) {2085GDScriptCompletionIdentifier key;2086if (!_guess_expression_type(c, dn->elements[i].key, key)) {2087continue;2088}2089if (key.value == String(subscript->attribute->name)) {2090r_type.assigned_expression = dn->elements[i].value;2091found = _guess_expression_type(c, dn->elements[i].value, r_type);2092break;2093}2094}2095}20962097if (!found) {2098found = _guess_identifier_type_from_base(c, base, subscript->attribute->name, r_type);2099}2100} else {2101if (subscript->index == nullptr) {2102found = false;2103break;2104}21052106GDScriptParser::CompletionContext c = p_context;2107c.current_line = subscript->start_line;21082109GDScriptCompletionIdentifier base;2110if (!_guess_expression_type(c, subscript->base, base)) {2111found = false;2112break;2113}21142115GDScriptCompletionIdentifier index;2116if (!_guess_expression_type(c, subscript->index, index)) {2117found = false;2118break;2119}21202121if (base.type.is_constant && index.type.is_constant) {2122if (base.value.get_type() == Variant::DICTIONARY) {2123Dictionary base_dict = base.value.operator Dictionary();2124if (base_dict.get_key_validator().test_validate(index.value) && base_dict.has(index.value)) {2125r_type = _type_from_variant(base_dict[index.value], p_context);2126found = true;2127break;2128}2129} else {2130bool valid;2131Variant value = base.value.get(index.value, &valid);2132if (valid) {2133r_type = _type_from_variant(value, p_context);2134found = true;2135break;2136}2137}2138}21392140// Look if it is a dictionary node.2141const GDScriptParser::DictionaryNode *dn = nullptr;2142if (subscript->base->type == GDScriptParser::Node::DICTIONARY) {2143dn = static_cast<const GDScriptParser::DictionaryNode *>(subscript->base);2144} else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::DICTIONARY) {2145dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);2146}21472148if (dn) {2149for (int i = 0; i < dn->elements.size(); i++) {2150GDScriptCompletionIdentifier key;2151if (!_guess_expression_type(c, dn->elements[i].key, key)) {2152continue;2153}2154if (key.value == index.value) {2155r_type.assigned_expression = dn->elements[i].value;2156found = _guess_expression_type(p_context, dn->elements[i].value, r_type);2157break;2158}2159}2160}21612162// Look if it is an array node.2163if (!found && index.value.is_num()) {2164int idx = index.value;2165const GDScriptParser::ArrayNode *an = nullptr;2166if (subscript->base->type == GDScriptParser::Node::ARRAY) {2167an = static_cast<const GDScriptParser::ArrayNode *>(subscript->base);2168} else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::ARRAY) {2169an = static_cast<const GDScriptParser::ArrayNode *>(base.assigned_expression);2170}21712172if (an && idx >= 0 && an->elements.size() > idx) {2173r_type.assigned_expression = an->elements[idx];2174found = _guess_expression_type(c, an->elements[idx], r_type);2175break;2176}2177}21782179// Look for valid indexing in other types2180if (!found && (index.value.is_string() || index.value.get_type() == Variant::NODE_PATH)) {2181StringName id = index.value;2182found = _guess_identifier_type_from_base(c, base, id, r_type);2183} else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) {2184Callable::CallError err;2185Variant base_val;2186Variant::construct(base.type.builtin_type, base_val, nullptr, 0, err);2187bool valid = false;2188Variant res = base_val.get(index.value, &valid);2189if (valid) {2190r_type = _type_from_variant(res, p_context);2191r_type.value = Variant();2192r_type.type.is_constant = false;2193found = true;2194}2195}2196}2197} break;2198case GDScriptParser::Node::BINARY_OPERATOR: {2199const GDScriptParser::BinaryOpNode *op = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);22002201if (op->variant_op == Variant::OP_MAX) {2202break;2203}22042205GDScriptParser::CompletionContext context = p_context;2206context.current_line = op->start_line;22072208GDScriptCompletionIdentifier p1;2209GDScriptCompletionIdentifier p2;22102211if (!_guess_expression_type(context, op->left_operand, p1)) {2212found = false;2213break;2214}22152216if (!_guess_expression_type(context, op->right_operand, p2)) {2217found = false;2218break;2219}22202221Callable::CallError ce;2222bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;2223Variant d1;2224Variant::construct(p1.type.builtin_type, d1, nullptr, 0, ce);2225Variant d2;2226Variant::construct(p2.type.builtin_type, d2, nullptr, 0, ce);22272228Variant v1 = (v1_use_value) ? p1.value : d1;2229bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;2230Variant v2 = (v2_use_value) ? p2.value : d2;2231// avoid potential invalid ops2232if ((op->variant_op == Variant::OP_DIVIDE || op->variant_op == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {2233v2 = 1;2234v2_use_value = false;2235}2236if (op->variant_op == Variant::OP_DIVIDE && v2.get_type() == Variant::FLOAT) {2237v2 = 1.0;2238v2_use_value = false;2239}22402241Variant res;2242bool valid;2243Variant::evaluate(op->variant_op, v1, v2, res, valid);2244if (!valid) {2245found = false;2246break;2247}2248r_type = _type_from_variant(res, p_context);2249if (!v1_use_value || !v2_use_value) {2250r_type.value = Variant();2251r_type.type.is_constant = false;2252}22532254found = true;2255} break;2256default:2257break;2258}2259}22602261// It may have found a null, but that's never useful2262if (found && r_type.type.kind == GDScriptParser::DataType::BUILTIN && r_type.type.builtin_type == Variant::NIL) {2263found = false;2264}22652266// If the found type was not fully analyzed we analyze it now.2267if (found && r_type.type.kind == GDScriptParser::DataType::CLASS && !r_type.type.class_type->resolved_body) {2268Error err;2269Ref<GDScriptParserRef> r = GDScriptCache::get_parser(r_type.type.script_path, GDScriptParserRef::FULLY_SOLVED, err);2270}22712272// Check type hint last. For collections we want chance to get the actual value first2273// This way we can detect types from the content of dictionaries and arrays2274if (!found && p_expression->get_datatype().is_hard_type()) {2275r_type.type = p_expression->get_datatype();2276if (!r_type.assigned_expression) {2277r_type.assigned_expression = p_expression;2278}2279found = true;2280}22812282return found;2283}22842285static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type) {2286static int recursion_depth = 0;2287RecursionCheck recursion(&recursion_depth);2288if (unlikely(recursion.check())) {2289ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");2290}22912292// Look in blocks first.2293int last_assign_line = -1;2294const GDScriptParser::ExpressionNode *last_assigned_expression = nullptr;2295GDScriptCompletionIdentifier id_type;2296GDScriptParser::SuiteNode *suite = p_context.current_suite;2297bool is_function_parameter = false;22982299bool can_be_local = true;2300switch (p_identifier->source) {2301case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:2302case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:2303case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:2304case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:2305case GDScriptParser::IdentifierNode::MEMBER_CLASS:2306case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:2307case GDScriptParser::IdentifierNode::STATIC_VARIABLE:2308case GDScriptParser::IdentifierNode::NATIVE_CLASS:2309can_be_local = false;2310break;2311default:2312break;2313}23142315if (can_be_local && suite && suite->has_local(p_identifier->name)) {2316const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name);23172318id_type.type = local.get_datatype();23192320// Check initializer as the first assignment.2321switch (local.type) {2322case GDScriptParser::SuiteNode::Local::VARIABLE:2323if (local.variable->initializer) {2324last_assign_line = local.variable->initializer->end_line;2325last_assigned_expression = local.variable->initializer;2326}2327break;2328case GDScriptParser::SuiteNode::Local::CONSTANT:2329if (local.constant->initializer) {2330last_assign_line = local.constant->initializer->end_line;2331last_assigned_expression = local.constant->initializer;2332}2333break;2334case GDScriptParser::SuiteNode::Local::PARAMETER:2335if (local.parameter->initializer) {2336last_assign_line = local.parameter->initializer->end_line;2337last_assigned_expression = local.parameter->initializer;2338}2339is_function_parameter = true;2340break;2341default:2342break;2343}2344} else {2345if (p_context.current_class) {2346GDScriptCompletionIdentifier base_identifier;23472348GDScriptCompletionIdentifier base;2349base.value = p_context.base;2350base.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2351base.type.kind = GDScriptParser::DataType::CLASS;2352base.type.class_type = p_context.current_class;2353base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static;23542355if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, base_identifier)) {2356id_type = base_identifier;2357}2358}2359}23602361while (suite) {2362for (int i = 0; i < suite->statements.size(); i++) {2363if (suite->statements[i]->end_line >= p_context.current_line) {2364break;2365}23662367switch (suite->statements[i]->type) {2368case GDScriptParser::Node::ASSIGNMENT: {2369const GDScriptParser::AssignmentNode *assign = static_cast<const GDScriptParser::AssignmentNode *>(suite->statements[i]);2370if (assign->end_line > last_assign_line && assign->assignee && assign->assigned_value && assign->assignee->type == GDScriptParser::Node::IDENTIFIER) {2371const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->assignee);2372if (id->name == p_identifier->name && id->source == p_identifier->source) {2373last_assign_line = assign->assigned_value->end_line;2374last_assigned_expression = assign->assigned_value;2375}2376}2377} break;2378default:2379// TODO: Check sub blocks (control flow statements) as they might also reassign stuff.2380break;2381}2382}23832384if (suite->parent_if && suite->parent_if->condition && suite->parent_if->condition->type == GDScriptParser::Node::TYPE_TEST) {2385// Operator `is` used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common..2386// Super dirty hack, but very useful.2387// Credit: Zylann.2388// TODO: this could be hacked to detect AND-ed conditions too...2389const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(suite->parent_if->condition);2390if (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) {2391// Bingo.2392GDScriptParser::CompletionContext c = p_context;2393c.current_line = type_test->operand->start_line;2394c.current_suite = suite;2395if (type_test->test_datatype.is_hard_type()) {2396id_type.type = type_test->test_datatype;2397if (last_assign_line < c.current_line) {2398// Override last assignment.2399last_assign_line = c.current_line;2400last_assigned_expression = nullptr;2401}2402}2403}2404}24052406suite = suite->parent_block;2407}24082409if (last_assigned_expression && last_assign_line < p_context.current_line) {2410GDScriptParser::CompletionContext c = p_context;2411c.current_line = last_assign_line;2412GDScriptCompletionIdentifier assigned_type;2413if (_guess_expression_type(c, last_assigned_expression, assigned_type)) {2414if (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)))) {2415// The assigned type is incompatible. The annotated type takes priority.2416r_type = id_type;2417r_type.assigned_expression = last_assigned_expression;2418} else {2419r_type = assigned_type;2420}2421return true;2422}2423}24242425if (is_function_parameter && p_context.current_function && p_context.current_function->source_lambda == nullptr && p_context.current_class) {2426// Check if it's override of native function, then we can assume the type from the signature.2427GDScriptParser::DataType base_type = p_context.current_class->base_type;2428while (base_type.is_set()) {2429switch (base_type.kind) {2430case GDScriptParser::DataType::CLASS:2431if (base_type.class_type->has_function(p_context.current_function->identifier->name)) {2432GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;2433if (parent_function->parameters_indices.has(p_identifier->name)) {2434const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier->name]];2435if ((!id_type.type.is_set() || id_type.type.is_variant()) && parameter->get_datatype().is_hard_type()) {2436id_type.type = parameter->get_datatype();2437}2438if (parameter->initializer) {2439GDScriptParser::CompletionContext c = p_context;2440c.current_function = parent_function;2441c.current_class = base_type.class_type;2442c.base = nullptr;2443if (_guess_expression_type(c, parameter->initializer, r_type)) {2444return true;2445}2446}2447}2448}2449base_type = base_type.class_type->base_type;2450break;2451case GDScriptParser::DataType::NATIVE: {2452if (id_type.type.is_set() && !id_type.type.is_variant()) {2453base_type = GDScriptParser::DataType();2454break;2455}2456MethodInfo info;2457if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) {2458for (const PropertyInfo &E : info.arguments) {2459if (E.name == p_identifier->name) {2460r_type = _type_from_property(E);2461return true;2462}2463}2464}2465base_type = GDScriptParser::DataType();2466} break;2467default:2468break;2469}2470}2471}24722473if (id_type.type.is_set() && !id_type.type.is_variant()) {2474r_type = id_type;2475return true;2476}24772478// Check global scripts.2479if (ScriptServer::is_global_class(p_identifier->name)) {2480String script = ScriptServer::get_global_class_path(p_identifier->name);2481if (script.to_lower().ends_with(".gd")) {2482Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(script);2483if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) {2484r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2485r_type.type.script_path = script;2486r_type.type.class_type = parser->get_parser()->get_tree();2487r_type.type.is_meta_type = true;2488r_type.type.is_constant = false;2489r_type.type.kind = GDScriptParser::DataType::CLASS;2490r_type.value = Variant();2491return true;2492}2493} else {2494Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier->name));2495if (scr.is_valid()) {2496r_type = _type_from_variant(scr, p_context);2497r_type.type.is_meta_type = true;2498return true;2499}2500}2501return false;2502}25032504// Check global variables (including autoloads).2505if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier->name)) {2506r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name], p_context);2507return true;2508}25092510// Check ClassDB.2511if (ClassDB::class_exists(p_identifier->name) && ClassDB::is_class_exposed(p_identifier->name)) {2512r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2513r_type.type.kind = GDScriptParser::DataType::NATIVE;2514r_type.type.builtin_type = Variant::OBJECT;2515r_type.type.native_type = p_identifier->name;2516r_type.type.is_constant = true;2517if (Engine::get_singleton()->has_singleton(p_identifier->name)) {2518r_type.type.is_meta_type = false;2519r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier->name);2520} else {2521r_type.type.is_meta_type = true;2522r_type.value = Variant();2523}2524return true;2525}25262527return false;2528}25292530static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {2531static int recursion_depth = 0;2532RecursionCheck recursion(&recursion_depth);2533if (unlikely(recursion.check())) {2534ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");2535}25362537GDScriptParser::DataType base_type = p_base.type;2538bool is_static = base_type.is_meta_type;2539while (base_type.is_set()) {2540switch (base_type.kind) {2541case GDScriptParser::DataType::CLASS:2542if (base_type.class_type->has_member(p_identifier)) {2543const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(p_identifier);2544switch (member.type) {2545case GDScriptParser::ClassNode::Member::CONSTANT:2546r_type.type = member.constant->get_datatype();2547if (member.constant->initializer && member.constant->initializer->is_constant) {2548r_type.value = member.constant->initializer->reduced_value;2549}2550return true;2551case GDScriptParser::ClassNode::Member::VARIABLE:2552if (!is_static || member.variable->is_static) {2553if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {2554r_type.type = member.variable->get_datatype();2555return true;2556} else if (member.variable->initializer) {2557const GDScriptParser::ExpressionNode *init = member.variable->initializer;2558if (init->is_constant) {2559r_type.value = init->reduced_value;2560r_type = _type_from_variant(init->reduced_value, p_context);2561return true;2562} else if (init->start_line == p_context.current_line) {2563return false;2564// Detects if variable is assigned to itself2565} else if (_is_expression_named_identifier(init, member.variable->identifier->name)) {2566if (member.variable->initializer->get_datatype().is_set()) {2567r_type.type = member.variable->initializer->get_datatype();2568} else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {2569r_type.type = member.variable->get_datatype();2570}2571return true;2572} else if (_guess_expression_type(p_context, init, r_type)) {2573return true;2574} else if (init->get_datatype().is_set() && !init->get_datatype().is_variant()) {2575r_type.type = init->get_datatype();2576return true;2577}2578}2579}2580// TODO: Check assignments in constructor.2581return false;2582case GDScriptParser::ClassNode::Member::ENUM:2583r_type.type = member.m_enum->get_datatype();2584r_type.enumeration = member.m_enum->identifier->name;2585return true;2586case GDScriptParser::ClassNode::Member::ENUM_VALUE:2587r_type = _type_from_variant(member.enum_value.value, p_context);2588return true;2589case GDScriptParser::ClassNode::Member::SIGNAL:2590r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2591r_type.type.kind = GDScriptParser::DataType::BUILTIN;2592r_type.type.builtin_type = Variant::SIGNAL;2593r_type.type.method_info = member.signal->method_info;2594return true;2595case GDScriptParser::ClassNode::Member::FUNCTION:2596if (is_static && !member.function->is_static) {2597return false;2598}2599r_type = _callable_type_from_method_info(member.function->info);2600return true;2601case GDScriptParser::ClassNode::Member::CLASS:2602r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2603r_type.type.kind = GDScriptParser::DataType::CLASS;2604r_type.type.class_type = member.m_class;2605r_type.type.is_meta_type = true;2606return true;2607case GDScriptParser::ClassNode::Member::GROUP:2608return false; // No-op, but silences warnings.2609case GDScriptParser::ClassNode::Member::UNDEFINED:2610return false; // Unreachable.2611}2612return false;2613}2614base_type = base_type.class_type->base_type;2615break;2616case GDScriptParser::DataType::SCRIPT: {2617Ref<Script> scr = base_type.script_type;2618if (scr.is_valid()) {2619HashMap<StringName, Variant> constants;2620scr->get_constants(&constants);2621if (constants.has(p_identifier)) {2622r_type = _type_from_variant(constants[p_identifier], p_context);2623return true;2624}26252626List<PropertyInfo> members;2627if (is_static) {2628scr->get_property_list(&members);2629} else {2630scr->get_script_property_list(&members);2631}2632for (const PropertyInfo &prop : members) {2633if (prop.name == p_identifier) {2634r_type = _type_from_property(prop);2635return true;2636}2637}26382639if (scr->has_method(p_identifier)) {2640MethodInfo mi = scr->get_method_info(p_identifier);2641r_type = _callable_type_from_method_info(mi);2642return true;2643}26442645Ref<Script> parent = scr->get_base_script();2646if (parent.is_valid()) {2647base_type.script_type = parent;2648} else {2649base_type.kind = GDScriptParser::DataType::NATIVE;2650base_type.builtin_type = Variant::OBJECT;2651base_type.native_type = scr->get_instance_base_type();2652}2653} else {2654return false;2655}2656} break;2657case GDScriptParser::DataType::NATIVE: {2658StringName class_name = base_type.native_type;2659if (!ClassDB::class_exists(class_name)) {2660return false;2661}26622663// Skip constants since they're all integers. Type does not matter because int has no members.26642665PropertyInfo prop;2666if (ClassDB::get_property_info(class_name, p_identifier, &prop)) {2667StringName getter = ClassDB::get_property_getter(class_name, p_identifier);2668if (getter != StringName()) {2669MethodBind *g = ClassDB::get_method(class_name, getter);2670if (g) {2671r_type = _type_from_property(g->get_return_info());2672return true;2673}2674} else {2675r_type = _type_from_property(prop);2676return true;2677}2678}26792680MethodInfo method;2681if (ClassDB::get_method_info(class_name, p_identifier, &method)) {2682r_type = _callable_type_from_method_info(method);2683return true;2684}26852686if (ClassDB::has_enum(class_name, p_identifier)) {2687r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2688r_type.type.kind = GDScriptParser::DataType::ENUM;2689r_type.type.enum_type = p_identifier;2690r_type.type.is_constant = true;2691r_type.type.is_meta_type = true;2692r_type.type.native_type = String(class_name) + "." + p_identifier;2693return true;2694}26952696return false;2697} break;2698case GDScriptParser::DataType::BUILTIN: {2699if (Variant::has_builtin_method(base_type.builtin_type, p_identifier)) {2700r_type = _callable_type_from_method_info(Variant::get_builtin_method_info(base_type.builtin_type, p_identifier));2701return true;2702} else {2703Callable::CallError err;2704Variant tmp;2705Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);27062707if (err.error != Callable::CallError::CALL_OK) {2708return false;2709}2710bool valid = false;2711Variant res = tmp.get(p_identifier, &valid);2712if (valid) {2713r_type = _type_from_variant(res, p_context);2714r_type.value = Variant();2715r_type.type.is_constant = false;2716return true;2717}2718}2719return false;2720} break;2721default: {2722return false;2723} break;2724}2725}2726return false;2727}27282729static void _find_last_return_in_block(GDScriptParser::CompletionContext &p_context, int &r_last_return_line, const GDScriptParser::ExpressionNode **r_last_returned_value) {2730if (!p_context.current_suite) {2731return;2732}27332734for (int i = 0; i < p_context.current_suite->statements.size(); i++) {2735if (p_context.current_suite->statements[i]->start_line < r_last_return_line) {2736break;2737}27382739GDScriptParser::CompletionContext c = p_context;2740switch (p_context.current_suite->statements[i]->type) {2741case GDScriptParser::Node::FOR:2742c.current_suite = static_cast<const GDScriptParser::ForNode *>(p_context.current_suite->statements[i])->loop;2743_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2744break;2745case GDScriptParser::Node::WHILE:2746c.current_suite = static_cast<const GDScriptParser::WhileNode *>(p_context.current_suite->statements[i])->loop;2747_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2748break;2749case GDScriptParser::Node::IF: {2750const GDScriptParser::IfNode *_if = static_cast<const GDScriptParser::IfNode *>(p_context.current_suite->statements[i]);2751c.current_suite = _if->true_block;2752_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2753if (_if->false_block) {2754c.current_suite = _if->false_block;2755_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2756}2757} break;2758case GDScriptParser::Node::MATCH: {2759const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(p_context.current_suite->statements[i]);2760for (int j = 0; j < match->branches.size(); j++) {2761c.current_suite = match->branches[j]->block;2762_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2763}2764} break;2765case GDScriptParser::Node::RETURN: {2766const GDScriptParser::ReturnNode *ret = static_cast<const GDScriptParser::ReturnNode *>(p_context.current_suite->statements[i]);2767if (ret->return_value) {2768if (ret->start_line > r_last_return_line) {2769r_last_return_line = ret->start_line;2770*r_last_returned_value = ret->return_value;2771}2772}2773} break;2774default:2775break;2776}2777}2778}27792780static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) {2781static int recursion_depth = 0;2782RecursionCheck recursion(&recursion_depth);2783if (unlikely(recursion.check())) {2784ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");2785}27862787GDScriptParser::DataType base_type = p_base.type;2788bool is_static = base_type.is_meta_type;27892790if (is_static && p_method == SNAME("new")) {2791r_type.type = base_type;2792r_type.type.is_meta_type = false;2793r_type.type.is_constant = false;2794return true;2795}27962797while (base_type.is_set() && !base_type.is_variant()) {2798switch (base_type.kind) {2799case GDScriptParser::DataType::CLASS:2800if (base_type.class_type->has_function(p_method)) {2801GDScriptParser::FunctionNode *method = base_type.class_type->get_member(p_method).function;2802if (!is_static || method->is_static) {2803if (method->get_datatype().is_set() && !method->get_datatype().is_variant()) {2804r_type.type = method->get_datatype();2805return true;2806}28072808int last_return_line = -1;2809const GDScriptParser::ExpressionNode *last_returned_value = nullptr;2810GDScriptParser::CompletionContext c = p_context;2811c.current_class = base_type.class_type;2812c.current_function = method;2813c.current_suite = method->body;28142815_find_last_return_in_block(c, last_return_line, &last_returned_value);2816if (last_returned_value) {2817c.current_line = c.current_suite->end_line;2818if (_guess_expression_type(c, last_returned_value, r_type)) {2819return true;2820}2821}2822}2823}2824base_type = base_type.class_type->base_type;2825break;2826case GDScriptParser::DataType::SCRIPT: {2827Ref<Script> scr = base_type.script_type;2828if (scr.is_valid()) {2829List<MethodInfo> methods;2830scr->get_script_method_list(&methods);2831for (const MethodInfo &mi : methods) {2832if (mi.name == p_method) {2833r_type = _type_from_property(mi.return_val);2834return true;2835}2836}2837Ref<Script> base_script = scr->get_base_script();2838if (base_script.is_valid()) {2839base_type.script_type = base_script;2840} else {2841base_type.kind = GDScriptParser::DataType::NATIVE;2842base_type.builtin_type = Variant::OBJECT;2843base_type.native_type = scr->get_instance_base_type();2844}2845} else {2846return false;2847}2848} break;2849case GDScriptParser::DataType::NATIVE: {2850if (!ClassDB::class_exists(base_type.native_type)) {2851return false;2852}2853MethodBind *mb = ClassDB::get_method(base_type.native_type, p_method);2854if (mb) {2855r_type = _type_from_property(mb->get_return_info());2856return true;2857}2858return false;2859} break;2860case GDScriptParser::DataType::BUILTIN: {2861Callable::CallError err;2862Variant tmp;2863Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);2864if (err.error != Callable::CallError::CALL_OK) {2865return false;2866}28672868List<MethodInfo> methods;2869tmp.get_method_list(&methods);28702871for (const MethodInfo &mi : methods) {2872if (mi.name == p_method) {2873r_type = _type_from_property(mi.return_val);2874return true;2875}2876}2877return false;2878} break;2879default: {2880return false;2881}2882}2883}28842885return false;2886}28872888static bool _guess_expecting_callable(GDScriptParser::CompletionContext &p_context) {2889if (p_context.call.call != nullptr && p_context.call.call->type == GDScriptParser::Node::CALL) {2890GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_context.call.call);2891GDScriptCompletionIdentifier ci;2892if (_guess_expression_type(p_context, call_node->callee, ci)) {2893if (ci.type.kind == GDScriptParser::DataType::BUILTIN && ci.type.builtin_type == Variant::CALLABLE) {2894if (p_context.call.argument >= 0 && p_context.call.argument < ci.type.method_info.arguments.size()) {2895return ci.type.method_info.arguments.get(p_context.call.argument).type == Variant::CALLABLE;2896}2897}2898}2899}29002901return false;2902}29032904static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {2905if (!p_enum_hint.contains_char('.')) {2906// Global constant or in the current class.2907StringName current_enum = p_enum_hint;2908if (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) {2909const GDScriptParser::EnumNode *_enum = p_context.current_class->get_member(current_enum).m_enum;2910for (int i = 0; i < _enum->values.size(); i++) {2911ScriptLanguage::CodeCompletionOption option(_enum->values[i].identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM);2912r_result.insert(option.display, option);2913}2914} else {2915for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {2916if (CoreConstants::get_global_constant_enum(i) == current_enum) {2917ScriptLanguage::CodeCompletionOption option(CoreConstants::get_global_constant_name(i), ScriptLanguage::CODE_COMPLETION_KIND_ENUM);2918r_result.insert(option.display, option);2919}2920}2921}2922} else {2923String class_name = p_enum_hint.get_slicec('.', 0);2924String enum_name = p_enum_hint.get_slicec('.', 1);29252926if (!ClassDB::class_exists(class_name)) {2927return;2928}29292930List<StringName> enum_constants;2931ClassDB::get_enum_constants(class_name, enum_name, &enum_constants);2932for (const StringName &E : enum_constants) {2933String candidate = class_name + "." + E;2934int location = _get_enum_constant_location(class_name, E);2935ScriptLanguage::CodeCompletionOption option(candidate, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);2936r_result.insert(option.display, option);2937}2938}2939}29402941static 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) {2942Variant base = p_base.value;2943GDScriptParser::DataType base_type = p_base.type;2944const StringName &method = p_call->function_name;29452946const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";2947const bool use_string_names = EDITOR_GET("text_editor/completion/add_string_name_literals");2948const bool use_node_paths = EDITOR_GET("text_editor/completion/add_node_path_literals");29492950while (base_type.is_set() && !base_type.is_variant()) {2951switch (base_type.kind) {2952case GDScriptParser::DataType::CLASS: {2953if (base_type.is_meta_type && method == SNAME("new")) {2954const GDScriptParser::ClassNode *current = base_type.class_type;29552956do {2957if (current->has_member("_init")) {2958const GDScriptParser::ClassNode::Member &member = current->get_member("_init");29592960if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {2961r_arghint = base_type.class_type->get_datatype().to_string() + " new" + _make_arguments_hint(member.function, p_argidx, true);2962return;2963}2964}2965current = current->base_type.class_type;2966} while (current != nullptr);29672968r_arghint = base_type.class_type->get_datatype().to_string() + " new()";2969return;2970}29712972if (base_type.class_type->has_member(method)) {2973const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(method);29742975if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {2976r_arghint = _make_arguments_hint(member.function, p_argidx);2977return;2978}2979}29802981base_type = base_type.class_type->base_type;2982} break;2983case GDScriptParser::DataType::SCRIPT: {2984if (base_type.script_type->is_valid() && base_type.script_type->has_method(method)) {2985r_arghint = _make_arguments_hint(base_type.script_type->get_method_info(method), p_argidx);2986return;2987}2988Ref<Script> base_script = base_type.script_type->get_base_script();2989if (base_script.is_valid()) {2990base_type.script_type = base_script;2991} else {2992base_type.kind = GDScriptParser::DataType::NATIVE;2993base_type.builtin_type = Variant::OBJECT;2994base_type.native_type = base_type.script_type->get_instance_base_type();2995}2996} break;2997case GDScriptParser::DataType::NATIVE: {2998StringName class_name = base_type.native_type;2999if (!ClassDB::class_exists(class_name)) {3000base_type.kind = GDScriptParser::DataType::UNRESOLVED;3001break;3002}30033004MethodInfo info;3005int method_args = 0;30063007if (ClassDB::get_method_info(class_name, method, &info)) {3008method_args = info.arguments.size();3009if (base.get_type() == Variant::OBJECT) {3010Object *obj = base.operator Object *();3011if (obj) {3012List<String> options;3013obj->get_argument_options(method, p_argidx, &options);3014for (String &opt : options) {3015// Handle user preference.3016if (opt.is_quoted()) {3017opt = opt.unquote().quote(quote_style);3018if (use_string_names && info.arguments[p_argidx].type == Variant::STRING_NAME) {3019if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3020GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3021if (literal->value.get_type() == Variant::STRING) {3022opt = "&" + opt;3023}3024} else {3025opt = "&" + opt;3026}3027} else if (use_node_paths && info.arguments[p_argidx].type == Variant::NODE_PATH) {3028if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3029GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3030if (literal->value.get_type() == Variant::STRING) {3031opt = "^" + opt;3032}3033} else {3034opt = "^" + opt;3035}3036}3037}3038ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3039r_result.insert(option.display, option);3040}3041}3042}30433044if (p_argidx < method_args) {3045const PropertyInfo &arg_info = info.arguments[p_argidx];3046if (arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {3047_find_enumeration_candidates(p_context, arg_info.class_name, r_result);3048}3049}30503051r_arghint = _make_arguments_hint(info, p_argidx);3052}30533054if (p_argidx == 1 && p_call && ClassDB::is_parent_class(class_name, SNAME("Tween")) && method == SNAME("tween_property")) {3055// Get tweened objects properties.3056if (p_call->arguments.is_empty()) {3057base_type.kind = GDScriptParser::DataType::UNRESOLVED;3058break;3059}3060GDScriptParser::ExpressionNode *tweened_object = p_call->arguments[0];3061if (!tweened_object) {3062base_type.kind = GDScriptParser::DataType::UNRESOLVED;3063break;3064}3065StringName native_type = tweened_object->datatype.native_type;3066switch (tweened_object->datatype.kind) {3067case GDScriptParser::DataType::SCRIPT: {3068Ref<Script> script = tweened_object->datatype.script_type;3069native_type = script->get_instance_base_type();3070int n = 0;3071while (script.is_valid()) {3072List<PropertyInfo> properties;3073script->get_script_property_list(&properties);3074for (const PropertyInfo &E : properties) {3075if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) {3076continue;3077}3078String name = E.name.quote(quote_style);3079if (use_node_paths) {3080if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3081GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3082if (literal->value.get_type() == Variant::STRING) {3083name = "^" + name;3084}3085} else {3086name = "^" + name;3087}3088}3089ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);3090r_result.insert(option.display, option);3091}3092script = script->get_base_script();3093n++;3094}3095} break;3096case GDScriptParser::DataType::CLASS: {3097GDScriptParser::ClassNode *clss = tweened_object->datatype.class_type;3098native_type = clss->base_type.native_type;3099int n = 0;3100while (clss) {3101for (GDScriptParser::ClassNode::Member member : clss->members) {3102if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {3103String name = member.get_name().quote(quote_style);3104if (use_node_paths) {3105if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3106GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3107if (literal->value.get_type() == Variant::STRING) {3108name = "^" + name;3109}3110} else {3111name = "^" + name;3112}3113}3114ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);3115r_result.insert(option.display, option);3116}3117}3118if (clss->base_type.kind == GDScriptParser::DataType::Kind::CLASS) {3119clss = clss->base_type.class_type;3120n++;3121} else {3122native_type = clss->base_type.native_type;3123clss = nullptr;3124}3125}3126} break;3127default:3128break;3129}31303131List<PropertyInfo> properties;3132ClassDB::get_property_list(native_type, &properties);3133for (const PropertyInfo &E : properties) {3134if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) {3135continue;3136}3137String name = E.name.quote(quote_style);3138if (use_node_paths) {3139if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3140GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3141if (literal->value.get_type() == Variant::STRING) {3142name = "^" + name;3143}3144} else {3145name = "^" + name;3146}3147}3148ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER);3149r_result.insert(option.display, option);3150}3151}31523153if (p_argidx == 0 && ClassDB::is_parent_class(class_name, SNAME("Node")) && (method == SNAME("get_node") || method == SNAME("has_node"))) {3154// Get autoloads3155List<PropertyInfo> props;3156ProjectSettings::get_singleton()->get_property_list(&props);31573158for (const PropertyInfo &E : props) {3159String s = E.name;3160if (!s.begins_with("autoload/")) {3161continue;3162}3163String name = s.get_slicec('/', 1);3164String path = ("/root/" + name).quote(quote_style);3165if (use_node_paths) {3166if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3167GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3168if (literal->value.get_type() == Variant::STRING) {3169path = "^" + path;3170}3171} else {3172path = "^" + path;3173}3174}3175ScriptLanguage::CodeCompletionOption option(path, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);3176r_result.insert(option.display, option);3177}3178}31793180if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, SNAME("InputEvent")) && method.operator String().contains("action")) {3181// Get input actions3182List<PropertyInfo> props;3183ProjectSettings::get_singleton()->get_property_list(&props);3184for (const PropertyInfo &E : props) {3185String s = E.name;3186if (!s.begins_with("input/")) {3187continue;3188}3189String name = s.get_slicec('/', 1).quote(quote_style);3190if (use_string_names) {3191if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3192GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3193if (literal->value.get_type() == Variant::STRING) {3194name = "&" + name;3195}3196} else {3197name = "&" + name;3198}3199}3200ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);3201r_result.insert(option.display, option);3202}3203}3204if (EDITOR_GET("text_editor/completion/complete_file_paths")) {3205if (p_argidx == 0 && method == SNAME("change_scene_to_file") && ClassDB::is_parent_class(class_name, SNAME("SceneTree"))) {3206HashMap<String, ScriptLanguage::CodeCompletionOption> list;3207_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), list, SNAME("PackedScene"));3208for (const KeyValue<String, ScriptLanguage::CodeCompletionOption> &key_value_pair : list) {3209ScriptLanguage::CodeCompletionOption option = key_value_pair.value;3210r_result.insert(option.display, option);3211}3212}3213}32143215base_type.kind = GDScriptParser::DataType::UNRESOLVED;3216} break;3217case GDScriptParser::DataType::BUILTIN: {3218if (base.get_type() == Variant::NIL) {3219Callable::CallError err;3220Variant::construct(base_type.builtin_type, base, nullptr, 0, err);3221if (err.error != Callable::CallError::CALL_OK) {3222return;3223}3224}32253226List<MethodInfo> methods;3227base.get_method_list(&methods);3228for (const MethodInfo &E : methods) {3229if (E.name == method) {3230r_arghint = _make_arguments_hint(E, p_argidx);3231return;3232}3233}32343235base_type.kind = GDScriptParser::DataType::UNRESOLVED;3236} break;3237default: {3238base_type.kind = GDScriptParser::DataType::UNRESOLVED;3239} break;3240}3241}3242}32433244static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::SubscriptNode *p_subscript, GDScriptParser::DataType &r_base_type, Variant *r_base = nullptr) {3245if (p_context.base == nullptr) {3246return false;3247}32483249const GDScriptParser::GetNodeNode *get_node = nullptr;32503251switch (p_subscript->base->type) {3252case GDScriptParser::Node::GET_NODE: {3253get_node = static_cast<GDScriptParser::GetNodeNode *>(p_subscript->base);3254} break;32553256case GDScriptParser::Node::IDENTIFIER: {3257const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base);32583259switch (identifier_node->source) {3260case GDScriptParser::IdentifierNode::Source::MEMBER_VARIABLE: {3261if (p_context.current_class != nullptr) {3262const StringName &member_name = identifier_node->name;3263const GDScriptParser::ClassNode *current_class = p_context.current_class;32643265if (current_class->has_member(member_name)) {3266const GDScriptParser::ClassNode::Member &member = current_class->get_member(member_name);32673268if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {3269const GDScriptParser::VariableNode *variable = static_cast<GDScriptParser::VariableNode *>(member.variable);32703271if (variable->initializer && variable->initializer->type == GDScriptParser::Node::GET_NODE) {3272get_node = static_cast<GDScriptParser::GetNodeNode *>(variable->initializer);3273}3274}3275}3276}3277} break;3278case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: {3279// TODO: Do basic assignment flow analysis like in `_guess_expression_type`.3280const GDScriptParser::SuiteNode::Local local = identifier_node->suite->get_local(identifier_node->name);3281switch (local.type) {3282case GDScriptParser::SuiteNode::Local::CONSTANT: {3283if (local.constant->initializer && local.constant->initializer->type == GDScriptParser::Node::GET_NODE) {3284get_node = static_cast<GDScriptParser::GetNodeNode *>(local.constant->initializer);3285}3286} break;3287case GDScriptParser::SuiteNode::Local::VARIABLE: {3288if (local.variable->initializer && local.variable->initializer->type == GDScriptParser::Node::GET_NODE) {3289get_node = static_cast<GDScriptParser::GetNodeNode *>(local.variable->initializer);3290}3291} break;3292default: {3293} break;3294}3295} break;3296default: {3297} break;3298}3299} break;3300default: {3301} break;3302}33033304if (get_node != nullptr) {3305const Object *node = p_context.base->call("get_node_or_null", NodePath(get_node->full_path));3306if (node != nullptr) {3307GDScriptParser::DataType assigned_type = _type_from_variant(node, p_context).type;3308GDScriptParser::DataType base_type = p_subscript->base->datatype;33093310if (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)) {3311// Annotated type takes precedence.3312return false;3313}33143315if (r_base != nullptr) {3316*r_base = node;3317}33183319r_base_type.type_source = GDScriptParser::DataType::INFERRED;3320r_base_type.builtin_type = Variant::OBJECT;3321r_base_type.native_type = node->get_class_name();33223323Ref<Script> scr = node->get_script();3324if (scr.is_null()) {3325r_base_type.kind = GDScriptParser::DataType::NATIVE;3326} else {3327r_base_type.kind = GDScriptParser::DataType::SCRIPT;3328r_base_type.script_type = scr;3329}33303331return true;3332}3333}33343335return false;3336}33373338static 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) {3339if (p_call->type == GDScriptParser::Node::PRELOAD) {3340if (p_argidx == 0 && bool(EDITOR_GET("text_editor/completion/complete_file_paths"))) {3341_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);3342}33433344MethodInfo mi(PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), "preload", PropertyInfo(Variant::STRING, "path"));3345r_arghint = _make_arguments_hint(mi, p_argidx);3346return;3347} else if (p_call->type != GDScriptParser::Node::CALL) {3348return;3349}33503351Variant base;3352GDScriptParser::DataType base_type;3353bool _static = false;3354const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call);3355GDScriptParser::Node::Type callee_type = call->get_callee_type();33563357if (callee_type == GDScriptParser::Node::SUBSCRIPT) {3358const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);33593360if (subscript->base != nullptr && subscript->base->type == GDScriptParser::Node::IDENTIFIER) {3361const GDScriptParser::IdentifierNode *base_identifier = static_cast<const GDScriptParser::IdentifierNode *>(subscript->base);33623363Variant::Type method_type = GDScriptParser::get_builtin_type(base_identifier->name);3364if (method_type < Variant::VARIANT_MAX) {3365Variant v;3366Callable::CallError err;3367Variant::construct(method_type, v, nullptr, 0, err);3368if (err.error != Callable::CallError::CALL_OK) {3369return;3370}3371List<MethodInfo> methods;3372v.get_method_list(&methods);33733374for (MethodInfo &E : methods) {3375if (p_argidx >= E.arguments.size()) {3376continue;3377}3378if (E.name == call->function_name) {3379r_arghint += _make_arguments_hint(E, p_argidx);3380return;3381}3382}3383}3384}33853386if (subscript->is_attribute) {3387bool found_type = _get_subscript_type(p_context, subscript, base_type, &base);33883389if (!found_type) {3390GDScriptCompletionIdentifier ci;3391if (_guess_expression_type(p_context, subscript->base, ci)) {3392base_type = ci.type;3393base = ci.value;3394} else {3395return;3396}3397}33983399_static = base_type.is_meta_type;3400}3401} else if (Variant::has_utility_function(call->function_name)) {3402MethodInfo info = Variant::get_utility_function_info(call->function_name);3403r_arghint = _make_arguments_hint(info, p_argidx);3404return;3405} else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {3406MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);3407r_arghint = _make_arguments_hint(info, p_argidx);3408return;3409} else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {3410// Complete constructor.3411List<MethodInfo> constructors;3412Variant::get_constructor_list(GDScriptParser::get_builtin_type(call->function_name), &constructors);34133414int i = 0;3415for (const MethodInfo &E : constructors) {3416if (p_argidx >= E.arguments.size()) {3417continue;3418}3419if (i > 0) {3420r_arghint += "\n";3421}3422r_arghint += _make_arguments_hint(E, p_argidx);3423i++;3424}3425return;3426} else if (call->is_super || callee_type == GDScriptParser::Node::IDENTIFIER) {3427base = p_context.base;34283429if (p_context.current_class) {3430base_type = p_context.current_class->get_datatype();3431_static = !p_context.current_function || p_context.current_function->is_static;3432}3433} else {3434return;3435}34363437GDScriptCompletionIdentifier ci;3438ci.type = base_type;3439ci.value = base;3440_list_call_arguments(p_context, ci, call, p_argidx, _static, r_result, r_arghint);34413442r_forced = r_result.size() > 0;3443}34443445::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) {3446const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";34473448GDScriptParser parser;3449GDScriptAnalyzer analyzer(&parser);34503451parser.parse(p_code, p_path, true);3452analyzer.analyze();34533454r_forced = false;3455HashMap<String, ScriptLanguage::CodeCompletionOption> options;34563457GDScriptParser::CompletionContext completion_context = parser.get_completion_context();3458if (completion_context.current_class != nullptr && completion_context.current_class->outer == nullptr) {3459completion_context.base = p_owner;3460}3461bool is_function = false;34623463switch (completion_context.type) {3464case GDScriptParser::COMPLETION_NONE:3465break;3466case GDScriptParser::COMPLETION_ANNOTATION: {3467List<MethodInfo> annotations;3468parser.get_annotation_list(&annotations);3469for (const MethodInfo &E : annotations) {3470ScriptLanguage::CodeCompletionOption option(E.name.substr(1), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3471if (E.arguments.size() > 0) {3472option.insert_text += "(";3473}3474options.insert(option.display, option);3475}3476r_forced = true;3477} break;3478case GDScriptParser::COMPLETION_ANNOTATION_ARGUMENTS: {3479if (completion_context.node == nullptr || completion_context.node->type != GDScriptParser::Node::ANNOTATION) {3480break;3481}3482const GDScriptParser::AnnotationNode *annotation = static_cast<const GDScriptParser::AnnotationNode *>(completion_context.node);3483_find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options, r_call_hint);3484r_forced = true;3485} break;3486case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: {3487// Constants.3488{3489List<StringName> constants;3490Variant::get_constants_for_type(completion_context.builtin_type, &constants);3491for (const StringName &E : constants) {3492ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);3493bool valid = false;3494Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E, &valid);3495if (valid) {3496option.default_value = default_value;3497}3498options.insert(option.display, option);3499}3500}3501// Methods.3502{3503List<StringName> methods;3504Variant::get_builtin_method_list(completion_context.builtin_type, &methods);3505for (const StringName &E : methods) {3506if (Variant::is_builtin_method_static(completion_context.builtin_type, E)) {3507ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3508if (!_guess_expecting_callable(completion_context)) {3509if (Variant::get_builtin_method_argument_count(completion_context.builtin_type, E) > 0 || Variant::is_builtin_method_vararg(completion_context.builtin_type, E)) {3510option.insert_text += "(";3511} else {3512option.insert_text += "()";3513}3514}3515options.insert(option.display, option);3516}3517}3518}3519} break;3520case GDScriptParser::COMPLETION_INHERIT_TYPE: {3521_list_available_types(true, completion_context, options);3522r_forced = true;3523} break;3524case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID: {3525ScriptLanguage::CodeCompletionOption option("void", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3526options.insert(option.display, option);3527}3528[[fallthrough]];3529case GDScriptParser::COMPLETION_TYPE_NAME: {3530_list_available_types(false, completion_context, options);3531r_forced = true;3532} break;3533case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE: {3534_list_available_types(false, completion_context, options);3535ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3536options.insert(get.display, get);3537ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3538options.insert(set.display, set);3539r_forced = true;3540} break;3541case GDScriptParser::COMPLETION_PROPERTY_DECLARATION: {3542ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3543options.insert(get.display, get);3544ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3545options.insert(set.display, set);3546r_forced = true;3547} break;3548case GDScriptParser::COMPLETION_PROPERTY_METHOD: {3549if (!completion_context.current_class) {3550break;3551}3552for (int i = 0; i < completion_context.current_class->members.size(); i++) {3553const GDScriptParser::ClassNode::Member &member = completion_context.current_class->members[i];3554if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {3555continue;3556}3557if (member.function->is_static) {3558continue;3559}3560ScriptLanguage::CodeCompletionOption option(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3561options.insert(option.display, option);3562}3563r_forced = true;3564} break;3565case GDScriptParser::COMPLETION_ASSIGN: {3566GDScriptCompletionIdentifier type;3567if (!completion_context.node || completion_context.node->type != GDScriptParser::Node::ASSIGNMENT) {3568break;3569}3570if (!_guess_expression_type(completion_context, static_cast<const GDScriptParser::AssignmentNode *>(completion_context.node)->assignee, type)) {3571_find_identifiers(completion_context, false, true, options, 0);3572r_forced = true;3573break;3574}35753576if (!type.enumeration.is_empty()) {3577_find_enumeration_candidates(completion_context, type.enumeration, options);3578r_forced = options.size() > 0;3579} else {3580_find_identifiers(completion_context, false, true, options, 0);3581r_forced = true;3582}3583} break;3584case GDScriptParser::COMPLETION_METHOD:3585is_function = true;3586[[fallthrough]];3587case GDScriptParser::COMPLETION_IDENTIFIER: {3588_find_identifiers(completion_context, is_function, !_guess_expecting_callable(completion_context), options, 0);3589} break;3590case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD:3591is_function = true;3592[[fallthrough]];3593case GDScriptParser::COMPLETION_ATTRIBUTE: {3594r_forced = true;3595const GDScriptParser::SubscriptNode *attr = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);3596if (attr->base) {3597GDScriptCompletionIdentifier base;3598bool found_type = _get_subscript_type(completion_context, attr, base.type);3599if (!found_type && !_guess_expression_type(completion_context, attr->base, base)) {3600break;3601}36023603_find_identifiers_in_base(base, is_function, false, !_guess_expecting_callable(completion_context), options, 0);3604}3605} break;3606case GDScriptParser::COMPLETION_SUBSCRIPT: {3607const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);3608GDScriptCompletionIdentifier base;3609const bool res = _guess_expression_type(completion_context, subscript->base, base);36103611// If the type is not known, we assume it is BUILTIN, since indices on arrays is the most common use case.3612if (!subscript->is_attribute && (!res || base.type.kind == GDScriptParser::DataType::BUILTIN || base.type.is_variant())) {3613if (base.value.get_type() == Variant::DICTIONARY) {3614List<PropertyInfo> members;3615base.value.get_property_list(&members);36163617for (const PropertyInfo &E : members) {3618ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::LOCATION_LOCAL);3619options.insert(option.display, option);3620}3621}3622if (!subscript->index || subscript->index->type != GDScriptParser::Node::LITERAL) {3623_find_identifiers(completion_context, false, !_guess_expecting_callable(completion_context), options, 0);3624}3625} else if (res) {3626if (!subscript->is_attribute) {3627// Quote the options if they are not accessed as attribute.36283629HashMap<String, ScriptLanguage::CodeCompletionOption> opt;3630_find_identifiers_in_base(base, false, false, false, opt, 0);3631for (const KeyValue<String, CodeCompletionOption> &E : opt) {3632ScriptLanguage::CodeCompletionOption option(E.value.insert_text.quote(quote_style), E.value.kind, E.value.location);3633options.insert(option.display, option);3634}3635} else {3636_find_identifiers_in_base(base, false, false, !_guess_expecting_callable(completion_context), options, 0);3637}3638}3639} break;3640case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {3641if (!completion_context.current_class) {3642break;3643}36443645const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(completion_context.node);3646ERR_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.");36473648GDScriptCompletionIdentifier base;36493650if (_guess_identifier_type(completion_context, type->type_chain[0], base)) {3651bool found = true;3652for (int i = 1; i < completion_context.type_chain_index; i++) {3653GDScriptCompletionIdentifier ci;3654found = _guess_identifier_type_from_base(completion_context, base, type->type_chain[i]->name, ci);3655base = ci;3656if (!found) {3657break;3658}3659}3660if (found) {3661_find_identifiers_in_base(base, false, true, true, options, 0);3662}3663}36643665r_forced = true;3666} break;3667case GDScriptParser::COMPLETION_RESOURCE_PATH: {3668if (EDITOR_GET("text_editor/completion/complete_file_paths")) {3669_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);3670r_forced = true;3671}3672} break;3673case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {3674if (!completion_context.node) {3675break;3676}3677_find_call_arguments(completion_context, completion_context.node, completion_context.current_argument, options, r_forced, r_call_hint);3678} break;3679case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {3680GDScriptParser::DataType native_type = completion_context.current_class->base_type;3681GDScriptParser::FunctionNode *function_node = static_cast<GDScriptParser::FunctionNode *>(completion_context.node);3682bool is_static = function_node != nullptr && function_node->is_static;3683while (native_type.is_set() && native_type.kind != GDScriptParser::DataType::NATIVE) {3684switch (native_type.kind) {3685case GDScriptParser::DataType::CLASS: {3686for (const GDScriptParser::ClassNode::Member &member : native_type.class_type->members) {3687if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {3688continue;3689}36903691if (options.has(member.function->identifier->name)) {3692continue;3693}36943695if (completion_context.current_class->has_function(member.get_name()) && completion_context.current_class->get_member(member.get_name()).function != function_node) {3696continue;3697}36983699if (is_static != member.function->is_static) {3700continue;3701}37023703String display_name = member.function->identifier->name;3704display_name += member.function->signature + ":";3705ScriptLanguage::CodeCompletionOption option(display_name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3706options.insert(member.function->identifier->name, option); // Insert name instead of display to track duplicates.3707}3708native_type = native_type.class_type->base_type;3709} break;3710default: {3711native_type.kind = GDScriptParser::DataType::UNRESOLVED;3712} break;3713}3714}37153716if (!native_type.is_set()) {3717break;3718}37193720StringName class_name = native_type.native_type;3721if (!ClassDB::class_exists(class_name)) {3722break;3723}37243725const bool type_hints = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");37263727List<MethodInfo> virtual_methods;3728if (is_static) {3729// Not truly a virtual method, but can also be "overridden".3730MethodInfo static_init("_static_init");3731static_init.return_val.type = Variant::NIL;3732static_init.flags |= METHOD_FLAG_STATIC | METHOD_FLAG_VIRTUAL;3733virtual_methods.push_back(static_init);3734} else {3735ClassDB::get_virtual_methods(class_name, &virtual_methods);3736}37373738for (const MethodInfo &mi : virtual_methods) {3739if (options.has(mi.name)) {3740continue;3741}3742if (completion_context.current_class->has_function(mi.name) && completion_context.current_class->get_member(mi.name).function != function_node) {3743continue;3744}3745String method_hint = mi.name;3746if (method_hint.contains_char(':')) {3747method_hint = method_hint.get_slicec(':', 0);3748}3749method_hint += "(";37503751for (int64_t i = 0; i < mi.arguments.size(); ++i) {3752if (i > 0) {3753method_hint += ", ";3754}3755String arg = mi.arguments[i].name;3756if (arg.contains_char(':')) {3757arg = arg.substr(0, arg.find_char(':'));3758}3759method_hint += arg;3760if (type_hints) {3761method_hint += ": " + _get_visual_datatype(mi.arguments[i], true, class_name);3762}3763}3764if (mi.flags & METHOD_FLAG_VARARG) {3765if (!mi.arguments.is_empty()) {3766method_hint += ", ";3767}3768method_hint += "...args"; // `MethodInfo` does not support the rest parameter name.3769if (type_hints) {3770method_hint += ": Array";3771}3772}3773method_hint += ")";3774if (type_hints) {3775method_hint += " -> " + _get_visual_datatype(mi.return_val, false, class_name);3776}3777method_hint += ":";37783779ScriptLanguage::CodeCompletionOption option(method_hint, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3780options.insert(option.display, option);3781}3782} break;3783case GDScriptParser::COMPLETION_GET_NODE: {3784// Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically.3785if (p_owner) {3786List<String> opts;3787p_owner->get_argument_options("get_node", 0, &opts);37883789bool for_unique_name = false;3790if (completion_context.node != nullptr && completion_context.node->type == GDScriptParser::Node::GET_NODE && !static_cast<GDScriptParser::GetNodeNode *>(completion_context.node)->use_dollar) {3791for_unique_name = true;3792}37933794for (const String &E : opts) {3795r_forced = true;3796String opt = E.strip_edges();3797if (opt.is_quoted()) {3798// Remove quotes so that we can handle user preferred quote style,3799// or handle NodePaths which are valid identifiers and don't need quotes.3800opt = opt.unquote();3801}38023803if (for_unique_name) {3804if (!opt.begins_with("%")) {3805continue;3806}3807opt = opt.substr(1);3808}38093810// The path needs quotes if at least one of its components (excluding `%` prefix and `/` separations)3811// is not a valid identifier.3812bool path_needs_quote = false;3813for (const String &part : opt.trim_prefix("%").split("/")) {3814if (!part.is_valid_ascii_identifier()) {3815path_needs_quote = true;3816break;3817}3818}38193820if (path_needs_quote) {3821// Ignore quote_style and just use double quotes for paths with apostrophes.3822// Double quotes don't need to be checked because they're not valid in node and property names.3823opt = opt.quote(opt.contains_char('\'') ? "\"" : quote_style); // Handle user preference.3824}3825ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);3826options.insert(option.display, option);3827}38283829if (!for_unique_name) {3830// Get autoloads.3831for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {3832String path = "/root/" + E.key;3833ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);3834options.insert(option.display, option);3835}3836}3837}3838} break;3839case GDScriptParser::COMPLETION_SUPER:3840break;3841case GDScriptParser::COMPLETION_SUPER_METHOD: {3842if (!completion_context.current_class) {3843break;3844}3845_find_identifiers_in_class(completion_context.current_class, true, false, false, true, !_guess_expecting_callable(completion_context), options, 0);3846} break;3847}38483849for (const KeyValue<String, ScriptLanguage::CodeCompletionOption> &E : options) {3850r_options->push_back(E.value);3851}38523853return OK;3854}38553856#else // !TOOLS_ENABLED38573858Error 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) {3859return OK;3860}38613862#endif // TOOLS_ENABLED38633864//////// END COMPLETION //////////38653866String GDScriptLanguage::_get_indentation() const {3867#ifdef TOOLS_ENABLED3868if (Engine::get_singleton()->is_editor_hint()) {3869bool use_space_indentation = EDITOR_GET("text_editor/behavior/indent/type");38703871if (use_space_indentation) {3872int indent_size = EDITOR_GET("text_editor/behavior/indent/size");3873return String(" ").repeat(indent_size);3874}3875}3876#endif3877return "\t";3878}38793880void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {3881String indent = _get_indentation();38823883Vector<String> lines = p_code.split("\n");3884List<int> indent_stack;38853886for (int i = 0; i < lines.size(); i++) {3887String l = lines[i];3888int tc = 0;3889for (int j = 0; j < l.length(); j++) {3890if (l[j] == ' ' || l[j] == '\t') {3891tc++;3892} else {3893break;3894}3895}38963897String st = l.substr(tc).strip_edges();3898if (st.is_empty() || st.begins_with("#")) {3899continue; //ignore!3900}39013902int ilevel = 0;3903if (indent_stack.size()) {3904ilevel = indent_stack.back()->get();3905}39063907if (tc > ilevel) {3908indent_stack.push_back(tc);3909} else if (tc < ilevel) {3910while (indent_stack.size() && indent_stack.back()->get() > tc) {3911indent_stack.pop_back();3912}39133914if (indent_stack.size() && indent_stack.back()->get() != tc) {3915indent_stack.push_back(tc); // this is not right but gets the job done3916}3917}39183919if (i >= p_from_line) {3920l = indent.repeat(indent_stack.size()) + st;3921} else if (i > p_to_line) {3922break;3923}39243925lines.write[i] = l;3926}39273928p_code = "";3929for (int i = 0; i < lines.size(); i++) {3930if (i > 0) {3931p_code += "\n";3932}3933p_code += lines[i];3934}3935}39363937#ifdef TOOLS_ENABLED39383939static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, const String &p_symbol, GDScriptLanguage::LookupResult &r_result) {3940GDScriptParser::DataType base_type = p_base;39413942while (true) {3943switch (base_type.kind) {3944case GDScriptParser::DataType::CLASS: {3945ERR_FAIL_NULL_V(base_type.class_type, ERR_BUG);39463947String name = p_symbol;3948if (name == "new") {3949name = "_init";3950}39513952if (!base_type.class_type->has_member(name)) {3953base_type = base_type.class_type->base_type;3954break;3955}39563957const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(name);39583959switch (member.type) {3960case GDScriptParser::ClassNode::Member::UNDEFINED:3961case GDScriptParser::ClassNode::Member::GROUP:3962return ERR_BUG;3963case GDScriptParser::ClassNode::Member::CLASS: {3964String doc_type_name;3965String doc_enum_name;3966GDScriptDocGen::doctype_from_gdtype(GDScriptAnalyzer::type_from_metatype(member.get_datatype()), doc_type_name, doc_enum_name);39673968r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;3969r_result.class_name = doc_type_name;3970} break;3971case GDScriptParser::ClassNode::Member::CONSTANT:3972r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;3973break;3974case GDScriptParser::ClassNode::Member::FUNCTION:3975r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;3976break;3977case GDScriptParser::ClassNode::Member::SIGNAL:3978r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;3979break;3980case GDScriptParser::ClassNode::Member::VARIABLE:3981r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;3982break;3983case GDScriptParser::ClassNode::Member::ENUM:3984r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;3985break;3986case GDScriptParser::ClassNode::Member::ENUM_VALUE:3987r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;3988break;3989}39903991if (member.type != GDScriptParser::ClassNode::Member::CLASS) {3992String doc_type_name;3993String doc_enum_name;3994GDScriptDocGen::doctype_from_gdtype(GDScriptAnalyzer::type_from_metatype(base_type), doc_type_name, doc_enum_name);39953996r_result.class_name = doc_type_name;3997r_result.class_member = name;3998}39994000Error err = OK;4001r_result.script = GDScriptCache::get_shallow_script(base_type.script_path, err);4002r_result.script_path = base_type.script_path;4003r_result.location = member.get_line();4004return err;4005} break;4006case GDScriptParser::DataType::SCRIPT: {4007const Ref<Script> scr = base_type.script_type;40084009if (scr.is_null()) {4010return ERR_CANT_RESOLVE;4011}40124013String name = p_symbol;4014if (name == "new") {4015name = "_init";4016}40174018const int line = scr->get_member_line(name);4019if (line >= 0) {4020bool found_type = false;4021r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION;4022{4023List<PropertyInfo> properties;4024scr->get_script_property_list(&properties);4025for (const PropertyInfo &property : properties) {4026if (property.name == name && (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) {4027found_type = true;4028r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;4029r_result.class_name = scr->get_doc_class_name();4030r_result.class_member = name;4031break;4032}4033}4034}4035if (!found_type) {4036List<MethodInfo> methods;4037scr->get_script_method_list(&methods);4038for (const MethodInfo &method : methods) {4039if (method.name == name) {4040found_type = true;4041r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4042r_result.class_name = scr->get_doc_class_name();4043r_result.class_member = name;4044break;4045}4046}4047}4048if (!found_type) {4049List<MethodInfo> signals;4050scr->get_script_method_list(&signals);4051for (const MethodInfo &signal : signals) {4052if (signal.name == name) {4053found_type = true;4054r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;4055r_result.class_name = scr->get_doc_class_name();4056r_result.class_member = name;4057break;4058}4059}4060}4061if (!found_type) {4062const Ref<GDScript> gds = scr;4063if (gds.is_valid()) {4064const Ref<GDScript> *subclass = gds->get_subclasses().getptr(name);4065if (subclass != nullptr) {4066found_type = true;4067r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4068r_result.class_name = subclass->ptr()->get_doc_class_name();4069}4070// TODO: enums.4071}4072}4073if (!found_type) {4074HashMap<StringName, Variant> constants;4075scr->get_constants(&constants);4076if (constants.has(name)) {4077found_type = true;4078r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4079r_result.class_name = scr->get_doc_class_name();4080r_result.class_member = name;4081}4082}40834084r_result.script = scr;4085r_result.script_path = base_type.script_path;4086r_result.location = line;4087return OK;4088}40894090const Ref<Script> base_script = scr->get_base_script();4091if (base_script.is_valid()) {4092base_type.script_type = base_script;4093} else {4094base_type.kind = GDScriptParser::DataType::NATIVE;4095base_type.builtin_type = Variant::OBJECT;4096base_type.native_type = scr->get_instance_base_type();4097}4098} break;4099case GDScriptParser::DataType::NATIVE: {4100const StringName &class_name = base_type.native_type;41014102ERR_FAIL_COND_V(!ClassDB::class_exists(class_name), ERR_BUG);41034104if (ClassDB::has_method(class_name, p_symbol, true)) {4105r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4106r_result.class_name = class_name;4107r_result.class_member = p_symbol;4108return OK;4109}41104111List<MethodInfo> virtual_methods;4112ClassDB::get_virtual_methods(class_name, &virtual_methods, true);4113for (const MethodInfo &E : virtual_methods) {4114if (E.name == p_symbol) {4115r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4116r_result.class_name = class_name;4117r_result.class_member = p_symbol;4118return OK;4119}4120}41214122if (ClassDB::has_signal(class_name, p_symbol, true)) {4123r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;4124r_result.class_name = class_name;4125r_result.class_member = p_symbol;4126return OK;4127}41284129List<StringName> enums;4130ClassDB::get_enum_list(class_name, &enums);4131for (const StringName &E : enums) {4132if (E == p_symbol) {4133r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4134r_result.class_name = class_name;4135r_result.class_member = p_symbol;4136return OK;4137}4138}41394140if (!String(ClassDB::get_integer_constant_enum(class_name, p_symbol, true)).is_empty()) {4141r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4142r_result.class_name = class_name;4143r_result.class_member = p_symbol;4144return OK;4145}41464147List<String> constants;4148ClassDB::get_integer_constant_list(class_name, &constants, true);4149for (const String &E : constants) {4150if (E == p_symbol) {4151r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4152r_result.class_name = class_name;4153r_result.class_member = p_symbol;4154return OK;4155}4156}41574158if (ClassDB::has_property(class_name, p_symbol, true)) {4159PropertyInfo prop_info;4160ClassDB::get_property_info(class_name, p_symbol, &prop_info, true);4161if (prop_info.usage & PROPERTY_USAGE_INTERNAL) {4162return ERR_CANT_RESOLVE;4163}41644165r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;4166r_result.class_name = class_name;4167r_result.class_member = p_symbol;4168return OK;4169}41704171const StringName parent_class = ClassDB::get_parent_class(class_name);4172if (parent_class != StringName()) {4173base_type.native_type = parent_class;4174} else {4175return ERR_CANT_RESOLVE;4176}4177} break;4178case GDScriptParser::DataType::BUILTIN: {4179if (base_type.is_meta_type) {4180if (Variant::has_enum(base_type.builtin_type, p_symbol)) {4181r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4182r_result.class_name = Variant::get_type_name(base_type.builtin_type);4183r_result.class_member = p_symbol;4184return OK;4185}41864187if (Variant::has_constant(base_type.builtin_type, p_symbol)) {4188r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4189r_result.class_name = Variant::get_type_name(base_type.builtin_type);4190r_result.class_member = p_symbol;4191return OK;4192}4193} else {4194if (Variant::has_member(base_type.builtin_type, p_symbol)) {4195r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;4196r_result.class_name = Variant::get_type_name(base_type.builtin_type);4197r_result.class_member = p_symbol;4198return OK;4199}4200}42014202if (Variant::has_builtin_method(base_type.builtin_type, p_symbol)) {4203r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4204r_result.class_name = Variant::get_type_name(base_type.builtin_type);4205r_result.class_member = p_symbol;4206return OK;4207}42084209return ERR_CANT_RESOLVE;4210} break;4211case GDScriptParser::DataType::ENUM: {4212if (base_type.is_meta_type) {4213if (base_type.enum_values.has(p_symbol)) {4214String doc_type_name;4215String doc_enum_name;4216GDScriptDocGen::doctype_from_gdtype(GDScriptAnalyzer::type_from_metatype(base_type), doc_type_name, doc_enum_name);42174218if (CoreConstants::is_global_enum(doc_enum_name)) {4219r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4220r_result.class_name = "@GlobalScope";4221r_result.class_member = p_symbol;4222return OK;4223} else {4224const int dot_pos = doc_enum_name.rfind_char('.');4225if (dot_pos >= 0) {4226Error err = OK;4227r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4228if (base_type.class_type != nullptr) {4229// For script enums the value isn't accessible as class constant so we need the full enum name.4230r_result.class_name = doc_enum_name;4231r_result.class_member = p_symbol;4232r_result.script = GDScriptCache::get_shallow_script(base_type.script_path, err);4233r_result.script_path = base_type.script_path;4234const String enum_name = doc_enum_name.substr(dot_pos + 1);4235if (base_type.class_type->has_member(enum_name)) {4236const GDScriptParser::ClassNode::Member member = base_type.class_type->get_member(enum_name);4237if (member.type == GDScriptParser::ClassNode::Member::ENUM) {4238for (const GDScriptParser::EnumNode::Value &value : member.m_enum->values) {4239if (value.identifier->name == p_symbol) {4240r_result.location = value.line;4241break;4242}4243}4244}4245}4246} else if (base_type.script_type.is_valid()) {4247// For script enums the value isn't accessible as class constant so we need the full enum name.4248r_result.class_name = doc_enum_name;4249r_result.class_member = p_symbol;4250r_result.script = base_type.script_type;4251r_result.script_path = base_type.script_path;4252// TODO: Find a way to obtain enum value location for a script4253r_result.location = base_type.script_type->get_member_line(doc_enum_name.substr(dot_pos + 1));4254} else {4255r_result.class_name = doc_enum_name.left(dot_pos);4256r_result.class_member = p_symbol;4257}4258return err;4259}4260}4261} else if (Variant::has_builtin_method(Variant::DICTIONARY, p_symbol)) {4262r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4263r_result.class_name = "Dictionary";4264r_result.class_member = p_symbol;4265return OK;4266}4267}42684269return ERR_CANT_RESOLVE;4270} break;4271case GDScriptParser::DataType::VARIANT: {4272if (base_type.is_meta_type) {4273const String enum_name = "Variant." + p_symbol;4274if (CoreConstants::is_global_enum(enum_name)) {4275r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4276r_result.class_name = "@GlobalScope";4277r_result.class_member = enum_name;4278return OK;4279}4280}42814282return ERR_CANT_RESOLVE;4283} break;4284case GDScriptParser::DataType::RESOLVING:4285case GDScriptParser::DataType::UNRESOLVED: {4286return ERR_CANT_RESOLVE;4287} break;4288}4289}42904291return ERR_CANT_RESOLVE;4292}42934294::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) {4295// Before parsing, try the usual stuff.4296if (ClassDB::class_exists(p_symbol)) {4297r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4298r_result.class_name = p_symbol;4299return OK;4300}43014302if (Variant::get_type_by_name(p_symbol) < Variant::VARIANT_MAX) {4303r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4304r_result.class_name = p_symbol;4305return OK;4306}43074308if (p_symbol == "Variant") {4309r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4310r_result.class_name = "Variant";4311return OK;4312}43134314if (p_symbol == "PI" || p_symbol == "TAU" || p_symbol == "INF" || p_symbol == "NAN") {4315r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4316r_result.class_name = "@GDScript";4317r_result.class_member = p_symbol;4318return OK;4319}43204321GDScriptParser parser;4322parser.parse(p_code, p_path, true);43234324GDScriptParser::CompletionContext context = parser.get_completion_context();4325context.base = p_owner;43264327// Allows class functions with the names like built-ins to be handled properly.4328if (context.type != GDScriptParser::COMPLETION_ATTRIBUTE) {4329// Need special checks for `assert` and `preload` as they are technically4330// keywords, so are not registered in `GDScriptUtilityFunctions`.4331if (GDScriptUtilityFunctions::function_exists(p_symbol) || p_symbol == "assert" || p_symbol == "preload") {4332r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4333r_result.class_name = "@GDScript";4334r_result.class_member = p_symbol;4335return OK;4336}4337}43384339GDScriptAnalyzer analyzer(&parser);4340analyzer.analyze();43414342if (context.current_class && context.current_class->extends.size() > 0) {4343StringName class_name = context.current_class->extends[0]->name;43444345bool success = false;4346ClassDB::get_integer_constant(class_name, p_symbol, &success);4347if (success) {4348r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4349r_result.class_name = class_name;4350r_result.class_member = p_symbol;4351return OK;4352}4353do {4354List<StringName> enums;4355ClassDB::get_enum_list(class_name, &enums, true);4356for (const StringName &enum_name : enums) {4357if (enum_name == p_symbol) {4358r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4359r_result.class_name = class_name;4360r_result.class_member = p_symbol;4361return OK;4362}4363}4364class_name = ClassDB::get_parent_class_nocheck(class_name);4365} while (class_name != StringName());4366}43674368const GDScriptParser::TypeNode *type_node = dynamic_cast<const GDScriptParser::TypeNode *>(context.node);4369if (type_node != nullptr && !type_node->type_chain.is_empty()) {4370StringName class_name = type_node->type_chain[0]->name;4371if (ScriptServer::is_global_class(class_name)) {4372class_name = ScriptServer::get_global_class_native_base(class_name);4373}4374do {4375List<StringName> enums;4376ClassDB::get_enum_list(class_name, &enums, true);4377for (const StringName &enum_name : enums) {4378if (enum_name == p_symbol) {4379r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4380r_result.class_name = class_name;4381r_result.class_member = p_symbol;4382return OK;4383}4384}4385class_name = ClassDB::get_parent_class_nocheck(class_name);4386} while (class_name != StringName());4387}43884389bool is_function = false;43904391switch (context.type) {4392case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: {4393GDScriptParser::DataType base_type;4394base_type.kind = GDScriptParser::DataType::BUILTIN;4395base_type.builtin_type = context.builtin_type;4396base_type.is_meta_type = true;4397if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4398return OK;4399}4400} break;4401case GDScriptParser::COMPLETION_SUPER: {4402if (context.current_class && context.current_function) {4403if (_lookup_symbol_from_base(context.current_class->base_type, context.current_function->info.name, r_result) == OK) {4404return OK;4405}4406}4407} break;4408case GDScriptParser::COMPLETION_SUPER_METHOD:4409case GDScriptParser::COMPLETION_METHOD:4410case GDScriptParser::COMPLETION_ASSIGN:4411case GDScriptParser::COMPLETION_CALL_ARGUMENTS:4412case GDScriptParser::COMPLETION_IDENTIFIER:4413case GDScriptParser::COMPLETION_PROPERTY_METHOD:4414case GDScriptParser::COMPLETION_SUBSCRIPT: {4415GDScriptParser::DataType base_type;4416if (context.current_class) {4417if (context.type != GDScriptParser::COMPLETION_SUPER_METHOD) {4418base_type = context.current_class->get_datatype();4419} else {4420base_type = context.current_class->base_type;4421}4422} else {4423break;4424}44254426if (!is_function && context.current_suite) {4427// Lookup local variables.4428const GDScriptParser::SuiteNode *suite = context.current_suite;4429while (suite) {4430if (suite->has_local(p_symbol)) {4431const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_symbol);44324433switch (local.type) {4434case GDScriptParser::SuiteNode::Local::UNDEFINED:4435return ERR_BUG;4436case GDScriptParser::SuiteNode::Local::CONSTANT:4437r_result.type = ScriptLanguage::LOOKUP_RESULT_LOCAL_CONSTANT;4438r_result.description = local.constant->doc_data.description;4439r_result.is_deprecated = local.constant->doc_data.is_deprecated;4440r_result.deprecated_message = local.constant->doc_data.deprecated_message;4441r_result.is_experimental = local.constant->doc_data.is_experimental;4442r_result.experimental_message = local.constant->doc_data.experimental_message;4443if (local.constant->initializer != nullptr) {4444r_result.value = GDScriptDocGen::docvalue_from_expression(local.constant->initializer);4445}4446break;4447case GDScriptParser::SuiteNode::Local::VARIABLE:4448r_result.type = ScriptLanguage::LOOKUP_RESULT_LOCAL_VARIABLE;4449r_result.description = local.variable->doc_data.description;4450r_result.is_deprecated = local.variable->doc_data.is_deprecated;4451r_result.deprecated_message = local.variable->doc_data.deprecated_message;4452r_result.is_experimental = local.variable->doc_data.is_experimental;4453r_result.experimental_message = local.variable->doc_data.experimental_message;4454if (local.variable->initializer != nullptr) {4455r_result.value = GDScriptDocGen::docvalue_from_expression(local.variable->initializer);4456}4457break;4458case GDScriptParser::SuiteNode::Local::PARAMETER:4459case GDScriptParser::SuiteNode::Local::FOR_VARIABLE:4460case GDScriptParser::SuiteNode::Local::PATTERN_BIND:4461r_result.type = ScriptLanguage::LOOKUP_RESULT_LOCAL_VARIABLE;4462break;4463}44644465GDScriptDocGen::doctype_from_gdtype(local.get_datatype(), r_result.doc_type, r_result.enumeration);44664467Error err = OK;4468r_result.script = GDScriptCache::get_shallow_script(base_type.script_path, err);4469r_result.script_path = base_type.script_path;4470r_result.location = local.start_line;4471return err;4472}4473suite = suite->parent_block;4474}4475}44764477if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4478return OK;4479}44804481if (!is_function) {4482if (ProjectSettings::get_singleton()->has_autoload(p_symbol)) {4483const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(p_symbol);4484if (autoload.is_singleton) {4485String scr_path = autoload.path;4486if (!scr_path.ends_with(".gd")) {4487// Not a script, try find the script anyway, may have some success.4488scr_path = scr_path.get_basename() + ".gd";4489}44904491if (FileAccess::exists(scr_path)) {4492r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4493r_result.class_name = p_symbol;4494r_result.script = ResourceLoader::load(scr_path);4495r_result.script_path = scr_path;4496r_result.location = 0;4497return OK;4498}4499}4500}45014502if (ScriptServer::is_global_class(p_symbol)) {4503const String scr_path = ScriptServer::get_global_class_path(p_symbol);4504const Ref<Script> scr = ResourceLoader::load(scr_path);4505if (scr.is_null()) {4506return ERR_BUG;4507}4508r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4509r_result.class_name = scr->get_doc_class_name();4510r_result.script = scr;4511r_result.script_path = scr_path;4512r_result.location = 0;4513return OK;4514}45154516const HashMap<StringName, int> &global_map = GDScriptLanguage::get_singleton()->get_global_map();4517if (global_map.has(p_symbol)) {4518Variant value = GDScriptLanguage::get_singleton()->get_global_array()[global_map[p_symbol]];4519if (value.get_type() == Variant::OBJECT) {4520const Object *obj = value;4521if (obj) {4522if (Object::cast_to<GDScriptNativeClass>(obj)) {4523r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4524r_result.class_name = Object::cast_to<GDScriptNativeClass>(obj)->get_name();4525} else {4526r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4527r_result.class_name = obj->get_class();4528}4529return OK;4530}4531}4532}45334534if (CoreConstants::is_global_enum(p_symbol)) {4535r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4536r_result.class_name = "@GlobalScope";4537r_result.class_member = p_symbol;4538return OK;4539}45404541if (CoreConstants::is_global_constant(p_symbol)) {4542r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4543r_result.class_name = "@GlobalScope";4544r_result.class_member = p_symbol;4545return OK;4546}45474548if (Variant::has_utility_function(p_symbol)) {4549r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4550r_result.class_name = "@GlobalScope";4551r_result.class_member = p_symbol;4552return OK;4553}4554}4555} break;4556case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD:4557case GDScriptParser::COMPLETION_ATTRIBUTE: {4558if (context.node->type != GDScriptParser::Node::SUBSCRIPT) {4559break;4560}4561const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(context.node);4562if (!subscript->is_attribute) {4563break;4564}4565GDScriptCompletionIdentifier base;45664567bool found_type = _get_subscript_type(context, subscript, base.type);4568if (!found_type && !_guess_expression_type(context, subscript->base, base)) {4569break;4570}45714572if (_lookup_symbol_from_base(base.type, p_symbol, r_result) == OK) {4573return OK;4574}4575} break;4576case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {4577if (context.node == nullptr || context.node->type != GDScriptParser::Node::TYPE) {4578break;4579}4580const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(context.node);45814582GDScriptParser::DataType base_type;4583const GDScriptParser::IdentifierNode *prev = nullptr;4584for (const GDScriptParser::IdentifierNode *E : type->type_chain) {4585if (E->name == p_symbol && prev != nullptr) {4586base_type = prev->get_datatype();4587break;4588}4589prev = E;4590}4591if (base_type.kind != GDScriptParser::DataType::CLASS) {4592GDScriptCompletionIdentifier base;4593if (!_guess_expression_type(context, prev, base)) {4594break;4595}4596base_type = base.type;4597}45984599if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4600return OK;4601}4602} break;4603case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {4604GDScriptParser::DataType base_type = context.current_class->base_type;46054606if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4607return OK;4608}4609} break;4610case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE:4611case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID:4612case GDScriptParser::COMPLETION_TYPE_NAME: {4613GDScriptParser::DataType base_type = context.current_class->get_datatype();46144615if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4616return OK;4617}4618} break;4619case GDScriptParser::COMPLETION_ANNOTATION: {4620const String annotation_symbol = "@" + p_symbol;4621if (parser.annotation_exists(annotation_symbol)) {4622r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION;4623r_result.class_name = "@GDScript";4624r_result.class_member = annotation_symbol;4625return OK;4626}4627} break;4628default: {4629}4630}46314632return ERR_CANT_RESOLVE;4633}46344635#endif // TOOLS_ENABLED463646374638