diff options
Diffstat (limited to 'modules/gdscript')
| -rw-r--r-- | modules/gdscript/gdscript.cpp | 8 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_analyzer.cpp | 88 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_analyzer.h | 6 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_compiler.cpp | 29 | ||||
| -rw-r--r-- | modules/gdscript/gdscript_parser.cpp | 46 |
5 files changed, 122 insertions, 55 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 4589cf1a36..7943cf7469 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -625,9 +625,9 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc String path = ""; if (String(c->extends_path) != "" && String(c->extends_path) != get_path()) { path = c->extends_path; - if (path.is_rel_path()) { + if (path.is_relative_path()) { String base = get_path(); - if (base == "" || base.is_rel_path()) { + if (base == "" || base.is_relative_path()) { ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data()); } else { path = base.get_base_dir().plus_file(path); @@ -2059,7 +2059,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b if (r_icon_path) { if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) { *r_icon_path = c->icon_path; - } else if (c->icon_path.is_rel_path()) { + } else if (c->icon_path.is_relative_path()) { *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path(); } } @@ -2087,7 +2087,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b break; } String subpath = subclass->extends_path; - if (subpath.is_rel_path()) { + if (subpath.is_relative_path()) { subpath = path.get_base_dir().plus_file(subpath).simplify_path(); } diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 6b7403d854..f2b601dc2c 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -132,6 +132,76 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) { return type; } +bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class) { + 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; + } + } + + 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; + } + + 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)) { + push_error(vformat(R"(The class "%s" shadows a native class.)", p_member_name), p_member_node); + return ERR_PARSE_ERROR; + } + + 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) { + 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; + if (has_member_name_conflict_in_script_class(p_member_name, current_class_node)) { + push_error(vformat(R"(The member "%s" already exists in a parent class.)", p_member_name), + p_member_node); + return ERR_PARSE_ERROR; + } + current_data_type = ¤t_class_node->base_type; + } + + if (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::NATIVE) { + if (current_data_type->native_type != StringName("")) { + return check_native_member_name_conflict( + p_member_name, + p_member_node, + current_data_type->native_type); + } + } + + return OK; +} + Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) { if (p_class->base_type.is_set()) { // Already resolved @@ -525,6 +595,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas switch (member.type) { case GDScriptParser::ClassNode::Member::VARIABLE: { + check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable); + GDScriptParser::DataType datatype; datatype.kind = GDScriptParser::DataType::VARIANT; datatype.type_source = GDScriptParser::DataType::UNDETECTED; @@ -598,6 +670,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } break; case GDScriptParser::ClassNode::Member::CONSTANT: { + check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant); + reduce_expression(member.constant->initializer); GDScriptParser::DataType specified_type; @@ -647,6 +721,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } break; case GDScriptParser::ClassNode::Member::SIGNAL: { + check_class_member_name_conflict(p_class, member.signal->identifier->name, member.signal); + for (int j = 0; j < member.signal->parameters.size(); j++) { GDScriptParser::DataType signal_type = resolve_datatype(member.signal->parameters[j]->datatype_specifier); signal_type.is_meta_type = false; @@ -666,6 +742,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } break; case GDScriptParser::ClassNode::Member::ENUM: { + check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum); + GDScriptParser::DataType enum_type; enum_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; enum_type.kind = GDScriptParser::DataType::ENUM; @@ -717,6 +795,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas break; case GDScriptParser::ClassNode::Member::ENUM_VALUE: { if (member.enum_value.custom_value) { + check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.custom_value); + current_enum = member.enum_value.parent_enum; reduce_expression(member.enum_value.custom_value); current_enum = nullptr; @@ -730,6 +810,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas member.enum_value.resolved = true; } } else { + check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.parent_enum); + if (member.enum_value.index > 0) { member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1; } else { @@ -742,7 +824,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas p_class->members.write[i].enum_value = member.enum_value; } break; case GDScriptParser::ClassNode::Member::CLASS: - break; // Done later. + check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class); + break; case GDScriptParser::ClassNode::Member::UNDEFINED: ERR_PRINT("Trying to resolve undefined member."); break; @@ -2411,6 +2494,7 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod case GDScriptParser::ClassNode::Member::VARIABLE: p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_VARIABLE; p_identifier->variable_source = member.variable; + member.variable->usages += 1; break; case GDScriptParser::ClassNode::Member::FUNCTION: resolve_function_signature(member.function); @@ -2727,7 +2811,7 @@ void GDScriptAnalyzer::reduce_preload(GDScriptParser::PreloadNode *p_preload) { } else { p_preload->resolved_path = p_preload->path->reduced_value; // TODO: Save this as script dependency. - if (p_preload->resolved_path.is_rel_path()) { + if (p_preload->resolved_path.is_relative_path()) { p_preload->resolved_path = parser->script_path.get_base_dir().plus_file(p_preload->resolved_path); } p_preload->resolved_path = p_preload->resolved_path.simplify_path(); diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 32bf049fa1..2e17e15452 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -44,6 +44,12 @@ class GDScriptAnalyzer { const GDScriptParser::EnumNode *current_enum = nullptr; List<const GDScriptParser::LambdaNode *> lambda_stack; + // Tests for detecting invalid overloading of script members + static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node); + static _FORCE_INLINE_ bool has_member_name_conflict_in_native_type(const StringName &p_name, const StringName &p_native_type_string); + Error check_native_member_name_conflict(const StringName &p_member_name, const GDScriptParser::Node *p_member_node, const StringName &p_native_type_string); + Error check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node); + Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true); GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index ddf4f281b4..e5cbcc545f 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -2186,6 +2186,13 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->tool = parser->is_tool(); p_script->name = p_class->identifier ? p_class->identifier->name : ""; + if (p_script->name != "") { + if (ClassDB::class_exists(p_script->name) && ClassDB::is_class_exposed(p_script->name)) { + _set_error("The class '" + p_script->name + "' shadows a native class", p_class); + return ERR_ALREADY_EXISTS; + } + } + Ref<GDScriptNativeClass> native; GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type); @@ -2337,28 +2344,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar const GDScriptParser::SignalNode *signal = member.signal; StringName name = signal->identifier->name; - GDScript *c = p_script; - - while (c) { - if (c->_signals.has(name)) { - _set_error("Signal '" + name + "' redefined (in current or parent class)", p_class); - return ERR_ALREADY_EXISTS; - } - - if (c->base.is_valid()) { - c = c->base.ptr(); - } else { - c = nullptr; - } - } - - if (native.is_valid()) { - if (ClassDB::has_signal(native->get_name(), name)) { - _set_error("Signal '" + name + "' redefined (original in native class '" + String(native->get_name()) + "')", p_class); - return ERR_ALREADY_EXISTS; - } - } - Vector<StringName> parameters_names; parameters_names.resize(signal->parameters.size()); for (int j = 0; j < signal->parameters.size(); j++) { diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index d202bd4420..f4c721e00a 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -133,7 +133,7 @@ GDScriptParser::GDScriptParser() { register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>); register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>); // Networking. - register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPET>, 4, true); + register_annotation(MethodInfo("@rpc", { Variant::STRING, "mode" }, { Variant::STRING, "sync" }, { Variant::STRING, "transfer_mode" }, { Variant::INT, "transfer_channel" }), AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_AUTHORITY>, 4, true); // TODO: Warning annotations. } @@ -3399,43 +3399,35 @@ bool GDScriptParser::network_annotations(const AnnotationNode *p_annotation, Nod MultiplayerAPI::RPCConfig rpc_config; rpc_config.rpc_mode = t_mode; - for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) { - if (i == 0) { + if (p_annotation->resolved_arguments.size()) { + int last = p_annotation->resolved_arguments.size() - 1; + if (p_annotation->resolved_arguments[last].get_type() == Variant::INT) { + rpc_config.channel = p_annotation->resolved_arguments[last].operator int(); + last -= 1; + } + if (last > 3) { + push_error(R"(Invalid RPC arguments. At most 4 arguments are allowed, where only the last argument can be an integer to specify the channel.')", p_annotation); + return false; + } + for (int i = last; i >= 0; i--) { String mode = p_annotation->resolved_arguments[i].operator String(); if (mode == "any") { - rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_REMOTE; - } else if (mode == "master") { - rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_MASTER; - } else if (mode == "puppet") { - rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_PUPPET; - } else { - push_error(R"(Invalid RPC mode. Must be one of: 'any', 'master', or 'puppet')", p_annotation); - return false; - } - } else if (i == 1) { - String sync = p_annotation->resolved_arguments[i].operator String(); - if (sync == "sync") { + rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_ANY; + } else if (mode == "auth") { + rpc_config.rpc_mode = MultiplayerAPI::RPC_MODE_AUTHORITY; + } else if (mode == "sync") { rpc_config.sync = true; - } else if (sync == "nosync") { + } else if (mode == "nosync") { rpc_config.sync = false; - } else { - push_error(R"(Invalid RPC sync mode. Must be one of: 'sync' or 'nosync')", p_annotation); - return false; - } - } else if (i == 2) { - String mode = p_annotation->resolved_arguments[i].operator String(); - if (mode == "reliable") { + } else if (mode == "reliable") { rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE; } else if (mode == "unreliable") { rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE; } else if (mode == "ordered") { rpc_config.transfer_mode = MultiplayerPeer::TRANSFER_MODE_UNRELIABLE_ORDERED; } else { - push_error(R"(Invalid RPC transfer mode. Must be one of: 'reliable', 'unreliable', 'ordered')", p_annotation); - return false; + push_error(R"(Invalid RPC argument. Must be one of: 'sync'/'nosync' (local calls), 'any'/'auth' (permission), 'reliable'/'unreliable'/'ordered' (transfer mode).)", p_annotation); } - } else if (i == 3) { - rpc_config.channel = p_annotation->resolved_arguments[i].operator int(); } } switch (p_node->type) { |