@ -42,6 +42,9 @@
# include "gdscript_utility_functions.h"
# include "scene/resources/packed_scene.h"
# define UNNAMED_ENUM "<anonymous enum>"
# define ENUM_SEPARATOR "::"
static MethodInfo info_from_utility_func ( const StringName & p_function ) {
ERR_FAIL_COND_V ( ! Variant : : has_utility_function ( p_function ) , MethodInfo ( ) ) ;
@ -106,13 +109,26 @@ static GDScriptParser::DataType make_native_meta_type(const StringName &p_class_
return type ;
}
static GDScriptParser : : DataType make_native_enum_type ( const StringName & p_native_class , const StringName & p_enum_name ) {
// 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 ) {
GDScriptParser : : DataType type ;
type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
type . kind = GDScriptParser : : DataType : : ENUM ;
type . builtin_type = Variant : : INT ;
type . builtin_type = p_meta ? Variant : : DICTIONARY : Variant : : INT ;
type . enum_type = p_enum_name ;
type . is_constant = true ;
type . is_meta_type = true ;
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
type . native_type = p_base_name + ENUM_SEPARATOR + p_enum_name ;
return type ;
}
static GDScriptParser : : DataType make_native_enum_type ( const StringName & p_enum_name , const StringName & p_native_class , const bool p_meta = true ) {
GDScriptParser : : DataType type = make_enum_type ( p_enum_name , p_native_class , p_meta ) ;
List < StringName > enum_values ;
ClassDB : : get_enum_constants ( p_native_class , p_enum_name , & enum_values ) ;
@ -134,6 +150,19 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) {
return type ;
}
static StringName enum_get_value_name ( const GDScriptParser : : DataType p_type , int64_t p_val ) {
// Check that an enum has a given value, not key.
// Make sure that implicit conversion to int64_t is sensible before calling!
HashMap < StringName , int64_t > : : ConstIterator i = p_type . enum_values . begin ( ) ;
while ( i ) {
if ( i - > value = = p_val ) {
return i - > key ;
}
+ + i ;
}
return StringName ( ) ;
}
bool GDScriptAnalyzer : : has_member_name_conflict_in_script_class ( const StringName & p_member_name , const GDScriptParser : : ClassNode * p_class , const GDScriptParser : : Node * p_member ) {
if ( p_class - > members_indices . has ( p_member_name ) ) {
int index = p_class - > members_indices [ p_member_name ] ;
@ -192,6 +221,7 @@ Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_me
}
Error GDScriptAnalyzer : : check_class_member_name_conflict ( const GDScriptParser : : ClassNode * p_class_node , const StringName & p_member_name , const GDScriptParser : : Node * p_member_node ) {
// TODO check outer classes for static members only
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 ;
@ -591,12 +621,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
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 ( parser- > current_class - > base_type . native_type , first ) ;
result = make_native_enum_type ( first, parser- > current_class - > base_type . native_type ) ;
} else {
// Classes in current scope.
List < GDScriptParser : : ClassNode * > script_classes ;
bool found = false ;
get_class_node_current_scope_classes ( parser - > current_class , & script_classes ) ;
for ( GDScriptParser : : ClassNode * script_class : script_classes ) {
if ( found ) {
break ;
}
if ( script_class - > identifier & & script_class - > identifier - > name = = first ) {
result = script_class - > get_datatype ( ) ;
break ;
@ -608,14 +643,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
switch ( member . type ) {
case GDScriptParser : : ClassNode : : Member : : CLASS :
result = member . get_datatype ( ) ;
found = true ;
break ;
case GDScriptParser : : ClassNode : : Member : : ENUM :
result = member . get_datatype ( ) ;
found = true ;
break ;
case GDScriptParser : : ClassNode : : Member : : CONSTANT :
if ( member . get_datatype ( ) . is_meta_type ) {
result = member . get_datatype ( ) ;
result . is_meta_type = false ;
found = true ;
break ;
} else if ( Ref < Script > ( member . constant - > initializer - > reduced_value ) . is_valid ( ) ) {
Ref < GDScript > gdscript = member . constant - > initializer - > reduced_value ;
@ -636,6 +674,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result . native_type = script - > get_instance_base_type ( ) ;
result . is_meta_type = false ;
}
found = true ;
break ;
}
[[fallthrough]] ;
@ -667,15 +706,17 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
} else if ( result . kind = = GDScriptParser : : DataType : : NATIVE ) {
// Only enums allowed for native.
if ( ! ClassDB : : has_enum ( result . native_type , p_type - > type_chain [ 1 ] - > name ) ) {
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 ] ) ;
return bad_type ;
}
if ( p_type - > type_chain . size ( ) > 2 ) {
push_error ( R " (Enums cannot contain nested types.) " , p_type - > type_chain [ 2 ] ) ;
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 ] ) ;
return bad_type ;
}
result = make_native_enum_type ( result . native_type , p_type - > type_chain [ 1 ] - > name ) ;
} 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 ] ) ;
return bad_type ;
@ -804,15 +845,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
check_class_member_name_conflict ( p_class , member . m_enum - > identifier - > name , member . m_enum ) ;
member . m_enum - > set_datatype ( resolving_datatype ) ;
GDScriptParser : : DataType enum_type ;
enum_type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
enum_type . kind = GDScriptParser : : DataType : : ENUM ;
enum_type . builtin_type = Variant : : DICTIONARY ;
enum_type . enum_type = member . m_enum - > identifier - > name ;
enum_type . native_type = p_class - > fqcn + " . " + member . m_enum - > identifier - > name ;
enum_type . is_meta_type = true ;
enum_type . is_constant = true ;
GDScriptParser : : DataType enum_type = make_enum_type ( member . m_enum - > identifier - > name , p_class - > fqcn , true ) ;
const GDScriptParser : : EnumNode * prev_enum = current_enum ;
current_enum = member . m_enum ;
@ -846,6 +879,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
current_enum = prev_enum ;
dictionary . set_read_only ( true ) ;
member . m_enum - > set_datatype ( enum_type ) ;
member . m_enum - > dictionary = dictionary ;
@ -892,11 +926,7 @@ void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class,
// Also update the original references.
member . enum_value . parent_enum - > values . set ( member . enum_value . index , member . enum_value ) ;
GDScriptParser : : DataType datatype ;
datatype . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
datatype . kind = GDScriptParser : : DataType : : BUILTIN ;
datatype . builtin_type = Variant : : INT ;
member . enum_value . identifier - > set_datatype ( datatype ) ;
member . enum_value . identifier - > set_datatype ( make_enum_type ( UNNAMED_ENUM , p_class - > fqcn , false ) ) ;
} break ;
case GDScriptParser : : ClassNode : : Member : : CLASS :
check_class_member_name_conflict ( p_class , member . m_class - > identifier - > name , member . m_class ) ;
@ -1330,7 +1360,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
}
} else {
if ( p_function - > return_type ! = nullptr ) {
p_function - > set_datatype ( resolve_datatype( p_function - > return_type ) ) ;
p_function - > set_datatype ( type_from_metatype( resolve_datatype( p_function - > return_type ) ) ) ;
} 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.
@ -1364,7 +1394,7 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
if ( ! valid ) {
// Compute parent signature as a string to show in the error message.
String parent_signature = function_name. operator String ( ) + " ( " ;
String parent_signature = String( function_name) + " ( " ;
int j = 0 ;
for ( const GDScriptParser : : DataType & par_type : parameters_types ) {
if ( j > 0 ) {
@ -2081,13 +2111,15 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
p_assignment - > set_datatype ( op_type ) ;
// If Assignee is a variant, then you can assign anything
// When the assigned value has a known type, further checks are possible.
if ( assignee_type . is_hard_type ( ) & & ! assignee_type . is_variant ( ) & & op_type . is_hard_type ( ) ) {
if ( compatible ) {
compatible = is_type_compatible ( assignee_type , op_type , true , p_assignment - > assigned_value ) ;
if ( ! compatible ) {
// Try reverse test since it can be a masked subtype.
if ( ! is_type_compatible ( op_type , assignee_type , true ) ) {
push_error ( vformat ( R " ( Cannot assign a value of type " % s " to a target of type " % s " .) " , assigned_value_type . to_string ( ) , assignee_type . to_string ( ) ) , p_assignment - > assigned_value ) ;
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 {
// TODO: Add warning.
mark_node_unsafe ( p_assignment ) ;
@ -2141,7 +2173,7 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
if ( ! id_type . is_hard_type ( ) ) {
id_type . kind = GDScriptParser : : DataType : : VARIANT ;
id_type . type_source = GDScriptParser : : DataType : : UNDETECTED ;
identifier - > variable _source- > set_datatype ( id_type ) ;
identifier - > bind _source- > set_datatype ( id_type ) ;
}
} break ;
default :
@ -2368,7 +2400,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
switch ( err . error ) {
case Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT :
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 ,
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 ,
Variant : : get_type_name ( Variant : : Type ( err . expected ) ) , p_call - > arguments [ err . argument ] - > get_datatype ( ) . to_string ( ) ) ,
p_call - > arguments [ err . argument ] ) ;
break ;
@ -2484,7 +2516,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
switch ( err . error ) {
case Callable : : CallError : : CALL_ERROR_INVALID_ARGUMENT : {
PropertyInfo wrong_arg = function_info . arguments [ err . argument ] ;
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: argument %d should be %s but is %s.)*" , function_name , err . argument + 1 ,
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: argument %d should be " % s" but is " % s" .)*" , function_name , err . argument + 1 ,
type_from_property ( wrong_arg ) . to_string ( ) , p_call - > arguments [ err . argument ] - > get_datatype ( ) . to_string ( ) ) ,
p_call - > arguments [ err . argument ] ) ;
} break ;
@ -2537,7 +2569,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
expected_type_name = Variant : : get_type_name ( ( Variant : : Type ) err . expected ) ;
}
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: argument %d should be %s but is %s.)*" , function_name , err . argument + 1 ,
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: argument %d should be " % s" but is " % s" .)*" , function_name , err . argument + 1 ,
expected_type_name , p_call - > arguments [ err . argument ] - > get_datatype ( ) . to_string ( ) ) ,
p_call - > arguments [ err . argument ] ) ;
} break ;
@ -2683,8 +2715,10 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
} else {
bool found = false ;
// Check if the name exists as something else.
if ( ! p_call - > is_super & & callee_type ! = GDScriptParser : : Node : : NONE ) {
// Enums do not have functions other than the built-in dictionary ones.
if ( base_type . kind = = GDScriptParser : : DataType : : ENUM & & base_type . is_meta_type ) {
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 if ( ! p_call - > is_super & & callee_type ! = GDScriptParser : : Node : : NONE ) { // Check if the name exists as something else.
GDScriptParser : : IdentifierNode * callee_id ;
if ( callee_type = = GDScriptParser : : Node : : IDENTIFIER ) {
callee_id = static_cast < GDScriptParser : : IdentifierNode * > ( p_call - > callee ) ;
@ -2714,7 +2748,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
String base_name = is_self & & ! p_call - > is_super ? " self " : base_type . to_string ( ) ;
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 ) ;
} else if ( ! found & & ( ! p_call - > is_super & & base_type . is_hard_type ( ) & & base_type . kind = = GDScriptParser : : DataType : : NATIVE & & base_type . is_meta_type ) ) {
push_error ( vformat ( R " *(Static function " % s ( ) " not found in base " % s " .)* " , p_call - > function_name , base_type . native_type .operator String ( ) ), p_call ) ;
push_error ( vformat ( R " *(Static function " % s ( ) " not found in base " % s " .)* " , p_call - > function_name , base_type . native_type ), p_call ) ;
}
}
@ -2742,19 +2776,48 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
GDScriptParser : : DataType op_type = p_cast - > operand - > get_datatype ( ) ;
if ( ! op_type . is_variant ( ) ) {
bool valid = false ;
bool more_informative_error = false ;
if ( op_type . kind = = GDScriptParser : : DataType : : ENUM & & cast_type . kind = = GDScriptParser : : DataType : : ENUM ) {
// Enum types are compatible between each other, so it's a safe cast.
valid = true ;
// Enum casts are compatible when value from operand exists in target enum
if ( p_cast - > operand - > is_constant & & p_cast - > operand - > reduced ) {
if ( enum_get_value_name ( cast_type , p_cast - > operand - > reduced_value ) ! = StringName ( ) ) {
valid = true ;
} else {
valid = false ;
more_informative_error = true ;
push_error ( vformat ( R " (Invalid cast. Enum " % s " does not have value corresponding to " % s . % s " (%d).) " ,
cast_type . to_string ( ) , op_type . enum_type ,
enum_get_value_name ( op_type , p_cast - > operand - > reduced_value ) , // Can never be null
p_cast - > operand - > reduced_value . operator uint64_t ( ) ) ,
p_cast - > cast_type ) ;
}
} else {
// Can't statically tell whether int has a corresponding enum value. Valid but dangerous!
mark_node_unsafe ( p_cast ) ;
valid = true ;
}
} else if ( op_type . kind = = GDScriptParser : : DataType : : BUILTIN & & op_type . builtin_type = = Variant : : INT & & cast_type . kind = = GDScriptParser : : DataType : : ENUM ) {
// Convertint int to enum is always valid.
valid = true ;
// Int assignment to enum not valid when exact int assigned is known but is not an enum value
if ( p_cast - > operand - > is_constant & & p_cast - > operand - > reduced ) {
if ( enum_get_value_name ( cast_type , p_cast - > operand - > reduced_value ) ! = StringName ( ) ) {
valid = true ;
} else {
valid = false ;
more_informative_error = true ;
push_error ( vformat ( R " (Invalid cast. Enum " % s " does not have enum value %d.) " , cast_type . to_string ( ) , p_cast - > operand - > reduced_value . operator uint64_t ( ) ) , p_cast - > cast_type ) ;
}
} else {
// Can't statically tell whether int has a corresponding enum value. Valid but dangerous!
mark_node_unsafe ( p_cast ) ;
valid = true ;
}
} else if ( op_type . kind = = GDScriptParser : : DataType : : BUILTIN & & cast_type . kind = = GDScriptParser : : DataType : : BUILTIN ) {
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 ) ;
}
if ( ! valid ) {
if ( ! valid & & ! more_informative_error ) {
push_error ( vformat ( R " (Invalid cast. Cannot convert from " % s " to " % s " .) " , op_type . to_string ( ) , cast_type . to_string ( ) ) , p_cast - > cast_type ) ;
}
}
@ -2869,26 +2932,14 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
if ( base . kind = = GDScriptParser : : DataType : : ENUM ) {
if ( base . is_meta_type ) {
if ( base . enum_values . has ( name ) ) {
p_identifier - > set_datatype ( type_from_metatype ( base ) ) ;
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = base . enum_values [ name ] ;
GDScriptParser : : DataType result ;
result . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
result . kind = GDScriptParser : : DataType : : ENUM ;
result . is_constant = true ;
result . builtin_type = Variant : : INT ;
result . native_type = base . native_type ;
result . enum_type = base . enum_type ;
result . enum_values = base . enum_values ;
p_identifier - > set_datatype ( result ) ;
return ;
} else {
// Consider as a Dictionary, so it can be anything.
// This will be evaluated in the next if block.
base . kind = GDScriptParser : : DataType : : BUILTIN ;
base . builtin_type = Variant : : DICTIONARY ;
base . is_meta_type = false ;
}
// Enum does not have this value, return.
return ;
} else {
push_error ( R " (Cannot get property from enum value.) " , p_identifier ) ;
return ;
@ -3000,12 +3051,18 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
return ;
}
// Check outer constants.
// TODO: Allow outer static functions.
if ( base_class - > outer ! = nullptr ) {
List < GDScriptParser : : ClassNode * > script_classes ;
get_class_node_current_scope_classes ( base_class - > outer , & script_classes ) ;
for ( GDScriptParser : : ClassNode * script_class : script_classes ) {
if ( script_class - > identifier & & script_class - > identifier - > name = = name ) {
p_identifier - > set_datatype ( script_class - > get_datatype ( ) ) ;
return ;
}
if ( script_class - > has_member ( name ) ) {
resolve_class_member ( script_class , name , p_identifier ) ;
@ -3067,35 +3124,39 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
return ;
}
if ( ClassDB : : has_enum ( native , name ) ) {
p_identifier - > set_datatype ( make_native_enum_type ( na tive, nam e) ) ;
p_identifier - > set_datatype ( make_native_enum_type ( na me, nativ e) ) ;
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT ;
return ;
}
bool valid = false ;
int64_t int_constant = ClassDB : : get_integer_constant ( native , name , & valid ) ;
if ( valid ) {
p_identifier - > is_constant = true ;
p_identifier - > reduced_value = int_constant ;
p_identifier - > set_datatype ( type_from_variant ( int_constant , p_identifier ) ) ;
p_identifier - > source = GDScriptParser : : IdentifierNode : : MEMBER_CONSTANT ;
return ;
// 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 ) ) ;
}
}
}
}
void GDScriptAnalyzer : : reduce_identifier ( GDScriptParser : : IdentifierNode * p_identifier , bool can_be_builtin ) {
// TODO: This is opportunity to further infer types.
// TODO: This is an opportunity to further infer types.
// Check if we are inside an d enum. This allows enum values to access other elements of the same enum.
// Check if we are inside an enum. This allows enum values to access other elements of the same enum.
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 ) {
GDScriptParser : : DataType type ;
type . type_source = GDScriptParser : : DataType : : ANNOTATED_EXPLICIT ;
type . kind = element . parent_enum - > identifier ? GDScriptParser : : DataType : : ENUM : GDScriptParser : : DataType : : BUILTIN ;
type . builtin_type = Variant : : INT ;
type . is_constant = true ;
StringName enum_name = current_enum - > identifier - > name ? current_enum - > identifier - > name : UNNAMED_ENUM ;
GDScriptParser : : DataType type = make_enum_type ( enum_name , parser - > current_class - > fqcn , false ) ;
if ( element . parent_enum - > identifier ) {
type . enum_type = element . parent_enum - > identifier - > name ;
}
@ -3865,12 +3926,13 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_va
return result ;
}
GDScriptParser : : DataType GDScriptAnalyzer : : type_from_metatype ( const GDScriptParser : : DataType & p_meta_type ) const {
GDScriptParser : : DataType GDScriptAnalyzer : : type_from_metatype ( const GDScriptParser : : DataType & p_meta_type ) {
GDScriptParser : : DataType result = p_meta_type ;
result . is_meta_type = false ;
result . is_constant = false ;
if ( p_meta_type . kind = = GDScriptParser : : DataType : : ENUM ) {
result . builtin_type = Variant : : INT ;
} else {
result . is_constant = false ;
}
return result ;
}
@ -3928,11 +3990,12 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
r_default_arg_count = 0 ;
StringName function_name = p_function ;
bool was_enum = false ;
if ( p_base_type . kind = = GDScriptParser : : DataType : : ENUM ) {
was_enum = true ;
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 . builtin_type = Variant : : DICTIONARY ;
p_base_type . is_meta_type = false ;
} else {
push_error ( " Cannot call function on enum value. " , p_source ) ;
@ -3955,6 +4018,10 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, bo
if ( E . name = = p_function ) {
function_signature_from_info ( E , r_return_type , r_par_types , r_default_arg_count , r_static , r_vararg ) ;
r_static = Variant : : is_builtin_method_static ( p_base_type . builtin_type , function_name ) ;
// Cannot use non-const methods on enums.
if ( ! r_static & & was_enum & & ! ( E . flags & METHOD_FLAG_CONST ) ) {
push_error ( vformat ( R " *(Cannot call non-const Dictionary function " % s ( ) " on enum " % s " .)* " , p_function , p_base_type . enum_type ) , p_source ) ;
}
return true ;
}
}
@ -4102,7 +4169,7 @@ bool GDScriptAnalyzer::validate_call_arg(const List<GDScriptParser::DataType> &p
// Supertypes are acceptable for dynamic compliance, but it's unsafe.
mark_node_unsafe ( p_call ) ;
if ( ! is_type_compatible ( arg_type , par_type ) ) {
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: argument %d should be %s but is %s.)*" ,
push_error ( vformat ( R " *(Invalid argument for " % s ( ) " function: argument %d should be " % s" but is " % s" .)*" ,
p_call - > function_name , i + 1 , par_type . to_string ( ) , arg_type . to_string ( ) ) ,
p_call - > arguments [ i ] ) ;
valid = false ;