2020-05-01 22:14:56 +00:00
/**************************************************************************/
/* gdscript_analyzer.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
# include "gdscript_analyzer.h"
2023-06-13 14:56:21 +00:00
# include "gdscript.h"
2024-01-05 10:56:42 +00:00
# include "gdscript_utility_callable.h"
2023-06-13 14:56:21 +00:00
# include "gdscript_utility_functions.h"
2021-08-20 13:52:58 +00:00
# include "core/config/engine.h"
2020-11-07 22:33:38 +00:00
# include "core/config/project_settings.h"
2023-02-19 15:57:09 +00:00
# include "core/core_constants.h"
2021-06-11 12:51:48 +00:00
# include "core/io/file_access.h"
2020-05-01 22:14:56 +00:00
# include "core/io/resource_loader.h"
2020-11-07 22:33:38 +00:00
# include "core/object/class_db.h"
# include "core/object/script_language.h"
# include "core/templates/hash_map.h"
2022-10-09 16:41:28 +00:00
# include "scene/resources/packed_scene.h"
2020-05-01 22:14:56 +00:00
2023-02-10 08:07:01 +00:00
# if defined(TOOLS_ENABLED) && !defined(DISABLE_DEPRECATED)
# define SUGGEST_GODOT4_RENAMES
# include "editor/renames_map_3_to_4.h"
# endif
2022-12-04 03:02:03 +00:00
# define UNNAMED_ENUM "<anonymous enum>"
2023-02-19 15:57:09 +00:00
# define ENUM_SEPARATOR "."
2022-12-04 03:02:03 +00:00
2020-11-26 14:56:32 +00:00
static MethodInfo info_from_utility_func ( const StringName & p_function ) {
ERR_FAIL_COND_V ( ! Variant : : has_utility_function ( p_function ) , MethodInfo ( ) ) ;
MethodInfo info ( p_function ) ;
if ( Variant : : has_utility_function_return_value ( p_function ) ) {
info . return_val . type = Variant : : get_utility_function_return_type ( p_function ) ;
if ( info . return_val . type = = Variant : : NIL ) {
info . return_val . usage | = PROPERTY_USAGE_NIL_IS_VARIANT ;
}
}
if ( Variant : : is_utility_function_vararg ( p_function ) ) {
info . flags | = METHOD_FLAG_VARARG ;
} else {
for ( int i = 0 ; i < Variant : : get_utility_function_argument_count ( p_function ) ; i + + ) {
PropertyInfo pi ;
# ifdef DEBUG_METHODS_ENABLED
pi . name = Variant : : get_utility_function_argument_name ( p_function , i ) ;
# else
pi . name = " arg " + itos ( i + 1 ) ;
# endif
pi . type = Variant : : get_utility_function_argument_type ( p_function , i ) ;
info . arguments . push_back ( pi ) ;
}
}
return info ;
}
2020-06-10 22:53:25 +00:00
static GDScriptParser : : DataType make_callable_type ( const MethodInfo & p_info ) {
GDScriptParser : : DataType type ;
type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
type . kind = GDScriptParser : : DataType : : BUILTIN ;
type . builtin_type = Variant : : CALLABLE ;
type . is_constant = true ;
type . method_info = p_info ;
return type ;
}
static GDScriptParser : : DataType make_signal_type ( const MethodInfo & p_info ) {
GDScriptParser : : DataType type ;
type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
type . kind = GDScriptParser : : DataType : : BUILTIN ;
type . builtin_type = Variant : : SIGNAL ;
type . is_constant = true ;
type . method_info = p_info ;
return type ;
}
static GDScriptParser : : DataType make_native_meta_type ( const StringName & p_class_name ) {
GDScriptParser : : DataType type ;
type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
type . kind = GDScriptParser : : DataType : : NATIVE ;
type . builtin_type = Variant : : OBJECT ;
type . native_type = p_class_name ;
2023-01-11 23:03:53 +00:00
type . is_constant = true ;
type . is_meta_type = true ;
return type ;
}
static GDScriptParser : : DataType make_script_meta_type ( const Ref < Script > & p_script ) {
GDScriptParser : : DataType type ;
type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
type . kind = GDScriptParser : : DataType : : SCRIPT ;
type . builtin_type = Variant : : OBJECT ;
type . native_type = p_script - > get_instance_base_type ( ) ;
type . script_type = p_script ;
type . script_path = p_script - > get_path ( ) ;
type . is_constant = true ;
2020-06-10 22:53:25 +00:00
type . is_meta_type = true ;
return type ;
}
2022-12-04 03:02:03 +00:00
// In enum types, native_type is used to store the class (native or otherwise) that the enum belongs to.
// This disambiguates between similarly named enums in base classes or outer classes
static GDScriptParser : : DataType make_enum_type ( const StringName & p_enum_name , const String & p_base_name , const bool p_meta = false ) {
2020-06-12 00:49:58 +00:00
GDScriptParser : : DataType type ;
type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
type . kind = GDScriptParser : : DataType : : ENUM ;
2022-12-04 03:02:03 +00:00
type . builtin_type = p_meta ? Variant : : DICTIONARY : Variant : : INT ;
type . enum_type = p_enum_name ;
2020-06-12 00:49:58 +00:00
type . is_constant = true ;
2022-12-04 03:02:03 +00:00
type . is_meta_type = p_meta ;
// For enums, native_type is only used to check compatibility in is_type_compatible()
// We can set anything readable here for error messages, as long as it uniquely identifies the type of the enum
2023-02-19 15:57:09 +00:00
if ( p_base_name . is_empty ( ) ) {
type . native_type = p_enum_name ;
} else {
type . native_type = p_base_name + ENUM_SEPARATOR + p_enum_name ;
}
2022-12-04 03:02:03 +00:00
return type ;
}
2023-02-19 15:57:09 +00:00
static GDScriptParser : : DataType make_native_enum_type ( const StringName & p_enum_name , const StringName & p_native_class , bool p_meta = true ) {
2023-02-02 14:57:22 +00:00
// Find out which base class declared the enum, so the name is always the same even when coming from other contexts.
StringName native_base = p_native_class ;
while ( true & & native_base ! = StringName ( ) ) {
if ( ClassDB : : has_enum ( native_base , p_enum_name , true ) ) {
break ;
}
native_base = ClassDB : : get_parent_class_nocheck ( native_base ) ;
}
GDScriptParser : : DataType type = make_enum_type ( p_enum_name , native_base , p_meta ) ;
if ( p_meta ) {
2023-02-19 15:57:09 +00:00
type . builtin_type = Variant : : NIL ; // Native enum types are not Dictionaries.
2023-02-02 14:57:22 +00:00
}
2020-06-12 00:49:58 +00:00
List < StringName > enum_values ;
2023-02-02 14:57:22 +00:00
ClassDB : : get_enum_constants ( native_base , p_enum_name , & enum_values , true ) ;
2020-06-12 00:49:58 +00:00
2021-07-16 03:45:57 +00:00
for ( const StringName & E : enum_values ) {
2023-02-02 14:57:22 +00:00
type . enum_values [ E ] = ClassDB : : get_integer_constant ( native_base , E ) ;
2020-06-12 00:49:58 +00:00
}
return type ;
}
2023-02-19 15:57:09 +00:00
static GDScriptParser : : DataType make_global_enum_type ( const StringName & p_enum_name , const StringName & p_base , bool p_meta = true ) {
GDScriptParser : : DataType type = make_enum_type ( p_enum_name , p_base , p_meta ) ;
if ( p_meta ) {
type . builtin_type = Variant : : NIL ; // Native enum types are not Dictionaries.
type . is_pseudo_type = true ;
}
HashMap < StringName , int64_t > enum_values ;
CoreConstants : : get_enum_values ( type . native_type , & enum_values ) ;
for ( const KeyValue < StringName , int64_t > & element : enum_values ) {
type . enum_values [ element . key ] = element . value ;
}
return type ;
}
2020-06-10 22:53:25 +00:00
static GDScriptParser : : DataType make_builtin_meta_type ( Variant : : Type p_type ) {
GDScriptParser : : DataType type ;
type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
type . kind = GDScriptParser : : DataType : : BUILTIN ;
type . builtin_type = p_type ;
type . is_constant = true ;
type . is_meta_type = true ;
return type ;
}
2022-11-08 22:41:50 +00:00
bool GDScriptAnalyzer : : has_member_name_conflict_in_script_class ( const StringName & p_member_name , const GDScriptParser : : ClassNode * p_class , const GDScriptParser : : Node * p_member ) {
2021-08-24 16:49:03 +00:00
if ( p_class - > members_indices . has ( p_member_name ) ) {
int index = p_class - > members_indices [ p_member_name ] ;
const GDScriptParser : : ClassNode : : Member * member = & p_class - > members [ index ] ;
if ( member - > type = = GDScriptParser : : ClassNode : : Member : : VARIABLE | |
member - > type = = GDScriptParser : : ClassNode : : Member : : CONSTANT | |
member - > type = = GDScriptParser : : ClassNode : : Member : : ENUM | |
member - > type = = GDScriptParser : : ClassNode : : Member : : ENUM_VALUE | |
member - > type = = GDScriptParser : : ClassNode : : Member : : CLASS | |
member - > type = = GDScriptParser : : ClassNode : : Member : : SIGNAL ) {
return true ;
}
2022-11-08 22:41:50 +00:00
if ( p_member - > type ! = GDScriptParser : : Node : : FUNCTION & & member - > type = = GDScriptParser : : ClassNode : : Member : : FUNCTION ) {
return true ;
}
2021-08-24 16:49:03 +00:00
}
return false ;
}
bool GDScriptAnalyzer : : has_member_name_conflict_in_native_type ( const StringName & p_member_name , const StringName & p_native_type_string ) {
if ( ClassDB : : has_signal ( p_native_type_string , p_member_name ) ) {
return true ;
}
if ( ClassDB : : has_property ( p_native_type_string , p_member_name ) ) {
return true ;
}
if ( ClassDB : : has_integer_constant ( p_native_type_string , p_member_name ) ) {
return true ;
}
2023-09-04 15:01:33 +00:00
if ( p_member_name = = CoreStringName ( script ) ) {
2022-11-08 22:41:50 +00:00
return true ;
}
2021-08-24 16:49:03 +00:00
return false ;
}
Error GDScriptAnalyzer : : check_native_member_name_conflict ( const StringName & p_member_name , const GDScriptParser : : Node * p_member_node , const StringName & p_native_type_string ) {
if ( has_member_name_conflict_in_native_type ( p_member_name , p_native_type_string ) ) {
push_error ( vformat ( R " (Member " % s " redefined (original in native class '%s')) " , p_member_name , p_native_type_string ) , p_member_node ) ;
return ERR_PARSE_ERROR ;
}
if ( class_exists ( p_member_name ) ) {
2021-09-13 13:51:29 +00:00
push_error ( vformat ( R " (The member " % s " shadows a native class.) " , p_member_name ) , p_member_node ) ;
2021-08-24 16:49:03 +00:00
return ERR_PARSE_ERROR ;
}
2023-09-21 09:42:55 +00:00
if ( GDScriptParser : : get_builtin_type ( p_member_name ) < Variant : : VARIANT_MAX ) {
2021-09-29 14:23:16 +00:00
push_error ( vformat ( R " (The member " % s " cannot have the same name as a builtin type.) " , p_member_name ) , p_member_node ) ;
return ERR_PARSE_ERROR ;
}
2021-08-24 16:49:03 +00:00
return OK ;
}
Error GDScriptAnalyzer : : check_class_member_name_conflict ( const GDScriptParser : : ClassNode * p_class_node , const StringName & p_member_name , const GDScriptParser : : Node * p_member_node ) {
2022-12-04 03:02:03 +00:00
// TODO check outer classes for static members only
2021-08-24 16:49:03 +00:00
const GDScriptParser : : DataType * current_data_type = & p_class_node - > base_type ;
while ( current_data_type & & current_data_type - > kind = = GDScriptParser : : DataType : : Kind : : CLASS ) {
GDScriptParser : : ClassNode * current_class_node = current_data_type - > class_type ;
2022-11-08 22:41:50 +00:00
if ( has_member_name_conflict_in_script_class ( p_member_name , current_class_node , p_member_node ) ) {
2022-12-03 02:30:19 +00:00
String parent_class_name = current_class_node - > fqcn ;
if ( current_class_node - > identifier ! = nullptr ) {
parent_class_name = current_class_node - > identifier - > name ;
}
push_error ( vformat ( R " (The member " % s " already exists in parent class %s.) " , p_member_name , parent_class_name ) , p_member_node ) ;
2021-08-24 16:49:03 +00:00
return ERR_PARSE_ERROR ;
}
current_data_type = & current_class_node - > base_type ;
}
2022-11-08 22:41:50 +00:00
// No need for native class recursion because Node exposes all Object's properties.
2021-08-24 16:49:03 +00:00
if ( current_data_type & & current_data_type - > kind = = GDScriptParser : : DataType : : Kind : : NATIVE ) {
2022-02-06 13:12:19 +00:00
if ( current_data_type - > native_type ! = StringName ( ) ) {
2021-08-24 16:49:03 +00:00
return check_native_member_name_conflict (
p_member_name ,
p_member_node ,
current_data_type - > native_type ) ;
}
}
return OK ;
}
2024-07-23 11:46:28 +00:00
void GDScriptAnalyzer : : get_class_node_current_scope_classes ( GDScriptParser : : ClassNode * p_node , List < GDScriptParser : : ClassNode * > * p_list , GDScriptParser : : Node * p_source ) {
2022-12-18 05:38:53 +00:00
ERR_FAIL_NULL ( p_node ) ;
ERR_FAIL_NULL ( p_list ) ;
2022-12-04 21:55:40 +00:00
if ( p_list - > find ( p_node ) ! = nullptr ) {
return ;
}
2022-12-18 05:38:53 +00:00
2022-12-04 21:55:40 +00:00
p_list - > push_back ( p_node ) ;
2022-12-11 02:57:35 +00:00
// TODO: Try to solve class inheritance if not yet resolving.
2022-12-04 21:55:40 +00:00
// Prioritize node base type over its outer class
if ( p_node - > base_type . class_type ! = nullptr ) {
2024-08-03 03:55:13 +00:00
// TODO: 'ensure_cached_external_parser_for_class()' is only necessary because 'resolve_class_inheritance()' is not getting called here.
ensure_cached_external_parser_for_class ( p_node - > base_type . class_type , p_node , " Trying to fetch classes in the current scope " , p_source ) ;
2024-07-23 11:46:28 +00:00
get_class_node_current_scope_classes ( p_node - > base_type . class_type , p_list , p_source ) ;
2022-12-04 21:55:40 +00:00
}
if ( p_node - > outer ! = nullptr ) {
2024-08-03 03:55:13 +00:00
// TODO: 'ensure_cached_external_parser_for_class()' is only necessary because 'resolve_class_inheritance()' is not getting called here.
ensure_cached_external_parser_for_class ( p_node - > outer , p_node , " Trying to fetch classes in the current scope " , p_source ) ;
2024-07-23 11:46:28 +00:00
get_class_node_current_scope_classes ( p_node - > outer , p_list , p_source ) ;
2022-12-04 21:55:40 +00:00
}
}
2022-12-11 02:57:35 +00:00
Error GDScriptAnalyzer : : resolve_class_inheritance ( GDScriptParser : : ClassNode * p_class , const GDScriptParser : : Node * p_source ) {
if ( p_source = = nullptr & & parser - > has_class ( p_class ) ) {
p_source = p_class ;
}
2024-08-03 03:55:13 +00:00
Ref < GDScriptParserRef > parser_ref = ensure_cached_external_parser_for_class ( p_class , nullptr , " Trying to resolve class inheritance " , p_source ) ;
2024-07-23 11:46:28 +00:00
Finally finally ( [ & ] ( ) {
for ( GDScriptParser : : ClassNode * look_class = p_class ; look_class ! = nullptr ; look_class = look_class - > base_type . class_type ) {
2024-08-03 03:55:13 +00:00
ensure_cached_external_parser_for_class ( look_class - > base_type . class_type , look_class , " Trying to resolve class inheritance " , p_source ) ;
2024-07-23 11:46:28 +00:00
}
} ) ;
2022-12-11 02:57:35 +00:00
if ( p_class - > base_type . is_resolving ( ) ) {
push_error ( vformat ( R " (Could not resolve class " % s " : Cyclic reference.) " , type_from_metatype ( p_class - > get_datatype ( ) ) . to_string ( ) ) , p_source ) ;
return ERR_PARSE_ERROR ;
}
if ( ! p_class - > base_type . has_no_type ( ) ) {
// Already resolved.
return OK ;
}
if ( ! parser - > has_class ( p_class ) ) {
if ( parser_ref . is_null ( ) ) {
2024-07-23 11:46:28 +00:00
// Error already pushed.
2022-12-11 02:57:35 +00:00
return ERR_PARSE_ERROR ;
}
Error err = parser_ref - > raise_status ( GDScriptParserRef : : PARSED ) ;
if ( err ) {
2024-07-23 11:46:28 +00:00
push_error ( vformat ( R " (Could not parse script " % s " : %s.) " , p_class - > get_datatype ( ) . script_path , error_names [ err ] ) , p_source ) ;
2022-12-11 02:57:35 +00:00
return ERR_PARSE_ERROR ;
}
GDScriptAnalyzer * other_analyzer = parser_ref - > get_analyzer ( ) ;
GDScriptParser * other_parser = parser_ref - > get_parser ( ) ;
int error_count = other_parser - > errors . size ( ) ;
other_analyzer - > resolve_class_inheritance ( p_class ) ;
if ( other_parser - > errors . size ( ) > error_count ) {
push_error ( vformat ( R " (Could not resolve inheritance for class " % s " .) " , p_class - > fqcn ) , p_source ) ;
return ERR_PARSE_ERROR ;
}
2020-05-01 22:14:56 +00:00
return OK ;
}
2022-12-11 02:57:35 +00:00
GDScriptParser : : ClassNode * previous_class = parser - > current_class ;
parser - > current_class = p_class ;
2021-09-13 13:51:29 +00:00
if ( p_class - > identifier ) {
StringName class_name = p_class - > identifier - > name ;
2022-12-11 02:57:35 +00:00
if ( GDScriptParser : : get_builtin_type ( class_name ) < Variant : : VARIANT_MAX ) {
push_error ( vformat ( R " (Class " % s " hides a built-in type.) " , class_name ) , p_class - > identifier ) ;
} else if ( class_exists ( class_name ) ) {
2021-09-13 13:51:29 +00:00
push_error ( vformat ( R " (Class " % s " hides a native class.) " , class_name ) , p_class - > identifier ) ;
2024-02-26 07:46:24 +00:00
} else if ( ScriptServer : : is_global_class ( class_name ) & & ( ! GDScript : : is_canonically_equal_paths ( ScriptServer : : get_global_class_path ( class_name ) , parser - > script_path ) | | p_class ! = parser - > head ) ) {
2021-09-13 13:51:29 +00:00
push_error ( vformat ( R " (Class " % s " hides a global script class.) " , class_name ) , p_class - > identifier ) ;
} else if ( ProjectSettings : : get_singleton ( ) - > has_autoload ( class_name ) & & ProjectSettings : : get_singleton ( ) - > get_autoload ( class_name ) . is_singleton ) {
push_error ( vformat ( R " (Class " % s " hides an autoload singleton.) " , class_name ) , p_class - > identifier ) ;
}
}
2022-12-11 02:57:35 +00:00
GDScriptParser : : DataType resolving_datatype ;
resolving_datatype . kind = GDScriptParser : : DataType : : RESOLVING ;
p_class - > base_type = resolving_datatype ;
2020-06-10 22:53:25 +00:00
// Set datatype for class.
GDScriptParser : : DataType class_type ;
class_type . is_constant = true ;
class_type . is_meta_type = true ;
class_type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
class_type . kind = GDScriptParser : : DataType : : CLASS ;
class_type . class_type = p_class ;
2020-07-16 01:02:44 +00:00
class_type . script_path = parser - > script_path ;
2021-10-06 15:01:34 +00:00
class_type . builtin_type = Variant : : OBJECT ;
2020-06-10 22:53:25 +00:00
p_class - > set_datatype ( class_type ) ;
2022-12-11 02:57:35 +00:00
GDScriptParser : : DataType result ;
2020-05-01 22:14:56 +00:00
if ( ! p_class - > extends_used ) {
result . type_source = GDScriptParser : : DataType : : ANNOTATED_INFERRED ;
result . kind = GDScriptParser : : DataType : : NATIVE ;
2023-10-04 12:03:53 +00:00
result . builtin_type = Variant : : OBJECT ;
2022-02-06 13:12:19 +00:00
result . native_type = SNAME ( " RefCounted " ) ;
2020-05-01 22:14:56 +00:00
} else {
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
GDScriptParser : : DataType base ;
2020-06-10 22:53:25 +00:00
int extends_index = 0 ;
2020-12-15 12:04:21 +00:00
if ( ! p_class - > extends_path . is_empty ( ) ) {
2021-07-14 15:23:54 +00:00
if ( p_class - > extends_path . is_relative_path ( ) ) {
2022-08-30 00:34:01 +00:00
p_class - > extends_path = class_type . script_path . get_base_dir ( ) . path_join ( p_class - > extends_path ) . simplify_path ( ) ;
2021-07-14 15:23:54 +00:00
}
2024-04-12 23:13:25 +00:00
Ref < GDScriptParserRef > ext_parser = parser - > get_depended_parser_for ( p_class - > extends_path ) ;
2022-09-29 09:53:28 +00:00
if ( ext_parser . is_null ( ) ) {
2020-06-10 22:53:25 +00:00
push_error ( vformat ( R " (Could not resolve super class path " % s " .) " , p_class - > extends_path ) , p_class ) ;
return ERR_PARSE_ERROR ;
}
2022-12-11 02:57:35 +00:00
Error err = ext_parser - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ;
2020-06-10 22:53:25 +00:00
if ( err ! = OK ) {
push_error ( vformat ( R " (Could not resolve super class inheritance from " % s " .) " , p_class - > extends_path ) , p_class ) ;
return err ;
}
2024-07-04 07:23:44 +00:00
# ifdef DEBUG_ENABLED
if ( ! parser - > _is_tool & & ext_parser - > get_parser ( ) - > _is_tool ) {
parser - > push_warning ( p_class , GDScriptWarning : : MISSING_TOOL ) ;
}
# endif
2022-09-29 09:53:28 +00:00
base = ext_parser - > get_parser ( ) - > head - > get_datatype ( ) ;
2020-05-01 22:14:56 +00:00
} else {
2020-12-15 12:04:21 +00:00
if ( p_class - > extends . is_empty ( ) ) {
2022-03-12 06:33:11 +00:00
push_error ( " Could not resolve an empty super class path. " , p_class ) ;
2020-07-16 01:02:44 +00:00
return ERR_PARSE_ERROR ;
}
2023-03-08 20:06:29 +00:00
GDScriptParser : : IdentifierNode * id = p_class - > extends [ extends_index + + ] ;
const StringName & name = id - > name ;
2020-05-01 22:14:56 +00:00
base . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
if ( ScriptServer : : is_global_class ( name ) ) {
2020-06-10 22:53:25 +00:00
String base_path = ScriptServer : : get_global_class_path ( name ) ;
2024-02-26 07:46:24 +00:00
if ( GDScript : : is_canonically_equal_paths ( base_path , parser - > script_path ) ) {
2020-07-16 01:02:44 +00:00
base = parser - > head - > get_datatype ( ) ;
} else {
2024-04-12 23:13:25 +00:00
Ref < GDScriptParserRef > base_parser = parser - > get_depended_parser_for ( base_path ) ;
2022-09-29 09:53:28 +00:00
if ( base_parser . is_null ( ) ) {
2023-03-08 20:06:29 +00:00
push_error ( vformat ( R " (Could not resolve super class " % s " .) " , name ) , id ) ;
2020-07-16 01:02:44 +00:00
return ERR_PARSE_ERROR ;
}
2020-06-10 22:53:25 +00:00
2022-12-11 02:57:35 +00:00
Error err = base_parser - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ;
2020-07-16 01:02:44 +00:00
if ( err ! = OK ) {
2023-03-08 20:06:29 +00:00
push_error ( vformat ( R " (Could not resolve super class inheritance from " % s " .) " , name ) , id ) ;
2020-07-16 01:02:44 +00:00
return err ;
}
2024-07-04 07:23:44 +00:00
# ifdef DEBUG_ENABLED
if ( ! parser - > _is_tool & & base_parser - > get_parser ( ) - > _is_tool ) {
parser - > push_warning ( p_class , GDScriptWarning : : MISSING_TOOL ) ;
}
# endif
2022-09-29 09:53:28 +00:00
base = base_parser - > get_parser ( ) - > head - > get_datatype ( ) ;
2020-05-01 22:14:56 +00:00
}
2020-06-10 22:53:25 +00:00
} else if ( ProjectSettings : : get_singleton ( ) - > has_autoload ( name ) & & ProjectSettings : : get_singleton ( ) - > get_autoload ( name ) . is_singleton ) {
const ProjectSettings : : AutoloadInfo & info = ProjectSettings : : get_singleton ( ) - > get_autoload ( name ) ;
2022-12-11 02:57:35 +00:00
if ( info . path . get_extension ( ) . to_lower ( ) ! = GDScriptLanguage : : get_singleton ( ) - > get_extension ( ) ) {
2023-03-08 20:06:29 +00:00
push_error ( vformat ( R " (Singleton %s is not a GDScript.) " , info . name ) , id ) ;
2020-06-10 22:53:25 +00:00
return ERR_PARSE_ERROR ;
}
2024-04-12 23:13:25 +00:00
Ref < GDScriptParserRef > info_parser = parser - > get_depended_parser_for ( info . path ) ;
2022-09-29 09:53:28 +00:00
if ( info_parser . is_null ( ) ) {
2023-03-08 20:06:29 +00:00
push_error ( vformat ( R " (Could not parse singleton from " % s " .) " , info . path ) , id ) ;
2020-06-10 22:53:25 +00:00
return ERR_PARSE_ERROR ;
}
2022-12-11 02:57:35 +00:00
Error err = info_parser - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ;
2020-06-10 22:53:25 +00:00
if ( err ! = OK ) {
2023-03-08 20:06:29 +00:00
push_error ( vformat ( R " (Could not resolve super class inheritance from " % s " .) " , name ) , id ) ;
2020-06-10 22:53:25 +00:00
return err ;
}
2024-07-04 07:23:44 +00:00
# ifdef DEBUG_ENABLED
if ( ! parser - > _is_tool & & info_parser - > get_parser ( ) - > _is_tool ) {
parser - > push_warning ( p_class , GDScriptWarning : : MISSING_TOOL ) ;
}
# endif
2022-12-11 02:57:35 +00:00
base = info_parser - > get_parser ( ) - > head - > get_datatype ( ) ;
2022-12-29 07:34:13 +00:00
} else if ( class_exists ( name ) ) {
2023-09-22 07:10:22 +00:00
if ( Engine : : get_singleton ( ) - > has_singleton ( name ) ) {
push_error ( vformat ( R " (Cannot inherit native class " % s " because it is an engine singleton.) " , name ) , id ) ;
return ERR_PARSE_ERROR ;
}
2020-06-10 22:53:25 +00:00
base . kind = GDScriptParser : : DataType : : NATIVE ;
2023-10-04 12:03:53 +00:00
base . builtin_type = Variant : : OBJECT ;
2020-06-10 22:53:25 +00:00
base . native_type = name ;
2020-05-01 22:14:56 +00:00
} else {
2020-06-10 22:53:25 +00:00
// Look for other classes in script.
bool found = false ;
2022-12-04 21:55:40 +00:00
List < GDScriptParser : : ClassNode * > script_classes ;
2024-07-23 11:46:28 +00:00
get_class_node_current_scope_classes ( p_class , & script_classes , id ) ;
2022-12-04 21:55:40 +00:00
for ( GDScriptParser : : ClassNode * look_class : script_classes ) {
2020-07-16 01:02:44 +00:00
if ( look_class - > identifier & & look_class - > identifier - > name = = name ) {
if ( ! look_class - > get_datatype ( ) . is_set ( ) ) {
2023-03-08 20:06:29 +00:00
Error err = resolve_class_inheritance ( look_class , id ) ;
2020-07-16 01:02:44 +00:00
if ( err ) {
return err ;
}
}
base = look_class - > get_datatype ( ) ;
found = true ;
break ;
}
2022-12-11 02:57:35 +00:00
if ( look_class - > has_member ( name ) ) {
2023-03-08 20:06:29 +00:00
resolve_class_member ( look_class , name , id ) ;
2023-04-10 06:54:53 +00:00
GDScriptParser : : ClassNode : : Member member = look_class - > get_member ( name ) ;
GDScriptParser : : DataType member_datatype = member . get_datatype ( ) ;
switch ( member . type ) {
case GDScriptParser : : ClassNode : : Member : : CLASS :
break ; // OK.
case GDScriptParser : : ClassNode : : Member : : CONSTANT :
if ( member_datatype . kind ! = GDScriptParser : : DataType : : SCRIPT & & member_datatype . kind ! = GDScriptParser : : DataType : : CLASS ) {
push_error ( vformat ( R " (Constant " % s " is not a preloaded script or class.) " , name ) , id ) ;
return ERR_PARSE_ERROR ;
}
break ;
default :
push_error ( vformat ( R " (Cannot use %s " % s " in extends chain.) " , member . get_type_name ( ) , name ) , id ) ;
return ERR_PARSE_ERROR ;
}
base = member_datatype ;
2020-06-10 22:53:25 +00:00
found = true ;
break ;
}
2020-05-01 22:14:56 +00:00
}
2020-06-10 22:53:25 +00:00
if ( ! found ) {
2023-03-08 20:06:29 +00:00
push_error ( vformat ( R " (Could not find base class " % s " .) " , name ) , id ) ;
2020-06-10 22:53:25 +00:00
return ERR_PARSE_ERROR ;
}
}
}
for ( int index = extends_index ; index < p_class - > extends . size ( ) ; index + + ) {
2023-03-08 20:06:29 +00:00
GDScriptParser : : IdentifierNode * id = p_class - > extends [ index ] ;
2020-06-10 22:53:25 +00:00
if ( base . kind ! = GDScriptParser : : DataType : : CLASS ) {
2023-03-08 20:06:29 +00:00
push_error ( vformat ( R " (Cannot get nested types for extension from non-GDScript type " % s " .) " , base . to_string ( ) ) , id ) ;
2020-06-10 22:53:25 +00:00
return ERR_PARSE_ERROR ;
}
reduce_identifier_from_base ( id , & base ) ;
GDScriptParser : : DataType id_type = id - > get_datatype ( ) ;
2023-03-08 20:06:29 +00:00
2020-06-10 22:53:25 +00:00
if ( ! id_type . is_set ( ) ) {
2023-03-08 20:06:29 +00:00
push_error ( vformat ( R " (Could not find nested type " % s " .) " , id - > name ) , id ) ;
return ERR_PARSE_ERROR ;
2023-04-10 06:54:53 +00:00
} else if ( id_type . kind ! = GDScriptParser : : DataType : : SCRIPT & & id_type . kind ! = GDScriptParser : : DataType : : CLASS ) {
push_error ( vformat ( R " (Identifier " % s " is not a preloaded script or class.) " , id - > name ) , id ) ;
return ERR_PARSE_ERROR ;
2020-05-01 22:14:56 +00:00
}
2020-06-10 22:53:25 +00:00
base = id_type ;
2020-05-01 22:14:56 +00:00
}
result = base ;
}
2022-12-11 02:57:35 +00:00
if ( ! result . is_set ( ) | | result . has_no_type ( ) ) {
2020-05-01 22:14:56 +00:00
// TODO: More specific error messages.
2020-06-10 22:53:25 +00:00
push_error ( vformat ( R " (Could not resolve inheritance for class " % s " .) " , p_class - > identifier = = nullptr ? " <main> " : p_class - > identifier - > name ) , p_class ) ;
2020-05-01 22:14:56 +00:00
return ERR_PARSE_ERROR ;
}
2020-08-26 19:01:08 +00:00
// Check for cyclic inheritance.
const GDScriptParser : : ClassNode * base_class = result . class_type ;
while ( base_class ) {
if ( base_class - > fqcn = = p_class - > fqcn ) {
push_error ( " Cyclic inheritance. " , p_class ) ;
return ERR_PARSE_ERROR ;
}
base_class = base_class - > base_type . class_type ;
}
2020-06-10 22:53:25 +00:00
p_class - > base_type = result ;
class_type . native_type = result . native_type ;
p_class - > set_datatype ( class_type ) ;
2020-05-01 22:14:56 +00:00
2024-02-28 14:23:11 +00:00
// Apply annotations.
for ( GDScriptParser : : AnnotationNode * & E : p_class - > annotations ) {
resolve_annotation ( E ) ;
E - > apply ( parser , p_class , p_class - > outer ) ;
}
2022-12-11 02:57:35 +00:00
parser - > current_class = previous_class ;
return OK ;
}
Error GDScriptAnalyzer : : resolve_class_inheritance ( GDScriptParser : : ClassNode * p_class , bool p_recursive ) {
Error err = resolve_class_inheritance ( p_class ) ;
if ( err ) {
return err ;
}
2020-05-01 22:14:56 +00:00
if ( p_recursive ) {
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
if ( p_class - > members [ i ] . type = = GDScriptParser : : ClassNode : : Member : : CLASS ) {
2022-12-11 02:57:35 +00:00
err = resolve_class_inheritance ( p_class - > members [ i ] . m_class , true ) ;
2020-08-26 19:01:08 +00:00
if ( err ) {
return err ;
}
2020-05-01 22:14:56 +00:00
}
}
}
return OK ;
}
2020-06-11 22:31:28 +00:00
GDScriptParser : : DataType GDScriptAnalyzer : : resolve_datatype ( GDScriptParser : : TypeNode * p_type ) {
2022-12-11 02:57:35 +00:00
GDScriptParser : : DataType bad_type ;
bad_type . kind = GDScriptParser : : DataType : : VARIANT ;
bad_type . type_source = GDScriptParser : : DataType : : INFERRED ;
2020-05-01 22:14:56 +00:00
if ( p_type = = nullptr ) {
2022-12-11 02:57:35 +00:00
return bad_type ;
}
if ( p_type - > get_datatype ( ) . is_resolving ( ) ) {
push_error ( R " (Could not resolve datatype: Cyclic reference.) " , p_type ) ;
return bad_type ;
}
if ( ! p_type - > get_datatype ( ) . has_no_type ( ) ) {
return p_type - > get_datatype ( ) ;
2020-05-01 22:14:56 +00:00
}
2022-12-11 02:57:35 +00:00
GDScriptParser : : DataType resolving_datatype ;
resolving_datatype . kind = GDScriptParser : : DataType : : RESOLVING ;
p_type - > set_datatype ( resolving_datatype ) ;
GDScriptParser : : DataType result ;
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
2020-06-10 22:53:25 +00:00
2020-12-15 12:04:21 +00:00
if ( p_type - > type_chain . is_empty ( ) ) {
2020-05-01 22:14:56 +00:00
// void.
result . kind = GDScriptParser : : DataType : : BUILTIN ;
result . builtin_type = Variant : : NIL ;
2020-06-11 22:31:28 +00:00
p_type - > set_datatype ( result ) ;
2020-05-01 22:14:56 +00:00
return result ;
}
2023-08-24 16:01:31 +00:00
const GDScriptParser : : IdentifierNode * first_id = p_type - > type_chain [ 0 ] ;
StringName first = first_id - > name ;
bool type_found = false ;
if ( first_id - > suite & & first_id - > suite - > has_local ( first ) ) {
const GDScriptParser : : SuiteNode : : Local & local = first_id - > suite - > get_local ( first ) ;
if ( local . type = = GDScriptParser : : SuiteNode : : Local : : CONSTANT ) {
result = local . get_datatype ( ) ;
if ( ! result . is_set ( ) ) {
// Don't try to resolve it as the constant can be declared below.
push_error ( vformat ( R " (Local constant " % s " is not resolved at this point.) " , first ) , first_id ) ;
return bad_type ;
}
if ( result . is_meta_type ) {
type_found = true ;
} else if ( Ref < Script > ( local . constant - > initializer - > reduced_value ) . is_valid ( ) ) {
Ref < GDScript > gdscript = local . constant - > initializer - > reduced_value ;
if ( gdscript . is_valid ( ) ) {
2024-04-12 23:13:25 +00:00
Ref < GDScriptParserRef > ref = parser - > get_depended_parser_for ( gdscript - > get_script_path ( ) ) ;
2023-08-24 16:01:31 +00:00
if ( ref - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ! = OK ) {
push_error ( vformat ( R " (Could not parse script from " % s " .) " , gdscript - > get_script_path ( ) ) , first_id ) ;
return bad_type ;
}
result = ref - > get_parser ( ) - > head - > get_datatype ( ) ;
} else {
result = make_script_meta_type ( local . constant - > initializer - > reduced_value ) ;
}
type_found = true ;
2023-02-19 15:57:09 +00:00
} else {
2023-08-24 16:01:31 +00:00
push_error ( vformat ( R " (Local constant " % s " is not a valid type.) " , first ) , first_id ) ;
2023-02-19 15:57:09 +00:00
return bad_type ;
}
2023-08-24 16:01:31 +00:00
} else {
push_error ( vformat ( R " (Local %s " % s " cannot be used as a type.) " , local . get_name ( ) , first ) , first_id ) ;
2022-12-11 02:57:35 +00:00
return bad_type ;
2020-05-01 22:14:56 +00:00
}
2023-08-24 16:01:31 +00:00
}
2021-03-09 15:32:35 +00:00
2023-08-24 16:01:31 +00:00
if ( ! type_found ) {
if ( first = = SNAME ( " Variant " ) ) {
if ( p_type - > type_chain . size ( ) = = 2 ) {
// May be nested enum.
StringName enum_name = p_type - > type_chain [ 1 ] - > name ;
StringName qualified_name = String ( first ) + ENUM_SEPARATOR + String ( p_type - > type_chain [ 1 ] - > name ) ;
if ( CoreConstants : : is_global_enum ( qualified_name ) ) {
result = make_global_enum_type ( enum_name , first , true ) ;
return result ;
} else {
push_error ( vformat ( R " (Name " % s " is not a nested type of " Variant " .) " , enum_name ) , p_type - > type_chain [ 1 ] ) ;
2022-12-11 02:57:35 +00:00
return bad_type ;
2022-04-25 04:33:18 +00:00
}
2023-08-24 16:01:31 +00:00
} else if ( p_type - > type_chain . size ( ) > 2 ) {
push_error ( R " (Variant only contains enum types, which do not have nested types.) " , p_type - > type_chain [ 2 ] ) ;
return bad_type ;
}
result . kind = GDScriptParser : : DataType : : VARIANT ;
} else if ( GDScriptParser : : get_builtin_type ( first ) < Variant : : VARIANT_MAX ) {
// Built-in types.
if ( p_type - > type_chain . size ( ) > 1 ) {
push_error ( R " (Built-in types don't contain nested types.) " , p_type - > type_chain [ 1 ] ) ;
return bad_type ;
}
result . kind = GDScriptParser : : DataType : : BUILTIN ;
result . builtin_type = GDScriptParser : : get_builtin_type ( first ) ;
if ( result . builtin_type = = Variant : : ARRAY ) {
2023-09-14 18:31:07 +00:00
GDScriptParser : : DataType container_type = type_from_metatype ( resolve_datatype ( p_type - > get_container_type_or_null ( 0 ) ) ) ;
2023-08-24 16:01:31 +00:00
if ( container_type . kind ! = GDScriptParser : : DataType : : VARIANT ) {
container_type . is_constant = false ;
2023-09-14 18:31:07 +00:00
result . set_container_element_type ( 0 , container_type ) ;
2023-08-24 16:01:31 +00:00
}
}
2023-06-24 18:03:28 +00:00
if ( result . builtin_type = = Variant : : DICTIONARY ) {
GDScriptParser : : DataType key_type = type_from_metatype ( resolve_datatype ( p_type - > get_container_type_or_null ( 0 ) ) ) ;
if ( key_type . kind ! = GDScriptParser : : DataType : : VARIANT ) {
key_type . is_constant = false ;
result . set_container_element_type ( 0 , key_type ) ;
}
GDScriptParser : : DataType value_type = type_from_metatype ( resolve_datatype ( p_type - > get_container_type_or_null ( 1 ) ) ) ;
if ( value_type . kind ! = GDScriptParser : : DataType : : VARIANT ) {
value_type . is_constant = false ;
result . set_container_element_type ( 1 , value_type ) ;
}
}
2023-08-24 16:01:31 +00:00
} else if ( class_exists ( first ) ) {
// Native engine classes.
result . kind = GDScriptParser : : DataType : : NATIVE ;
result . builtin_type = Variant : : OBJECT ;
result . native_type = first ;
} else if ( ScriptServer : : is_global_class ( first ) ) {
2024-02-26 07:46:24 +00:00
if ( GDScript : : is_canonically_equal_paths ( parser - > script_path , ScriptServer : : get_global_class_path ( first ) ) ) {
2023-08-24 16:01:31 +00:00
result = parser - > head - > get_datatype ( ) ;
2022-04-25 04:33:18 +00:00
} else {
2023-08-24 16:01:31 +00:00
String path = ScriptServer : : get_global_class_path ( first ) ;
String ext = path . get_extension ( ) ;
if ( ext = = GDScriptLanguage : : get_singleton ( ) - > get_extension ( ) ) {
2024-04-12 23:13:25 +00:00
Ref < GDScriptParserRef > ref = parser - > get_depended_parser_for ( path ) ;
2023-08-24 16:01:31 +00:00
if ( ! ref . is_valid ( ) | | ref - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ! = OK ) {
push_error ( vformat ( R " (Could not parse global class " % s " from " % s " .) " , first , ScriptServer : : get_global_class_path ( first ) ) , p_type ) ;
return bad_type ;
}
result = ref - > get_parser ( ) - > head - > get_datatype ( ) ;
} else {
result = make_script_meta_type ( ResourceLoader : : load ( path , " Script " ) ) ;
}
2020-07-16 01:02:44 +00:00
}
2023-08-24 16:01:31 +00:00
} else if ( ProjectSettings : : get_singleton ( ) - > has_autoload ( first ) & & ProjectSettings : : get_singleton ( ) - > get_autoload ( first ) . is_singleton ) {
const ProjectSettings : : AutoloadInfo & autoload = ProjectSettings : : get_singleton ( ) - > get_autoload ( first ) ;
2024-04-10 16:57:27 +00:00
String script_path ;
if ( ResourceLoader : : get_resource_type ( autoload . path ) = = " PackedScene " ) {
// Try to get script from scene if possible.
if ( GDScriptLanguage : : get_singleton ( ) - > has_any_global_constant ( autoload . name ) ) {
Variant constant = GDScriptLanguage : : get_singleton ( ) - > get_any_global_constant ( autoload . name ) ;
Node * node = Object : : cast_to < Node > ( constant ) ;
if ( node ! = nullptr ) {
Ref < GDScript > scr = node - > get_script ( ) ;
if ( scr . is_valid ( ) ) {
script_path = scr - > get_script_path ( ) ;
}
}
}
} else if ( ResourceLoader : : get_resource_type ( autoload . path ) = = " GDScript " ) {
script_path = autoload . path ;
}
if ( script_path . is_empty ( ) ) {
return bad_type ;
}
Ref < GDScriptParserRef > ref = parser - > get_depended_parser_for ( script_path ) ;
2023-08-24 16:01:31 +00:00
if ( ref . is_null ( ) ) {
2024-04-10 16:57:27 +00:00
push_error ( vformat ( R " (The referenced autoload " % s " (from " % s " ) could not be loaded.) " , first , script_path ) , p_type ) ;
2023-08-24 16:01:31 +00:00
return bad_type ;
2022-12-04 03:02:03 +00:00
}
2023-08-24 16:01:31 +00:00
if ( ref - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ! = OK ) {
2024-04-10 16:57:27 +00:00
push_error ( vformat ( R " (Could not parse singleton " % s " from " % s " .) " , first , script_path ) , p_type ) ;
2023-08-24 16:01:31 +00:00
return bad_type ;
2020-07-16 01:02:44 +00:00
}
2023-08-24 16:01:31 +00:00
result = ref - > get_parser ( ) - > head - > get_datatype ( ) ;
} else if ( ClassDB : : has_enum ( parser - > current_class - > base_type . native_type , first ) ) {
// Native enum in current class.
result = make_native_enum_type ( first , parser - > current_class - > base_type . native_type ) ;
} else if ( CoreConstants : : is_global_enum ( first ) ) {
if ( p_type - > type_chain . size ( ) > 1 ) {
push_error ( R " (Enums cannot contain nested types.) " , p_type - > type_chain [ 1 ] ) ;
return bad_type ;
}
result = make_global_enum_type ( first , StringName ( ) ) ;
} else {
// Classes in current scope.
List < GDScriptParser : : ClassNode * > script_classes ;
bool found = false ;
2024-07-23 11:46:28 +00:00
get_class_node_current_scope_classes ( parser - > current_class , & script_classes , p_type ) ;
2023-08-24 16:01:31 +00:00
for ( GDScriptParser : : ClassNode * script_class : script_classes ) {
if ( found ) {
break ;
}
2022-12-11 02:57:35 +00:00
2023-08-24 16:01:31 +00:00
if ( script_class - > identifier & & script_class - > identifier - > name = = first ) {
result = script_class - > get_datatype ( ) ;
break ;
}
if ( script_class - > members_indices . has ( first ) ) {
resolve_class_member ( script_class , first , p_type ) ;
GDScriptParser : : ClassNode : : Member member = script_class - > get_member ( first ) ;
switch ( member . type ) {
case GDScriptParser : : ClassNode : : Member : : CLASS :
2022-12-11 02:57:35 +00:00
result = member . get_datatype ( ) ;
2022-12-04 03:02:03 +00:00
found = true ;
2020-06-10 22:53:25 +00:00
break ;
2023-08-24 16:01:31 +00:00
case GDScriptParser : : ClassNode : : Member : : ENUM :
result = member . get_datatype ( ) ;
2022-12-04 03:02:03 +00:00
found = true ;
2021-05-26 12:23:17 +00:00
break ;
2023-08-24 16:01:31 +00:00
case GDScriptParser : : ClassNode : : Member : : CONSTANT :
if ( member . get_datatype ( ) . is_meta_type ) {
result = member . get_datatype ( ) ;
found = true ;
break ;
} else if ( Ref < Script > ( member . constant - > initializer - > reduced_value ) . is_valid ( ) ) {
Ref < GDScript > gdscript = member . constant - > initializer - > reduced_value ;
if ( gdscript . is_valid ( ) ) {
2024-04-12 23:13:25 +00:00
Ref < GDScriptParserRef > ref = parser - > get_depended_parser_for ( gdscript - > get_script_path ( ) ) ;
2023-08-24 16:01:31 +00:00
if ( ref - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ! = OK ) {
push_error ( vformat ( R " (Could not parse script from " % s " .) " , gdscript - > get_script_path ( ) ) , p_type ) ;
return bad_type ;
}
result = ref - > get_parser ( ) - > head - > get_datatype ( ) ;
} else {
result = make_script_meta_type ( member . constant - > initializer - > reduced_value ) ;
}
found = true ;
break ;
}
[[fallthrough]] ;
default :
push_error ( vformat ( R " ( " % s " is a %s but does not contain a type.) " , first , member . get_type_name ( ) ) , p_type ) ;
return bad_type ;
}
2020-05-01 22:14:56 +00:00
}
}
}
2020-06-12 00:49:58 +00:00
}
2023-08-24 16:01:31 +00:00
2020-06-12 00:49:58 +00:00
if ( ! result . is_set ( ) ) {
2023-02-21 21:00:29 +00:00
push_error ( vformat ( R " (Could not find type " % s " in the current scope.) " , first ) , p_type ) ;
2022-12-11 02:57:35 +00:00
return bad_type ;
2020-05-01 22:14:56 +00:00
}
2020-06-10 22:53:25 +00:00
if ( p_type - > type_chain . size ( ) > 1 ) {
2020-06-12 00:49:58 +00:00
if ( result . kind = = GDScriptParser : : DataType : : CLASS ) {
2020-06-10 22:53:25 +00:00
for ( int i = 1 ; i < p_type - > type_chain . size ( ) ; i + + ) {
GDScriptParser : : DataType base = result ;
reduce_identifier_from_base ( p_type - > type_chain [ i ] , & base ) ;
result = p_type - > type_chain [ i ] - > get_datatype ( ) ;
if ( ! result . is_set ( ) ) {
push_error ( vformat ( R " (Could not find type " % s " under base " % s " .) " , p_type - > type_chain [ i ] - > name , base . to_string ( ) ) , p_type - > type_chain [ 1 ] ) ;
2022-12-11 02:57:35 +00:00
return bad_type ;
2020-06-10 22:53:25 +00:00
} else if ( ! result . is_meta_type ) {
push_error ( vformat ( R " (Member " % s " under base " % s " is not a valid type.) " , p_type - > type_chain [ i ] - > name , base . to_string ( ) ) , p_type - > type_chain [ 1 ] ) ;
2022-12-11 02:57:35 +00:00
return bad_type ;
2020-06-10 22:53:25 +00:00
}
}
2020-06-12 00:49:58 +00:00
} else if ( result . kind = = GDScriptParser : : DataType : : NATIVE ) {
// Only enums allowed for native.
2022-12-04 03:02:03 +00:00
if ( ClassDB : : has_enum ( result . native_type , p_type - > type_chain [ 1 ] - > name ) ) {
if ( p_type - > type_chain . size ( ) > 2 ) {
push_error ( R " (Enums cannot contain nested types.) " , p_type - > type_chain [ 2 ] ) ;
return bad_type ;
} else {
result = make_native_enum_type ( p_type - > type_chain [ 1 ] - > name , result . native_type ) ;
}
} else {
push_error ( vformat ( R " (Could not find type " % s " in " % s " .) " , p_type - > type_chain [ 1 ] - > name , first ) , p_type - > type_chain [ 1 ] ) ;
2022-12-11 02:57:35 +00:00
return bad_type ;
2020-06-12 00:49:58 +00:00
}
} else {
push_error ( vformat ( R " (Could not find nested type " % s " under base " % s " .) " , p_type - > type_chain [ 1 ] - > name , result . to_string ( ) ) , p_type - > type_chain [ 1 ] ) ;
2022-12-11 02:57:35 +00:00
return bad_type ;
2020-06-10 22:53:25 +00:00
}
2020-05-01 22:14:56 +00:00
}
2023-09-14 18:31:07 +00:00
if ( ! p_type - > container_types . is_empty ( ) ) {
if ( result . builtin_type = = Variant : : ARRAY ) {
if ( p_type - > container_types . size ( ) ! = 1 ) {
2023-06-24 18:03:28 +00:00
push_error ( R " (Typed arrays require exactly one collection element type.) " , p_type ) ;
return bad_type ;
}
} else if ( result . builtin_type = = Variant : : DICTIONARY ) {
if ( p_type - > container_types . size ( ) ! = 2 ) {
push_error ( R " (Typed dictionaries require exactly two collection element types.) " , p_type ) ;
2023-09-14 18:31:07 +00:00
return bad_type ;
}
} else {
2023-06-24 18:03:28 +00:00
push_error ( R " (Only arrays and dictionaries can specify collection element types.) " , p_type ) ;
2023-09-14 18:31:07 +00:00
return bad_type ;
}
2021-03-09 15:32:35 +00:00
}
2020-06-11 22:31:28 +00:00
p_type - > set_datatype ( result ) ;
2020-05-01 22:14:56 +00:00
return result ;
}
2021-08-01 19:47:20 +00:00
void GDScriptAnalyzer : : resolve_class_member ( GDScriptParser : : ClassNode * p_class , const StringName & p_name , const GDScriptParser : : Node * p_source ) {
2022-12-11 02:57:35 +00:00
ERR_FAIL_COND ( ! p_class - > has_member ( p_name ) ) ;
resolve_class_member ( p_class , p_class - > members_indices [ p_name ] , p_source ) ;
}
void GDScriptAnalyzer : : resolve_class_member ( GDScriptParser : : ClassNode * p_class , int p_index , const GDScriptParser : : Node * p_source ) {
ERR_FAIL_INDEX ( p_index , p_class - > members . size ( ) ) ;
GDScriptParser : : ClassNode : : Member & member = p_class - > members . write [ p_index ] ;
if ( p_source = = nullptr & & parser - > has_class ( p_class ) ) {
p_source = member . get_source_node ( ) ;
}
2024-08-03 03:55:13 +00:00
Ref < GDScriptParserRef > parser_ref = ensure_cached_external_parser_for_class ( p_class , nullptr , " Trying to resolve class member " , p_source ) ;
2024-07-23 11:46:28 +00:00
Finally finally ( [ & ] ( ) {
2024-08-03 03:55:13 +00:00
ensure_cached_external_parser_for_class ( member . get_datatype ( ) . class_type , p_class , " Trying to resolve datatype of class member " , p_source ) ;
2024-07-23 11:46:28 +00:00
GDScriptParser : : DataType member_type = member . get_datatype ( ) ;
if ( member_type . has_container_element_type ( 0 ) ) {
2024-08-03 03:55:13 +00:00
ensure_cached_external_parser_for_class ( member_type . get_container_element_type ( 0 ) . class_type , p_class , " Trying to resolve datatype of class member " , p_source ) ;
2024-07-23 11:46:28 +00:00
}
} ) ;
2022-12-11 02:57:35 +00:00
if ( member . get_datatype ( ) . is_resolving ( ) ) {
push_error ( vformat ( R " (Could not resolve member " % s " : Cyclic reference.) " , member . get_name ( ) ) , p_source ) ;
return ;
}
if ( member . get_datatype ( ) . is_set ( ) ) {
return ;
}
2024-07-23 11:46:28 +00:00
// If it's already resolving, that's ok.
if ( ! p_class - > base_type . is_resolving ( ) ) {
Error err = resolve_class_inheritance ( p_class ) ;
if ( err ) {
return ;
}
}
2022-12-11 02:57:35 +00:00
if ( ! parser - > has_class ( p_class ) ) {
if ( parser_ref . is_null ( ) ) {
2024-07-23 11:46:28 +00:00
// Error already pushed.
2022-12-11 02:57:35 +00:00
return ;
}
Error err = parser_ref - > raise_status ( GDScriptParserRef : : PARSED ) ;
if ( err ) {
2024-07-23 11:46:28 +00:00
push_error ( vformat ( R " (Could not parse script " % s " : %s (While resolving external class member " % s " ).) " , p_class - > get_datatype ( ) . script_path , error_names [ err ] , member . get_name ( ) ) , p_source ) ;
2022-12-11 02:57:35 +00:00
return ;
}
GDScriptAnalyzer * other_analyzer = parser_ref - > get_analyzer ( ) ;
GDScriptParser * other_parser = parser_ref - > get_parser ( ) ;
int error_count = other_parser - > errors . size ( ) ;
other_analyzer - > resolve_class_member ( p_class , p_index ) ;
if ( other_parser - > errors . size ( ) > error_count ) {
2024-07-23 11:46:28 +00:00
push_error ( vformat ( R " (Could not resolve external class member " % s " .) " , member . get_name ( ) ) , p_source ) ;
return ;
2022-12-11 02:57:35 +00:00
}
2020-06-10 22:53:25 +00:00
return ;
}
2022-12-11 02:57:35 +00:00
2020-05-01 22:14:56 +00:00
GDScriptParser : : ClassNode * previous_class = parser - > current_class ;
parser - > current_class = p_class ;
2022-12-11 02:57:35 +00:00
GDScriptParser : : DataType resolving_datatype ;
resolving_datatype . kind = GDScriptParser : : DataType : : RESOLVING ;
2020-05-01 22:14:56 +00:00
2022-12-11 02:57:35 +00:00
{
2023-02-02 14:57:22 +00:00
# ifdef DEBUG_ENABLED
GDScriptParser : : Node * member_node = member . get_source_node ( ) ;
if ( member_node & & member_node - > type ! = GDScriptParser : : Node : : ANNOTATION ) {
// Apply @warning_ignore annotations before resolving member.
for ( GDScriptParser : : AnnotationNode * & E : member_node - > annotations ) {
if ( E - > name = = SNAME ( " @warning_ignore " ) ) {
resolve_annotation ( E ) ;
2023-10-05 10:50:26 +00:00
E - > apply ( parser , member . variable , p_class ) ;
2023-02-02 14:57:22 +00:00
}
}
}
# endif
2020-05-01 22:14:56 +00:00
switch ( member . type ) {
case GDScriptParser : : ClassNode : : Member : : VARIABLE : {
2023-04-19 14:10:35 +00:00
bool previous_static_context = static_context ;
static_context = member . variable - > is_static ;
2023-08-23 09:37:18 +00:00
2021-08-24 16:49:03 +00:00
check_class_member_name_conflict ( p_class , member . variable - > identifier - > name , member . variable ) ;
2023-08-23 09:37:18 +00:00
2022-12-11 02:57:35 +00:00
member . variable - > set_datatype ( resolving_datatype ) ;
2022-12-22 20:43:36 +00:00
resolve_variable ( member . variable , false ) ;
2023-08-23 09:37:18 +00:00
resolve_pending_lambda_bodies ( ) ;
2021-03-17 13:57:30 +00:00
// Apply annotations.
2021-07-16 03:45:57 +00:00
for ( GDScriptParser : : AnnotationNode * & E : member . variable - > annotations ) {
2023-02-02 14:57:22 +00:00
if ( E - > name ! = SNAME ( " @warning_ignore " ) ) {
resolve_annotation ( E ) ;
2023-10-05 10:50:26 +00:00
E - > apply ( parser , member . variable , p_class ) ;
2023-02-02 14:57:22 +00:00
}
}
2023-08-23 09:37:18 +00:00
2023-04-19 14:10:35 +00:00
static_context = previous_static_context ;
2023-08-23 09:37:18 +00:00
2023-02-02 14:57:22 +00:00
# ifdef DEBUG_ENABLED
if ( member . variable - > exported & & member . variable - > onready ) {
parser - > push_warning ( member . variable , GDScriptWarning : : ONREADY_WITH_EXPORT ) ;
2020-05-01 22:14:56 +00:00
}
2023-02-02 14:57:22 +00:00
if ( member . variable - > initializer ) {
// Check if it is call to get_node() on self (using shorthand $ or not), so we can check if @onready is needed.
// This could be improved by traversing the expression fully and checking the presence of get_node at any level.
2023-04-19 14:10:35 +00:00
if ( ! member . variable - > is_static & & ! member . variable - > onready & & member . variable - > initializer & & ( member . variable - > initializer - > type = = GDScriptParser : : Node : : GET_NODE | | member . variable - > initializer - > type = = GDScriptParser : : Node : : CALL | | member . variable - > initializer - > type = = GDScriptParser : : Node : : CAST ) ) {
2023-02-02 14:57:22 +00:00
GDScriptParser : : Node * expr = member . variable - > initializer ;
if ( expr - > type = = GDScriptParser : : Node : : CAST ) {
expr = static_cast < GDScriptParser : : CastNode * > ( expr ) - > operand ;
}
bool is_get_node = expr - > type = = GDScriptParser : : Node : : GET_NODE ;
bool is_using_shorthand = is_get_node ;
if ( ! is_get_node & & expr - > type = = GDScriptParser : : Node : : CALL ) {
is_using_shorthand = false ;
GDScriptParser : : CallNode * call = static_cast < GDScriptParser : : CallNode * > ( expr ) ;
if ( call - > function_name = = SNAME ( " get_node " ) ) {
switch ( call - > get_callee_type ( ) ) {
case GDScriptParser : : Node : : IDENTIFIER : {
is_get_node = true ;
} break ;
case GDScriptParser : : Node : : SUBSCRIPT : {
GDScriptParser : : SubscriptNode * subscript = static_cast < GDScriptParser : : SubscriptNode * > ( call - > callee ) ;
is_get_node = subscript - > is_attribute & & subscript - > base - > type = = GDScriptParser : : Node : : SELF ;
} break ;
default :
break ;
}
}
}
if ( is_get_node ) {
parser - > push_warning ( member . variable , GDScriptWarning : : GET_NODE_DEFAULT_WITHOUT_ONREADY , is_using_shorthand ? " $ " : " get_node() " ) ;
}
}
}
# endif
2020-06-10 22:53:25 +00:00
} break ;
case GDScriptParser : : ClassNode : : Member : : CONSTANT : {
2021-08-24 16:49:03 +00:00
check_class_member_name_conflict ( p_class , member . constant - > identifier - > name , member . constant ) ;
2022-12-11 02:57:35 +00:00
member . constant - > set_datatype ( resolving_datatype ) ;
2022-12-22 20:43:36 +00:00
resolve_constant ( member . constant , false ) ;
2021-03-17 13:57:30 +00:00
// Apply annotations.
2021-07-16 03:45:57 +00:00
for ( GDScriptParser : : AnnotationNode * & E : member . constant - > annotations ) {
2023-01-18 17:12:33 +00:00
resolve_annotation ( E ) ;
2023-10-05 10:50:26 +00:00
E - > apply ( parser , member . constant , p_class ) ;
2021-03-17 13:57:30 +00:00
}
2020-06-10 22:53:25 +00:00
} break ;
case GDScriptParser : : ClassNode : : Member : : SIGNAL : {
2021-08-24 16:49:03 +00:00
check_class_member_name_conflict ( p_class , member . signal - > identifier - > name , member . signal ) ;
2022-12-11 02:57:35 +00:00
member . signal - > set_datatype ( resolving_datatype ) ;
2022-12-16 03:45:46 +00:00
// This is the _only_ way to declare a signal. Therefore, we can generate its
// MethodInfo inline so it's a tiny bit more efficient.
MethodInfo mi = MethodInfo ( member . signal - > identifier - > name ) ;
2020-06-10 22:53:25 +00:00
for ( int j = 0 ; j < member . signal - > parameters . size ( ) ; j + + ) {
2022-12-16 03:45:46 +00:00
GDScriptParser : : ParameterNode * param = member . signal - > parameters [ j ] ;
2023-01-11 23:03:53 +00:00
GDScriptParser : : DataType param_type = type_from_metatype ( resolve_datatype ( param - > datatype_specifier ) ) ;
2022-12-16 03:45:46 +00:00
param - > set_datatype ( param_type ) ;
2023-09-05 17:56:37 +00:00
# ifdef DEBUG_ENABLED
if ( param - > datatype_specifier = = nullptr ) {
parser - > push_warning ( param , GDScriptWarning : : UNTYPED_DECLARATION , " Parameter " , param - > identifier - > name ) ;
}
# endif
2023-08-28 16:20:10 +00:00
mi . arguments . push_back ( param_type . to_property_info ( param - > identifier - > name ) ) ;
// Signals do not support parameter default values.
2020-06-10 22:53:25 +00:00
}
2022-12-16 03:45:46 +00:00
member . signal - > set_datatype ( make_signal_type ( mi ) ) ;
2023-08-28 16:20:10 +00:00
member . signal - > method_info = mi ;
2021-03-17 13:57:30 +00:00
// Apply annotations.
2021-07-16 03:45:57 +00:00
for ( GDScriptParser : : AnnotationNode * & E : member . signal - > annotations ) {
2023-01-18 17:12:33 +00:00
resolve_annotation ( E ) ;
2023-10-05 10:50:26 +00:00
E - > apply ( parser , member . signal , p_class ) ;
2021-03-17 13:57:30 +00:00
}
2020-06-10 22:53:25 +00:00
} break ;
case GDScriptParser : : ClassNode : : Member : : ENUM : {
2021-08-24 16:49:03 +00:00
check_class_member_name_conflict ( p_class , member . m_enum - > identifier - > name , member . m_enum ) ;
2022-12-11 02:57:35 +00:00
member . m_enum - > set_datatype ( resolving_datatype ) ;
2022-12-04 03:02:03 +00:00
GDScriptParser : : DataType enum_type = make_enum_type ( member . m_enum - > identifier - > name , p_class - > fqcn , true ) ;
2020-06-12 00:49:58 +00:00
2022-12-11 02:57:35 +00:00
const GDScriptParser : : EnumNode * prev_enum = current_enum ;
2020-08-18 20:44:20 +00:00
current_enum = member . m_enum ;
2022-12-27 03:06:11 +00:00
Dictionary dictionary ;
2020-07-16 01:02:44 +00:00
for ( int j = 0 ; j < member . m_enum - > values . size ( ) ; j + + ) {
2020-08-18 20:44:20 +00:00
GDScriptParser : : EnumNode : : Value & element = member . m_enum - > values . write [ j ] ;
if ( element . custom_value ) {
reduce_expression ( element . custom_value ) ;
if ( ! element . custom_value - > is_constant ) {
push_error ( R " (Enum values must be constant.) " , element . custom_value ) ;
} else if ( element . custom_value - > reduced_value . get_type ( ) ! = Variant : : INT ) {
push_error ( R " (Enum values must be integers.) " , element . custom_value ) ;
} else {
element . value = element . custom_value - > reduced_value ;
element . resolved = true ;
}
} else {
if ( element . index > 0 ) {
element . value = element . parent_enum - > values [ element . index - 1 ] . value + 1 ;
} else {
element . value = 0 ;
}
element . resolved = true ;
}
enum_type . enum_values [ element . identifier - > name ] = element . value ;
2022-12-27 03:06:11 +00:00
dictionary [ String ( element . identifier - > name ) ] = element . value ;
2024-02-28 14:23:11 +00:00
# ifdef DEBUG_ENABLED
// Named enum identifiers do not shadow anything since you can only access them with `NamedEnum.ENUM_VALUE`.
if ( member . m_enum - > identifier - > name = = StringName ( ) ) {
is_shadowing ( element . identifier , " enum member " , false ) ;
}
# endif
2020-06-12 00:49:58 +00:00
}
2020-06-10 22:53:25 +00:00
2022-12-11 02:57:35 +00:00
current_enum = prev_enum ;
2020-08-18 20:44:20 +00:00
2023-01-22 09:07:48 +00:00
dictionary . make_read_only ( ) ;
2020-06-10 22:53:25 +00:00
member . m_enum - > set_datatype ( enum_type ) ;
2022-12-27 03:06:11 +00:00
member . m_enum - > dictionary = dictionary ;
2021-03-17 13:57:30 +00:00
// Apply annotations.
2021-07-16 03:45:57 +00:00
for ( GDScriptParser : : AnnotationNode * & E : member . m_enum - > annotations ) {
2023-01-18 17:12:33 +00:00
resolve_annotation ( E ) ;
2023-10-05 10:50:26 +00:00
E - > apply ( parser , member . m_enum , p_class ) ;
2021-03-17 13:57:30 +00:00
}
2020-06-10 22:53:25 +00:00
} break ;
case GDScriptParser : : ClassNode : : Member : : FUNCTION :
2023-02-02 14:57:22 +00:00
for ( GDScriptParser : : AnnotationNode * & E : member . function - > annotations ) {
resolve_annotation ( E ) ;
2023-10-05 10:50:26 +00:00
E - > apply ( parser , member . function , p_class ) ;
2023-02-02 14:57:22 +00:00
}
2022-12-11 02:57:35 +00:00
resolve_function_signature ( member . function , p_source ) ;
2020-05-01 22:14:56 +00:00
break ;
2020-08-18 20:44:20 +00:00
case GDScriptParser : : ClassNode : : Member : : ENUM_VALUE : {
2023-01-13 13:17:30 +00:00
member . enum_value . identifier - > set_datatype ( resolving_datatype ) ;
2020-08-18 20:44:20 +00:00
if ( member . enum_value . custom_value ) {
2021-08-24 16:49:03 +00:00
check_class_member_name_conflict ( p_class , member . enum_value . identifier - > name , member . enum_value . custom_value ) ;
2022-12-11 02:57:35 +00:00
const GDScriptParser : : EnumNode * prev_enum = current_enum ;
2020-08-18 20:44:20 +00:00
current_enum = member . enum_value . parent_enum ;
reduce_expression ( member . enum_value . custom_value ) ;
2022-12-11 02:57:35 +00:00
current_enum = prev_enum ;
2020-08-18 20:44:20 +00:00
if ( ! member . enum_value . custom_value - > is_constant ) {
push_error ( R " (Enum values must be constant.) " , member . enum_value . custom_value ) ;
} else if ( member . enum_value . custom_value - > reduced_value . get_type ( ) ! = Variant : : INT ) {
push_error ( R " (Enum values must be integers.) " , member . enum_value . custom_value ) ;
} else {
member . enum_value . value = member . enum_value . custom_value - > reduced_value ;
member . enum_value . resolved = true ;
}
} else {
2021-08-24 16:49:03 +00:00
check_class_member_name_conflict ( p_class , member . enum_value . identifier - > name , member . enum_value . parent_enum ) ;
2020-08-18 20:44:20 +00:00
if ( member . enum_value . index > 0 ) {
2022-12-17 03:46:00 +00:00
const GDScriptParser : : EnumNode : : Value & prev_value = member . enum_value . parent_enum - > values [ member . enum_value . index - 1 ] ;
resolve_class_member ( p_class , prev_value . identifier - > name , member . enum_value . identifier ) ;
member . enum_value . value = prev_value . value + 1 ;
2020-08-18 20:44:20 +00:00
} else {
member . enum_value . value = 0 ;
}
member . enum_value . resolved = true ;
}
2022-12-11 02:57:35 +00:00
2020-08-18 20:44:20 +00:00
// Also update the original references.
2022-12-11 02:57:35 +00:00
member . enum_value . parent_enum - > values . set ( member . enum_value . index , member . enum_value ) ;
2022-12-04 03:02:03 +00:00
member . enum_value . identifier - > set_datatype ( make_enum_type ( UNNAMED_ENUM , p_class - > fqcn , false ) ) ;
2020-08-18 20:44:20 +00:00
} break ;
2020-06-10 22:53:25 +00:00
case GDScriptParser : : ClassNode : : Member : : CLASS :
2021-08-24 16:49:03 +00:00
check_class_member_name_conflict ( p_class , member . m_class - > identifier - > name , member . m_class ) ;
2022-12-11 02:57:35 +00:00
// If it's already resolving, that's ok.
if ( ! member . m_class - > base_type . is_resolving ( ) ) {
resolve_class_inheritance ( member . m_class , p_source ) ;
}
2021-08-24 16:49:03 +00:00
break ;
2022-07-03 19:30:08 +00:00
case GDScriptParser : : ClassNode : : Member : : GROUP :
// No-op, but needed to silence warnings.
break ;
2020-06-10 22:53:25 +00:00
case GDScriptParser : : ClassNode : : Member : : UNDEFINED :
ERR_PRINT ( " Trying to resolve undefined member. " ) ;
2020-05-01 22:14:56 +00:00
break ;
}
}
2020-06-10 22:53:25 +00:00
2022-12-11 02:57:35 +00:00
parser - > current_class = previous_class ;
}
void GDScriptAnalyzer : : resolve_class_interface ( GDScriptParser : : ClassNode * p_class , const GDScriptParser : : Node * p_source ) {
if ( p_source = = nullptr & & parser - > has_class ( p_class ) ) {
p_source = p_class ;
}
2024-08-03 03:55:13 +00:00
Ref < GDScriptParserRef > parser_ref = ensure_cached_external_parser_for_class ( p_class , nullptr , " Trying to resolve class interface " , p_source ) ;
2024-07-23 11:46:28 +00:00
2024-02-28 14:23:11 +00:00
if ( ! p_class - > resolved_interface ) {
2023-04-19 14:10:35 +00:00
# ifdef DEBUG_ENABLED
2024-02-28 14:23:11 +00:00
bool has_static_data = p_class - > has_static_data ;
2023-04-19 14:10:35 +00:00
# endif
2022-12-11 02:57:35 +00:00
if ( ! parser - > has_class ( p_class ) ) {
if ( parser_ref . is_null ( ) ) {
2024-07-23 11:46:28 +00:00
// Error already pushed.
2022-12-11 02:57:35 +00:00
return ;
}
Error err = parser_ref - > raise_status ( GDScriptParserRef : : PARSED ) ;
if ( err ) {
2024-07-23 11:46:28 +00:00
push_error ( vformat ( R " (Could not parse script " % s " : %s.) " , p_class - > get_datatype ( ) . script_path , error_names [ err ] ) , p_source ) ;
2022-12-11 02:57:35 +00:00
return ;
}
GDScriptAnalyzer * other_analyzer = parser_ref - > get_analyzer ( ) ;
GDScriptParser * other_parser = parser_ref - > get_parser ( ) ;
int error_count = other_parser - > errors . size ( ) ;
other_analyzer - > resolve_class_interface ( p_class ) ;
if ( other_parser - > errors . size ( ) > error_count ) {
push_error ( vformat ( R " (Could not resolve class " % s " .) " , p_class - > fqcn ) , p_source ) ;
2024-07-23 11:46:28 +00:00
return ;
2022-12-11 02:57:35 +00:00
}
return ;
}
2024-02-28 14:23:11 +00:00
2022-12-11 02:57:35 +00:00
p_class - > resolved_interface = true ;
if ( resolve_class_inheritance ( p_class ) ! = OK ) {
return ;
}
GDScriptParser : : DataType base_type = p_class - > base_type ;
if ( base_type . kind = = GDScriptParser : : DataType : : CLASS ) {
GDScriptParser : : ClassNode * base_class = base_type . class_type ;
resolve_class_interface ( base_class , p_class ) ;
2020-06-10 22:53:25 +00:00
}
2022-12-11 02:57:35 +00:00
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
resolve_class_member ( p_class , i ) ;
2023-04-19 14:10:35 +00:00
# ifdef DEBUG_ENABLED
if ( ! has_static_data ) {
GDScriptParser : : ClassNode : : Member member = p_class - > members [ i ] ;
if ( member . type = = GDScriptParser : : ClassNode : : Member : : CLASS ) {
has_static_data = member . m_class - > has_static_data ;
}
}
# endif
}
# ifdef DEBUG_ENABLED
if ( ! has_static_data & & p_class - > annotated_static_unload ) {
GDScriptParser : : Node * static_unload = nullptr ;
for ( GDScriptParser : : AnnotationNode * node : p_class - > annotations ) {
if ( node - > name = = " @static_unload " ) {
static_unload = node ;
break ;
}
}
parser - > push_warning ( static_unload ? static_unload : p_class , GDScriptWarning : : REDUNDANT_STATIC_UNLOAD ) ;
2022-12-11 02:57:35 +00:00
}
2023-04-19 14:10:35 +00:00
# endif
2020-06-10 22:53:25 +00:00
}
2022-12-11 02:57:35 +00:00
}
2020-06-10 22:53:25 +00:00
2022-12-11 02:57:35 +00:00
void GDScriptAnalyzer : : resolve_class_interface ( GDScriptParser : : ClassNode * p_class , bool p_recursive ) {
resolve_class_interface ( p_class ) ;
if ( p_recursive ) {
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
GDScriptParser : : ClassNode : : Member member = p_class - > members [ i ] ;
if ( member . type = = GDScriptParser : : ClassNode : : Member : : CLASS ) {
resolve_class_interface ( member . m_class , true ) ;
}
}
}
2020-06-10 22:53:25 +00:00
}
2022-12-11 02:57:35 +00:00
void GDScriptAnalyzer : : resolve_class_body ( GDScriptParser : : ClassNode * p_class , const GDScriptParser : : Node * p_source ) {
if ( p_source = = nullptr & & parser - > has_class ( p_class ) ) {
p_source = p_class ;
}
2024-08-03 03:55:13 +00:00
Ref < GDScriptParserRef > parser_ref = ensure_cached_external_parser_for_class ( p_class , nullptr , " Trying to resolve class body " , p_source ) ;
2024-07-23 11:46:28 +00:00
2020-06-10 22:53:25 +00:00
if ( p_class - > resolved_body ) {
return ;
}
2022-12-11 02:57:35 +00:00
if ( ! parser - > has_class ( p_class ) ) {
if ( parser_ref . is_null ( ) ) {
2024-07-23 11:46:28 +00:00
// Error already pushed.
2022-12-11 02:57:35 +00:00
return ;
}
Error err = parser_ref - > raise_status ( GDScriptParserRef : : PARSED ) ;
if ( err ) {
2024-07-23 11:46:28 +00:00
push_error ( vformat ( R " (Could not parse script " % s " : %s.) " , p_class - > get_datatype ( ) . script_path , error_names [ err ] ) , p_source ) ;
2022-12-11 02:57:35 +00:00
return ;
}
GDScriptAnalyzer * other_analyzer = parser_ref - > get_analyzer ( ) ;
GDScriptParser * other_parser = parser_ref - > get_parser ( ) ;
int error_count = other_parser - > errors . size ( ) ;
other_analyzer - > resolve_class_body ( p_class ) ;
if ( other_parser - > errors . size ( ) > error_count ) {
push_error ( vformat ( R " (Could not resolve class " % s " .) " , p_class - > fqcn ) , p_source ) ;
2024-07-23 11:46:28 +00:00
return ;
2022-12-11 02:57:35 +00:00
}
return ;
}
2020-06-10 22:53:25 +00:00
p_class - > resolved_body = true ;
GDScriptParser : : ClassNode * previous_class = parser - > current_class ;
parser - > current_class = p_class ;
2022-12-11 02:57:35 +00:00
resolve_class_interface ( p_class , p_source ) ;
GDScriptParser : : DataType base_type = p_class - > base_type ;
if ( base_type . kind = = GDScriptParser : : DataType : : CLASS ) {
GDScriptParser : : ClassNode * base_class = base_type . class_type ;
resolve_class_body ( base_class , p_class ) ;
}
2023-01-27 13:25:15 +00:00
// Do functions, properties, and groups now.
2020-06-10 22:53:25 +00:00
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
GDScriptParser : : ClassNode : : Member member = p_class - > members [ i ] ;
2021-09-06 05:04:43 +00:00
if ( member . type = = GDScriptParser : : ClassNode : : Member : : FUNCTION ) {
// Apply annotations.
for ( GDScriptParser : : AnnotationNode * & E : member . function - > annotations ) {
2023-01-18 17:12:33 +00:00
resolve_annotation ( E ) ;
2023-10-05 10:50:26 +00:00
E - > apply ( parser , member . function , p_class ) ;
2021-09-06 05:04:43 +00:00
}
2022-01-04 12:32:43 +00:00
resolve_function_body ( member . function ) ;
2021-09-06 05:04:43 +00:00
} else if ( member . type = = GDScriptParser : : ClassNode : : Member : : VARIABLE & & member . variable - > property ! = GDScriptParser : : VariableNode : : PROP_NONE ) {
if ( member . variable - > property = = GDScriptParser : : VariableNode : : PROP_INLINE ) {
if ( member . variable - > getter ! = nullptr ) {
2023-09-05 17:56:37 +00:00
member . variable - > getter - > return_type = member . variable - > datatype_specifier ;
member . variable - > getter - > set_datatype ( member . get_datatype ( ) ) ;
2021-03-17 13:57:30 +00:00
2021-09-06 05:04:43 +00:00
resolve_function_body ( member . variable - > getter ) ;
}
if ( member . variable - > setter ! = nullptr ) {
2023-09-05 17:56:37 +00:00
ERR_CONTINUE ( member . variable - > setter - > parameters . is_empty ( ) ) ;
member . variable - > setter - > parameters [ 0 ] - > datatype_specifier = member . variable - > datatype_specifier ;
member . variable - > setter - > parameters [ 0 ] - > set_datatype ( member . get_datatype ( ) ) ;
2021-09-06 05:04:43 +00:00
resolve_function_body ( member . variable - > setter ) ;
}
}
2023-01-27 13:25:15 +00:00
} else if ( member . type = = GDScriptParser : : ClassNode : : Member : : GROUP ) {
// Apply annotation (`@export_{category,group,subgroup}`).
resolve_annotation ( member . annotation ) ;
2023-10-05 10:50:26 +00:00
member . annotation - > apply ( parser , nullptr , p_class ) ;
2021-03-17 13:57:30 +00:00
}
2020-06-10 22:53:25 +00:00
}
2021-09-06 05:04:43 +00:00
// Check unused variables and datatypes of property getters and setters.
2020-06-11 22:31:28 +00:00
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
GDScriptParser : : ClassNode : : Member member = p_class - > members [ i ] ;
2021-09-06 05:04:43 +00:00
if ( member . type = = GDScriptParser : : ClassNode : : Member : : VARIABLE ) {
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2021-09-06 05:04:43 +00:00
if ( member . variable - > usages = = 0 & & String ( member . variable - > identifier - > name ) . begins_with ( " _ " ) ) {
parser - > push_warning ( member . variable - > identifier , GDScriptWarning : : UNUSED_PRIVATE_CLASS_VARIABLE , member . variable - > identifier - > name ) ;
}
# endif
if ( member . variable - > property = = GDScriptParser : : VariableNode : : PROP_SETGET ) {
GDScriptParser : : FunctionNode * getter_function = nullptr ;
GDScriptParser : : FunctionNode * setter_function = nullptr ;
bool has_valid_getter = false ;
bool has_valid_setter = false ;
if ( member . variable - > getter_pointer ! = nullptr ) {
if ( p_class - > has_function ( member . variable - > getter_pointer - > name ) ) {
getter_function = p_class - > get_member ( member . variable - > getter_pointer - > name ) . function ;
}
if ( getter_function = = nullptr ) {
push_error ( vformat ( R " (Getter " % s " not found.) " , member . variable - > getter_pointer - > name ) , member . variable ) ;
} else {
2022-10-30 00:38:26 +00:00
GDScriptParser : : DataType return_datatype = getter_function - > datatype ;
if ( getter_function - > return_type ! = nullptr ) {
return_datatype = getter_function - > return_type - > datatype ;
return_datatype . is_meta_type = false ;
}
if ( getter_function - > parameters . size ( ) ! = 0 | | return_datatype . has_no_type ( ) ) {
push_error ( vformat ( R " (Function " % s " cannot be used as getter because of its signature.) " , getter_function - > identifier - > name ) , member . variable ) ;
} else if ( ! is_type_compatible ( member . variable - > datatype , return_datatype , true ) ) {
push_error ( vformat ( R " (Function with return type " % s " cannot be used as getter for a property of type " % s " .) " , return_datatype . to_string ( ) , member . variable - > datatype . to_string ( ) ) , member . variable ) ;
2021-09-06 05:04:43 +00:00
2022-10-30 00:38:26 +00:00
} else {
has_valid_getter = true ;
2021-09-06 05:04:43 +00:00
# ifdef DEBUG_ENABLED
2022-10-30 00:38:26 +00:00
if ( member . variable - > datatype . builtin_type = = Variant : : INT & & return_datatype . builtin_type = = Variant : : FLOAT ) {
parser - > push_warning ( member . variable , GDScriptWarning : : NARROWING_CONVERSION ) ;
}
2021-09-06 05:04:43 +00:00
# endif
2022-10-30 00:38:26 +00:00
}
2021-09-06 05:04:43 +00:00
}
}
if ( member . variable - > setter_pointer ! = nullptr ) {
if ( p_class - > has_function ( member . variable - > setter_pointer - > name ) ) {
setter_function = p_class - > get_member ( member . variable - > setter_pointer - > name ) . function ;
}
if ( setter_function = = nullptr ) {
push_error ( vformat ( R " (Setter " % s " not found.) " , member . variable - > setter_pointer - > name ) , member . variable ) ;
} else if ( setter_function - > parameters . size ( ) ! = 1 ) {
push_error ( vformat ( R " (Function " % s " cannot be used as setter because of its signature.) " , setter_function - > identifier - > name ) , member . variable ) ;
} else if ( ! is_type_compatible ( member . variable - > datatype , setter_function - > parameters [ 0 ] - > datatype , true ) ) {
push_error ( vformat ( R " (Function with argument type " % s " cannot be used as setter for a property of type " % s " .) " , setter_function - > parameters [ 0 ] - > datatype . to_string ( ) , member . variable - > datatype . to_string ( ) ) , member . variable ) ;
} else {
has_valid_setter = true ;
# ifdef DEBUG_ENABLED
2021-10-18 07:01:16 +00:00
if ( member . variable - > datatype . builtin_type = = Variant : : FLOAT & & setter_function - > parameters [ 0 ] - > datatype . builtin_type = = Variant : : INT ) {
2021-09-06 05:04:43 +00:00
parser - > push_warning ( member . variable , GDScriptWarning : : NARROWING_CONVERSION ) ;
}
2020-07-16 01:02:44 +00:00
# endif
2021-09-06 05:04:43 +00:00
}
}
if ( member . variable - > datatype . is_variant ( ) & & has_valid_getter & & has_valid_setter ) {
if ( ! is_type_compatible ( getter_function - > datatype , setter_function - > parameters [ 0 ] - > datatype , true ) ) {
push_error ( vformat ( R " (Getter with type " % s " cannot be used along with setter of type " % s " .) " , getter_function - > datatype . to_string ( ) , setter_function - > parameters [ 0 ] - > datatype . to_string ( ) ) , member . variable ) ;
}
}
2023-04-18 11:08:48 +00:00
}
2024-02-28 14:23:11 +00:00
} else if ( member . type = = GDScriptParser : : ClassNode : : Member : : SIGNAL ) {
2022-01-04 12:32:43 +00:00
# ifdef DEBUG_ENABLED
2024-02-28 14:23:11 +00:00
if ( member . signal - > usages = = 0 ) {
parser - > push_warning ( member . signal - > identifier , GDScriptWarning : : UNUSED_SIGNAL , member . signal - > identifier - > name ) ;
}
# endif
2021-09-06 05:04:43 +00:00
}
2020-06-11 22:31:28 +00:00
}
2022-12-11 02:57:35 +00:00
2023-08-23 09:37:18 +00:00
if ( ! pending_body_resolution_lambdas . is_empty ( ) ) {
ERR_PRINT ( " GDScript bug (please report): Not all pending lambda bodies were resolved in time. " ) ;
resolve_pending_lambda_bodies ( ) ;
}
2022-12-11 02:57:35 +00:00
parser - > current_class = previous_class ;
}
void GDScriptAnalyzer : : resolve_class_body ( GDScriptParser : : ClassNode * p_class , bool p_recursive ) {
resolve_class_body ( p_class ) ;
if ( p_recursive ) {
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
GDScriptParser : : ClassNode : : Member member = p_class - > members [ i ] ;
if ( member . type = = GDScriptParser : : ClassNode : : Member : : CLASS ) {
resolve_class_body ( member . m_class , true ) ;
}
}
}
2020-06-10 22:53:25 +00:00
}
2022-10-13 17:31:12 +00:00
void GDScriptAnalyzer : : resolve_node ( GDScriptParser : : Node * p_node , bool p_is_root ) {
2023-09-09 15:40:07 +00:00
ERR_FAIL_NULL_MSG ( p_node , " Trying to resolve type of a null node. " ) ;
2020-06-10 22:53:25 +00:00
switch ( p_node - > type ) {
case GDScriptParser : : Node : : NONE :
break ; // Unreachable.
case GDScriptParser : : Node : : CLASS :
2024-02-28 14:23:11 +00:00
// NOTE: Currently this route is never executed, `resolve_class_*()` is called directly.
2022-12-11 02:57:35 +00:00
if ( OK = = resolve_class_inheritance ( static_cast < GDScriptParser : : ClassNode * > ( p_node ) , true ) ) {
resolve_class_interface ( static_cast < GDScriptParser : : ClassNode * > ( p_node ) , true ) ;
resolve_class_body ( static_cast < GDScriptParser : : ClassNode * > ( p_node ) , true ) ;
}
2020-06-10 22:53:25 +00:00
break ;
case GDScriptParser : : Node : : CONSTANT :
2022-12-22 20:43:36 +00:00
resolve_constant ( static_cast < GDScriptParser : : ConstantNode * > ( p_node ) , true ) ;
2020-06-10 22:53:25 +00:00
break ;
case GDScriptParser : : Node : : FOR :
resolve_for ( static_cast < GDScriptParser : : ForNode * > ( p_node ) ) ;
break ;
case GDScriptParser : : Node : : IF :
resolve_if ( static_cast < GDScriptParser : : IfNode * > ( p_node ) ) ;
break ;
case GDScriptParser : : Node : : SUITE :
resolve_suite ( static_cast < GDScriptParser : : SuiteNode * > ( p_node ) ) ;
break ;
case GDScriptParser : : Node : : VARIABLE :
2022-12-22 20:43:36 +00:00
resolve_variable ( static_cast < GDScriptParser : : VariableNode * > ( p_node ) , true ) ;
2020-06-10 22:53:25 +00:00
break ;
case GDScriptParser : : Node : : WHILE :
resolve_while ( static_cast < GDScriptParser : : WhileNode * > ( p_node ) ) ;
break ;
case GDScriptParser : : Node : : ANNOTATION :
resolve_annotation ( static_cast < GDScriptParser : : AnnotationNode * > ( p_node ) ) ;
break ;
case GDScriptParser : : Node : : ASSERT :
resolve_assert ( static_cast < GDScriptParser : : AssertNode * > ( p_node ) ) ;
break ;
case GDScriptParser : : Node : : MATCH :
resolve_match ( static_cast < GDScriptParser : : MatchNode * > ( p_node ) ) ;
break ;
case GDScriptParser : : Node : : MATCH_BRANCH :
resolve_match_branch ( static_cast < GDScriptParser : : MatchBranchNode * > ( p_node ) , nullptr ) ;
break ;
case GDScriptParser : : Node : : PARAMETER :
2020-09-06 07:56:52 +00:00
resolve_parameter ( static_cast < GDScriptParser : : ParameterNode * > ( p_node ) ) ;
2020-06-10 22:53:25 +00:00
break ;
case GDScriptParser : : Node : : PATTERN :
resolve_match_pattern ( static_cast < GDScriptParser : : PatternNode * > ( p_node ) , nullptr ) ;
break ;
case GDScriptParser : : Node : : RETURN :
resolve_return ( static_cast < GDScriptParser : : ReturnNode * > ( p_node ) ) ;
break ;
case GDScriptParser : : Node : : TYPE :
resolve_datatype ( static_cast < GDScriptParser : : TypeNode * > ( p_node ) ) ;
break ;
// Resolving expression is the same as reducing them.
case GDScriptParser : : Node : : ARRAY :
case GDScriptParser : : Node : : ASSIGNMENT :
case GDScriptParser : : Node : : AWAIT :
case GDScriptParser : : Node : : BINARY_OPERATOR :
case GDScriptParser : : Node : : CALL :
case GDScriptParser : : Node : : CAST :
case GDScriptParser : : Node : : DICTIONARY :
case GDScriptParser : : Node : : GET_NODE :
case GDScriptParser : : Node : : IDENTIFIER :
2021-03-26 12:03:16 +00:00
case GDScriptParser : : Node : : LAMBDA :
2020-06-10 22:53:25 +00:00
case GDScriptParser : : Node : : LITERAL :
case GDScriptParser : : Node : : PRELOAD :
case GDScriptParser : : Node : : SELF :
case GDScriptParser : : Node : : SUBSCRIPT :
case GDScriptParser : : Node : : TERNARY_OPERATOR :
2023-02-16 23:16:24 +00:00
case GDScriptParser : : Node : : TYPE_TEST :
2020-06-10 22:53:25 +00:00
case GDScriptParser : : Node : : UNARY_OPERATOR :
2022-10-13 17:31:12 +00:00
reduce_expression ( static_cast < GDScriptParser : : ExpressionNode * > ( p_node ) , p_is_root ) ;
2020-06-10 22:53:25 +00:00
break ;
case GDScriptParser : : Node : : BREAK :
case GDScriptParser : : Node : : BREAKPOINT :
case GDScriptParser : : Node : : CONTINUE :
case GDScriptParser : : Node : : ENUM :
2022-12-28 05:41:03 +00:00
case GDScriptParser : : Node : : FUNCTION :
2020-06-10 22:53:25 +00:00
case GDScriptParser : : Node : : PASS :
case GDScriptParser : : Node : : SIGNAL :
// Nothing to do.
break ;
}
}
void GDScriptAnalyzer : : resolve_annotation ( GDScriptParser : : AnnotationNode * p_annotation ) {
2023-01-18 17:12:33 +00:00
ERR_FAIL_COND_MSG ( ! parser - > valid_annotations . has ( p_annotation - > name ) , vformat ( R " (Annotation " % s " not found to validate.) " , p_annotation - > name ) ) ;
2023-02-02 14:57:22 +00:00
if ( p_annotation - > is_resolved ) {
return ;
}
p_annotation - > is_resolved = true ;
2023-01-18 17:12:33 +00:00
const MethodInfo & annotation_info = parser - > valid_annotations [ p_annotation - > name ] . info ;
const List < PropertyInfo > : : Element * E = annotation_info . arguments . front ( ) ;
for ( int i = 0 ; i < p_annotation - > arguments . size ( ) ; i + + ) {
GDScriptParser : : ExpressionNode * argument = p_annotation - > arguments [ i ] ;
const PropertyInfo & argument_info = E - > get ( ) ;
if ( E - > next ( ) ! = nullptr ) {
E = E - > next ( ) ;
}
reduce_expression ( argument ) ;
if ( ! argument - > is_constant ) {
push_error ( vformat ( R " (Argument %d of annotation " % s " isn't a constant expression.) " , i + 1 , p_annotation - > name ) , argument ) ;
return ;
}
Variant value = argument - > reduced_value ;
if ( value . get_type ( ) ! = argument_info . type ) {
# ifdef DEBUG_ENABLED
if ( argument_info . type = = Variant : : INT & & value . get_type ( ) = = Variant : : FLOAT ) {
parser - > push_warning ( argument , GDScriptWarning : : NARROWING_CONVERSION ) ;
}
# endif
if ( ! Variant : : can_convert_strict ( value . get_type ( ) , argument_info . type ) ) {
push_error ( vformat ( R " (Invalid argument for annotation " % s " : argument %d should be " % s " but is " % s " .) " , p_annotation - > name , i + 1 , Variant : : get_type_name ( argument_info . type ) , argument - > get_datatype ( ) . to_string ( ) ) , argument ) ;
return ;
}
Variant converted_to ;
const Variant * converted_from = & value ;
Callable : : CallError call_error ;
Variant : : construct ( argument_info . type , converted_to , & converted_from , 1 , call_error ) ;
if ( call_error . error ! = Callable : : CallError : : CALL_OK ) {
push_error ( vformat ( R " (Cannot convert argument %d of annotation " % s " from " % s " to " % s " .) " , i + 1 , p_annotation - > name , Variant : : get_type_name ( value . get_type ( ) ) , Variant : : get_type_name ( argument_info . type ) ) , argument ) ;
return ;
}
value = converted_to ;
}
p_annotation - > resolved_arguments . push_back ( value ) ;
}
2020-06-10 22:53:25 +00:00
}
2022-12-28 05:41:03 +00:00
void GDScriptAnalyzer : : resolve_function_signature ( GDScriptParser : : FunctionNode * p_function , const GDScriptParser : : Node * p_source , bool p_is_lambda ) {
2022-12-11 02:57:35 +00:00
if ( p_source = = nullptr ) {
p_source = p_function ;
}
2022-12-28 05:41:03 +00:00
StringName function_name = p_function - > identifier ! = nullptr ? p_function - > identifier - > name : StringName ( ) ;
2022-12-11 02:57:35 +00:00
if ( p_function - > get_datatype ( ) . is_resolving ( ) ) {
2022-12-28 05:41:03 +00:00
push_error ( vformat ( R " (Could not resolve function " % s " : Cyclic reference.) " , function_name ) , p_source ) ;
2022-12-11 02:57:35 +00:00
return ;
}
2020-06-10 22:53:25 +00:00
if ( p_function - > resolved_signature ) {
return ;
}
p_function - > resolved_signature = true ;
GDScriptParser : : FunctionNode * previous_function = parser - > current_function ;
parser - > current_function = p_function ;
2023-04-19 14:10:35 +00:00
bool previous_static_context = static_context ;
2023-10-17 09:46:41 +00:00
if ( p_is_lambda ) {
// For lambdas this is determined from the context, the `static` keyword is not allowed.
p_function - > is_static = static_context ;
} else {
// For normal functions, this is determined in the parser by the `static` keyword.
static_context = p_function - > is_static ;
}
2020-06-10 22:53:25 +00:00
2022-12-11 02:57:35 +00:00
GDScriptParser : : DataType prev_datatype = p_function - > get_datatype ( ) ;
GDScriptParser : : DataType resolving_datatype ;
resolving_datatype . kind = GDScriptParser : : DataType : : RESOLVING ;
p_function - > set_datatype ( resolving_datatype ) ;
2022-03-14 14:55:37 +00:00
# ifdef TOOLS_ENABLED
int default_value_count = 0 ;
# endif // TOOLS_ENABLED
2023-09-05 17:56:37 +00:00
# ifdef DEBUG_ENABLED
String function_visible_name = function_name ;
if ( function_name = = StringName ( ) ) {
function_visible_name = p_is_lambda ? " <anonymous lambda> " : " <unknown function> " ;
}
# endif
2020-06-10 22:53:25 +00:00
for ( int i = 0 ; i < p_function - > parameters . size ( ) ; i + + ) {
2020-09-06 07:56:52 +00:00
resolve_parameter ( p_function - > parameters [ i ] ) ;
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2020-06-11 22:31:28 +00:00
if ( p_function - > parameters [ i ] - > usages = = 0 & & ! String ( p_function - > parameters [ i ] - > identifier - > name ) . begins_with ( " _ " ) ) {
2023-09-05 17:56:37 +00:00
parser - > push_warning ( p_function - > parameters [ i ] - > identifier , GDScriptWarning : : UNUSED_PARAMETER , function_visible_name , p_function - > parameters [ i ] - > identifier - > name ) ;
2020-06-11 22:31:28 +00:00
}
2023-04-03 13:57:41 +00:00
is_shadowing ( p_function - > parameters [ i ] - > identifier , " function parameter " , true ) ;
2020-11-29 02:37:57 +00:00
# endif // DEBUG_ENABLED
2023-08-28 16:20:10 +00:00
2022-12-22 20:43:36 +00:00
if ( p_function - > parameters [ i ] - > initializer ) {
2023-08-28 16:20:10 +00:00
# ifdef TOOLS_ENABLED
2022-03-14 14:55:37 +00:00
default_value_count + + ;
2023-08-28 16:20:10 +00:00
# endif // TOOLS_ENABLED
2022-03-14 14:55:37 +00:00
2022-12-22 20:43:36 +00:00
if ( p_function - > parameters [ i ] - > initializer - > is_constant ) {
p_function - > default_arg_values . push_back ( p_function - > parameters [ i ] - > initializer - > reduced_value ) ;
2022-11-10 16:22:52 +00:00
} else {
p_function - > default_arg_values . push_back ( Variant ( ) ) ; // Prevent shift.
2022-03-14 14:55:37 +00:00
}
2020-11-29 02:37:57 +00:00
}
2020-06-10 22:53:25 +00:00
}
2022-12-28 05:41:03 +00:00
if ( ! p_is_lambda & & function_name = = GDScriptLanguage : : get_singleton ( ) - > strings . _init ) {
2020-07-16 01:02:44 +00:00
// Constructor.
GDScriptParser : : DataType return_type = parser - > current_class - > get_datatype ( ) ;
return_type . is_meta_type = false ;
p_function - > set_datatype ( return_type ) ;
if ( p_function - > return_type ) {
2021-10-03 18:55:54 +00:00
GDScriptParser : : DataType declared_return = resolve_datatype ( p_function - > return_type ) ;
if ( declared_return . kind ! = GDScriptParser : : DataType : : BUILTIN | | declared_return . builtin_type ! = Variant : : NIL ) {
push_error ( " Constructor cannot have an explicit return type. " , p_function - > return_type ) ;
}
2020-07-16 01:02:44 +00:00
}
2023-04-19 14:10:35 +00:00
} else if ( ! p_is_lambda & & function_name = = GDScriptLanguage : : get_singleton ( ) - > strings . _static_init ) {
// Static constructor.
GDScriptParser : : DataType return_type ;
return_type . kind = GDScriptParser : : DataType : : BUILTIN ;
return_type . builtin_type = Variant : : NIL ;
p_function - > set_datatype ( return_type ) ;
if ( p_function - > return_type ) {
GDScriptParser : : DataType declared_return = resolve_datatype ( p_function - > return_type ) ;
if ( declared_return . kind ! = GDScriptParser : : DataType : : BUILTIN | | declared_return . builtin_type ! = Variant : : NIL ) {
push_error ( " Static constructor cannot have an explicit return type. " , p_function - > return_type ) ;
}
}
2020-07-16 01:02:44 +00:00
} else {
2022-05-24 16:40:36 +00:00
if ( p_function - > return_type ! = nullptr ) {
2022-12-04 03:02:03 +00:00
p_function - > set_datatype ( type_from_metatype ( resolve_datatype ( p_function - > return_type ) ) ) ;
2022-05-24 16:40:36 +00:00
} else {
// In case the function is not typed, we can safely assume it's a Variant, so it's okay to mark as "inferred" here.
// It's not "undetected" to not mix up with unknown functions.
GDScriptParser : : DataType return_type ;
return_type . type_source = GDScriptParser : : DataType : : INFERRED ;
return_type . kind = GDScriptParser : : DataType : : VARIANT ;
p_function - > set_datatype ( return_type ) ;
}
2022-03-06 14:09:12 +00:00
2022-03-07 07:01:52 +00:00
# ifdef TOOLS_ENABLED
2022-03-06 14:09:12 +00:00
// Check if the function signature matches the parent. If not it's an error since it breaks polymorphism.
// Not for the constructor which can vary in signature.
GDScriptParser : : DataType base_type = parser - > current_class - > base_type ;
2023-02-21 17:37:08 +00:00
base_type . is_meta_type = false ;
2022-03-06 14:09:12 +00:00
GDScriptParser : : DataType parent_return_type ;
List < GDScriptParser : : DataType > parameters_types ;
int default_par_count = 0 ;
2023-05-21 23:22:00 +00:00
BitField < MethodFlags > method_flags ;
2023-02-02 14:57:22 +00:00
StringName native_base ;
2023-05-21 23:22:00 +00:00
if ( ! p_is_lambda & & get_function_signature ( p_function , false , base_type , function_name , parent_return_type , parameters_types , default_par_count , method_flags , & native_base ) ) {
bool valid = p_function - > is_static = = method_flags . has_flag ( METHOD_FLAG_STATIC ) ;
2023-09-28 08:28:28 +00:00
if ( p_function - > return_type ! = nullptr ) {
// Check return type covariance.
GDScriptParser : : DataType return_type = p_function - > get_datatype ( ) ;
if ( return_type . is_variant ( ) ) {
// `is_type_compatible()` returns `true` if one of the types is `Variant`.
// Don't allow an explicitly specified `Variant` if the parent return type is narrower.
valid = valid & & parent_return_type . is_variant ( ) ;
} else if ( return_type . kind = = GDScriptParser : : DataType : : BUILTIN & & return_type . builtin_type = = Variant : : NIL ) {
// `is_type_compatible()` returns `true` if target is an `Object` and source is `null`.
// Don't allow `void` if the parent return type is a hard non-`void` type.
if ( parent_return_type . is_hard_type ( ) & & ! ( parent_return_type . kind = = GDScriptParser : : DataType : : BUILTIN & & parent_return_type . builtin_type = = Variant : : NIL ) ) {
valid = false ;
}
} else {
valid = valid & & is_type_compatible ( parent_return_type , return_type ) ;
}
}
2022-03-06 14:09:12 +00:00
int par_count_diff = p_function - > parameters . size ( ) - parameters_types . size ( ) ;
valid = valid & & par_count_diff > = 0 ;
2022-03-14 14:55:37 +00:00
valid = valid & & default_value_count > = default_par_count + par_count_diff ;
2022-03-06 14:09:12 +00:00
2023-09-28 08:28:28 +00:00
if ( valid ) {
int i = 0 ;
for ( const GDScriptParser : : DataType & parent_par_type : parameters_types ) {
// Check parameter type contravariance.
GDScriptParser : : DataType current_par_type = p_function - > parameters [ i + + ] - > get_datatype ( ) ;
if ( parent_par_type . is_variant ( ) & & parent_par_type . is_hard_type ( ) ) {
// `is_type_compatible()` returns `true` if one of the types is `Variant`.
// Don't allow narrowing a hard `Variant`.
valid = valid & & current_par_type . is_variant ( ) ;
} else {
valid = valid & & is_type_compatible ( current_par_type , parent_par_type ) ;
}
}
2022-03-06 14:09:12 +00:00
}
if ( ! valid ) {
// Compute parent signature as a string to show in the error message.
2022-12-04 03:02:03 +00:00
String parent_signature = String ( function_name ) + " ( " ;
2022-03-06 14:09:12 +00:00
int j = 0 ;
for ( const GDScriptParser : : DataType & par_type : parameters_types ) {
if ( j > 0 ) {
parent_signature + = " , " ;
}
String parameter = par_type . to_string ( ) ;
if ( parameter = = " null " ) {
parameter = " Variant " ;
}
parent_signature + = parameter ;
2023-02-02 14:57:22 +00:00
if ( j > = parameters_types . size ( ) - default_par_count ) {
parent_signature + = " = <default> " ;
2022-03-06 14:09:12 +00:00
}
j + + ;
}
2022-12-01 00:46:01 +00:00
parent_signature + = " ) -> " ;
2023-09-21 09:42:55 +00:00
const String return_type = parent_return_type . to_string_strict ( ) ;
2022-12-01 00:46:01 +00:00
if ( return_type = = " null " ) {
parent_signature + = " void " ;
} else {
parent_signature + = return_type ;
}
2022-03-06 14:09:12 +00:00
push_error ( vformat ( R " (The function signature doesn't match the parent. Parent signature is " % s " .) " , parent_signature ) , p_function ) ;
}
2023-02-02 14:57:22 +00:00
# ifdef DEBUG_ENABLED
if ( native_base ! = StringName ( ) ) {
parser - > push_warning ( p_function , GDScriptWarning : : NATIVE_METHOD_OVERRIDE , function_name , native_base ) ;
}
# endif
2022-03-06 14:09:12 +00:00
}
2022-03-14 14:55:37 +00:00
# endif // TOOLS_ENABLED
2020-07-16 01:02:44 +00:00
}
2020-06-10 22:53:25 +00:00
2023-09-05 17:56:37 +00:00
# ifdef DEBUG_ENABLED
if ( p_function - > return_type = = nullptr ) {
parser - > push_warning ( p_function , GDScriptWarning : : UNTYPED_DECLARATION , " Function " , function_visible_name ) ;
}
# endif
2022-12-11 02:57:35 +00:00
if ( p_function - > get_datatype ( ) . is_resolving ( ) ) {
p_function - > set_datatype ( prev_datatype ) ;
}
2020-06-10 22:53:25 +00:00
parser - > current_function = previous_function ;
2023-04-19 14:10:35 +00:00
static_context = previous_static_context ;
2020-06-10 22:53:25 +00:00
}
2022-12-28 05:41:03 +00:00
void GDScriptAnalyzer : : resolve_function_body ( GDScriptParser : : FunctionNode * p_function , bool p_is_lambda ) {
2020-06-10 22:53:25 +00:00
if ( p_function - > resolved_body ) {
return ;
}
p_function - > resolved_body = true ;
GDScriptParser : : FunctionNode * previous_function = parser - > current_function ;
parser - > current_function = p_function ;
2023-05-17 07:25:03 +00:00
bool previous_static_context = static_context ;
static_context = p_function - > is_static ;
2020-06-10 22:53:25 +00:00
resolve_suite ( p_function - > body ) ;
2022-11-22 22:09:30 +00:00
if ( ! p_function - > get_datatype ( ) . is_hard_type ( ) & & p_function - > body - > get_datatype ( ) . is_set ( ) ) {
2020-06-10 22:53:25 +00:00
// Use the suite inferred type if return isn't explicitly set.
p_function - > set_datatype ( p_function - > body - > get_datatype ( ) ) ;
2020-06-11 22:31:28 +00:00
} else if ( p_function - > get_datatype ( ) . is_hard_type ( ) & & ( p_function - > get_datatype ( ) . kind ! = GDScriptParser : : DataType : : BUILTIN | | p_function - > get_datatype ( ) . builtin_type ! = Variant : : NIL ) ) {
2022-12-28 05:41:03 +00:00
if ( ! p_function - > body - > has_return & & ( p_is_lambda | | p_function - > identifier - > name ! = GDScriptLanguage : : get_singleton ( ) - > strings . _init ) ) {
2020-06-11 22:31:28 +00:00
push_error ( R " (Not all code paths return a value.) " , p_function ) ;
}
2020-06-10 22:53:25 +00:00
}
parser - > current_function = previous_function ;
2023-05-17 07:25:03 +00:00
static_context = previous_static_context ;
2020-06-10 22:53:25 +00:00
}
void GDScriptAnalyzer : : decide_suite_type ( GDScriptParser : : Node * p_suite , GDScriptParser : : Node * p_statement ) {
2020-07-16 01:02:44 +00:00
if ( p_statement = = nullptr ) {
return ;
}
2020-06-10 22:53:25 +00:00
switch ( p_statement - > type ) {
case GDScriptParser : : Node : : IF :
case GDScriptParser : : Node : : FOR :
case GDScriptParser : : Node : : MATCH :
case GDScriptParser : : Node : : PATTERN :
case GDScriptParser : : Node : : RETURN :
case GDScriptParser : : Node : : WHILE :
// Use return or nested suite type as this suite type.
if ( p_suite - > get_datatype ( ) . is_set ( ) & & ( p_suite - > get_datatype ( ) ! = p_statement - > get_datatype ( ) ) ) {
// Mixed types.
// TODO: This could use the common supertype instead.
p_suite - > datatype . kind = GDScriptParser : : DataType : : VARIANT ;
p_suite - > datatype . type_source = GDScriptParser : : DataType : : UNDETECTED ;
} else {
p_suite - > set_datatype ( p_statement - > get_datatype ( ) ) ;
2020-08-07 17:51:56 +00:00
p_suite - > datatype . type_source = GDScriptParser : : DataType : : INFERRED ;
2020-06-10 22:53:25 +00:00
}
break ;
default :
break ;
}
}
void GDScriptAnalyzer : : resolve_suite ( GDScriptParser : : SuiteNode * p_suite ) {
for ( int i = 0 ; i < p_suite - > statements . size ( ) ; i + + ) {
GDScriptParser : : Node * stmt = p_suite - > statements [ i ] ;
2023-01-18 17:12:33 +00:00
// Apply annotations.
for ( GDScriptParser : : AnnotationNode * & E : stmt - > annotations ) {
resolve_annotation ( E ) ;
2024-02-28 14:23:11 +00:00
E - > apply ( parser , stmt , nullptr ) ; // TODO: Provide `p_class`.
2022-01-04 12:32:43 +00:00
}
2020-06-10 22:53:25 +00:00
resolve_node ( stmt ) ;
2023-08-23 09:37:18 +00:00
resolve_pending_lambda_bodies ( ) ;
2020-06-10 22:53:25 +00:00
decide_suite_type ( p_suite , stmt ) ;
}
}
2022-12-22 20:43:36 +00:00
void GDScriptAnalyzer : : resolve_assignable ( GDScriptParser : : AssignableNode * p_assignable , const char * p_kind ) {
GDScriptParser : : DataType type ;
type . kind = GDScriptParser : : DataType : : VARIANT ;
bool is_constant = p_assignable - > type = = GDScriptParser : : Node : : CONSTANT ;
2023-07-25 11:21:49 +00:00
# ifdef DEBUG_ENABLED
if ( p_assignable - > identifier ! = nullptr & & p_assignable - > identifier - > suite ! = nullptr & & p_assignable - > identifier - > suite - > parent_block ! = nullptr ) {
if ( p_assignable - > identifier - > suite - > parent_block - > has_local ( p_assignable - > identifier - > name ) ) {
const GDScriptParser : : SuiteNode : : Local & local = p_assignable - > identifier - > suite - > parent_block - > get_local ( p_assignable - > identifier - > name ) ;
parser - > push_warning ( p_assignable - > identifier , GDScriptWarning : : CONFUSABLE_LOCAL_DECLARATION , local . get_name ( ) , p_assignable - > identifier - > name ) ;
}
}
# endif
2022-12-22 20:43:36 +00:00
GDScriptParser : : DataType specified_type ;
bool has_specified_type = p_assignable - > datatype_specifier ! = nullptr ;
if ( has_specified_type ) {
2023-01-11 23:03:53 +00:00
specified_type = type_from_metatype ( resolve_datatype ( p_assignable - > datatype_specifier ) ) ;
2022-12-22 20:43:36 +00:00
type = specified_type ;
}
if ( p_assignable - > initializer ! = nullptr ) {
reduce_expression ( p_assignable - > initializer ) ;
if ( p_assignable - > initializer - > type = = GDScriptParser : : Node : : ARRAY ) {
GDScriptParser : : ArrayNode * array = static_cast < GDScriptParser : : ArrayNode * > ( p_assignable - > initializer ) ;
2023-09-14 18:31:07 +00:00
if ( has_specified_type & & specified_type . has_container_element_type ( 0 ) ) {
update_array_literal_element_type ( array , specified_type . get_container_element_type ( 0 ) ) ;
2022-12-22 20:43:36 +00:00
}
2023-06-24 18:03:28 +00:00
} else if ( p_assignable - > initializer - > type = = GDScriptParser : : Node : : DICTIONARY ) {
GDScriptParser : : DictionaryNode * dictionary = static_cast < GDScriptParser : : DictionaryNode * > ( p_assignable - > initializer ) ;
if ( has_specified_type & & specified_type . has_container_element_types ( ) ) {
update_dictionary_literal_element_type ( dictionary , specified_type . get_container_element_type_or_variant ( 0 ) , specified_type . get_container_element_type_or_variant ( 1 ) ) ;
}
2022-12-22 20:43:36 +00:00
}
2022-11-27 07:56:53 +00:00
if ( is_constant & & ! p_assignable - > initializer - > is_constant ) {
bool is_initializer_value_reduced = false ;
Variant initializer_value = make_expression_reduced_value ( p_assignable - > initializer , is_initializer_value_reduced ) ;
if ( is_initializer_value_reduced ) {
p_assignable - > initializer - > is_constant = true ;
p_assignable - > initializer - > reduced_value = initializer_value ;
} else {
2022-12-22 20:43:36 +00:00
push_error ( vformat ( R " (Assigned value for %s " % s " isn't a constant expression.) " , p_kind , p_assignable - > identifier - > name ) , p_assignable - > initializer ) ;
}
}
2023-01-22 08:32:05 +00:00
if ( has_specified_type & & p_assignable - > initializer - > is_constant ) {
update_const_expression_builtin_type ( p_assignable - > initializer , specified_type , " assign " ) ;
}
2022-12-22 20:43:36 +00:00
GDScriptParser : : DataType initializer_type = p_assignable - > initializer - > get_datatype ( ) ;
if ( p_assignable - > infer_datatype ) {
2023-01-13 18:51:29 +00:00
if ( ! initializer_type . is_set ( ) | | initializer_type . has_no_type ( ) | | ! initializer_type . is_hard_type ( ) ) {
2022-12-22 20:43:36 +00:00
push_error ( vformat ( R " (Cannot infer the type of " % s " %s because the value doesn't have a set type.) " , p_assignable - > identifier - > name , p_kind ) , p_assignable - > initializer ) ;
} else if ( initializer_type . kind = = GDScriptParser : : DataType : : BUILTIN & & initializer_type . builtin_type = = Variant : : NIL & & ! is_constant ) {
push_error ( vformat ( R " (Cannot infer the type of " % s " %s because the value is " null " .) " , p_assignable - > identifier - > name , p_kind ) , p_assignable - > initializer ) ;
}
2023-02-02 14:57:22 +00:00
# ifdef DEBUG_ENABLED
if ( initializer_type . is_hard_type ( ) & & initializer_type . is_variant ( ) ) {
parser - > push_warning ( p_assignable , GDScriptWarning : : INFERENCE_ON_VARIANT , p_kind ) ;
}
# endif
2022-12-22 20:43:36 +00:00
} else {
if ( ! initializer_type . is_set ( ) ) {
push_error ( vformat ( R " (Could not resolve type for %s " % s " .) " , p_kind , p_assignable - > identifier - > name ) , p_assignable - > initializer ) ;
}
}
if ( ! has_specified_type ) {
type = initializer_type ;
if ( ! type . is_set ( ) | | ( type . is_hard_type ( ) & & type . kind = = GDScriptParser : : DataType : : BUILTIN & & type . builtin_type = = Variant : : NIL & & ! is_constant ) ) {
type . kind = GDScriptParser : : DataType : : VARIANT ;
}
if ( p_assignable - > infer_datatype | | is_constant ) {
type . type_source = GDScriptParser : : DataType : : ANNOTATED_INFERRED ;
} else {
type . type_source = GDScriptParser : : DataType : : INFERRED ;
}
} else if ( ! specified_type . is_variant ( ) ) {
if ( initializer_type . is_variant ( ) | | ! initializer_type . is_hard_type ( ) ) {
mark_node_unsafe ( p_assignable - > initializer ) ;
2023-01-06 09:49:06 +00:00
p_assignable - > use_conversion_assign = true ;
2022-12-30 07:58:07 +00:00
if ( ! initializer_type . is_variant ( ) & & ! is_type_compatible ( specified_type , initializer_type , true , p_assignable - > initializer ) ) {
downgrade_node_type_source ( p_assignable - > initializer ) ;
}
2022-12-22 20:43:36 +00:00
} else if ( ! is_type_compatible ( specified_type , initializer_type , true , p_assignable - > initializer ) ) {
2023-01-22 08:32:05 +00:00
if ( ! is_constant & & is_type_compatible ( initializer_type , specified_type ) ) {
2022-12-22 20:43:36 +00:00
mark_node_unsafe ( p_assignable - > initializer ) ;
2023-01-06 09:49:06 +00:00
p_assignable - > use_conversion_assign = true ;
2022-12-22 20:43:36 +00:00
} else {
push_error ( vformat ( R " (Cannot assign a value of type %s to %s " % s " with specified type %s.) " , initializer_type . to_string ( ) , p_kind , p_assignable - > identifier - > name , specified_type . to_string ( ) ) , p_assignable - > initializer ) ;
}
2023-06-24 18:03:28 +00:00
} else if ( ( specified_type . has_container_element_type ( 0 ) & & ! initializer_type . has_container_element_type ( 0 ) ) | | ( specified_type . has_container_element_type ( 1 ) & & ! initializer_type . has_container_element_type ( 1 ) ) ) {
2022-11-27 07:56:53 +00:00
mark_node_unsafe ( p_assignable - > initializer ) ;
2022-12-22 20:43:36 +00:00
# ifdef DEBUG_ENABLED
} else if ( specified_type . builtin_type = = Variant : : INT & & initializer_type . builtin_type = = Variant : : FLOAT ) {
parser - > push_warning ( p_assignable - > initializer , GDScriptWarning : : NARROWING_CONVERSION ) ;
# endif
}
}
}
2023-09-05 17:56:37 +00:00
# ifdef DEBUG_ENABLED
2024-07-22 18:45:01 +00:00
const bool is_parameter = p_assignable - > type = = GDScriptParser : : Node : : PARAMETER ;
2023-09-22 17:57:24 +00:00
if ( ! has_specified_type ) {
const String declaration_type = is_constant ? " Constant " : ( is_parameter ? " Parameter " : " Variable " ) ;
if ( p_assignable - > infer_datatype | | is_constant ) {
2024-05-30 16:16:09 +00:00
// Do not produce the `INFERRED_DECLARATION` warning on type import because there is no way to specify the true type.
// And removing the metatype makes it impossible to use the constant as a type hint (especially for enums).
const bool is_type_import = is_constant & & p_assignable - > initializer ! = nullptr & & p_assignable - > initializer - > datatype . is_meta_type ;
if ( ! is_type_import ) {
parser - > push_warning ( p_assignable , GDScriptWarning : : INFERRED_DECLARATION , declaration_type , p_assignable - > identifier - > name ) ;
}
2023-09-22 17:57:24 +00:00
} else {
parser - > push_warning ( p_assignable , GDScriptWarning : : UNTYPED_DECLARATION , declaration_type , p_assignable - > identifier - > name ) ;
}
2024-07-22 18:45:01 +00:00
} else if ( ! is_parameter & & specified_type . kind = = GDScriptParser : : DataType : : ENUM & & p_assignable - > initializer = = nullptr ) {
2024-04-16 14:16:36 +00:00
// Warn about enum variables without default value. Unless the enum defines the "0" value, then it's fine.
bool has_zero_value = false ;
for ( const KeyValue < StringName , int64_t > & kv : specified_type . enum_values ) {
if ( kv . value = = 0 ) {
has_zero_value = true ;
break ;
}
}
if ( ! has_zero_value ) {
parser - > push_warning ( p_assignable , GDScriptWarning : : ENUM_VARIABLE_WITHOUT_DEFAULT , p_assignable - > identifier - > name ) ;
}
2023-09-05 17:56:37 +00:00
}
# endif
2022-12-22 20:43:36 +00:00
type . is_constant = is_constant ;
2023-02-13 18:25:41 +00:00
type . is_read_only = false ;
2022-12-22 20:43:36 +00:00
p_assignable - > set_datatype ( type ) ;
}
void GDScriptAnalyzer : : resolve_variable ( GDScriptParser : : VariableNode * p_variable , bool p_is_local ) {
static constexpr const char * kind = " variable " ;
resolve_assignable ( p_variable , kind ) ;
# ifdef DEBUG_ENABLED
if ( p_is_local ) {
if ( p_variable - > usages = = 0 & & ! String ( p_variable - > identifier - > name ) . begins_with ( " _ " ) ) {
parser - > push_warning ( p_variable , GDScriptWarning : : UNUSED_VARIABLE , p_variable - > identifier - > name ) ;
}
}
2023-04-03 13:57:41 +00:00
is_shadowing ( p_variable - > identifier , kind , p_is_local ) ;
2022-12-22 20:43:36 +00:00
# endif
}
void GDScriptAnalyzer : : resolve_constant ( GDScriptParser : : ConstantNode * p_constant , bool p_is_local ) {
static constexpr const char * kind = " constant " ;
resolve_assignable ( p_constant , kind ) ;
# ifdef DEBUG_ENABLED
if ( p_is_local ) {
2024-04-17 12:40:58 +00:00
if ( p_constant - > usages = = 0 & & ! String ( p_constant - > identifier - > name ) . begins_with ( " _ " ) ) {
2022-12-22 20:43:36 +00:00
parser - > push_warning ( p_constant , GDScriptWarning : : UNUSED_LOCAL_CONSTANT , p_constant - > identifier - > name ) ;
}
}
2023-04-03 13:57:41 +00:00
is_shadowing ( p_constant - > identifier , kind , p_is_local ) ;
2022-12-22 20:43:36 +00:00
# endif
}
void GDScriptAnalyzer : : resolve_parameter ( GDScriptParser : : ParameterNode * p_parameter ) {
static constexpr const char * kind = " parameter " ;
resolve_assignable ( p_parameter , kind ) ;
}
2020-06-10 22:53:25 +00:00
void GDScriptAnalyzer : : resolve_if ( GDScriptParser : : IfNode * p_if ) {
reduce_expression ( p_if - > condition ) ;
resolve_suite ( p_if - > true_block ) ;
p_if - > set_datatype ( p_if - > true_block - > get_datatype ( ) ) ;
if ( p_if - > false_block ! = nullptr ) {
resolve_suite ( p_if - > false_block ) ;
decide_suite_type ( p_if , p_if - > false_block ) ;
}
}
void GDScriptAnalyzer : : resolve_for ( GDScriptParser : : ForNode * p_for ) {
bool list_resolved = false ;
// Optimize constant range() call to not allocate an array.
2022-02-17 23:48:33 +00:00
// Use int, Vector2i, Vector3i instead, which also can be used as range iterators.
2020-07-16 01:02:44 +00:00
if ( p_for - > list & & p_for - > list - > type = = GDScriptParser : : Node : : CALL ) {
2020-06-10 22:53:25 +00:00
GDScriptParser : : CallNode * call = static_cast < GDScriptParser : : CallNode * > ( p_for - > list ) ;
2020-08-05 19:41:46 +00:00
GDScriptParser : : Node : : Type callee_type = call - > get_callee_type ( ) ;
if ( callee_type = = GDScriptParser : : Node : : IDENTIFIER ) {
2020-06-10 22:53:25 +00:00
GDScriptParser : : IdentifierNode * callee = static_cast < GDScriptParser : : IdentifierNode * > ( call - > callee ) ;
if ( callee - > name = = " range " ) {
list_resolved = true ;
if ( call - > arguments . size ( ) < 1 ) {
push_error ( R " *(Invalid call for " range ( ) " function. Expected at least 1 argument, none given.)* " , call - > callee ) ;
} else if ( call - > arguments . size ( ) > 3 ) {
push_error ( vformat ( R " *(Invalid call for " range ( ) " function. Expected at most 3 arguments, %d given.)* " , call - > arguments . size ( ) ) , call - > callee ) ;
} else {
// Now we can optimize it.
2023-02-23 22:01:27 +00:00
bool can_reduce = true ;
2020-06-10 22:53:25 +00:00
Vector < Variant > args ;
args . resize ( call - > arguments . size ( ) ) ;
for ( int i = 0 ; i < call - > arguments . size ( ) ; i + + ) {
2023-02-23 01:25:26 +00:00
GDScriptParser : : ExpressionNode * argument = call - > arguments [ i ] ;
reduce_expression ( argument ) ;
2020-06-10 22:53:25 +00:00
2023-02-23 22:01:27 +00:00
if ( argument - > is_constant ) {
if ( argument - > reduced_value . get_type ( ) ! = Variant : : INT & & argument - > reduced_value . get_type ( ) ! = Variant : : FLOAT ) {
can_reduce = false ;
push_error ( vformat ( R " *(Invalid argument for " range ( ) " call. Argument %d should be int or float but " % s " was given.)* " , i + 1 , Variant : : get_type_name ( argument - > reduced_value . get_type ( ) ) ) , argument ) ;
}
if ( can_reduce ) {
args . write [ i ] = argument - > reduced_value ;
}
} else {
can_reduce = false ;
GDScriptParser : : DataType argument_type = argument - > get_datatype ( ) ;
if ( argument_type . is_variant ( ) | | ! argument_type . is_hard_type ( ) ) {
mark_node_unsafe ( argument ) ;
}
if ( ! argument_type . is_variant ( ) & & ( argument_type . builtin_type ! = Variant : : INT & & argument_type . builtin_type ! = Variant : : FLOAT ) ) {
if ( ! argument_type . is_hard_type ( ) ) {
downgrade_node_type_source ( argument ) ;
} else {
push_error ( vformat ( R " *(Invalid argument for " range ( ) " call. Argument %d should be int or float but " % s " was given.)* " , i + 1 , argument_type . to_string ( ) ) , argument ) ;
}
}
2020-06-10 22:53:25 +00:00
}
}
Variant reduced ;
2023-02-23 22:01:27 +00:00
if ( can_reduce ) {
2020-06-10 22:53:25 +00:00
switch ( args . size ( ) ) {
case 1 :
2022-11-01 16:07:40 +00:00
reduced = ( int32_t ) args [ 0 ] ;
2020-06-10 22:53:25 +00:00
break ;
case 2 :
reduced = Vector2i ( args [ 0 ] , args [ 1 ] ) ;
break ;
case 3 :
reduced = Vector3i ( args [ 0 ] , args [ 1 ] , args [ 2 ] ) ;
break ;
}
p_for - > list - > is_constant = true ;
p_for - > list - > reduced_value = reduced ;
}
}
2020-11-25 14:35:07 +00:00
if ( p_for - > list - > is_constant ) {
p_for - > list - > set_datatype ( type_from_variant ( p_for - > list - > reduced_value , p_for - > list ) ) ;
} else {
GDScriptParser : : DataType list_type ;
list_type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
list_type . kind = GDScriptParser : : DataType : : BUILTIN ;
list_type . builtin_type = Variant : : ARRAY ;
p_for - > list - > set_datatype ( list_type ) ;
}
2020-06-10 22:53:25 +00:00
}
}
}
2021-08-14 03:44:22 +00:00
GDScriptParser : : DataType variable_type ;
2023-08-04 09:19:11 +00:00
String list_visible_type = " <unresolved type> " ;
2021-08-14 03:44:22 +00:00
if ( list_resolved ) {
variable_type . type_source = GDScriptParser : : DataType : : ANNOTATED_INFERRED ;
variable_type . kind = GDScriptParser : : DataType : : BUILTIN ;
2023-01-12 18:18:23 +00:00
variable_type . builtin_type = Variant : : INT ;
2023-08-04 09:19:11 +00:00
list_visible_type = " Array[int] " ; // NOTE: `range()` has `Array` return type.
2021-10-02 17:53:56 +00:00
} else if ( p_for - > list ) {
2022-10-13 17:31:12 +00:00
resolve_node ( p_for - > list , false ) ;
2023-01-12 18:18:23 +00:00
GDScriptParser : : DataType list_type = p_for - > list - > get_datatype ( ) ;
2023-08-04 09:19:11 +00:00
list_visible_type = list_type . to_string ( ) ;
2023-01-12 18:18:23 +00:00
if ( ! list_type . is_hard_type ( ) ) {
mark_node_unsafe ( p_for - > list ) ;
}
if ( list_type . is_variant ( ) ) {
variable_type . kind = GDScriptParser : : DataType : : VARIANT ;
mark_node_unsafe ( p_for - > list ) ;
2023-09-14 18:31:07 +00:00
} else if ( list_type . has_container_element_type ( 0 ) ) {
variable_type = list_type . get_container_element_type ( 0 ) ;
2023-01-12 18:18:23 +00:00
variable_type . type_source = list_type . type_source ;
} else if ( list_type . is_typed_container_type ( ) ) {
variable_type = list_type . get_typed_container_type ( ) ;
variable_type . type_source = list_type . type_source ;
} else if ( list_type . builtin_type = = Variant : : INT | | list_type . builtin_type = = Variant : : FLOAT | | list_type . builtin_type = = Variant : : STRING ) {
variable_type . type_source = list_type . type_source ;
variable_type . kind = GDScriptParser : : DataType : : BUILTIN ;
variable_type . builtin_type = list_type . builtin_type ;
} else if ( list_type . builtin_type = = Variant : : VECTOR2I | | list_type . builtin_type = = Variant : : VECTOR3I ) {
variable_type . type_source = list_type . type_source ;
variable_type . kind = GDScriptParser : : DataType : : BUILTIN ;
variable_type . builtin_type = Variant : : INT ;
} else if ( list_type . builtin_type = = Variant : : VECTOR2 | | list_type . builtin_type = = Variant : : VECTOR3 ) {
variable_type . type_source = list_type . type_source ;
variable_type . kind = GDScriptParser : : DataType : : BUILTIN ;
variable_type . builtin_type = Variant : : FLOAT ;
} else if ( list_type . builtin_type = = Variant : : OBJECT ) {
GDScriptParser : : DataType return_type ;
List < GDScriptParser : : DataType > par_types ;
int default_arg_count = 0 ;
2023-05-21 23:22:00 +00:00
BitField < MethodFlags > method_flags ;
2023-09-04 15:01:33 +00:00
if ( get_function_signature ( p_for - > list , false , list_type , CoreStringName ( _iter_get ) , return_type , par_types , default_arg_count , method_flags ) ) {
2023-01-12 18:18:23 +00:00
variable_type = return_type ;
variable_type . type_source = list_type . type_source ;
} else if ( ! list_type . is_hard_type ( ) ) {
variable_type . kind = GDScriptParser : : DataType : : VARIANT ;
} else {
push_error ( vformat ( R " (Unable to iterate on object of type " % s " .) " , list_type . to_string ( ) ) , p_for - > list ) ;
}
} else if ( list_type . builtin_type = = Variant : : ARRAY | | list_type . builtin_type = = Variant : : DICTIONARY | | ! list_type . is_hard_type ( ) ) {
2021-08-14 03:44:22 +00:00
variable_type . kind = GDScriptParser : : DataType : : VARIANT ;
2023-01-12 18:18:23 +00:00
} else {
push_error ( vformat ( R " (Unable to iterate on value of type " % s " .) " , list_type . to_string ( ) ) , p_for - > list ) ;
2021-08-14 03:44:22 +00:00
}
2020-06-10 22:53:25 +00:00
}
2023-08-04 09:19:11 +00:00
2021-10-02 17:53:56 +00:00
if ( p_for - > variable ) {
2023-08-04 09:19:11 +00:00
if ( p_for - > datatype_specifier ) {
GDScriptParser : : DataType specified_type = type_from_metatype ( resolve_datatype ( p_for - > datatype_specifier ) ) ;
if ( ! specified_type . is_variant ( ) ) {
if ( variable_type . is_variant ( ) | | ! variable_type . is_hard_type ( ) ) {
mark_node_unsafe ( p_for - > variable ) ;
p_for - > use_conversion_assign = true ;
} else if ( ! is_type_compatible ( specified_type , variable_type , true , p_for - > variable ) ) {
if ( is_type_compatible ( variable_type , specified_type ) ) {
mark_node_unsafe ( p_for - > variable ) ;
p_for - > use_conversion_assign = true ;
} else {
push_error ( vformat ( R " (Unable to iterate on value of type " % s " with variable of type " % s " .) " , list_visible_type , specified_type . to_string ( ) ) , p_for - > datatype_specifier ) ;
}
} else if ( ! is_type_compatible ( specified_type , variable_type ) ) {
p_for - > use_conversion_assign = true ;
}
2023-06-24 18:03:28 +00:00
if ( p_for - > list ) {
if ( p_for - > list - > type = = GDScriptParser : : Node : : ARRAY ) {
update_array_literal_element_type ( static_cast < GDScriptParser : : ArrayNode * > ( p_for - > list ) , specified_type ) ;
} else if ( p_for - > list - > type = = GDScriptParser : : Node : : DICTIONARY ) {
update_dictionary_literal_element_type ( static_cast < GDScriptParser : : DictionaryNode * > ( p_for - > list ) , specified_type , GDScriptParser : : DataType : : get_variant_type ( ) ) ;
}
2023-09-21 08:14:28 +00:00
}
2023-08-04 09:19:11 +00:00
}
p_for - > variable - > set_datatype ( specified_type ) ;
} else {
p_for - > variable - > set_datatype ( variable_type ) ;
2023-09-05 17:56:37 +00:00
# ifdef DEBUG_ENABLED
2023-09-22 17:57:24 +00:00
if ( variable_type . is_hard_type ( ) ) {
parser - > push_warning ( p_for - > variable , GDScriptWarning : : INFERRED_DECLARATION , R " ( " for " iterator variable) " , p_for - > variable - > name ) ;
} else {
2023-09-05 17:56:37 +00:00
parser - > push_warning ( p_for - > variable , GDScriptWarning : : UNTYPED_DECLARATION , R " ( " for " iterator variable) " , p_for - > variable - > name ) ;
}
# endif
2023-08-04 09:19:11 +00:00
}
2021-10-02 17:53:56 +00:00
}
2020-06-10 22:53:25 +00:00
resolve_suite ( p_for - > loop ) ;
p_for - > set_datatype ( p_for - > loop - > get_datatype ( ) ) ;
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
if ( p_for - > variable ) {
2023-04-03 13:57:41 +00:00
is_shadowing ( p_for - > variable , R " ( " for " iterator variable) " , true ) ;
2020-07-16 01:02:44 +00:00
}
# endif
2020-06-10 22:53:25 +00:00
}
void GDScriptAnalyzer : : resolve_while ( GDScriptParser : : WhileNode * p_while ) {
2022-10-13 17:31:12 +00:00
resolve_node ( p_while - > condition , false ) ;
2020-06-10 22:53:25 +00:00
resolve_suite ( p_while - > loop ) ;
p_while - > set_datatype ( p_while - > loop - > get_datatype ( ) ) ;
}
void GDScriptAnalyzer : : resolve_assert ( GDScriptParser : : AssertNode * p_assert ) {
reduce_expression ( p_assert - > condition ) ;
2020-06-11 22:31:28 +00:00
if ( p_assert - > message ! = nullptr ) {
2020-12-27 05:57:50 +00:00
reduce_expression ( p_assert - > message ) ;
2022-07-04 03:53:31 +00:00
if ( ! p_assert - > message - > get_datatype ( ) . has_no_type ( ) & & ( p_assert - > message - > get_datatype ( ) . kind ! = GDScriptParser : : DataType : : BUILTIN | | p_assert - > message - > get_datatype ( ) . builtin_type ! = Variant : : STRING ) ) {
push_error ( R " (Expected string for assert error message.) " , p_assert - > message ) ;
2020-12-27 05:57:50 +00:00
}
2020-06-11 22:31:28 +00:00
}
2020-06-10 22:53:25 +00:00
p_assert - > set_datatype ( p_assert - > condition - > get_datatype ( ) ) ;
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2020-06-11 22:31:28 +00:00
if ( p_assert - > condition - > is_constant ) {
if ( p_assert - > condition - > reduced_value . booleanize ( ) ) {
parser - > push_warning ( p_assert - > condition , GDScriptWarning : : ASSERT_ALWAYS_TRUE ) ;
2023-03-17 03:36:50 +00:00
} else if ( ! ( p_assert - > condition - > type = = GDScriptParser : : Node : : LITERAL & & static_cast < GDScriptParser : : LiteralNode * > ( p_assert - > condition ) - > value . get_type ( ) = = Variant : : BOOL ) ) {
2020-06-11 22:31:28 +00:00
parser - > push_warning ( p_assert - > condition , GDScriptWarning : : ASSERT_ALWAYS_FALSE ) ;
}
}
2020-07-16 01:02:44 +00:00
# endif
2020-06-10 22:53:25 +00:00
}
void GDScriptAnalyzer : : resolve_match ( GDScriptParser : : MatchNode * p_match ) {
reduce_expression ( p_match - > test ) ;
for ( int i = 0 ; i < p_match - > branches . size ( ) ; i + + ) {
resolve_match_branch ( p_match - > branches [ i ] , p_match - > test ) ;
decide_suite_type ( p_match , p_match - > branches [ i ] ) ;
}
}
void GDScriptAnalyzer : : resolve_match_branch ( GDScriptParser : : MatchBranchNode * p_match_branch , GDScriptParser : : ExpressionNode * p_match_test ) {
2024-02-28 14:23:11 +00:00
// Apply annotations.
for ( GDScriptParser : : AnnotationNode * & E : p_match_branch - > annotations ) {
resolve_annotation ( E ) ;
E - > apply ( parser , p_match_branch , nullptr ) ; // TODO: Provide `p_class`.
}
2020-06-10 22:53:25 +00:00
for ( int i = 0 ; i < p_match_branch - > patterns . size ( ) ; i + + ) {
resolve_match_pattern ( p_match_branch - > patterns [ i ] , p_match_test ) ;
}
2023-07-31 10:47:26 +00:00
if ( p_match_branch - > guard_body ) {
resolve_suite ( p_match_branch - > guard_body ) ;
}
2020-06-10 22:53:25 +00:00
resolve_suite ( p_match_branch - > block ) ;
decide_suite_type ( p_match_branch , p_match_branch - > block ) ;
}
void GDScriptAnalyzer : : resolve_match_pattern ( GDScriptParser : : PatternNode * p_match_pattern , GDScriptParser : : ExpressionNode * p_match_test ) {
2020-07-16 01:02:44 +00:00
if ( p_match_pattern = = nullptr ) {
return ;
}
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType result ;
switch ( p_match_pattern - > pattern_type ) {
case GDScriptParser : : PatternNode : : PT_LITERAL :
2020-07-16 01:02:44 +00:00
if ( p_match_pattern - > literal ) {
reduce_literal ( p_match_pattern - > literal ) ;
result = p_match_pattern - > literal - > get_datatype ( ) ;
}
2020-06-10 22:53:25 +00:00
break ;
case GDScriptParser : : PatternNode : : PT_EXPRESSION :
2020-07-16 01:02:44 +00:00
if ( p_match_pattern - > expression ) {
2023-01-28 22:49:14 +00:00
GDScriptParser : : ExpressionNode * expr = p_match_pattern - > expression ;
reduce_expression ( expr ) ;
result = expr - > get_datatype ( ) ;
if ( ! expr - > is_constant ) {
while ( expr & & expr - > type = = GDScriptParser : : Node : : SUBSCRIPT ) {
GDScriptParser : : SubscriptNode * sub = static_cast < GDScriptParser : : SubscriptNode * > ( expr ) ;
if ( ! sub - > is_attribute ) {
expr = nullptr ;
} else {
expr = sub - > base ;
}
}
if ( ! expr | | expr - > type ! = GDScriptParser : : Node : : IDENTIFIER ) {
push_error ( R " (Expression in match pattern must be a constant expression, an identifier, or an attribute access ( " A . B " ).) " , expr ) ;
}
2020-07-16 01:02:44 +00:00
}
2020-06-10 22:53:25 +00:00
}
break ;
case GDScriptParser : : PatternNode : : PT_BIND :
if ( p_match_test ! = nullptr ) {
result = p_match_test - > get_datatype ( ) ;
} else {
result . kind = GDScriptParser : : DataType : : VARIANT ;
}
p_match_pattern - > bind - > set_datatype ( result ) ;
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2023-04-03 13:57:41 +00:00
is_shadowing ( p_match_pattern - > bind , " pattern bind " , true ) ;
2022-06-03 17:42:03 +00:00
if ( p_match_pattern - > bind - > usages = = 0 & & ! String ( p_match_pattern - > bind - > name ) . begins_with ( " _ " ) ) {
parser - > push_warning ( p_match_pattern - > bind , GDScriptWarning : : UNUSED_VARIABLE , p_match_pattern - > bind - > name ) ;
2020-06-11 22:31:28 +00:00
}
2020-07-16 01:02:44 +00:00
# endif
2020-06-10 22:53:25 +00:00
break ;
case GDScriptParser : : PatternNode : : PT_ARRAY :
for ( int i = 0 ; i < p_match_pattern - > array . size ( ) ; i + + ) {
resolve_match_pattern ( p_match_pattern - > array [ i ] , nullptr ) ;
decide_suite_type ( p_match_pattern , p_match_pattern - > array [ i ] ) ;
}
result = p_match_pattern - > get_datatype ( ) ;
break ;
case GDScriptParser : : PatternNode : : PT_DICTIONARY :
for ( int i = 0 ; i < p_match_pattern - > dictionary . size ( ) ; i + + ) {
2020-07-16 01:02:44 +00:00
if ( p_match_pattern - > dictionary [ i ] . key ) {
reduce_expression ( p_match_pattern - > dictionary [ i ] . key ) ;
if ( ! p_match_pattern - > dictionary [ i ] . key - > is_constant ) {
2022-01-25 20:10:07 +00:00
push_error ( R " (Expression in dictionary pattern key must be a constant.) " , p_match_pattern - > dictionary [ i ] . key ) ;
2020-07-16 01:02:44 +00:00
}
2020-06-10 22:53:25 +00:00
}
2020-07-16 01:02:44 +00:00
if ( p_match_pattern - > dictionary [ i ] . value_pattern ) {
resolve_match_pattern ( p_match_pattern - > dictionary [ i ] . value_pattern , nullptr ) ;
decide_suite_type ( p_match_pattern , p_match_pattern - > dictionary [ i ] . value_pattern ) ;
}
2020-06-10 22:53:25 +00:00
}
result = p_match_pattern - > get_datatype ( ) ;
break ;
case GDScriptParser : : PatternNode : : PT_WILDCARD :
case GDScriptParser : : PatternNode : : PT_REST :
result . kind = GDScriptParser : : DataType : : VARIANT ;
break ;
}
p_match_pattern - > set_datatype ( result ) ;
}
void GDScriptAnalyzer : : resolve_return ( GDScriptParser : : ReturnNode * p_return ) {
GDScriptParser : : DataType result ;
2021-09-06 05:04:43 +00:00
GDScriptParser : : DataType expected_type ;
2023-01-24 21:21:54 +00:00
bool has_expected_type = parser - > current_function ! = nullptr ;
if ( has_expected_type ) {
2021-09-06 05:04:43 +00:00
expected_type = parser - > current_function - > get_datatype ( ) ;
}
2020-06-10 22:53:25 +00:00
if ( p_return - > return_value ! = nullptr ) {
2023-01-27 20:54:07 +00:00
bool is_void_function = has_expected_type & & expected_type . is_hard_type ( ) & & expected_type . kind = = GDScriptParser : : DataType : : BUILTIN & & expected_type . builtin_type = = Variant : : NIL ;
bool is_call = p_return - > return_value - > type = = GDScriptParser : : Node : : CALL ;
if ( is_void_function & & is_call ) {
// Pretend the call is a root expression to allow those that are "void".
reduce_call ( static_cast < GDScriptParser : : CallNode * > ( p_return - > return_value ) , false , true ) ;
} else {
reduce_expression ( p_return - > return_value ) ;
2022-12-28 05:12:16 +00:00
}
2023-01-27 20:54:07 +00:00
if ( is_void_function ) {
p_return - > void_return = true ;
const GDScriptParser : : DataType & return_type = p_return - > return_value - > datatype ;
if ( is_call & & ! return_type . is_hard_type ( ) ) {
String function_name = parser - > current_function - > identifier ? parser - > current_function - > identifier - > name . operator String ( ) : String ( " <anonymous function> " ) ;
String called_function_name = static_cast < GDScriptParser : : CallNode * > ( p_return - > return_value ) - > function_name . operator String ( ) ;
# ifdef DEBUG_ENABLED
parser - > push_warning ( p_return , GDScriptWarning : : UNSAFE_VOID_RETURN , function_name , called_function_name ) ;
# endif
mark_node_unsafe ( p_return ) ;
} else if ( ! is_call ) {
push_error ( " A void function cannot return a value. " , p_return ) ;
}
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
result . kind = GDScriptParser : : DataType : : BUILTIN ;
result . builtin_type = Variant : : NIL ;
result . is_constant = true ;
} else {
2023-09-14 18:31:07 +00:00
if ( p_return - > return_value - > type = = GDScriptParser : : Node : : ARRAY & & has_expected_type & & expected_type . has_container_element_type ( 0 ) ) {
update_array_literal_element_type ( static_cast < GDScriptParser : : ArrayNode * > ( p_return - > return_value ) , expected_type . get_container_element_type ( 0 ) ) ;
2023-06-24 18:03:28 +00:00
} else if ( p_return - > return_value - > type = = GDScriptParser : : Node : : DICTIONARY & & has_expected_type & & expected_type . has_container_element_types ( ) ) {
update_dictionary_literal_element_type ( static_cast < GDScriptParser : : DictionaryNode * > ( p_return - > return_value ) ,
expected_type . get_container_element_type_or_variant ( 0 ) , expected_type . get_container_element_type_or_variant ( 1 ) ) ;
2023-01-27 20:54:07 +00:00
}
if ( has_expected_type & & expected_type . is_hard_type ( ) & & p_return - > return_value - > is_constant ) {
update_const_expression_builtin_type ( p_return - > return_value , expected_type , " return " ) ;
}
result = p_return - > return_value - > get_datatype ( ) ;
2023-01-22 08:32:05 +00:00
}
2020-06-10 22:53:25 +00:00
} else {
// Return type is null by default.
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
result . kind = GDScriptParser : : DataType : : BUILTIN ;
result . builtin_type = Variant : : NIL ;
result . is_constant = true ;
}
2023-01-24 21:21:54 +00:00
if ( has_expected_type & & ! expected_type . is_variant ( ) ) {
if ( result . is_variant ( ) | | ! result . is_hard_type ( ) ) {
mark_node_unsafe ( p_return ) ;
if ( ! is_type_compatible ( expected_type , result , true , p_return ) ) {
downgrade_node_type_source ( p_return ) ;
}
} else if ( ! is_type_compatible ( expected_type , result , true , p_return ) ) {
mark_node_unsafe ( p_return ) ;
if ( ! is_type_compatible ( result , expected_type ) ) {
push_error ( vformat ( R " (Cannot return value of type " % s " because the function return type is " % s " .) " , result . to_string ( ) , expected_type . to_string ( ) ) , p_return ) ;
}
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2023-01-24 21:21:54 +00:00
} else if ( expected_type . builtin_type = = Variant : : INT & & result . builtin_type = = Variant : : FLOAT ) {
parser - > push_warning ( p_return , GDScriptWarning : : NARROWING_CONVERSION ) ;
2020-07-16 01:02:44 +00:00
# endif
2020-06-11 22:31:28 +00:00
}
}
2020-06-10 22:53:25 +00:00
p_return - > set_datatype ( result ) ;
}
2021-10-14 23:12:01 +00:00
void GDScriptAnalyzer : : reduce_expression ( GDScriptParser : : ExpressionNode * p_expression , bool p_is_root ) {
2020-06-10 22:53:25 +00:00
// This one makes some magic happen.
2020-07-06 15:24:24 +00:00
if ( p_expression = = nullptr ) {
return ;
}
2020-06-10 22:53:25 +00:00
if ( p_expression - > reduced ) {
// Don't do this more than once.
return ;
}
p_expression - > reduced = true ;
switch ( p_expression - > type ) {
case GDScriptParser : : Node : : ARRAY :
reduce_array ( static_cast < GDScriptParser : : ArrayNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : ASSIGNMENT :
reduce_assignment ( static_cast < GDScriptParser : : AssignmentNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : AWAIT :
reduce_await ( static_cast < GDScriptParser : : AwaitNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : BINARY_OPERATOR :
reduce_binary_op ( static_cast < GDScriptParser : : BinaryOpNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : CALL :
2022-10-13 17:31:12 +00:00
reduce_call ( static_cast < GDScriptParser : : CallNode * > ( p_expression ) , false , p_is_root ) ;
2020-06-10 22:53:25 +00:00
break ;
case GDScriptParser : : Node : : CAST :
reduce_cast ( static_cast < GDScriptParser : : CastNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : DICTIONARY :
reduce_dictionary ( static_cast < GDScriptParser : : DictionaryNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : GET_NODE :
reduce_get_node ( static_cast < GDScriptParser : : GetNodeNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : IDENTIFIER :
reduce_identifier ( static_cast < GDScriptParser : : IdentifierNode * > ( p_expression ) ) ;
break ;
2021-03-26 12:03:16 +00:00
case GDScriptParser : : Node : : LAMBDA :
reduce_lambda ( static_cast < GDScriptParser : : LambdaNode * > ( p_expression ) ) ;
break ;
2020-06-10 22:53:25 +00:00
case GDScriptParser : : Node : : LITERAL :
reduce_literal ( static_cast < GDScriptParser : : LiteralNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : PRELOAD :
reduce_preload ( static_cast < GDScriptParser : : PreloadNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : SELF :
reduce_self ( static_cast < GDScriptParser : : SelfNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : SUBSCRIPT :
reduce_subscript ( static_cast < GDScriptParser : : SubscriptNode * > ( p_expression ) ) ;
break ;
case GDScriptParser : : Node : : TERNARY_OPERATOR :
2023-01-09 14:02:37 +00:00
reduce_ternary_op ( static_cast < GDScriptParser : : TernaryOpNode * > ( p_expression ) , p_is_root ) ;
2020-06-10 22:53:25 +00:00
break ;
2023-02-16 23:16:24 +00:00
case GDScriptParser : : Node : : TYPE_TEST :
reduce_type_test ( static_cast < GDScriptParser : : TypeTestNode * > ( p_expression ) ) ;
break ;
2020-06-10 22:53:25 +00:00
case GDScriptParser : : Node : : UNARY_OPERATOR :
reduce_unary_op ( static_cast < GDScriptParser : : UnaryOpNode * > ( p_expression ) ) ;
break ;
// Non-expressions. Here only to make sure new nodes aren't forgotten.
case GDScriptParser : : Node : : NONE :
case GDScriptParser : : Node : : ANNOTATION :
case GDScriptParser : : Node : : ASSERT :
case GDScriptParser : : Node : : BREAK :
case GDScriptParser : : Node : : BREAKPOINT :
case GDScriptParser : : Node : : CLASS :
case GDScriptParser : : Node : : CONSTANT :
case GDScriptParser : : Node : : CONTINUE :
case GDScriptParser : : Node : : ENUM :
case GDScriptParser : : Node : : FOR :
case GDScriptParser : : Node : : FUNCTION :
case GDScriptParser : : Node : : IF :
case GDScriptParser : : Node : : MATCH :
case GDScriptParser : : Node : : MATCH_BRANCH :
case GDScriptParser : : Node : : PARAMETER :
case GDScriptParser : : Node : : PASS :
case GDScriptParser : : Node : : PATTERN :
case GDScriptParser : : Node : : RETURN :
case GDScriptParser : : Node : : SIGNAL :
case GDScriptParser : : Node : : SUITE :
case GDScriptParser : : Node : : TYPE :
case GDScriptParser : : Node : : VARIABLE :
case GDScriptParser : : Node : : WHILE :
ERR_FAIL_MSG ( " Reaching unreachable case " ) ;
}
2023-10-13 09:52:14 +00:00
if ( p_expression - > get_datatype ( ) . kind = = GDScriptParser : : DataType : : UNRESOLVED ) {
// Prevent `is_type_compatible()` errors for incomplete expressions.
// The error can still occur if `reduce_*()` is called directly.
GDScriptParser : : DataType dummy ;
dummy . kind = GDScriptParser : : DataType : : VARIANT ;
p_expression - > set_datatype ( dummy ) ;
}
2020-06-10 22:53:25 +00:00
}
void GDScriptAnalyzer : : reduce_array ( GDScriptParser : : ArrayNode * p_array ) {
for ( int i = 0 ; i < p_array - > elements . size ( ) ; i + + ) {
GDScriptParser : : ExpressionNode * element = p_array - > elements [ i ] ;
reduce_expression ( element ) ;
}
// It's array in any case.
GDScriptParser : : DataType arr_type ;
arr_type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
arr_type . kind = GDScriptParser : : DataType : : BUILTIN ;
arr_type . builtin_type = Variant : : ARRAY ;
arr_type . is_constant = true ;
p_array - > set_datatype ( arr_type ) ;
}
2023-01-22 08:32:05 +00:00
# ifdef DEBUG_ENABLED
static bool enum_has_value ( const GDScriptParser : : DataType p_type , int64_t p_value ) {
for ( const KeyValue < StringName , int64_t > & E : p_type . enum_values ) {
if ( E . value = = p_value ) {
return true ;
}
}
return false ;
}
# endif
void GDScriptAnalyzer : : update_const_expression_builtin_type ( GDScriptParser : : ExpressionNode * p_expression , const GDScriptParser : : DataType & p_type , const char * p_usage , bool p_is_cast ) {
if ( p_expression - > get_datatype ( ) = = p_type ) {
return ;
}
if ( p_type . kind ! = GDScriptParser : : DataType : : BUILTIN & & p_type . kind ! = GDScriptParser : : DataType : : ENUM ) {
return ;
}
GDScriptParser : : DataType expression_type = p_expression - > get_datatype ( ) ;
bool is_enum_cast = p_is_cast & & p_type . kind = = GDScriptParser : : DataType : : ENUM & & p_type . is_meta_type = = false & & expression_type . builtin_type = = Variant : : INT ;
if ( ! is_enum_cast & & ! is_type_compatible ( p_type , expression_type , true , p_expression ) ) {
push_error ( vformat ( R " (Cannot %s a value of type " % s " as " % s " .) " , p_usage , expression_type . to_string ( ) , p_type . to_string ( ) ) , p_expression ) ;
return ;
}
GDScriptParser : : DataType value_type = type_from_variant ( p_expression - > reduced_value , p_expression ) ;
if ( expression_type . is_variant ( ) & & ! is_enum_cast & & ! is_type_compatible ( p_type , value_type , true , p_expression ) ) {
push_error ( vformat ( R " (Cannot %s a value of type " % s " as " % s " .) " , p_usage , value_type . to_string ( ) , p_type . to_string ( ) ) , p_expression ) ;
return ;
}
# ifdef DEBUG_ENABLED
if ( p_type . kind = = GDScriptParser : : DataType : : ENUM & & value_type . builtin_type = = Variant : : INT & & ! enum_has_value ( p_type , p_expression - > reduced_value ) ) {
parser - > push_warning ( p_expression , GDScriptWarning : : INT_AS_ENUM_WITHOUT_MATCH , p_usage , p_expression - > reduced_value . stringify ( ) , p_type . to_string ( ) ) ;
}
# endif
if ( value_type . builtin_type = = p_type . builtin_type ) {
p_expression - > set_datatype ( p_type ) ;
return ;
}
Variant converted_to ;
const Variant * converted_from = & p_expression - > reduced_value ;
Callable : : CallError call_error ;
Variant : : construct ( p_type . builtin_type , converted_to , & converted_from , 1 , call_error ) ;
if ( call_error . error ) {
push_error ( vformat ( R " (Failed to convert a value of type " % s " to " % s " .) " , value_type . to_string ( ) , p_type . to_string ( ) ) , p_expression ) ;
return ;
}
# ifdef DEBUG_ENABLED
if ( p_type . builtin_type = = Variant : : INT & & value_type . builtin_type = = Variant : : FLOAT ) {
parser - > push_warning ( p_expression , GDScriptWarning : : NARROWING_CONVERSION ) ;
}
# endif
p_expression - > reduced_value = converted_to ;
p_expression - > set_datatype ( p_type ) ;
}
2021-03-09 15:32:35 +00:00
// When an array literal is stored (or passed as function argument) to a typed context, we then assume the array is typed.
// This function determines which type is that (if any).
2022-11-27 07:56:53 +00:00
void GDScriptAnalyzer : : update_array_literal_element_type ( GDScriptParser : : ArrayNode * p_array , const GDScriptParser : : DataType & p_element_type ) {
2023-09-21 08:14:28 +00:00
GDScriptParser : : DataType expected_type = p_element_type ;
2023-09-14 18:31:07 +00:00
expected_type . container_element_types . clear ( ) ; // Nested types (like `Array[Array[int]]`) are not currently supported.
2023-09-21 08:14:28 +00:00
2022-11-27 07:56:53 +00:00
for ( int i = 0 ; i < p_array - > elements . size ( ) ; i + + ) {
GDScriptParser : : ExpressionNode * element_node = p_array - > elements [ i ] ;
if ( element_node - > is_constant ) {
2023-09-21 08:14:28 +00:00
update_const_expression_builtin_type ( element_node , expected_type , " include " ) ;
2022-11-27 07:56:53 +00:00
}
2023-09-21 08:14:28 +00:00
const GDScriptParser : : DataType & actual_type = element_node - > get_datatype ( ) ;
if ( actual_type . has_no_type ( ) | | actual_type . is_variant ( ) | | ! actual_type . is_hard_type ( ) ) {
2022-11-27 07:56:53 +00:00
mark_node_unsafe ( element_node ) ;
continue ;
2021-03-09 15:32:35 +00:00
}
2023-09-21 08:14:28 +00:00
if ( ! is_type_compatible ( expected_type , actual_type , true , p_array ) ) {
if ( is_type_compatible ( actual_type , expected_type ) ) {
2023-03-28 10:47:24 +00:00
mark_node_unsafe ( element_node ) ;
continue ;
}
2023-09-21 08:14:28 +00:00
push_error ( vformat ( R " (Cannot have an element of type " % s " in an array of type " Array [ % s ] " .) " , actual_type . to_string ( ) , expected_type . to_string ( ) ) , element_node ) ;
2022-11-27 07:56:53 +00:00
return ;
2021-03-09 15:32:35 +00:00
}
}
2022-11-27 07:56:53 +00:00
GDScriptParser : : DataType array_type = p_array - > get_datatype ( ) ;
2023-09-14 18:31:07 +00:00
array_type . set_container_element_type ( 0 , expected_type ) ;
2022-11-27 07:56:53 +00:00
p_array - > set_datatype ( array_type ) ;
2021-03-09 15:32:35 +00:00
}
2023-06-24 18:03:28 +00:00
// When a dictionary literal is stored (or passed as function argument) to a typed context, we then assume the dictionary is typed.
// This function determines which type is that (if any).
void GDScriptAnalyzer : : update_dictionary_literal_element_type ( GDScriptParser : : DictionaryNode * p_dictionary , const GDScriptParser : : DataType & p_key_element_type , const GDScriptParser : : DataType & p_value_element_type ) {
GDScriptParser : : DataType expected_key_type = p_key_element_type ;
GDScriptParser : : DataType expected_value_type = p_value_element_type ;
expected_key_type . container_element_types . clear ( ) ; // Nested types (like `Dictionary[String, Array[int]]`) are not currently supported.
expected_value_type . container_element_types . clear ( ) ;
for ( int i = 0 ; i < p_dictionary - > elements . size ( ) ; i + + ) {
GDScriptParser : : ExpressionNode * key_element_node = p_dictionary - > elements [ i ] . key ;
if ( key_element_node - > is_constant ) {
update_const_expression_builtin_type ( key_element_node , expected_key_type , " include " ) ;
}
const GDScriptParser : : DataType & actual_key_type = key_element_node - > get_datatype ( ) ;
if ( actual_key_type . has_no_type ( ) | | actual_key_type . is_variant ( ) | | ! actual_key_type . is_hard_type ( ) ) {
mark_node_unsafe ( key_element_node ) ;
} else if ( ! is_type_compatible ( expected_key_type , actual_key_type , true , p_dictionary ) ) {
if ( is_type_compatible ( actual_key_type , expected_key_type ) ) {
mark_node_unsafe ( key_element_node ) ;
} else {
push_error ( vformat ( R " (Cannot have a key of type " % s " in a dictionary of type " Dictionary [ % s , % s ] " .) " , actual_key_type . to_string ( ) , expected_key_type . to_string ( ) , expected_value_type . to_string ( ) ) , key_element_node ) ;
return ;
}
}
GDScriptParser : : ExpressionNode * value_element_node = p_dictionary - > elements [ i ] . value ;
if ( value_element_node - > is_constant ) {
update_const_expression_builtin_type ( value_element_node , expected_value_type , " include " ) ;
}
const GDScriptParser : : DataType & actual_value_type = value_element_node - > get_datatype ( ) ;
if ( actual_value_type . has_no_type ( ) | | actual_value_type . is_variant ( ) | | ! actual_value_type . is_hard_type ( ) ) {
mark_node_unsafe ( value_element_node ) ;
} else if ( ! is_type_compatible ( expected_value_type , actual_value_type , true , p_dictionary ) ) {
if ( is_type_compatible ( actual_value_type , expected_value_type ) ) {
mark_node_unsafe ( value_element_node ) ;
} else {
push_error ( vformat ( R " (Cannot have a value of type " % s " in a dictionary of type " Dictionary [ % s , % s ] " .) " , actual_value_type . to_string ( ) , expected_key_type . to_string ( ) , expected_value_type . to_string ( ) ) , value_element_node ) ;
return ;
}
}
}
GDScriptParser : : DataType dictionary_type = p_dictionary - > get_datatype ( ) ;
dictionary_type . set_container_element_type ( 0 , expected_key_type ) ;
dictionary_type . set_container_element_type ( 1 , expected_value_type ) ;
p_dictionary - > set_datatype ( dictionary_type ) ;
}
2020-06-10 22:53:25 +00:00
void GDScriptAnalyzer : : reduce_assignment ( GDScriptParser : : AssignmentNode * p_assignment ) {
reduce_expression ( p_assignment - > assigned_value ) ;
2024-04-09 17:15:51 +00:00
# ifdef DEBUG_ENABLED
// Increment assignment count for local variables.
// Before we reduce the assignee because we don't want to warn about not being assigned when performing the assignment.
if ( p_assignment - > assignee - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
GDScriptParser : : IdentifierNode * id = static_cast < GDScriptParser : : IdentifierNode * > ( p_assignment - > assignee ) ;
if ( id - > source = = GDScriptParser : : IdentifierNode : : LOCAL_VARIABLE & & id - > variable_source ) {
id - > variable_source - > assignments + + ;
}
}
# endif
reduce_expression ( p_assignment - > assignee ) ;
2024-06-28 06:35:04 +00:00
# ifdef DEBUG_ENABLED
{
2024-06-28 06:35:04 +00:00
bool is_subscript = false ;
2024-06-28 06:35:04 +00:00
GDScriptParser : : ExpressionNode * base = p_assignment - > assignee ;
while ( base & & base - > type = = GDScriptParser : : Node : : SUBSCRIPT ) {
2024-06-28 06:35:04 +00:00
is_subscript = true ;
2024-06-28 06:35:04 +00:00
base = static_cast < GDScriptParser : : SubscriptNode * > ( base ) - > base ;
}
if ( base & & base - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
GDScriptParser : : IdentifierNode * id = static_cast < GDScriptParser : : IdentifierNode * > ( base ) ;
if ( current_lambda & & current_lambda - > captures_indices . has ( id - > name ) ) {
2024-06-28 06:35:04 +00:00
bool need_warn = false ;
if ( is_subscript ) {
const GDScriptParser : : DataType & id_type = id - > datatype ;
if ( id_type . is_hard_type ( ) ) {
switch ( id_type . kind ) {
case GDScriptParser : : DataType : : BUILTIN :
// TODO: Change `Variant::is_type_shared()` to include packed arrays?
need_warn = ! Variant : : is_type_shared ( id_type . builtin_type ) & & id_type . builtin_type < Variant : : PACKED_BYTE_ARRAY ;
break ;
case GDScriptParser : : DataType : : ENUM :
need_warn = true ;
break ;
default :
break ;
}
}
} else {
need_warn = true ;
}
if ( need_warn ) {
parser - > push_warning ( p_assignment , GDScriptWarning : : CONFUSABLE_CAPTURE_REASSIGNMENT , id - > name ) ;
}
2024-06-28 06:35:04 +00:00
}
}
}
# endif
2020-07-06 15:24:24 +00:00
if ( p_assignment - > assigned_value = = nullptr | | p_assignment - > assignee = = nullptr ) {
return ;
}
2021-03-09 15:32:35 +00:00
GDScriptParser : : DataType assignee_type = p_assignment - > assignee - > get_datatype ( ) ;
2023-05-16 10:03:53 +00:00
if ( assignee_type . is_constant ) {
2023-01-08 03:41:06 +00:00
push_error ( " Cannot assign a new value to a constant. " , p_assignment - > assignee ) ;
2023-01-30 17:50:08 +00:00
return ;
2023-05-16 10:03:53 +00:00
} else if ( p_assignment - > assignee - > type = = GDScriptParser : : Node : : SUBSCRIPT & & static_cast < GDScriptParser : : SubscriptNode * > ( p_assignment - > assignee ) - > base - > is_constant ) {
const GDScriptParser : : DataType & base_type = static_cast < GDScriptParser : : SubscriptNode * > ( p_assignment - > assignee ) - > base - > datatype ;
if ( base_type . kind ! = GDScriptParser : : DataType : : SCRIPT & & base_type . kind ! = GDScriptParser : : DataType : : CLASS ) { // Static variables.
push_error ( " Cannot assign a new value to a constant. " , p_assignment - > assignee ) ;
return ;
}
2023-01-30 17:50:08 +00:00
} else if ( assignee_type . is_read_only ) {
push_error ( " Cannot assign a new value to a read-only property. " , p_assignment - > assignee ) ;
return ;
} else if ( p_assignment - > assignee - > type = = GDScriptParser : : Node : : SUBSCRIPT ) {
GDScriptParser : : SubscriptNode * sub = static_cast < GDScriptParser : : SubscriptNode * > ( p_assignment - > assignee ) ;
while ( sub ) {
const GDScriptParser : : DataType & base_type = sub - > base - > datatype ;
if ( base_type . is_hard_type ( ) & & base_type . is_read_only ) {
if ( base_type . kind = = GDScriptParser : : DataType : : BUILTIN & & ! Variant : : is_type_shared ( base_type . builtin_type ) ) {
push_error ( " Cannot assign a new value to a read-only property. " , p_assignment - > assignee ) ;
return ;
}
} else {
break ;
}
if ( sub - > base - > type = = GDScriptParser : : Node : : SUBSCRIPT ) {
sub = static_cast < GDScriptParser : : SubscriptNode * > ( sub - > base ) ;
} else {
sub = nullptr ;
}
}
2023-01-08 03:41:06 +00:00
}
2023-06-24 18:03:28 +00:00
// Check if assigned value is an array/dictionary literal, so we can make it a typed container too if appropriate.
2023-09-14 18:31:07 +00:00
if ( p_assignment - > assigned_value - > type = = GDScriptParser : : Node : : ARRAY & & assignee_type . is_hard_type ( ) & & assignee_type . has_container_element_type ( 0 ) ) {
update_array_literal_element_type ( static_cast < GDScriptParser : : ArrayNode * > ( p_assignment - > assigned_value ) , assignee_type . get_container_element_type ( 0 ) ) ;
2023-06-24 18:03:28 +00:00
} else if ( p_assignment - > assigned_value - > type = = GDScriptParser : : Node : : DICTIONARY & & assignee_type . is_hard_type ( ) & & assignee_type . has_container_element_types ( ) ) {
update_dictionary_literal_element_type ( static_cast < GDScriptParser : : DictionaryNode * > ( p_assignment - > assigned_value ) ,
assignee_type . get_container_element_type_or_variant ( 0 ) , assignee_type . get_container_element_type_or_variant ( 1 ) ) ;
2021-03-09 15:32:35 +00:00
}
2023-01-22 08:32:05 +00:00
if ( p_assignment - > operation = = GDScriptParser : : AssignmentNode : : OP_NONE & & assignee_type . is_hard_type ( ) & & p_assignment - > assigned_value - > is_constant ) {
update_const_expression_builtin_type ( p_assignment - > assigned_value , assignee_type , " assign " ) ;
}
2021-03-09 15:32:35 +00:00
GDScriptParser : : DataType assigned_value_type = p_assignment - > assigned_value - > get_datatype ( ) ;
2022-12-30 07:58:07 +00:00
bool assignee_is_variant = assignee_type . is_variant ( ) ;
bool assignee_is_hard = assignee_type . is_hard_type ( ) ;
bool assigned_is_variant = assigned_value_type . is_variant ( ) ;
bool assigned_is_hard = assigned_value_type . is_hard_type ( ) ;
2021-12-26 19:32:22 +00:00
bool compatible = true ;
2022-12-30 07:58:07 +00:00
bool downgrades_assignee = false ;
bool downgrades_assigned = false ;
2021-12-26 19:32:22 +00:00
GDScriptParser : : DataType op_type = assigned_value_type ;
2023-01-10 23:01:11 +00:00
if ( p_assignment - > operation ! = GDScriptParser : : AssignmentNode : : OP_NONE & & ! op_type . is_variant ( ) ) {
2021-12-26 19:32:22 +00:00
op_type = get_operation_type ( p_assignment - > variant_op , assignee_type , assigned_value_type , compatible , p_assignment - > assigned_value ) ;
2022-12-30 07:58:07 +00:00
if ( assignee_is_variant ) {
// variant assignee
mark_node_unsafe ( p_assignment ) ;
} else if ( ! compatible ) {
// incompatible hard types and non-variant assignee
mark_node_unsafe ( p_assignment ) ;
if ( assigned_is_variant ) {
// incompatible hard non-variant assignee and hard variant assigned
p_assignment - > use_conversion_assign = true ;
} else {
// incompatible hard non-variant types
push_error ( vformat ( R " (Invalid operands " % s " and " % s " for assignment operator.) " , assignee_type . to_string ( ) , assigned_value_type . to_string ( ) ) , p_assignment ) ;
}
} else if ( op_type . type_source = = GDScriptParser : : DataType : : UNDETECTED & & ! assigned_is_variant ) {
// incompatible non-variant types (at least one weak)
downgrades_assignee = ! assignee_is_hard ;
downgrades_assigned = ! assigned_is_hard ;
}
2021-12-26 19:32:22 +00:00
}
p_assignment - > set_datatype ( op_type ) ;
2020-07-25 02:27:58 +00:00
2022-12-30 07:58:07 +00:00
if ( assignee_is_variant ) {
if ( ! assignee_is_hard ) {
// weak variant assignee
mark_node_unsafe ( p_assignment ) ;
2020-06-10 22:53:25 +00:00
}
2022-07-03 22:10:16 +00:00
} else {
2022-12-30 07:58:07 +00:00
if ( assignee_is_hard & & ! assigned_is_hard ) {
// hard non-variant assignee and weak assigned
mark_node_unsafe ( p_assignment ) ;
2021-05-26 17:05:31 +00:00
p_assignment - > use_conversion_assign = true ;
2022-12-30 07:58:07 +00:00
downgrades_assigned = downgrades_assigned | | ( ! assigned_is_variant & & ! is_type_compatible ( assignee_type , op_type , true , p_assignment - > assigned_value ) ) ;
} else if ( compatible ) {
if ( op_type . is_variant ( ) ) {
// non-variant assignee and variant result
mark_node_unsafe ( p_assignment ) ;
if ( assignee_is_hard ) {
// hard non-variant assignee and variant result
p_assignment - > use_conversion_assign = true ;
} else {
// weak non-variant assignee and variant result
downgrades_assignee = true ;
2020-06-10 22:53:25 +00:00
}
2022-12-30 07:58:07 +00:00
} else if ( ! is_type_compatible ( assignee_type , op_type , assignee_is_hard , p_assignment - > assigned_value ) ) {
// non-variant assignee and incompatible result
mark_node_unsafe ( p_assignment ) ;
if ( assignee_is_hard ) {
2023-01-22 08:32:05 +00:00
if ( is_type_compatible ( op_type , assignee_type ) ) {
2022-12-30 07:58:07 +00:00
// hard non-variant assignee and maybe compatible result
p_assignment - > use_conversion_assign = true ;
} else {
// hard non-variant assignee and incompatible result
push_error ( vformat ( R " (Value of type " % s " cannot be assigned to a variable of type " % s " .) " , assigned_value_type . to_string ( ) , assignee_type . to_string ( ) ) , p_assignment - > assigned_value ) ;
}
} else {
// weak non-variant assignee and incompatible result
downgrades_assignee = true ;
2020-06-10 22:53:25 +00:00
}
2023-06-24 18:03:28 +00:00
} else if ( ( assignee_type . has_container_element_type ( 0 ) & & ! op_type . has_container_element_type ( 0 ) ) | | ( assignee_type . has_container_element_type ( 1 ) & & ! op_type . has_container_element_type ( 1 ) ) ) {
// Typed assignee and untyped result.
2022-11-27 07:56:53 +00:00
mark_node_unsafe ( p_assignment ) ;
2022-12-30 07:58:07 +00:00
}
2020-06-10 22:53:25 +00:00
}
}
2020-06-11 22:31:28 +00:00
2022-12-30 07:58:07 +00:00
if ( downgrades_assignee ) {
downgrade_node_type_source ( p_assignment - > assignee ) ;
}
if ( downgrades_assigned ) {
downgrade_node_type_source ( p_assignment - > assigned_value ) ;
}
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2022-12-30 14:57:25 +00:00
if ( assignee_type . is_hard_type ( ) & & assignee_type . builtin_type = = Variant : : INT & & assigned_value_type . builtin_type = = Variant : : FLOAT ) {
2020-06-11 22:31:28 +00:00
parser - > push_warning ( p_assignment - > assigned_value , GDScriptWarning : : NARROWING_CONVERSION ) ;
}
2024-04-09 17:15:51 +00:00
// Check for assignment with operation before assignment.
if ( p_assignment - > operation ! = GDScriptParser : : AssignmentNode : : OP_NONE & & p_assignment - > assignee - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
GDScriptParser : : IdentifierNode * id = static_cast < GDScriptParser : : IdentifierNode * > ( p_assignment - > assignee ) ;
// Use == 1 here because this assignment was already counted in the beginning of the function.
if ( id - > source = = GDScriptParser : : IdentifierNode : : LOCAL_VARIABLE & & id - > variable_source & & id - > variable_source - > assignments = = 1 ) {
parser - > push_warning ( p_assignment , GDScriptWarning : : UNASSIGNED_VARIABLE_OP_ASSIGN , id - > name ) ;
}
}
2020-07-16 01:02:44 +00:00
# endif
2020-06-10 22:53:25 +00:00
}
void GDScriptAnalyzer : : reduce_await ( GDScriptParser : : AwaitNode * p_await ) {
2020-07-27 12:50:36 +00:00
if ( p_await - > to_await = = nullptr ) {
GDScriptParser : : DataType await_type ;
await_type . kind = GDScriptParser : : DataType : : VARIANT ;
p_await - > set_datatype ( await_type ) ;
return ;
}
2021-10-14 23:30:06 +00:00
2020-06-11 22:31:28 +00:00
if ( p_await - > to_await - > type = = GDScriptParser : : Node : : CALL ) {
reduce_call ( static_cast < GDScriptParser : : CallNode * > ( p_await - > to_await ) , true ) ;
} else {
reduce_expression ( p_await - > to_await ) ;
}
2020-06-10 22:53:25 +00:00
2023-02-03 17:51:00 +00:00
GDScriptParser : : DataType await_type = p_await - > to_await - > get_datatype ( ) ;
// We cannot infer the type of the result of waiting for a signal.
if ( await_type . is_hard_type ( ) & & await_type . kind = = GDScriptParser : : DataType : : BUILTIN & & await_type . builtin_type = = Variant : : SIGNAL ) {
await_type . kind = GDScriptParser : : DataType : : VARIANT ;
await_type . type_source = GDScriptParser : : DataType : : UNDETECTED ;
} else if ( p_await - > to_await - > is_constant ) {
2021-10-14 23:30:06 +00:00
p_await - > is_constant = p_await - > to_await - > is_constant ;
p_await - > reduced_value = p_await - > to_await - > reduced_value ;
}
2023-02-03 17:51:00 +00:00
await_type . is_coroutine = false ;
p_await - > set_datatype ( await_type ) ;
2020-06-10 22:53:25 +00:00
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2023-02-03 17:51:00 +00:00
GDScriptParser : : DataType to_await_type = p_await - > to_await - > get_datatype ( ) ;
2023-03-15 19:11:02 +00:00
if ( ! to_await_type . is_coroutine & & ! to_await_type . is_variant ( ) & & to_await_type . builtin_type ! = Variant : : SIGNAL ) {
2020-06-11 22:31:28 +00:00
parser - > push_warning ( p_await , GDScriptWarning : : REDUNDANT_AWAIT ) ;
}
2020-07-16 01:02:44 +00:00
# endif
2020-06-10 22:53:25 +00:00
}
void GDScriptAnalyzer : : reduce_binary_op ( GDScriptParser : : BinaryOpNode * p_binary_op ) {
reduce_expression ( p_binary_op - > left_operand ) ;
2023-02-16 23:16:24 +00:00
reduce_expression ( p_binary_op - > right_operand ) ;
2020-06-10 22:53:25 +00:00
2020-07-16 01:02:44 +00:00
GDScriptParser : : DataType left_type ;
if ( p_binary_op - > left_operand ) {
2021-09-30 16:56:33 +00:00
left_type = p_binary_op - > left_operand - > get_datatype ( ) ;
2020-07-16 01:02:44 +00:00
}
GDScriptParser : : DataType right_type ;
if ( p_binary_op - > right_operand ) {
2021-09-30 16:56:33 +00:00
right_type = p_binary_op - > right_operand - > get_datatype ( ) ;
2020-07-16 01:02:44 +00:00
}
if ( ! left_type . is_set ( ) | | ! right_type . is_set ( ) ) {
return ;
}
2020-06-11 22:31:28 +00:00
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2020-06-11 22:31:28 +00:00
if ( p_binary_op - > variant_op = = Variant : : OP_DIVIDE & & left_type . builtin_type = = Variant : : INT & & right_type . builtin_type = = Variant : : INT ) {
parser - > push_warning ( p_binary_op , GDScriptWarning : : INTEGER_DIVISION ) ;
}
2020-07-16 01:02:44 +00:00
# endif
2020-06-11 22:31:28 +00:00
2020-06-10 22:53:25 +00:00
if ( p_binary_op - > left_operand - > is_constant & & p_binary_op - > right_operand - > is_constant ) {
p_binary_op - > is_constant = true ;
if ( p_binary_op - > variant_op < Variant : : OP_MAX ) {
2020-07-24 18:02:29 +00:00
bool valid = false ;
Variant : : evaluate ( p_binary_op - > variant_op , p_binary_op - > left_operand - > reduced_value , p_binary_op - > right_operand - > reduced_value , p_binary_op - > reduced_value , valid ) ;
if ( ! valid ) {
if ( p_binary_op - > reduced_value . get_type ( ) = = Variant : : STRING ) {
push_error ( vformat ( R " (%s in operator %s.) " , p_binary_op - > reduced_value , Variant : : get_operator_name ( p_binary_op - > variant_op ) ) , p_binary_op ) ;
} else {
2020-12-01 14:16:31 +00:00
push_error ( vformat ( R " (Invalid operands to operator %s, %s and %s.) " ,
2020-07-24 18:02:29 +00:00
Variant : : get_operator_name ( p_binary_op - > variant_op ) ,
Variant : : get_type_name ( p_binary_op - > left_operand - > reduced_value . get_type ( ) ) ,
Variant : : get_type_name ( p_binary_op - > right_operand - > reduced_value . get_type ( ) ) ) ,
p_binary_op ) ;
}
}
2020-06-10 22:53:25 +00:00
} else {
2023-02-16 23:16:24 +00:00
ERR_PRINT ( " Parser bug: unknown binary operation. " ) ;
2020-06-10 22:53:25 +00:00
}
2020-08-31 12:53:02 +00:00
p_binary_op - > set_datatype ( type_from_variant ( p_binary_op - > reduced_value , p_binary_op ) ) ;
2020-06-11 22:31:28 +00:00
2020-06-10 22:53:25 +00:00
return ;
}
GDScriptParser : : DataType result ;
2023-02-16 23:16:24 +00:00
if ( ( p_binary_op - > variant_op = = Variant : : OP_EQUAL | | p_binary_op - > variant_op = = Variant : : OP_NOT_EQUAL ) & &
2022-11-25 09:01:18 +00:00
( ( left_type . kind = = GDScriptParser : : DataType : : BUILTIN & & left_type . builtin_type = = Variant : : NIL ) | | ( right_type . kind = = GDScriptParser : : DataType : : BUILTIN & & right_type . builtin_type = = Variant : : NIL ) ) ) {
// "==" and "!=" operators always return a boolean when comparing to null.
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
result . kind = GDScriptParser : : DataType : : BUILTIN ;
result . builtin_type = Variant : : BOOL ;
2024-04-09 18:49:44 +00:00
} else if ( p_binary_op - > variant_op = = Variant : : OP_MODULE & & left_type . builtin_type = = Variant : : STRING ) {
// The modulo operator (%) on string acts as formatting and will always return a string.
result . type_source = left_type . type_source ;
result . kind = GDScriptParser : : DataType : : BUILTIN ;
result . builtin_type = Variant : : STRING ;
2022-11-25 09:01:18 +00:00
} else if ( left_type . is_variant ( ) | | right_type . is_variant ( ) ) {
// Cannot infer type because one operand can be anything.
result . kind = GDScriptParser : : DataType : : VARIANT ;
mark_node_unsafe ( p_binary_op ) ;
} else if ( p_binary_op - > variant_op < Variant : : OP_MAX ) {
bool valid = false ;
result = get_operation_type ( p_binary_op - > variant_op , left_type , right_type , valid , p_binary_op ) ;
if ( ! valid ) {
push_error ( vformat ( R " (Invalid operands " % s " and " % s " for " % s " operator.) " , left_type . to_string ( ) , right_type . to_string ( ) , Variant : : get_operator_name ( p_binary_op - > variant_op ) ) , p_binary_op ) ;
2023-02-25 23:35:53 +00:00
} else if ( ! result . is_hard_type ( ) ) {
2023-02-14 14:57:21 +00:00
mark_node_unsafe ( p_binary_op ) ;
2022-11-25 09:01:18 +00:00
}
} else {
ERR_PRINT ( " Parser bug: unknown binary operation. " ) ;
2020-06-10 22:53:25 +00:00
}
p_binary_op - > set_datatype ( result ) ;
}
2023-02-10 08:07:01 +00:00
# ifdef SUGGEST_GODOT4_RENAMES
const char * get_rename_from_map ( const char * map [ ] [ 2 ] , String key ) {
2022-03-23 17:54:41 +00:00
for ( int index = 0 ; map [ index ] [ 0 ] ; index + + ) {
if ( map [ index ] [ 0 ] = = key ) {
return map [ index ] [ 1 ] ;
}
}
return nullptr ;
}
// Checks if an identifier/function name has been renamed in Godot 4, uses ProjectConverter3To4 for rename map.
// Returns the new name if found, nullptr otherwise.
2023-02-10 08:07:01 +00:00
const char * check_for_renamed_identifier ( String identifier , GDScriptParser : : Node : : Type type ) {
2022-03-23 17:54:41 +00:00
switch ( type ) {
case GDScriptParser : : Node : : IDENTIFIER : {
// Check properties
2023-02-10 08:07:01 +00:00
const char * result = get_rename_from_map ( RenamesMap3To4 : : gdscript_properties_renames , identifier ) ;
2022-03-23 17:54:41 +00:00
if ( result ) {
return result ;
}
// Check enum values
2023-02-10 08:07:01 +00:00
result = get_rename_from_map ( RenamesMap3To4 : : enum_renames , identifier ) ;
2022-03-23 17:54:41 +00:00
if ( result ) {
return result ;
}
// Check color constants
2023-02-10 08:07:01 +00:00
result = get_rename_from_map ( RenamesMap3To4 : : color_renames , identifier ) ;
2022-03-23 17:54:41 +00:00
if ( result ) {
return result ;
}
// Check type names
2023-02-10 08:07:01 +00:00
result = get_rename_from_map ( RenamesMap3To4 : : class_renames , identifier ) ;
2022-03-23 17:54:41 +00:00
if ( result ) {
return result ;
}
2023-02-10 08:07:01 +00:00
return get_rename_from_map ( RenamesMap3To4 : : builtin_types_renames , identifier ) ;
2022-03-23 17:54:41 +00:00
}
case GDScriptParser : : Node : : CALL : {
2023-02-10 08:07:01 +00:00
const char * result = get_rename_from_map ( RenamesMap3To4 : : gdscript_function_renames , identifier ) ;
2022-03-23 17:54:41 +00:00
if ( result ) {
return result ;
}
// Built-in Types are mistaken for function calls when the built-in type is not found.
// Check built-in types if function rename not found
2023-02-10 08:07:01 +00:00
return get_rename_from_map ( RenamesMap3To4 : : builtin_types_renames , identifier ) ;
2022-03-23 17:54:41 +00:00
}
// Signal references don't get parsed through the GDScriptAnalyzer. No support for signal rename hints.
default :
// No rename found, return null
return nullptr ;
}
}
2023-02-10 08:07:01 +00:00
# endif // SUGGEST_GODOT4_RENAMES
2022-03-23 17:54:41 +00:00
2021-10-14 23:12:01 +00:00
void GDScriptAnalyzer : : reduce_call ( GDScriptParser : : CallNode * p_call , bool p_is_await , bool p_is_root ) {
2020-06-10 22:53:25 +00:00
bool all_is_constant = true ;
2022-05-13 13:04:37 +00:00
HashMap < int , GDScriptParser : : ArrayNode * > arrays ; // For array literal to potentially type when passing.
2023-06-24 18:03:28 +00:00
HashMap < int , GDScriptParser : : DictionaryNode * > dictionaries ; // Same, but for dictionaries.
2020-06-10 22:53:25 +00:00
for ( int i = 0 ; i < p_call - > arguments . size ( ) ; i + + ) {
reduce_expression ( p_call - > arguments [ i ] ) ;
2021-03-09 15:32:35 +00:00
if ( p_call - > arguments [ i ] - > type = = GDScriptParser : : Node : : ARRAY ) {
arrays [ i ] = static_cast < GDScriptParser : : ArrayNode * > ( p_call - > arguments [ i ] ) ;
2023-06-24 18:03:28 +00:00
} else if ( p_call - > arguments [ i ] - > type = = GDScriptParser : : Node : : DICTIONARY ) {
dictionaries [ i ] = static_cast < GDScriptParser : : DictionaryNode * > ( p_call - > arguments [ i ] ) ;
2021-03-09 15:32:35 +00:00
}
2020-06-10 22:53:25 +00:00
all_is_constant = all_is_constant & & p_call - > arguments [ i ] - > is_constant ;
}
2020-08-05 19:41:46 +00:00
GDScriptParser : : Node : : Type callee_type = p_call - > get_callee_type ( ) ;
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType call_type ;
2020-08-05 19:41:46 +00:00
if ( ! p_call - > is_super & & callee_type = = GDScriptParser : : Node : : IDENTIFIER ) {
2020-06-10 22:53:25 +00:00
// Call to name directly.
StringName function_name = p_call - > function_name ;
2023-09-21 09:42:55 +00:00
if ( function_name = = SNAME ( " Object " ) ) {
push_error ( R " *(Invalid constructor " Object ( ) " , use " Object . new ( ) " instead.)* " , p_call ) ;
p_call - > set_datatype ( call_type ) ;
return ;
}
Variant : : Type builtin_type = GDScriptParser : : get_builtin_type ( function_name ) ;
2020-06-10 22:53:25 +00:00
if ( builtin_type < Variant : : VARIANT_MAX ) {
// Is a builtin constructor.
call_type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
call_type . kind = GDScriptParser : : DataType : : BUILTIN ;
call_type . builtin_type = builtin_type ;
2020-11-30 12:42:22 +00:00
bool safe_to_fold = true ;
switch ( builtin_type ) {
// Those are stored by reference so not suited for compile-time construction.
// Because in this case they would be the same reference in all constructed values.
case Variant : : OBJECT :
2020-12-25 07:03:55 +00:00
case Variant : : DICTIONARY :
case Variant : : ARRAY :
2020-11-30 12:42:22 +00:00
case Variant : : PACKED_BYTE_ARRAY :
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY :
case Variant : : PACKED_FLOAT32_ARRAY :
case Variant : : PACKED_FLOAT64_ARRAY :
case Variant : : PACKED_STRING_ARRAY :
case Variant : : PACKED_VECTOR2_ARRAY :
case Variant : : PACKED_VECTOR3_ARRAY :
case Variant : : PACKED_COLOR_ARRAY :
2024-04-08 14:51:34 +00:00
case Variant : : PACKED_VECTOR4_ARRAY :
2020-11-30 12:42:22 +00:00
safe_to_fold = false ;
break ;
default :
break ;
}
if ( all_is_constant & & safe_to_fold ) {
2020-06-10 22:53:25 +00:00
// Construct here.
Vector < const Variant * > args ;
for ( int i = 0 ; i < p_call - > arguments . size ( ) ; i + + ) {
args . push_back ( & ( p_call - > arguments [ i ] - > reduced_value ) ) ;
}
Callable : : CallError err ;
2020-11-09 03:19:09 +00:00
Variant value ;
Variant : : construct ( builtin_type , value , ( const Variant * * ) args . ptr ( ) , args . size ( ) , err ) ;
2020-06-10 22:53:25 +00:00
switch ( err . error ) {
case Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT :
2023-09-21 09:42:55 +00:00
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " constructor: argument %d should be " % s " but is " % s " .)* " , Variant : : get_type_name ( builtin_type ) , err . argument + 1 ,
2020-06-10 22:53:25 +00:00
Variant : : get_type_name ( Variant : : Type ( err . expected ) ) , p_call - > arguments [ err . argument ] - > get_datatype ( ) . to_string ( ) ) ,
p_call - > arguments [ err . argument ] ) ;
break ;
case Callable : : CallError : : CALL_ERROR_INVALID_METHOD : {
String signature = Variant : : get_type_name ( builtin_type ) + " ( " ;
for ( int i = 0 ; i < p_call - > arguments . size ( ) ; i + + ) {
if ( i > 0 ) {
signature + = " , " ;
}
signature + = p_call - > arguments [ i ] - > get_datatype ( ) . to_string ( ) ;
}
2020-08-18 16:12:18 +00:00
signature + = " ) " ;
2020-06-10 22:53:25 +00:00
push_error ( vformat ( R " (No constructor of " % s " matches the signature " % s " .) " , Variant : : get_type_name ( builtin_type ) , signature ) , p_call - > callee ) ;
} break ;
case Callable : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS :
2023-09-21 09:42:55 +00:00
push_error ( vformat ( R " *(Too many arguments for " % s ( ) " constructor. Received %d but expected %d.)* " , Variant : : get_type_name ( builtin_type ) , p_call - > arguments . size ( ) , err . expected ) , p_call ) ;
2020-06-10 22:53:25 +00:00
break ;
case Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS :
2023-09-21 09:42:55 +00:00
push_error ( vformat ( R " *(Too few arguments for " % s ( ) " constructor. Received %d but expected %d.)* " , Variant : : get_type_name ( builtin_type ) , p_call - > arguments . size ( ) , err . expected ) , p_call ) ;
2020-06-10 22:53:25 +00:00
break ;
case Callable : : CallError : : CALL_ERROR_INSTANCE_IS_NULL :
2022-06-27 20:10:04 +00:00
case Callable : : CallError : : CALL_ERROR_METHOD_NOT_CONST :
2020-06-10 22:53:25 +00:00
break ; // Can't happen in a builtin constructor.
case Callable : : CallError : : CALL_OK :
p_call - > is_constant = true ;
p_call - > reduced_value = value ;
break ;
}
} else {
// If there's one argument, try to use copy constructor (those aren't explicitly defined).
if ( p_call - > arguments . size ( ) = = 1 ) {
GDScriptParser : : DataType arg_type = p_call - > arguments [ 0 ] - > get_datatype ( ) ;
2023-09-21 09:42:55 +00:00
if ( arg_type . is_hard_type ( ) & & ! arg_type . is_variant ( ) ) {
2020-06-10 22:53:25 +00:00
if ( arg_type . kind = = GDScriptParser : : DataType : : BUILTIN & & arg_type . builtin_type = = builtin_type ) {
// Okay.
p_call - > set_datatype ( call_type ) ;
return ;
}
2023-09-21 09:42:55 +00:00
} else {
# ifdef DEBUG_ENABLED
mark_node_unsafe ( p_call ) ;
2023-09-29 19:43:56 +00:00
// Constructors support overloads.
Vector < String > types ;
for ( int i = 0 ; i < Variant : : VARIANT_MAX ; i + + ) {
if ( i ! = builtin_type & & Variant : : can_convert_strict ( ( Variant : : Type ) i , builtin_type ) ) {
types . push_back ( Variant : : get_type_name ( ( Variant : : Type ) i ) ) ;
}
}
String expected_types = function_name ;
if ( types . size ( ) = = 1 ) {
expected_types + = " \" or \" " + types [ 0 ] ;
} else if ( types . size ( ) > = 2 ) {
for ( int i = 0 ; i < types . size ( ) - 1 ; i + + ) {
expected_types + = " \" , \" " + types [ i ] ;
}
expected_types + = " \" , or \" " + types [ types . size ( ) - 1 ] ;
}
parser - > push_warning ( p_call - > arguments [ 0 ] , GDScriptWarning : : UNSAFE_CALL_ARGUMENT , " 1 " , " constructor " , function_name , expected_types , " Variant " ) ;
2023-09-21 09:42:55 +00:00
# endif
p_call - > set_datatype ( call_type ) ;
return ;
2020-06-10 22:53:25 +00:00
}
}
2023-09-21 09:42:55 +00:00
2020-06-10 22:53:25 +00:00
List < MethodInfo > constructors ;
Variant : : get_constructor_list ( builtin_type , & constructors ) ;
bool match = false ;
2021-07-16 03:45:57 +00:00
for ( const MethodInfo & info : constructors ) {
2020-06-10 22:53:25 +00:00
if ( p_call - > arguments . size ( ) < info . arguments . size ( ) - info . default_arguments . size ( ) ) {
continue ;
}
if ( p_call - > arguments . size ( ) > info . arguments . size ( ) ) {
continue ;
}
bool types_match = true ;
2024-04-15 13:18:34 +00:00
{
List < PropertyInfo > : : ConstIterator arg_itr = info . arguments . begin ( ) ;
for ( int i = 0 ; i < p_call - > arguments . size ( ) ; + + arg_itr , + + i ) {
GDScriptParser : : DataType par_type = type_from_property ( * arg_itr , true ) ;
GDScriptParser : : DataType arg_type = p_call - > arguments [ i ] - > get_datatype ( ) ;
if ( ! is_type_compatible ( par_type , arg_type , true ) ) {
types_match = false ;
break ;
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2024-04-15 13:18:34 +00:00
} else {
if ( par_type . builtin_type = = Variant : : INT & & arg_type . builtin_type = = Variant : : FLOAT & & builtin_type ! = Variant : : INT ) {
parser - > push_warning ( p_call , GDScriptWarning : : NARROWING_CONVERSION , function_name ) ;
}
2020-07-16 01:02:44 +00:00
# endif
2024-04-15 13:18:34 +00:00
}
2020-06-10 22:53:25 +00:00
}
}
if ( types_match ) {
2024-04-15 13:18:34 +00:00
List < PropertyInfo > : : ConstIterator arg_itr = info . arguments . begin ( ) ;
for ( int i = 0 ; i < p_call - > arguments . size ( ) ; + + arg_itr , + + i ) {
GDScriptParser : : DataType par_type = type_from_property ( * arg_itr , true ) ;
2023-01-22 08:32:05 +00:00
if ( p_call - > arguments [ i ] - > is_constant ) {
2023-09-21 09:42:55 +00:00
update_const_expression_builtin_type ( p_call - > arguments [ i ] , par_type , " pass " ) ;
}
# ifdef DEBUG_ENABLED
if ( ! ( par_type . is_variant ( ) & & par_type . is_hard_type ( ) ) ) {
GDScriptParser : : DataType arg_type = p_call - > arguments [ i ] - > get_datatype ( ) ;
2023-09-29 19:43:56 +00:00
if ( arg_type . is_variant ( ) | | ! arg_type . is_hard_type ( ) ) {
2023-09-21 09:42:55 +00:00
mark_node_unsafe ( p_call ) ;
2023-09-29 19:43:56 +00:00
parser - > push_warning ( p_call - > arguments [ i ] , GDScriptWarning : : UNSAFE_CALL_ARGUMENT , itos ( i + 1 ) , " constructor " , function_name , par_type . to_string ( ) , arg_type . to_string_strict ( ) ) ;
2023-09-21 09:42:55 +00:00
}
2023-01-22 08:32:05 +00:00
}
2023-09-21 09:42:55 +00:00
# endif
2023-01-22 08:32:05 +00:00
}
2020-06-10 22:53:25 +00:00
match = true ;
call_type = type_from_property ( info . return_val ) ;
break ;
}
}
if ( ! match ) {
String signature = Variant : : get_type_name ( builtin_type ) + " ( " ;
for ( int i = 0 ; i < p_call - > arguments . size ( ) ; i + + ) {
if ( i > 0 ) {
signature + = " , " ;
}
signature + = p_call - > arguments [ i ] - > get_datatype ( ) . to_string ( ) ;
}
2020-08-18 16:12:18 +00:00
signature + = " ) " ;
2020-06-10 22:53:25 +00:00
push_error ( vformat ( R " (No constructor of " % s " matches the signature " % s " .) " , Variant : : get_type_name ( builtin_type ) , signature ) , p_call ) ;
}
}
2024-03-19 12:02:54 +00:00
# ifdef DEBUG_ENABLED
// Consider `Signal(self, "my_signal")` as an implicit use of the signal.
if ( builtin_type = = Variant : : SIGNAL & & p_call - > arguments . size ( ) > = 2 ) {
const GDScriptParser : : ExpressionNode * object_arg = p_call - > arguments [ 0 ] ;
if ( object_arg & & object_arg - > type = = GDScriptParser : : Node : : SELF ) {
const GDScriptParser : : ExpressionNode * signal_arg = p_call - > arguments [ 1 ] ;
if ( signal_arg & & signal_arg - > is_constant ) {
const StringName & signal_name = signal_arg - > reduced_value ;
if ( parser - > current_class - > has_member ( signal_name ) ) {
const GDScriptParser : : ClassNode : : Member & member = parser - > current_class - > get_member ( signal_name ) ;
if ( member . type = = GDScriptParser : : ClassNode : : Member : : SIGNAL ) {
member . signal - > usages + + ;
}
}
}
}
}
# endif
2020-06-10 22:53:25 +00:00
p_call - > set_datatype ( call_type ) ;
return ;
2020-11-26 14:56:32 +00:00
} else if ( GDScriptUtilityFunctions : : function_exists ( function_name ) ) {
MethodInfo function_info = GDScriptUtilityFunctions : : get_function_info ( function_name ) ;
2023-01-09 12:55:05 +00:00
if ( ! p_is_root & & ! p_is_await & & function_info . return_val . type = = Variant : : NIL & & ( ( function_info . return_val . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) = = 0 ) ) {
2022-12-30 14:57:25 +00:00
push_error ( vformat ( R " *(Cannot get return value of call to " % s ( ) " because it returns " void " .)* " , function_name ) , p_call ) ;
}
2020-11-26 14:56:32 +00:00
if ( all_is_constant & & GDScriptUtilityFunctions : : is_function_constant ( function_name ) ) {
// Can call on compilation.
Vector < const Variant * > args ;
for ( int i = 0 ; i < p_call - > arguments . size ( ) ; i + + ) {
args . push_back ( & ( p_call - > arguments [ i ] - > reduced_value ) ) ;
}
Variant value ;
Callable : : CallError err ;
GDScriptUtilityFunctions : : get_function ( function_name ) ( & value , ( const Variant * * ) args . ptr ( ) , args . size ( ) , err ) ;
switch ( err . error ) {
2023-06-30 17:40:02 +00:00
case Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT :
if ( value . get_type ( ) = = Variant : : STRING & & ! value . operator String ( ) . is_empty ( ) ) {
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: %s)* " , function_name , value ) , p_call - > arguments [ err . argument ] ) ;
} else {
// Do not use `type_from_property()` for expected type, since utility functions use their own checks.
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: argument %d should be " % s " but is " % s " .)* " , function_name , err . argument + 1 ,
Variant : : get_type_name ( ( Variant : : Type ) err . expected ) , p_call - > arguments [ err . argument ] - > get_datatype ( ) . to_string ( ) ) ,
p_call - > arguments [ err . argument ] ) ;
}
break ;
2020-11-26 14:56:32 +00:00
case Callable : : CallError : : CALL_ERROR_INVALID_METHOD :
push_error ( vformat ( R " (Invalid call for function " % s " .) " , function_name ) , p_call ) ;
break ;
case Callable : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS :
push_error ( vformat ( R " *(Too many arguments for " % s ( ) " call. Expected at most %d but received %d.)* " , function_name , err . expected , p_call - > arguments . size ( ) ) , p_call ) ;
break ;
case Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS :
push_error ( vformat ( R " *(Too few arguments for " % s ( ) " call. Expected at least %d but received %d.)* " , function_name , err . expected , p_call - > arguments . size ( ) ) , p_call ) ;
break ;
2022-06-27 20:10:04 +00:00
case Callable : : CallError : : CALL_ERROR_METHOD_NOT_CONST :
2020-11-26 14:56:32 +00:00
case Callable : : CallError : : CALL_ERROR_INSTANCE_IS_NULL :
break ; // Can't happen in a builtin constructor.
case Callable : : CallError : : CALL_OK :
p_call - > is_constant = true ;
p_call - > reduced_value = value ;
break ;
}
} else {
validate_call_arg ( function_info , p_call ) ;
}
p_call - > set_datatype ( type_from_property ( function_info . return_val ) ) ;
return ;
} else if ( Variant : : has_utility_function ( function_name ) ) {
MethodInfo function_info = info_from_utility_func ( function_name ) ;
2020-06-10 22:53:25 +00:00
2023-01-09 12:55:05 +00:00
if ( ! p_is_root & & ! p_is_await & & function_info . return_val . type = = Variant : : NIL & & ( ( function_info . return_val . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) = = 0 ) ) {
2022-12-30 14:57:25 +00:00
push_error ( vformat ( R " *(Cannot get return value of call to " % s ( ) " because it returns " void " .)* " , function_name ) , p_call ) ;
}
2020-11-26 14:56:32 +00:00
if ( all_is_constant & & Variant : : get_utility_function_type ( function_name ) = = Variant : : UTILITY_FUNC_TYPE_MATH ) {
2020-06-10 22:53:25 +00:00
// Can call on compilation.
Vector < const Variant * > args ;
for ( int i = 0 ; i < p_call - > arguments . size ( ) ; i + + ) {
args . push_back ( & ( p_call - > arguments [ i ] - > reduced_value ) ) ;
}
Variant value ;
Callable : : CallError err ;
2020-11-26 14:56:32 +00:00
Variant : : call_utility_function ( function_name , & value , ( const Variant * * ) args . ptr ( ) , args . size ( ) , err ) ;
2020-06-10 22:53:25 +00:00
switch ( err . error ) {
2023-06-30 17:40:02 +00:00
case Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT :
if ( value . get_type ( ) = = Variant : : STRING & & ! value . operator String ( ) . is_empty ( ) ) {
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: %s)* " , function_name , value ) , p_call - > arguments [ err . argument ] ) ;
2022-10-30 21:24:39 +00:00
} else {
2023-06-30 17:40:02 +00:00
// Do not use `type_from_property()` for expected type, since utility functions use their own checks.
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: argument %d should be " % s " but is " % s " .)* " , function_name , err . argument + 1 ,
Variant : : get_type_name ( ( Variant : : Type ) err . expected ) , p_call - > arguments [ err . argument ] - > get_datatype ( ) . to_string ( ) ) ,
p_call - > arguments [ err . argument ] ) ;
2022-10-30 21:24:39 +00:00
}
2023-06-30 17:40:02 +00:00
break ;
2020-06-10 22:53:25 +00:00
case Callable : : CallError : : CALL_ERROR_INVALID_METHOD :
2020-11-26 14:56:32 +00:00
push_error ( vformat ( R " (Invalid call for function " % s " .) " , function_name ) , p_call ) ;
2020-06-10 22:53:25 +00:00
break ;
case Callable : : CallError : : CALL_ERROR_TOO_MANY_ARGUMENTS :
2020-11-26 14:56:32 +00:00
push_error ( vformat ( R " *(Too many arguments for " % s ( ) " call. Expected at most %d but received %d.)* " , function_name , err . expected , p_call - > arguments . size ( ) ) , p_call ) ;
2020-06-10 22:53:25 +00:00
break ;
case Callable : : CallError : : CALL_ERROR_TOO_FEW_ARGUMENTS :
2020-11-26 14:56:32 +00:00
push_error ( vformat ( R " *(Too few arguments for " % s ( ) " call. Expected at least %d but received %d.)* " , function_name , err . expected , p_call - > arguments . size ( ) ) , p_call ) ;
2020-06-10 22:53:25 +00:00
break ;
2022-06-27 20:10:04 +00:00
case Callable : : CallError : : CALL_ERROR_METHOD_NOT_CONST :
2020-06-10 22:53:25 +00:00
case Callable : : CallError : : CALL_ERROR_INSTANCE_IS_NULL :
break ; // Can't happen in a builtin constructor.
case Callable : : CallError : : CALL_OK :
p_call - > is_constant = true ;
p_call - > reduced_value = value ;
break ;
}
} else {
validate_call_arg ( function_info , p_call ) ;
}
2020-06-11 22:31:28 +00:00
p_call - > set_datatype ( type_from_property ( function_info . return_val ) ) ;
2020-06-10 22:53:25 +00:00
return ;
}
}
GDScriptParser : : DataType base_type ;
2020-06-11 22:31:28 +00:00
call_type . kind = GDScriptParser : : DataType : : VARIANT ;
2020-06-10 22:53:25 +00:00
bool is_self = false ;
if ( p_call - > is_super ) {
base_type = parser - > current_class - > base_type ;
2021-05-26 18:33:18 +00:00
base_type . is_meta_type = false ;
2020-06-10 22:53:25 +00:00
is_self = true ;
2022-03-30 14:53:49 +00:00
2023-08-23 09:37:18 +00:00
if ( p_call - > callee = = nullptr & & current_lambda ! = nullptr ) {
2022-03-30 14:53:49 +00:00
push_error ( " Cannot use `super()` inside a lambda. " , p_call ) ;
}
2020-08-05 19:41:46 +00:00
} else if ( callee_type = = GDScriptParser : : Node : : IDENTIFIER ) {
2020-06-10 22:53:25 +00:00
base_type = parser - > current_class - > get_datatype ( ) ;
2021-05-26 18:33:18 +00:00
base_type . is_meta_type = false ;
2020-06-10 22:53:25 +00:00
is_self = true ;
2020-08-05 19:41:46 +00:00
} else if ( callee_type = = GDScriptParser : : Node : : SUBSCRIPT ) {
2020-06-10 22:53:25 +00:00
GDScriptParser : : SubscriptNode * subscript = static_cast < GDScriptParser : : SubscriptNode * > ( p_call - > callee ) ;
2021-04-05 14:17:59 +00:00
if ( subscript - > base = = nullptr ) {
// Invalid syntax, error already set on parser.
p_call - > set_datatype ( call_type ) ;
mark_node_unsafe ( p_call ) ;
return ;
}
2020-06-10 22:53:25 +00:00
if ( ! subscript - > is_attribute ) {
2020-06-11 22:31:28 +00:00
// Invalid call. Error already sent in parser.
2020-06-10 22:53:25 +00:00
// TODO: Could check if Callable here.
2020-06-11 22:31:28 +00:00
p_call - > set_datatype ( call_type ) ;
2020-06-10 22:53:25 +00:00
mark_node_unsafe ( p_call ) ;
return ;
}
2021-05-16 14:48:53 +00:00
if ( subscript - > attribute = = nullptr ) {
// Invalid call. Error already sent in parser.
p_call - > set_datatype ( call_type ) ;
mark_node_unsafe ( p_call ) ;
return ;
}
2020-06-10 22:53:25 +00:00
2021-05-16 14:48:53 +00:00
GDScriptParser : : IdentifierNode * base_id = nullptr ;
if ( subscript - > base - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
base_id = static_cast < GDScriptParser : : IdentifierNode * > ( subscript - > base ) ;
}
if ( base_id & & GDScriptParser : : get_builtin_type ( base_id - > name ) < Variant : : VARIANT_MAX ) {
base_type = make_builtin_meta_type ( GDScriptParser : : get_builtin_type ( base_id - > name ) ) ;
} else {
reduce_expression ( subscript - > base ) ;
base_type = subscript - > base - > get_datatype ( ) ;
2022-12-30 07:58:07 +00:00
is_self = subscript - > base - > type = = GDScriptParser : : Node : : SELF ;
2021-05-16 14:48:53 +00:00
}
2020-06-10 22:53:25 +00:00
} else {
2020-06-11 22:31:28 +00:00
// Invalid call. Error already sent in parser.
2020-06-10 22:53:25 +00:00
// TODO: Could check if Callable here too.
2020-06-11 22:31:28 +00:00
p_call - > set_datatype ( call_type ) ;
2020-06-10 22:53:25 +00:00
mark_node_unsafe ( p_call ) ;
return ;
}
int default_arg_count = 0 ;
2023-05-21 23:22:00 +00:00
BitField < MethodFlags > method_flags ;
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType return_type ;
List < GDScriptParser : : DataType > par_types ;
2022-03-06 14:09:12 +00:00
bool is_constructor = ( base_type . is_meta_type | | ( p_call - > callee & & p_call - > callee - > type = = GDScriptParser : : Node : : IDENTIFIER ) ) & & p_call - > function_name = = SNAME ( " new " ) ;
2023-09-22 07:10:22 +00:00
if ( is_constructor & & Engine : : get_singleton ( ) - > has_singleton ( base_type . native_type ) ) {
push_error ( vformat ( R " (Cannot construct native class " % s " because it is an engine singleton.) " , base_type . native_type ) , p_call ) ;
p_call - > set_datatype ( call_type ) ;
return ;
}
2023-05-21 23:22:00 +00:00
if ( get_function_signature ( p_call , is_constructor , base_type , p_call - > function_name , return_type , par_types , default_arg_count , method_flags ) ) {
2023-09-17 15:34:18 +00:00
// If the method is implemented in the class hierarchy, the virtual flag will not be set for that MethodInfo and the search stops there.
// Virtual check only possible for super() calls because class hierarchy is known. Node/Objects may have scripts attached we don't know of at compile-time.
2024-04-04 21:15:17 +00:00
p_call - > is_static = method_flags . has_flag ( METHOD_FLAG_STATIC ) ;
2023-09-17 15:34:18 +00:00
if ( p_call - > is_super & & method_flags . has_flag ( METHOD_FLAG_VIRTUAL ) ) {
push_error ( vformat ( R " *(Cannot call the parent class' virtual function " % s ( ) " because it hasn't been defined.)* " , p_call - > function_name ) , p_call ) ;
}
// If the function requires typed arrays we must make literals be typed.
2021-08-09 20:13:42 +00:00
for ( const KeyValue < int , GDScriptParser : : ArrayNode * > & E : arrays ) {
int index = E . key ;
2024-04-15 13:18:34 +00:00
if ( index < par_types . size ( ) & & par_types . get ( index ) . is_hard_type ( ) & & par_types . get ( index ) . has_container_element_type ( 0 ) ) {
update_array_literal_element_type ( E . value , par_types . get ( index ) . get_container_element_type ( 0 ) ) ;
2021-03-09 15:32:35 +00:00
}
}
2023-06-24 18:03:28 +00:00
for ( const KeyValue < int , GDScriptParser : : DictionaryNode * > & E : dictionaries ) {
int index = E . key ;
if ( index < par_types . size ( ) & & par_types . get ( index ) . is_hard_type ( ) & & par_types . get ( index ) . has_container_element_types ( ) ) {
GDScriptParser : : DataType key = par_types . get ( index ) . get_container_element_type_or_variant ( 0 ) ;
GDScriptParser : : DataType value = par_types . get ( index ) . get_container_element_type_or_variant ( 1 ) ;
update_dictionary_literal_element_type ( E . value , key , value ) ;
}
}
2023-05-21 23:22:00 +00:00
validate_call_arg ( par_types , default_arg_count , method_flags . has_flag ( METHOD_FLAG_VARARG ) , p_call ) ;
2020-06-10 22:53:25 +00:00
2022-02-02 16:57:24 +00:00
if ( base_type . kind = = GDScriptParser : : DataType : : ENUM & & base_type . is_meta_type ) {
// Enum type is treated as a dictionary value for function calls.
base_type . is_meta_type = false ;
}
2024-04-04 21:15:17 +00:00
if ( is_self & & static_context & & ! p_call - > is_static ) {
2023-10-17 09:46:41 +00:00
// Get the parent function above any lambda.
GDScriptParser : : FunctionNode * parent_function = parser - > current_function ;
while ( parent_function & & parent_function - > source_lambda ) {
parent_function = parent_function - > source_lambda - > parent_function ;
}
if ( parent_function ) {
2024-05-01 13:49:12 +00:00
push_error ( vformat ( R " *(Cannot call non-static function " % s ( ) " from the static function " % s ( ) " .)* " , p_call - > function_name , parent_function - > identifier - > name ) , p_call ) ;
2023-04-19 14:10:35 +00:00
} else {
2023-10-17 09:46:41 +00:00
push_error ( vformat ( R " *(Cannot call non-static function " % s ( ) " from a static variable initializer.)* " , p_call - > function_name ) , p_call ) ;
2022-04-20 17:22:22 +00:00
}
2024-04-04 21:15:17 +00:00
} else if ( ! is_self & & base_type . is_meta_type & & ! p_call - > is_static ) {
2021-05-26 12:23:17 +00:00
base_type . is_meta_type = false ; // For `to_string()`.
2022-03-30 14:53:49 +00:00
push_error ( vformat ( R " *(Cannot call non-static function " % s ( ) " on the class " % s " directly. Make an instance instead.)* " , p_call - > function_name , base_type . to_string ( ) ) , p_call ) ;
2024-04-04 21:15:17 +00:00
} else if ( is_self & & ! p_call - > is_static ) {
2022-04-20 17:22:22 +00:00
mark_lambda_use_self ( ) ;
2020-06-10 22:53:25 +00:00
}
2023-01-09 12:55:05 +00:00
if ( ! p_is_root & & ! p_is_await & & return_type . is_hard_type ( ) & & return_type . kind = = GDScriptParser : : DataType : : BUILTIN & & return_type . builtin_type = = Variant : : NIL ) {
2022-12-30 14:57:25 +00:00
push_error ( vformat ( R " *(Cannot get return value of call to " % s ( ) " because it returns " void " .)* " , p_call - > function_name ) , p_call ) ;
}
2022-10-13 17:31:12 +00:00
# ifdef DEBUG_ENABLED
2024-05-16 19:11:56 +00:00
// FIXME: No warning for built-in constructors and utilities due to early return.
2023-05-22 13:21:25 +00:00
if ( p_is_root & & return_type . kind ! = GDScriptParser : : DataType : : UNRESOLVED & & return_type . builtin_type ! = Variant : : NIL & &
! ( p_call - > is_super & & p_call - > function_name = = GDScriptLanguage : : get_singleton ( ) - > strings . _init ) ) {
2022-10-13 17:31:12 +00:00
parser - > push_warning ( p_call , GDScriptWarning : : RETURN_VALUE_DISCARDED , p_call - > function_name ) ;
}
2022-10-13 19:42:11 +00:00
2023-12-08 12:26:44 +00:00
if ( method_flags . has_flag ( METHOD_FLAG_STATIC ) & & ! is_constructor & & ! base_type . is_meta_type & & ! is_self ) {
String caller_type = base_type . to_string ( ) ;
2022-11-24 20:06:11 +00:00
parser - > push_warning ( p_call , GDScriptWarning : : STATIC_CALLED_ON_INSTANCE , p_call - > function_name , caller_type ) ;
2022-10-13 19:42:11 +00:00
}
2024-03-19 12:02:54 +00:00
// Consider `emit_signal()`, `connect()`, and `disconnect()` as implicit uses of the signal.
if ( is_self & & ( p_call - > function_name = = SNAME ( " emit_signal " ) | | p_call - > function_name = = SNAME ( " connect " ) | | p_call - > function_name = = SNAME ( " disconnect " ) ) & & ! p_call - > arguments . is_empty ( ) ) {
const GDScriptParser : : ExpressionNode * signal_arg = p_call - > arguments [ 0 ] ;
if ( signal_arg & & signal_arg - > is_constant ) {
const StringName & signal_name = signal_arg - > reduced_value ;
if ( parser - > current_class - > has_member ( signal_name ) ) {
const GDScriptParser : : ClassNode : : Member & member = parser - > current_class - > get_member ( signal_name ) ;
if ( member . type = = GDScriptParser : : ClassNode : : Member : : SIGNAL ) {
member . signal - > usages + + ;
}
}
}
}
2022-10-13 17:31:12 +00:00
# endif // DEBUG_ENABLED
2020-06-11 22:31:28 +00:00
call_type = return_type ;
2020-06-10 22:53:25 +00:00
} else {
2020-06-11 22:31:28 +00:00
bool found = false ;
2022-04-06 17:14:38 +00:00
2022-12-04 03:02:03 +00:00
// Enums do not have functions other than the built-in dictionary ones.
if ( base_type . kind = = GDScriptParser : : DataType : : ENUM & & base_type . is_meta_type ) {
2023-02-02 14:57:22 +00:00
if ( base_type . builtin_type = = Variant : : DICTIONARY ) {
push_error ( vformat ( R " *(Enums only have Dictionary built-in methods. Function " % s ( ) " does not exist for enum " % s " .)* " , p_call - > function_name , base_type . enum_type ) , p_call - > callee ) ;
} else {
push_error ( vformat ( R " *(The native enum " % s " does not behave like Dictionary and does not have methods of its own.)* " , base_type . enum_type ) , p_call - > callee ) ;
}
2022-12-04 03:02:03 +00:00
} else if ( ! p_call - > is_super & & callee_type ! = GDScriptParser : : Node : : NONE ) { // Check if the name exists as something else.
2020-06-11 22:31:28 +00:00
GDScriptParser : : IdentifierNode * callee_id ;
2020-08-05 19:41:46 +00:00
if ( callee_type = = GDScriptParser : : Node : : IDENTIFIER ) {
2020-06-11 22:31:28 +00:00
callee_id = static_cast < GDScriptParser : : IdentifierNode * > ( p_call - > callee ) ;
} else {
// Can only be attribute.
callee_id = static_cast < GDScriptParser : : SubscriptNode * > ( p_call - > callee ) - > attribute ;
}
2020-07-16 01:02:44 +00:00
if ( callee_id ) {
reduce_identifier_from_base ( callee_id , & base_type ) ;
2020-08-05 19:41:46 +00:00
GDScriptParser : : DataType callee_datatype = callee_id - > get_datatype ( ) ;
if ( callee_datatype . is_set ( ) & & ! callee_datatype . is_variant ( ) ) {
2020-07-16 01:02:44 +00:00
found = true ;
2020-08-05 19:41:46 +00:00
if ( callee_datatype . builtin_type = = Variant : : CALLABLE ) {
2020-07-16 01:02:44 +00:00
push_error ( vformat ( R " *(Name " % s " is a Callable. You can call it with " % s . call ( ) " instead.)* " , p_call - > function_name , p_call - > function_name ) , p_call - > callee ) ;
} else {
2020-08-05 19:41:46 +00:00
push_error ( vformat ( R " *(Name " % s " called as a function but is a " % s " .)* " , p_call - > function_name , callee_datatype . to_string ( ) ) , p_call - > callee ) ;
2020-07-16 01:02:44 +00:00
}
# ifdef DEBUG_ENABLED
2021-01-03 13:14:01 +00:00
} else if ( ! is_self & & ! ( base_type . is_hard_type ( ) & & base_type . kind = = GDScriptParser : : DataType : : BUILTIN ) ) {
2020-07-16 01:02:44 +00:00
parser - > push_warning ( p_call , GDScriptWarning : : UNSAFE_METHOD_ACCESS , p_call - > function_name , base_type . to_string ( ) ) ;
mark_node_unsafe ( p_call ) ;
# endif
2020-06-11 22:31:28 +00:00
}
}
}
2021-01-03 13:14:01 +00:00
if ( ! found & & ( is_self | | ( base_type . is_hard_type ( ) & & base_type . kind = = GDScriptParser : : DataType : : BUILTIN ) ) ) {
2020-06-11 22:31:28 +00:00
String base_name = is_self & & ! p_call - > is_super ? " self " : base_type . to_string ( ) ;
2023-02-10 08:07:01 +00:00
# ifdef SUGGEST_GODOT4_RENAMES
2024-01-05 10:56:42 +00:00
String rename_hint ;
if ( GLOBAL_GET ( GDScriptWarning : : get_settings_path_from_code ( GDScriptWarning : : RENAMED_IN_GODOT_4_HINT ) ) . booleanize ( ) ) {
2022-03-23 17:54:41 +00:00
const char * renamed_function_name = check_for_renamed_identifier ( p_call - > function_name , p_call - > type ) ;
if ( renamed_function_name ) {
rename_hint = " " + vformat ( R " (Did you mean to use " % s " ?) " , String ( renamed_function_name ) + " () " ) ;
}
}
push_error ( vformat ( R " *(Function " % s ( ) " not found in base %s.%s)* " , p_call - > function_name , base_name , rename_hint ) , p_call - > is_super ? p_call : p_call - > callee ) ;
# else
push_error ( vformat ( R " *(Function " % s ( ) " not found in base %s.)* " , p_call - > function_name , base_name ) , p_call - > is_super ? p_call : p_call - > callee ) ;
2023-02-10 08:07:01 +00:00
# endif // SUGGEST_GODOT4_RENAMES
2023-09-21 09:42:55 +00:00
} else if ( ! found & & ( ! p_call - > is_super & & base_type . is_hard_type ( ) & & base_type . is_meta_type ) ) {
push_error ( vformat ( R " *(Static function " % s ( ) " not found in base " % s " .)* " , p_call - > function_name , base_type . to_string ( ) ) , p_call ) ;
2020-06-11 22:31:28 +00:00
}
}
2021-10-14 23:12:01 +00:00
if ( call_type . is_coroutine & & ! p_is_await & & ! p_is_root ) {
2022-03-30 14:53:49 +00:00
push_error ( vformat ( R " *(Function " % s ( ) " is a coroutine, so it must be called with " await " .)* " , p_call - > function_name ) , p_call ) ;
2020-06-10 22:53:25 +00:00
}
2020-06-11 22:31:28 +00:00
p_call - > set_datatype ( call_type ) ;
2020-06-10 22:53:25 +00:00
}
void GDScriptAnalyzer : : reduce_cast ( GDScriptParser : : CastNode * p_cast ) {
reduce_expression ( p_cast - > operand ) ;
2023-01-11 23:03:53 +00:00
GDScriptParser : : DataType cast_type = type_from_metatype ( resolve_datatype ( p_cast - > cast_type ) ) ;
2020-06-10 22:53:25 +00:00
if ( ! cast_type . is_set ( ) ) {
2022-01-27 14:34:33 +00:00
mark_node_unsafe ( p_cast ) ;
2020-06-10 22:53:25 +00:00
return ;
}
p_cast - > set_datatype ( cast_type ) ;
2023-01-22 08:32:05 +00:00
if ( p_cast - > operand - > is_constant ) {
update_const_expression_builtin_type ( p_cast - > operand , cast_type , " cast " , true ) ;
if ( cast_type . is_variant ( ) | | p_cast - > operand - > get_datatype ( ) = = cast_type ) {
p_cast - > is_constant = true ;
p_cast - > reduced_value = p_cast - > operand - > reduced_value ;
}
}
2020-06-10 22:53:25 +00:00
2023-09-14 18:31:07 +00:00
if ( p_cast - > operand - > type = = GDScriptParser : : Node : : ARRAY & & cast_type . has_container_element_type ( 0 ) ) {
update_array_literal_element_type ( static_cast < GDScriptParser : : ArrayNode * > ( p_cast - > operand ) , cast_type . get_container_element_type ( 0 ) ) ;
2022-11-27 07:56:53 +00:00
}
2023-06-24 18:03:28 +00:00
if ( p_cast - > operand - > type = = GDScriptParser : : Node : : DICTIONARY & & cast_type . has_container_element_types ( ) ) {
update_dictionary_literal_element_type ( static_cast < GDScriptParser : : DictionaryNode * > ( p_cast - > operand ) ,
cast_type . get_container_element_type_or_variant ( 0 ) , cast_type . get_container_element_type_or_variant ( 1 ) ) ;
}
2020-06-10 22:53:25 +00:00
if ( ! cast_type . is_variant ( ) ) {
2020-07-16 01:02:44 +00:00
GDScriptParser : : DataType op_type = p_cast - > operand - > get_datatype ( ) ;
2023-01-22 08:32:05 +00:00
if ( op_type . is_variant ( ) | | ! op_type . is_hard_type ( ) ) {
mark_node_unsafe ( p_cast ) ;
# ifdef DEBUG_ENABLED
2023-10-27 08:08:54 +00:00
parser - > push_warning ( p_cast , GDScriptWarning : : UNSAFE_CAST , cast_type . to_string ( ) ) ;
2023-01-22 08:32:05 +00:00
# endif
} else {
2020-07-16 01:02:44 +00:00
bool valid = false ;
2023-01-22 08:32:05 +00:00
if ( op_type . builtin_type = = Variant : : INT & & cast_type . kind = = GDScriptParser : : DataType : : ENUM ) {
mark_node_unsafe ( p_cast ) ;
valid = true ;
2024-04-12 14:49:21 +00:00
} else if ( op_type . kind = = GDScriptParser : : DataType : : ENUM & & cast_type . builtin_type = = Variant : : INT ) {
valid = true ;
2022-01-27 14:34:33 +00:00
} else if ( op_type . kind = = GDScriptParser : : DataType : : BUILTIN & & cast_type . kind = = GDScriptParser : : DataType : : BUILTIN ) {
2020-07-16 01:02:44 +00:00
valid = Variant : : can_convert ( op_type . builtin_type , cast_type . builtin_type ) ;
} else if ( op_type . kind ! = GDScriptParser : : DataType : : BUILTIN & & cast_type . kind ! = GDScriptParser : : DataType : : BUILTIN ) {
valid = is_type_compatible ( cast_type , op_type ) | | is_type_compatible ( op_type , cast_type ) ;
}
2020-06-10 22:53:25 +00:00
2023-01-22 08:32:05 +00:00
if ( ! valid ) {
2020-07-16 01:02:44 +00:00
push_error ( vformat ( R " (Invalid cast. Cannot convert from " % s " to " % s " .) " , op_type . to_string ( ) , cast_type . to_string ( ) ) , p_cast - > cast_type ) ;
2020-06-10 22:53:25 +00:00
}
}
2020-06-11 22:31:28 +00:00
}
2020-06-10 22:53:25 +00:00
}
void GDScriptAnalyzer : : reduce_dictionary ( GDScriptParser : : DictionaryNode * p_dictionary ) {
2022-12-06 02:46:47 +00:00
HashMap < Variant , GDScriptParser : : ExpressionNode * , VariantHasher , StringLikeVariantComparator > elements ;
2020-08-19 14:14:16 +00:00
2020-06-10 22:53:25 +00:00
for ( int i = 0 ; i < p_dictionary - > elements . size ( ) ; i + + ) {
const GDScriptParser : : DictionaryNode : : Pair & element = p_dictionary - > elements [ i ] ;
2020-07-16 01:02:44 +00:00
if ( p_dictionary - > style = = GDScriptParser : : DictionaryNode : : PYTHON_DICT ) {
reduce_expression ( element . key ) ;
}
2020-06-10 22:53:25 +00:00
reduce_expression ( element . value ) ;
2020-08-19 14:14:16 +00:00
if ( element . key - > is_constant ) {
if ( elements . has ( element . key - > reduced_value ) ) {
push_error ( vformat ( R " (Key " % s " was already used in this dictionary (at line %d).) " , element . key - > reduced_value , elements [ element . key - > reduced_value ] - > start_line ) , element . key ) ;
} else {
elements [ element . key - > reduced_value ] = element . value ;
}
}
2020-06-10 22:53:25 +00:00
}
// It's dictionary in any case.
GDScriptParser : : DataType dict_type ;
dict_type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
dict_type . kind = GDScriptParser : : DataType : : BUILTIN ;
dict_type . builtin_type = Variant : : DICTIONARY ;
dict_type . is_constant = true ;
p_dictionary - > set_datatype ( dict_type ) ;
}
void GDScriptAnalyzer : : reduce_get_node ( GDScriptParser : : GetNodeNode * p_get_node ) {
GDScriptParser : : DataType result ;
2023-06-22 09:19:14 +00:00
result . kind = GDScriptParser : : DataType : : VARIANT ;
if ( ! ClassDB : : is_parent_class ( parser - > current_class - > base_type . native_type , SNAME ( " Node " ) ) ) {
push_error ( vformat ( R " *(Cannot use shorthand " get_node ( ) " notation ( " % c " ) on a class that isn't a node.)* " , p_get_node - > use_dollar ? ' $ ' : ' % ' ) , p_get_node ) ;
p_get_node - > set_datatype ( result ) ;
return ;
}
2020-06-10 22:53:25 +00:00
2023-06-22 09:19:14 +00:00
if ( static_context ) {
push_error ( vformat ( R " *(Cannot use shorthand " get_node ( ) " notation ( " % c " ) in a static function.)* " , p_get_node - > use_dollar ? ' $ ' : ' % ' ) , p_get_node ) ;
p_get_node - > set_datatype ( result ) ;
return ;
2020-06-10 22:53:25 +00:00
}
2022-04-20 17:22:22 +00:00
mark_lambda_use_self ( ) ;
2023-06-22 09:19:14 +00:00
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
result . kind = GDScriptParser : : DataType : : NATIVE ;
result . builtin_type = Variant : : OBJECT ;
result . native_type = SNAME ( " Node " ) ;
2020-06-10 22:53:25 +00:00
p_get_node - > set_datatype ( result ) ;
}
2020-11-26 15:27:48 +00:00
GDScriptParser : : DataType GDScriptAnalyzer : : make_global_class_meta_type ( const StringName & p_class_name , const GDScriptParser : : Node * p_source ) {
2022-02-28 11:20:00 +00:00
GDScriptParser : : DataType type ;
2022-04-25 04:33:18 +00:00
String path = ScriptServer : : get_global_class_path ( p_class_name ) ;
String ext = path . get_extension ( ) ;
if ( ext = = GDScriptLanguage : : get_singleton ( ) - > get_extension ( ) ) {
2024-04-12 23:13:25 +00:00
Ref < GDScriptParserRef > ref = parser - > get_depended_parser_for ( path ) ;
2022-04-25 04:33:18 +00:00
if ( ref . is_null ( ) ) {
push_error ( vformat ( R " (Could not find script for class " % s " .) " , p_class_name ) , p_source ) ;
type . type_source = GDScriptParser : : DataType : : UNDETECTED ;
type . kind = GDScriptParser : : DataType : : VARIANT ;
return type ;
}
2022-12-11 02:57:35 +00:00
Error err = ref - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ;
2022-04-25 04:33:18 +00:00
if ( err ) {
push_error ( vformat ( R " (Could not resolve class " % s " , because of a parser error.) " , p_class_name ) , p_source ) ;
type . type_source = GDScriptParser : : DataType : : UNDETECTED ;
type . kind = GDScriptParser : : DataType : : VARIANT ;
return type ;
}
2022-11-25 10:21:15 +00:00
return ref - > get_parser ( ) - > head - > get_datatype ( ) ;
2022-04-25 04:33:18 +00:00
} else {
2023-01-11 23:03:53 +00:00
return make_script_meta_type ( ResourceLoader : : load ( path , " Script " ) ) ;
2020-11-26 15:27:48 +00:00
}
2020-06-10 22:53:25 +00:00
}
2024-08-03 03:55:13 +00:00
Ref < GDScriptParserRef > GDScriptAnalyzer : : ensure_cached_external_parser_for_class ( const GDScriptParser : : ClassNode * p_class , const GDScriptParser : : ClassNode * p_from_class , const char * p_context , const GDScriptParser : : Node * p_source ) {
2024-07-23 11:46:28 +00:00
if ( p_class = = nullptr ) {
return nullptr ;
}
if ( HashMap < const GDScriptParser : : ClassNode * , Ref < GDScriptParserRef > > : : Iterator E = external_class_parser_cache . find ( p_class ) ) {
return E - > value ;
}
2024-08-03 03:55:13 +00:00
if ( parser - > has_class ( p_class ) ) {
return nullptr ;
}
2024-07-23 11:46:28 +00:00
if ( p_from_class = = nullptr ) {
p_from_class = parser - > head ;
}
String script_path = p_class - > get_datatype ( ) . script_path ;
Ref < GDScriptParserRef > parser_ref ;
for ( const GDScriptParser : : ClassNode * look_class = p_from_class ; look_class ! = nullptr ; look_class = look_class - > base_type . class_type ) {
if ( parser - > has_class ( look_class ) ) {
2024-08-03 03:55:13 +00:00
parser_ref = find_cached_external_parser_for_class ( p_class , parser ) ;
2024-07-23 11:46:28 +00:00
if ( parser_ref . is_valid ( ) ) {
break ;
}
}
if ( HashMap < const GDScriptParser : : ClassNode * , Ref < GDScriptParserRef > > : : Iterator E = external_class_parser_cache . find ( look_class ) ) {
2024-08-03 03:55:13 +00:00
parser_ref = find_cached_external_parser_for_class ( p_class , E - > value ) ;
2024-07-23 11:46:28 +00:00
if ( parser_ref . is_valid ( ) ) {
break ;
}
}
String look_class_script_path = look_class - > get_datatype ( ) . script_path ;
if ( HashMap < String , Ref < GDScriptParserRef > > : : Iterator E = parser - > depended_parsers . find ( look_class_script_path ) ) {
2024-08-03 03:55:13 +00:00
parser_ref = find_cached_external_parser_for_class ( p_class , E - > value ) ;
2024-07-23 11:46:28 +00:00
if ( parser_ref . is_valid ( ) ) {
break ;
}
}
}
if ( parser_ref . is_null ( ) ) {
2024-08-03 03:55:13 +00:00
push_error ( vformat ( R " (Parser bug (please report): Could not find external parser for class " % s " . (%s)) " , p_class - > fqcn , p_context ) , p_source ) ;
// A null parser will be inserted into the cache, so this error won't spam for the same class.
2024-07-23 11:46:28 +00:00
// This is ok, the values of external_class_parser_cache are not assumed to be valid references.
}
external_class_parser_cache . insert ( p_class , parser_ref ) ;
return parser_ref ;
}
2024-08-03 03:55:13 +00:00
Ref < GDScriptParserRef > GDScriptAnalyzer : : find_cached_external_parser_for_class ( const GDScriptParser : : ClassNode * p_class , const Ref < GDScriptParserRef > & p_dependant_parser ) {
2024-07-23 11:46:28 +00:00
if ( p_dependant_parser . is_null ( ) ) {
return nullptr ;
}
if ( HashMap < const GDScriptParser : : ClassNode * , Ref < GDScriptParserRef > > : : Iterator E = p_dependant_parser - > get_analyzer ( ) - > external_class_parser_cache . find ( p_class ) ) {
if ( E - > value . is_valid ( ) ) {
// Silently ensure it's parsed.
E - > value - > raise_status ( GDScriptParserRef : : PARSED ) ;
if ( E - > value - > get_parser ( ) - > has_class ( p_class ) ) {
return E - > value ;
}
}
}
if ( p_dependant_parser - > get_parser ( ) - > has_class ( p_class ) ) {
return p_dependant_parser ;
}
// Silently ensure it's parsed.
p_dependant_parser - > raise_status ( GDScriptParserRef : : PARSED ) ;
2024-08-03 03:55:13 +00:00
return find_cached_external_parser_for_class ( p_class , p_dependant_parser - > get_parser ( ) ) ;
2024-07-23 11:46:28 +00:00
}
2024-08-03 03:55:13 +00:00
Ref < GDScriptParserRef > GDScriptAnalyzer : : find_cached_external_parser_for_class ( const GDScriptParser : : ClassNode * p_class , GDScriptParser * p_dependant_parser ) {
2024-07-23 11:46:28 +00:00
if ( p_dependant_parser = = nullptr ) {
return nullptr ;
}
String script_path = p_class - > get_datatype ( ) . script_path ;
if ( HashMap < String , Ref < GDScriptParserRef > > : : Iterator E = p_dependant_parser - > depended_parsers . find ( script_path ) ) {
if ( E - > value . is_valid ( ) ) {
// Silently ensure it's parsed.
E - > value - > raise_status ( GDScriptParserRef : : PARSED ) ;
if ( E - > value - > get_parser ( ) - > has_class ( p_class ) ) {
return E - > value ;
}
}
}
return nullptr ;
}
Ref < GDScript > GDScriptAnalyzer : : get_depended_shallow_script ( const String & p_path , Error & r_error ) {
// To keep a local cache of the parser for resolving external nodes later.
parser - > get_depended_parser_for ( p_path ) ;
Ref < GDScript > scr = GDScriptCache : : get_shallow_script ( p_path , r_error , parser - > script_path ) ;
return scr ;
}
2022-12-18 05:38:53 +00:00
void GDScriptAnalyzer : : reduce_identifier_from_base_set_class ( GDScriptParser : : IdentifierNode * p_identifier , GDScriptParser : : DataType p_identifier_datatype ) {
ERR_FAIL_NULL ( p_identifier ) ;
p_identifier - > set_datatype ( p_identifier_datatype ) ;
Error err = OK ;
2024-07-23 11:46:28 +00:00
Ref < GDScript > scr = get_depended_shallow_script ( p_identifier_datatype . script_path , err ) ;
2022-11-27 07:56:53 +00:00
if ( err ) {
push_error ( vformat ( R " (Error while getting cache for script " % s " .) " , p_identifier_datatype . script_path ) , p_identifier ) ;
return ;
}
p_identifier - > reduced_value = scr - > find_class ( p_identifier_datatype . class_type - > fqcn ) ;
2022-12-18 05:38:53 +00:00
p_identifier - > is_constant = true ;
}
2020-06-10 22:53:25 +00:00
void GDScriptAnalyzer : : reduce_identifier_from_base ( GDScriptParser : : IdentifierNode * p_identifier , GDScriptParser : : DataType * p_base ) {
2022-12-11 02:57:35 +00:00
if ( ! p_identifier - > get_datatype ( ) . has_no_type ( ) ) {
return ;
}
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType base ;
if ( p_base = = nullptr ) {
base = type_from_metatype ( parser - > current_class - > get_datatype ( ) ) ;
} else {
base = * p_base ;
}
2023-02-21 02:55:35 +00:00
StringName name = p_identifier - > name ;
2020-06-12 00:49:58 +00:00
2022-02-02 16:57:24 +00:00
if ( base . kind = = GDScriptParser : : DataType : : ENUM ) {
if ( base . is_meta_type ) {
if ( base . enum_values . has ( name ) ) {
2022-12-04 03:02:03 +00:00
p_identifier - > set_datatype ( type_from_metatype ( base ) ) ;
2022-02-02 16:57:24 +00:00
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = base . enum_values [ name ] ;
return ;
}
2022-12-04 03:02:03 +00:00
// Enum does not have this value, return.
return ;
2022-02-02 16:57:24 +00:00
} else {
push_error ( R " (Cannot get property from enum value.) " , p_identifier ) ;
return ;
}
}
2020-06-12 00:49:58 +00:00
if ( base . kind = = GDScriptParser : : DataType : : BUILTIN ) {
if ( base . is_meta_type ) {
bool valid = true ;
Variant result = Variant : : get_constant_value ( base . builtin_type , name , & valid ) ;
if ( valid ) {
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = result ;
2020-08-31 12:53:02 +00:00
p_identifier - > set_datatype ( type_from_variant ( result , p_identifier ) ) ;
2021-09-17 14:52:30 +00:00
} else if ( base . is_hard_type ( ) ) {
2023-02-10 08:07:01 +00:00
# ifdef SUGGEST_GODOT4_RENAMES
2024-01-05 10:56:42 +00:00
String rename_hint ;
if ( GLOBAL_GET ( GDScriptWarning : : get_settings_path_from_code ( GDScriptWarning : : RENAMED_IN_GODOT_4_HINT ) ) . booleanize ( ) ) {
2022-03-23 17:54:41 +00:00
const char * renamed_identifier_name = check_for_renamed_identifier ( name , p_identifier - > type ) ;
if ( renamed_identifier_name ) {
rename_hint = " " + vformat ( R " (Did you mean to use " % s " ?) " , renamed_identifier_name ) ;
}
}
push_error ( vformat ( R " (Cannot find constant " % s " on base " % s " .%s) " , name , base . to_string ( ) , rename_hint ) , p_identifier ) ;
# else
push_error ( vformat ( R " (Cannot find constant " % s " on base " % s " .) " , name , base . to_string ( ) ) , p_identifier ) ;
2023-02-10 08:07:01 +00:00
# endif // SUGGEST_GODOT4_RENAMES
2020-06-12 00:49:58 +00:00
}
} else {
2020-09-02 15:08:49 +00:00
switch ( base . builtin_type ) {
case Variant : : NIL : {
2021-09-17 14:52:30 +00:00
if ( base . is_hard_type ( ) ) {
2022-10-05 18:49:35 +00:00
push_error ( vformat ( R " (Cannot get property " % s " on a null object.) " , name ) , p_identifier ) ;
2021-09-17 14:52:30 +00:00
}
2020-06-12 00:49:58 +00:00
return ;
}
2020-09-02 15:08:49 +00:00
case Variant : : DICTIONARY : {
GDScriptParser : : DataType dummy ;
dummy . kind = GDScriptParser : : DataType : : VARIANT ;
p_identifier - > set_datatype ( dummy ) ;
return ;
}
default : {
Callable : : CallError temp ;
2020-11-09 03:19:09 +00:00
Variant dummy ;
Variant : : construct ( base . builtin_type , dummy , nullptr , 0 , temp ) ;
2020-09-02 15:08:49 +00:00
List < PropertyInfo > properties ;
dummy . get_property_list ( & properties ) ;
2021-07-16 03:45:57 +00:00
for ( const PropertyInfo & prop : properties ) {
2020-09-02 15:08:49 +00:00
if ( prop . name = = name ) {
p_identifier - > set_datatype ( type_from_property ( prop ) ) ;
return ;
}
}
2023-09-24 20:33:28 +00:00
if ( Variant : : has_builtin_method ( base . builtin_type , name ) ) {
p_identifier - > set_datatype ( make_callable_type ( Variant : : get_builtin_method_info ( base . builtin_type , name ) ) ) ;
return ;
}
2021-09-17 14:52:30 +00:00
if ( base . is_hard_type ( ) ) {
2023-02-10 08:07:01 +00:00
# ifdef SUGGEST_GODOT4_RENAMES
2024-01-05 10:56:42 +00:00
String rename_hint ;
if ( GLOBAL_GET ( GDScriptWarning : : get_settings_path_from_code ( GDScriptWarning : : RENAMED_IN_GODOT_4_HINT ) ) . booleanize ( ) ) {
2022-03-23 17:54:41 +00:00
const char * renamed_identifier_name = check_for_renamed_identifier ( name , p_identifier - > type ) ;
if ( renamed_identifier_name ) {
rename_hint = " " + vformat ( R " (Did you mean to use " % s " ?) " , renamed_identifier_name ) ;
}
}
push_error ( vformat ( R " (Cannot find property " % s " on base " % s " .%s) " , name , base . to_string ( ) , rename_hint ) , p_identifier ) ;
# else
2021-09-17 14:52:30 +00:00
push_error ( vformat ( R " (Cannot find property " % s " on base " % s " .) " , name , base . to_string ( ) ) , p_identifier ) ;
2023-02-10 08:07:01 +00:00
# endif // SUGGEST_GODOT4_RENAMES
2021-09-17 14:52:30 +00:00
}
2020-09-02 15:08:49 +00:00
}
2020-06-12 00:49:58 +00:00
}
}
return ;
}
2020-06-10 22:53:25 +00:00
GDScriptParser : : ClassNode * base_class = base . class_type ;
2022-12-18 05:38:53 +00:00
List < GDScriptParser : : ClassNode * > script_classes ;
bool is_base = true ;
2020-06-10 22:53:25 +00:00
2022-12-18 05:38:53 +00:00
if ( base_class ! = nullptr ) {
2024-07-23 11:46:28 +00:00
get_class_node_current_scope_classes ( base_class , & script_classes , p_identifier ) ;
2022-12-18 05:38:53 +00:00
}
2023-02-21 02:55:35 +00:00
bool is_constructor = base . is_meta_type & & p_identifier - > name = = SNAME ( " new " ) ;
2022-12-18 05:38:53 +00:00
for ( GDScriptParser : : ClassNode * script_class : script_classes ) {
if ( p_base = = nullptr & & script_class - > identifier & & script_class - > identifier - > name = = name ) {
reduce_identifier_from_base_set_class ( p_identifier , script_class - > get_datatype ( ) ) ;
2023-08-11 08:22:01 +00:00
if ( script_class - > outer ! = nullptr ) {
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_CLASS ;
}
2020-07-16 01:02:44 +00:00
return ;
}
2022-12-11 02:57:35 +00:00
2023-02-21 02:55:35 +00:00
if ( is_constructor ) {
name = " _init " ;
}
2022-12-18 05:38:53 +00:00
if ( script_class - > has_member ( name ) ) {
resolve_class_member ( script_class , name , p_identifier ) ;
2022-12-11 02:57:35 +00:00
2022-12-18 05:38:53 +00:00
GDScriptParser : : ClassNode : : Member member = script_class - > get_member ( name ) ;
2020-06-12 00:49:58 +00:00
switch ( member . type ) {
2022-12-18 05:38:53 +00:00
case GDScriptParser : : ClassNode : : Member : : CONSTANT : {
p_identifier - > set_datatype ( member . get_datatype ( ) ) ;
2020-06-12 00:49:58 +00:00
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = member . constant - > initializer - > reduced_value ;
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT ;
p_identifier - > constant_source = member . constant ;
2022-12-18 05:38:53 +00:00
return ;
}
case GDScriptParser : : ClassNode : : Member : : ENUM_VALUE : {
p_identifier - > set_datatype ( member . get_datatype ( ) ) ;
2020-06-12 00:49:58 +00:00
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = member . enum_value . value ;
2021-03-26 12:03:16 +00:00
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT ;
2022-12-18 05:38:53 +00:00
return ;
}
case GDScriptParser : : ClassNode : : Member : : ENUM : {
p_identifier - > set_datatype ( member . get_datatype ( ) ) ;
2022-12-27 03:06:11 +00:00
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = member . m_enum - > dictionary ;
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT ;
2022-12-04 03:02:03 +00:00
return ;
}
2022-12-18 05:38:53 +00:00
case GDScriptParser : : ClassNode : : Member : : VARIABLE : {
2023-04-19 14:10:35 +00:00
if ( is_base & & ( ! base . is_meta_type | | member . variable - > is_static ) ) {
2022-12-18 05:38:53 +00:00
p_identifier - > set_datatype ( member . get_datatype ( ) ) ;
2023-04-19 14:10:35 +00:00
p_identifier - > source = member . variable - > is_static ? GDScriptParser : : IdentifierNode : : STATIC_VARIABLE : GDScriptParser : : IdentifierNode : : MEMBER_VARIABLE ;
2022-12-18 05:38:53 +00:00
p_identifier - > variable_source = member . variable ;
member . variable - > usages + = 1 ;
return ;
}
} break ;
2022-12-11 02:57:35 +00:00
2022-12-18 05:38:53 +00:00
case GDScriptParser : : ClassNode : : Member : : SIGNAL : {
if ( is_base & & ! base . is_meta_type ) {
p_identifier - > set_datatype ( member . get_datatype ( ) ) ;
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_SIGNAL ;
2024-02-28 14:23:11 +00:00
p_identifier - > signal_source = member . signal ;
member . signal - > usages + = 1 ;
2022-12-18 05:38:53 +00:00
return ;
2022-12-04 21:55:40 +00:00
}
2022-12-18 05:38:53 +00:00
} break ;
case GDScriptParser : : ClassNode : : Member : : FUNCTION : {
2024-04-11 23:28:46 +00:00
if ( is_base & & ( ! base . is_meta_type | | member . function - > is_static | | is_constructor ) ) {
2022-12-18 05:38:53 +00:00
p_identifier - > set_datatype ( make_callable_type ( member . function - > info ) ) ;
2023-07-25 11:21:49 +00:00
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_FUNCTION ;
2024-05-01 13:49:12 +00:00
p_identifier - > function_source = member . function ;
p_identifier - > function_source_is_static = member . function - > is_static ;
2022-12-18 05:38:53 +00:00
return ;
}
} break ;
case GDScriptParser : : ClassNode : : Member : : CLASS : {
reduce_identifier_from_base_set_class ( p_identifier , member . get_datatype ( ) ) ;
2023-07-25 11:21:49 +00:00
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_CLASS ;
2022-12-18 05:38:53 +00:00
return ;
}
default : {
// Do nothing
2020-06-10 22:53:25 +00:00
}
}
}
2022-12-18 05:38:53 +00:00
if ( is_base ) {
is_base = script_class - > base_type . class_type ! = nullptr ;
if ( ! is_base & & p_base ! = nullptr ) {
break ;
}
}
2020-06-10 22:53:25 +00:00
}
2023-12-03 11:48:51 +00:00
// Check non-GDScript scripts.
Ref < Script > script_type = base . script_type ;
if ( base_class = = nullptr & & script_type . is_valid ( ) ) {
List < PropertyInfo > property_list ;
script_type - > get_script_property_list ( & property_list ) ;
for ( const PropertyInfo & property_info : property_list ) {
if ( property_info . name ! = p_identifier - > name ) {
continue ;
}
const GDScriptParser : : DataType property_type = GDScriptAnalyzer : : type_from_property ( property_info , false , false ) ;
p_identifier - > set_datatype ( property_type ) ;
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_VARIABLE ;
return ;
}
MethodInfo method_info = script_type - > get_method_info ( p_identifier - > name ) ;
if ( method_info . name = = p_identifier - > name ) {
p_identifier - > set_datatype ( make_callable_type ( method_info ) ) ;
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_FUNCTION ;
2024-05-01 13:49:12 +00:00
p_identifier - > function_source_is_static = method_info . flags & METHOD_FLAG_STATIC ;
2023-12-03 11:48:51 +00:00
return ;
}
List < MethodInfo > signal_list ;
script_type - > get_script_signal_list ( & signal_list ) ;
for ( const MethodInfo & signal_info : signal_list ) {
if ( signal_info . name ! = p_identifier - > name ) {
continue ;
}
const GDScriptParser : : DataType signal_type = make_signal_type ( signal_info ) ;
p_identifier - > set_datatype ( signal_type ) ;
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_SIGNAL ;
return ;
}
HashMap < StringName , Variant > constant_map ;
script_type - > get_constants ( & constant_map ) ;
if ( constant_map . has ( p_identifier - > name ) ) {
Variant constant = constant_map . get ( p_identifier - > name ) ;
p_identifier - > set_datatype ( make_builtin_meta_type ( constant . get_type ( ) ) ) ;
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT ;
return ;
}
}
2022-11-08 22:41:50 +00:00
// Check native members. No need for native class recursion because Node exposes all Object's properties.
2021-08-17 13:06:54 +00:00
const StringName & native = base . native_type ;
2020-06-10 22:53:25 +00:00
if ( class_exists ( native ) ) {
2023-02-21 02:55:35 +00:00
if ( is_constructor ) {
name = " _init " ;
}
2020-06-10 22:53:25 +00:00
MethodInfo method_info ;
2021-09-21 18:29:06 +00:00
if ( ClassDB : : has_property ( native , name ) ) {
StringName getter_name = ClassDB : : get_property_getter ( native , name ) ;
MethodBind * getter = ClassDB : : get_method ( native , getter_name ) ;
if ( getter ! = nullptr ) {
2023-01-30 17:50:08 +00:00
bool has_setter = ClassDB : : get_property_setter ( native , name ) ! = StringName ( ) ;
p_identifier - > set_datatype ( type_from_property ( getter - > get_return_info ( ) , false , ! has_setter ) ) ;
2022-04-20 17:22:22 +00:00
p_identifier - > source = GDScriptParser : : IdentifierNode : : INHERITED_VARIABLE ;
2021-09-21 18:29:06 +00:00
}
2020-06-10 22:53:25 +00:00
return ;
}
if ( ClassDB : : get_method_info ( native , name , & method_info ) ) {
// Method is callable.
p_identifier - > set_datatype ( make_callable_type ( method_info ) ) ;
2022-04-20 17:22:22 +00:00
p_identifier - > source = GDScriptParser : : IdentifierNode : : INHERITED_VARIABLE ;
2020-06-10 22:53:25 +00:00
return ;
}
if ( ClassDB : : get_signal ( native , name , & method_info ) ) {
// Signal is a type too.
p_identifier - > set_datatype ( make_signal_type ( method_info ) ) ;
2022-04-20 17:22:22 +00:00
p_identifier - > source = GDScriptParser : : IdentifierNode : : INHERITED_VARIABLE ;
2020-06-10 22:53:25 +00:00
return ;
}
2020-06-12 00:49:58 +00:00
if ( ClassDB : : has_enum ( native , name ) ) {
2022-12-04 03:02:03 +00:00
p_identifier - > set_datatype ( make_native_enum_type ( name , native ) ) ;
2022-04-20 17:22:22 +00:00
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT ;
2020-06-12 00:49:58 +00:00
return ;
}
2020-06-10 22:53:25 +00:00
bool valid = false ;
2022-12-04 03:02:03 +00:00
2022-05-09 09:47:10 +00:00
int64_t int_constant = ClassDB : : get_integer_constant ( native , name , & valid ) ;
2020-06-10 22:53:25 +00:00
if ( valid ) {
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = int_constant ;
2022-04-20 17:22:22 +00:00
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT ;
2022-12-04 03:02:03 +00:00
// Check whether this constant, which exists, belongs to an enum
StringName enum_name = ClassDB : : get_integer_constant_enum ( native , name ) ;
if ( enum_name ! = StringName ( ) ) {
p_identifier - > set_datatype ( make_native_enum_type ( enum_name , native , false ) ) ;
} else {
p_identifier - > set_datatype ( type_from_variant ( int_constant , p_identifier ) ) ;
}
2020-06-10 22:53:25 +00:00
}
}
}
2020-06-12 00:49:58 +00:00
void GDScriptAnalyzer : : reduce_identifier ( GDScriptParser : : IdentifierNode * p_identifier , bool can_be_builtin ) {
2022-12-04 03:02:03 +00:00
// TODO: This is an opportunity to further infer types.
2020-08-18 20:44:20 +00:00
2022-12-04 03:02:03 +00:00
// Check if we are inside an enum. This allows enum values to access other elements of the same enum.
2020-08-18 20:44:20 +00:00
if ( current_enum ) {
for ( int i = 0 ; i < current_enum - > values . size ( ) ; i + + ) {
const GDScriptParser : : EnumNode : : Value & element = current_enum - > values [ i ] ;
if ( element . identifier - > name = = p_identifier - > name ) {
2023-01-18 14:12:27 +00:00
StringName enum_name = current_enum - > identifier ? current_enum - > identifier - > name : UNNAMED_ENUM ;
2022-12-04 03:02:03 +00:00
GDScriptParser : : DataType type = make_enum_type ( enum_name , parser - > current_class - > fqcn , false ) ;
2020-08-18 20:44:20 +00:00
if ( element . parent_enum - > identifier ) {
type . enum_type = element . parent_enum - > identifier - > name ;
}
p_identifier - > set_datatype ( type ) ;
if ( element . resolved ) {
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = element . value ;
} else {
push_error ( R " (Cannot use another enum element before it was declared.) " , p_identifier ) ;
}
return ; // Found anyway.
}
}
}
2021-03-26 12:03:16 +00:00
bool found_source = false ;
2020-06-10 22:53:25 +00:00
// Check if identifier is local.
// If that's the case, the declaration already was solved before.
switch ( p_identifier - > source ) {
case GDScriptParser : : IdentifierNode : : FUNCTION_PARAMETER :
p_identifier - > set_datatype ( p_identifier - > parameter_source - > get_datatype ( ) ) ;
2021-03-26 12:03:16 +00:00
found_source = true ;
break ;
2020-06-10 22:53:25 +00:00
case GDScriptParser : : IdentifierNode : : LOCAL_CONSTANT :
case GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT :
p_identifier - > set_datatype ( p_identifier - > constant_source - > get_datatype ( ) ) ;
p_identifier - > is_constant = true ;
// TODO: Constant should have a value on the node itself.
p_identifier - > reduced_value = p_identifier - > constant_source - > initializer - > reduced_value ;
2021-03-26 12:03:16 +00:00
found_source = true ;
break ;
2022-11-21 19:38:02 +00:00
case GDScriptParser : : IdentifierNode : : MEMBER_SIGNAL :
2024-02-28 14:23:11 +00:00
p_identifier - > signal_source - > usages + + ;
[[fallthrough]] ;
2022-04-20 17:22:22 +00:00
case GDScriptParser : : IdentifierNode : : INHERITED_VARIABLE :
mark_lambda_use_self ( ) ;
break ;
2020-06-10 22:53:25 +00:00
case GDScriptParser : : IdentifierNode : : MEMBER_VARIABLE :
2022-04-20 17:22:22 +00:00
mark_lambda_use_self ( ) ;
2020-06-11 22:31:28 +00:00
p_identifier - > variable_source - > usages + + ;
[[fallthrough]] ;
2023-04-19 14:10:35 +00:00
case GDScriptParser : : IdentifierNode : : STATIC_VARIABLE :
2020-06-11 22:31:28 +00:00
case GDScriptParser : : IdentifierNode : : LOCAL_VARIABLE :
2020-06-10 22:53:25 +00:00
p_identifier - > set_datatype ( p_identifier - > variable_source - > get_datatype ( ) ) ;
2021-03-26 12:03:16 +00:00
found_source = true ;
2024-04-09 17:15:51 +00:00
# ifdef DEBUG_ENABLED
if ( p_identifier - > variable_source & & p_identifier - > variable_source - > assignments = = 0 & & ! ( p_identifier - > get_datatype ( ) . is_hard_type ( ) & & p_identifier - > get_datatype ( ) . kind = = GDScriptParser : : DataType : : BUILTIN ) ) {
parser - > push_warning ( p_identifier , GDScriptWarning : : UNASSIGNED_VARIABLE , p_identifier - > name ) ;
}
# endif
2021-03-26 12:03:16 +00:00
break ;
2020-06-10 22:53:25 +00:00
case GDScriptParser : : IdentifierNode : : LOCAL_ITERATOR :
p_identifier - > set_datatype ( p_identifier - > bind_source - > get_datatype ( ) ) ;
2021-03-26 12:03:16 +00:00
found_source = true ;
break ;
2020-06-10 22:53:25 +00:00
case GDScriptParser : : IdentifierNode : : LOCAL_BIND : {
GDScriptParser : : DataType result = p_identifier - > bind_source - > get_datatype ( ) ;
result . is_constant = true ;
p_identifier - > set_datatype ( result ) ;
2021-03-26 12:03:16 +00:00
found_source = true ;
} break ;
2020-06-10 22:53:25 +00:00
case GDScriptParser : : IdentifierNode : : UNDEFINED_SOURCE :
2023-07-25 11:21:49 +00:00
case GDScriptParser : : IdentifierNode : : MEMBER_FUNCTION :
case GDScriptParser : : IdentifierNode : : MEMBER_CLASS :
2020-06-10 22:53:25 +00:00
break ;
}
2023-07-25 11:21:49 +00:00
# ifdef DEBUG_ENABLED
if ( ! found_source & & p_identifier - > suite ! = nullptr & & p_identifier - > suite - > has_local ( p_identifier - > name ) ) {
parser - > push_warning ( p_identifier , GDScriptWarning : : CONFUSABLE_LOCAL_USAGE , p_identifier - > name ) ;
}
# endif
2020-06-10 22:53:25 +00:00
// Not a local, so check members.
2023-09-21 09:42:55 +00:00
2021-03-26 12:03:16 +00:00
if ( ! found_source ) {
reduce_identifier_from_base ( p_identifier ) ;
if ( p_identifier - > source ! = GDScriptParser : : IdentifierNode : : UNDEFINED_SOURCE | | p_identifier - > get_datatype ( ) . is_set ( ) ) {
// Found.
found_source = true ;
}
}
if ( found_source ) {
2024-05-01 13:49:12 +00:00
const bool source_is_instance_variable = p_identifier - > source = = GDScriptParser : : IdentifierNode : : MEMBER_VARIABLE | | p_identifier - > source = = GDScriptParser : : IdentifierNode : : INHERITED_VARIABLE ;
const bool source_is_instance_function = p_identifier - > source = = GDScriptParser : : IdentifierNode : : MEMBER_FUNCTION & & ! p_identifier - > function_source_is_static ;
const bool source_is_signal = p_identifier - > source = = GDScriptParser : : IdentifierNode : : MEMBER_SIGNAL ;
if ( static_context & & ( source_is_instance_variable | | source_is_instance_function | | source_is_signal ) ) {
2023-10-17 09:46:41 +00:00
// Get the parent function above any lambda.
GDScriptParser : : FunctionNode * parent_function = parser - > current_function ;
while ( parent_function & & parent_function - > source_lambda ) {
parent_function = parent_function - > source_lambda - > parent_function ;
}
2024-05-01 13:49:12 +00:00
String source_type ;
if ( source_is_instance_variable ) {
source_type = " non-static variable " ;
} else if ( source_is_instance_function ) {
source_type = " non-static function " ;
} else { // source_is_signal
source_type = " signal " ;
}
2023-10-17 09:46:41 +00:00
if ( parent_function ) {
2024-05-01 13:49:12 +00:00
push_error ( vformat ( R " *(Cannot access %s " % s " from the static function " % s ( ) " .)* " , source_type , p_identifier - > name , parent_function - > identifier - > name ) , p_identifier ) ;
2023-04-19 14:10:35 +00:00
} else {
2024-05-01 13:49:12 +00:00
push_error ( vformat ( R " *(Cannot access %s " % s " from a static variable initializer.)* " , source_type , p_identifier - > name ) , p_identifier ) ;
2022-04-20 17:22:22 +00:00
}
2021-03-26 12:03:16 +00:00
}
2023-08-23 09:37:18 +00:00
if ( current_lambda ! = nullptr ) {
2024-05-01 13:49:12 +00:00
// If the identifier is a member variable (including the native class properties), member function, or a signal,
// we consider the lambda to be using `self`, so we keep a reference to the current instance.
if ( source_is_instance_variable | | source_is_instance_function | | source_is_signal ) {
2022-04-20 17:22:22 +00:00
mark_lambda_use_self ( ) ;
return ; // No need to capture.
}
2024-05-22 07:07:31 +00:00
switch ( p_identifier - > source ) {
case GDScriptParser : : IdentifierNode : : FUNCTION_PARAMETER :
case GDScriptParser : : IdentifierNode : : LOCAL_VARIABLE :
case GDScriptParser : : IdentifierNode : : LOCAL_ITERATOR :
case GDScriptParser : : IdentifierNode : : LOCAL_BIND :
break ; // Need to capture.
case GDScriptParser : : IdentifierNode : : UNDEFINED_SOURCE : // A global.
case GDScriptParser : : IdentifierNode : : LOCAL_CONSTANT :
case GDScriptParser : : IdentifierNode : : MEMBER_VARIABLE :
case GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT :
case GDScriptParser : : IdentifierNode : : MEMBER_FUNCTION :
case GDScriptParser : : IdentifierNode : : MEMBER_SIGNAL :
case GDScriptParser : : IdentifierNode : : MEMBER_CLASS :
case GDScriptParser : : IdentifierNode : : INHERITED_VARIABLE :
case GDScriptParser : : IdentifierNode : : STATIC_VARIABLE :
return ; // No need to capture.
2022-04-20 17:22:22 +00:00
}
2023-08-23 09:37:18 +00:00
GDScriptParser : : FunctionNode * function_test = current_lambda - > function ;
2022-04-20 17:22:22 +00:00
// Make sure we aren't capturing variable in the same lambda.
// This also add captures for nested lambdas.
while ( function_test ! = nullptr & & function_test ! = p_identifier - > source_function & & function_test - > source_lambda ! = nullptr & & ! function_test - > source_lambda - > captures_indices . has ( p_identifier - > name ) ) {
function_test - > source_lambda - > captures_indices [ p_identifier - > name ] = function_test - > source_lambda - > captures . size ( ) ;
function_test - > source_lambda - > captures . push_back ( p_identifier ) ;
function_test = function_test - > source_lambda - > parent_function ;
}
2021-03-26 12:03:16 +00:00
}
2022-04-20 17:22:22 +00:00
2020-06-10 22:53:25 +00:00
return ;
}
StringName name = p_identifier - > name ;
p_identifier - > source = GDScriptParser : : IdentifierNode : : UNDEFINED_SOURCE ;
2023-09-21 09:42:55 +00:00
// Not a local or a member, so check globals.
2020-08-25 19:00:56 +00:00
Variant : : Type builtin_type = GDScriptParser : : get_builtin_type ( name ) ;
2023-09-21 09:42:55 +00:00
if ( builtin_type < Variant : : VARIANT_MAX ) {
2020-06-12 00:49:58 +00:00
if ( can_be_builtin ) {
2020-08-25 19:00:56 +00:00
p_identifier - > set_datatype ( make_builtin_meta_type ( builtin_type ) ) ;
2020-06-12 00:49:58 +00:00
return ;
} else {
push_error ( R " (Builtin type cannot be used as a name on its own.) " , p_identifier ) ;
}
2020-06-10 22:53:25 +00:00
}
if ( class_exists ( name ) ) {
p_identifier - > set_datatype ( make_native_meta_type ( name ) ) ;
return ;
}
if ( ScriptServer : : is_global_class ( name ) ) {
2020-11-26 15:27:48 +00:00
p_identifier - > set_datatype ( make_global_class_meta_type ( name , p_identifier ) ) ;
2020-06-10 22:53:25 +00:00
return ;
}
2020-08-31 23:08:46 +00:00
// Try singletons.
// Do this before globals because this might be a singleton loading another one before it's compiled.
if ( ProjectSettings : : get_singleton ( ) - > has_autoload ( name ) ) {
const ProjectSettings : : AutoloadInfo & autoload = ProjectSettings : : get_singleton ( ) - > get_autoload ( name ) ;
if ( autoload . is_singleton ) {
// Singleton exists, so it's at least a Node.
GDScriptParser : : DataType result ;
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
2023-10-04 12:03:53 +00:00
result . kind = GDScriptParser : : DataType : : NATIVE ;
result . builtin_type = Variant : : OBJECT ;
result . native_type = SNAME ( " Node " ) ;
2022-10-09 16:41:28 +00:00
if ( ResourceLoader : : get_resource_type ( autoload . path ) = = " GDScript " ) {
2024-04-12 23:13:25 +00:00
Ref < GDScriptParserRef > singl_parser = parser - > get_depended_parser_for ( autoload . path ) ;
2022-09-29 09:53:28 +00:00
if ( singl_parser . is_valid ( ) ) {
2022-12-11 02:57:35 +00:00
Error err = singl_parser - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ;
2020-08-31 23:08:46 +00:00
if ( err = = OK ) {
2022-09-29 09:53:28 +00:00
result = type_from_metatype ( singl_parser - > get_parser ( ) - > head - > get_datatype ( ) ) ;
2020-08-31 23:08:46 +00:00
}
}
2022-11-22 04:24:10 +00:00
} else if ( ResourceLoader : : get_resource_type ( autoload . path ) = = " PackedScene " ) {
2022-12-20 04:09:32 +00:00
if ( GDScriptLanguage : : get_singleton ( ) - > has_any_global_constant ( name ) ) {
Variant constant = GDScriptLanguage : : get_singleton ( ) - > get_any_global_constant ( name ) ;
2022-11-23 23:13:13 +00:00
Node * node = Object : : cast_to < Node > ( constant ) ;
if ( node ! = nullptr ) {
2022-12-01 10:20:42 +00:00
Ref < GDScript > scr = node - > get_script ( ) ;
2022-11-23 23:13:13 +00:00
if ( scr . is_valid ( ) ) {
2024-04-12 23:13:25 +00:00
Ref < GDScriptParserRef > singl_parser = parser - > get_depended_parser_for ( scr - > get_script_path ( ) ) ;
2022-11-23 23:13:13 +00:00
if ( singl_parser . is_valid ( ) ) {
2022-12-11 02:57:35 +00:00
Error err = singl_parser - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ;
2022-11-23 23:13:13 +00:00
if ( err = = OK ) {
result = type_from_metatype ( singl_parser - > get_parser ( ) - > head - > get_datatype ( ) ) ;
}
}
2022-11-22 04:24:10 +00:00
}
}
}
2020-08-31 23:08:46 +00:00
}
result . is_constant = true ;
p_identifier - > set_datatype ( result ) ;
return ;
}
}
2023-02-19 15:57:09 +00:00
if ( CoreConstants : : is_global_constant ( name ) ) {
int index = CoreConstants : : get_global_constant_index ( name ) ;
StringName enum_name = CoreConstants : : get_global_constant_enum ( index ) ;
int64_t value = CoreConstants : : get_global_constant_value ( index ) ;
if ( enum_name ! = StringName ( ) ) {
p_identifier - > set_datatype ( make_global_enum_type ( enum_name , StringName ( ) , false ) ) ;
} else {
p_identifier - > set_datatype ( type_from_variant ( value , p_identifier ) ) ;
}
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = value ;
return ;
}
2022-12-20 04:09:32 +00:00
if ( GDScriptLanguage : : get_singleton ( ) - > has_any_global_constant ( name ) ) {
Variant constant = GDScriptLanguage : : get_singleton ( ) - > get_any_global_constant ( name ) ;
2020-08-31 12:53:02 +00:00
p_identifier - > set_datatype ( type_from_variant ( constant , p_identifier ) ) ;
2020-06-10 22:53:25 +00:00
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = constant ;
return ;
}
2023-02-19 15:57:09 +00:00
if ( CoreConstants : : is_global_enum ( name ) ) {
p_identifier - > set_datatype ( make_global_enum_type ( name , StringName ( ) , true ) ) ;
if ( ! can_be_builtin ) {
push_error ( vformat ( R " (Global enum " % s " cannot be used on its own.) " , name ) , p_identifier ) ;
}
return ;
}
2024-01-05 10:56:42 +00:00
if ( Variant : : has_utility_function ( name ) | | GDScriptUtilityFunctions : : function_exists ( name ) ) {
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = Callable ( memnew ( GDScriptUtilityCallable ( name ) ) ) ;
MethodInfo method_info ;
if ( GDScriptUtilityFunctions : : function_exists ( name ) ) {
method_info = GDScriptUtilityFunctions : : get_function_info ( name ) ;
} else {
method_info = Variant : : get_utility_function_info ( name ) ;
}
p_identifier - > set_datatype ( make_callable_type ( method_info ) ) ;
return ;
}
2023-02-19 15:57:09 +00:00
// Allow "Variant" here since it might be used for nested enums.
if ( can_be_builtin & & name = = SNAME ( " Variant " ) ) {
GDScriptParser : : DataType variant ;
variant . kind = GDScriptParser : : DataType : : VARIANT ;
variant . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
variant . is_meta_type = true ;
variant . is_pseudo_type = true ;
p_identifier - > set_datatype ( variant ) ;
return ;
}
2020-06-10 22:53:25 +00:00
// Not found.
2023-02-10 08:07:01 +00:00
# ifdef SUGGEST_GODOT4_RENAMES
2024-01-05 10:56:42 +00:00
String rename_hint ;
if ( GLOBAL_GET ( GDScriptWarning : : get_settings_path_from_code ( GDScriptWarning : : RENAMED_IN_GODOT_4_HINT ) ) . booleanize ( ) ) {
const char * renamed_identifier_name = check_for_renamed_identifier ( name , p_identifier - > type ) ;
if ( renamed_identifier_name ) {
rename_hint = " " + vformat ( R " (Did you mean to use " % s " ?) " , renamed_identifier_name ) ;
2022-03-23 17:54:41 +00:00
}
2024-01-05 10:56:42 +00:00
}
push_error ( vformat ( R " (Identifier " % s " not declared in the current scope.%s) " , name , rename_hint ) , p_identifier ) ;
2022-03-23 17:54:41 +00:00
# else
2024-01-05 10:56:42 +00:00
push_error ( vformat ( R " (Identifier " % s " not declared in the current scope.) " , name ) , p_identifier ) ;
2023-02-10 08:07:01 +00:00
# endif // SUGGEST_GODOT4_RENAMES
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType dummy ;
dummy . kind = GDScriptParser : : DataType : : VARIANT ;
p_identifier - > set_datatype ( dummy ) ; // Just so type is set to something.
}
2021-03-26 12:03:16 +00:00
void GDScriptAnalyzer : : reduce_lambda ( GDScriptParser : : LambdaNode * p_lambda ) {
// Lambda is always a Callable.
GDScriptParser : : DataType lambda_type ;
lambda_type . type_source = GDScriptParser : : DataType : : ANNOTATED_INFERRED ;
lambda_type . kind = GDScriptParser : : DataType : : BUILTIN ;
lambda_type . builtin_type = Variant : : CALLABLE ;
p_lambda - > set_datatype ( lambda_type ) ;
if ( p_lambda - > function = = nullptr ) {
return ;
}
2023-08-23 09:37:18 +00:00
GDScriptParser : : LambdaNode * previous_lambda = current_lambda ;
current_lambda = p_lambda ;
2022-12-28 05:41:03 +00:00
resolve_function_signature ( p_lambda - > function , p_lambda , true ) ;
2023-08-23 09:37:18 +00:00
current_lambda = previous_lambda ;
2021-03-26 12:03:16 +00:00
2023-08-23 09:37:18 +00:00
pending_body_resolution_lambdas . push_back ( p_lambda ) ;
2021-03-26 12:03:16 +00:00
}
2020-06-10 22:53:25 +00:00
void GDScriptAnalyzer : : reduce_literal ( GDScriptParser : : LiteralNode * p_literal ) {
p_literal - > reduced_value = p_literal - > value ;
p_literal - > is_constant = true ;
2020-08-31 12:53:02 +00:00
p_literal - > set_datatype ( type_from_variant ( p_literal - > reduced_value , p_literal ) ) ;
2020-06-10 22:53:25 +00:00
}
void GDScriptAnalyzer : : reduce_preload ( GDScriptParser : : PreloadNode * p_preload ) {
2020-08-19 13:45:00 +00:00
if ( ! p_preload - > path ) {
return ;
}
reduce_expression ( p_preload - > path ) ;
if ( ! p_preload - > path - > is_constant ) {
push_error ( " Preloaded path must be a constant string. " , p_preload - > path ) ;
return ;
}
if ( p_preload - > path - > reduced_value . get_type ( ) ! = Variant : : STRING ) {
push_error ( " Preloaded path must be a constant string. " , p_preload - > path ) ;
} else {
p_preload - > resolved_path = p_preload - > path - > reduced_value ;
2024-05-13 14:59:43 +00:00
// TODO: Save this as script dependency.
2021-08-29 23:43:47 +00:00
if ( p_preload - > resolved_path . is_relative_path ( ) ) {
2022-08-30 00:34:01 +00:00
p_preload - > resolved_path = parser - > script_path . get_base_dir ( ) . path_join ( p_preload - > resolved_path ) ;
2020-08-19 13:45:00 +00:00
}
p_preload - > resolved_path = p_preload - > resolved_path . simplify_path ( ) ;
2022-08-31 15:52:37 +00:00
if ( ! ResourceLoader : : exists ( p_preload - > resolved_path ) ) {
2022-10-16 03:20:57 +00:00
Ref < FileAccess > file_check = FileAccess : : create ( FileAccess : : ACCESS_RESOURCES ) ;
if ( file_check - > file_exists ( p_preload - > resolved_path ) ) {
push_error ( vformat ( R " (Preload file " % s " has no resource loaders (unrecognized file extension).) " , p_preload - > resolved_path ) , p_preload - > path ) ;
} else {
push_error ( vformat ( R " (Preload file " % s " does not exist.) " , p_preload - > resolved_path ) , p_preload - > path ) ;
}
2020-08-19 13:45:00 +00:00
} else {
// TODO: Don't load if validating: use completion cache.
2022-10-09 16:41:28 +00:00
2023-11-29 10:35:54 +00:00
// Must load GDScript separately to permit cyclic references
// as ResourceLoader::load() detects and rejects those.
2024-06-25 12:12:40 +00:00
const String & res_type = ResourceLoader : : get_resource_type ( p_preload - > resolved_path ) ;
if ( res_type = = " GDScript " ) {
2022-10-09 16:41:28 +00:00
Error err = OK ;
2024-07-23 11:46:28 +00:00
Ref < GDScript > res = get_depended_shallow_script ( p_preload - > resolved_path , err ) ;
2022-10-09 16:41:28 +00:00
p_preload - > resource = res ;
if ( err ! = OK ) {
push_error ( vformat ( R " (Could not preload resource script " % s " .) " , p_preload - > resolved_path ) , p_preload - > path ) ;
}
} else {
2024-06-25 12:12:40 +00:00
Error err = OK ;
p_preload - > resource = ResourceLoader : : load ( p_preload - > resolved_path , res_type , ResourceFormatLoader : : CACHE_MODE_REUSE , & err ) ;
if ( err = = ERR_BUSY ) {
p_preload - > resource = ResourceLoader : : ensure_resource_ref_override_for_outer_load ( p_preload - > resolved_path , res_type ) ;
}
2022-10-09 16:41:28 +00:00
if ( p_preload - > resource . is_null ( ) ) {
push_error ( vformat ( R " (Could not preload resource file " % s " .) " , p_preload - > resolved_path ) , p_preload - > path ) ;
}
2020-08-19 13:45:00 +00:00
}
}
}
2020-06-10 22:53:25 +00:00
p_preload - > is_constant = true ;
p_preload - > reduced_value = p_preload - > resource ;
2020-08-31 12:53:02 +00:00
p_preload - > set_datatype ( type_from_variant ( p_preload - > reduced_value , p_preload ) ) ;
2024-08-03 03:55:13 +00:00
// TODO: Not sure if this is necessary anymore.
// 'type_from_variant()' should call 'resolve_class_inheritance()' which would call 'ensure_cached_external_parser_for_class()'
// Better safe than sorry.
ensure_cached_external_parser_for_class ( p_preload - > get_datatype ( ) . class_type , nullptr , " Trying to resolve preload " , p_preload ) ;
2020-06-10 22:53:25 +00:00
}
void GDScriptAnalyzer : : reduce_self ( GDScriptParser : : SelfNode * p_self ) {
p_self - > is_constant = false ;
p_self - > set_datatype ( type_from_metatype ( parser - > current_class - > get_datatype ( ) ) ) ;
2022-04-20 17:22:22 +00:00
mark_lambda_use_self ( ) ;
2020-06-10 22:53:25 +00:00
}
2023-02-19 15:57:09 +00:00
void GDScriptAnalyzer : : reduce_subscript ( GDScriptParser : : SubscriptNode * p_subscript , bool p_can_be_pseudo_type ) {
2021-06-24 20:03:40 +00:00
if ( p_subscript - > base = = nullptr ) {
return ;
}
2020-06-12 00:49:58 +00:00
if ( p_subscript - > base - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
reduce_identifier ( static_cast < GDScriptParser : : IdentifierNode * > ( p_subscript - > base ) , true ) ;
2023-02-19 15:57:09 +00:00
} else if ( p_subscript - > base - > type = = GDScriptParser : : Node : : SUBSCRIPT ) {
reduce_subscript ( static_cast < GDScriptParser : : SubscriptNode * > ( p_subscript - > base ) , true ) ;
2020-06-12 00:49:58 +00:00
} else {
reduce_expression ( p_subscript - > base ) ;
}
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType result_type ;
if ( p_subscript - > is_attribute ) {
2020-07-06 15:24:24 +00:00
if ( p_subscript - > attribute = = nullptr ) {
return ;
}
2022-12-11 12:59:43 +00:00
GDScriptParser : : DataType base_type = p_subscript - > base - > get_datatype ( ) ;
2022-12-11 02:57:35 +00:00
bool valid = false ;
2024-05-30 12:52:42 +00:00
2022-12-11 02:57:35 +00:00
// If the base is a metatype, use the analyzer instead.
2024-05-30 12:52:42 +00:00
if ( p_subscript - > base - > is_constant & & ! base_type . is_meta_type ) {
// GH-92534. If the base is a GDScript, use the analyzer instead.
bool base_is_gdscript = false ;
if ( p_subscript - > base - > reduced_value . get_type ( ) = = Variant : : OBJECT ) {
Ref < GDScript > gdscript = Object : : cast_to < GDScript > ( p_subscript - > base - > reduced_value . get_validated_object ( ) ) ;
if ( gdscript . is_valid ( ) ) {
base_is_gdscript = true ;
// Makes a metatype from a constant GDScript, since `base_type` is not a metatype.
GDScriptParser : : DataType base_type_meta = type_from_variant ( gdscript , p_subscript ) ;
// First try to reduce the attribute from the metatype.
reduce_identifier_from_base ( p_subscript - > attribute , & base_type_meta ) ;
GDScriptParser : : DataType attr_type = p_subscript - > attribute - > get_datatype ( ) ;
if ( attr_type . is_set ( ) ) {
valid = ! attr_type . is_pseudo_type | | p_can_be_pseudo_type ;
result_type = attr_type ;
p_subscript - > is_constant = p_subscript - > attribute - > is_constant ;
p_subscript - > reduced_value = p_subscript - > attribute - > reduced_value ;
}
if ( ! valid ) {
// If unsuccessful, reset and return to the normal route.
p_subscript - > attribute - > set_datatype ( GDScriptParser : : DataType ( ) ) ;
}
}
2020-06-10 22:53:25 +00:00
}
2024-05-30 12:52:42 +00:00
if ( ! base_is_gdscript ) {
// Just try to get it.
Variant value = p_subscript - > base - > reduced_value . get_named ( p_subscript - > attribute - > name , valid ) ;
if ( valid ) {
p_subscript - > is_constant = true ;
p_subscript - > reduced_value = value ;
result_type = type_from_variant ( value , p_subscript ) ;
}
}
}
if ( valid ) {
// Do nothing.
2022-12-11 02:57:35 +00:00
} else if ( base_type . is_variant ( ) | | ! base_type . is_hard_type ( ) ) {
2023-02-19 15:57:09 +00:00
valid = ! base_type . is_pseudo_type | | p_can_be_pseudo_type ;
2022-12-11 02:57:35 +00:00
result_type . kind = GDScriptParser : : DataType : : VARIANT ;
2023-02-19 15:57:09 +00:00
if ( base_type . is_variant ( ) & & base_type . is_hard_type ( ) & & base_type . is_meta_type & & base_type . is_pseudo_type ) {
// Special case: it may be a global enum with pseudo base (e.g. Variant.Type).
String enum_name ;
if ( p_subscript - > base - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
enum_name = String ( static_cast < GDScriptParser : : IdentifierNode * > ( p_subscript - > base ) - > name ) + ENUM_SEPARATOR + String ( p_subscript - > attribute - > name ) ;
}
if ( CoreConstants : : is_global_enum ( enum_name ) ) {
result_type = make_global_enum_type ( enum_name , StringName ( ) ) ;
} else {
valid = false ;
mark_node_unsafe ( p_subscript ) ;
}
} else {
mark_node_unsafe ( p_subscript ) ;
}
2020-06-10 22:53:25 +00:00
} else {
2022-12-11 02:57:35 +00:00
reduce_identifier_from_base ( p_subscript - > attribute , & base_type ) ;
GDScriptParser : : DataType attr_type = p_subscript - > attribute - > get_datatype ( ) ;
if ( attr_type . is_set ( ) ) {
2023-06-24 18:03:28 +00:00
if ( base_type . builtin_type = = Variant : : DICTIONARY & & base_type . has_container_element_types ( ) ) {
Variant : : Type key_type = base_type . get_container_element_type_or_variant ( 0 ) . builtin_type ;
valid = key_type = = Variant : : NIL | | key_type = = Variant : : STRING | | key_type = = Variant : : STRING_NAME ;
if ( base_type . has_container_element_type ( 1 ) ) {
result_type = base_type . get_container_element_type ( 1 ) ;
result_type . type_source = base_type . type_source ;
} else {
result_type . builtin_type = Variant : : NIL ;
result_type . kind = GDScriptParser : : DataType : : VARIANT ;
result_type . type_source = GDScriptParser : : DataType : : UNDETECTED ;
}
} else {
valid = ! attr_type . is_pseudo_type | | p_can_be_pseudo_type ;
result_type = attr_type ;
p_subscript - > is_constant = p_subscript - > attribute - > is_constant ;
p_subscript - > reduced_value = p_subscript - > attribute - > reduced_value ;
}
2022-12-18 23:36:41 +00:00
} else if ( ! base_type . is_meta_type | | ! base_type . is_constant ) {
2022-12-11 02:57:35 +00:00
valid = base_type . kind ! = GDScriptParser : : DataType : : BUILTIN ;
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2022-12-11 02:57:35 +00:00
if ( valid ) {
parser - > push_warning ( p_subscript , GDScriptWarning : : UNSAFE_PROPERTY_ACCESS , p_subscript - > attribute - > name , base_type . to_string ( ) ) ;
2020-06-10 22:53:25 +00:00
}
2022-12-11 02:57:35 +00:00
# endif
result_type . kind = GDScriptParser : : DataType : : VARIANT ;
2023-01-24 23:45:40 +00:00
mark_node_unsafe ( p_subscript ) ;
2020-06-10 22:53:25 +00:00
}
}
2024-05-30 12:52:42 +00:00
2022-12-11 02:57:35 +00:00
if ( ! valid ) {
2023-02-19 15:57:09 +00:00
GDScriptParser : : DataType attr_type = p_subscript - > attribute - > get_datatype ( ) ;
if ( ! p_can_be_pseudo_type & & ( attr_type . is_pseudo_type | | result_type . is_pseudo_type ) ) {
push_error ( vformat ( R " (Type " % s " in base " % s " cannot be used on its own.) " , p_subscript - > attribute - > name , type_from_metatype ( base_type ) . to_string ( ) ) , p_subscript - > attribute ) ;
} else {
push_error ( vformat ( R " (Cannot find member " % s " in base " % s " .) " , p_subscript - > attribute - > name , type_from_metatype ( base_type ) . to_string ( ) ) , p_subscript - > attribute ) ;
}
2022-12-11 02:57:35 +00:00
result_type . kind = GDScriptParser : : DataType : : VARIANT ;
}
2020-06-10 22:53:25 +00:00
} else {
2021-04-23 18:42:33 +00:00
if ( p_subscript - > index = = nullptr ) {
return ;
}
reduce_expression ( p_subscript - > index ) ;
2020-06-10 22:53:25 +00:00
if ( p_subscript - > base - > is_constant & & p_subscript - > index - > is_constant ) {
// Just try to get it.
bool valid = false ;
2024-05-30 12:52:42 +00:00
// TODO: Check if `p_subscript->base->reduced_value` is GDScript.
2020-06-10 22:53:25 +00:00
Variant value = p_subscript - > base - > reduced_value . get ( p_subscript - > index - > reduced_value , & valid ) ;
if ( ! valid ) {
push_error ( vformat ( R " (Cannot get index " % s " from " % s " .) " , p_subscript - > index - > reduced_value , p_subscript - > base - > reduced_value ) , p_subscript - > index ) ;
2023-01-08 03:41:06 +00:00
result_type . kind = GDScriptParser : : DataType : : VARIANT ;
2020-06-10 22:53:25 +00:00
} else {
p_subscript - > is_constant = true ;
p_subscript - > reduced_value = value ;
2020-08-31 12:53:02 +00:00
result_type = type_from_variant ( value , p_subscript ) ;
2020-06-10 22:53:25 +00:00
}
} else {
GDScriptParser : : DataType base_type = p_subscript - > base - > get_datatype ( ) ;
GDScriptParser : : DataType index_type = p_subscript - > index - > get_datatype ( ) ;
if ( base_type . is_variant ( ) ) {
result_type . kind = GDScriptParser : : DataType : : VARIANT ;
mark_node_unsafe ( p_subscript ) ;
} else {
if ( base_type . kind = = GDScriptParser : : DataType : : BUILTIN & & ! index_type . is_variant ( ) ) {
// Check if indexing is valid.
bool error = index_type . kind ! = GDScriptParser : : DataType : : BUILTIN & & base_type . builtin_type ! = Variant : : DICTIONARY ;
if ( ! error ) {
switch ( base_type . builtin_type ) {
// Expect int or real as index.
case Variant : : PACKED_BYTE_ARRAY :
case Variant : : PACKED_FLOAT32_ARRAY :
case Variant : : PACKED_FLOAT64_ARRAY :
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY :
case Variant : : PACKED_STRING_ARRAY :
case Variant : : PACKED_VECTOR2_ARRAY :
case Variant : : PACKED_VECTOR3_ARRAY :
2024-04-08 14:51:34 +00:00
case Variant : : PACKED_COLOR_ARRAY :
case Variant : : PACKED_VECTOR4_ARRAY :
2020-06-10 22:53:25 +00:00
case Variant : : ARRAY :
case Variant : : STRING :
error = index_type . builtin_type ! = Variant : : INT & & index_type . builtin_type ! = Variant : : FLOAT ;
break ;
// Expect String only.
case Variant : : RECT2 :
case Variant : : RECT2I :
case Variant : : PLANE :
2021-01-20 07:02:02 +00:00
case Variant : : QUATERNION :
2020-06-10 22:53:25 +00:00
case Variant : : AABB :
case Variant : : OBJECT :
2022-12-06 02:46:47 +00:00
error = index_type . builtin_type ! = Variant : : STRING & & index_type . builtin_type ! = Variant : : STRING_NAME ;
2020-06-10 22:53:25 +00:00
break ;
// Expect String or number.
case Variant : : BASIS :
case Variant : : VECTOR2 :
case Variant : : VECTOR2I :
case Variant : : VECTOR3 :
case Variant : : VECTOR3I :
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-19 23:11:13 +00:00
case Variant : : VECTOR4 :
case Variant : : VECTOR4I :
2020-06-10 22:53:25 +00:00
case Variant : : TRANSFORM2D :
2021-04-28 07:36:08 +00:00
case Variant : : TRANSFORM3D :
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-19 23:11:13 +00:00
case Variant : : PROJECTION :
2020-06-10 22:53:25 +00:00
error = index_type . builtin_type ! = Variant : : INT & & index_type . builtin_type ! = Variant : : FLOAT & &
2022-12-06 02:46:47 +00:00
index_type . builtin_type ! = Variant : : STRING & & index_type . builtin_type ! = Variant : : STRING_NAME ;
2020-06-10 22:53:25 +00:00
break ;
// Expect String or int.
case Variant : : COLOR :
2022-12-06 02:46:47 +00:00
error = index_type . builtin_type ! = Variant : : INT & & index_type . builtin_type ! = Variant : : STRING & & index_type . builtin_type ! = Variant : : STRING_NAME ;
2020-06-10 22:53:25 +00:00
break ;
// Don't support indexing, but we will check it later.
2020-11-09 13:53:05 +00:00
case Variant : : RID :
2020-06-10 22:53:25 +00:00
case Variant : : BOOL :
case Variant : : CALLABLE :
case Variant : : FLOAT :
case Variant : : INT :
case Variant : : NIL :
case Variant : : NODE_PATH :
case Variant : : SIGNAL :
case Variant : : STRING_NAME :
break ;
2023-06-24 18:03:28 +00:00
// Support depends on if the dictionary has a typed key, otherwise anything is valid.
2020-06-10 22:53:25 +00:00
case Variant : : DICTIONARY :
2023-06-24 18:03:28 +00:00
if ( base_type . has_container_element_type ( 0 ) ) {
GDScriptParser : : DataType key_type = base_type . get_container_element_type ( 0 ) ;
switch ( index_type . builtin_type ) {
// Null value will be treated as an empty object, allow.
case Variant : : NIL :
error = key_type . builtin_type ! = Variant : : OBJECT ;
break ;
// Objects are parsed for validity in a similar manner to container types.
case Variant : : OBJECT :
if ( key_type . builtin_type = = Variant : : OBJECT ) {
error = ! key_type . can_reference ( index_type ) ;
} else {
error = key_type . builtin_type ! = Variant : : NIL ;
}
break ;
// String and StringName interchangeable in this context.
case Variant : : STRING :
case Variant : : STRING_NAME :
error = key_type . builtin_type ! = Variant : : STRING_NAME & & key_type . builtin_type ! = Variant : : STRING ;
break ;
// Ints are valid indices for floats, but not the other way around.
case Variant : : INT :
error = key_type . builtin_type ! = Variant : : INT & & key_type . builtin_type ! = Variant : : FLOAT ;
break ;
// All other cases require the types to match exactly.
default :
error = key_type . builtin_type ! = index_type . builtin_type ;
break ;
}
}
break ;
// Here for completeness.
2020-06-10 22:53:25 +00:00
case Variant : : VARIANT_MAX :
break ;
}
if ( error ) {
push_error ( vformat ( R " (Invalid index type " % s " for a base of type " % s " .) " , index_type . to_string ( ) , base_type . to_string ( ) ) , p_subscript - > index ) ;
}
}
} else if ( base_type . kind ! = GDScriptParser : : DataType : : BUILTIN & & ! index_type . is_variant ( ) ) {
if ( index_type . builtin_type ! = Variant : : STRING & & index_type . builtin_type ! = Variant : : STRING_NAME ) {
2023-10-16 18:29:26 +00:00
push_error ( vformat ( R " (Only " String " or " StringName " can be used as index for type " % s " , but received " % s " .) " , base_type . to_string ( ) , index_type . to_string ( ) ) , p_subscript - > index ) ;
2020-06-10 22:53:25 +00:00
}
}
// Check resulting type if possible.
result_type . builtin_type = Variant : : NIL ;
result_type . kind = GDScriptParser : : DataType : : BUILTIN ;
2020-08-19 17:35:26 +00:00
result_type . type_source = base_type . is_hard_type ( ) ? GDScriptParser : : DataType : : ANNOTATED_INFERRED : GDScriptParser : : DataType : : INFERRED ;
2020-06-10 22:53:25 +00:00
2021-10-06 15:01:34 +00:00
if ( base_type . kind ! = GDScriptParser : : DataType : : BUILTIN ) {
base_type . builtin_type = Variant : : OBJECT ;
}
2020-06-10 22:53:25 +00:00
switch ( base_type . builtin_type ) {
// Can't index at all.
2020-11-09 13:53:05 +00:00
case Variant : : RID :
2020-06-10 22:53:25 +00:00
case Variant : : BOOL :
case Variant : : CALLABLE :
case Variant : : FLOAT :
case Variant : : INT :
case Variant : : NIL :
case Variant : : NODE_PATH :
case Variant : : SIGNAL :
case Variant : : STRING_NAME :
result_type . kind = GDScriptParser : : DataType : : VARIANT ;
push_error ( vformat ( R " (Cannot use subscript operator on a base of type " % s " .) " , base_type . to_string ( ) ) , p_subscript - > base ) ;
break ;
// Return int.
case Variant : : PACKED_BYTE_ARRAY :
case Variant : : PACKED_INT32_ARRAY :
case Variant : : PACKED_INT64_ARRAY :
case Variant : : VECTOR2I :
case Variant : : VECTOR3I :
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-19 23:11:13 +00:00
case Variant : : VECTOR4I :
2020-06-10 22:53:25 +00:00
result_type . builtin_type = Variant : : INT ;
break ;
// Return float.
case Variant : : PACKED_FLOAT32_ARRAY :
case Variant : : PACKED_FLOAT64_ARRAY :
case Variant : : VECTOR2 :
case Variant : : VECTOR3 :
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-19 23:11:13 +00:00
case Variant : : VECTOR4 :
2021-01-20 07:02:02 +00:00
case Variant : : QUATERNION :
2020-06-10 22:53:25 +00:00
result_type . builtin_type = Variant : : FLOAT ;
break ;
// Return String.
case Variant : : PACKED_STRING_ARRAY :
case Variant : : STRING :
result_type . builtin_type = Variant : : STRING ;
break ;
// Return Vector2.
case Variant : : PACKED_VECTOR2_ARRAY :
case Variant : : TRANSFORM2D :
case Variant : : RECT2 :
result_type . builtin_type = Variant : : VECTOR2 ;
break ;
// Return Vector2I.
case Variant : : RECT2I :
result_type . builtin_type = Variant : : VECTOR2I ;
break ;
// Return Vector3.
case Variant : : PACKED_VECTOR3_ARRAY :
case Variant : : AABB :
case Variant : : BASIS :
result_type . builtin_type = Variant : : VECTOR3 ;
break ;
2024-04-08 14:51:34 +00:00
// Return Color.
case Variant : : PACKED_COLOR_ARRAY :
result_type . builtin_type = Variant : : COLOR ;
break ;
// Return Vector4.
case Variant : : PACKED_VECTOR4_ARRAY :
result_type . builtin_type = Variant : : VECTOR4 ;
break ;
2020-06-10 22:53:25 +00:00
// Depends on the index.
2021-04-28 07:36:08 +00:00
case Variant : : TRANSFORM3D :
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-19 23:11:13 +00:00
case Variant : : PROJECTION :
2020-06-10 22:53:25 +00:00
case Variant : : PLANE :
case Variant : : COLOR :
2021-10-06 15:01:34 +00:00
case Variant : : OBJECT :
2020-06-10 22:53:25 +00:00
result_type . kind = GDScriptParser : : DataType : : VARIANT ;
result_type . type_source = GDScriptParser : : DataType : : UNDETECTED ;
break ;
2021-03-09 15:32:35 +00:00
// Can have an element type.
case Variant : : ARRAY :
2023-09-14 18:31:07 +00:00
if ( base_type . has_container_element_type ( 0 ) ) {
result_type = base_type . get_container_element_type ( 0 ) ;
2021-03-09 15:32:35 +00:00
result_type . type_source = base_type . type_source ;
} else {
result_type . kind = GDScriptParser : : DataType : : VARIANT ;
result_type . type_source = GDScriptParser : : DataType : : UNDETECTED ;
}
break ;
2023-06-24 18:03:28 +00:00
// Can have two element types, but we only care about the value.
case Variant : : DICTIONARY :
if ( base_type . has_container_element_type ( 1 ) ) {
result_type = base_type . get_container_element_type ( 1 ) ;
result_type . type_source = base_type . type_source ;
} else {
result_type . kind = GDScriptParser : : DataType : : VARIANT ;
result_type . type_source = GDScriptParser : : DataType : : UNDETECTED ;
}
break ;
2020-06-10 22:53:25 +00:00
// Here for completeness.
case Variant : : VARIANT_MAX :
break ;
}
}
}
}
p_subscript - > set_datatype ( result_type ) ;
}
2023-01-09 14:02:37 +00:00
void GDScriptAnalyzer : : reduce_ternary_op ( GDScriptParser : : TernaryOpNode * p_ternary_op , bool p_is_root ) {
2020-06-10 22:53:25 +00:00
reduce_expression ( p_ternary_op - > condition ) ;
2023-01-09 14:02:37 +00:00
reduce_expression ( p_ternary_op - > true_expr , p_is_root ) ;
reduce_expression ( p_ternary_op - > false_expr , p_is_root ) ;
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType result ;
2020-07-16 01:02:44 +00:00
if ( p_ternary_op - > condition & & p_ternary_op - > condition - > is_constant & & p_ternary_op - > true_expr - > is_constant & & p_ternary_op - > false_expr & & p_ternary_op - > false_expr - > is_constant ) {
2020-06-10 22:53:25 +00:00
p_ternary_op - > is_constant = true ;
if ( p_ternary_op - > condition - > reduced_value . booleanize ( ) ) {
p_ternary_op - > reduced_value = p_ternary_op - > true_expr - > reduced_value ;
} else {
p_ternary_op - > reduced_value = p_ternary_op - > false_expr - > reduced_value ;
}
}
2020-07-16 01:02:44 +00:00
GDScriptParser : : DataType true_type ;
if ( p_ternary_op - > true_expr ) {
true_type = p_ternary_op - > true_expr - > get_datatype ( ) ;
} else {
true_type . kind = GDScriptParser : : DataType : : VARIANT ;
}
GDScriptParser : : DataType false_type ;
if ( p_ternary_op - > false_expr ) {
false_type = p_ternary_op - > false_expr - > get_datatype ( ) ;
} else {
false_type . kind = GDScriptParser : : DataType : : VARIANT ;
}
2020-06-10 22:53:25 +00:00
if ( true_type . is_variant ( ) | | false_type . is_variant ( ) ) {
result . kind = GDScriptParser : : DataType : : VARIANT ;
} else {
result = true_type ;
if ( ! is_type_compatible ( true_type , false_type ) ) {
result = false_type ;
if ( ! is_type_compatible ( false_type , true_type ) ) {
result . kind = GDScriptParser : : DataType : : VARIANT ;
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2020-06-11 22:31:28 +00:00
parser - > push_warning ( p_ternary_op , GDScriptWarning : : INCOMPATIBLE_TERNARY ) ;
2020-07-16 01:02:44 +00:00
# endif
2020-06-10 22:53:25 +00:00
}
}
}
2023-02-01 12:52:18 +00:00
result . type_source = true_type . is_hard_type ( ) & & false_type . is_hard_type ( ) ? GDScriptParser : : DataType : : ANNOTATED_INFERRED : GDScriptParser : : DataType : : INFERRED ;
2020-06-10 22:53:25 +00:00
p_ternary_op - > set_datatype ( result ) ;
}
2023-02-16 23:16:24 +00:00
void GDScriptAnalyzer : : reduce_type_test ( GDScriptParser : : TypeTestNode * p_type_test ) {
GDScriptParser : : DataType result ;
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
result . kind = GDScriptParser : : DataType : : BUILTIN ;
result . builtin_type = Variant : : BOOL ;
p_type_test - > set_datatype ( result ) ;
if ( ! p_type_test - > operand | | ! p_type_test - > test_type ) {
return ;
}
reduce_expression ( p_type_test - > operand ) ;
GDScriptParser : : DataType operand_type = p_type_test - > operand - > get_datatype ( ) ;
GDScriptParser : : DataType test_type = type_from_metatype ( resolve_datatype ( p_type_test - > test_type ) ) ;
p_type_test - > test_datatype = test_type ;
if ( ! operand_type . is_set ( ) | | ! test_type . is_set ( ) ) {
return ;
}
if ( p_type_test - > operand - > is_constant ) {
p_type_test - > is_constant = true ;
p_type_test - > reduced_value = false ;
if ( ! is_type_compatible ( test_type , operand_type ) ) {
push_error ( vformat ( R " (Expression is of type " % s " so it can't be of type " % s " .) " , operand_type . to_string ( ) , test_type . to_string ( ) ) , p_type_test - > operand ) ;
} else if ( is_type_compatible ( test_type , type_from_variant ( p_type_test - > operand - > reduced_value , p_type_test - > operand ) ) ) {
p_type_test - > reduced_value = test_type . builtin_type ! = Variant : : OBJECT | | ! p_type_test - > operand - > reduced_value . is_null ( ) ;
}
return ;
}
if ( ! is_type_compatible ( test_type , operand_type ) & & ! is_type_compatible ( operand_type , test_type ) ) {
if ( operand_type . is_hard_type ( ) ) {
push_error ( vformat ( R " (Expression is of type " % s " so it can't be of type " % s " .) " , operand_type . to_string ( ) , test_type . to_string ( ) ) , p_type_test - > operand ) ;
} else {
downgrade_node_type_source ( p_type_test - > operand ) ;
}
}
}
2020-06-10 22:53:25 +00:00
void GDScriptAnalyzer : : reduce_unary_op ( GDScriptParser : : UnaryOpNode * p_unary_op ) {
reduce_expression ( p_unary_op - > operand ) ;
GDScriptParser : : DataType result ;
2020-08-17 23:49:04 +00:00
if ( p_unary_op - > operand = = nullptr ) {
result . kind = GDScriptParser : : DataType : : VARIANT ;
p_unary_op - > set_datatype ( result ) ;
return ;
}
2022-12-15 22:15:53 +00:00
GDScriptParser : : DataType operand_type = p_unary_op - > operand - > get_datatype ( ) ;
2020-06-10 22:53:25 +00:00
if ( p_unary_op - > operand - > is_constant ) {
p_unary_op - > is_constant = true ;
p_unary_op - > reduced_value = Variant : : evaluate ( p_unary_op - > variant_op , p_unary_op - > operand - > reduced_value , Variant ( ) ) ;
2020-08-31 12:53:02 +00:00
result = type_from_variant ( p_unary_op - > reduced_value , p_unary_op ) ;
2022-10-02 00:13:06 +00:00
}
if ( operand_type . is_variant ( ) ) {
2020-06-10 22:53:25 +00:00
result . kind = GDScriptParser : : DataType : : VARIANT ;
mark_node_unsafe ( p_unary_op ) ;
} else {
bool valid = false ;
2022-10-02 00:13:06 +00:00
result = get_operation_type ( p_unary_op - > variant_op , operand_type , valid , p_unary_op ) ;
2020-06-10 22:53:25 +00:00
if ( ! valid ) {
2022-10-02 00:13:06 +00:00
push_error ( vformat ( R " (Invalid operand of type " % s " for unary operator " % s " .) " , operand_type . to_string ( ) , Variant : : get_operator_name ( p_unary_op - > variant_op ) ) , p_unary_op ) ;
2020-06-10 22:53:25 +00:00
}
}
p_unary_op - > set_datatype ( result ) ;
}
2022-11-27 07:56:53 +00:00
Variant GDScriptAnalyzer : : make_expression_reduced_value ( GDScriptParser : : ExpressionNode * p_expression , bool & is_reduced ) {
Variant value ;
2023-02-02 00:14:25 +00:00
if ( p_expression = = nullptr ) {
return value ;
}
2022-11-27 07:56:53 +00:00
if ( p_expression - > is_constant ) {
is_reduced = true ;
value = p_expression - > reduced_value ;
} else if ( p_expression - > type = = GDScriptParser : : Node : : ARRAY ) {
value = make_array_reduced_value ( static_cast < GDScriptParser : : ArrayNode * > ( p_expression ) , is_reduced ) ;
} else if ( p_expression - > type = = GDScriptParser : : Node : : DICTIONARY ) {
value = make_dictionary_reduced_value ( static_cast < GDScriptParser : : DictionaryNode * > ( p_expression ) , is_reduced ) ;
} else if ( p_expression - > type = = GDScriptParser : : Node : : SUBSCRIPT ) {
value = make_subscript_reduced_value ( static_cast < GDScriptParser : : SubscriptNode * > ( p_expression ) , is_reduced ) ;
}
return value ;
}
Variant GDScriptAnalyzer : : make_array_reduced_value ( GDScriptParser : : ArrayNode * p_array , bool & is_reduced ) {
2023-09-14 18:31:07 +00:00
Array array = p_array - > get_datatype ( ) . has_container_element_type ( 0 ) ? make_array_from_element_datatype ( p_array - > get_datatype ( ) . get_container_element_type ( 0 ) ) : Array ( ) ;
2022-11-27 07:56:53 +00:00
array . resize ( p_array - > elements . size ( ) ) ;
2020-09-11 14:16:20 +00:00
for ( int i = 0 ; i < p_array - > elements . size ( ) ; i + + ) {
GDScriptParser : : ExpressionNode * element = p_array - > elements [ i ] ;
2022-04-13 18:03:13 +00:00
2022-11-27 07:56:53 +00:00
bool is_element_value_reduced = false ;
Variant element_value = make_expression_reduced_value ( element , is_element_value_reduced ) ;
if ( ! is_element_value_reduced ) {
return Variant ( ) ;
2022-04-13 18:03:13 +00:00
}
2022-11-27 07:56:53 +00:00
array [ i ] = element_value ;
2020-09-11 14:16:20 +00:00
}
2022-11-27 07:56:53 +00:00
array . make_read_only ( ) ;
is_reduced = true ;
return array ;
2020-09-11 14:16:20 +00:00
}
2022-11-27 07:56:53 +00:00
Variant GDScriptAnalyzer : : make_dictionary_reduced_value ( GDScriptParser : : DictionaryNode * p_dictionary , bool & is_reduced ) {
2023-06-24 18:03:28 +00:00
Dictionary dictionary = p_dictionary - > get_datatype ( ) . has_container_element_types ( )
? make_dictionary_from_element_datatype ( p_dictionary - > get_datatype ( ) . get_container_element_type_or_variant ( 0 ) , p_dictionary - > get_datatype ( ) . get_container_element_type_or_variant ( 1 ) )
: Dictionary ( ) ;
2022-11-27 07:56:53 +00:00
2020-09-11 14:16:20 +00:00
for ( int i = 0 ; i < p_dictionary - > elements . size ( ) ; i + + ) {
const GDScriptParser : : DictionaryNode : : Pair & element = p_dictionary - > elements [ i ] ;
2022-04-13 18:03:13 +00:00
2022-11-27 07:56:53 +00:00
bool is_element_key_reduced = false ;
Variant element_key = make_expression_reduced_value ( element . key , is_element_key_reduced ) ;
if ( ! is_element_key_reduced ) {
return Variant ( ) ;
2022-04-13 18:03:13 +00:00
}
2022-11-27 07:56:53 +00:00
bool is_element_value_reduced = false ;
Variant element_value = make_expression_reduced_value ( element . value , is_element_value_reduced ) ;
if ( ! is_element_value_reduced ) {
return Variant ( ) ;
2020-09-11 14:16:20 +00:00
}
2022-11-27 07:56:53 +00:00
dictionary [ element_key ] = element_value ;
2020-09-11 14:16:20 +00:00
}
2022-11-27 07:56:53 +00:00
dictionary . make_read_only ( ) ;
is_reduced = true ;
return dictionary ;
}
Variant GDScriptAnalyzer : : make_subscript_reduced_value ( GDScriptParser : : SubscriptNode * p_subscript , bool & is_reduced ) {
2023-02-02 00:14:25 +00:00
if ( p_subscript - > base = = nullptr | | p_subscript - > index = = nullptr ) {
return Variant ( ) ;
}
2022-11-27 07:56:53 +00:00
bool is_base_value_reduced = false ;
Variant base_value = make_expression_reduced_value ( p_subscript - > base , is_base_value_reduced ) ;
if ( ! is_base_value_reduced ) {
return Variant ( ) ;
2020-09-11 14:16:20 +00:00
}
2022-11-27 07:56:53 +00:00
if ( p_subscript - > is_attribute ) {
bool is_valid = false ;
Variant value = base_value . get_named ( p_subscript - > attribute - > name , is_valid ) ;
if ( is_valid ) {
is_reduced = true ;
return value ;
} else {
return Variant ( ) ;
}
} else {
bool is_index_value_reduced = false ;
Variant index_value = make_expression_reduced_value ( p_subscript - > index , is_index_value_reduced ) ;
if ( ! is_index_value_reduced ) {
return Variant ( ) ;
}
bool is_valid = false ;
Variant value = base_value . get ( index_value , & is_valid ) ;
if ( is_valid ) {
is_reduced = true ;
return value ;
} else {
return Variant ( ) ;
}
2023-01-08 03:41:06 +00:00
}
2022-11-27 07:56:53 +00:00
}
Array GDScriptAnalyzer : : make_array_from_element_datatype ( const GDScriptParser : : DataType & p_element_datatype , const GDScriptParser : : Node * p_source_node ) {
Array array ;
2023-02-08 22:31:40 +00:00
if ( p_element_datatype . builtin_type = = Variant : : OBJECT ) {
Ref < Script > script_type = p_element_datatype . script_type ;
if ( p_element_datatype . kind = = GDScriptParser : : DataType : : CLASS & & script_type . is_null ( ) ) {
Error err = OK ;
2024-07-23 11:46:28 +00:00
Ref < GDScript > scr = get_depended_shallow_script ( p_element_datatype . script_path , err ) ;
2023-02-08 22:31:40 +00:00
if ( err ) {
push_error ( vformat ( R " (Error while getting cache for script " % s " .) " , p_element_datatype . script_path ) , p_source_node ) ;
return array ;
}
script_type . reference_ptr ( scr - > find_class ( p_element_datatype . class_type - > fqcn ) ) ;
2022-11-27 07:56:53 +00:00
}
2023-02-08 22:31:40 +00:00
array . set_typed ( p_element_datatype . builtin_type , p_element_datatype . native_type , script_type ) ;
} else {
array . set_typed ( p_element_datatype . builtin_type , StringName ( ) , Variant ( ) ) ;
}
2022-11-27 07:56:53 +00:00
return array ;
}
2023-06-24 18:03:28 +00:00
Dictionary GDScriptAnalyzer : : make_dictionary_from_element_datatype ( const GDScriptParser : : DataType & p_key_element_datatype , const GDScriptParser : : DataType & p_value_element_datatype , const GDScriptParser : : Node * p_source_node ) {
Dictionary dictionary ;
StringName key_name ;
Variant key_script ;
StringName value_name ;
Variant value_script ;
if ( p_key_element_datatype . builtin_type = = Variant : : OBJECT ) {
Ref < Script > script_type = p_key_element_datatype . script_type ;
if ( p_key_element_datatype . kind = = GDScriptParser : : DataType : : CLASS & & script_type . is_null ( ) ) {
Error err = OK ;
Ref < GDScript > scr = get_depended_shallow_script ( p_key_element_datatype . script_path , err ) ;
if ( err ) {
push_error ( vformat ( R " (Error while getting cache for script " % s " .) " , p_key_element_datatype . script_path ) , p_source_node ) ;
return dictionary ;
}
script_type . reference_ptr ( scr - > find_class ( p_key_element_datatype . class_type - > fqcn ) ) ;
}
key_name = p_key_element_datatype . native_type ;
key_script = script_type ;
}
if ( p_value_element_datatype . builtin_type = = Variant : : OBJECT ) {
Ref < Script > script_type = p_value_element_datatype . script_type ;
if ( p_value_element_datatype . kind = = GDScriptParser : : DataType : : CLASS & & script_type . is_null ( ) ) {
Error err = OK ;
Ref < GDScript > scr = get_depended_shallow_script ( p_value_element_datatype . script_path , err ) ;
if ( err ) {
push_error ( vformat ( R " (Error while getting cache for script " % s " .) " , p_value_element_datatype . script_path ) , p_source_node ) ;
return dictionary ;
}
script_type . reference_ptr ( scr - > find_class ( p_value_element_datatype . class_type - > fqcn ) ) ;
}
value_name = p_value_element_datatype . native_type ;
value_script = script_type ;
}
dictionary . set_typed ( p_key_element_datatype . builtin_type , key_name , key_script , p_value_element_datatype . builtin_type , value_name , value_script ) ;
return dictionary ;
}
2022-11-27 07:56:53 +00:00
Variant GDScriptAnalyzer : : make_variable_default_value ( GDScriptParser : : VariableNode * p_variable ) {
Variant result = Variant ( ) ;
if ( p_variable - > initializer ) {
bool is_initializer_value_reduced = false ;
Variant initializer_value = make_expression_reduced_value ( p_variable - > initializer , is_initializer_value_reduced ) ;
if ( is_initializer_value_reduced ) {
result = initializer_value ;
}
} else {
GDScriptParser : : DataType datatype = p_variable - > get_datatype ( ) ;
2023-02-14 15:07:50 +00:00
if ( datatype . is_hard_type ( ) ) {
if ( datatype . kind = = GDScriptParser : : DataType : : BUILTIN & & datatype . builtin_type ! = Variant : : OBJECT ) {
2023-09-14 18:31:07 +00:00
if ( datatype . builtin_type = = Variant : : ARRAY & & datatype . has_container_element_type ( 0 ) ) {
result = make_array_from_element_datatype ( datatype . get_container_element_type ( 0 ) ) ;
2023-06-24 18:03:28 +00:00
} else if ( datatype . builtin_type = = Variant : : DICTIONARY & & datatype . has_container_element_types ( ) ) {
GDScriptParser : : DataType key = datatype . get_container_element_type_or_variant ( 0 ) ;
GDScriptParser : : DataType value = datatype . get_container_element_type_or_variant ( 1 ) ;
result = make_dictionary_from_element_datatype ( key , value ) ;
2023-02-14 15:07:50 +00:00
} else {
VariantInternal : : initialize ( & result , datatype . builtin_type ) ;
}
} else if ( datatype . kind = = GDScriptParser : : DataType : : ENUM ) {
result = 0 ;
2022-11-27 07:56:53 +00:00
}
}
}
return result ;
2020-09-11 14:16:20 +00:00
}
2020-08-31 12:53:02 +00:00
GDScriptParser : : DataType GDScriptAnalyzer : : type_from_variant ( const Variant & p_value , const GDScriptParser : : Node * p_source ) {
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType result ;
result . is_constant = true ;
result . kind = GDScriptParser : : DataType : : BUILTIN ;
result . builtin_type = p_value . get_type ( ) ;
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ; // Constant has explicit type.
2023-02-22 21:38:29 +00:00
if ( p_value . get_type ( ) = = Variant : : ARRAY ) {
const Array & array = p_value ;
if ( array . get_typed_script ( ) ) {
2023-09-14 18:31:07 +00:00
result . set_container_element_type ( 0 , type_from_metatype ( make_script_meta_type ( array . get_typed_script ( ) ) ) ) ;
2023-02-22 21:38:29 +00:00
} else if ( array . get_typed_class_name ( ) ) {
2023-09-14 18:31:07 +00:00
result . set_container_element_type ( 0 , type_from_metatype ( make_native_meta_type ( array . get_typed_class_name ( ) ) ) ) ;
2023-02-22 21:38:29 +00:00
} else if ( array . get_typed_builtin ( ) ! = Variant : : NIL ) {
2023-09-14 18:31:07 +00:00
result . set_container_element_type ( 0 , type_from_metatype ( make_builtin_meta_type ( ( Variant : : Type ) array . get_typed_builtin ( ) ) ) ) ;
2023-02-22 21:38:29 +00:00
}
2023-06-24 18:03:28 +00:00
} else if ( p_value . get_type ( ) = = Variant : : DICTIONARY ) {
const Dictionary & dict = p_value ;
if ( dict . get_typed_key_script ( ) ) {
result . set_container_element_type ( 0 , type_from_metatype ( make_script_meta_type ( dict . get_typed_key_script ( ) ) ) ) ;
} else if ( dict . get_typed_key_class_name ( ) ) {
result . set_container_element_type ( 0 , type_from_metatype ( make_native_meta_type ( dict . get_typed_key_class_name ( ) ) ) ) ;
} else if ( dict . get_typed_key_builtin ( ) ! = Variant : : NIL ) {
result . set_container_element_type ( 0 , type_from_metatype ( make_builtin_meta_type ( ( Variant : : Type ) dict . get_typed_key_builtin ( ) ) ) ) ;
}
if ( dict . get_typed_value_script ( ) ) {
result . set_container_element_type ( 1 , type_from_metatype ( make_script_meta_type ( dict . get_typed_value_script ( ) ) ) ) ;
} else if ( dict . get_typed_value_class_name ( ) ) {
result . set_container_element_type ( 1 , type_from_metatype ( make_native_meta_type ( dict . get_typed_value_class_name ( ) ) ) ) ;
} else if ( dict . get_typed_value_builtin ( ) ! = Variant : : NIL ) {
result . set_container_element_type ( 1 , type_from_metatype ( make_builtin_meta_type ( ( Variant : : Type ) dict . get_typed_value_builtin ( ) ) ) ) ;
}
2023-02-22 21:38:29 +00:00
} else if ( p_value . get_type ( ) = = Variant : : OBJECT ) {
2022-12-04 10:35:41 +00:00
// Object is treated as a native type, not a builtin type.
result . kind = GDScriptParser : : DataType : : NATIVE ;
2020-06-10 22:53:25 +00:00
Object * obj = p_value ;
if ( ! obj ) {
return GDScriptParser : : DataType ( ) ;
}
result . native_type = obj - > get_class_name ( ) ;
Ref < Script > scr = p_value ; // Check if value is a script itself.
if ( scr . is_valid ( ) ) {
result . is_meta_type = true ;
} else {
result . is_meta_type = false ;
scr = obj - > get_script ( ) ;
}
if ( scr . is_valid ( ) ) {
2022-10-09 16:41:28 +00:00
Ref < GDScript > gds = scr ;
if ( gds . is_valid ( ) ) {
// This might be an inner class, so we want to get the parser for the root.
// But still get the inner class from that tree.
2022-12-12 12:35:55 +00:00
String script_path = gds - > get_script_path ( ) ;
2024-04-12 23:13:25 +00:00
Ref < GDScriptParserRef > ref = parser - > get_depended_parser_for ( script_path ) ;
2022-10-09 16:41:28 +00:00
if ( ref . is_null ( ) ) {
2022-12-12 12:35:55 +00:00
push_error ( vformat ( R " (Could not find script " % s " .) " , script_path ) , p_source ) ;
2022-10-09 16:41:28 +00:00
GDScriptParser : : DataType error_type ;
error_type . kind = GDScriptParser : : DataType : : VARIANT ;
return error_type ;
}
2022-12-12 12:35:55 +00:00
Error err = ref - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ;
GDScriptParser : : ClassNode * found = nullptr ;
if ( err = = OK ) {
found = ref - > get_parser ( ) - > find_class ( gds - > fully_qualified_name ) ;
if ( found ! = nullptr ) {
err = resolve_class_inheritance ( found , p_source ) ;
2022-12-11 02:57:35 +00:00
}
2022-12-12 12:35:55 +00:00
}
if ( err | | found = = nullptr ) {
push_error ( vformat ( R " (Could not resolve script " % s " .) " , script_path ) , p_source ) ;
GDScriptParser : : DataType error_type ;
error_type . kind = GDScriptParser : : DataType : : VARIANT ;
return error_type ;
2022-10-09 16:41:28 +00:00
}
2023-01-03 02:54:51 +00:00
result . kind = GDScriptParser : : DataType : : CLASS ;
result . native_type = found - > get_datatype ( ) . native_type ;
2022-12-15 21:46:15 +00:00
result . class_type = found ;
result . script_path = ref - > get_parser ( ) - > script_path ;
2020-06-10 22:53:25 +00:00
} else {
2022-10-09 16:41:28 +00:00
result . kind = GDScriptParser : : DataType : : SCRIPT ;
2022-12-11 02:57:35 +00:00
result . native_type = scr - > get_instance_base_type ( ) ;
2023-01-03 02:54:51 +00:00
result . script_path = scr - > get_path ( ) ;
2020-06-10 22:53:25 +00:00
}
2022-12-11 02:57:35 +00:00
result . script_type = scr ;
2020-06-10 22:53:25 +00:00
} else {
result . kind = GDScriptParser : : DataType : : NATIVE ;
if ( result . native_type = = GDScriptNativeClass : : get_class_static ( ) ) {
result . is_meta_type = true ;
}
}
}
return result ;
}
2022-12-04 03:02:03 +00:00
GDScriptParser : : DataType GDScriptAnalyzer : : type_from_metatype ( const GDScriptParser : : DataType & p_meta_type ) {
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType result = p_meta_type ;
result . is_meta_type = false ;
2023-02-19 15:57:09 +00:00
result . is_pseudo_type = false ;
2022-02-02 16:57:24 +00:00
if ( p_meta_type . kind = = GDScriptParser : : DataType : : ENUM ) {
result . builtin_type = Variant : : INT ;
2022-12-04 03:02:03 +00:00
} else {
result . is_constant = false ;
2022-02-02 16:57:24 +00:00
}
2020-06-10 22:53:25 +00:00
return result ;
}
2023-01-30 17:50:08 +00:00
GDScriptParser : : DataType GDScriptAnalyzer : : type_from_property ( const PropertyInfo & p_property , bool p_is_arg , bool p_is_readonly ) const {
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType result ;
2023-01-30 17:50:08 +00:00
result . is_read_only = p_is_readonly ;
2020-06-10 22:53:25 +00:00
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
2022-12-27 22:11:51 +00:00
if ( p_property . type = = Variant : : NIL & & ( p_is_arg | | ( p_property . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) ) ) {
2020-06-10 22:53:25 +00:00
// Variant
result . kind = GDScriptParser : : DataType : : VARIANT ;
return result ;
}
result . builtin_type = p_property . type ;
if ( p_property . type = = Variant : : OBJECT ) {
2023-11-22 13:12:50 +00:00
if ( ScriptServer : : is_global_class ( p_property . class_name ) ) {
result . kind = GDScriptParser : : DataType : : SCRIPT ;
result . script_path = ScriptServer : : get_global_class_path ( p_property . class_name ) ;
result . native_type = ScriptServer : : get_global_class_native_base ( p_property . class_name ) ;
Ref < Script > scr = ResourceLoader : : load ( ScriptServer : : get_global_class_path ( p_property . class_name ) ) ;
if ( scr . is_valid ( ) ) {
result . script_type = scr ;
}
} else {
result . kind = GDScriptParser : : DataType : : NATIVE ;
result . native_type = p_property . class_name = = StringName ( ) ? " Object " : p_property . class_name ;
}
2020-06-10 22:53:25 +00:00
} else {
result . kind = GDScriptParser : : DataType : : BUILTIN ;
2021-03-09 15:32:35 +00:00
result . builtin_type = p_property . type ;
if ( p_property . type = = Variant : : ARRAY & & p_property . hint = = PROPERTY_HINT_ARRAY_TYPE ) {
// Check element type.
StringName elem_type_name = p_property . hint_string ;
GDScriptParser : : DataType elem_type ;
elem_type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
Variant : : Type elem_builtin_type = GDScriptParser : : get_builtin_type ( elem_type_name ) ;
if ( elem_builtin_type < Variant : : VARIANT_MAX ) {
// Builtin type.
elem_type . kind = GDScriptParser : : DataType : : BUILTIN ;
elem_type . builtin_type = elem_builtin_type ;
} else if ( class_exists ( elem_type_name ) ) {
elem_type . kind = GDScriptParser : : DataType : : NATIVE ;
elem_type . builtin_type = Variant : : OBJECT ;
2023-10-04 12:03:53 +00:00
elem_type . native_type = elem_type_name ;
2021-03-09 15:32:35 +00:00
} else if ( ScriptServer : : is_global_class ( elem_type_name ) ) {
// Just load this as it shouldn't be a GDScript.
Ref < Script > script = ResourceLoader : : load ( ScriptServer : : get_global_class_path ( elem_type_name ) ) ;
elem_type . kind = GDScriptParser : : DataType : : SCRIPT ;
elem_type . builtin_type = Variant : : OBJECT ;
elem_type . native_type = script - > get_instance_base_type ( ) ;
elem_type . script_type = script ;
} else {
ERR_FAIL_V_MSG ( result , " Could not find element type from property hint of a typed array. " ) ;
}
2021-10-15 13:40:50 +00:00
elem_type . is_constant = false ;
2023-09-14 18:31:07 +00:00
result . set_container_element_type ( 0 , elem_type ) ;
2023-06-24 18:03:28 +00:00
} else if ( p_property . type = = Variant : : DICTIONARY & & p_property . hint = = PROPERTY_HINT_DICTIONARY_TYPE ) {
// Check element type.
StringName key_elem_type_name = p_property . hint_string . get_slice ( " ; " , 0 ) ;
GDScriptParser : : DataType key_elem_type ;
key_elem_type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
Variant : : Type key_elem_builtin_type = GDScriptParser : : get_builtin_type ( key_elem_type_name ) ;
if ( key_elem_builtin_type < Variant : : VARIANT_MAX ) {
// Builtin type.
key_elem_type . kind = GDScriptParser : : DataType : : BUILTIN ;
key_elem_type . builtin_type = key_elem_builtin_type ;
} else if ( class_exists ( key_elem_type_name ) ) {
key_elem_type . kind = GDScriptParser : : DataType : : NATIVE ;
key_elem_type . builtin_type = Variant : : OBJECT ;
key_elem_type . native_type = key_elem_type_name ;
} else if ( ScriptServer : : is_global_class ( key_elem_type_name ) ) {
// Just load this as it shouldn't be a GDScript.
Ref < Script > script = ResourceLoader : : load ( ScriptServer : : get_global_class_path ( key_elem_type_name ) ) ;
key_elem_type . kind = GDScriptParser : : DataType : : SCRIPT ;
key_elem_type . builtin_type = Variant : : OBJECT ;
key_elem_type . native_type = script - > get_instance_base_type ( ) ;
key_elem_type . script_type = script ;
} else {
ERR_FAIL_V_MSG ( result , " Could not find element type from property hint of a typed dictionary. " ) ;
}
key_elem_type . is_constant = false ;
StringName value_elem_type_name = p_property . hint_string . get_slice ( " ; " , 1 ) ;
GDScriptParser : : DataType value_elem_type ;
value_elem_type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
Variant : : Type value_elem_builtin_type = GDScriptParser : : get_builtin_type ( value_elem_type_name ) ;
if ( value_elem_builtin_type < Variant : : VARIANT_MAX ) {
// Builtin type.
value_elem_type . kind = GDScriptParser : : DataType : : BUILTIN ;
value_elem_type . builtin_type = value_elem_builtin_type ;
} else if ( class_exists ( value_elem_type_name ) ) {
value_elem_type . kind = GDScriptParser : : DataType : : NATIVE ;
value_elem_type . builtin_type = Variant : : OBJECT ;
value_elem_type . native_type = value_elem_type_name ;
} else if ( ScriptServer : : is_global_class ( value_elem_type_name ) ) {
// Just load this as it shouldn't be a GDScript.
Ref < Script > script = ResourceLoader : : load ( ScriptServer : : get_global_class_path ( value_elem_type_name ) ) ;
value_elem_type . kind = GDScriptParser : : DataType : : SCRIPT ;
value_elem_type . builtin_type = Variant : : OBJECT ;
value_elem_type . native_type = script - > get_instance_base_type ( ) ;
value_elem_type . script_type = script ;
} else {
ERR_FAIL_V_MSG ( result , " Could not find element type from property hint of a typed dictionary. " ) ;
}
value_elem_type . is_constant = false ;
result . set_container_element_type ( 0 , key_elem_type ) ;
result . set_container_element_type ( 1 , value_elem_type ) ;
2023-02-02 14:57:22 +00:00
} else if ( p_property . type = = Variant : : INT ) {
// Check if it's enum.
2023-04-05 14:46:22 +00:00
if ( ( p_property . usage & PROPERTY_USAGE_CLASS_IS_ENUM ) & & p_property . class_name ! = StringName ( ) ) {
2023-02-19 15:57:09 +00:00
if ( CoreConstants : : is_global_enum ( p_property . class_name ) ) {
result = make_global_enum_type ( p_property . class_name , StringName ( ) , false ) ;
2023-02-02 14:57:22 +00:00
result . is_constant = false ;
2023-02-19 15:57:09 +00:00
} else {
Vector < String > names = String ( p_property . class_name ) . split ( ENUM_SEPARATOR ) ;
if ( names . size ( ) = = 2 ) {
result = make_native_enum_type ( names [ 1 ] , names [ 0 ] , false ) ;
result . is_constant = false ;
}
2023-02-02 14:57:22 +00:00
}
}
2023-04-05 14:46:22 +00:00
// PROPERTY_USAGE_CLASS_IS_BITFIELD: BitField[T] isn't supported (yet?), use plain int.
2021-03-09 15:32:35 +00:00
}
2020-06-10 22:53:25 +00:00
}
return result ;
}
2023-05-21 23:22:00 +00:00
bool GDScriptAnalyzer : : get_function_signature ( GDScriptParser : : Node * p_source , bool p_is_constructor , GDScriptParser : : DataType p_base_type , const StringName & p_function , GDScriptParser : : DataType & r_return_type , List < GDScriptParser : : DataType > & r_par_types , int & r_default_arg_count , BitField < MethodFlags > & r_method_flags , StringName * r_native_class ) {
r_method_flags = METHOD_FLAGS_DEFAULT ;
2020-06-10 22:53:25 +00:00
r_default_arg_count = 0 ;
2023-02-02 14:57:22 +00:00
if ( r_native_class ) {
* r_native_class = StringName ( ) ;
}
2020-07-16 01:02:44 +00:00
StringName function_name = p_function ;
2020-06-10 22:53:25 +00:00
2022-12-04 03:02:03 +00:00
bool was_enum = false ;
2022-02-02 16:57:24 +00:00
if ( p_base_type . kind = = GDScriptParser : : DataType : : ENUM ) {
2022-12-04 03:02:03 +00:00
was_enum = true ;
2022-02-02 16:57:24 +00:00
if ( p_base_type . is_meta_type ) {
// Enum type can be treated as a dictionary value.
p_base_type . kind = GDScriptParser : : DataType : : BUILTIN ;
p_base_type . is_meta_type = false ;
} else {
push_error ( " Cannot call function on enum value. " , p_source ) ;
return false ;
}
}
2020-06-10 22:53:25 +00:00
if ( p_base_type . kind = = GDScriptParser : : DataType : : BUILTIN ) {
// Construct a base type to get methods.
Callable : : CallError err ;
2020-11-09 03:19:09 +00:00
Variant dummy ;
Variant : : construct ( p_base_type . builtin_type , dummy , nullptr , 0 , err ) ;
2020-06-10 22:53:25 +00:00
if ( err . error ! = Callable : : CallError : : CALL_OK ) {
ERR_FAIL_V_MSG ( false , " Could not construct base Variant type. " ) ;
}
List < MethodInfo > methods ;
dummy . get_method_list ( & methods ) ;
2021-07-16 03:45:57 +00:00
for ( const MethodInfo & E : methods ) {
if ( E . name = = p_function ) {
2023-05-21 23:22:00 +00:00
function_signature_from_info ( E , r_return_type , r_par_types , r_default_arg_count , r_method_flags ) ;
2022-12-04 03:02:03 +00:00
// Cannot use non-const methods on enums.
2023-05-21 23:22:00 +00:00
if ( ! r_method_flags . has_flag ( METHOD_FLAG_STATIC ) & & was_enum & & ! ( E . flags & METHOD_FLAG_CONST ) ) {
2022-12-04 03:02:03 +00:00
push_error ( vformat ( R " *(Cannot call non-const Dictionary function " % s ( ) " on enum " % s " .)* " , p_function , p_base_type . enum_type ) , p_source ) ;
}
2021-06-03 12:51:51 +00:00
return true ;
2020-06-10 22:53:25 +00:00
}
}
return false ;
}
2022-12-29 07:34:13 +00:00
StringName base_native = p_base_type . native_type ;
if ( base_native ! = StringName ( ) ) {
// Empty native class might happen in some Script implementations.
// Just ignore it.
if ( ! class_exists ( base_native ) ) {
push_error ( vformat ( " Native class %s used in script doesn't exist or isn't exposed. " , base_native ) , p_source ) ;
return false ;
2023-02-27 03:42:55 +00:00
} else if ( p_is_constructor & & ClassDB : : is_abstract ( base_native ) ) {
2022-12-29 07:34:13 +00:00
if ( p_base_type . kind = = GDScriptParser : : DataType : : CLASS ) {
push_error ( vformat ( R " (Class " % s " cannot be constructed as it is based on abstract native class " % s " .) " , p_base_type . class_type - > fqcn . get_file ( ) , base_native ) , p_source ) ;
} else if ( p_base_type . kind = = GDScriptParser : : DataType : : SCRIPT ) {
push_error ( vformat ( R " (Script " % s " cannot be constructed as it is based on abstract native class " % s " .) " , p_base_type . script_path . get_file ( ) , base_native ) , p_source ) ;
} else {
push_error ( vformat ( R " (Native class " % s " cannot be constructed as it is abstract.) " , base_native ) , p_source ) ;
}
return false ;
}
}
2022-03-06 14:09:12 +00:00
if ( p_is_constructor ) {
2023-05-22 13:21:25 +00:00
function_name = GDScriptLanguage : : get_singleton ( ) - > strings . _init ;
2023-05-21 23:22:00 +00:00
r_method_flags . set_flag ( METHOD_FLAG_STATIC ) ;
2020-07-16 01:02:44 +00:00
}
2020-06-10 22:53:25 +00:00
GDScriptParser : : ClassNode * base_class = p_base_type . class_type ;
GDScriptParser : : FunctionNode * found_function = nullptr ;
while ( found_function = = nullptr & & base_class ! = nullptr ) {
2020-07-16 01:02:44 +00:00
if ( base_class - > has_member ( function_name ) ) {
if ( base_class - > get_member ( function_name ) . type ! = GDScriptParser : : ClassNode : : Member : : FUNCTION ) {
2020-06-10 22:53:25 +00:00
// TODO: If this is Callable it can have a better error message.
2020-07-16 01:02:44 +00:00
push_error ( vformat ( R " (Member " % s " is not a function.) " , function_name ) , p_source ) ;
2020-06-10 22:53:25 +00:00
return false ;
}
2022-12-11 02:57:35 +00:00
resolve_class_member ( base_class , function_name , p_source ) ;
2020-07-16 01:02:44 +00:00
found_function = base_class - > get_member ( function_name ) . function ;
2020-06-10 22:53:25 +00:00
}
2022-12-11 02:57:35 +00:00
resolve_class_inheritance ( base_class , p_source ) ;
2020-06-10 22:53:25 +00:00
base_class = base_class - > base_type . class_type ;
}
if ( found_function ! = nullptr ) {
2023-05-21 23:22:00 +00:00
if ( p_is_constructor | | found_function - > is_static ) {
r_method_flags . set_flag ( METHOD_FLAG_STATIC ) ;
}
2020-06-10 22:53:25 +00:00
for ( int i = 0 ; i < found_function - > parameters . size ( ) ; i + + ) {
r_par_types . push_back ( found_function - > parameters [ i ] - > get_datatype ( ) ) ;
2022-12-22 20:43:36 +00:00
if ( found_function - > parameters [ i ] - > initializer ! = nullptr ) {
2020-06-10 22:53:25 +00:00
r_default_arg_count + + ;
}
}
2022-12-27 20:49:26 +00:00
r_return_type = p_is_constructor ? p_base_type : found_function - > get_datatype ( ) ;
2021-05-26 12:23:17 +00:00
r_return_type . is_meta_type = false ;
2020-06-10 22:53:25 +00:00
r_return_type . is_coroutine = found_function - > is_coroutine ;
return true ;
}
Ref < Script > base_script = p_base_type . script_type ;
2022-04-25 04:33:18 +00:00
while ( base_script . is_valid ( ) & & base_script - > has_method ( function_name ) ) {
2020-07-16 01:02:44 +00:00
MethodInfo info = base_script - > get_method_info ( function_name ) ;
2020-06-10 22:53:25 +00:00
if ( ! ( info = = MethodInfo ( ) ) ) {
2023-05-21 23:22:00 +00:00
return function_signature_from_info ( info , r_return_type , r_par_types , r_default_arg_count , r_method_flags ) ;
2020-06-10 22:53:25 +00:00
}
base_script = base_script - > get_base_script ( ) ;
}
// If the base is a script, it might be trying to access members of the Script class itself.
2022-03-06 14:09:12 +00:00
if ( p_base_type . is_meta_type & & ! p_is_constructor & & ( p_base_type . kind = = GDScriptParser : : DataType : : SCRIPT | | p_base_type . kind = = GDScriptParser : : DataType : : CLASS ) ) {
2020-06-10 22:53:25 +00:00
MethodInfo info ;
StringName script_class = p_base_type . kind = = GDScriptParser : : DataType : : SCRIPT ? p_base_type . script_type - > get_class_name ( ) : StringName ( GDScript : : get_class_static ( ) ) ;
2020-07-16 01:02:44 +00:00
if ( ClassDB : : get_method_info ( script_class , function_name , & info ) ) {
2023-05-21 23:22:00 +00:00
return function_signature_from_info ( info , r_return_type , r_par_types , r_default_arg_count , r_method_flags ) ;
2020-06-10 22:53:25 +00:00
}
}
2022-03-06 14:09:12 +00:00
if ( p_is_constructor ) {
2020-07-16 01:02:44 +00:00
// Native types always have a default constructor.
r_return_type = p_base_type ;
r_return_type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
r_return_type . is_meta_type = false ;
return true ;
2020-06-10 22:53:25 +00:00
}
MethodInfo info ;
2021-08-17 13:06:54 +00:00
if ( ClassDB : : get_method_info ( base_native , function_name , & info ) ) {
2023-05-21 23:22:00 +00:00
bool valid = function_signature_from_info ( info , r_return_type , r_par_types , r_default_arg_count , r_method_flags ) ;
2021-08-20 13:52:58 +00:00
if ( valid & & Engine : : get_singleton ( ) - > has_singleton ( base_native ) ) {
2023-05-21 23:22:00 +00:00
r_method_flags . set_flag ( METHOD_FLAG_STATIC ) ;
2021-08-20 13:52:58 +00:00
}
2023-02-02 14:57:22 +00:00
# ifdef DEBUG_ENABLED
MethodBind * native_method = ClassDB : : get_method ( base_native , function_name ) ;
if ( native_method & & r_native_class ) {
* r_native_class = native_method - > get_instance_class ( ) ;
}
# endif
2021-08-20 13:52:58 +00:00
return valid ;
2020-06-10 22:53:25 +00:00
}
return false ;
}
2023-05-21 23:22:00 +00:00
bool GDScriptAnalyzer : : function_signature_from_info ( const MethodInfo & p_info , GDScriptParser : : DataType & r_return_type , List < GDScriptParser : : DataType > & r_par_types , int & r_default_arg_count , BitField < MethodFlags > & r_method_flags ) {
2020-06-10 22:53:25 +00:00
r_return_type = type_from_property ( p_info . return_val ) ;
r_default_arg_count = p_info . default_arguments . size ( ) ;
2023-05-21 23:22:00 +00:00
r_method_flags = p_info . flags ;
2020-06-10 22:53:25 +00:00
2021-07-16 03:45:57 +00:00
for ( const PropertyInfo & E : p_info . arguments ) {
2022-12-27 22:11:51 +00:00
r_par_types . push_back ( type_from_property ( E , true ) ) ;
2020-06-10 22:53:25 +00:00
}
return true ;
}
2023-01-22 08:32:05 +00:00
void GDScriptAnalyzer : : validate_call_arg ( const MethodInfo & p_method , const GDScriptParser : : CallNode * p_call ) {
2020-06-10 22:53:25 +00:00
List < GDScriptParser : : DataType > arg_types ;
2021-07-16 03:45:57 +00:00
for ( const PropertyInfo & E : p_method . arguments ) {
2022-12-27 22:11:51 +00:00
arg_types . push_back ( type_from_property ( E , true ) ) ;
2020-06-10 22:53:25 +00:00
}
2023-01-22 08:32:05 +00:00
validate_call_arg ( arg_types , p_method . default_arguments . size ( ) , ( p_method . flags & METHOD_FLAG_VARARG ) ! = 0 , p_call ) ;
2020-06-10 22:53:25 +00:00
}
2023-01-22 08:32:05 +00:00
void GDScriptAnalyzer : : validate_call_arg ( const List < GDScriptParser : : DataType > & p_par_types , int p_default_args_count , bool p_is_vararg , const GDScriptParser : : CallNode * p_call ) {
2020-06-10 22:53:25 +00:00
if ( p_call - > arguments . size ( ) < p_par_types . size ( ) - p_default_args_count ) {
push_error ( vformat ( R " *(Too few arguments for " % s ( ) " call. Expected at least %d but received %d.)* " , p_call - > function_name , p_par_types . size ( ) - p_default_args_count , p_call - > arguments . size ( ) ) , p_call ) ;
}
if ( ! p_is_vararg & & p_call - > arguments . size ( ) > p_par_types . size ( ) ) {
push_error ( vformat ( R " *(Too many arguments for " % s ( ) " call. Expected at most %d but received %d.)* " , p_call - > function_name , p_par_types . size ( ) , p_call - > arguments . size ( ) ) , p_call - > arguments [ p_par_types . size ( ) ] ) ;
}
2024-04-15 13:18:34 +00:00
List < GDScriptParser : : DataType > : : ConstIterator par_itr = p_par_types . begin ( ) ;
for ( int i = 0 ; i < p_call - > arguments . size ( ) ; + + par_itr , + + i ) {
2020-06-10 22:53:25 +00:00
if ( i > = p_par_types . size ( ) ) {
// Already on vararg place.
break ;
}
2024-04-15 13:18:34 +00:00
GDScriptParser : : DataType par_type = * par_itr ;
2023-01-22 08:32:05 +00:00
if ( par_type . is_hard_type ( ) & & p_call - > arguments [ i ] - > is_constant ) {
update_const_expression_builtin_type ( p_call - > arguments [ i ] , par_type , " pass " ) ;
}
2020-06-10 22:53:25 +00:00
GDScriptParser : : DataType arg_type = p_call - > arguments [ i ] - > get_datatype ( ) ;
2023-05-15 08:17:53 +00:00
if ( arg_type . is_variant ( ) | | ! arg_type . is_hard_type ( ) ) {
2023-09-21 09:42:55 +00:00
# ifdef DEBUG_ENABLED
2023-05-15 08:17:53 +00:00
// Argument can be anything, so this is unsafe (unless the parameter is a hard variant).
if ( ! ( par_type . is_hard_type ( ) & & par_type . is_variant ( ) ) ) {
mark_node_unsafe ( p_call - > arguments [ i ] ) ;
2023-09-29 19:43:56 +00:00
parser - > push_warning ( p_call - > arguments [ i ] , GDScriptWarning : : UNSAFE_CALL_ARGUMENT , itos ( i + 1 ) , " function " , p_call - > function_name , par_type . to_string ( ) , arg_type . to_string_strict ( ) ) ;
2023-05-15 08:17:53 +00:00
}
2023-09-21 09:42:55 +00:00
# endif
2020-09-06 07:56:52 +00:00
} else if ( par_type . is_hard_type ( ) & & ! is_type_compatible ( par_type , arg_type , true ) ) {
2020-06-10 22:53:25 +00:00
if ( ! is_type_compatible ( arg_type , par_type ) ) {
2022-12-04 03:02:03 +00:00
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: argument %d should be " % s " but is " % s " .)* " ,
2020-06-10 22:53:25 +00:00
p_call - > function_name , i + 1 , par_type . to_string ( ) , arg_type . to_string ( ) ) ,
p_call - > arguments [ i ] ) ;
2023-09-21 09:42:55 +00:00
# ifdef DEBUG_ENABLED
} else {
// Supertypes are acceptable for dynamic compliance, but it's unsafe.
mark_node_unsafe ( p_call ) ;
2023-09-29 19:43:56 +00:00
parser - > push_warning ( p_call - > arguments [ i ] , GDScriptWarning : : UNSAFE_CALL_ARGUMENT , itos ( i + 1 ) , " function " , p_call - > function_name , par_type . to_string ( ) , arg_type . to_string_strict ( ) ) ;
2023-09-21 09:42:55 +00:00
# endif
2020-06-10 22:53:25 +00:00
}
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2023-01-22 08:32:05 +00:00
} else if ( par_type . kind = = GDScriptParser : : DataType : : BUILTIN & & par_type . builtin_type = = Variant : : INT & & arg_type . kind = = GDScriptParser : : DataType : : BUILTIN & & arg_type . builtin_type = = Variant : : FLOAT ) {
2023-09-21 09:42:55 +00:00
parser - > push_warning ( p_call - > arguments [ i ] , GDScriptWarning : : NARROWING_CONVERSION , p_call - > function_name ) ;
2020-07-16 01:02:44 +00:00
# endif
2020-06-10 22:53:25 +00:00
}
}
}
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2023-04-03 13:57:41 +00:00
void GDScriptAnalyzer : : is_shadowing ( GDScriptParser : : IdentifierNode * p_identifier , const String & p_context , const bool p_in_local_scope ) {
const StringName & name = p_identifier - > name ;
2020-06-11 22:31:28 +00:00
GDScriptParser : : DataType base = parser - > current_class - > get_datatype ( ) ;
GDScriptParser : : ClassNode * base_class = base . class_type ;
2021-12-13 08:39:16 +00:00
{
List < MethodInfo > gdscript_funcs ;
GDScriptLanguage : : get_singleton ( ) - > get_public_functions ( & gdscript_funcs ) ;
for ( MethodInfo & info : gdscript_funcs ) {
if ( info . name = = name ) {
2023-04-03 13:57:41 +00:00
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_GLOBAL_IDENTIFIER , p_context , name , " built-in function " ) ;
2023-03-08 20:06:29 +00:00
return ;
2021-12-13 08:39:16 +00:00
}
}
if ( Variant : : has_utility_function ( name ) ) {
2023-04-03 13:57:41 +00:00
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_GLOBAL_IDENTIFIER , p_context , name , " built-in function " ) ;
2023-03-08 20:06:29 +00:00
return ;
2021-12-13 08:39:16 +00:00
} else if ( ClassDB : : class_exists ( name ) ) {
2023-08-13 09:27:37 +00:00
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_GLOBAL_IDENTIFIER , p_context , name , " native class " ) ;
return ;
} else if ( ScriptServer : : is_global_class ( name ) ) {
String class_path = ScriptServer : : get_global_class_path ( name ) . get_file ( ) ;
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_GLOBAL_IDENTIFIER , p_context , name , vformat ( R " (global class defined in " % s " ) " , class_path ) ) ;
2023-03-08 20:06:29 +00:00
return ;
2023-09-21 09:42:55 +00:00
} else if ( GDScriptParser : : get_builtin_type ( name ) < Variant : : VARIANT_MAX ) {
2023-04-03 13:57:41 +00:00
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_GLOBAL_IDENTIFIER , p_context , name , " built-in type " ) ;
2023-03-08 20:06:29 +00:00
return ;
2021-12-13 08:39:16 +00:00
}
}
2023-04-03 13:57:41 +00:00
if ( p_in_local_scope ) {
while ( base_class ! = nullptr ) {
if ( base_class - > has_member ( name ) ) {
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_VARIABLE , p_context , p_identifier - > name , base_class - > get_member ( name ) . get_type_name ( ) , itos ( base_class - > get_member ( name ) . get_line ( ) ) ) ;
return ;
}
base_class = base_class - > base_type . class_type ;
2020-06-11 22:31:28 +00:00
}
}
2022-12-11 02:57:35 +00:00
StringName parent = base . native_type ;
2020-06-11 22:31:28 +00:00
while ( parent ! = StringName ( ) ) {
2023-03-08 20:06:29 +00:00
ERR_FAIL_COND_MSG ( ! class_exists ( parent ) , " Non-existent native base class. " ) ;
2022-12-11 02:57:35 +00:00
2021-08-17 13:06:54 +00:00
if ( ClassDB : : has_method ( parent , name , true ) ) {
2023-04-03 13:57:41 +00:00
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_VARIABLE_BASE_CLASS , p_context , p_identifier - > name , " method " , parent ) ;
2023-03-08 20:06:29 +00:00
return ;
2021-08-17 13:06:54 +00:00
} else if ( ClassDB : : has_signal ( parent , name , true ) ) {
2023-04-03 13:57:41 +00:00
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_VARIABLE_BASE_CLASS , p_context , p_identifier - > name , " signal " , parent ) ;
2023-03-08 20:06:29 +00:00
return ;
2021-08-17 13:06:54 +00:00
} else if ( ClassDB : : has_property ( parent , name , true ) ) {
2023-04-03 13:57:41 +00:00
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_VARIABLE_BASE_CLASS , p_context , p_identifier - > name , " property " , parent ) ;
2023-03-08 20:06:29 +00:00
return ;
2021-08-17 13:06:54 +00:00
} else if ( ClassDB : : has_integer_constant ( parent , name , true ) ) {
2023-04-03 13:57:41 +00:00
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_VARIABLE_BASE_CLASS , p_context , p_identifier - > name , " constant " , parent ) ;
2023-03-08 20:06:29 +00:00
return ;
2021-08-17 13:06:54 +00:00
} else if ( ClassDB : : has_enum ( parent , name , true ) ) {
2023-04-03 13:57:41 +00:00
parser - > push_warning ( p_identifier , GDScriptWarning : : SHADOWED_VARIABLE_BASE_CLASS , p_context , p_identifier - > name , " enum " , parent ) ;
2023-03-08 20:06:29 +00:00
return ;
2020-06-11 22:31:28 +00:00
}
2021-08-17 13:06:54 +00:00
parent = ClassDB : : get_parent_class ( parent ) ;
2020-06-11 22:31:28 +00:00
}
}
2020-07-16 01:02:44 +00:00
# endif
2020-06-11 22:31:28 +00:00
2020-11-26 17:41:55 +00:00
GDScriptParser : : DataType GDScriptAnalyzer : : get_operation_type ( Variant : : Operator p_operation , const GDScriptParser : : DataType & p_a , bool & r_valid , const GDScriptParser : : Node * p_source ) {
// Unary version.
GDScriptParser : : DataType nil_type ;
nil_type . builtin_type = Variant : : NIL ;
2021-12-23 12:45:24 +00:00
nil_type . type_source = GDScriptParser : : DataType : : ANNOTATED_INFERRED ;
2020-11-26 17:41:55 +00:00
return get_operation_type ( p_operation , p_a , nil_type , r_valid , p_source ) ;
}
2020-06-10 22:53:25 +00:00
2020-11-26 17:41:55 +00:00
GDScriptParser : : DataType GDScriptAnalyzer : : get_operation_type ( Variant : : Operator p_operation , const GDScriptParser : : DataType & p_a , const GDScriptParser : : DataType & p_b , bool & r_valid , const GDScriptParser : : Node * p_source ) {
2023-03-10 19:01:17 +00:00
if ( p_operation = = Variant : : OP_AND | | p_operation = = Variant : : OP_OR ) {
// Those work for any type of argument and always return a boolean.
// They don't use the Variant operator since they have short-circuit semantics.
r_valid = true ;
GDScriptParser : : DataType result ;
result . type_source = GDScriptParser : : DataType : : ANNOTATED_INFERRED ;
result . kind = GDScriptParser : : DataType : : BUILTIN ;
result . builtin_type = Variant : : BOOL ;
return result ;
}
2020-06-10 22:53:25 +00:00
Variant : : Type a_type = p_a . builtin_type ;
Variant : : Type b_type = p_b . builtin_type ;
2022-02-02 16:57:24 +00:00
if ( p_a . kind = = GDScriptParser : : DataType : : ENUM ) {
if ( p_a . is_meta_type ) {
a_type = Variant : : DICTIONARY ;
} else {
a_type = Variant : : INT ;
}
}
if ( p_b . kind = = GDScriptParser : : DataType : : ENUM ) {
if ( p_b . is_meta_type ) {
b_type = Variant : : DICTIONARY ;
} else {
b_type = Variant : : INT ;
}
}
2023-02-22 21:38:29 +00:00
GDScriptParser : : DataType result ;
2021-12-23 12:45:24 +00:00
bool hard_operation = p_a . is_hard_type ( ) & & p_b . is_hard_type ( ) ;
2023-02-22 21:38:29 +00:00
if ( p_operation = = Variant : : OP_ADD & & a_type = = Variant : : ARRAY & & b_type = = Variant : : ARRAY ) {
2023-09-14 18:31:07 +00:00
if ( p_a . has_container_element_type ( 0 ) & & p_b . has_container_element_type ( 0 ) & & p_a . get_container_element_type ( 0 ) = = p_b . get_container_element_type ( 0 ) ) {
2023-02-22 21:38:29 +00:00
r_valid = true ;
result = p_a ;
result . type_source = hard_operation ? GDScriptParser : : DataType : : ANNOTATED_INFERRED : GDScriptParser : : DataType : : INFERRED ;
return result ;
}
}
Variant : : ValidatedOperatorEvaluator op_eval = Variant : : get_validated_operator_evaluator ( p_operation , a_type , b_type ) ;
2021-12-23 12:45:24 +00:00
bool validated = op_eval ! = nullptr ;
2022-12-30 07:58:07 +00:00
if ( validated ) {
2021-12-23 12:45:24 +00:00
r_valid = true ;
2022-12-30 07:58:07 +00:00
result . type_source = hard_operation ? GDScriptParser : : DataType : : ANNOTATED_INFERRED : GDScriptParser : : DataType : : INFERRED ;
2021-12-23 12:45:24 +00:00
result . kind = GDScriptParser : : DataType : : BUILTIN ;
result . builtin_type = Variant : : get_operator_return_type ( p_operation , a_type , b_type ) ;
2022-12-30 07:58:07 +00:00
} else {
r_valid = ! hard_operation ;
2021-12-23 12:45:24 +00:00
result . kind = GDScriptParser : : DataType : : VARIANT ;
2020-06-10 22:53:25 +00:00
}
return result ;
}
2022-01-27 14:34:33 +00:00
bool GDScriptAnalyzer : : is_type_compatible ( const GDScriptParser : : DataType & p_target , const GDScriptParser : : DataType & p_source , bool p_allow_implicit_conversion , const GDScriptParser : : Node * p_source_node ) {
2023-11-22 15:39:30 +00:00
# ifdef DEBUG_ENABLED
if ( p_source_node ) {
if ( p_target . kind = = GDScriptParser : : DataType : : ENUM ) {
if ( p_source . kind = = GDScriptParser : : DataType : : BUILTIN & & p_source . builtin_type = = Variant : : INT ) {
parser - > push_warning ( p_source_node , GDScriptWarning : : INT_AS_ENUM_WITHOUT_CAST ) ;
}
}
}
# endif
return check_type_compatibility ( p_target , p_source , p_allow_implicit_conversion , p_source_node ) ;
}
// TODO: Add safe/unsafe return variable (for variant cases)
bool GDScriptAnalyzer : : check_type_compatibility ( const GDScriptParser : : DataType & p_target , const GDScriptParser : : DataType & p_source , bool p_allow_implicit_conversion , const GDScriptParser : : Node * p_source_node ) {
2020-06-10 22:53:25 +00:00
// These return "true" so it doesn't affect users negatively.
ERR_FAIL_COND_V_MSG ( ! p_target . is_set ( ) , true , " Parser bug (please report): Trying to check compatibility of unset target type " ) ;
ERR_FAIL_COND_V_MSG ( ! p_source . is_set ( ) , true , " Parser bug (please report): Trying to check compatibility of unset value type " ) ;
if ( p_target . kind = = GDScriptParser : : DataType : : VARIANT ) {
// Variant can receive anything.
return true ;
}
if ( p_source . kind = = GDScriptParser : : DataType : : VARIANT ) {
// TODO: This is acceptable but unsafe. Make sure unsafe line is set.
return true ;
}
if ( p_target . kind = = GDScriptParser : : DataType : : BUILTIN ) {
bool valid = p_source . kind = = GDScriptParser : : DataType : : BUILTIN & & p_target . builtin_type = = p_source . builtin_type ;
if ( ! valid & & p_allow_implicit_conversion ) {
valid = Variant : : can_convert_strict ( p_source . builtin_type , p_target . builtin_type ) ;
}
2022-01-27 14:34:33 +00:00
if ( ! valid & & p_target . builtin_type = = Variant : : INT & & p_source . kind = = GDScriptParser : : DataType : : ENUM & & ! p_source . is_meta_type ) {
2020-06-12 00:49:58 +00:00
// Enum value is also integer.
valid = true ;
}
2021-03-09 15:32:35 +00:00
if ( valid & & p_target . builtin_type = = Variant : : ARRAY & & p_source . builtin_type = = Variant : : ARRAY ) {
// Check the element type.
2023-09-14 18:31:07 +00:00
if ( p_target . has_container_element_type ( 0 ) & & p_source . has_container_element_type ( 0 ) ) {
valid = p_target . get_container_element_type ( 0 ) = = p_source . get_container_element_type ( 0 ) ;
2021-03-09 15:32:35 +00:00
}
}
2023-06-24 18:03:28 +00:00
if ( valid & & p_target . builtin_type = = Variant : : DICTIONARY & & p_source . builtin_type = = Variant : : DICTIONARY ) {
// Check the element types.
if ( p_target . has_container_element_type ( 0 ) & & p_source . has_container_element_type ( 0 ) ) {
valid = p_target . get_container_element_type ( 0 ) = = p_source . get_container_element_type ( 0 ) ;
}
if ( valid & & p_target . has_container_element_type ( 1 ) & & p_source . has_container_element_type ( 1 ) ) {
valid = p_target . get_container_element_type ( 1 ) = = p_source . get_container_element_type ( 1 ) ;
}
}
2020-06-10 22:53:25 +00:00
return valid ;
}
2020-06-12 00:49:58 +00:00
if ( p_target . kind = = GDScriptParser : : DataType : : ENUM ) {
if ( p_source . kind = = GDScriptParser : : DataType : : BUILTIN & & p_source . builtin_type = = Variant : : INT ) {
return true ;
}
2021-08-15 19:25:47 +00:00
if ( p_source . kind = = GDScriptParser : : DataType : : ENUM ) {
if ( p_source . native_type = = p_target . native_type ) {
return true ;
}
}
2020-06-12 00:49:58 +00:00
return false ;
}
2020-06-10 22:53:25 +00:00
// From here on the target type is an object, so we have to test polymorphism.
if ( p_source . kind = = GDScriptParser : : DataType : : BUILTIN & & p_source . builtin_type = = Variant : : NIL ) {
// null is acceptable in object.
return true ;
}
StringName src_native ;
Ref < Script > src_script ;
const GDScriptParser : : ClassNode * src_class = nullptr ;
switch ( p_source . kind ) {
case GDScriptParser : : DataType : : NATIVE :
if ( p_target . kind ! = GDScriptParser : : DataType : : NATIVE ) {
// Non-native class cannot be supertype of native.
return false ;
}
if ( p_source . is_meta_type ) {
src_native = GDScriptNativeClass : : get_class_static ( ) ;
} else {
src_native = p_source . native_type ;
}
break ;
case GDScriptParser : : DataType : : SCRIPT :
if ( p_target . kind = = GDScriptParser : : DataType : : CLASS ) {
// A script type cannot be a subtype of a GDScript class.
return false ;
}
2024-06-24 21:35:52 +00:00
if ( p_source . script_type . is_null ( ) ) {
return false ;
}
2020-06-10 22:53:25 +00:00
if ( p_source . is_meta_type ) {
src_native = p_source . script_type - > get_class_name ( ) ;
} else {
src_script = p_source . script_type ;
src_native = src_script - > get_instance_base_type ( ) ;
}
break ;
case GDScriptParser : : DataType : : CLASS :
if ( p_source . is_meta_type ) {
src_native = GDScript : : get_class_static ( ) ;
} else {
src_class = p_source . class_type ;
const GDScriptParser : : ClassNode * base = src_class ;
while ( base - > base_type . kind = = GDScriptParser : : DataType : : CLASS ) {
base = base - > base_type . class_type ;
}
src_native = base - > base_type . native_type ;
src_script = base - > base_type . script_type ;
}
break ;
case GDScriptParser : : DataType : : VARIANT :
case GDScriptParser : : DataType : : BUILTIN :
2020-06-12 00:49:58 +00:00
case GDScriptParser : : DataType : : ENUM :
2022-12-11 02:57:35 +00:00
case GDScriptParser : : DataType : : RESOLVING :
2020-06-10 22:53:25 +00:00
case GDScriptParser : : DataType : : UNRESOLVED :
break ; // Already solved before.
}
switch ( p_target . kind ) {
case GDScriptParser : : DataType : : NATIVE : {
if ( p_target . is_meta_type ) {
return ClassDB : : is_parent_class ( src_native , GDScriptNativeClass : : get_class_static ( ) ) ;
}
2021-08-17 13:06:54 +00:00
return ClassDB : : is_parent_class ( src_native , p_target . native_type ) ;
2020-06-10 22:53:25 +00:00
}
case GDScriptParser : : DataType : : SCRIPT :
if ( p_target . is_meta_type ) {
return ClassDB : : is_parent_class ( src_native , p_target . script_type - > get_class_name ( ) ) ;
}
while ( src_script . is_valid ( ) ) {
if ( src_script = = p_target . script_type ) {
return true ;
}
src_script = src_script - > get_base_script ( ) ;
}
return false ;
case GDScriptParser : : DataType : : CLASS :
if ( p_target . is_meta_type ) {
return ClassDB : : is_parent_class ( src_native , GDScript : : get_class_static ( ) ) ;
}
while ( src_class ! = nullptr ) {
2022-11-27 07:56:53 +00:00
if ( src_class = = p_target . class_type | | src_class - > fqcn = = p_target . class_type - > fqcn ) {
2020-06-10 22:53:25 +00:00
return true ;
}
src_class = src_class - > base_type . class_type ;
}
return false ;
case GDScriptParser : : DataType : : VARIANT :
case GDScriptParser : : DataType : : BUILTIN :
2020-06-12 00:49:58 +00:00
case GDScriptParser : : DataType : : ENUM :
2022-12-11 02:57:35 +00:00
case GDScriptParser : : DataType : : RESOLVING :
2020-06-10 22:53:25 +00:00
case GDScriptParser : : DataType : : UNRESOLVED :
break ; // Already solved before.
}
return false ;
}
void GDScriptAnalyzer : : push_error ( const String & p_message , const GDScriptParser : : Node * p_origin ) {
mark_node_unsafe ( p_origin ) ;
parser - > push_error ( p_message , p_origin ) ;
}
void GDScriptAnalyzer : : mark_node_unsafe ( const GDScriptParser : : Node * p_node ) {
2020-07-16 01:02:44 +00:00
# ifdef DEBUG_ENABLED
2022-12-11 02:57:35 +00:00
if ( p_node = = nullptr ) {
return ;
}
2020-06-10 22:53:25 +00:00
for ( int i = p_node - > start_line ; i < = p_node - > end_line ; i + + ) {
parser - > unsafe_lines . insert ( i ) ;
}
2020-07-16 01:02:44 +00:00
# endif
2020-06-10 22:53:25 +00:00
}
2022-12-30 07:58:07 +00:00
void GDScriptAnalyzer : : downgrade_node_type_source ( GDScriptParser : : Node * p_node ) {
GDScriptParser : : IdentifierNode * identifier = nullptr ;
if ( p_node - > type = = GDScriptParser : : Node : : IDENTIFIER ) {
identifier = static_cast < GDScriptParser : : IdentifierNode * > ( p_node ) ;
} else if ( p_node - > type = = GDScriptParser : : Node : : SUBSCRIPT ) {
GDScriptParser : : SubscriptNode * subscript = static_cast < GDScriptParser : : SubscriptNode * > ( p_node ) ;
if ( subscript - > is_attribute ) {
identifier = subscript - > attribute ;
}
}
if ( identifier = = nullptr ) {
return ;
}
GDScriptParser : : Node * source = nullptr ;
switch ( identifier - > source ) {
case GDScriptParser : : IdentifierNode : : MEMBER_VARIABLE : {
source = identifier - > variable_source ;
} break ;
case GDScriptParser : : IdentifierNode : : FUNCTION_PARAMETER : {
source = identifier - > parameter_source ;
} break ;
case GDScriptParser : : IdentifierNode : : LOCAL_VARIABLE : {
source = identifier - > variable_source ;
} break ;
case GDScriptParser : : IdentifierNode : : LOCAL_ITERATOR : {
source = identifier - > bind_source ;
} break ;
default :
break ;
}
if ( source = = nullptr ) {
return ;
}
GDScriptParser : : DataType datatype ;
datatype . kind = GDScriptParser : : DataType : : VARIANT ;
source - > set_datatype ( datatype ) ;
}
2022-04-20 17:22:22 +00:00
void GDScriptAnalyzer : : mark_lambda_use_self ( ) {
2023-08-23 09:37:18 +00:00
GDScriptParser : : LambdaNode * lambda = current_lambda ;
while ( lambda ! = nullptr ) {
2022-04-20 17:22:22 +00:00
lambda - > use_self = true ;
2023-08-23 09:37:18 +00:00
lambda = lambda - > parent_lambda ;
}
}
void GDScriptAnalyzer : : resolve_pending_lambda_bodies ( ) {
if ( pending_body_resolution_lambdas . is_empty ( ) ) {
return ;
2022-04-20 17:22:22 +00:00
}
2023-08-23 09:37:18 +00:00
GDScriptParser : : LambdaNode * previous_lambda = current_lambda ;
2023-10-17 09:46:41 +00:00
bool previous_static_context = static_context ;
2023-08-23 09:37:18 +00:00
List < GDScriptParser : : LambdaNode * > lambdas = pending_body_resolution_lambdas ;
pending_body_resolution_lambdas . clear ( ) ;
for ( GDScriptParser : : LambdaNode * lambda : lambdas ) {
current_lambda = lambda ;
2023-10-17 09:46:41 +00:00
static_context = lambda - > function - > is_static ;
2023-08-23 09:37:18 +00:00
resolve_function_body ( lambda - > function , true ) ;
int captures_amount = lambda - > captures . size ( ) ;
if ( captures_amount > 0 ) {
// Create space for lambda parameters.
// At the beginning to not mess with optional parameters.
int param_count = lambda - > function - > parameters . size ( ) ;
lambda - > function - > parameters . resize ( param_count + captures_amount ) ;
for ( int i = param_count - 1 ; i > = 0 ; i - - ) {
lambda - > function - > parameters . write [ i + captures_amount ] = lambda - > function - > parameters [ i ] ;
lambda - > function - > parameters_indices [ lambda - > function - > parameters [ i ] - > identifier - > name ] = i + captures_amount ;
}
// Add captures as extra parameters at the beginning.
for ( int i = 0 ; i < lambda - > captures . size ( ) ; i + + ) {
GDScriptParser : : IdentifierNode * capture = lambda - > captures [ i ] ;
GDScriptParser : : ParameterNode * capture_param = parser - > alloc_node < GDScriptParser : : ParameterNode > ( ) ;
capture_param - > identifier = capture ;
capture_param - > usages = capture - > usages ;
capture_param - > set_datatype ( capture - > get_datatype ( ) ) ;
lambda - > function - > parameters . write [ i ] = capture_param ;
lambda - > function - > parameters_indices [ capture - > name ] = i ;
}
}
}
current_lambda = previous_lambda ;
2023-10-17 09:46:41 +00:00
static_context = previous_static_context ;
2022-04-20 17:22:22 +00:00
}
2021-03-09 15:32:35 +00:00
bool GDScriptAnalyzer : : class_exists ( const StringName & p_class ) const {
2021-08-17 13:06:54 +00:00
return ClassDB : : class_exists ( p_class ) & & ClassDB : : is_class_exposed ( p_class ) ;
2020-06-10 22:53:25 +00:00
}
Error GDScriptAnalyzer : : resolve_inheritance ( ) {
2022-12-11 02:57:35 +00:00
return resolve_class_inheritance ( parser - > head , true ) ;
2020-06-10 22:53:25 +00:00
}
Error GDScriptAnalyzer : : resolve_interface ( ) {
2022-12-11 02:57:35 +00:00
resolve_class_interface ( parser - > head , true ) ;
2020-12-15 12:04:21 +00:00
return parser - > errors . is_empty ( ) ? OK : ERR_PARSE_ERROR ;
2020-06-10 22:53:25 +00:00
}
Error GDScriptAnalyzer : : resolve_body ( ) {
2022-12-11 02:57:35 +00:00
resolve_class_body ( parser - > head , true ) ;
2024-04-12 23:13:25 +00:00
# ifdef DEBUG_ENABLED
// Apply here, after all `@warning_ignore`s have been resolved and applied.
parser - > apply_pending_warnings ( ) ;
# endif
2020-12-15 12:04:21 +00:00
return parser - > errors . is_empty ( ) ? OK : ERR_PARSE_ERROR ;
2020-06-10 22:53:25 +00:00
}
2022-12-11 02:57:35 +00:00
Error GDScriptAnalyzer : : resolve_dependencies ( ) {
2024-04-12 23:13:25 +00:00
for ( KeyValue < String , Ref < GDScriptParserRef > > & K : parser - > depended_parsers ) {
2022-05-08 08:09:19 +00:00
if ( K . value . is_null ( ) ) {
2020-08-26 17:50:27 +00:00
return ERR_PARSE_ERROR ;
}
2022-12-11 02:57:35 +00:00
K . value - > raise_status ( GDScriptParserRef : : INHERITANCE_SOLVED ) ;
2020-06-11 22:31:28 +00:00
}
2022-12-11 02:57:35 +00:00
2020-12-15 12:04:21 +00:00
return parser - > errors . is_empty ( ) ? OK : ERR_PARSE_ERROR ;
2020-05-01 22:14:56 +00:00
}
Error GDScriptAnalyzer : : analyze ( ) {
parser - > errors . clear ( ) ;
2022-12-11 02:57:35 +00:00
2024-02-28 14:23:11 +00:00
Error err = resolve_inheritance ( ) ;
2020-05-01 22:14:56 +00:00
if ( err ) {
return err ;
}
2022-12-11 02:57:35 +00:00
resolve_interface ( ) ;
2024-04-12 23:13:25 +00:00
err = resolve_body ( ) ;
if ( err ) {
return err ;
2022-12-11 02:57:35 +00:00
}
return resolve_dependencies ( ) ;
2020-05-01 22:14:56 +00:00
}
GDScriptAnalyzer : : GDScriptAnalyzer ( GDScriptParser * p_parser ) {
parser = p_parser ;
}