@@ -1764,6 +1764,113 @@ static bool _is_expression_named_identifier(const GDScriptParser::ExpressionNode
17641764 return false ;
17651765}
17661766
1767+ // Creates a map of exemplary results for some functions that return a structured dictionary.
1768+ // Setting this example as value allows autocompletion to suggest the specific keys in some cases.
1769+ static HashMap<String, Dictionary> make_structure_samples () {
1770+ HashMap<String, Dictionary> res;
1771+ const Array arr;
1772+
1773+ {
1774+ Dictionary d;
1775+ d.set (" major" , 0 );
1776+ d.set (" minor" , 0 );
1777+ d.set (" patch" , 0 );
1778+ d.set (" hex" , 0 );
1779+ d.set (" status" , String ());
1780+ d.set (" build" , String ());
1781+ d.set (" hash" , String ());
1782+ d.set (" timestamp" , 0 );
1783+ d.set (" string" , String ());
1784+ res[" Engine::get_version_info" ] = d;
1785+ }
1786+
1787+ {
1788+ Dictionary d;
1789+ d.set (" lead_developers" , arr);
1790+ d.set (" founders" , arr);
1791+ d.set (" project_managers" , arr);
1792+ d.set (" developers" , arr);
1793+ res[" Engine::get_author_info" ] = d;
1794+ }
1795+
1796+ {
1797+ Dictionary d;
1798+ d.set (" platinum_sponsors" , arr);
1799+ d.set (" gold_sponsors" , arr);
1800+ d.set (" silver_sponsors" , arr);
1801+ d.set (" bronze_sponsors" , arr);
1802+ d.set (" mini_sponsors" , arr);
1803+ d.set (" gold_donors" , arr);
1804+ d.set (" silver_donors" , arr);
1805+ d.set (" bronze_donors" , arr);
1806+ res[" Engine::get_donor_info" ] = d;
1807+ }
1808+
1809+ {
1810+ Dictionary d;
1811+ d.set (" physical" , -1 );
1812+ d.set (" free" , -1 );
1813+ d.set (" available" , -1 );
1814+ d.set (" stack" , -1 );
1815+ res[" OS::get_memory_info" ] = d;
1816+ }
1817+
1818+ {
1819+ Dictionary d;
1820+ d.set (" year" , 0 );
1821+ d.set (" month" , 0 );
1822+ d.set (" day" , 0 );
1823+ d.set (" weekday" , 0 );
1824+ d.set (" hour" , 0 );
1825+ d.set (" minute" , 0 );
1826+ d.set (" second" , 0 );
1827+ d.set (" dst" , 0 );
1828+ res[" Time::get_datetime_dict_from_system" ] = d;
1829+ }
1830+
1831+ {
1832+ Dictionary d;
1833+ d.set (" year" , 0 );
1834+ d.set (" month" , 0 );
1835+ d.set (" day" , 0 );
1836+ d.set (" weekday" , 0 );
1837+ d.set (" hour" , 0 );
1838+ d.set (" minute" , 0 );
1839+ d.set (" second" , 0 );
1840+ res[" Time::get_datetime_dict_from_unix_time" ] = d;
1841+ }
1842+
1843+ {
1844+ Dictionary d;
1845+ d.set (" year" , 0 );
1846+ d.set (" month" , 0 );
1847+ d.set (" day" , 0 );
1848+ d.set (" weekday" , 0 );
1849+ res[" Time::get_date_dict_from_system" ] = d;
1850+ res[" Time::get_date_dict_from_unix_time" ] = d;
1851+ }
1852+
1853+ {
1854+ Dictionary d;
1855+ d.set (" hour" , 0 );
1856+ d.set (" minute" , 0 );
1857+ d.set (" second" , 0 );
1858+ res[" Time::get_time_dict_from_system" ] = d;
1859+ res[" Time::get_time_dict_from_unix_time" ] = d;
1860+ }
1861+
1862+ {
1863+ Dictionary d;
1864+ d.set (" bias" , 0 );
1865+ d.set (" name" , String ());
1866+ res[" Time::get_time_zone_from_system" ] = d;
1867+ }
1868+
1869+ return res;
1870+ }
1871+
1872+ static const HashMap<String, Dictionary> structure_examples = make_structure_samples();
1873+
17671874static bool _guess_expression_type (GDScriptParser::CompletionContext &p_context, const GDScriptParser::ExpressionNode *p_expression, GDScriptCompletionIdentifier &r_type) {
17681875 bool found = false ;
17691876
@@ -1884,156 +1991,68 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
18841991 } break ;
18851992 case GDScriptParser::Node::CALL: {
18861993 const GDScriptParser::CallNode *call = static_cast <const GDScriptParser::CallNode *>(p_expression);
1887- if (GDScriptParser::get_builtin_type (call->function_name ) < Variant::VARIANT_MAX) {
1888- r_type.type .type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
1889- r_type.type .kind = GDScriptParser::DataType::BUILTIN;
1890- r_type.type .builtin_type = GDScriptParser::get_builtin_type (call->function_name );
1891- found = true ;
1892- break ;
1893- } else if (GDScriptUtilityFunctions::function_exists (call->function_name )) {
1894- MethodInfo mi = GDScriptUtilityFunctions::get_function_info (call->function_name );
1895- r_type = _type_from_property (mi.return_val );
1896- found = true ;
1897- break ;
1898- } else {
1899- GDScriptParser::CompletionContext c = p_context;
1900- c.current_line = call->start_line ;
1994+ GDScriptParser::CompletionContext c = p_context;
1995+ c.current_line = call->start_line ;
19011996
1902- GDScriptParser::Node::Type callee_type = call->get_callee_type ();
1997+ GDScriptParser::Node::Type callee_type = call->get_callee_type ();
19031998
1904- GDScriptCompletionIdentifier base;
1905- if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super ) {
1906- // Simple call, so base is 'self'.
1907- if (p_context.current_class ) {
1908- if (call->is_super ) {
1909- base.type = p_context.current_class ->base_type ;
1910- base.value = p_context.base ;
1911- } else {
1912- base.type .kind = GDScriptParser::DataType::CLASS;
1913- base.type .type_source = GDScriptParser::DataType::INFERRED;
1914- base.type .is_constant = true ;
1915- base.type .class_type = p_context.current_class ;
1916- base.value = p_context.base ;
1917- }
1999+ GDScriptCompletionIdentifier base;
2000+ if (callee_type == GDScriptParser::Node::IDENTIFIER || call->is_super ) {
2001+ // Simple call, so base is 'self'.
2002+ if (p_context.current_class ) {
2003+ if (call->is_super ) {
2004+ base.type = p_context.current_class ->base_type ;
2005+ base.value = p_context.base ;
19182006 } else {
1919- break ;
1920- }
1921- } else if (callee_type == GDScriptParser::Node::SUBSCRIPT && static_cast <const GDScriptParser::SubscriptNode *>(call->callee )->is_attribute ) {
1922- if (!_guess_expression_type (c, static_cast <const GDScriptParser::SubscriptNode *>(call->callee )->base , base)) {
1923- found = false ;
1924- break ;
2007+ base.type .kind = GDScriptParser::DataType::CLASS;
2008+ base.type .type_source = GDScriptParser::DataType::INFERRED;
2009+ base.type .is_constant = true ;
2010+ base.type .class_type = p_context.current_class ;
2011+ base.value = p_context.base ;
19252012 }
19262013 } else {
19272014 break ;
19282015 }
2016+ } else if (callee_type == GDScriptParser::Node::SUBSCRIPT && static_cast <const GDScriptParser::SubscriptNode *>(call->callee )->is_attribute ) {
2017+ if (!_guess_expression_type (c, static_cast <const GDScriptParser::SubscriptNode *>(call->callee )->base , base)) {
2018+ found = false ;
2019+ break ;
2020+ }
2021+ } else {
2022+ break ;
2023+ }
19292024
1930- // Try call if constant methods with constant arguments
1931- if (base.type .is_constant && base.value .get_type () == Variant::OBJECT) {
1932- GDScriptParser::DataType native_type = base.type ;
1933-
1934- while (native_type.kind == GDScriptParser::DataType::CLASS) {
1935- native_type = native_type.class_type ->base_type ;
1936- }
1937-
1938- while (native_type.kind == GDScriptParser::DataType::SCRIPT) {
1939- if (native_type.script_type .is_valid ()) {
1940- Ref<Script> parent = native_type.script_type ->get_base_script ();
1941- if (parent.is_valid ()) {
1942- native_type.script_type = parent;
1943- } else {
1944- native_type.kind = GDScriptParser::DataType::NATIVE;
1945- native_type.builtin_type = Variant::OBJECT;
1946- native_type.native_type = native_type.script_type ->get_instance_base_type ();
1947- if (!ClassDB::class_exists (native_type.native_type )) {
1948- native_type.kind = GDScriptParser::DataType::UNRESOLVED;
1949- }
1950- }
1951- }
2025+ // Apply additional behavior aware inference that the analyzer can't do.
2026+ if (base.type .is_set ()) {
2027+ // Maintain type for duplicate methods.
2028+ if (call->function_name == SNAME (" duplicate" )) {
2029+ if (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" )))) {
2030+ r_type.type = base.type ;
2031+ found = true ;
2032+ break ;
19522033 }
2034+ }
19532035
1954- if (native_type.kind == GDScriptParser::DataType::NATIVE) {
1955- MethodBind *mb = ClassDB::get_method (native_type.native_type , call->function_name );
1956- if (mb && mb->is_const ()) {
1957- bool all_is_const = true ;
1958- Vector<Variant> args;
1959- for (int i = 0 ; all_is_const && i < call->arguments .size (); i++) {
1960- GDScriptCompletionIdentifier arg;
1961-
1962- if (!call->arguments [i]->is_constant ) {
1963- all_is_const = false ;
1964- }
1965- }
1966-
1967- Object *baseptr = base.value ;
1968-
1969- if (all_is_const && call->function_name == SNAME (" get_node" ) && ClassDB::is_parent_class (native_type.native_type , SNAME (" Node" )) && args.size ()) {
1970- String arg1 = args[0 ];
1971- if (arg1.begins_with (" /root/" )) {
1972- String which = arg1.get_slicec (' /' , 2 );
1973- if (!which.is_empty ()) {
1974- // Try singletons first
1975- if (GDScriptLanguage::get_singleton ()->get_named_globals_map ().has (which)) {
1976- r_type = _type_from_variant (GDScriptLanguage::get_singleton ()->get_named_globals_map ()[which], p_context);
1977- found = true ;
1978- } else {
1979- for (const KeyValue<StringName, ProjectSettings::AutoloadInfo> &E : ProjectSettings::get_singleton ()->get_autoload_list ()) {
1980- String name = E.key ;
1981- if (name == which) {
1982- String script = E.value .path ;
1983-
1984- if (!script.begins_with (" res://" )) {
1985- script = " res://" + script;
1986- }
1987-
1988- if (!script.ends_with (" .gd" )) {
1989- // not a script, try find the script anyway,
1990- // may have some success
1991- script = script.get_basename () + " .gd" ;
1992- }
1993-
1994- if (FileAccess::exists (script)) {
1995- Ref<GDScriptParserRef> parser = p_context.parser ->get_depended_parser_for (script);
1996- if (parser.is_valid () && parser->raise_status (GDScriptParserRef::INTERFACE_SOLVED) == OK) {
1997- r_type.type .type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
1998- r_type.type .script_path = script;
1999- r_type.type .class_type = parser->get_parser ()->get_tree ();
2000- r_type.type .is_constant = false ;
2001- r_type.type .kind = GDScriptParser::DataType::CLASS;
2002- r_type.value = Variant ();
2003- found = true ;
2004- }
2005- }
2006- break ;
2007- }
2008- }
2009- }
2010- }
2011- }
2012- }
2013-
2014- if (!found && all_is_const && baseptr) {
2015- Vector<const Variant *> argptr;
2016- for (int i = 0 ; i < args.size (); i++) {
2017- argptr.push_back (&args[i]);
2018- }
2019-
2020- Callable::CallError ce;
2021- Variant ret = mb->call (baseptr, (const Variant **)argptr.ptr (), argptr.size (), ce);
2036+ // Simulate generics for some typed array methods.
2037+ if (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" ))) {
2038+ r_type.type = base.type .get_container_element_type (0 );
2039+ found = true ;
2040+ break ;
2041+ }
20222042
2023- if (ce.error == Callable::CallError::CALL_OK && ret.get_type () != Variant::NIL) {
2024- if (ret.get_type () != Variant::OBJECT || ret.operator Object *() != nullptr ) {
2025- r_type = _type_from_variant (ret, p_context);
2026- found = true ;
2027- }
2028- }
2029- }
2030- }
2043+ // Insert example values for functions which a structured dictionary response.
2044+ if (!base.type .is_meta_type ) {
2045+ const Dictionary *example = structure_examples.getptr (base.type .native_type .operator String () + " ::" + call->function_name );
2046+ if (example != nullptr ) {
2047+ r_type = _type_from_variant (*example, p_context);
2048+ found = true ;
2049+ break ;
20312050 }
20322051 }
2052+ }
20332053
2034- if (!found) {
2035- found = _guess_method_return_type_from_base (c, base, call->function_name , r_type);
2036- }
2054+ if (!found) {
2055+ found = _guess_method_return_type_from_base (c, base, call->function_name , r_type);
20372056 }
20382057 } break ;
20392058 case GDScriptParser::Node::SUBSCRIPT: {
0 commit comments