Path: blob/master/modules/gdscript/gdscript_editor.cpp
11351 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("prefer_slider", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);928slider3.insert_text = slider3.display.quote(p_quote_style);929r_result.insert(slider3.display, slider3);930ScriptLanguage::CodeCompletionOption slider4("hide_control", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);931slider4.insert_text = slider4.display.quote(p_quote_style);932r_result.insert(slider4.display, slider4);933}934} else if (p_annotation->name == SNAME("@export_exp_easing")) {935if (p_argument == 0 || p_argument == 1) {936// Easing hint.937ScriptLanguage::CodeCompletionOption hint1("attenuation", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);938hint1.insert_text = hint1.display.quote(p_quote_style);939r_result.insert(hint1.display, hint1);940ScriptLanguage::CodeCompletionOption hint2("inout", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);941hint2.insert_text = hint2.display.quote(p_quote_style);942r_result.insert(hint2.display, hint2);943}944} else if (p_annotation->name == SNAME("@export_node_path")) {945ScriptLanguage::CodeCompletionOption node("Node", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);946node.insert_text = node.display.quote(p_quote_style);947r_result.insert(node.display, node);948949LocalVector<StringName> native_classes;950ClassDB::get_inheriters_from_class("Node", native_classes);951for (const StringName &E : native_classes) {952if (!ClassDB::is_class_exposed(E)) {953continue;954}955ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);956option.insert_text = option.display.quote(p_quote_style);957r_result.insert(option.display, option);958}959960LocalVector<StringName> global_script_classes;961ScriptServer::get_global_class_list(global_script_classes);962for (const StringName &class_name : global_script_classes) {963if (!ClassDB::is_parent_class(ScriptServer::get_global_class_native_base(class_name), "Node")) {964continue;965}966ScriptLanguage::CodeCompletionOption option(class_name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);967option.insert_text = option.display.quote(p_quote_style);968r_result.insert(option.display, option);969}970} else if (p_annotation->name == SNAME("@export_tool_button")) {971if (p_argument == 1) {972const Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme();973if (theme.is_valid()) {974List<StringName> icon_list;975theme->get_icon_list(EditorStringName(EditorIcons), &icon_list);976for (const StringName &E : icon_list) {977ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);978option.insert_text = option.display.quote(p_quote_style);979r_result.insert(option.display, option);980}981}982}983} else if (p_annotation->name == SNAME("@export_custom")) {984switch (p_argument) {985case 0: {986static HashMap<StringName, int64_t> items;987if (unlikely(items.is_empty())) {988CoreConstants::get_enum_values(SNAME("PropertyHint"), &items);989}990for (const KeyValue<StringName, int64_t> &item : items) {991ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);992r_result.insert(option.display, option);993}994} break;995case 2: {996static HashMap<StringName, int64_t> items;997if (unlikely(items.is_empty())) {998CoreConstants::get_enum_values(SNAME("PropertyUsageFlags"), &items);999}1000for (const KeyValue<StringName, int64_t> &item : items) {1001ScriptLanguage::CodeCompletionOption option(item.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);1002r_result.insert(option.display, option);1003}1004} break;1005}1006} else if (p_annotation->name == SNAME("@warning_ignore") || p_annotation->name == SNAME("@warning_ignore_start") || p_annotation->name == SNAME("@warning_ignore_restore")) {1007for (int warning_code = 0; warning_code < GDScriptWarning::WARNING_MAX; warning_code++) {1008#ifndef DISABLE_DEPRECATED1009if (warning_code >= GDScriptWarning::FIRST_DEPRECATED_WARNING) {1010break; // Don't suggest deprecated warnings as they are never produced.1011}1012#endif1013ScriptLanguage::CodeCompletionOption warning(GDScriptWarning::get_name_from_code((GDScriptWarning::Code)warning_code).to_lower(), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1014warning.insert_text = warning.display.quote(p_quote_style);1015r_result.insert(warning.display, warning);1016}1017} else if (p_annotation->name == SNAME("@rpc")) {1018if (p_argument == 0 || p_argument == 1 || p_argument == 2) {1019static const char *options[7] = { "call_local", "call_remote", "any_peer", "authority", "reliable", "unreliable", "unreliable_ordered" };1020for (int i = 0; i < 7; i++) {1021ScriptLanguage::CodeCompletionOption option(options[i], ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1022option.insert_text = option.display.quote(p_quote_style);1023r_result.insert(option.display, option);1024}1025}1026}1027}10281029static void _find_built_in_variants(HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, bool exclude_nil = false) {1030for (int i = 0; i < Variant::VARIANT_MAX; i++) {1031if (!exclude_nil && Variant::Type(i) == Variant::Type::NIL) {1032ScriptLanguage::CodeCompletionOption option("null", ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1033r_result.insert(option.display, option);1034} else {1035ScriptLanguage::CodeCompletionOption option(Variant::get_type_name(Variant::Type(i)), ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1036r_result.insert(option.display, option);1037}1038}1039}10401041static void _find_global_enums(HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {1042List<StringName> global_enums;1043CoreConstants::get_global_enums(&global_enums);1044for (const StringName &enum_name : global_enums) {1045ScriptLanguage::CodeCompletionOption option(enum_name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_OTHER);1046r_result.insert(option.display, option);1047}1048}10491050static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {1051// Built-in Variant Types1052_find_built_in_variants(r_result, true);10531054LocalVector<StringName> native_types;1055ClassDB::get_class_list(native_types);1056for (const StringName &type : native_types) {1057if (ClassDB::is_class_exposed(type) && !Engine::get_singleton()->has_singleton(type)) {1058ScriptLanguage::CodeCompletionOption option(type, ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1059r_result.insert(option.display, option);1060}1061}10621063// TODO: Unify with _find_identifiers_in_class.1064if (p_context.current_class) {1065if (!p_inherit_only && p_context.current_class->base_type.is_set()) {1066// Native enums from base class1067List<StringName> enums;1068ClassDB::get_enum_list(p_context.current_class->base_type.native_type, &enums);1069for (const StringName &E : enums) {1070ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM);1071r_result.insert(option.display, option);1072}1073}1074// Check current class for potential types.1075// TODO: Also check classes the current class inherits from.1076const GDScriptParser::ClassNode *current = p_context.current_class;1077int location_offset = 0;1078while (current) {1079for (int i = 0; i < current->members.size(); i++) {1080const GDScriptParser::ClassNode::Member &member = current->members[i];1081switch (member.type) {1082case GDScriptParser::ClassNode::Member::CLASS: {1083ScriptLanguage::CodeCompletionOption option(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL + location_offset);1084r_result.insert(option.display, option);1085} break;1086case GDScriptParser::ClassNode::Member::ENUM: {1087if (!p_inherit_only) {1088ScriptLanguage::CodeCompletionOption option(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, ScriptLanguage::LOCATION_LOCAL + location_offset);1089r_result.insert(option.display, option);1090}1091} break;1092case GDScriptParser::ClassNode::Member::CONSTANT: {1093if (member.constant->get_datatype().is_meta_type) {1094ScriptLanguage::CodeCompletionOption option(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_LOCAL + location_offset);1095r_result.insert(option.display, option);1096}1097} break;1098default:1099break;1100}1101}1102location_offset += 1;1103current = current->outer;1104}1105}11061107// Global scripts1108LocalVector<StringName> global_classes;1109ScriptServer::get_global_class_list(global_classes);1110for (const StringName &class_name : global_classes) {1111ScriptLanguage::CodeCompletionOption option(class_name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);1112r_result.insert(option.display, option);1113}11141115// Global enums1116if (!p_inherit_only) {1117_find_global_enums(r_result);1118}11191120// Autoload singletons1121HashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();11221123for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : autoloads) {1124const ProjectSettings::AutoloadInfo &info = E.value;1125if (!info.is_singleton || info.path.get_extension().to_lower() != "gd") {1126continue;1127}1128ScriptLanguage::CodeCompletionOption option(info.name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);1129r_result.insert(option.display, option);1130}1131}11321133static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result, int p_recursion_depth = 0) {1134for (int i = 0; i < p_suite->locals.size(); i++) {1135ScriptLanguage::CodeCompletionOption option;1136int location = p_recursion_depth == 0 ? ScriptLanguage::LOCATION_LOCAL : (p_recursion_depth | ScriptLanguage::LOCATION_PARENT_MASK);1137if (p_suite->locals[i].type == GDScriptParser::SuiteNode::Local::CONSTANT) {1138option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1139option.default_value = p_suite->locals[i].constant->initializer->reduced_value;1140} else {1141option = ScriptLanguage::CodeCompletionOption(p_suite->locals[i].name, ScriptLanguage::CODE_COMPLETION_KIND_VARIABLE, location);1142}1143r_result.insert(option.display, option);1144}1145if (p_suite->parent_block) {1146_find_identifiers_in_suite(p_suite->parent_block, r_result, p_recursion_depth + 1);1147}1148}11491150static 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);11511152static 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) {1153ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);11541155if (!p_parent_only) {1156bool outer = false;1157const GDScriptParser::ClassNode *clss = p_class;1158int classes_processed = 0;1159while (clss) {1160for (int i = 0; i < clss->members.size(); i++) {1161const int location = p_recursion_depth == 0 ? classes_processed : (p_recursion_depth | ScriptLanguage::LOCATION_PARENT_MASK);1162const GDScriptParser::ClassNode::Member &member = clss->members[i];1163ScriptLanguage::CodeCompletionOption option;1164switch (member.type) {1165case GDScriptParser::ClassNode::Member::VARIABLE:1166if (p_types_only || p_only_functions || outer || (p_static && !member.variable->is_static)) {1167continue;1168}1169option = ScriptLanguage::CodeCompletionOption(member.variable->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1170break;1171case GDScriptParser::ClassNode::Member::CONSTANT:1172if ((p_types_only && !member.constant->datatype.is_meta_type) || p_only_functions) {1173continue;1174}1175if (r_result.has(member.constant->identifier->name)) {1176continue;1177}1178option = ScriptLanguage::CodeCompletionOption(member.constant->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1179if (member.constant->initializer) {1180option.default_value = member.constant->initializer->reduced_value;1181}1182break;1183case GDScriptParser::ClassNode::Member::CLASS:1184if (p_only_functions) {1185continue;1186}1187option = ScriptLanguage::CodeCompletionOption(member.m_class->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, location);1188break;1189case GDScriptParser::ClassNode::Member::ENUM_VALUE:1190if (p_types_only || p_only_functions) {1191continue;1192}1193option = ScriptLanguage::CodeCompletionOption(member.enum_value.identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1194break;1195case GDScriptParser::ClassNode::Member::ENUM:1196if (p_only_functions) {1197continue;1198}1199option = ScriptLanguage::CodeCompletionOption(member.m_enum->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);1200break;1201case GDScriptParser::ClassNode::Member::FUNCTION:1202if (p_types_only || outer || (p_static && !member.function->is_static) || member.function->identifier->name.operator String().begins_with("@")) {1203continue;1204}1205option = ScriptLanguage::CodeCompletionOption(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1206if (p_add_braces) {1207if (member.function->parameters.size() > 0 || (member.function->info.flags & METHOD_FLAG_VARARG)) {1208option.insert_text += "(";1209option.display += U"(\u2026)";1210} else {1211option.insert_text += "()";1212option.display += "()";1213}1214}1215break;1216case GDScriptParser::ClassNode::Member::SIGNAL:1217if (p_types_only || p_only_functions || outer || p_static) {1218continue;1219}1220option = ScriptLanguage::CodeCompletionOption(member.signal->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);1221break;1222case GDScriptParser::ClassNode::Member::GROUP:1223break; // No-op, but silences warnings.1224case GDScriptParser::ClassNode::Member::UNDEFINED:1225break;1226}1227r_result.insert(option.display, option);1228}1229if (p_types_only) {1230break; // Otherwise, it will fill the results with types from the outer class (which is undesired for that case).1231}12321233outer = true;1234clss = clss->outer;1235classes_processed++;1236}1237}12381239// Parents.1240GDScriptCompletionIdentifier base_type;1241base_type.type = p_class->base_type;1242base_type.type.is_meta_type = p_static;12431244_find_identifiers_in_base(base_type, p_only_functions, p_types_only, p_add_braces, r_result, p_recursion_depth + 1);1245}12461247static 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) {1248ERR_FAIL_COND(p_recursion_depth > COMPLETION_RECURSION_LIMIT);12491250GDScriptParser::DataType base_type = p_base.type;12511252if (!p_types_only && base_type.is_meta_type && base_type.kind != GDScriptParser::DataType::BUILTIN && base_type.kind != GDScriptParser::DataType::ENUM) {1253ScriptLanguage::CodeCompletionOption option("new", ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, ScriptLanguage::LOCATION_LOCAL);1254if (p_add_braces) {1255option.insert_text += "(";1256option.display += U"(\u2026)";1257}1258r_result.insert(option.display, option);1259}12601261while (!base_type.has_no_type()) {1262switch (base_type.kind) {1263case GDScriptParser::DataType::CLASS: {1264_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);1265// This already finds all parent identifiers, so we are done.1266base_type = GDScriptParser::DataType();1267} break;1268case GDScriptParser::DataType::SCRIPT: {1269Ref<Script> scr = base_type.script_type;1270if (scr.is_valid()) {1271if (p_types_only) {1272// TODO: Need to implement Script::get_script_enum_list and retrieve the enum list from a script.1273} else if (!p_only_functions) {1274if (!base_type.is_meta_type) {1275List<PropertyInfo> members;1276scr->get_script_property_list(&members);1277for (const PropertyInfo &E : members) {1278if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {1279continue;1280}1281if (E.name.contains_char('/')) {1282continue;1283}1284int location = p_recursion_depth + _get_property_location(scr, E.name);1285ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1286r_result.insert(option.display, option);1287}12881289List<MethodInfo> signals;1290scr->get_script_signal_list(&signals);1291for (const MethodInfo &E : signals) {1292int location = p_recursion_depth + _get_signal_location(scr, E.name);1293ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);1294r_result.insert(option.display, option);1295}1296}1297HashMap<StringName, Variant> constants;1298scr->get_constants(&constants);1299for (const KeyValue<StringName, Variant> &E : constants) {1300int location = p_recursion_depth + _get_constant_location(scr, E.key);1301ScriptLanguage::CodeCompletionOption option(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1302r_result.insert(option.display, option);1303}1304}13051306if (!p_types_only) {1307List<MethodInfo> methods;1308scr->get_script_method_list(&methods);1309for (const MethodInfo &E : methods) {1310if (E.name.begins_with("@")) {1311continue;1312}1313int location = p_recursion_depth + _get_method_location(scr->get_class_name(), E.name);1314ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1315if (p_add_braces) {1316if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {1317option.insert_text += "(";1318option.display += U"(\u2026)";1319} else {1320option.insert_text += "()";1321option.display += "()";1322}1323}1324r_result.insert(option.display, option);1325}1326}13271328Ref<Script> base_script = scr->get_base_script();1329if (base_script.is_valid()) {1330base_type.script_type = base_script;1331} else {1332base_type.kind = GDScriptParser::DataType::NATIVE;1333base_type.builtin_type = Variant::OBJECT;1334base_type.native_type = scr->get_instance_base_type();1335}1336} else {1337return;1338}1339} break;1340case GDScriptParser::DataType::NATIVE: {1341StringName type = base_type.native_type;1342if (!ClassDB::class_exists(type)) {1343return;1344}13451346List<StringName> enums;1347ClassDB::get_enum_list(type, &enums);1348for (const StringName &E : enums) {1349int location = p_recursion_depth + _get_enum_location(type, E);1350ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);1351r_result.insert(option.display, option);1352}13531354if (p_types_only) {1355return;1356}13571358if (!p_only_functions) {1359List<String> constants;1360ClassDB::get_integer_constant_list(type, &constants);1361for (const String &E : constants) {1362int location = p_recursion_depth + _get_constant_location(type, StringName(E));1363ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1364r_result.insert(option.display, option);1365}13661367if (!base_type.is_meta_type || Engine::get_singleton()->has_singleton(type)) {1368List<PropertyInfo> pinfo;1369ClassDB::get_property_list(type, &pinfo);1370for (const PropertyInfo &E : pinfo) {1371if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {1372continue;1373}1374if (E.name.contains_char('/')) {1375continue;1376}1377int location = p_recursion_depth + _get_property_location(type, E.name);1378ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1379r_result.insert(option.display, option);1380}13811382List<MethodInfo> signals;1383ClassDB::get_signal_list(type, &signals);1384for (const MethodInfo &E : signals) {1385int location = p_recursion_depth + _get_signal_location(type, StringName(E.name));1386ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_SIGNAL, location);1387r_result.insert(option.display, option);1388}1389}1390}13911392bool only_static = base_type.is_meta_type && !Engine::get_singleton()->has_singleton(type);13931394List<MethodInfo> methods;1395ClassDB::get_method_list(type, &methods, false, true);1396for (const MethodInfo &E : methods) {1397if (only_static && (E.flags & METHOD_FLAG_STATIC) == 0) {1398continue;1399}1400if (E.name.begins_with("_")) {1401continue;1402}1403int location = p_recursion_depth + _get_method_location(type, E.name);1404ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1405if (p_add_braces) {1406if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {1407option.insert_text += "(";1408option.display += U"(\u2026)";1409} else {1410option.insert_text += "()";1411option.display += "()";1412}1413}1414r_result.insert(option.display, option);1415}1416return;1417} break;1418case GDScriptParser::DataType::ENUM: {1419if (p_types_only) {1420return;1421}14221423String type_str = base_type.native_type;14241425if (type_str.contains_char('.')) {1426StringName type = type_str.get_slicec('.', 0);1427StringName type_enum = base_type.enum_type;14281429List<StringName> enum_values;14301431ClassDB::get_enum_constants(type, type_enum, &enum_values);14321433for (const StringName &E : enum_values) {1434int location = p_recursion_depth + _get_enum_constant_location(type, E);1435ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1436r_result.insert(option.display, option);1437}1438} else if (CoreConstants::is_global_enum(base_type.enum_type)) {1439HashMap<StringName, int64_t> enum_values;1440CoreConstants::get_enum_values(base_type.enum_type, &enum_values);14411442for (const KeyValue<StringName, int64_t> &enum_value : enum_values) {1443int location = p_recursion_depth + ScriptLanguage::LOCATION_OTHER;1444ScriptLanguage::CodeCompletionOption option(enum_value.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT, location);1445r_result.insert(option.display, option);1446}1447}1448}1449[[fallthrough]];1450case GDScriptParser::DataType::BUILTIN: {1451if (p_types_only) {1452return;1453}14541455Callable::CallError err;1456Variant tmp;1457Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);1458if (err.error != Callable::CallError::CALL_OK) {1459return;1460}14611462int location = ScriptLanguage::LOCATION_OTHER;14631464if (!p_only_functions) {1465List<PropertyInfo> members;1466if (p_base.value.get_type() != Variant::NIL) {1467p_base.value.get_property_list(&members);1468} else {1469tmp.get_property_list(&members);1470}14711472for (const PropertyInfo &E : members) {1473if (E.usage & (PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_INTERNAL)) {1474continue;1475}1476if (!String(E.name).contains_char('/')) {1477ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, location);1478if (base_type.kind == GDScriptParser::DataType::ENUM) {1479// Sort enum members in their declaration order.1480location += 1;1481}1482if (GDScriptParser::theme_color_names.has(E.name)) {1483option.theme_color_name = GDScriptParser::theme_color_names[E.name];1484}1485r_result.insert(option.display, option);1486}1487}1488}14891490List<MethodInfo> methods;1491tmp.get_method_list(&methods);1492for (const MethodInfo &E : methods) {1493if (base_type.kind == GDScriptParser::DataType::ENUM && base_type.is_meta_type && !(E.flags & METHOD_FLAG_CONST)) {1494// Enum types are static and cannot change, therefore we skip non-const dictionary methods.1495continue;1496}1497ScriptLanguage::CodeCompletionOption option(E.name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION, location);1498if (p_add_braces) {1499if (E.arguments.size() || (E.flags & METHOD_FLAG_VARARG)) {1500option.insert_text += "(";1501option.display += U"(\u2026)";1502} else {1503option.insert_text += "()";1504option.display += "()";1505}1506}1507r_result.insert(option.display, option);1508}15091510return;1511} break;1512default: {1513return;1514} break;1515}1516}1517}15181519static 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) {1520if (!p_only_functions && p_context.current_suite) {1521// This includes function parameters, since they are also locals.1522_find_identifiers_in_suite(p_context.current_suite, r_result);1523}15241525if (p_context.current_class) {1526_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);1527}15281529List<StringName> functions;1530GDScriptUtilityFunctions::get_function_list(&functions);15311532for (const StringName &E : functions) {1533MethodInfo function = GDScriptUtilityFunctions::get_function_info(E);1534ScriptLanguage::CodeCompletionOption option(String(E), ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);1535if (p_add_braces) {1536if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {1537option.insert_text += "(";1538option.display += U"(\u2026)";1539} else {1540option.insert_text += "()";1541option.display += "()";1542}1543}1544r_result.insert(option.display, option);1545}15461547if (p_only_functions) {1548return;1549}15501551_find_built_in_variants(r_result);15521553static const char *_keywords[] = {1554"true", "false", "PI", "TAU", "INF", "NAN", "null", "self", "super",1555"break", "breakpoint", "continue", "pass", "return",1556nullptr1557};15581559const char **kw = _keywords;1560while (*kw) {1561ScriptLanguage::CodeCompletionOption option(*kw, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1562r_result.insert(option.display, option);1563kw++;1564}15651566static const char *_keywords_with_space[] = {1567"and", "not", "or", "in", "as", "class", "class_name", "extends", "is", "func", "signal", "await",1568"const", "enum", "static", "var", "if", "elif", "else", "for", "match", "when", "while",1569nullptr1570};15711572const char **kws = _keywords_with_space;1573while (*kws) {1574ScriptLanguage::CodeCompletionOption option(*kws, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);1575option.insert_text += " ";1576r_result.insert(option.display, option);1577kws++;1578}15791580static const char *_keywords_with_args[] = {1581"assert", "preload",1582nullptr1583};15841585const char **kwa = _keywords_with_args;1586while (*kwa) {1587ScriptLanguage::CodeCompletionOption option(*kwa, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);1588if (p_add_braces) {1589option.insert_text += "(";1590option.display += U"(\u2026)";1591}1592r_result.insert(option.display, option);1593kwa++;1594}15951596List<StringName> utility_func_names;1597Variant::get_utility_function_list(&utility_func_names);15981599for (const StringName &util_func_name : utility_func_names) {1600ScriptLanguage::CodeCompletionOption option(util_func_name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);1601if (p_add_braces) {1602option.insert_text += "(";1603option.display += U"(\u2026)"; // As all utility functions contain an argument or more, this is hardcoded here.1604}1605r_result.insert(option.display, option);1606}16071608for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {1609if (!E.value.is_singleton) {1610continue;1611}1612ScriptLanguage::CodeCompletionOption option(E.key, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);1613r_result.insert(option.display, option);1614}16151616// Native classes and global constants.1617for (const KeyValue<StringName, int> &E : GDScriptLanguage::get_singleton()->get_global_map()) {1618ScriptLanguage::CodeCompletionOption option;1619if (ClassDB::class_exists(E.key) || Engine::get_singleton()->has_singleton(E.key)) {1620option = ScriptLanguage::CodeCompletionOption(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CLASS);1621} else {1622option = ScriptLanguage::CodeCompletionOption(E.key.operator String(), ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);1623}1624r_result.insert(option.display, option);1625}16261627// Global enums1628_find_global_enums(r_result);16291630// Global classes1631LocalVector<StringName> global_classes;1632ScriptServer::get_global_class_list(global_classes);1633for (const StringName &class_name : global_classes) {1634ScriptLanguage::CodeCompletionOption option(class_name, ScriptLanguage::CODE_COMPLETION_KIND_CLASS, ScriptLanguage::LOCATION_OTHER_USER_CODE);1635r_result.insert(option.display, option);1636}1637}16381639static GDScriptCompletionIdentifier _type_from_variant(const Variant &p_value, GDScriptParser::CompletionContext &p_context) {1640GDScriptCompletionIdentifier ci;1641ci.value = p_value;1642ci.type.is_constant = true;1643ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1644ci.type.kind = GDScriptParser::DataType::BUILTIN;1645ci.type.builtin_type = p_value.get_type();16461647if (ci.type.builtin_type == Variant::OBJECT) {1648Object *obj = p_value.operator Object *();1649if (!obj) {1650return ci;1651}1652ci.type.native_type = obj->get_class_name();1653Ref<Script> scr = p_value;1654if (scr.is_valid()) {1655ci.type.is_meta_type = true;1656} else {1657ci.type.is_meta_type = false;1658scr = obj->get_script();1659}1660if (scr.is_valid()) {1661ci.type.script_path = scr->get_path();1662ci.type.script_type = scr;1663ci.type.native_type = scr->get_instance_base_type();1664ci.type.kind = GDScriptParser::DataType::SCRIPT;16651666if (scr->get_path().ends_with(".gd")) {1667Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(scr->get_path());1668if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) {1669ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1670ci.type.class_type = parser->get_parser()->get_tree();1671ci.type.kind = GDScriptParser::DataType::CLASS;1672return ci;1673}1674}1675} else {1676ci.type.kind = GDScriptParser::DataType::NATIVE;1677}1678}16791680return ci;1681}16821683static GDScriptCompletionIdentifier _type_from_property(const PropertyInfo &p_property) {1684GDScriptCompletionIdentifier ci;16851686if (p_property.type == Variant::NIL) {1687// Variant1688ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1689ci.type.kind = GDScriptParser::DataType::VARIANT;1690return ci;1691}16921693if (p_property.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {1694ci.enumeration = p_property.class_name;1695}16961697ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1698ci.type.builtin_type = p_property.type;1699if (p_property.type == Variant::OBJECT) {1700if (ScriptServer::is_global_class(p_property.class_name)) {1701ci.type.kind = GDScriptParser::DataType::SCRIPT;1702ci.type.script_path = ScriptServer::get_global_class_path(p_property.class_name);1703ci.type.native_type = ScriptServer::get_global_class_native_base(p_property.class_name);17041705Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_property.class_name));1706if (scr.is_valid()) {1707ci.type.script_type = scr;1708}1709} else {1710ci.type.kind = GDScriptParser::DataType::NATIVE;1711ci.type.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;1712}1713} else {1714ci.type.kind = GDScriptParser::DataType::BUILTIN;1715}1716return ci;1717}17181719static GDScriptCompletionIdentifier _callable_type_from_method_info(const MethodInfo &p_method) {1720GDScriptCompletionIdentifier ci;1721ci.type.kind = GDScriptParser::DataType::BUILTIN;1722ci.type.builtin_type = Variant::CALLABLE;1723ci.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1724ci.type.is_constant = true;1725ci.type.method_info = p_method;1726return ci;1727}17281729#define MAX_COMPLETION_RECURSION 1001730struct RecursionCheck {1731int *counter;1732_FORCE_INLINE_ bool check() {1733return (*counter) > MAX_COMPLETION_RECURSION;1734}1735RecursionCheck(int *p_counter) :1736counter(p_counter) {1737(*counter)++;1738}1739~RecursionCheck() {1740(*counter)--;1741}1742};17431744static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type);1745static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type);1746static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type);17471748static bool _is_expression_named_identifier(const GDScriptParser::ExpressionNode *p_expression, const StringName &p_name) {1749if (p_expression) {1750switch (p_expression->type) {1751case GDScriptParser::Node::IDENTIFIER: {1752const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);1753if (id->name == p_name) {1754return true;1755}1756} break;1757case GDScriptParser::Node::CAST: {1758const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);1759return _is_expression_named_identifier(cn->operand, p_name);1760} break;1761default:1762break;1763}1764}17651766return false;1767}17681769// Creates a map of exemplary results for some functions that return a structured dictionary.1770// Setting this example as value allows autocompletion to suggest the specific keys in some cases.1771static HashMap<String, Dictionary> make_structure_samples() {1772HashMap<String, Dictionary> res;1773const Array arr;17741775{1776Dictionary d;1777d.set("major", 0);1778d.set("minor", 0);1779d.set("patch", 0);1780d.set("hex", 0);1781d.set("status", String());1782d.set("build", String());1783d.set("hash", String());1784d.set("timestamp", 0);1785d.set("string", String());1786res["Engine::get_version_info"] = d;1787}17881789{1790Dictionary d;1791d.set("lead_developers", arr);1792d.set("founders", arr);1793d.set("project_managers", arr);1794d.set("developers", arr);1795res["Engine::get_author_info"] = d;1796}17971798{1799Dictionary d;1800d.set("platinum_sponsors", arr);1801d.set("gold_sponsors", arr);1802d.set("silver_sponsors", arr);1803d.set("bronze_sponsors", arr);1804d.set("mini_sponsors", arr);1805d.set("gold_donors", arr);1806d.set("silver_donors", arr);1807d.set("bronze_donors", arr);1808res["Engine::get_donor_info"] = d;1809}18101811{1812Dictionary d;1813d.set("physical", -1);1814d.set("free", -1);1815d.set("available", -1);1816d.set("stack", -1);1817res["OS::get_memory_info"] = d;1818}18191820{1821Dictionary d;1822d.set("year", 0);1823d.set("month", 0);1824d.set("day", 0);1825d.set("weekday", 0);1826d.set("hour", 0);1827d.set("minute", 0);1828d.set("second", 0);1829d.set("dst", 0);1830res["Time::get_datetime_dict_from_system"] = d;1831}18321833{1834Dictionary d;1835d.set("year", 0);1836d.set("month", 0);1837d.set("day", 0);1838d.set("weekday", 0);1839d.set("hour", 0);1840d.set("minute", 0);1841d.set("second", 0);1842res["Time::get_datetime_dict_from_unix_time"] = d;1843}18441845{1846Dictionary d;1847d.set("year", 0);1848d.set("month", 0);1849d.set("day", 0);1850d.set("weekday", 0);1851res["Time::get_date_dict_from_system"] = d;1852res["Time::get_date_dict_from_unix_time"] = d;1853}18541855{1856Dictionary d;1857d.set("hour", 0);1858d.set("minute", 0);1859d.set("second", 0);1860res["Time::get_time_dict_from_system"] = d;1861res["Time::get_time_dict_from_unix_time"] = d;1862}18631864{1865Dictionary d;1866d.set("bias", 0);1867d.set("name", String());1868res["Time::get_time_zone_from_system"] = d;1869}18701871return res;1872}18731874static const HashMap<String, Dictionary> structure_examples = make_structure_samples();18751876static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) {1877bool found = false;18781879if (p_expression == nullptr) {1880return false;1881}18821883static int recursion_depth = 0;1884RecursionCheck recursion(&recursion_depth);1885if (unlikely(recursion.check())) {1886ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");1887}18881889if (p_expression->is_constant) {1890// Already has a value, so just use that.1891r_type = _type_from_variant(p_expression->reduced_value, p_context);1892switch (p_expression->get_datatype().kind) {1893case GDScriptParser::DataType::ENUM:1894case GDScriptParser::DataType::CLASS:1895r_type.type = p_expression->get_datatype();1896break;1897default:1898break;1899}1900found = true;1901} else {1902switch (p_expression->type) {1903case GDScriptParser::Node::IDENTIFIER: {1904const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(p_expression);1905found = _guess_identifier_type(p_context, id, r_type);1906} break;1907case GDScriptParser::Node::DICTIONARY: {1908// Try to recreate the dictionary.1909const GDScriptParser::DictionaryNode *dn = static_cast<const GDScriptParser::DictionaryNode *>(p_expression);1910Dictionary d;1911bool full = true;1912for (int i = 0; i < dn->elements.size(); i++) {1913GDScriptCompletionIdentifier key;1914if (_guess_expression_type(p_context, dn->elements[i].key, key)) {1915if (!key.type.is_constant) {1916full = false;1917break;1918}1919GDScriptCompletionIdentifier value;1920if (_guess_expression_type(p_context, dn->elements[i].value, value)) {1921if (!value.type.is_constant) {1922full = false;1923break;1924}1925d[key.value] = value.value;1926} else {1927full = false;1928break;1929}1930} else {1931full = false;1932break;1933}1934}1935if (full) {1936r_type.value = d;1937r_type.type.is_constant = true;1938}1939r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1940r_type.type.kind = GDScriptParser::DataType::BUILTIN;1941r_type.type.builtin_type = Variant::DICTIONARY;1942found = true;1943} break;1944case GDScriptParser::Node::ARRAY: {1945// Try to recreate the array1946const GDScriptParser::ArrayNode *an = static_cast<const GDScriptParser::ArrayNode *>(p_expression);1947Array a;1948bool full = true;1949a.resize(an->elements.size());1950for (int i = 0; i < an->elements.size(); i++) {1951GDScriptCompletionIdentifier value;1952if (_guess_expression_type(p_context, an->elements[i], value)) {1953if (value.type.is_constant) {1954a[i] = value.value;1955} else {1956full = false;1957break;1958}1959} else {1960full = false;1961break;1962}1963}1964if (full) {1965// If not fully constant, setting this value is detrimental to the inference.1966r_type.value = a;1967r_type.type.is_constant = true;1968}1969r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;1970r_type.type.kind = GDScriptParser::DataType::BUILTIN;1971r_type.type.builtin_type = Variant::ARRAY;1972found = true;1973} break;1974case GDScriptParser::Node::CAST: {1975const GDScriptParser::CastNode *cn = static_cast<const GDScriptParser::CastNode *>(p_expression);1976GDScriptCompletionIdentifier value;1977if (_guess_expression_type(p_context, cn->operand, r_type)) {1978r_type.type = cn->get_datatype();1979found = true;1980}1981} break;1982case GDScriptParser::Node::CALL: {1983const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_expression);1984GDScriptParser::CompletionContext c = p_context;1985c.current_line = call->start_line;19861987GDScriptParser::Node::Type callee_type = call->get_callee_type();19881989GDScriptCompletionIdentifier base;1990if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super) {1991// Simple call, so base is 'self'.1992if (p_context.current_class) {1993if (call->is_super) {1994base.type = p_context.current_class->base_type;1995base.value = p_context.base;1996} else {1997base.type.kind = GDScriptParser::DataType::CLASS;1998base.type.type_source = GDScriptParser::DataType::INFERRED;1999base.type.is_constant = true;2000base.type.class_type = p_context.current_class;2001base.value = p_context.base;2002}2003} else {2004break;2005}2006} else if (callee_type == GDScriptParser::Node::SUBSCRIPT && static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->is_attribute) {2007if (!_guess_expression_type(c, static_cast<const GDScriptParser::SubscriptNode *>(call->callee)->base, base)) {2008found = false;2009break;2010}2011} else {2012break;2013}20142015// Apply additional behavior aware inference that the analyzer can't do.2016if (base.type.is_set()) {2017// Maintain type for duplicate methods.2018if (call->function_name == SNAME("duplicate")) {2019if (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")))) {2020r_type.type = base.type;2021found = true;2022break;2023}2024}20252026// Simulate generics for some typed array methods.2027if (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"))) {2028r_type.type = base.type.get_container_element_type(0);2029found = true;2030break;2031}20322033// Insert example values for functions which a structured dictionary response.2034if (!base.type.is_meta_type) {2035const Dictionary *example = structure_examples.getptr(base.type.native_type.operator String() + "::" + call->function_name);2036if (example != nullptr) {2037r_type = _type_from_variant(*example, p_context);2038found = true;2039break;2040}2041}2042}20432044if (!found) {2045found = _guess_method_return_type_from_base(c, base, call->function_name, r_type);2046}2047} break;2048case GDScriptParser::Node::SUBSCRIPT: {2049const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(p_expression);2050if (subscript->is_attribute) {2051GDScriptParser::CompletionContext c = p_context;2052c.current_line = subscript->start_line;20532054GDScriptCompletionIdentifier base;2055if (!_guess_expression_type(c, subscript->base, base)) {2056found = false;2057break;2058}20592060if (base.value.get_type() == Variant::DICTIONARY && base.value.operator Dictionary().has(String(subscript->attribute->name))) {2061Variant value = base.value.operator Dictionary()[String(subscript->attribute->name)];2062r_type = _type_from_variant(value, p_context);2063found = true;2064break;2065}20662067const GDScriptParser::DictionaryNode *dn = nullptr;2068if (subscript->base->type == GDScriptParser::Node::DICTIONARY) {2069dn = static_cast<const GDScriptParser::DictionaryNode *>(subscript->base);2070} else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::DICTIONARY) {2071dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);2072}20732074if (dn) {2075for (int i = 0; i < dn->elements.size(); i++) {2076GDScriptCompletionIdentifier key;2077if (!_guess_expression_type(c, dn->elements[i].key, key)) {2078continue;2079}2080if (key.value == String(subscript->attribute->name)) {2081r_type.assigned_expression = dn->elements[i].value;2082found = _guess_expression_type(c, dn->elements[i].value, r_type);2083break;2084}2085}2086}20872088if (!found) {2089found = _guess_identifier_type_from_base(c, base, subscript->attribute->name, r_type);2090}2091} else {2092if (subscript->index == nullptr) {2093found = false;2094break;2095}20962097GDScriptParser::CompletionContext c = p_context;2098c.current_line = subscript->start_line;20992100GDScriptCompletionIdentifier base;2101if (!_guess_expression_type(c, subscript->base, base)) {2102found = false;2103break;2104}21052106GDScriptCompletionIdentifier index;2107if (!_guess_expression_type(c, subscript->index, index)) {2108found = false;2109break;2110}21112112if (base.type.is_constant && index.type.is_constant) {2113if (base.value.get_type() == Variant::DICTIONARY) {2114Dictionary base_dict = base.value.operator Dictionary();2115if (base_dict.get_key_validator().test_validate(index.value) && base_dict.has(index.value)) {2116r_type = _type_from_variant(base_dict[index.value], p_context);2117found = true;2118break;2119}2120} else {2121bool valid;2122Variant value = base.value.get(index.value, &valid);2123if (valid) {2124r_type = _type_from_variant(value, p_context);2125found = true;2126break;2127}2128}2129}21302131// Look if it is a dictionary node.2132const GDScriptParser::DictionaryNode *dn = nullptr;2133if (subscript->base->type == GDScriptParser::Node::DICTIONARY) {2134dn = static_cast<const GDScriptParser::DictionaryNode *>(subscript->base);2135} else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::DICTIONARY) {2136dn = static_cast<const GDScriptParser::DictionaryNode *>(base.assigned_expression);2137}21382139if (dn) {2140for (int i = 0; i < dn->elements.size(); i++) {2141GDScriptCompletionIdentifier key;2142if (!_guess_expression_type(c, dn->elements[i].key, key)) {2143continue;2144}2145if (key.value == index.value) {2146r_type.assigned_expression = dn->elements[i].value;2147found = _guess_expression_type(p_context, dn->elements[i].value, r_type);2148break;2149}2150}2151}21522153// Look if it is an array node.2154if (!found && index.value.is_num()) {2155int idx = index.value;2156const GDScriptParser::ArrayNode *an = nullptr;2157if (subscript->base->type == GDScriptParser::Node::ARRAY) {2158an = static_cast<const GDScriptParser::ArrayNode *>(subscript->base);2159} else if (base.assigned_expression && base.assigned_expression->type == GDScriptParser::Node::ARRAY) {2160an = static_cast<const GDScriptParser::ArrayNode *>(base.assigned_expression);2161}21622163if (an && idx >= 0 && an->elements.size() > idx) {2164r_type.assigned_expression = an->elements[idx];2165found = _guess_expression_type(c, an->elements[idx], r_type);2166break;2167}2168}21692170// Look for valid indexing in other types2171if (!found && (index.value.is_string() || index.value.get_type() == Variant::NODE_PATH)) {2172StringName id = index.value;2173found = _guess_identifier_type_from_base(c, base, id, r_type);2174} else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) {2175Callable::CallError err;2176Variant base_val;2177Variant::construct(base.type.builtin_type, base_val, nullptr, 0, err);2178bool valid = false;2179Variant res = base_val.get(index.value, &valid);2180if (valid) {2181r_type = _type_from_variant(res, p_context);2182r_type.value = Variant();2183r_type.type.is_constant = false;2184found = true;2185}2186}2187}2188} break;2189case GDScriptParser::Node::BINARY_OPERATOR: {2190const GDScriptParser::BinaryOpNode *op = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);21912192if (op->variant_op == Variant::OP_MAX) {2193break;2194}21952196GDScriptParser::CompletionContext context = p_context;2197context.current_line = op->start_line;21982199GDScriptCompletionIdentifier p1;2200GDScriptCompletionIdentifier p2;22012202if (!_guess_expression_type(context, op->left_operand, p1)) {2203found = false;2204break;2205}22062207if (!_guess_expression_type(context, op->right_operand, p2)) {2208found = false;2209break;2210}22112212Callable::CallError ce;2213bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;2214Variant d1;2215Variant::construct(p1.type.builtin_type, d1, nullptr, 0, ce);2216Variant d2;2217Variant::construct(p2.type.builtin_type, d2, nullptr, 0, ce);22182219Variant v1 = (v1_use_value) ? p1.value : d1;2220bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;2221Variant v2 = (v2_use_value) ? p2.value : d2;2222// avoid potential invalid ops2223if ((op->variant_op == Variant::OP_DIVIDE || op->variant_op == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {2224v2 = 1;2225v2_use_value = false;2226}2227if (op->variant_op == Variant::OP_DIVIDE && v2.get_type() == Variant::FLOAT) {2228v2 = 1.0;2229v2_use_value = false;2230}22312232Variant res;2233bool valid;2234Variant::evaluate(op->variant_op, v1, v2, res, valid);2235if (!valid) {2236found = false;2237break;2238}2239r_type = _type_from_variant(res, p_context);2240if (!v1_use_value || !v2_use_value) {2241r_type.value = Variant();2242r_type.type.is_constant = false;2243}22442245found = true;2246} break;2247default:2248break;2249}2250}22512252// It may have found a null, but that's never useful2253if (found && r_type.type.kind == GDScriptParser::DataType::BUILTIN && r_type.type.builtin_type == Variant::NIL) {2254found = false;2255}22562257// If the found type was not fully analyzed we analyze it now.2258if (found && r_type.type.kind == GDScriptParser::DataType::CLASS && !r_type.type.class_type->resolved_body) {2259Error err;2260Ref<GDScriptParserRef> r = GDScriptCache::get_parser(r_type.type.script_path, GDScriptParserRef::FULLY_SOLVED, err);2261}22622263// Check type hint last. For collections we want chance to get the actual value first2264// This way we can detect types from the content of dictionaries and arrays2265if (!found && p_expression->get_datatype().is_hard_type()) {2266r_type.type = p_expression->get_datatype();2267if (!r_type.assigned_expression) {2268r_type.assigned_expression = p_expression;2269}2270found = true;2271}22722273return found;2274}22752276static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::IdentifierNode *p_identifier, GDScriptCompletionIdentifier &r_type) {2277static int recursion_depth = 0;2278RecursionCheck recursion(&recursion_depth);2279if (unlikely(recursion.check())) {2280ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");2281}22822283// Look in blocks first.2284int last_assign_line = -1;2285const GDScriptParser::ExpressionNode *last_assigned_expression = nullptr;2286GDScriptCompletionIdentifier id_type;2287GDScriptParser::SuiteNode *suite = p_context.current_suite;2288bool is_function_parameter = false;22892290bool can_be_local = true;2291switch (p_identifier->source) {2292case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:2293case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:2294case GDScriptParser::IdentifierNode::MEMBER_FUNCTION:2295case GDScriptParser::IdentifierNode::MEMBER_SIGNAL:2296case GDScriptParser::IdentifierNode::MEMBER_CLASS:2297case GDScriptParser::IdentifierNode::INHERITED_VARIABLE:2298case GDScriptParser::IdentifierNode::STATIC_VARIABLE:2299case GDScriptParser::IdentifierNode::NATIVE_CLASS:2300can_be_local = false;2301break;2302default:2303break;2304}23052306if (can_be_local && suite && suite->has_local(p_identifier->name)) {2307const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_identifier->name);23082309id_type.type = local.get_datatype();23102311// Check initializer as the first assignment.2312switch (local.type) {2313case GDScriptParser::SuiteNode::Local::VARIABLE:2314if (local.variable->initializer) {2315last_assign_line = local.variable->initializer->end_line;2316last_assigned_expression = local.variable->initializer;2317}2318break;2319case GDScriptParser::SuiteNode::Local::CONSTANT:2320if (local.constant->initializer) {2321last_assign_line = local.constant->initializer->end_line;2322last_assigned_expression = local.constant->initializer;2323}2324break;2325case GDScriptParser::SuiteNode::Local::PARAMETER:2326if (local.parameter->initializer) {2327last_assign_line = local.parameter->initializer->end_line;2328last_assigned_expression = local.parameter->initializer;2329}2330is_function_parameter = true;2331break;2332default:2333break;2334}2335} else {2336if (p_context.current_class) {2337GDScriptCompletionIdentifier base_identifier;23382339GDScriptCompletionIdentifier base;2340base.value = p_context.base;2341base.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2342base.type.kind = GDScriptParser::DataType::CLASS;2343base.type.class_type = p_context.current_class;2344base.type.is_meta_type = p_context.current_function && p_context.current_function->is_static;23452346if (_guess_identifier_type_from_base(p_context, base, p_identifier->name, base_identifier)) {2347id_type = base_identifier;2348}2349}2350}23512352while (suite) {2353for (int i = 0; i < suite->statements.size(); i++) {2354if (suite->statements[i]->end_line >= p_context.current_line) {2355break;2356}23572358switch (suite->statements[i]->type) {2359case GDScriptParser::Node::ASSIGNMENT: {2360const GDScriptParser::AssignmentNode *assign = static_cast<const GDScriptParser::AssignmentNode *>(suite->statements[i]);2361if (assign->end_line > last_assign_line && assign->assignee && assign->assigned_value && assign->assignee->type == GDScriptParser::Node::IDENTIFIER) {2362const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(assign->assignee);2363if (id->name == p_identifier->name && id->source == p_identifier->source) {2364last_assign_line = assign->assigned_value->end_line;2365last_assigned_expression = assign->assigned_value;2366}2367}2368} break;2369default:2370// TODO: Check sub blocks (control flow statements) as they might also reassign stuff.2371break;2372}2373}23742375if (suite->parent_if && suite->parent_if->condition && suite->parent_if->condition->type == GDScriptParser::Node::TYPE_TEST) {2376// Operator `is` used, check if identifier is in there! this helps resolve in blocks that are (if (identifier is value)): which are very common..2377// Super dirty hack, but very useful.2378// Credit: Zylann.2379// TODO: this could be hacked to detect AND-ed conditions too...2380const GDScriptParser::TypeTestNode *type_test = static_cast<const GDScriptParser::TypeTestNode *>(suite->parent_if->condition);2381if (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) {2382// Bingo.2383GDScriptParser::CompletionContext c = p_context;2384c.current_line = type_test->operand->start_line;2385c.current_suite = suite;2386if (type_test->test_datatype.is_hard_type()) {2387id_type.type = type_test->test_datatype;2388if (last_assign_line < c.current_line) {2389// Override last assignment.2390last_assign_line = c.current_line;2391last_assigned_expression = nullptr;2392}2393}2394}2395}23962397suite = suite->parent_block;2398}23992400if (last_assigned_expression && last_assign_line < p_context.current_line) {2401GDScriptParser::CompletionContext c = p_context;2402c.current_line = last_assign_line;2403GDScriptCompletionIdentifier assigned_type;2404if (_guess_expression_type(c, last_assigned_expression, assigned_type)) {2405if (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)))) {2406// The assigned type is incompatible. The annotated type takes priority.2407r_type = id_type;2408r_type.assigned_expression = last_assigned_expression;2409} else {2410r_type = assigned_type;2411}2412return true;2413}2414}24152416if (is_function_parameter && p_context.current_function && p_context.current_function->source_lambda == nullptr && p_context.current_class) {2417// Check if it's override of native function, then we can assume the type from the signature.2418GDScriptParser::DataType base_type = p_context.current_class->base_type;2419while (base_type.is_set()) {2420switch (base_type.kind) {2421case GDScriptParser::DataType::CLASS:2422if (base_type.class_type->has_function(p_context.current_function->identifier->name)) {2423GDScriptParser::FunctionNode *parent_function = base_type.class_type->get_member(p_context.current_function->identifier->name).function;2424if (parent_function->parameters_indices.has(p_identifier->name)) {2425const GDScriptParser::ParameterNode *parameter = parent_function->parameters[parent_function->parameters_indices[p_identifier->name]];2426if ((!id_type.type.is_set() || id_type.type.is_variant()) && parameter->get_datatype().is_hard_type()) {2427id_type.type = parameter->get_datatype();2428}2429if (parameter->initializer) {2430GDScriptParser::CompletionContext c = p_context;2431c.current_function = parent_function;2432c.current_class = base_type.class_type;2433c.base = nullptr;2434if (_guess_expression_type(c, parameter->initializer, r_type)) {2435return true;2436}2437}2438}2439}2440base_type = base_type.class_type->base_type;2441break;2442case GDScriptParser::DataType::NATIVE: {2443if (id_type.type.is_set() && !id_type.type.is_variant()) {2444base_type = GDScriptParser::DataType();2445break;2446}2447MethodInfo info;2448if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) {2449for (const PropertyInfo &E : info.arguments) {2450if (E.name == p_identifier->name) {2451r_type = _type_from_property(E);2452return true;2453}2454}2455}2456base_type = GDScriptParser::DataType();2457} break;2458default:2459break;2460}2461}2462}24632464if (id_type.type.is_set() && !id_type.type.is_variant()) {2465r_type = id_type;2466return true;2467}24682469// Check global scripts.2470if (ScriptServer::is_global_class(p_identifier->name)) {2471String script = ScriptServer::get_global_class_path(p_identifier->name);2472if (script.to_lower().ends_with(".gd")) {2473Ref<GDScriptParserRef> parser = p_context.parser->get_depended_parser_for(script);2474if (parser.is_valid() && parser->raise_status(GDScriptParserRef::INTERFACE_SOLVED) == OK) {2475r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2476r_type.type.script_path = script;2477r_type.type.class_type = parser->get_parser()->get_tree();2478r_type.type.is_meta_type = true;2479r_type.type.is_constant = false;2480r_type.type.kind = GDScriptParser::DataType::CLASS;2481r_type.value = Variant();2482return true;2483}2484} else {2485Ref<Script> scr = ResourceLoader::load(ScriptServer::get_global_class_path(p_identifier->name));2486if (scr.is_valid()) {2487r_type = _type_from_variant(scr, p_context);2488r_type.type.is_meta_type = true;2489return true;2490}2491}2492return false;2493}24942495// Check global variables (including autoloads).2496if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(p_identifier->name)) {2497r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[p_identifier->name], p_context);2498return true;2499}25002501// Check ClassDB.2502if (ClassDB::class_exists(p_identifier->name) && ClassDB::is_class_exposed(p_identifier->name)) {2503r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2504r_type.type.kind = GDScriptParser::DataType::NATIVE;2505r_type.type.builtin_type = Variant::OBJECT;2506r_type.type.native_type = p_identifier->name;2507r_type.type.is_constant = true;2508if (Engine::get_singleton()->has_singleton(p_identifier->name)) {2509r_type.type.is_meta_type = false;2510r_type.value = Engine::get_singleton()->get_singleton_object(p_identifier->name);2511} else {2512r_type.type.is_meta_type = true;2513r_type.value = Variant();2514}2515return true;2516}25172518return false;2519}25202521static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_identifier, GDScriptCompletionIdentifier &r_type) {2522static int recursion_depth = 0;2523RecursionCheck recursion(&recursion_depth);2524if (unlikely(recursion.check())) {2525ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");2526}25272528GDScriptParser::DataType base_type = p_base.type;2529bool is_static = base_type.is_meta_type;2530while (base_type.is_set()) {2531switch (base_type.kind) {2532case GDScriptParser::DataType::CLASS:2533if (base_type.class_type->has_member(p_identifier)) {2534const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(p_identifier);2535switch (member.type) {2536case GDScriptParser::ClassNode::Member::CONSTANT:2537r_type.type = member.constant->get_datatype();2538if (member.constant->initializer && member.constant->initializer->is_constant) {2539r_type.value = member.constant->initializer->reduced_value;2540}2541return true;2542case GDScriptParser::ClassNode::Member::VARIABLE:2543if (!is_static || member.variable->is_static) {2544if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {2545r_type.type = member.variable->get_datatype();2546return true;2547} else if (member.variable->initializer) {2548const GDScriptParser::ExpressionNode *init = member.variable->initializer;2549if (init->is_constant) {2550r_type.value = init->reduced_value;2551r_type = _type_from_variant(init->reduced_value, p_context);2552return true;2553} else if (init->start_line == p_context.current_line) {2554return false;2555// Detects if variable is assigned to itself2556} else if (_is_expression_named_identifier(init, member.variable->identifier->name)) {2557if (member.variable->initializer->get_datatype().is_set()) {2558r_type.type = member.variable->initializer->get_datatype();2559} else if (member.variable->get_datatype().is_set() && !member.variable->get_datatype().is_variant()) {2560r_type.type = member.variable->get_datatype();2561}2562return true;2563} else if (_guess_expression_type(p_context, init, r_type)) {2564return true;2565} else if (init->get_datatype().is_set() && !init->get_datatype().is_variant()) {2566r_type.type = init->get_datatype();2567return true;2568}2569}2570}2571// TODO: Check assignments in constructor.2572return false;2573case GDScriptParser::ClassNode::Member::ENUM:2574r_type.type = member.m_enum->get_datatype();2575r_type.enumeration = member.m_enum->identifier->name;2576return true;2577case GDScriptParser::ClassNode::Member::ENUM_VALUE:2578r_type = _type_from_variant(member.enum_value.value, p_context);2579return true;2580case GDScriptParser::ClassNode::Member::SIGNAL:2581r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2582r_type.type.kind = GDScriptParser::DataType::BUILTIN;2583r_type.type.builtin_type = Variant::SIGNAL;2584r_type.type.method_info = member.signal->method_info;2585return true;2586case GDScriptParser::ClassNode::Member::FUNCTION:2587if (is_static && !member.function->is_static) {2588return false;2589}2590r_type = _callable_type_from_method_info(member.function->info);2591return true;2592case GDScriptParser::ClassNode::Member::CLASS:2593r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2594r_type.type.kind = GDScriptParser::DataType::CLASS;2595r_type.type.class_type = member.m_class;2596r_type.type.is_meta_type = true;2597return true;2598case GDScriptParser::ClassNode::Member::GROUP:2599return false; // No-op, but silences warnings.2600case GDScriptParser::ClassNode::Member::UNDEFINED:2601return false; // Unreachable.2602}2603return false;2604}2605base_type = base_type.class_type->base_type;2606break;2607case GDScriptParser::DataType::SCRIPT: {2608Ref<Script> scr = base_type.script_type;2609if (scr.is_valid()) {2610HashMap<StringName, Variant> constants;2611scr->get_constants(&constants);2612if (constants.has(p_identifier)) {2613r_type = _type_from_variant(constants[p_identifier], p_context);2614return true;2615}26162617List<PropertyInfo> members;2618if (is_static) {2619scr->get_property_list(&members);2620} else {2621scr->get_script_property_list(&members);2622}2623for (const PropertyInfo &prop : members) {2624if (prop.name == p_identifier) {2625r_type = _type_from_property(prop);2626return true;2627}2628}26292630if (scr->has_method(p_identifier)) {2631MethodInfo mi = scr->get_method_info(p_identifier);2632r_type = _callable_type_from_method_info(mi);2633return true;2634}26352636Ref<Script> parent = scr->get_base_script();2637if (parent.is_valid()) {2638base_type.script_type = parent;2639} else {2640base_type.kind = GDScriptParser::DataType::NATIVE;2641base_type.builtin_type = Variant::OBJECT;2642base_type.native_type = scr->get_instance_base_type();2643}2644} else {2645return false;2646}2647} break;2648case GDScriptParser::DataType::NATIVE: {2649StringName class_name = base_type.native_type;2650if (!ClassDB::class_exists(class_name)) {2651return false;2652}26532654// Skip constants since they're all integers. Type does not matter because int has no members.26552656PropertyInfo prop;2657if (ClassDB::get_property_info(class_name, p_identifier, &prop)) {2658StringName getter = ClassDB::get_property_getter(class_name, p_identifier);2659if (getter != StringName()) {2660MethodBind *g = ClassDB::get_method(class_name, getter);2661if (g) {2662r_type = _type_from_property(g->get_return_info());2663return true;2664}2665} else {2666r_type = _type_from_property(prop);2667return true;2668}2669}26702671MethodInfo method;2672if (ClassDB::get_method_info(class_name, p_identifier, &method)) {2673r_type = _callable_type_from_method_info(method);2674return true;2675}26762677if (ClassDB::has_enum(class_name, p_identifier)) {2678r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;2679r_type.type.kind = GDScriptParser::DataType::ENUM;2680r_type.type.enum_type = p_identifier;2681r_type.type.is_constant = true;2682r_type.type.is_meta_type = true;2683r_type.type.native_type = String(class_name) + "." + p_identifier;2684return true;2685}26862687return false;2688} break;2689case GDScriptParser::DataType::BUILTIN: {2690if (Variant::has_builtin_method(base_type.builtin_type, p_identifier)) {2691r_type = _callable_type_from_method_info(Variant::get_builtin_method_info(base_type.builtin_type, p_identifier));2692return true;2693} else {2694Callable::CallError err;2695Variant tmp;2696Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);26972698if (err.error != Callable::CallError::CALL_OK) {2699return false;2700}2701bool valid = false;2702Variant res = tmp.get(p_identifier, &valid);2703if (valid) {2704r_type = _type_from_variant(res, p_context);2705r_type.value = Variant();2706r_type.type.is_constant = false;2707return true;2708}2709}2710return false;2711} break;2712default: {2713return false;2714} break;2715}2716}2717return false;2718}27192720static void _find_last_return_in_block(GDScriptParser::CompletionContext &p_context, int &r_last_return_line, const GDScriptParser::ExpressionNode **r_last_returned_value) {2721if (!p_context.current_suite) {2722return;2723}27242725for (int i = 0; i < p_context.current_suite->statements.size(); i++) {2726if (p_context.current_suite->statements[i]->start_line < r_last_return_line) {2727break;2728}27292730GDScriptParser::CompletionContext c = p_context;2731switch (p_context.current_suite->statements[i]->type) {2732case GDScriptParser::Node::FOR:2733c.current_suite = static_cast<const GDScriptParser::ForNode *>(p_context.current_suite->statements[i])->loop;2734_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2735break;2736case GDScriptParser::Node::WHILE:2737c.current_suite = static_cast<const GDScriptParser::WhileNode *>(p_context.current_suite->statements[i])->loop;2738_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2739break;2740case GDScriptParser::Node::IF: {2741const GDScriptParser::IfNode *_if = static_cast<const GDScriptParser::IfNode *>(p_context.current_suite->statements[i]);2742c.current_suite = _if->true_block;2743_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2744if (_if->false_block) {2745c.current_suite = _if->false_block;2746_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2747}2748} break;2749case GDScriptParser::Node::MATCH: {2750const GDScriptParser::MatchNode *match = static_cast<const GDScriptParser::MatchNode *>(p_context.current_suite->statements[i]);2751for (int j = 0; j < match->branches.size(); j++) {2752c.current_suite = match->branches[j]->block;2753_find_last_return_in_block(c, r_last_return_line, r_last_returned_value);2754}2755} break;2756case GDScriptParser::Node::RETURN: {2757const GDScriptParser::ReturnNode *ret = static_cast<const GDScriptParser::ReturnNode *>(p_context.current_suite->statements[i]);2758if (ret->return_value) {2759if (ret->start_line > r_last_return_line) {2760r_last_return_line = ret->start_line;2761*r_last_returned_value = ret->return_value;2762}2763}2764} break;2765default:2766break;2767}2768}2769}27702771static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContext &p_context, const GDScriptCompletionIdentifier &p_base, const StringName &p_method, GDScriptCompletionIdentifier &r_type) {2772static int recursion_depth = 0;2773RecursionCheck recursion(&recursion_depth);2774if (unlikely(recursion.check())) {2775ERR_FAIL_V_MSG(false, "Reached recursion limit while trying to guess type.");2776}27772778GDScriptParser::DataType base_type = p_base.type;2779bool is_static = base_type.is_meta_type;27802781if (is_static && p_method == SNAME("new")) {2782r_type.type = base_type;2783r_type.type.is_meta_type = false;2784r_type.type.is_constant = false;2785return true;2786}27872788while (base_type.is_set() && !base_type.is_variant()) {2789switch (base_type.kind) {2790case GDScriptParser::DataType::CLASS:2791if (base_type.class_type->has_function(p_method)) {2792GDScriptParser::FunctionNode *method = base_type.class_type->get_member(p_method).function;2793if (!is_static || method->is_static) {2794if (method->get_datatype().is_set() && !method->get_datatype().is_variant()) {2795r_type.type = method->get_datatype();2796return true;2797}27982799int last_return_line = -1;2800const GDScriptParser::ExpressionNode *last_returned_value = nullptr;2801GDScriptParser::CompletionContext c = p_context;2802c.current_class = base_type.class_type;2803c.current_function = method;2804c.current_suite = method->body;28052806_find_last_return_in_block(c, last_return_line, &last_returned_value);2807if (last_returned_value) {2808c.current_line = c.current_suite->end_line;2809if (_guess_expression_type(c, last_returned_value, r_type)) {2810return true;2811}2812}2813}2814}2815base_type = base_type.class_type->base_type;2816break;2817case GDScriptParser::DataType::SCRIPT: {2818Ref<Script> scr = base_type.script_type;2819if (scr.is_valid()) {2820List<MethodInfo> methods;2821scr->get_script_method_list(&methods);2822for (const MethodInfo &mi : methods) {2823if (mi.name == p_method) {2824r_type = _type_from_property(mi.return_val);2825return true;2826}2827}2828Ref<Script> base_script = scr->get_base_script();2829if (base_script.is_valid()) {2830base_type.script_type = base_script;2831} else {2832base_type.kind = GDScriptParser::DataType::NATIVE;2833base_type.builtin_type = Variant::OBJECT;2834base_type.native_type = scr->get_instance_base_type();2835}2836} else {2837return false;2838}2839} break;2840case GDScriptParser::DataType::NATIVE: {2841if (!ClassDB::class_exists(base_type.native_type)) {2842return false;2843}2844MethodBind *mb = ClassDB::get_method(base_type.native_type, p_method);2845if (mb) {2846r_type = _type_from_property(mb->get_return_info());2847return true;2848}2849return false;2850} break;2851case GDScriptParser::DataType::BUILTIN: {2852Callable::CallError err;2853Variant tmp;2854Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);2855if (err.error != Callable::CallError::CALL_OK) {2856return false;2857}28582859List<MethodInfo> methods;2860tmp.get_method_list(&methods);28612862for (const MethodInfo &mi : methods) {2863if (mi.name == p_method) {2864r_type = _type_from_property(mi.return_val);2865return true;2866}2867}2868return false;2869} break;2870default: {2871return false;2872}2873}2874}28752876return false;2877}28782879static bool _guess_expecting_callable(GDScriptParser::CompletionContext &p_context) {2880if (p_context.call.call != nullptr && p_context.call.call->type == GDScriptParser::Node::CALL) {2881GDScriptParser::CallNode *call_node = static_cast<GDScriptParser::CallNode *>(p_context.call.call);2882GDScriptCompletionIdentifier ci;2883if (_guess_expression_type(p_context, call_node->callee, ci)) {2884if (ci.type.kind == GDScriptParser::DataType::BUILTIN && ci.type.builtin_type == Variant::CALLABLE) {2885if (p_context.call.argument >= 0 && p_context.call.argument < ci.type.method_info.arguments.size()) {2886return ci.type.method_info.arguments.get(p_context.call.argument).type == Variant::CALLABLE;2887}2888}2889}2890}28912892return false;2893}28942895static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_context, const String &p_enum_hint, HashMap<String, ScriptLanguage::CodeCompletionOption> &r_result) {2896if (!p_enum_hint.contains_char('.')) {2897// Global constant or in the current class.2898StringName current_enum = p_enum_hint;2899if (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) {2900const GDScriptParser::EnumNode *_enum = p_context.current_class->get_member(current_enum).m_enum;2901for (int i = 0; i < _enum->values.size(); i++) {2902ScriptLanguage::CodeCompletionOption option(_enum->values[i].identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_ENUM);2903r_result.insert(option.display, option);2904}2905} else {2906for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {2907if (CoreConstants::get_global_constant_enum(i) == current_enum) {2908ScriptLanguage::CodeCompletionOption option(CoreConstants::get_global_constant_name(i), ScriptLanguage::CODE_COMPLETION_KIND_ENUM);2909r_result.insert(option.display, option);2910}2911}2912}2913} else {2914String class_name = p_enum_hint.get_slicec('.', 0);2915String enum_name = p_enum_hint.get_slicec('.', 1);29162917if (!ClassDB::class_exists(class_name)) {2918return;2919}29202921List<StringName> enum_constants;2922ClassDB::get_enum_constants(class_name, enum_name, &enum_constants);2923for (const StringName &E : enum_constants) {2924String candidate = class_name + "." + E;2925int location = _get_enum_constant_location(class_name, E);2926ScriptLanguage::CodeCompletionOption option(candidate, ScriptLanguage::CODE_COMPLETION_KIND_ENUM, location);2927r_result.insert(option.display, option);2928}2929}2930}29312932static 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) {2933Variant base = p_base.value;2934GDScriptParser::DataType base_type = p_base.type;2935const StringName &method = p_call->function_name;29362937const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";2938const bool use_string_names = EDITOR_GET("text_editor/completion/add_string_name_literals");2939const bool use_node_paths = EDITOR_GET("text_editor/completion/add_node_path_literals");29402941while (base_type.is_set() && !base_type.is_variant()) {2942switch (base_type.kind) {2943case GDScriptParser::DataType::CLASS: {2944if (base_type.is_meta_type && method == SNAME("new")) {2945const GDScriptParser::ClassNode *current = base_type.class_type;29462947do {2948if (current->has_member("_init")) {2949const GDScriptParser::ClassNode::Member &member = current->get_member("_init");29502951if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {2952r_arghint = base_type.class_type->get_datatype().to_string() + " new" + _make_arguments_hint(member.function, p_argidx, true);2953return;2954}2955}2956current = current->base_type.class_type;2957} while (current != nullptr);29582959r_arghint = base_type.class_type->get_datatype().to_string() + " new()";2960return;2961}29622963if (base_type.class_type->has_member(method)) {2964const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(method);29652966if (member.type == GDScriptParser::ClassNode::Member::FUNCTION) {2967r_arghint = _make_arguments_hint(member.function, p_argidx);2968return;2969}2970}29712972base_type = base_type.class_type->base_type;2973} break;2974case GDScriptParser::DataType::SCRIPT: {2975if (base_type.script_type->is_valid() && base_type.script_type->has_method(method)) {2976r_arghint = _make_arguments_hint(base_type.script_type->get_method_info(method), p_argidx);2977return;2978}2979Ref<Script> base_script = base_type.script_type->get_base_script();2980if (base_script.is_valid()) {2981base_type.script_type = base_script;2982} else {2983base_type.kind = GDScriptParser::DataType::NATIVE;2984base_type.builtin_type = Variant::OBJECT;2985base_type.native_type = base_type.script_type->get_instance_base_type();2986}2987} break;2988case GDScriptParser::DataType::NATIVE: {2989StringName class_name = base_type.native_type;2990if (!ClassDB::class_exists(class_name)) {2991base_type.kind = GDScriptParser::DataType::UNRESOLVED;2992break;2993}29942995MethodInfo info;2996int method_args = 0;29972998if (ClassDB::get_method_info(class_name, method, &info)) {2999method_args = info.arguments.size();3000if (base.get_type() == Variant::OBJECT) {3001Object *obj = base.operator Object *();3002if (obj) {3003List<String> options;3004obj->get_argument_options(method, p_argidx, &options);3005for (String &opt : options) {3006// Handle user preference.3007if (opt.is_quoted()) {3008opt = opt.unquote().quote(quote_style);3009if (use_string_names && info.arguments[p_argidx].type == Variant::STRING_NAME) {3010if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3011GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3012if (literal->value.get_type() == Variant::STRING) {3013opt = "&" + opt;3014}3015} else {3016opt = "&" + opt;3017}3018} else if (use_node_paths && info.arguments[p_argidx].type == Variant::NODE_PATH) {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}3028}3029ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3030r_result.insert(option.display, option);3031}3032}3033}30343035if (p_argidx < method_args) {3036const PropertyInfo &arg_info = info.arguments[p_argidx];3037if (arg_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {3038_find_enumeration_candidates(p_context, arg_info.class_name, r_result);3039}3040}30413042r_arghint = _make_arguments_hint(info, p_argidx);3043}30443045if (p_argidx == 1 && p_call && ClassDB::is_parent_class(class_name, SNAME("Tween")) && method == SNAME("tween_property")) {3046// Get tweened objects properties.3047if (p_call->arguments.is_empty()) {3048base_type.kind = GDScriptParser::DataType::UNRESOLVED;3049break;3050}3051GDScriptParser::ExpressionNode *tweened_object = p_call->arguments[0];3052if (!tweened_object) {3053base_type.kind = GDScriptParser::DataType::UNRESOLVED;3054break;3055}3056StringName native_type = tweened_object->datatype.native_type;3057switch (tweened_object->datatype.kind) {3058case GDScriptParser::DataType::SCRIPT: {3059Ref<Script> script = tweened_object->datatype.script_type;3060native_type = script->get_instance_base_type();3061int n = 0;3062while (script.is_valid()) {3063List<PropertyInfo> properties;3064script->get_script_property_list(&properties);3065for (const PropertyInfo &E : properties) {3066if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) {3067continue;3068}3069String name = E.name.quote(quote_style);3070if (use_node_paths) {3071if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3072GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3073if (literal->value.get_type() == Variant::STRING) {3074name = "^" + name;3075}3076} else {3077name = "^" + name;3078}3079}3080ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);3081r_result.insert(option.display, option);3082}3083script = script->get_base_script();3084n++;3085}3086} break;3087case GDScriptParser::DataType::CLASS: {3088GDScriptParser::ClassNode *clss = tweened_object->datatype.class_type;3089native_type = clss->base_type.native_type;3090int n = 0;3091while (clss) {3092for (GDScriptParser::ClassNode::Member member : clss->members) {3093if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {3094String name = member.get_name().quote(quote_style);3095if (use_node_paths) {3096if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3097GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3098if (literal->value.get_type() == Variant::STRING) {3099name = "^" + name;3100}3101} else {3102name = "^" + name;3103}3104}3105ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::CodeCompletionLocation::LOCATION_LOCAL + n);3106r_result.insert(option.display, option);3107}3108}3109if (clss->base_type.kind == GDScriptParser::DataType::Kind::CLASS) {3110clss = clss->base_type.class_type;3111n++;3112} else {3113native_type = clss->base_type.native_type;3114clss = nullptr;3115}3116}3117} break;3118default:3119break;3120}31213122List<PropertyInfo> properties;3123ClassDB::get_property_list(native_type, &properties);3124for (const PropertyInfo &E : properties) {3125if (E.usage & (PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_INTERNAL)) {3126continue;3127}3128String name = E.name.quote(quote_style);3129if (use_node_paths) {3130if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3131GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3132if (literal->value.get_type() == Variant::STRING) {3133name = "^" + name;3134}3135} else {3136name = "^" + name;3137}3138}3139ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_MEMBER);3140r_result.insert(option.display, option);3141}3142}31433144if (p_argidx == 0 && ClassDB::is_parent_class(class_name, SNAME("Node")) && (method == SNAME("get_node") || method == SNAME("has_node"))) {3145// Get autoloads3146List<PropertyInfo> props;3147ProjectSettings::get_singleton()->get_property_list(&props);31483149for (const PropertyInfo &E : props) {3150String s = E.name;3151if (!s.begins_with("autoload/")) {3152continue;3153}3154String name = s.get_slicec('/', 1);3155String path = ("/root/" + name).quote(quote_style);3156if (use_node_paths) {3157if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3158GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3159if (literal->value.get_type() == Variant::STRING) {3160path = "^" + path;3161}3162} else {3163path = "^" + path;3164}3165}3166ScriptLanguage::CodeCompletionOption option(path, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);3167r_result.insert(option.display, option);3168}3169}31703171if (p_argidx == 0 && method_args > 0 && ClassDB::is_parent_class(class_name, SNAME("InputEvent")) && method.operator String().contains("action")) {3172// Get input actions3173List<PropertyInfo> props;3174ProjectSettings::get_singleton()->get_property_list(&props);3175for (const PropertyInfo &E : props) {3176String s = E.name;3177if (!s.begins_with("input/")) {3178continue;3179}3180String name = s.get_slicec('/', 1).quote(quote_style);3181if (use_string_names) {3182if (p_call->arguments.size() > p_argidx && p_call->arguments[p_argidx] && p_call->arguments[p_argidx]->type == GDScriptParser::Node::LITERAL) {3183GDScriptParser::LiteralNode *literal = static_cast<GDScriptParser::LiteralNode *>(p_call->arguments[p_argidx]);3184if (literal->value.get_type() == Variant::STRING) {3185name = "&" + name;3186}3187} else {3188name = "&" + name;3189}3190}3191ScriptLanguage::CodeCompletionOption option(name, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);3192r_result.insert(option.display, option);3193}3194}3195if (EDITOR_GET("text_editor/completion/complete_file_paths")) {3196if (p_argidx == 0 && method == SNAME("change_scene_to_file") && ClassDB::is_parent_class(class_name, SNAME("SceneTree"))) {3197HashMap<String, ScriptLanguage::CodeCompletionOption> list;3198_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), list, SNAME("PackedScene"));3199for (const KeyValue<String, ScriptLanguage::CodeCompletionOption> &key_value_pair : list) {3200ScriptLanguage::CodeCompletionOption option = key_value_pair.value;3201r_result.insert(option.display, option);3202}3203}3204}32053206base_type.kind = GDScriptParser::DataType::UNRESOLVED;3207} break;3208case GDScriptParser::DataType::BUILTIN: {3209if (base.get_type() == Variant::NIL) {3210Callable::CallError err;3211Variant::construct(base_type.builtin_type, base, nullptr, 0, err);3212if (err.error != Callable::CallError::CALL_OK) {3213return;3214}3215}32163217List<MethodInfo> methods;3218base.get_method_list(&methods);3219for (const MethodInfo &E : methods) {3220if (E.name == method) {3221r_arghint = _make_arguments_hint(E, p_argidx);3222return;3223}3224}32253226base_type.kind = GDScriptParser::DataType::UNRESOLVED;3227} break;3228default: {3229base_type.kind = GDScriptParser::DataType::UNRESOLVED;3230} break;3231}3232}3233}32343235static bool _get_subscript_type(GDScriptParser::CompletionContext &p_context, const GDScriptParser::SubscriptNode *p_subscript, GDScriptParser::DataType &r_base_type, Variant *r_base = nullptr) {3236if (p_context.base == nullptr) {3237return false;3238}32393240const GDScriptParser::GetNodeNode *get_node = nullptr;32413242switch (p_subscript->base->type) {3243case GDScriptParser::Node::GET_NODE: {3244get_node = static_cast<GDScriptParser::GetNodeNode *>(p_subscript->base);3245} break;32463247case GDScriptParser::Node::IDENTIFIER: {3248const GDScriptParser::IdentifierNode *identifier_node = static_cast<GDScriptParser::IdentifierNode *>(p_subscript->base);32493250switch (identifier_node->source) {3251case GDScriptParser::IdentifierNode::Source::MEMBER_VARIABLE: {3252if (p_context.current_class != nullptr) {3253const StringName &member_name = identifier_node->name;3254const GDScriptParser::ClassNode *current_class = p_context.current_class;32553256if (current_class->has_member(member_name)) {3257const GDScriptParser::ClassNode::Member &member = current_class->get_member(member_name);32583259if (member.type == GDScriptParser::ClassNode::Member::VARIABLE) {3260const GDScriptParser::VariableNode *variable = static_cast<GDScriptParser::VariableNode *>(member.variable);32613262if (variable->initializer && variable->initializer->type == GDScriptParser::Node::GET_NODE) {3263get_node = static_cast<GDScriptParser::GetNodeNode *>(variable->initializer);3264}3265}3266}3267}3268} break;3269case GDScriptParser::IdentifierNode::Source::LOCAL_VARIABLE: {3270// TODO: Do basic assignment flow analysis like in `_guess_expression_type`.3271const GDScriptParser::SuiteNode::Local local = identifier_node->suite->get_local(identifier_node->name);3272switch (local.type) {3273case GDScriptParser::SuiteNode::Local::CONSTANT: {3274if (local.constant->initializer && local.constant->initializer->type == GDScriptParser::Node::GET_NODE) {3275get_node = static_cast<GDScriptParser::GetNodeNode *>(local.constant->initializer);3276}3277} break;3278case GDScriptParser::SuiteNode::Local::VARIABLE: {3279if (local.variable->initializer && local.variable->initializer->type == GDScriptParser::Node::GET_NODE) {3280get_node = static_cast<GDScriptParser::GetNodeNode *>(local.variable->initializer);3281}3282} break;3283default: {3284} break;3285}3286} break;3287default: {3288} break;3289}3290} break;3291default: {3292} break;3293}32943295if (get_node != nullptr) {3296const Object *node = p_context.base->call("get_node_or_null", NodePath(get_node->full_path));3297if (node != nullptr) {3298GDScriptParser::DataType assigned_type = _type_from_variant(node, p_context).type;3299GDScriptParser::DataType base_type = p_subscript->base->datatype;33003301if (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)) {3302// Annotated type takes precedence.3303return false;3304}33053306if (r_base != nullptr) {3307*r_base = node;3308}33093310r_base_type.type_source = GDScriptParser::DataType::INFERRED;3311r_base_type.builtin_type = Variant::OBJECT;3312r_base_type.native_type = node->get_class_name();33133314Ref<Script> scr = node->get_script();3315if (scr.is_null()) {3316r_base_type.kind = GDScriptParser::DataType::NATIVE;3317} else {3318r_base_type.kind = GDScriptParser::DataType::SCRIPT;3319r_base_type.script_type = scr;3320}33213322return true;3323}3324}33253326return false;3327}33283329static 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) {3330if (p_call->type == GDScriptParser::Node::PRELOAD) {3331if (p_argidx == 0 && bool(EDITOR_GET("text_editor/completion/complete_file_paths"))) {3332_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);3333}33343335MethodInfo mi(PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), "preload", PropertyInfo(Variant::STRING, "path"));3336r_arghint = _make_arguments_hint(mi, p_argidx);3337return;3338} else if (p_call->type != GDScriptParser::Node::CALL) {3339return;3340}33413342Variant base;3343GDScriptParser::DataType base_type;3344bool _static = false;3345const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call);3346GDScriptParser::Node::Type callee_type = call->get_callee_type();33473348if (callee_type == GDScriptParser::Node::SUBSCRIPT) {3349const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);33503351if (subscript->base != nullptr && subscript->base->type == GDScriptParser::Node::IDENTIFIER) {3352const GDScriptParser::IdentifierNode *base_identifier = static_cast<const GDScriptParser::IdentifierNode *>(subscript->base);33533354Variant::Type method_type = GDScriptParser::get_builtin_type(base_identifier->name);3355if (method_type < Variant::VARIANT_MAX) {3356Variant v;3357Callable::CallError err;3358Variant::construct(method_type, v, nullptr, 0, err);3359if (err.error != Callable::CallError::CALL_OK) {3360return;3361}3362List<MethodInfo> methods;3363v.get_method_list(&methods);33643365for (MethodInfo &E : methods) {3366if (p_argidx >= E.arguments.size()) {3367continue;3368}3369if (E.name == call->function_name) {3370r_arghint += _make_arguments_hint(E, p_argidx);3371return;3372}3373}3374}3375}33763377if (subscript->is_attribute) {3378bool found_type = _get_subscript_type(p_context, subscript, base_type, &base);33793380if (!found_type) {3381GDScriptCompletionIdentifier ci;3382if (_guess_expression_type(p_context, subscript->base, ci)) {3383base_type = ci.type;3384base = ci.value;3385} else {3386return;3387}3388}33893390_static = base_type.is_meta_type;3391}3392} else if (Variant::has_utility_function(call->function_name)) {3393MethodInfo info = Variant::get_utility_function_info(call->function_name);3394r_arghint = _make_arguments_hint(info, p_argidx);3395return;3396} else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {3397MethodInfo info = GDScriptUtilityFunctions::get_function_info(call->function_name);3398r_arghint = _make_arguments_hint(info, p_argidx);3399return;3400} else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {3401// Complete constructor.3402List<MethodInfo> constructors;3403Variant::get_constructor_list(GDScriptParser::get_builtin_type(call->function_name), &constructors);34043405int i = 0;3406for (const MethodInfo &E : constructors) {3407if (p_argidx >= E.arguments.size()) {3408continue;3409}3410if (i > 0) {3411r_arghint += "\n";3412}3413r_arghint += _make_arguments_hint(E, p_argidx);3414i++;3415}3416return;3417} else if (call->is_super || callee_type == GDScriptParser::Node::IDENTIFIER) {3418base = p_context.base;34193420if (p_context.current_class) {3421base_type = p_context.current_class->get_datatype();3422_static = !p_context.current_function || p_context.current_function->is_static;3423}3424} else {3425return;3426}34273428GDScriptCompletionIdentifier ci;3429ci.type = base_type;3430ci.value = base;3431_list_call_arguments(p_context, ci, call, p_argidx, _static, r_result, r_arghint);34323433r_forced = r_result.size() > 0;3434}34353436::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) {3437const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";34383439GDScriptParser parser;3440GDScriptAnalyzer analyzer(&parser);34413442parser.parse(p_code, p_path, true);3443analyzer.analyze();34443445r_forced = false;3446HashMap<String, ScriptLanguage::CodeCompletionOption> options;34473448GDScriptParser::CompletionContext completion_context = parser.get_completion_context();3449if (completion_context.current_class != nullptr && completion_context.current_class->outer == nullptr) {3450completion_context.base = p_owner;3451}3452bool is_function = false;34533454switch (completion_context.type) {3455case GDScriptParser::COMPLETION_NONE:3456break;3457case GDScriptParser::COMPLETION_ANNOTATION: {3458List<MethodInfo> annotations;3459parser.get_annotation_list(&annotations);3460for (const MethodInfo &E : annotations) {3461ScriptLanguage::CodeCompletionOption option(E.name.substr(1), ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3462if (E.arguments.size() > 0) {3463option.insert_text += "(";3464}3465options.insert(option.display, option);3466}3467r_forced = true;3468} break;3469case GDScriptParser::COMPLETION_ANNOTATION_ARGUMENTS: {3470if (completion_context.node == nullptr || completion_context.node->type != GDScriptParser::Node::ANNOTATION) {3471break;3472}3473const GDScriptParser::AnnotationNode *annotation = static_cast<const GDScriptParser::AnnotationNode *>(completion_context.node);3474_find_annotation_arguments(annotation, completion_context.current_argument, quote_style, options, r_call_hint);3475r_forced = true;3476} break;3477case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: {3478// Constants.3479{3480List<StringName> constants;3481Variant::get_constants_for_type(completion_context.builtin_type, &constants);3482for (const StringName &E : constants) {3483ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_CONSTANT);3484bool valid = false;3485Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E, &valid);3486if (valid) {3487option.default_value = default_value;3488}3489options.insert(option.display, option);3490}3491}3492// Methods.3493{3494List<StringName> methods;3495Variant::get_builtin_method_list(completion_context.builtin_type, &methods);3496for (const StringName &E : methods) {3497if (Variant::is_builtin_method_static(completion_context.builtin_type, E)) {3498ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3499if (!_guess_expecting_callable(completion_context)) {3500if (Variant::get_builtin_method_argument_count(completion_context.builtin_type, E) > 0 || Variant::is_builtin_method_vararg(completion_context.builtin_type, E)) {3501option.insert_text += "(";3502} else {3503option.insert_text += "()";3504}3505}3506options.insert(option.display, option);3507}3508}3509}3510} break;3511case GDScriptParser::COMPLETION_INHERIT_TYPE: {3512_list_available_types(true, completion_context, options);3513r_forced = true;3514} break;3515case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID: {3516ScriptLanguage::CodeCompletionOption option("void", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3517options.insert(option.display, option);3518}3519[[fallthrough]];3520case GDScriptParser::COMPLETION_TYPE_NAME: {3521_list_available_types(false, completion_context, options);3522r_forced = true;3523} break;3524case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE: {3525_list_available_types(false, completion_context, options);3526ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3527options.insert(get.display, get);3528ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3529options.insert(set.display, set);3530r_forced = true;3531} break;3532case GDScriptParser::COMPLETION_PROPERTY_DECLARATION: {3533ScriptLanguage::CodeCompletionOption get("get", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3534options.insert(get.display, get);3535ScriptLanguage::CodeCompletionOption set("set", ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT);3536options.insert(set.display, set);3537r_forced = true;3538} break;3539case GDScriptParser::COMPLETION_PROPERTY_METHOD: {3540if (!completion_context.current_class) {3541break;3542}3543for (int i = 0; i < completion_context.current_class->members.size(); i++) {3544const GDScriptParser::ClassNode::Member &member = completion_context.current_class->members[i];3545if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {3546continue;3547}3548if (member.function->is_static) {3549continue;3550}3551ScriptLanguage::CodeCompletionOption option(member.function->identifier->name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3552options.insert(option.display, option);3553}3554r_forced = true;3555} break;3556case GDScriptParser::COMPLETION_ASSIGN: {3557GDScriptCompletionIdentifier type;3558if (!completion_context.node || completion_context.node->type != GDScriptParser::Node::ASSIGNMENT) {3559break;3560}3561if (!_guess_expression_type(completion_context, static_cast<const GDScriptParser::AssignmentNode *>(completion_context.node)->assignee, type)) {3562_find_identifiers(completion_context, false, true, options, 0);3563r_forced = true;3564break;3565}35663567if (!type.enumeration.is_empty()) {3568_find_enumeration_candidates(completion_context, type.enumeration, options);3569r_forced = options.size() > 0;3570} else {3571_find_identifiers(completion_context, false, true, options, 0);3572r_forced = true;3573}3574} break;3575case GDScriptParser::COMPLETION_METHOD:3576is_function = true;3577[[fallthrough]];3578case GDScriptParser::COMPLETION_IDENTIFIER: {3579_find_identifiers(completion_context, is_function, !_guess_expecting_callable(completion_context), options, 0);3580} break;3581case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD:3582is_function = true;3583[[fallthrough]];3584case GDScriptParser::COMPLETION_ATTRIBUTE: {3585r_forced = true;3586const GDScriptParser::SubscriptNode *attr = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);3587if (attr->base) {3588GDScriptCompletionIdentifier base;3589bool found_type = _get_subscript_type(completion_context, attr, base.type);3590if (!found_type && !_guess_expression_type(completion_context, attr->base, base)) {3591break;3592}35933594_find_identifiers_in_base(base, is_function, false, !_guess_expecting_callable(completion_context), options, 0);3595}3596} break;3597case GDScriptParser::COMPLETION_SUBSCRIPT: {3598const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(completion_context.node);3599GDScriptCompletionIdentifier base;3600const bool res = _guess_expression_type(completion_context, subscript->base, base);36013602// If the type is not known, we assume it is BUILTIN, since indices on arrays is the most common use case.3603if (!subscript->is_attribute && (!res || base.type.kind == GDScriptParser::DataType::BUILTIN || base.type.is_variant())) {3604if (base.value.get_type() == Variant::DICTIONARY) {3605List<PropertyInfo> members;3606base.value.get_property_list(&members);36073608for (const PropertyInfo &E : members) {3609ScriptLanguage::CodeCompletionOption option(E.name.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_MEMBER, ScriptLanguage::LOCATION_LOCAL);3610options.insert(option.display, option);3611}3612}3613if (!subscript->index || subscript->index->type != GDScriptParser::Node::LITERAL) {3614_find_identifiers(completion_context, false, !_guess_expecting_callable(completion_context), options, 0);3615}3616} else if (res) {3617if (!subscript->is_attribute) {3618// Quote the options if they are not accessed as attribute.36193620HashMap<String, ScriptLanguage::CodeCompletionOption> opt;3621_find_identifiers_in_base(base, false, false, false, opt, 0);3622for (const KeyValue<String, CodeCompletionOption> &E : opt) {3623ScriptLanguage::CodeCompletionOption option(E.value.insert_text.quote(quote_style), E.value.kind, E.value.location);3624options.insert(option.display, option);3625}3626} else {3627_find_identifiers_in_base(base, false, false, !_guess_expecting_callable(completion_context), options, 0);3628}3629}3630} break;3631case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {3632if (!completion_context.current_class) {3633break;3634}36353636const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(completion_context.node);3637ERR_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.");36383639GDScriptCompletionIdentifier base;36403641if (_guess_identifier_type(completion_context, type->type_chain[0], base)) {3642bool found = true;3643for (int i = 1; i < completion_context.type_chain_index; i++) {3644GDScriptCompletionIdentifier ci;3645found = _guess_identifier_type_from_base(completion_context, base, type->type_chain[i]->name, ci);3646base = ci;3647if (!found) {3648break;3649}3650}3651if (found) {3652_find_identifiers_in_base(base, false, true, true, options, 0);3653}3654}36553656r_forced = true;3657} break;3658case GDScriptParser::COMPLETION_RESOURCE_PATH: {3659if (EDITOR_GET("text_editor/completion/complete_file_paths")) {3660_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);3661r_forced = true;3662}3663} break;3664case GDScriptParser::COMPLETION_CALL_ARGUMENTS: {3665if (!completion_context.node) {3666break;3667}3668_find_call_arguments(completion_context, completion_context.node, completion_context.current_argument, options, r_forced, r_call_hint);3669} break;3670case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {3671GDScriptParser::DataType native_type = completion_context.current_class->base_type;3672GDScriptParser::FunctionNode *function_node = static_cast<GDScriptParser::FunctionNode *>(completion_context.node);3673bool is_static = function_node != nullptr && function_node->is_static;3674while (native_type.is_set() && native_type.kind != GDScriptParser::DataType::NATIVE) {3675switch (native_type.kind) {3676case GDScriptParser::DataType::CLASS: {3677for (const GDScriptParser::ClassNode::Member &member : native_type.class_type->members) {3678if (member.type != GDScriptParser::ClassNode::Member::FUNCTION) {3679continue;3680}36813682if (options.has(member.function->identifier->name)) {3683continue;3684}36853686if (completion_context.current_class->has_function(member.get_name()) && completion_context.current_class->get_member(member.get_name()).function != function_node) {3687continue;3688}36893690if (is_static != member.function->is_static) {3691continue;3692}36933694String display_name = member.function->identifier->name;3695display_name += member.function->signature + ":";3696ScriptLanguage::CodeCompletionOption option(display_name, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3697options.insert(member.function->identifier->name, option); // Insert name instead of display to track duplicates.3698}3699native_type = native_type.class_type->base_type;3700} break;3701default: {3702native_type.kind = GDScriptParser::DataType::UNRESOLVED;3703} break;3704}3705}37063707if (!native_type.is_set()) {3708break;3709}37103711StringName class_name = native_type.native_type;3712if (!ClassDB::class_exists(class_name)) {3713break;3714}37153716const bool type_hints = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");37173718List<MethodInfo> virtual_methods;3719if (is_static) {3720// Not truly a virtual method, but can also be "overridden".3721MethodInfo static_init("_static_init");3722static_init.return_val.type = Variant::NIL;3723static_init.flags |= METHOD_FLAG_STATIC | METHOD_FLAG_VIRTUAL;3724virtual_methods.push_back(static_init);3725} else {3726ClassDB::get_virtual_methods(class_name, &virtual_methods);3727}37283729for (const MethodInfo &mi : virtual_methods) {3730if (options.has(mi.name)) {3731continue;3732}3733if (completion_context.current_class->has_function(mi.name) && completion_context.current_class->get_member(mi.name).function != function_node) {3734continue;3735}3736String method_hint = mi.name;3737if (method_hint.contains_char(':')) {3738method_hint = method_hint.get_slicec(':', 0);3739}3740method_hint += "(";37413742for (int64_t i = 0; i < mi.arguments.size(); ++i) {3743if (i > 0) {3744method_hint += ", ";3745}3746String arg = mi.arguments[i].name;3747if (arg.contains_char(':')) {3748arg = arg.substr(0, arg.find_char(':'));3749}3750method_hint += arg;3751if (type_hints) {3752method_hint += ": " + _get_visual_datatype(mi.arguments[i], true, class_name);3753}3754}3755if (mi.flags & METHOD_FLAG_VARARG) {3756if (!mi.arguments.is_empty()) {3757method_hint += ", ";3758}3759method_hint += "...args"; // `MethodInfo` does not support the rest parameter name.3760if (type_hints) {3761method_hint += ": Array";3762}3763}3764method_hint += ")";3765if (type_hints) {3766method_hint += " -> " + _get_visual_datatype(mi.return_val, false, class_name);3767}3768method_hint += ":";37693770ScriptLanguage::CodeCompletionOption option(method_hint, ScriptLanguage::CODE_COMPLETION_KIND_FUNCTION);3771options.insert(option.display, option);3772}3773} break;3774case GDScriptParser::COMPLETION_GET_NODE: {3775// Handles the `$Node/Path` or `$"Some NodePath"` syntax specifically.3776if (p_owner) {3777List<String> opts;3778p_owner->get_argument_options("get_node", 0, &opts);37793780bool for_unique_name = false;3781if (completion_context.node != nullptr && completion_context.node->type == GDScriptParser::Node::GET_NODE && !static_cast<GDScriptParser::GetNodeNode *>(completion_context.node)->use_dollar) {3782for_unique_name = true;3783}37843785for (const String &E : opts) {3786r_forced = true;3787String opt = E.strip_edges();3788if (opt.is_quoted()) {3789// Remove quotes so that we can handle user preferred quote style,3790// or handle NodePaths which are valid identifiers and don't need quotes.3791opt = opt.unquote();3792}37933794if (for_unique_name) {3795if (!opt.begins_with("%")) {3796continue;3797}3798opt = opt.substr(1);3799}38003801// The path needs quotes if at least one of its components (excluding `%` prefix and `/` separations)3802// is not a valid identifier.3803bool path_needs_quote = false;3804for (const String &part : opt.trim_prefix("%").split("/")) {3805if (!part.is_valid_ascii_identifier()) {3806path_needs_quote = true;3807break;3808}3809}38103811if (path_needs_quote) {3812// Ignore quote_style and just use double quotes for paths with apostrophes.3813// Double quotes don't need to be checked because they're not valid in node and property names.3814opt = opt.quote(opt.contains_char('\'') ? "\"" : quote_style); // Handle user preference.3815}3816ScriptLanguage::CodeCompletionOption option(opt, ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);3817options.insert(option.display, option);3818}38193820if (!for_unique_name) {3821// Get autoloads.3822for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton()->get_autoload_list()) {3823String path = "/root/" + E.key;3824ScriptLanguage::CodeCompletionOption option(path.quote(quote_style), ScriptLanguage::CODE_COMPLETION_KIND_NODE_PATH);3825options.insert(option.display, option);3826}3827}3828}3829} break;3830case GDScriptParser::COMPLETION_SUPER:3831break;3832case GDScriptParser::COMPLETION_SUPER_METHOD: {3833if (!completion_context.current_class) {3834break;3835}3836_find_identifiers_in_class(completion_context.current_class, true, false, false, true, !_guess_expecting_callable(completion_context), options, 0);3837} break;3838}38393840for (const KeyValue<String, ScriptLanguage::CodeCompletionOption> &E : options) {3841r_options->push_back(E.value);3842}38433844return OK;3845}38463847#else // !TOOLS_ENABLED38483849Error 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) {3850return OK;3851}38523853#endif // TOOLS_ENABLED38543855//////// END COMPLETION //////////38563857String GDScriptLanguage::_get_indentation() const {3858#ifdef TOOLS_ENABLED3859if (Engine::get_singleton()->is_editor_hint()) {3860bool use_space_indentation = EDITOR_GET("text_editor/behavior/indent/type");38613862if (use_space_indentation) {3863int indent_size = EDITOR_GET("text_editor/behavior/indent/size");3864return String(" ").repeat(indent_size);3865}3866}3867#endif3868return "\t";3869}38703871void GDScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {3872String indent = _get_indentation();38733874Vector<String> lines = p_code.split("\n");3875List<int> indent_stack;38763877for (int i = 0; i < lines.size(); i++) {3878String l = lines[i];3879int tc = 0;3880for (int j = 0; j < l.length(); j++) {3881if (l[j] == ' ' || l[j] == '\t') {3882tc++;3883} else {3884break;3885}3886}38873888String st = l.substr(tc).strip_edges();3889if (st.is_empty() || st.begins_with("#")) {3890continue; //ignore!3891}38923893int ilevel = 0;3894if (indent_stack.size()) {3895ilevel = indent_stack.back()->get();3896}38973898if (tc > ilevel) {3899indent_stack.push_back(tc);3900} else if (tc < ilevel) {3901while (indent_stack.size() && indent_stack.back()->get() > tc) {3902indent_stack.pop_back();3903}39043905if (indent_stack.size() && indent_stack.back()->get() != tc) {3906indent_stack.push_back(tc); // this is not right but gets the job done3907}3908}39093910if (i >= p_from_line) {3911l = indent.repeat(indent_stack.size()) + st;3912} else if (i > p_to_line) {3913break;3914}39153916lines.write[i] = l;3917}39183919p_code = "";3920for (int i = 0; i < lines.size(); i++) {3921if (i > 0) {3922p_code += "\n";3923}3924p_code += lines[i];3925}3926}39273928#ifdef TOOLS_ENABLED39293930static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, const String &p_symbol, GDScriptLanguage::LookupResult &r_result) {3931GDScriptParser::DataType base_type = p_base;39323933while (true) {3934switch (base_type.kind) {3935case GDScriptParser::DataType::CLASS: {3936ERR_FAIL_NULL_V(base_type.class_type, ERR_BUG);39373938String name = p_symbol;3939if (name == "new") {3940name = "_init";3941}39423943if (!base_type.class_type->has_member(name)) {3944base_type = base_type.class_type->base_type;3945break;3946}39473948const GDScriptParser::ClassNode::Member &member = base_type.class_type->get_member(name);39493950switch (member.type) {3951case GDScriptParser::ClassNode::Member::UNDEFINED:3952case GDScriptParser::ClassNode::Member::GROUP:3953return ERR_BUG;3954case GDScriptParser::ClassNode::Member::CLASS: {3955String doc_type_name;3956String doc_enum_name;3957GDScriptDocGen::doctype_from_gdtype(GDScriptAnalyzer::type_from_metatype(member.get_datatype()), doc_type_name, doc_enum_name);39583959r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;3960r_result.class_name = doc_type_name;3961} break;3962case GDScriptParser::ClassNode::Member::CONSTANT:3963r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;3964break;3965case GDScriptParser::ClassNode::Member::FUNCTION:3966r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;3967break;3968case GDScriptParser::ClassNode::Member::SIGNAL:3969r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;3970break;3971case GDScriptParser::ClassNode::Member::VARIABLE:3972r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;3973break;3974case GDScriptParser::ClassNode::Member::ENUM:3975r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;3976break;3977case GDScriptParser::ClassNode::Member::ENUM_VALUE:3978r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;3979break;3980}39813982if (member.type != GDScriptParser::ClassNode::Member::CLASS) {3983String doc_type_name;3984String doc_enum_name;3985GDScriptDocGen::doctype_from_gdtype(GDScriptAnalyzer::type_from_metatype(base_type), doc_type_name, doc_enum_name);39863987r_result.class_name = doc_type_name;3988r_result.class_member = name;3989}39903991Error err = OK;3992r_result.script = GDScriptCache::get_shallow_script(base_type.script_path, err);3993r_result.script_path = base_type.script_path;3994r_result.location = member.get_line();3995return err;3996} break;3997case GDScriptParser::DataType::SCRIPT: {3998const Ref<Script> scr = base_type.script_type;39994000if (scr.is_null()) {4001return ERR_CANT_RESOLVE;4002}40034004String name = p_symbol;4005if (name == "new") {4006name = "_init";4007}40084009const int line = scr->get_member_line(name);4010if (line >= 0) {4011bool found_type = false;4012r_result.type = ScriptLanguage::LOOKUP_RESULT_SCRIPT_LOCATION;4013{4014List<PropertyInfo> properties;4015scr->get_script_property_list(&properties);4016for (const PropertyInfo &property : properties) {4017if (property.name == name && (property.usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) {4018found_type = true;4019r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;4020r_result.class_name = scr->get_doc_class_name();4021r_result.class_member = name;4022break;4023}4024}4025}4026if (!found_type) {4027List<MethodInfo> methods;4028scr->get_script_method_list(&methods);4029for (const MethodInfo &method : methods) {4030if (method.name == name) {4031found_type = true;4032r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4033r_result.class_name = scr->get_doc_class_name();4034r_result.class_member = name;4035break;4036}4037}4038}4039if (!found_type) {4040List<MethodInfo> signals;4041scr->get_script_method_list(&signals);4042for (const MethodInfo &signal : signals) {4043if (signal.name == name) {4044found_type = true;4045r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;4046r_result.class_name = scr->get_doc_class_name();4047r_result.class_member = name;4048break;4049}4050}4051}4052if (!found_type) {4053const Ref<GDScript> gds = scr;4054if (gds.is_valid()) {4055const Ref<GDScript> *subclass = gds->get_subclasses().getptr(name);4056if (subclass != nullptr) {4057found_type = true;4058r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4059r_result.class_name = subclass->ptr()->get_doc_class_name();4060}4061// TODO: enums.4062}4063}4064if (!found_type) {4065HashMap<StringName, Variant> constants;4066scr->get_constants(&constants);4067if (constants.has(name)) {4068found_type = true;4069r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4070r_result.class_name = scr->get_doc_class_name();4071r_result.class_member = name;4072}4073}40744075r_result.script = scr;4076r_result.script_path = base_type.script_path;4077r_result.location = line;4078return OK;4079}40804081const Ref<Script> base_script = scr->get_base_script();4082if (base_script.is_valid()) {4083base_type.script_type = base_script;4084} else {4085base_type.kind = GDScriptParser::DataType::NATIVE;4086base_type.builtin_type = Variant::OBJECT;4087base_type.native_type = scr->get_instance_base_type();4088}4089} break;4090case GDScriptParser::DataType::NATIVE: {4091const StringName &class_name = base_type.native_type;40924093ERR_FAIL_COND_V(!ClassDB::class_exists(class_name), ERR_BUG);40944095if (ClassDB::has_method(class_name, p_symbol, true)) {4096r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4097r_result.class_name = class_name;4098r_result.class_member = p_symbol;4099return OK;4100}41014102List<MethodInfo> virtual_methods;4103ClassDB::get_virtual_methods(class_name, &virtual_methods, true);4104for (const MethodInfo &E : virtual_methods) {4105if (E.name == p_symbol) {4106r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4107r_result.class_name = class_name;4108r_result.class_member = p_symbol;4109return OK;4110}4111}41124113if (ClassDB::has_signal(class_name, p_symbol, true)) {4114r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_SIGNAL;4115r_result.class_name = class_name;4116r_result.class_member = p_symbol;4117return OK;4118}41194120List<StringName> enums;4121ClassDB::get_enum_list(class_name, &enums);4122for (const StringName &E : enums) {4123if (E == p_symbol) {4124r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4125r_result.class_name = class_name;4126r_result.class_member = p_symbol;4127return OK;4128}4129}41304131if (!String(ClassDB::get_integer_constant_enum(class_name, p_symbol, true)).is_empty()) {4132r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4133r_result.class_name = class_name;4134r_result.class_member = p_symbol;4135return OK;4136}41374138List<String> constants;4139ClassDB::get_integer_constant_list(class_name, &constants, true);4140for (const String &E : constants) {4141if (E == p_symbol) {4142r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4143r_result.class_name = class_name;4144r_result.class_member = p_symbol;4145return OK;4146}4147}41484149if (ClassDB::has_property(class_name, p_symbol, true)) {4150PropertyInfo prop_info;4151ClassDB::get_property_info(class_name, p_symbol, &prop_info, true);4152if (prop_info.usage & PROPERTY_USAGE_INTERNAL) {4153return ERR_CANT_RESOLVE;4154}41554156r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;4157r_result.class_name = class_name;4158r_result.class_member = p_symbol;4159return OK;4160}41614162const StringName parent_class = ClassDB::get_parent_class(class_name);4163if (parent_class != StringName()) {4164base_type.native_type = parent_class;4165} else {4166return ERR_CANT_RESOLVE;4167}4168} break;4169case GDScriptParser::DataType::BUILTIN: {4170if (base_type.is_meta_type) {4171if (Variant::has_enum(base_type.builtin_type, p_symbol)) {4172r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4173r_result.class_name = Variant::get_type_name(base_type.builtin_type);4174r_result.class_member = p_symbol;4175return OK;4176}41774178if (Variant::has_constant(base_type.builtin_type, p_symbol)) {4179r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4180r_result.class_name = Variant::get_type_name(base_type.builtin_type);4181r_result.class_member = p_symbol;4182return OK;4183}4184} else {4185if (Variant::has_member(base_type.builtin_type, p_symbol)) {4186r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_PROPERTY;4187r_result.class_name = Variant::get_type_name(base_type.builtin_type);4188r_result.class_member = p_symbol;4189return OK;4190}4191}41924193if (Variant::has_builtin_method(base_type.builtin_type, p_symbol)) {4194r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4195r_result.class_name = Variant::get_type_name(base_type.builtin_type);4196r_result.class_member = p_symbol;4197return OK;4198}41994200return ERR_CANT_RESOLVE;4201} break;4202case GDScriptParser::DataType::ENUM: {4203if (base_type.is_meta_type) {4204if (base_type.enum_values.has(p_symbol)) {4205String doc_type_name;4206String doc_enum_name;4207GDScriptDocGen::doctype_from_gdtype(GDScriptAnalyzer::type_from_metatype(base_type), doc_type_name, doc_enum_name);42084209if (CoreConstants::is_global_enum(doc_enum_name)) {4210r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4211r_result.class_name = "@GlobalScope";4212r_result.class_member = p_symbol;4213return OK;4214} else {4215const int dot_pos = doc_enum_name.rfind_char('.');4216if (dot_pos >= 0) {4217Error err = OK;4218r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4219if (base_type.class_type != nullptr) {4220// For script enums the value isn't accessible as class constant so we need the full enum name.4221r_result.class_name = doc_enum_name;4222r_result.class_member = p_symbol;4223r_result.script = GDScriptCache::get_shallow_script(base_type.script_path, err);4224r_result.script_path = base_type.script_path;4225const String enum_name = doc_enum_name.substr(dot_pos + 1);4226if (base_type.class_type->has_member(enum_name)) {4227const GDScriptParser::ClassNode::Member member = base_type.class_type->get_member(enum_name);4228if (member.type == GDScriptParser::ClassNode::Member::ENUM) {4229for (const GDScriptParser::EnumNode::Value &value : member.m_enum->values) {4230if (value.identifier->name == p_symbol) {4231r_result.location = value.line;4232break;4233}4234}4235}4236}4237} else if (base_type.script_type.is_valid()) {4238// For script enums the value isn't accessible as class constant so we need the full enum name.4239r_result.class_name = doc_enum_name;4240r_result.class_member = p_symbol;4241r_result.script = base_type.script_type;4242r_result.script_path = base_type.script_path;4243// TODO: Find a way to obtain enum value location for a script4244r_result.location = base_type.script_type->get_member_line(doc_enum_name.substr(dot_pos + 1));4245} else {4246r_result.class_name = doc_enum_name.left(dot_pos);4247r_result.class_member = p_symbol;4248}4249return err;4250}4251}4252} else if (Variant::has_builtin_method(Variant::DICTIONARY, p_symbol)) {4253r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4254r_result.class_name = "Dictionary";4255r_result.class_member = p_symbol;4256return OK;4257}4258}42594260return ERR_CANT_RESOLVE;4261} break;4262case GDScriptParser::DataType::VARIANT: {4263if (base_type.is_meta_type) {4264const String enum_name = "Variant." + p_symbol;4265if (CoreConstants::is_global_enum(enum_name)) {4266r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4267r_result.class_name = "@GlobalScope";4268r_result.class_member = enum_name;4269return OK;4270}4271}42724273return ERR_CANT_RESOLVE;4274} break;4275case GDScriptParser::DataType::RESOLVING:4276case GDScriptParser::DataType::UNRESOLVED: {4277return ERR_CANT_RESOLVE;4278} break;4279}4280}42814282return ERR_CANT_RESOLVE;4283}42844285::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) {4286// Before parsing, try the usual stuff.4287if (ClassDB::class_exists(p_symbol)) {4288r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4289r_result.class_name = p_symbol;4290return OK;4291}42924293if (Variant::get_type_by_name(p_symbol) < Variant::VARIANT_MAX) {4294r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4295r_result.class_name = p_symbol;4296return OK;4297}42984299if (p_symbol == "Variant") {4300r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4301r_result.class_name = "Variant";4302return OK;4303}43044305if (p_symbol == "PI" || p_symbol == "TAU" || p_symbol == "INF" || p_symbol == "NAN") {4306r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4307r_result.class_name = "@GDScript";4308r_result.class_member = p_symbol;4309return OK;4310}43114312GDScriptParser parser;4313parser.parse(p_code, p_path, true);43144315GDScriptParser::CompletionContext context = parser.get_completion_context();4316context.base = p_owner;43174318// Allows class functions with the names like built-ins to be handled properly.4319if (context.type != GDScriptParser::COMPLETION_ATTRIBUTE) {4320// Need special checks for `assert` and `preload` as they are technically4321// keywords, so are not registered in `GDScriptUtilityFunctions`.4322if (GDScriptUtilityFunctions::function_exists(p_symbol) || p_symbol == "assert" || p_symbol == "preload") {4323r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4324r_result.class_name = "@GDScript";4325r_result.class_member = p_symbol;4326return OK;4327}4328}43294330GDScriptAnalyzer analyzer(&parser);4331analyzer.analyze();43324333if (context.current_class && context.current_class->extends.size() > 0) {4334StringName class_name = context.current_class->extends[0]->name;43354336bool success = false;4337ClassDB::get_integer_constant(class_name, p_symbol, &success);4338if (success) {4339r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4340r_result.class_name = class_name;4341r_result.class_member = p_symbol;4342return OK;4343}4344do {4345List<StringName> enums;4346ClassDB::get_enum_list(class_name, &enums, true);4347for (const StringName &enum_name : enums) {4348if (enum_name == p_symbol) {4349r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4350r_result.class_name = class_name;4351r_result.class_member = p_symbol;4352return OK;4353}4354}4355class_name = ClassDB::get_parent_class_nocheck(class_name);4356} while (class_name != StringName());4357}43584359const GDScriptParser::TypeNode *type_node = dynamic_cast<const GDScriptParser::TypeNode *>(context.node);4360if (type_node != nullptr && !type_node->type_chain.is_empty()) {4361StringName class_name = type_node->type_chain[0]->name;4362if (ScriptServer::is_global_class(class_name)) {4363class_name = ScriptServer::get_global_class_native_base(class_name);4364}4365do {4366List<StringName> enums;4367ClassDB::get_enum_list(class_name, &enums, true);4368for (const StringName &enum_name : enums) {4369if (enum_name == p_symbol) {4370r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4371r_result.class_name = class_name;4372r_result.class_member = p_symbol;4373return OK;4374}4375}4376class_name = ClassDB::get_parent_class_nocheck(class_name);4377} while (class_name != StringName());4378}43794380bool is_function = false;43814382switch (context.type) {4383case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT_OR_STATIC_METHOD: {4384GDScriptParser::DataType base_type;4385base_type.kind = GDScriptParser::DataType::BUILTIN;4386base_type.builtin_type = context.builtin_type;4387base_type.is_meta_type = true;4388if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4389return OK;4390}4391} break;4392case GDScriptParser::COMPLETION_SUPER: {4393if (context.current_class && context.current_function) {4394if (_lookup_symbol_from_base(context.current_class->base_type, context.current_function->info.name, r_result) == OK) {4395return OK;4396}4397}4398} break;4399case GDScriptParser::COMPLETION_SUPER_METHOD:4400case GDScriptParser::COMPLETION_METHOD:4401case GDScriptParser::COMPLETION_ASSIGN:4402case GDScriptParser::COMPLETION_CALL_ARGUMENTS:4403case GDScriptParser::COMPLETION_IDENTIFIER:4404case GDScriptParser::COMPLETION_PROPERTY_METHOD:4405case GDScriptParser::COMPLETION_SUBSCRIPT: {4406GDScriptParser::DataType base_type;4407if (context.current_class) {4408if (context.type != GDScriptParser::COMPLETION_SUPER_METHOD) {4409base_type = context.current_class->get_datatype();4410} else {4411base_type = context.current_class->base_type;4412}4413} else {4414break;4415}44164417if (!is_function && context.current_suite) {4418// Lookup local variables.4419const GDScriptParser::SuiteNode *suite = context.current_suite;4420while (suite) {4421if (suite->has_local(p_symbol)) {4422const GDScriptParser::SuiteNode::Local &local = suite->get_local(p_symbol);44234424switch (local.type) {4425case GDScriptParser::SuiteNode::Local::UNDEFINED:4426return ERR_BUG;4427case GDScriptParser::SuiteNode::Local::CONSTANT:4428r_result.type = ScriptLanguage::LOOKUP_RESULT_LOCAL_CONSTANT;4429r_result.description = local.constant->doc_data.description;4430r_result.is_deprecated = local.constant->doc_data.is_deprecated;4431r_result.deprecated_message = local.constant->doc_data.deprecated_message;4432r_result.is_experimental = local.constant->doc_data.is_experimental;4433r_result.experimental_message = local.constant->doc_data.experimental_message;4434if (local.constant->initializer != nullptr) {4435r_result.value = GDScriptDocGen::docvalue_from_expression(local.constant->initializer);4436}4437break;4438case GDScriptParser::SuiteNode::Local::VARIABLE:4439r_result.type = ScriptLanguage::LOOKUP_RESULT_LOCAL_VARIABLE;4440r_result.description = local.variable->doc_data.description;4441r_result.is_deprecated = local.variable->doc_data.is_deprecated;4442r_result.deprecated_message = local.variable->doc_data.deprecated_message;4443r_result.is_experimental = local.variable->doc_data.is_experimental;4444r_result.experimental_message = local.variable->doc_data.experimental_message;4445if (local.variable->initializer != nullptr) {4446r_result.value = GDScriptDocGen::docvalue_from_expression(local.variable->initializer);4447}4448break;4449case GDScriptParser::SuiteNode::Local::PARAMETER:4450case GDScriptParser::SuiteNode::Local::FOR_VARIABLE:4451case GDScriptParser::SuiteNode::Local::PATTERN_BIND:4452r_result.type = ScriptLanguage::LOOKUP_RESULT_LOCAL_VARIABLE;4453break;4454}44554456GDScriptDocGen::doctype_from_gdtype(local.get_datatype(), r_result.doc_type, r_result.enumeration);44574458Error err = OK;4459r_result.script = GDScriptCache::get_shallow_script(base_type.script_path, err);4460r_result.script_path = base_type.script_path;4461r_result.location = local.start_line;4462return err;4463}4464suite = suite->parent_block;4465}4466}44674468if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4469return OK;4470}44714472if (!is_function) {4473if (ProjectSettings::get_singleton()->has_autoload(p_symbol)) {4474const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(p_symbol);4475if (autoload.is_singleton) {4476String scr_path = autoload.path;4477if (!scr_path.ends_with(".gd")) {4478// Not a script, try find the script anyway, may have some success.4479scr_path = scr_path.get_basename() + ".gd";4480}44814482if (FileAccess::exists(scr_path)) {4483r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4484r_result.class_name = p_symbol;4485r_result.script = ResourceLoader::load(scr_path);4486r_result.script_path = scr_path;4487r_result.location = 0;4488return OK;4489}4490}4491}44924493if (ScriptServer::is_global_class(p_symbol)) {4494const String scr_path = ScriptServer::get_global_class_path(p_symbol);4495const Ref<Script> scr = ResourceLoader::load(scr_path);4496if (scr.is_null()) {4497return ERR_BUG;4498}4499r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4500r_result.class_name = scr->get_doc_class_name();4501r_result.script = scr;4502r_result.script_path = scr_path;4503r_result.location = 0;4504return OK;4505}45064507const HashMap<StringName, int> &global_map = GDScriptLanguage::get_singleton()->get_global_map();4508if (global_map.has(p_symbol)) {4509Variant value = GDScriptLanguage::get_singleton()->get_global_array()[global_map[p_symbol]];4510if (value.get_type() == Variant::OBJECT) {4511const Object *obj = value;4512if (obj) {4513if (Object::cast_to<GDScriptNativeClass>(obj)) {4514r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4515r_result.class_name = Object::cast_to<GDScriptNativeClass>(obj)->get_name();4516} else {4517r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS;4518r_result.class_name = obj->get_class();4519}4520return OK;4521}4522}4523}45244525if (CoreConstants::is_global_enum(p_symbol)) {4526r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ENUM;4527r_result.class_name = "@GlobalScope";4528r_result.class_member = p_symbol;4529return OK;4530}45314532if (CoreConstants::is_global_constant(p_symbol)) {4533r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_CONSTANT;4534r_result.class_name = "@GlobalScope";4535r_result.class_member = p_symbol;4536return OK;4537}45384539if (Variant::has_utility_function(p_symbol)) {4540r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_METHOD;4541r_result.class_name = "@GlobalScope";4542r_result.class_member = p_symbol;4543return OK;4544}4545}4546} break;4547case GDScriptParser::COMPLETION_ATTRIBUTE_METHOD:4548case GDScriptParser::COMPLETION_ATTRIBUTE: {4549if (context.node->type != GDScriptParser::Node::SUBSCRIPT) {4550break;4551}4552const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(context.node);4553if (!subscript->is_attribute) {4554break;4555}4556GDScriptCompletionIdentifier base;45574558bool found_type = _get_subscript_type(context, subscript, base.type);4559if (!found_type && !_guess_expression_type(context, subscript->base, base)) {4560break;4561}45624563if (_lookup_symbol_from_base(base.type, p_symbol, r_result) == OK) {4564return OK;4565}4566} break;4567case GDScriptParser::COMPLETION_TYPE_ATTRIBUTE: {4568if (context.node == nullptr || context.node->type != GDScriptParser::Node::TYPE) {4569break;4570}4571const GDScriptParser::TypeNode *type = static_cast<const GDScriptParser::TypeNode *>(context.node);45724573GDScriptParser::DataType base_type;4574const GDScriptParser::IdentifierNode *prev = nullptr;4575for (const GDScriptParser::IdentifierNode *E : type->type_chain) {4576if (E->name == p_symbol && prev != nullptr) {4577base_type = prev->get_datatype();4578break;4579}4580prev = E;4581}4582if (base_type.kind != GDScriptParser::DataType::CLASS) {4583GDScriptCompletionIdentifier base;4584if (!_guess_expression_type(context, prev, base)) {4585break;4586}4587base_type = base.type;4588}45894590if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4591return OK;4592}4593} break;4594case GDScriptParser::COMPLETION_OVERRIDE_METHOD: {4595GDScriptParser::DataType base_type = context.current_class->base_type;45964597if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4598return OK;4599}4600} break;4601case GDScriptParser::COMPLETION_PROPERTY_DECLARATION_OR_TYPE:4602case GDScriptParser::COMPLETION_TYPE_NAME_OR_VOID:4603case GDScriptParser::COMPLETION_TYPE_NAME: {4604GDScriptParser::DataType base_type = context.current_class->get_datatype();46054606if (_lookup_symbol_from_base(base_type, p_symbol, r_result) == OK) {4607return OK;4608}4609} break;4610case GDScriptParser::COMPLETION_ANNOTATION: {4611const String annotation_symbol = "@" + p_symbol;4612if (parser.annotation_exists(annotation_symbol)) {4613r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION;4614r_result.class_name = "@GDScript";4615r_result.class_member = annotation_symbol;4616return OK;4617}4618} break;4619default: {4620}4621}46224623return ERR_CANT_RESOLVE;4624}46254626#endif // TOOLS_ENABLED462746284629