@ -875,18 +875,32 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
GDScriptParser : : ClassNode * previous_class = parser - > current_class ;
parser - > current_class = p_class ;
// Do functions now.
// Do functions and properties now.
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
GDScriptParser : : ClassNode : : Member member = p_class - > members [ i ] ;
if ( member . type ! = GDScriptParser : : ClassNode : : Member : : FUNCTION ) {
continue ;
}
if ( member . type = = GDScriptParser : : ClassNode : : Member : : FUNCTION ) {
resolve_function_body ( member . function ) ;
resolve_function_body ( member . function ) ;
// Apply annotations.
for ( GDScriptParser : : AnnotationNode * & E : member . function - > annotations ) {
E - > apply ( parser , member . function ) ;
}
} 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 ) {
member . variable - > getter - > set_datatype ( member . variable - > datatype ) ;
// Apply annotations.
for ( GDScriptParser : : AnnotationNode * & E : member . function - > annotations ) {
E - > apply ( parser , member . function ) ;
resolve_function_body ( member . variable - > getter ) ;
}
if ( member . variable - > setter ! = nullptr ) {
if ( member . variable - > setter - > parameters . size ( ) > 0 ) {
member . variable - > setter - > parameters [ 0 ] - > datatype_specifier = member . variable - > datatype_specifier ;
}
resolve_function_signature ( member . variable - > setter ) ;
resolve_function_body ( member . variable - > setter ) ;
}
}
}
}
@ -902,17 +916,80 @@ void GDScriptAnalyzer::resolve_class_body(GDScriptParser::ClassNode *p_class) {
resolve_class_body ( member . m_class ) ;
}
// Check unused variables .
// Check unused variables and datatypes of property getters and setters .
for ( int i = 0 ; i < p_class - > members . size ( ) ; i + + ) {
GDScriptParser : : ClassNode : : Member member = p_class - > members [ i ] ;
if ( member . type ! = GDScriptParser : : ClassNode : : Member : : VARIABLE ) {
continue ;
}
if ( member . type = = GDScriptParser : : ClassNode : : Member : : VARIABLE ) {
# ifdef DEBUG_ENABLED
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 ) ;
}
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 if ( getter_function - > parameters . size ( ) ! = 0 | | getter_function - > 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 , getter_function - > datatype , true ) ) {
push_error ( vformat ( R " (Function with return type " % s " cannot be used as getter for a property of type " % s " .) " , getter_function - > datatype . to_string ( ) , member . variable - > datatype . to_string ( ) ) , member . variable ) ;
} else {
has_valid_getter = true ;
# ifdef DEBUG_ENABLED
if ( member . variable - > datatype . builtin_type = = Variant : : INT & & getter_function - > datatype . builtin_type = = Variant : : FLOAT ) {
parser - > push_warning ( member . variable , GDScriptWarning : : NARROWING_CONVERSION ) ;
}
# endif
}
}
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
if ( member . variable - > datatype . builtin_type = = Variant : : INT & & setter_function - > return_type - > datatype . builtin_type = = Variant : : FLOAT ) {
parser - > push_warning ( member . variable , GDScriptWarning : : NARROWING_CONVERSION ) ;
}
# endif
}
}
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 ) ;
}
}
}
}
}
}
@ -1529,12 +1606,20 @@ void GDScriptAnalyzer::resolve_parameter(GDScriptParser::ParameterNode *p_parame
void GDScriptAnalyzer : : resolve_return ( GDScriptParser : : ReturnNode * p_return ) {
GDScriptParser : : DataType result ;
GDScriptParser : : DataType expected_type ;
bool has_expected_type = false ;
if ( parser - > current_function ! = nullptr ) {
expected_type = parser - > current_function - > get_datatype ( ) ;
has_expected_type = true ;
}
if ( p_return - > return_value ! = nullptr ) {
reduce_expression ( p_return - > return_value ) ;
if ( p_return - > return_value - > type = = GDScriptParser : : Node : : ARRAY ) {
// Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
if ( parser - > current_function - > get_datatype ( ) . has_container_element_type ( ) & & p_return - > return_value - > type = = GDScriptParser : : Node : : ARRAY ) {
update_array_literal_element_type ( parser - > current_function - > get_datatype ( ) , static_cast < GDScriptParser : : ArrayNode * > ( p_return - > return_value ) ) ;
if ( has_expected_type & & expected_type . has_container_element_type ( ) & & p_return - > return_value - > type = = GDScriptParser : : Node : : ARRAY ) {
update_array_literal_element_type ( expected_type , static_cast < GDScriptParser : : ArrayNode * > ( p_return - > return_value ) ) ;
}
}
result = p_return - > return_value - > get_datatype ( ) ;
@ -1546,23 +1631,24 @@ void GDScriptAnalyzer::resolve_return(GDScriptParser::ReturnNode *p_return) {
result . is_constant = true ;
}
GDScriptParser : : DataType function_type = parser - > current_function - > get_datatype ( ) ;
function _type. is_meta_type = false ;
if ( function _type. is_hard_type ( ) ) {
if ( ! is_type_compatible ( function _type, result ) ) {
// Try other way. Okay but not safe.
if ( ! is_type_compatible ( result , function _type) ) {
push_error ( vformat ( R " (Cannot return value of type " % s " because the function return type is " % s " .) " , result . to_string ( ) , function _type. to_string ( ) ) , p_return ) ;
} else {
// TODO: Add warning.
mark_node_unsafe ( p_return ) ;
}
if ( has_expected_type ) {
expected _type. is_meta_type = false ;
if ( expected _type. is_hard_type ( ) ) {
if ( ! is_type_compatible ( expected _type, result ) ) {
// Try other way. Okay but not safe.
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 ) ;
} else {
// TODO: Add warning.
mark_node_unsafe ( p_return ) ;
}
# ifdef DEBUG_ENABLED
} else if ( function _type. builtin_type = = Variant : : INT & & result . builtin_type = = Variant : : FLOAT ) {
parser - > push_warning ( p_return , GDScriptWarning : : NARROWING_CONVERSION ) ;
} else if ( result . is_variant ( ) ) {
mark_node_unsafe ( p_return ) ;
} else if ( expected _type. builtin_type = = Variant : : INT & & result . builtin_type = = Variant : : FLOAT ) {
parser - > push_warning ( p_return , GDScriptWarning : : NARROWING_CONVERSION ) ;
} else if ( result . is_variant ( ) ) {
mark_node_unsafe ( p_return ) ;
# endif
}
}
}