From ff82f35f3e4a30ead31a6aa613b264b1f9c28ab2 Mon Sep 17 00:00:00 2001 From: Tiago Date: Fri, 17 Oct 2025 12:32:01 -0700 Subject: [PATCH] ruby codegen: support generation of rbs files (#15633) this introduces support for a new protoc option, `--rbs_out`, which points to a directory where ruby type definition files, defined in the RBS format, are stored. [rbs](https://github.com/ruby/rbs) is the type signature syntax blessed by the ruby core team, used by static analysis tools such as [steep](https://github.com/soutaro/steep), which integrates with VS Code, the `irb` console (for features such as autocompletion). and [typeprof](https://github.com/ruby/typeprof). It relies on type definitions written into `.rbs` files. The `protobuf` library already exposes type definitions in [gem_rbs_collection](https://github.com/ruby/gem_rbs_collection/tree/main/gems/google-protobuf/3.22), which is used to source type definitions for libraries which do not want, or can't maintain type definitions themselves. (`protobuf` could arguably import these into this repository, lmk if you're interested). This should fix gaps such as better IDE integration, such as the ones described [here](https://github.com/protocolbuffers/protobuf/issues/9495). The plan is to do roughly the same type of integration as was done for `.pyi` annotations, which also write to separate files: add the cli option and the rbs generator. protobuf classes in ruby rely on dynamic attribution of the base class, which makes what I'm trying to achieve a bit difficult. Ideally the type hierarchy could be specified statically in the ruby source code. ```ruby class Bar < AbstractMessage class Bar < Google::Protobuf::DescriptorPool.generated_pool.lookup("Bar").msgclass ``` ``` Closes #15633 FUTURE_COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/15633 from HoneyryderChuck:ruby-rbs-integration 569f032d08034b5c7b84e1e6d7466d6396341972 PiperOrigin-RevId: 820790992 --- .github/workflows/test_ruby.yml | 20 + protobuf.bzl | 59 +- src/file_lists.cmake | 3 + src/google/protobuf/compiler/main.cc | 5 + src/google/protobuf/compiler/ruby/BUILD.bazel | 23 +- .../protobuf/compiler/ruby/rbs_generator.cc | 825 ++++++++++++++++++ .../protobuf/compiler/ruby/rbs_generator.h | 39 + .../compiler/ruby/rbs_generator_unittest.cc | 110 +++ .../compiler/ruby/ruby_generated_code.proto | 2 + .../compiler/ruby/ruby_generated_code_pb.rbs | 448 ++++++++++ .../ruby/ruby_generated_code_proto2_pb.rbs | 484 ++++++++++ .../ruby_generated_pkg_explicit_legacy_pb.rbs | 43 + .../ruby/ruby_generated_pkg_explicit_pb.rbs | 43 + .../ruby/ruby_generated_pkg_implicit_pb.rbs | 43 + .../protobuf/compiler/ruby/ruby_generator.h | 6 + 15 files changed, 2148 insertions(+), 5 deletions(-) create mode 100644 src/google/protobuf/compiler/ruby/rbs_generator.cc create mode 100644 src/google/protobuf/compiler/ruby/rbs_generator.h create mode 100644 src/google/protobuf/compiler/ruby/rbs_generator_unittest.cc create mode 100644 src/google/protobuf/compiler/ruby/ruby_generated_code_pb.rbs create mode 100644 src/google/protobuf/compiler/ruby/ruby_generated_code_proto2_pb.rbs create mode 100644 src/google/protobuf/compiler/ruby/ruby_generated_pkg_explicit_legacy_pb.rbs create mode 100644 src/google/protobuf/compiler/ruby/ruby_generated_pkg_explicit_pb.rbs create mode 100644 src/google/protobuf/compiler/ruby/ruby_generated_pkg_implicit_pb.rbs diff --git a/.github/workflows/test_ruby.yml b/.github/workflows/test_ruby.yml index 65fae34fc3136..2358fcca2dcc0 100644 --- a/.github/workflows/test_ruby.yml +++ b/.github/workflows/test_ruby.yml @@ -213,3 +213,23 @@ jobs: bazel-bin/protoc --proto_path=src --proto_path=ruby/tests --proto_path=ruby --ruby_out=ruby tests/basic_test.proto; ${{ matrix.ffi == 'FFI' && 'PROTOCOL_BUFFERS_RUBY_IMPLEMENTATION=FFI' || '' }} ruby ruby/tests/basic.rb; ${{ matrix.ffi == 'FFI' && 'PROTOCOL_BUFFERS_RUBY_IMPLEMENTATION=FFI' || '' }} ruby ruby/tests/implementation.rb + + typecheck: + name: Typecheck + runs-on: ubuntu-latest + steps: + - name: Checkout pending changes + uses: protocolbuffers/protobuf-ci/checkout@v2 + with: + ref: ${{ inputs.safe-checkout }} + - name: Run type checker + uses: protocolbuffers/protobuf-ci/bazel-docker@v2 + with: + image: us-docker.pkg.dev/protobuf-build/containers/common/linux/bazel:7.6.1-e0df73e51131ccaf53451355d22577f377357604 + credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }} + bazel-cache: ruby_typecheck + bash: + bazel build //:protoc + cd ruby + cp typecheck.gemfile.lock Gemfile.lock + bundle exec rake steep diff --git a/protobuf.bzl b/protobuf.bzl index fdf09bd6be7bf..3f0c7e5676b6f 100644 --- a/protobuf.bzl +++ b/protobuf.bzl @@ -65,6 +65,9 @@ def _PyOuts(srcs, use_grpc_plugin = False): def _RubyOuts(srcs): return [s[:-len(".proto")] + "_pb.rb" for s in srcs] +def _RBSOuts(srcs): + return [s[:-len(".proto")] + "_pb.rbs" for s in srcs] + def _CsharpOuts(srcs): return [ "".join([token.capitalize() for token in src[:-len(".proto")].split("_")]) + ".cs" @@ -158,6 +161,8 @@ def _proto_gen_impl(ctx): outs.extend(_PyOuts([src.basename], use_grpc_plugin = use_grpc_plugin)) elif lang == "ruby": outs.extend(_RubyOuts([src.basename])) + elif lang == "rbs": + outs.extend(_RBSOuts([src.basename])) # Otherwise, rely on user-supplied outs. args.append(("--%s_out=" + path_tpl) % (lang, gen_dir)) @@ -526,8 +531,6 @@ def internal_ruby_proto_library( """ - # Note: we need to run the protoc build twice to get separate targets for - # the generated header and the source files. _proto_gen( name = name + "_genproto", srcs = srcs, @@ -552,6 +555,58 @@ def internal_ruby_proto_library( **kwargs ) +def internal_rbs_proto_library( + name, + ruby_library, + srcs = [], + deps = [], + includes = ["."], + protoc = "@com_google_protobuf//:protoc", + testonly = None, + visibility = ["//visibility:public"], + **kwargs): + """Bazel rule to create an RBS type definitions for Ruby from proto source files + + NOTE: the rule is only an internal workaround to generate protos. The + interface may change and the rule may be removed when bazel has introduced + the native rule. + + Args: + name: the name of the ruby_proto_library. + ruby_library: the ruby library rules to use. + srcs: the .proto files to compile. + deps: a list of dependency labels; must be a internal_rbs_proto_library. + includes: a string indicating the include path of the .proto files. + protoc: the label of the protocol compiler to generate the sources. + testonly: common rule attribute (see: + https://bazel.build/reference/be/common-definitions#common-attributes) + visibility: the visibility of the generated files. + **kwargs: other keyword arguments that are passed to ruby_library. + + """ + + _proto_gen( + name = name + "_genproto_rbs", + srcs = srcs, + deps = [s + "_genproto_rbs" for s in deps], + langs = ["rbs"], + includes = includes, + protoc = protoc, + testonly = testonly, + visibility = visibility, + tags = ["manual"], + ) + + ruby_library( + name = name, + srcs = [name + "_genproto_rbs"], + deps = [], + testonly = testonly, + visibility = visibility, + includes = includes, + **kwargs + ) + # When canonical labels are in use, use additional "@" prefix _canonical_label_prefix = "@" if str(Label("//:protoc")).startswith("@@") else "" diff --git a/src/file_lists.cmake b/src/file_lists.cmake index e293434865128..be2292c7cdf37 100644 --- a/src/file_lists.cmake +++ b/src/file_lists.cmake @@ -472,6 +472,7 @@ set(libprotoc_srcs ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/helpers.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/pyi_generator.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/retention.cc + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/rbs_generator.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/ruby_generator.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/rust/accessors/accessor_case.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/rust/accessors/accessors.cc @@ -619,6 +620,7 @@ set(libprotoc_hdrs ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/helpers.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/pyi_generator.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/retention.h + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/rbs_generator.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/ruby_generator.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/rust/accessors/accessor_case.h ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/rust/accessors/accessors.h @@ -1395,6 +1397,7 @@ set(compiler_test_files ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/php/generator_unittest.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/python/plugin_unittest.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/retention_unittest.cc + ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/rbs_generator_unittest.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/versions_test.cc ) diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc index 4a093008939e7..dbf8d8975f770 100644 --- a/src/google/protobuf/compiler/main.cc +++ b/src/google/protobuf/compiler/main.cc @@ -15,6 +15,7 @@ #include "google/protobuf/compiler/php/php_generator.h" #include "google/protobuf/compiler/python/generator.h" #include "google/protobuf/compiler/python/pyi_generator.h" +#include "google/protobuf/compiler/ruby/rbs_generator.h" #include "google/protobuf/compiler/ruby/ruby_generator.h" #include "google/protobuf/compiler/rust/generator.h" #ifdef GOOGLE_PROTOBUF_RUNTIME_INCLUDE_BASE @@ -95,6 +96,10 @@ int ProtobufMain(int argc, char* argv[]) { cli.RegisterGenerator("--ruby_out", "--ruby_opt", &rb_generator, "Generate Ruby source file."); + ruby::RBSGenerator rbs_generator; + cli.RegisterGenerator("--rbs_out", "--rbs_opt", &rbs_generator, + "Generate RBS type definition."); + // CSharp csharp::Generator csharp_generator; cli.RegisterGenerator("--csharp_out", "--csharp_opt", &csharp_generator, diff --git a/src/google/protobuf/compiler/ruby/BUILD.bazel b/src/google/protobuf/compiler/ruby/BUILD.bazel index 7a2fdd8cd9c96..2e4ce44156ad0 100644 --- a/src/google/protobuf/compiler/ruby/BUILD.bazel +++ b/src/google/protobuf/compiler/ruby/BUILD.bazel @@ -20,8 +20,14 @@ cc_library( cc_library( name = "ruby", - srcs = ["ruby_generator.cc"], - hdrs = ["ruby_generator.h"], + srcs = [ + "rbs_generator.cc", + "ruby_generator.cc", + ], + hdrs = [ + "rbs_generator.h", + "ruby_generator.h", + ], copts = COPTS, strip_include_prefix = "/src", visibility = [ @@ -35,7 +41,9 @@ cc_library( "//src/google/protobuf/compiler:retention", "//src/google/protobuf/io", "//src/google/protobuf/io:printer", + "@abseil-cpp//absl/container:flat_hash_map", "@abseil-cpp//absl/container:flat_hash_set", + "@abseil-cpp//absl/log:absl_check", "@abseil-cpp//absl/log:absl_log", "@abseil-cpp//absl/strings", ], @@ -43,19 +51,27 @@ cc_library( cc_test( name = "generator_unittest", - srcs = ["ruby_generator_unittest.cc"], + srcs = [ + "rbs_generator_unittest.cc", + "ruby_generator_unittest.cc", + ], data = [ "ruby_generated_code.proto", "ruby_generated_code_pb.rb", + "ruby_generated_code_pb.rbs", "ruby_generated_code_proto2.proto", "ruby_generated_code_proto2_import.proto", "ruby_generated_code_proto2_pb.rb", + "ruby_generated_code_proto2_pb.rbs", "ruby_generated_pkg_explicit.proto", "ruby_generated_pkg_explicit_legacy.proto", "ruby_generated_pkg_explicit_legacy_pb.rb", + "ruby_generated_pkg_explicit_legacy_pb.rbs", "ruby_generated_pkg_explicit_pb.rb", + "ruby_generated_pkg_explicit_pb.rbs", "ruby_generated_pkg_implicit.proto", "ruby_generated_pkg_implicit_pb.rb", + "ruby_generated_pkg_implicit_pb.rbs", "//src/google/protobuf:testdata", ], deps = [ @@ -65,6 +81,7 @@ cc_test( "//src/google/protobuf/io:printer", "//src/google/protobuf/testing", "//src/google/protobuf/testing:file", + "@abseil-cpp//absl/log:absl_check", "@googletest//:gtest", "@googletest//:gtest_main", ], diff --git a/src/google/protobuf/compiler/ruby/rbs_generator.cc b/src/google/protobuf/compiler/ruby/rbs_generator.cc new file mode 100644 index 0000000000000..d41ce97824730 --- /dev/null +++ b/src/google/protobuf/compiler/ruby/rbs_generator.cc @@ -0,0 +1,825 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "google/protobuf/compiler/ruby/rbs_generator.h" + +#include +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/ruby/ruby_generator.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/io/printer.h" +#include "google/protobuf/io/zero_copy_stream.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace ruby { + +std::string GetRBSOutputFilename(absl::string_view proto_file) { + return absl::StrCat(GetRequireName(proto_file), ".rbs"); +} + +std::string WrapUnion(const std::string& type) { + // This is known not to work as intended for ::Array[::Integer | ::Float]. + // Maybe we need an AST + if (absl::StrContains(type, '|')) { + return absl::StrCat("(", type, ")"); + } else { + return absl::StrCat(type); + } +} + +std::string MakeOptionalType(const std::string& type) { + if (type.back() == '?') { + return type; + } else { + return absl::StrCat(WrapUnion(type), "?"); + } +} + +void PrintComment(const std::string& comment, io::Printer* printer) { + if (comment.empty()) { + return; + } + std::vector lines = + absl::StrSplit(comment, '\n', absl::SkipEmpty()); + if (!lines.empty() && lines.back().empty()) { + lines.pop_back(); + } + for (const std::string& line : lines) { + // Spaces after `#` are usually included in the comment itself + printer->Print("#$line$\n", "line", line); + } +} + +void PrintDeprecationComment(bool deprecated, io::Printer* printer) { + if (deprecated) { + printer->Print("# @deprecated\n"); + } +} + +void InsertMidLine(bool& initial, io::Printer* printer) { + if (initial) { + initial = false; + } else { + printer->Print("\n"); + } +} + +std::string UnionSeparator(bool& initial) { + if (initial) { + initial = false; + return " "; + } else { + return "| "; + } +} + +std::string ModulePath(const FileDescriptor* file) { + // Mirror implementation of GeneratePackageModules + bool need_change_to_module = true; + std::string package_name; + if (file->options().has_ruby_package()) { + package_name = file->options().ruby_package(); + + if (absl::StrContains(package_name, "::")) { + need_change_to_module = false; + } + } else { + package_name = std::string(file->package()); + } + if (need_change_to_module) { + std::vector parts = absl::StrSplit(package_name, '.'); + package_name = ""; + for (const std::string& part : parts) { + if (!package_name.empty()) { + package_name += "::"; + } + package_name += PackageToModule(part); + } + } + if (!package_name.empty()) { + // Absolutify + package_name = absl::StrCat("::", package_name); + } + + return package_name; +} +std::string RBSMessageFullName(const Descriptor* message) { + if (message->containing_type() != nullptr) { + return absl::StrCat(RBSMessageFullName(message->containing_type()), + "::", RubifyConstant(message->name())); + } else { + return absl::StrCat(ModulePath(message->file()), + "::", RubifyConstant(message->name())); + } +} +std::string RBSEnumFullName(const EnumDescriptor* enum_) { + if (enum_->containing_type() != nullptr) { + return absl::StrCat(RBSMessageFullName(enum_->containing_type()), + "::", RubifyConstant(enum_->name())); + } else { + return absl::StrCat(ModulePath(enum_->file()), + "::", RubifyConstant(enum_->name())); + } +} + +// Corresponds with: +// - Convert_UpbToRuby in convert.c (protobuf_c) +// - convert_upb_to_ruby in convert.rb (protobuf_ffi) +// - wrapField in RubyMessage.java (protobuf_java) +std::string ScalarReadType(const FieldDescriptor* field) { + switch (field->type()) { + case FieldDescriptor::TYPE_DOUBLE: + case FieldDescriptor::TYPE_FLOAT: + return "::Float"; + case FieldDescriptor::TYPE_FIXED32: + case FieldDescriptor::TYPE_FIXED64: + case FieldDescriptor::TYPE_INT32: + case FieldDescriptor::TYPE_INT64: + case FieldDescriptor::TYPE_SFIXED32: + case FieldDescriptor::TYPE_SFIXED64: + case FieldDescriptor::TYPE_SINT32: + case FieldDescriptor::TYPE_SINT64: + case FieldDescriptor::TYPE_UINT32: + case FieldDescriptor::TYPE_UINT64: + return "::Integer"; + case FieldDescriptor::TYPE_BOOL: + return "bool"; + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_BYTES: + return "::String"; + + case FieldDescriptor::TYPE_ENUM: + // ::MyEnum::names | ::Integer + // Integer is for unknown enum values + return absl::StrCat(RBSEnumFullName(field->enum_type()), + "::names | ::Integer"); + case FieldDescriptor::TYPE_GROUP: + case FieldDescriptor::TYPE_MESSAGE: + return RBSMessageFullName(field->message_type()); + default: + return "untyped"; + } +} + +// Corresponds with: +// - Convert_RubyToUpb in convert.c (protobuf_c) +// - convert_ruby_to_upb in convert.rb (protobuf_ffi) +// - checkType in Utils.java (protobuf_java) +std::string ScalarWriteType(const FieldDescriptor* field) { + switch (field->type()) { + case FieldDescriptor::TYPE_DOUBLE: + case FieldDescriptor::TYPE_FLOAT: + // - protobuf_c: Float | Integer + // - protobuf_ffi: _ToF + return "::Float | ::Integer"; + case FieldDescriptor::TYPE_FIXED32: + case FieldDescriptor::TYPE_FIXED64: + case FieldDescriptor::TYPE_INT32: + case FieldDescriptor::TYPE_INT64: + case FieldDescriptor::TYPE_SFIXED32: + case FieldDescriptor::TYPE_SFIXED64: + case FieldDescriptor::TYPE_SINT32: + case FieldDescriptor::TYPE_SINT64: + case FieldDescriptor::TYPE_UINT32: + case FieldDescriptor::TYPE_UINT64: + // It accepts Float as long as it's a whole number + // - protobuf_c: Integer | Float + // - protobuf_ffi: Numeric & _ToI + return "::Integer | ::Float"; + case FieldDescriptor::TYPE_BOOL: + // Not `boolish` as it only accepts true or false + return "bool"; + case FieldDescriptor::TYPE_STRING: + // string accepts Symbol and bytes not. + // - protobuf_c: String | Symbol + // - protobuf_ffi: String | Symbol but rejects subclasses of String + return "::String | ::Symbol"; + case FieldDescriptor::TYPE_BYTES: + return "::String"; + + case FieldDescriptor::TYPE_ENUM: { + // ::MyEnum::names | ::MyEnum::strings | ::Integer | ::Float + // - protobuf_c: Integer | Float | String | Symbol where String and Symbol + // must be known names + // - protobuf_ffi: (Numeric & _ToI) | String | Symbol where String and + // Symbol must be known names + const std::string enum_name = RBSEnumFullName(field->enum_type()); + return absl::StrCat(enum_name, "::names | ", enum_name, + "::strings | ::Integer | ::Float"); + } + case FieldDescriptor::TYPE_GROUP: + case FieldDescriptor::TYPE_MESSAGE: { + // - protobuf_c: TheMessageType | Time (for Timestamp) | (Numeric & + // _ToInt) (for Duration) + // - protobuf_ffi: TheMessageType | Hash | Time (for Timestamp) | (Numeric + // & _ToInt) (for Duration) + const Descriptor::WellKnownType wkt = + field->message_type()->well_known_type(); + if (wkt == Descriptor::WELLKNOWNTYPE_TIMESTAMP) { + return absl::StrCat(RBSMessageFullName(field->message_type()), + " | ::Time"); + } else if (wkt == Descriptor::WELLKNOWNTYPE_DURATION) { + return absl::StrCat(RBSMessageFullName(field->message_type()), + " | ::int"); + } else { + return RBSMessageFullName(field->message_type()); + } + } + default: + return "untyped"; + } +} + +// Corresponds with: +// - protobuf_c: Message_getfield in message.c +// - protobuf_ffi: get_field in message.rb +// - protobuf_java: getFieldInternal in RubyMessage.java +std::string FieldReadType(const FieldDescriptor* field) { + // Things not covered in these branches: + // - read optionalities: unfortunately, neither of them are handled in a + // Ruby-friendly way: + // - oneof fields + // - proto2 optional + // - proto3 optional + // though you can manipulate optionality through `clear_*` and `has_*?` + // methods + // - write optionalities: same as above, but real oneofs are handled as + // exceptions + if (field->is_map()) { + const std::string key_read_type = + ScalarReadType(field->message_type()->map_key()); + const std::string key_write_type = + ScalarWriteType(field->message_type()->map_key()); + const std::string value_read_type = + ScalarReadType(field->message_type()->map_value()); + const std::string value_write_type = + ScalarWriteType(field->message_type()->map_value()); + return absl::StrCat("::Google::Protobuf::Map[", key_read_type, ", ", + value_read_type, ", ", key_write_type, ", ", + value_write_type, "]"); + } else if (field->is_repeated()) { + const std::string element_read_type = ScalarReadType(field); + const std::string element_write_type = ScalarWriteType(field); + return absl::StrCat("::Google::Protobuf::RepeatedField[", element_read_type, + ", ", element_write_type, "]"); + } else if (field->type() == FieldDescriptor::TYPE_MESSAGE || + field->type() == FieldDescriptor::TYPE_GROUP) { + return MakeOptionalType(ScalarReadType(field)); + } else { + // Proto2 optionals, proto3 optionals, and real oneof fields don't return + // nil. And that is why they are handled here. This is unfortunate, but you + // can still manipulate optionality through `clear_*` and `has_*?` methods. + return ScalarReadType(field); + } +} + +// Corresponds with: +// - protobuf_c: Message_setfield in message.c +// - protobuf_ffi: set_value_on_message in field_descriptor.rb +// - protobuf_java: setFieldInternal in RubyMessage.java +std::string FieldWriteType(const FieldDescriptor* field) { + // Things not covered in these branches: + // - read optionalities: unfortunately, neither of them are handled in a + // Ruby-friendly way: + // - oneof fields + // - proto2 optional + // - proto3 optional + // though you can manipulate optionality through `clear_*` and `has_*?` + // methods + // - write optionalities: same as above, but real oneofs are handled as + // exceptions + if (field->is_map() || field->is_repeated()) { + return FieldReadType(field); + } else if (field->type() == FieldDescriptor::TYPE_MESSAGE || + field->type() == FieldDescriptor::TYPE_GROUP || + field->real_containing_oneof()) { + // In addition to sub-messages, real oneofs accept nil (but won't return + // it). + return MakeOptionalType(ScalarWriteType(field)); + } else { + // Proto2 optionals and proto3 optionals don't accept nil. + // And that is why they are handled here. + // This is unfortunate, but you can still manipulate optionality through + // `clear_*` and `has_*?` methods. + return ScalarWriteType(field); + } +} + +// Corresponds with: +// - protobuf_c: Message_InitFieldFromValue in message.c +// - protobuf_ffi: initialize and index_assign_internal in message.rb and +// - protobuf_java: initialize in RubyMessage.java +std::string FieldInitType(const FieldDescriptor* field) { + // All of them are optional here, as nil means to just skip initialization for + // the field. + if (field->is_map()) { + const std::string key_write_type = + ScalarWriteType(field->message_type()->map_key()); + const std::string value_write_type = + ScalarWriteType(field->message_type()->map_value()); + return absl::StrCat("::Hash[", key_write_type, ", ", value_write_type, + "]?"); + } else if (field->is_map() || field->is_repeated()) { + const std::string element_write_type = ScalarWriteType(field); + return absl::StrCat("::Array[", element_write_type, "]?"); + } else if (field->type() == FieldDescriptor::TYPE_MESSAGE || + field->type() == FieldDescriptor::TYPE_GROUP) { + const std::string full_name = RBSMessageFullName(field->message_type()); + // ::MyMessage | ::MyMessage::init_map + return MakeOptionalType( + absl::StrCat(full_name, " | ", full_name, "::init_map")); + } else { + return MakeOptionalType(ScalarWriteType(field)); + } +} + +bool IsWrapper(const FieldDescriptor* field) { + const Descriptor* message = field->message_type(); + if (message == nullptr) { + return false; + } + switch (message->well_known_type()) { + case Descriptor::WELLKNOWNTYPE_DOUBLEVALUE: + case Descriptor::WELLKNOWNTYPE_FLOATVALUE: + case Descriptor::WELLKNOWNTYPE_INT64VALUE: + case Descriptor::WELLKNOWNTYPE_UINT64VALUE: + case Descriptor::WELLKNOWNTYPE_INT32VALUE: + case Descriptor::WELLKNOWNTYPE_UINT32VALUE: + case Descriptor::WELLKNOWNTYPE_STRINGVALUE: + case Descriptor::WELLKNOWNTYPE_BYTESVALUE: + case Descriptor::WELLKNOWNTYPE_BOOLVALUE: + return true; + default: + return false; + } +} + +void GenerateEnumValueTypeDefinition(const EnumValueDescriptor* value, + io::Printer* printer) { + SourceLocation location; + bool has_location = value->GetSourceLocation(&location); + if (has_location) { + PrintComment(location.leading_comments, printer); + } + PrintDeprecationComment(value->options().deprecated(), printer); + + std::string name{value->name()}; + + if (name[0] < 'A' || name[0] > 'Z') { + if ('a' <= name[0] && name[0] <= 'z') { + // auto capitalize + name[0] = name[0] - 'a' + 'A'; + } else { + printer->Print( + "# Enum value '$name$' does not start with an uppercase letter " + "as is required for Ruby constants.\n" + "# $name$: $number$\n", + "name", value->name(), "number", absl::StrCat(value->number())); + return; + } + } + + printer->Print("$name$: $number$\n", "name", name, "number", + absl::StrCat(value->number())); +} + +void GenerateEnumTypeDefinition(const EnumDescriptor* enum_, + io::Printer* printer) { + SourceLocation location; + bool has_location = enum_->GetSourceLocation(&location); + if (has_location) { + PrintComment(location.leading_comments, printer); + } + PrintDeprecationComment(enum_->options().deprecated(), printer); + + bool initial = true; + + printer->Print("module $name$\n", "name", RubifyConstant(enum_->name())); + printer->Indent(); + InsertMidLine(initial, printer); + printer->Print("extend ::Google::Protobuf::_EnumModule\n"); + + for (int i = 0; i < enum_->value_count(); i++) { + const EnumValueDescriptor* value = enum_->value(i); + InsertMidLine(initial, printer); + GenerateEnumValueTypeDefinition(value, printer); + } + + std::vector unique_numbers; + absl::flat_hash_map> names_by_number; + for (int i = 0; i < enum_->value_count(); i++) { + const EnumValueDescriptor* value = enum_->value(i); + // Check if there is an entry from names_by_number and if so, get the + // position + auto it = names_by_number.find(value->number()); + if (it == names_by_number.end()) { + names_by_number[value->number()].push_back(std::string{value->name()}); + unique_numbers.push_back(value->number()); + } else { + it->second.push_back(std::string{value->name()}); + } + } + + InsertMidLine(initial, printer); + printer->Print("def self.lookup:\n"); + printer->Indent(); + bool overload_initial = true; + for (int number : unique_numbers) { + const std::vector& names = names_by_number[number]; + std::string name_union = absl::StrCat(":", names[0]); + for (size_t i = 1; i < names.size(); i++) { + absl::StrAppend(&name_union, " | :", names[i]); + } + printer->Print("$sep$($number$ number) -> $name_union$\n", "sep", + UnionSeparator(overload_initial), "number", + absl::StrCat(number), "name_union", WrapUnion(name_union)); + } + printer->Print("$sep$(::int number) -> names?\n", "sep", + UnionSeparator(overload_initial)); + printer->Print("$sep$...\n", "sep", UnionSeparator(overload_initial)); + printer->Outdent(); + + InsertMidLine(initial, printer); + printer->Print("def self.resolve:\n"); + printer->Indent(); + overload_initial = true; + for (int i = 0; i < enum_->value_count(); i++) { + const EnumValueDescriptor* value = enum_->value(i); + printer->Print("$sep$(:$name$ name) -> $number$\n", "sep", + UnionSeparator(overload_initial), "name", value->name(), + "number", absl::StrCat(value->number())); + } + printer->Print("$sep$(::Symbol name) -> numbers?\n", "sep", + UnionSeparator(overload_initial)); + printer->Print("$sep$...\n", "sep", UnionSeparator(overload_initial)); + printer->Outdent(); + + std::string all_name_union = absl::StrCat(":", enum_->value(0)->name()); + for (int i = 1; i < enum_->value_count(); i++) { + absl::StrAppend(&all_name_union, " | :", enum_->value(i)->name()); + } + InsertMidLine(initial, printer); + printer->Print("type names = $name_union$\n", "name_union", all_name_union); + + std::string all_string_union = + absl::StrCat("\"", enum_->value(0)->name(), "\""); + for (int i = 1; i < enum_->value_count(); i++) { + absl::StrAppend(&all_string_union, " | \"", enum_->value(i)->name(), "\""); + } + InsertMidLine(initial, printer); + printer->Print("type strings = $string_union$\n", "string_union", + all_string_union); + + std::string all_number_union = absl::StrCat(unique_numbers[0]); + for (size_t i = 1; i < unique_numbers.size(); i++) { + absl::StrAppend(&all_number_union, " | ", unique_numbers[i]); + } + InsertMidLine(initial, printer); + printer->Print("type numbers = $number_union$\n", "number_union", + all_number_union); + + printer->Outdent(); + printer->Print("end\n"); +} + +void GenerateFieldTypeDefinition(const FieldDescriptor* field, + io::Printer* printer) { + SourceLocation location; + bool has_location = field->GetSourceLocation(&location); + if (has_location) { + PrintComment(location.leading_comments, printer); + } + PrintDeprecationComment(field->options().deprecated(), printer); + + // attr_accessor my_field(): ::Integer + + std::string read_type = FieldReadType(field); + std::string write_type = FieldWriteType(field); + if (read_type == write_type) { + printer->Print("attr_accessor $name$(): $read_type$\n", "name", + field->name(), "read_type", read_type); + } else { + printer->Print( + "attr_reader $name$(): $read_type$\n" + "attr_writer $name$(): $write_type$\n", + "name", field->name(), "read_type", read_type, "write_type", + write_type); + } + + if (!field->is_repeated() && IsWrapper(field)) { + // field is already known to be a message + const FieldDescriptor* wrapped_field = + field->message_type()->FindFieldByNumber(1); + if (wrapped_field != nullptr) { + // attr_accessor my_field_as_value(): ::Integer? + std::string wrapped_read_type = + MakeOptionalType(ScalarReadType(wrapped_field)); + std::string wrapped_write_type = + MakeOptionalType(ScalarWriteType(wrapped_field)); + if (wrapped_read_type == wrapped_write_type) { + printer->Print("attr_accessor $name$_as_value(): $read_type$\n", "name", + field->name(), "read_type", wrapped_read_type); + } else { + printer->Print( + "attr_reader $name$_as_value(): $read_type$\n" + "attr_writer $name$_as_value(): $write_type$\n", + "name", field->name(), "read_type", wrapped_read_type, "write_type", + wrapped_write_type); + } + } + } + + if (field->type() == FieldDescriptor::TYPE_ENUM) { + // attr_accessor my_field_const(): ::Integer + if (field->is_repeated()) { + printer->Print("attr_reader $name$_const(): ::Array[::Integer]\n", "name", + field->name()); + } else { + // Always non-optional + printer->Print("attr_reader $name$_const(): ::Integer\n", "name", + field->name()); + } + } + + if (field->has_presence()) { + // def has_my_field?: () -> bool + printer->Print("def has_$name$?: () -> bool\n", "name", field->name()); + } + + // def clear_my_field: () -> void + printer->Print("def clear_$name$: () -> void\n", "name", field->name()); +} + +void GenerateOneofDeclTypeDefinition(const OneofDescriptor* oneof, + io::Printer* printer) { + SourceLocation location; + bool has_location = oneof->GetSourceLocation(&location); + if (has_location) { + PrintComment(location.leading_comments, printer); + } + + // attr_reader my_oneof(): ::Integer? + std::vector oneof_scalar_types; + std::unordered_set oneof_scalar_types_set; + for (int i = 0; i < oneof->field_count(); i++) { + const FieldDescriptor* field = oneof->field(i); + const std::string read_type = ScalarReadType(field); + if (oneof_scalar_types_set.insert(read_type).second) { + oneof_scalar_types.push_back(read_type); + } + } + std::string oneof_scalar_type = + MakeOptionalType(absl::StrJoin(oneof_scalar_types, " | ")); + + printer->Print("attr_reader $name$(): $type$\n", "name", oneof->name(), + "type", oneof_scalar_type); + + // def has_my_field?: () -> bool + printer->Print("def has_$name$?: () -> bool\n", "name", oneof->name()); + + // def clear_my_field: () -> void + printer->Print("def clear_$name$: () -> void\n", "name", oneof->name()); +} + +void GenerateMessageInitMap(const Descriptor* message, io::Printer* printer) { + printer->Print("type init_map = {\n"); + printer->Indent(); + for (int i = 0; i < message->field_count(); i++) { + const FieldDescriptor* field = message->field(i); + + std::string init_type = FieldInitType(field); + // We may add support for https://github.com/ruby/rbs/pull/1717 + // when it is shipped in Sorbet and RubyMine. + printer->Print( + // "?$name$: $type$,\n", // Wait for + // https://github.com/ruby/rbs/pull/1717 + "$name$: $type$,\n", "name", field->name(), "type", init_type); + printer->Print( + // "\"$name$\" => $type$,\n", // Wait for + // https://github.com/ruby/rbs/pull/1717 + "\"$name$\" => $type$,\n", "name", field->name(), "type", init_type); + } + printer->Outdent(); + printer->Print("}\n"); +} + +void GenerateIndexReaderDefinition(const Descriptor* message, + io::Printer* printer) { + printer->Print("def []:\n"); + printer->Indent(); + bool overload_initial = true; + for (int i = 0; i < message->field_count(); i++) { + const FieldDescriptor* field = message->field(i); + printer->Print("$sep$(\"$name$\" name) -> $type$\n", "sep", + UnionSeparator(overload_initial), "name", field->name(), + "type", WrapUnion(FieldReadType(field))); + } + printer->Outdent(); +} + +void GenerateIndexWriterDefinition(const Descriptor* message, + io::Printer* printer) { + printer->Print("def []=:\n"); + printer->Indent(); + bool overload_initial = true; + for (int i = 0; i < message->field_count(); i++) { + const FieldDescriptor* field = message->field(i); + printer->Print("$sep$(\"$name$\" name, $type$ value) -> void\n", "sep", + UnionSeparator(overload_initial), "name", field->name(), + "type", WrapUnion(FieldWriteType(field))); + } + printer->Outdent(); +} + +void GenerateMessageTypeDefinition(const Descriptor* message, + io::Printer* printer) { + SourceLocation location; + bool has_location = message->GetSourceLocation(&location); + if (has_location) { + PrintComment(location.leading_comments, printer); + } + PrintDeprecationComment(message->options().deprecated(), printer); + + printer->Print("class $classname$ < ::Google::Protobuf::AbstractMessage\n", + "classname", RubifyConstant(message->name())); + printer->Indent(); + + bool initial = true; + + for (int i = 0; i < message->nested_type_count(); i++) { + if (message->nested_type(i)->map_key() != nullptr) { + continue; + } + InsertMidLine(initial, printer); + GenerateMessageTypeDefinition(message->nested_type(i), printer); + } + + for (int i = 0; i < message->enum_type_count(); i++) { + InsertMidLine(initial, printer); + GenerateEnumTypeDefinition(message->enum_type(i), printer); + } + + InsertMidLine(initial, printer); + printer->Print("include ::Google::Protobuf::_MessageClass[$name$]\n", "name", + RBSMessageFullName(message)); + + for (int i = 0; i < message->field_count(); i++) { + InsertMidLine(initial, printer); + const FieldDescriptor* field = message->field(i); + GenerateFieldTypeDefinition(field, printer); + } + + for (int i = 0; i < message->oneof_decl_count(); i++) { + InsertMidLine(initial, printer); + // Note: Ruby PB impl currently treats synthetic oneofs indifferently. + const OneofDescriptor* oneof = message->oneof_decl(i); + GenerateOneofDeclTypeDefinition(oneof, printer); + } + + InsertMidLine(initial, printer); + GenerateMessageInitMap(message, printer); + + InsertMidLine(initial, printer); + printer->Print("def initialize: (?init_map initial_value) -> void\n"); + + const Descriptor::WellKnownType wkt = message->well_known_type(); + if (message->field_count() > 0 + // These two redefine `[]` and `[]=` + && wkt != Descriptor::WELLKNOWNTYPE_LISTVALUE && + wkt != Descriptor::WELLKNOWNTYPE_STRUCT) { + InsertMidLine(initial, printer); + GenerateIndexReaderDefinition(message, printer); + InsertMidLine(initial, printer); + GenerateIndexWriterDefinition(message, printer); + } + + printer->Outdent(); + printer->Print("end\n"); +} + +void GenerateEnumLookup(const EnumDescriptor* enum_, io::Printer* printer, + bool& initial) { + printer->Print( + "$sep$(\"$full_name$\" name) -> (::Google::Protobuf::EnumDescriptor & " + "::Google::Protobuf::_SpecificEnumDescriptor[singleton($ruby_name$)])\n", + "sep", UnionSeparator(initial), "full_name", enum_->full_name(), + "ruby_name", RBSEnumFullName(enum_)); +} +void GenerateMessageLookup(const Descriptor* message, io::Printer* printer, + bool& initial) { + printer->Print( + "$sep$(\"$full_name$\" name) -> (::Google::Protobuf::Descriptor & " + "::Google::Protobuf::_SpecificDescriptor[singleton($ruby_name$)])\n", + "sep", UnionSeparator(initial), "full_name", message->full_name(), + "ruby_name", RBSMessageFullName(message)); + + for (int i = 0; i < message->nested_type_count(); i++) { + if (message->nested_type(i)->map_key() != nullptr) { + continue; + } + GenerateMessageLookup(message->nested_type(i), printer, initial); + } + for (int i = 0; i < message->enum_type_count(); i++) { + GenerateEnumLookup(message->enum_type(i), printer, initial); + } +} + +void GenerateDescriptorLookupOverride(const FileDescriptor* file, + io::Printer* printer) { + printer->Print("module Google\n"); + printer->Indent(); + printer->Print("module Protobuf\n"); + printer->Indent(); + printer->Print("class DescriptorPool\n"); + printer->Indent(); + printer->Print("def lookup:\n"); + printer->Indent(); + + bool overload_initial = true; + + for (int i = 0; i < file->message_type_count(); i++) { + const Descriptor* message = file->message_type(i); + GenerateMessageLookup(message, printer, overload_initial); + } + for (int i = 0; i < file->enum_type_count(); i++) { + const EnumDescriptor* enum_ = file->enum_type(i); + GenerateEnumLookup(enum_, printer, overload_initial); + } + + printer->Print("$sep$...\n", "sep", UnionSeparator(overload_initial)); + + printer->Outdent(); + printer->Outdent(); + printer->Print("end\n"); + printer->Outdent(); + printer->Print("end\n"); + printer->Outdent(); + printer->Print("end\n"); +} + +bool GenerateRBSFile(const FileDescriptor* file, io::Printer* printer, + std::string* error) { + printer->Print( + "# Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "# This RBS interface is provided for convenience, on a best-effort " + "basis.\n" + "# The library is the definitive source for the API contract; if the RBS " + "file\n" + "# and the library's behavior differ, the library behavior is " + "authoritative.\n" + "# We welcome fixes to change the RBS file to match.\n" + "# source: $filename$\n" + "\n", + "filename", file->name()); + + bool initial = true; + + int levels = GeneratePackageModules(file, printer); + for (int i = 0; i < file->message_type_count(); i++) { + InsertMidLine(initial, printer); + GenerateMessageTypeDefinition(file->message_type(i), printer); + } + for (int i = 0; i < file->enum_type_count(); i++) { + InsertMidLine(initial, printer); + GenerateEnumTypeDefinition(file->enum_type(i), printer); + } + EndPackageModules(levels, printer); + + printer->Print("\n"); + GenerateDescriptorLookupOverride(file, printer); + + return true; +} + +bool RBSGenerator::Generate(const FileDescriptor* file, + const std::string& parameter, + GeneratorContext* generator_context, + std::string* error) const { + std::unique_ptr output( + generator_context->Open(GetRBSOutputFilename(file->name()))); + io::Printer printer(output.get(), '$'); + + return GenerateRBSFile(file, &printer, error); +} + +} // namespace ruby +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/ruby/rbs_generator.h b/src/google/protobuf/compiler/ruby/rbs_generator.h new file mode 100644 index 0000000000000..899885efeb082 --- /dev/null +++ b/src/google/protobuf/compiler/ruby/rbs_generator.h @@ -0,0 +1,39 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +// Generates Ruby code for a given .proto file. + +#ifndef GOOGLE_PROTOBUF_COMPILER_RUBY_RBS_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_RUBY_RBS_GENERATOR_H__ + +#include + +#include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/port_def.inc" + +namespace google { +namespace protobuf { +namespace compiler { +namespace ruby { +// CodeGenerator implementation for generated RBS type definition. +class PROTOC_EXPORT RBSGenerator : public CodeGenerator { + bool Generate(const FileDescriptor* file, const std::string& parameter, + GeneratorContext* generator_context, + std::string* error) const override; + uint64_t GetSupportedFeatures() const override { + return FEATURE_PROTO3_OPTIONAL; + } +}; + +} // namespace ruby +} // namespace compiler +} // namespace protobuf +} // namespace google + +#include "google/protobuf/port_undef.inc" + +#endif // GOOGLE_PROTOBUF_COMPILER_RUBY_RBS_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/ruby/rbs_generator_unittest.cc b/src/google/protobuf/compiler/ruby/rbs_generator_unittest.cc new file mode 100644 index 0000000000000..9b166de8473f8 --- /dev/null +++ b/src/google/protobuf/compiler/ruby/rbs_generator_unittest.cc @@ -0,0 +1,110 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2014 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "google/protobuf/compiler/ruby/rbs_generator.h" + +#include + +#include +#include "absl/log/absl_check.h" +#include "google/protobuf/compiler/command_line_interface.h" +#include "google/protobuf/testing/googletest.h" +#include "google/protobuf/testing/file.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace ruby { +namespace { + +std::string FindRubyTestDir() { + return absl::StrCat(TestSourceDir(), "/google/protobuf/compiler/ruby"); +} + +// This test is a simple golden-file test over the output of the Ruby code +// generator. When we make changes to the Ruby extension and alter the Ruby code +// generator to use those changes, we should (i) manually test the output of the +// code generator with the extension, and (ii) update the golden output above. +// Some day, we may integrate build systems between protoc and the language +// extensions to the point where we can do this test in a more automated way. + +void RBSTest(std::string proto_file, std::string import_proto_file = "") { + std::string ruby_tests = FindRubyTestDir(); + + google::protobuf::compiler::CommandLineInterface cli; + cli.SetInputsAreProtoPathRelative(true); + + ruby::RBSGenerator rbs_generator; + cli.RegisterGenerator("--rbs_out", &rbs_generator, ""); + + // Copy generated_code.proto to the temporary test directory. + std::string test_input; + ABSL_CHECK_OK(File::GetContents( + absl::StrCat(ruby_tests, proto_file, ".proto"), &test_input, true)); + ABSL_CHECK_OK(File::SetContents( + absl::StrCat(TestTempDir(), proto_file, ".proto"), test_input, true)); + + // Copy generated_code_import.proto to the temporary test directory. + std::string test_import; + if (!import_proto_file.empty()) { + ABSL_CHECK_OK( + File::GetContents(absl::StrCat(ruby_tests, import_proto_file, ".proto"), + &test_import, true)); + ABSL_CHECK_OK(File::SetContents( + absl::StrCat(TestTempDir(), import_proto_file, ".proto"), test_import, + true)); + } + + // Invoke the proto compiler (we will be inside TestTempDir() at this point). + std::string ruby_out = absl::StrCat("--rbs_out=", TestTempDir()); + std::string proto_path = absl::StrCat("--proto_path=", TestTempDir()); + + std::string proto_target = absl::StrCat(TestTempDir(), proto_file, ".proto"); + const char* argv[] = { + "protoc", + ruby_out.c_str(), + proto_path.c_str(), + proto_target.c_str(), + }; + + EXPECT_EQ(0, cli.Run(4, argv)); + + // Load the generated output and compare to the expected result. + std::string output; + ABSL_CHECK_OK(File::GetContentsAsText( + absl::StrCat(TestTempDir(), proto_file, "_pb.rbs"), &output, true)); + std::string expected_output; + ABSL_CHECK_OK(File::GetContentsAsText( + absl::StrCat(ruby_tests, proto_file, "_pb.rbs"), &expected_output, true)); + EXPECT_EQ(expected_output, output); +} + +TEST(RubyGeneratorTest, Proto3GeneratorTest) { + RBSTest("/ruby_generated_code", "/ruby_generated_code_proto2_import"); +} + +TEST(RubyGeneratorTest, Proto2GeneratorTest) { + RBSTest("/ruby_generated_code_proto2", "/ruby_generated_code_proto2_import"); +} + +TEST(RubyGeneratorTest, Proto3ImplicitPackageTest) { + RBSTest("/ruby_generated_pkg_implicit"); +} + +TEST(RubyGeneratorTest, Proto3ExplictPackageTest) { + RBSTest("/ruby_generated_pkg_explicit"); +} + +TEST(RubyGeneratorTest, Proto3ExplictLegacyPackageTest) { + RBSTest("/ruby_generated_pkg_explicit_legacy"); +} + +} // namespace +} // namespace ruby +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_code.proto b/src/google/protobuf/compiler/ruby/ruby_generated_code.proto index 216734829820d..f4d0ddb2bfb81 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generated_code.proto +++ b/src/google/protobuf/compiler/ruby/ruby_generated_code.proto @@ -11,7 +11,9 @@ package A.B.C; import "ruby_generated_code_proto2_import.proto"; +// Leading comment for TestMessage message TestMessage { + // Leading comment for optional_int32 int32 optional_int32 = 1; int64 optional_int64 = 2; uint32 optional_uint32 = 3; diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_code_pb.rbs b/src/google/protobuf/compiler/ruby/ruby_generated_code_pb.rbs new file mode 100644 index 0000000000000..42513ce9530af --- /dev/null +++ b/src/google/protobuf/compiler/ruby/ruby_generated_code_pb.rbs @@ -0,0 +1,448 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# This RBS interface is provided for convenience, on a best-effort basis. +# The library is the definitive source for the API contract; if the RBS file +# and the library's behavior differ, the library behavior is authoritative. +# We welcome fixes to change the RBS file to match. +# source: ruby_generated_code.proto + +module A + module B + module C + # Leading comment for TestMessage + class TestMessage < ::Google::Protobuf::AbstractMessage + class NestedMessage < ::Google::Protobuf::AbstractMessage + include ::Google::Protobuf::_MessageClass[::A::B::C::TestMessage::NestedMessage] + + attr_reader foo(): ::Integer + attr_writer foo(): ::Integer | ::Float + def clear_foo: () -> void + + type init_map = { + foo: (::Integer | ::Float)?, + "foo" => (::Integer | ::Float)?, + } + + def initialize: (?init_map initial_value) -> void + + def []: + ("foo" name) -> ::Integer + + def []=: + ("foo" name, (::Integer | ::Float) value) -> void + end + + include ::Google::Protobuf::_MessageClass[::A::B::C::TestMessage] + + # Leading comment for optional_int32 + attr_reader optional_int32(): ::Integer + attr_writer optional_int32(): ::Integer | ::Float + def clear_optional_int32: () -> void + + attr_reader optional_int64(): ::Integer + attr_writer optional_int64(): ::Integer | ::Float + def clear_optional_int64: () -> void + + attr_reader optional_uint32(): ::Integer + attr_writer optional_uint32(): ::Integer | ::Float + def clear_optional_uint32: () -> void + + attr_reader optional_uint64(): ::Integer + attr_writer optional_uint64(): ::Integer | ::Float + def clear_optional_uint64: () -> void + + attr_accessor optional_bool(): bool + def clear_optional_bool: () -> void + + attr_reader optional_double(): ::Float + attr_writer optional_double(): ::Float | ::Integer + def clear_optional_double: () -> void + + attr_reader optional_float(): ::Float + attr_writer optional_float(): ::Float | ::Integer + def clear_optional_float: () -> void + + attr_reader optional_string(): ::String + attr_writer optional_string(): ::String | ::Symbol + def clear_optional_string: () -> void + + attr_accessor optional_bytes(): ::String + def clear_optional_bytes: () -> void + + attr_reader optional_enum(): ::A::B::C::TestEnum::names | ::Integer + attr_writer optional_enum(): ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float + attr_reader optional_enum_const(): ::Integer + def clear_optional_enum: () -> void + + attr_accessor optional_msg(): ::A::B::C::TestMessage? + def has_optional_msg?: () -> bool + def clear_optional_msg: () -> void + + attr_accessor optional_proto2_submessage(): ::A::B::C::TestImportedMessage? + def has_optional_proto2_submessage?: () -> bool + def clear_optional_proto2_submessage: () -> void + + attr_accessor repeated_int32(): ::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float] + def clear_repeated_int32: () -> void + + attr_accessor repeated_int64(): ::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float] + def clear_repeated_int64: () -> void + + attr_accessor repeated_uint32(): ::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float] + def clear_repeated_uint32: () -> void + + attr_accessor repeated_uint64(): ::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float] + def clear_repeated_uint64: () -> void + + attr_accessor repeated_bool(): ::Google::Protobuf::RepeatedField[bool, bool] + def clear_repeated_bool: () -> void + + attr_accessor repeated_double(): ::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer] + def clear_repeated_double: () -> void + + attr_accessor repeated_float(): ::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer] + def clear_repeated_float: () -> void + + attr_accessor repeated_string(): ::Google::Protobuf::RepeatedField[::String, ::String | ::Symbol] + def clear_repeated_string: () -> void + + attr_accessor repeated_bytes(): ::Google::Protobuf::RepeatedField[::String, ::String] + def clear_repeated_bytes: () -> void + + attr_accessor repeated_enum(): ::Google::Protobuf::RepeatedField[::A::B::C::TestEnum::names | ::Integer, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float] + attr_reader repeated_enum_const(): ::Array[::Integer] + def clear_repeated_enum: () -> void + + attr_accessor repeated_msg(): ::Google::Protobuf::RepeatedField[::A::B::C::TestMessage, ::A::B::C::TestMessage] + def clear_repeated_msg: () -> void + + attr_reader oneof_int32(): ::Integer + attr_writer oneof_int32(): (::Integer | ::Float)? + def has_oneof_int32?: () -> bool + def clear_oneof_int32: () -> void + + attr_reader oneof_int64(): ::Integer + attr_writer oneof_int64(): (::Integer | ::Float)? + def has_oneof_int64?: () -> bool + def clear_oneof_int64: () -> void + + attr_reader oneof_uint32(): ::Integer + attr_writer oneof_uint32(): (::Integer | ::Float)? + def has_oneof_uint32?: () -> bool + def clear_oneof_uint32: () -> void + + attr_reader oneof_uint64(): ::Integer + attr_writer oneof_uint64(): (::Integer | ::Float)? + def has_oneof_uint64?: () -> bool + def clear_oneof_uint64: () -> void + + attr_reader oneof_bool(): bool + attr_writer oneof_bool(): bool? + def has_oneof_bool?: () -> bool + def clear_oneof_bool: () -> void + + attr_reader oneof_double(): ::Float + attr_writer oneof_double(): (::Float | ::Integer)? + def has_oneof_double?: () -> bool + def clear_oneof_double: () -> void + + attr_reader oneof_float(): ::Float + attr_writer oneof_float(): (::Float | ::Integer)? + def has_oneof_float?: () -> bool + def clear_oneof_float: () -> void + + attr_reader oneof_string(): ::String + attr_writer oneof_string(): (::String | ::Symbol)? + def has_oneof_string?: () -> bool + def clear_oneof_string: () -> void + + attr_reader oneof_bytes(): ::String + attr_writer oneof_bytes(): ::String? + def has_oneof_bytes?: () -> bool + def clear_oneof_bytes: () -> void + + attr_reader oneof_enum(): ::A::B::C::TestEnum::names | ::Integer + attr_writer oneof_enum(): (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)? + attr_reader oneof_enum_const(): ::Integer + def has_oneof_enum?: () -> bool + def clear_oneof_enum: () -> void + + attr_accessor oneof_msg(): ::A::B::C::TestMessage? + def has_oneof_msg?: () -> bool + def clear_oneof_msg: () -> void + + attr_accessor map_int32_string(): ::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol] + def clear_map_int32_string: () -> void + + attr_accessor map_int64_string(): ::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol] + def clear_map_int64_string: () -> void + + attr_accessor map_uint32_string(): ::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol] + def clear_map_uint32_string: () -> void + + attr_accessor map_uint64_string(): ::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol] + def clear_map_uint64_string: () -> void + + attr_accessor map_bool_string(): ::Google::Protobuf::Map[bool, ::String, bool, ::String | ::Symbol] + def clear_map_bool_string: () -> void + + attr_accessor map_string_string(): ::Google::Protobuf::Map[::String, ::String, ::String | ::Symbol, ::String | ::Symbol] + def clear_map_string_string: () -> void + + attr_accessor map_string_msg(): ::Google::Protobuf::Map[::String, ::A::B::C::TestMessage, ::String | ::Symbol, ::A::B::C::TestMessage] + def clear_map_string_msg: () -> void + + attr_accessor map_string_enum(): ::Google::Protobuf::Map[::String, ::A::B::C::TestEnum::names | ::Integer, ::String | ::Symbol, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float] + def clear_map_string_enum: () -> void + + attr_accessor map_string_int32(): ::Google::Protobuf::Map[::String, ::Integer, ::String | ::Symbol, ::Integer | ::Float] + def clear_map_string_int32: () -> void + + attr_accessor map_string_bool(): ::Google::Protobuf::Map[::String, bool, ::String | ::Symbol, bool] + def clear_map_string_bool: () -> void + + attr_accessor nested_message(): ::A::B::C::TestMessage::NestedMessage? + def has_nested_message?: () -> bool + def clear_nested_message: () -> void + + attr_reader my_oneof(): (::Integer | bool | ::Float | ::String | ::A::B::C::TestEnum::names | ::Integer | ::A::B::C::TestMessage)? + def has_my_oneof?: () -> bool + def clear_my_oneof: () -> void + + type init_map = { + optional_int32: (::Integer | ::Float)?, + "optional_int32" => (::Integer | ::Float)?, + optional_int64: (::Integer | ::Float)?, + "optional_int64" => (::Integer | ::Float)?, + optional_uint32: (::Integer | ::Float)?, + "optional_uint32" => (::Integer | ::Float)?, + optional_uint64: (::Integer | ::Float)?, + "optional_uint64" => (::Integer | ::Float)?, + optional_bool: bool?, + "optional_bool" => bool?, + optional_double: (::Float | ::Integer)?, + "optional_double" => (::Float | ::Integer)?, + optional_float: (::Float | ::Integer)?, + "optional_float" => (::Float | ::Integer)?, + optional_string: (::String | ::Symbol)?, + "optional_string" => (::String | ::Symbol)?, + optional_bytes: ::String?, + "optional_bytes" => ::String?, + optional_enum: (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?, + "optional_enum" => (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?, + optional_msg: (::A::B::C::TestMessage | ::A::B::C::TestMessage::init_map)?, + "optional_msg" => (::A::B::C::TestMessage | ::A::B::C::TestMessage::init_map)?, + optional_proto2_submessage: (::A::B::C::TestImportedMessage | ::A::B::C::TestImportedMessage::init_map)?, + "optional_proto2_submessage" => (::A::B::C::TestImportedMessage | ::A::B::C::TestImportedMessage::init_map)?, + repeated_int32: ::Array[::Integer | ::Float]?, + "repeated_int32" => ::Array[::Integer | ::Float]?, + repeated_int64: ::Array[::Integer | ::Float]?, + "repeated_int64" => ::Array[::Integer | ::Float]?, + repeated_uint32: ::Array[::Integer | ::Float]?, + "repeated_uint32" => ::Array[::Integer | ::Float]?, + repeated_uint64: ::Array[::Integer | ::Float]?, + "repeated_uint64" => ::Array[::Integer | ::Float]?, + repeated_bool: ::Array[bool]?, + "repeated_bool" => ::Array[bool]?, + repeated_double: ::Array[::Float | ::Integer]?, + "repeated_double" => ::Array[::Float | ::Integer]?, + repeated_float: ::Array[::Float | ::Integer]?, + "repeated_float" => ::Array[::Float | ::Integer]?, + repeated_string: ::Array[::String | ::Symbol]?, + "repeated_string" => ::Array[::String | ::Symbol]?, + repeated_bytes: ::Array[::String]?, + "repeated_bytes" => ::Array[::String]?, + repeated_enum: ::Array[::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]?, + "repeated_enum" => ::Array[::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]?, + repeated_msg: ::Array[::A::B::C::TestMessage]?, + "repeated_msg" => ::Array[::A::B::C::TestMessage]?, + oneof_int32: (::Integer | ::Float)?, + "oneof_int32" => (::Integer | ::Float)?, + oneof_int64: (::Integer | ::Float)?, + "oneof_int64" => (::Integer | ::Float)?, + oneof_uint32: (::Integer | ::Float)?, + "oneof_uint32" => (::Integer | ::Float)?, + oneof_uint64: (::Integer | ::Float)?, + "oneof_uint64" => (::Integer | ::Float)?, + oneof_bool: bool?, + "oneof_bool" => bool?, + oneof_double: (::Float | ::Integer)?, + "oneof_double" => (::Float | ::Integer)?, + oneof_float: (::Float | ::Integer)?, + "oneof_float" => (::Float | ::Integer)?, + oneof_string: (::String | ::Symbol)?, + "oneof_string" => (::String | ::Symbol)?, + oneof_bytes: ::String?, + "oneof_bytes" => ::String?, + oneof_enum: (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?, + "oneof_enum" => (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?, + oneof_msg: (::A::B::C::TestMessage | ::A::B::C::TestMessage::init_map)?, + "oneof_msg" => (::A::B::C::TestMessage | ::A::B::C::TestMessage::init_map)?, + map_int32_string: ::Hash[::Integer | ::Float, ::String | ::Symbol]?, + "map_int32_string" => ::Hash[::Integer | ::Float, ::String | ::Symbol]?, + map_int64_string: ::Hash[::Integer | ::Float, ::String | ::Symbol]?, + "map_int64_string" => ::Hash[::Integer | ::Float, ::String | ::Symbol]?, + map_uint32_string: ::Hash[::Integer | ::Float, ::String | ::Symbol]?, + "map_uint32_string" => ::Hash[::Integer | ::Float, ::String | ::Symbol]?, + map_uint64_string: ::Hash[::Integer | ::Float, ::String | ::Symbol]?, + "map_uint64_string" => ::Hash[::Integer | ::Float, ::String | ::Symbol]?, + map_bool_string: ::Hash[bool, ::String | ::Symbol]?, + "map_bool_string" => ::Hash[bool, ::String | ::Symbol]?, + map_string_string: ::Hash[::String | ::Symbol, ::String | ::Symbol]?, + "map_string_string" => ::Hash[::String | ::Symbol, ::String | ::Symbol]?, + map_string_msg: ::Hash[::String | ::Symbol, ::A::B::C::TestMessage]?, + "map_string_msg" => ::Hash[::String | ::Symbol, ::A::B::C::TestMessage]?, + map_string_enum: ::Hash[::String | ::Symbol, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]?, + "map_string_enum" => ::Hash[::String | ::Symbol, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]?, + map_string_int32: ::Hash[::String | ::Symbol, ::Integer | ::Float]?, + "map_string_int32" => ::Hash[::String | ::Symbol, ::Integer | ::Float]?, + map_string_bool: ::Hash[::String | ::Symbol, bool]?, + "map_string_bool" => ::Hash[::String | ::Symbol, bool]?, + nested_message: (::A::B::C::TestMessage::NestedMessage | ::A::B::C::TestMessage::NestedMessage::init_map)?, + "nested_message" => (::A::B::C::TestMessage::NestedMessage | ::A::B::C::TestMessage::NestedMessage::init_map)?, + } + + def initialize: (?init_map initial_value) -> void + + def []: + ("optional_int32" name) -> ::Integer + | ("optional_int64" name) -> ::Integer + | ("optional_uint32" name) -> ::Integer + | ("optional_uint64" name) -> ::Integer + | ("optional_bool" name) -> bool + | ("optional_double" name) -> ::Float + | ("optional_float" name) -> ::Float + | ("optional_string" name) -> ::String + | ("optional_bytes" name) -> ::String + | ("optional_enum" name) -> (::A::B::C::TestEnum::names | ::Integer) + | ("optional_msg" name) -> ::A::B::C::TestMessage? + | ("optional_proto2_submessage" name) -> ::A::B::C::TestImportedMessage? + | ("repeated_int32" name) -> (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) + | ("repeated_int64" name) -> (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) + | ("repeated_uint32" name) -> (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) + | ("repeated_uint64" name) -> (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) + | ("repeated_bool" name) -> ::Google::Protobuf::RepeatedField[bool, bool] + | ("repeated_double" name) -> (::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer]) + | ("repeated_float" name) -> (::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer]) + | ("repeated_string" name) -> (::Google::Protobuf::RepeatedField[::String, ::String | ::Symbol]) + | ("repeated_bytes" name) -> ::Google::Protobuf::RepeatedField[::String, ::String] + | ("repeated_enum" name) -> (::Google::Protobuf::RepeatedField[::A::B::C::TestEnum::names | ::Integer, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]) + | ("repeated_msg" name) -> ::Google::Protobuf::RepeatedField[::A::B::C::TestMessage, ::A::B::C::TestMessage] + | ("oneof_int32" name) -> ::Integer + | ("oneof_int64" name) -> ::Integer + | ("oneof_uint32" name) -> ::Integer + | ("oneof_uint64" name) -> ::Integer + | ("oneof_bool" name) -> bool + | ("oneof_double" name) -> ::Float + | ("oneof_float" name) -> ::Float + | ("oneof_string" name) -> ::String + | ("oneof_bytes" name) -> ::String + | ("oneof_enum" name) -> (::A::B::C::TestEnum::names | ::Integer) + | ("oneof_msg" name) -> ::A::B::C::TestMessage? + | ("map_int32_string" name) -> (::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol]) + | ("map_int64_string" name) -> (::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol]) + | ("map_uint32_string" name) -> (::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol]) + | ("map_uint64_string" name) -> (::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol]) + | ("map_bool_string" name) -> (::Google::Protobuf::Map[bool, ::String, bool, ::String | ::Symbol]) + | ("map_string_string" name) -> (::Google::Protobuf::Map[::String, ::String, ::String | ::Symbol, ::String | ::Symbol]) + | ("map_string_msg" name) -> (::Google::Protobuf::Map[::String, ::A::B::C::TestMessage, ::String | ::Symbol, ::A::B::C::TestMessage]) + | ("map_string_enum" name) -> (::Google::Protobuf::Map[::String, ::A::B::C::TestEnum::names | ::Integer, ::String | ::Symbol, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]) + | ("map_string_int32" name) -> (::Google::Protobuf::Map[::String, ::Integer, ::String | ::Symbol, ::Integer | ::Float]) + | ("map_string_bool" name) -> (::Google::Protobuf::Map[::String, bool, ::String | ::Symbol, bool]) + | ("nested_message" name) -> ::A::B::C::TestMessage::NestedMessage? + + def []=: + ("optional_int32" name, (::Integer | ::Float) value) -> void + | ("optional_int64" name, (::Integer | ::Float) value) -> void + | ("optional_uint32" name, (::Integer | ::Float) value) -> void + | ("optional_uint64" name, (::Integer | ::Float) value) -> void + | ("optional_bool" name, bool value) -> void + | ("optional_double" name, (::Float | ::Integer) value) -> void + | ("optional_float" name, (::Float | ::Integer) value) -> void + | ("optional_string" name, (::String | ::Symbol) value) -> void + | ("optional_bytes" name, ::String value) -> void + | ("optional_enum" name, (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float) value) -> void + | ("optional_msg" name, ::A::B::C::TestMessage? value) -> void + | ("optional_proto2_submessage" name, ::A::B::C::TestImportedMessage? value) -> void + | ("repeated_int32" name, (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) value) -> void + | ("repeated_int64" name, (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) value) -> void + | ("repeated_uint32" name, (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) value) -> void + | ("repeated_uint64" name, (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) value) -> void + | ("repeated_bool" name, ::Google::Protobuf::RepeatedField[bool, bool] value) -> void + | ("repeated_double" name, (::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer]) value) -> void + | ("repeated_float" name, (::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer]) value) -> void + | ("repeated_string" name, (::Google::Protobuf::RepeatedField[::String, ::String | ::Symbol]) value) -> void + | ("repeated_bytes" name, ::Google::Protobuf::RepeatedField[::String, ::String] value) -> void + | ("repeated_enum" name, (::Google::Protobuf::RepeatedField[::A::B::C::TestEnum::names | ::Integer, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]) value) -> void + | ("repeated_msg" name, ::Google::Protobuf::RepeatedField[::A::B::C::TestMessage, ::A::B::C::TestMessage] value) -> void + | ("oneof_int32" name, ((::Integer | ::Float)?) value) -> void + | ("oneof_int64" name, ((::Integer | ::Float)?) value) -> void + | ("oneof_uint32" name, ((::Integer | ::Float)?) value) -> void + | ("oneof_uint64" name, ((::Integer | ::Float)?) value) -> void + | ("oneof_bool" name, bool? value) -> void + | ("oneof_double" name, ((::Float | ::Integer)?) value) -> void + | ("oneof_float" name, ((::Float | ::Integer)?) value) -> void + | ("oneof_string" name, ((::String | ::Symbol)?) value) -> void + | ("oneof_bytes" name, ::String? value) -> void + | ("oneof_enum" name, ((::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?) value) -> void + | ("oneof_msg" name, ::A::B::C::TestMessage? value) -> void + | ("map_int32_string" name, (::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol]) value) -> void + | ("map_int64_string" name, (::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol]) value) -> void + | ("map_uint32_string" name, (::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol]) value) -> void + | ("map_uint64_string" name, (::Google::Protobuf::Map[::Integer, ::String, ::Integer | ::Float, ::String | ::Symbol]) value) -> void + | ("map_bool_string" name, (::Google::Protobuf::Map[bool, ::String, bool, ::String | ::Symbol]) value) -> void + | ("map_string_string" name, (::Google::Protobuf::Map[::String, ::String, ::String | ::Symbol, ::String | ::Symbol]) value) -> void + | ("map_string_msg" name, (::Google::Protobuf::Map[::String, ::A::B::C::TestMessage, ::String | ::Symbol, ::A::B::C::TestMessage]) value) -> void + | ("map_string_enum" name, (::Google::Protobuf::Map[::String, ::A::B::C::TestEnum::names | ::Integer, ::String | ::Symbol, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]) value) -> void + | ("map_string_int32" name, (::Google::Protobuf::Map[::String, ::Integer, ::String | ::Symbol, ::Integer | ::Float]) value) -> void + | ("map_string_bool" name, (::Google::Protobuf::Map[::String, bool, ::String | ::Symbol, bool]) value) -> void + | ("nested_message" name, ::A::B::C::TestMessage::NestedMessage? value) -> void + end + + module TestEnum + extend ::Google::Protobuf::_EnumModule + + Default: 0 + + A: 1 + + B: 2 + + C: 3 + + def self.lookup: + (0 number) -> :Default + | (1 number) -> :A + | (2 number) -> :B + | (3 number) -> :C + | (::int number) -> names? + | ... + + def self.resolve: + (:Default name) -> 0 + | (:A name) -> 1 + | (:B name) -> 2 + | (:C name) -> 3 + | (::Symbol name) -> numbers? + | ... + + type names = :Default | :A | :B | :C + + type strings = "Default" | "A" | "B" | "C" + + type numbers = 0 | 1 | 2 | 3 + end + end + end +end + +module Google + module Protobuf + class DescriptorPool + def lookup: + ("A.B.C.TestMessage" name) -> (::Google::Protobuf::Descriptor & ::Google::Protobuf::_SpecificDescriptor[singleton(::A::B::C::TestMessage)]) + | ("A.B.C.TestMessage.NestedMessage" name) -> (::Google::Protobuf::Descriptor & ::Google::Protobuf::_SpecificDescriptor[singleton(::A::B::C::TestMessage::NestedMessage)]) + | ("A.B.C.TestEnum" name) -> (::Google::Protobuf::EnumDescriptor & ::Google::Protobuf::_SpecificEnumDescriptor[singleton(::A::B::C::TestEnum)]) + | ... + end + end +end diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_code_proto2_pb.rbs b/src/google/protobuf/compiler/ruby/ruby_generated_code_proto2_pb.rbs new file mode 100644 index 0000000000000..f43c57a6a1aff --- /dev/null +++ b/src/google/protobuf/compiler/ruby/ruby_generated_code_proto2_pb.rbs @@ -0,0 +1,484 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# This RBS interface is provided for convenience, on a best-effort basis. +# The library is the definitive source for the API contract; if the RBS file +# and the library's behavior differ, the library behavior is authoritative. +# We welcome fixes to change the RBS file to match. +# source: ruby_generated_code_proto2.proto + +module A + module B + module C + class TestMessage < ::Google::Protobuf::AbstractMessage + class NestedMessage < ::Google::Protobuf::AbstractMessage + include ::Google::Protobuf::_MessageClass[::A::B::C::TestMessage::NestedMessage] + + attr_reader foo(): ::Integer + attr_writer foo(): ::Integer | ::Float + def has_foo?: () -> bool + def clear_foo: () -> void + + type init_map = { + foo: (::Integer | ::Float)?, + "foo" => (::Integer | ::Float)?, + } + + def initialize: (?init_map initial_value) -> void + + def []: + ("foo" name) -> ::Integer + + def []=: + ("foo" name, (::Integer | ::Float) value) -> void + end + + include ::Google::Protobuf::_MessageClass[::A::B::C::TestMessage] + + attr_reader optional_int32(): ::Integer + attr_writer optional_int32(): ::Integer | ::Float + def has_optional_int32?: () -> bool + def clear_optional_int32: () -> void + + attr_reader optional_int64(): ::Integer + attr_writer optional_int64(): ::Integer | ::Float + def has_optional_int64?: () -> bool + def clear_optional_int64: () -> void + + attr_reader optional_uint32(): ::Integer + attr_writer optional_uint32(): ::Integer | ::Float + def has_optional_uint32?: () -> bool + def clear_optional_uint32: () -> void + + attr_reader optional_uint64(): ::Integer + attr_writer optional_uint64(): ::Integer | ::Float + def has_optional_uint64?: () -> bool + def clear_optional_uint64: () -> void + + attr_accessor optional_bool(): bool + def has_optional_bool?: () -> bool + def clear_optional_bool: () -> void + + attr_reader optional_double(): ::Float + attr_writer optional_double(): ::Float | ::Integer + def has_optional_double?: () -> bool + def clear_optional_double: () -> void + + attr_reader optional_float(): ::Float + attr_writer optional_float(): ::Float | ::Integer + def has_optional_float?: () -> bool + def clear_optional_float: () -> void + + attr_reader optional_string(): ::String + attr_writer optional_string(): ::String | ::Symbol + def has_optional_string?: () -> bool + def clear_optional_string: () -> void + + attr_accessor optional_bytes(): ::String + def has_optional_bytes?: () -> bool + def clear_optional_bytes: () -> void + + attr_reader optional_enum(): ::A::B::C::TestEnum::names | ::Integer + attr_writer optional_enum(): ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float + attr_reader optional_enum_const(): ::Integer + def has_optional_enum?: () -> bool + def clear_optional_enum: () -> void + + attr_accessor optional_msg(): ::A::B::C::TestMessage? + def has_optional_msg?: () -> bool + def clear_optional_msg: () -> void + + attr_accessor optional_proto2_submessage(): ::A::B::C::TestImportedMessage? + def has_optional_proto2_submessage?: () -> bool + def clear_optional_proto2_submessage: () -> void + + attr_accessor repeated_int32(): ::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float] + def clear_repeated_int32: () -> void + + attr_accessor repeated_int64(): ::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float] + def clear_repeated_int64: () -> void + + attr_accessor repeated_uint32(): ::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float] + def clear_repeated_uint32: () -> void + + attr_accessor repeated_uint64(): ::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float] + def clear_repeated_uint64: () -> void + + attr_accessor repeated_bool(): ::Google::Protobuf::RepeatedField[bool, bool] + def clear_repeated_bool: () -> void + + attr_accessor repeated_double(): ::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer] + def clear_repeated_double: () -> void + + attr_accessor repeated_float(): ::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer] + def clear_repeated_float: () -> void + + attr_accessor repeated_string(): ::Google::Protobuf::RepeatedField[::String, ::String | ::Symbol] + def clear_repeated_string: () -> void + + attr_accessor repeated_bytes(): ::Google::Protobuf::RepeatedField[::String, ::String] + def clear_repeated_bytes: () -> void + + attr_accessor repeated_enum(): ::Google::Protobuf::RepeatedField[::A::B::C::TestEnum::names | ::Integer, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float] + attr_reader repeated_enum_const(): ::Array[::Integer] + def clear_repeated_enum: () -> void + + attr_accessor repeated_msg(): ::Google::Protobuf::RepeatedField[::A::B::C::TestMessage, ::A::B::C::TestMessage] + def clear_repeated_msg: () -> void + + attr_reader required_int32(): ::Integer + attr_writer required_int32(): ::Integer | ::Float + def has_required_int32?: () -> bool + def clear_required_int32: () -> void + + attr_reader required_int64(): ::Integer + attr_writer required_int64(): ::Integer | ::Float + def has_required_int64?: () -> bool + def clear_required_int64: () -> void + + attr_reader required_uint32(): ::Integer + attr_writer required_uint32(): ::Integer | ::Float + def has_required_uint32?: () -> bool + def clear_required_uint32: () -> void + + attr_reader required_uint64(): ::Integer + attr_writer required_uint64(): ::Integer | ::Float + def has_required_uint64?: () -> bool + def clear_required_uint64: () -> void + + attr_accessor required_bool(): bool + def has_required_bool?: () -> bool + def clear_required_bool: () -> void + + attr_reader required_double(): ::Float + attr_writer required_double(): ::Float | ::Integer + def has_required_double?: () -> bool + def clear_required_double: () -> void + + attr_reader required_float(): ::Float + attr_writer required_float(): ::Float | ::Integer + def has_required_float?: () -> bool + def clear_required_float: () -> void + + attr_reader required_string(): ::String + attr_writer required_string(): ::String | ::Symbol + def has_required_string?: () -> bool + def clear_required_string: () -> void + + attr_accessor required_bytes(): ::String + def has_required_bytes?: () -> bool + def clear_required_bytes: () -> void + + attr_reader required_enum(): ::A::B::C::TestEnum::names | ::Integer + attr_writer required_enum(): ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float + attr_reader required_enum_const(): ::Integer + def has_required_enum?: () -> bool + def clear_required_enum: () -> void + + attr_accessor required_msg(): ::A::B::C::TestMessage? + def has_required_msg?: () -> bool + def clear_required_msg: () -> void + + attr_reader oneof_int32(): ::Integer + attr_writer oneof_int32(): (::Integer | ::Float)? + def has_oneof_int32?: () -> bool + def clear_oneof_int32: () -> void + + attr_reader oneof_int64(): ::Integer + attr_writer oneof_int64(): (::Integer | ::Float)? + def has_oneof_int64?: () -> bool + def clear_oneof_int64: () -> void + + attr_reader oneof_uint32(): ::Integer + attr_writer oneof_uint32(): (::Integer | ::Float)? + def has_oneof_uint32?: () -> bool + def clear_oneof_uint32: () -> void + + attr_reader oneof_uint64(): ::Integer + attr_writer oneof_uint64(): (::Integer | ::Float)? + def has_oneof_uint64?: () -> bool + def clear_oneof_uint64: () -> void + + attr_reader oneof_bool(): bool + attr_writer oneof_bool(): bool? + def has_oneof_bool?: () -> bool + def clear_oneof_bool: () -> void + + attr_reader oneof_double(): ::Float + attr_writer oneof_double(): (::Float | ::Integer)? + def has_oneof_double?: () -> bool + def clear_oneof_double: () -> void + + attr_reader oneof_float(): ::Float + attr_writer oneof_float(): (::Float | ::Integer)? + def has_oneof_float?: () -> bool + def clear_oneof_float: () -> void + + attr_reader oneof_string(): ::String + attr_writer oneof_string(): (::String | ::Symbol)? + def has_oneof_string?: () -> bool + def clear_oneof_string: () -> void + + attr_reader oneof_bytes(): ::String + attr_writer oneof_bytes(): ::String? + def has_oneof_bytes?: () -> bool + def clear_oneof_bytes: () -> void + + attr_reader oneof_enum(): ::A::B::C::TestEnum::names | ::Integer + attr_writer oneof_enum(): (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)? + attr_reader oneof_enum_const(): ::Integer + def has_oneof_enum?: () -> bool + def clear_oneof_enum: () -> void + + attr_accessor oneof_msg(): ::A::B::C::TestMessage? + def has_oneof_msg?: () -> bool + def clear_oneof_msg: () -> void + + attr_accessor nested_message(): ::A::B::C::TestMessage::NestedMessage? + def has_nested_message?: () -> bool + def clear_nested_message: () -> void + + attr_reader my_oneof(): (::Integer | bool | ::Float | ::String | ::A::B::C::TestEnum::names | ::Integer | ::A::B::C::TestMessage)? + def has_my_oneof?: () -> bool + def clear_my_oneof: () -> void + + type init_map = { + optional_int32: (::Integer | ::Float)?, + "optional_int32" => (::Integer | ::Float)?, + optional_int64: (::Integer | ::Float)?, + "optional_int64" => (::Integer | ::Float)?, + optional_uint32: (::Integer | ::Float)?, + "optional_uint32" => (::Integer | ::Float)?, + optional_uint64: (::Integer | ::Float)?, + "optional_uint64" => (::Integer | ::Float)?, + optional_bool: bool?, + "optional_bool" => bool?, + optional_double: (::Float | ::Integer)?, + "optional_double" => (::Float | ::Integer)?, + optional_float: (::Float | ::Integer)?, + "optional_float" => (::Float | ::Integer)?, + optional_string: (::String | ::Symbol)?, + "optional_string" => (::String | ::Symbol)?, + optional_bytes: ::String?, + "optional_bytes" => ::String?, + optional_enum: (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?, + "optional_enum" => (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?, + optional_msg: (::A::B::C::TestMessage | ::A::B::C::TestMessage::init_map)?, + "optional_msg" => (::A::B::C::TestMessage | ::A::B::C::TestMessage::init_map)?, + optional_proto2_submessage: (::A::B::C::TestImportedMessage | ::A::B::C::TestImportedMessage::init_map)?, + "optional_proto2_submessage" => (::A::B::C::TestImportedMessage | ::A::B::C::TestImportedMessage::init_map)?, + repeated_int32: ::Array[::Integer | ::Float]?, + "repeated_int32" => ::Array[::Integer | ::Float]?, + repeated_int64: ::Array[::Integer | ::Float]?, + "repeated_int64" => ::Array[::Integer | ::Float]?, + repeated_uint32: ::Array[::Integer | ::Float]?, + "repeated_uint32" => ::Array[::Integer | ::Float]?, + repeated_uint64: ::Array[::Integer | ::Float]?, + "repeated_uint64" => ::Array[::Integer | ::Float]?, + repeated_bool: ::Array[bool]?, + "repeated_bool" => ::Array[bool]?, + repeated_double: ::Array[::Float | ::Integer]?, + "repeated_double" => ::Array[::Float | ::Integer]?, + repeated_float: ::Array[::Float | ::Integer]?, + "repeated_float" => ::Array[::Float | ::Integer]?, + repeated_string: ::Array[::String | ::Symbol]?, + "repeated_string" => ::Array[::String | ::Symbol]?, + repeated_bytes: ::Array[::String]?, + "repeated_bytes" => ::Array[::String]?, + repeated_enum: ::Array[::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]?, + "repeated_enum" => ::Array[::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]?, + repeated_msg: ::Array[::A::B::C::TestMessage]?, + "repeated_msg" => ::Array[::A::B::C::TestMessage]?, + required_int32: (::Integer | ::Float)?, + "required_int32" => (::Integer | ::Float)?, + required_int64: (::Integer | ::Float)?, + "required_int64" => (::Integer | ::Float)?, + required_uint32: (::Integer | ::Float)?, + "required_uint32" => (::Integer | ::Float)?, + required_uint64: (::Integer | ::Float)?, + "required_uint64" => (::Integer | ::Float)?, + required_bool: bool?, + "required_bool" => bool?, + required_double: (::Float | ::Integer)?, + "required_double" => (::Float | ::Integer)?, + required_float: (::Float | ::Integer)?, + "required_float" => (::Float | ::Integer)?, + required_string: (::String | ::Symbol)?, + "required_string" => (::String | ::Symbol)?, + required_bytes: ::String?, + "required_bytes" => ::String?, + required_enum: (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?, + "required_enum" => (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?, + required_msg: (::A::B::C::TestMessage | ::A::B::C::TestMessage::init_map)?, + "required_msg" => (::A::B::C::TestMessage | ::A::B::C::TestMessage::init_map)?, + oneof_int32: (::Integer | ::Float)?, + "oneof_int32" => (::Integer | ::Float)?, + oneof_int64: (::Integer | ::Float)?, + "oneof_int64" => (::Integer | ::Float)?, + oneof_uint32: (::Integer | ::Float)?, + "oneof_uint32" => (::Integer | ::Float)?, + oneof_uint64: (::Integer | ::Float)?, + "oneof_uint64" => (::Integer | ::Float)?, + oneof_bool: bool?, + "oneof_bool" => bool?, + oneof_double: (::Float | ::Integer)?, + "oneof_double" => (::Float | ::Integer)?, + oneof_float: (::Float | ::Integer)?, + "oneof_float" => (::Float | ::Integer)?, + oneof_string: (::String | ::Symbol)?, + "oneof_string" => (::String | ::Symbol)?, + oneof_bytes: ::String?, + "oneof_bytes" => ::String?, + oneof_enum: (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?, + "oneof_enum" => (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?, + oneof_msg: (::A::B::C::TestMessage | ::A::B::C::TestMessage::init_map)?, + "oneof_msg" => (::A::B::C::TestMessage | ::A::B::C::TestMessage::init_map)?, + nested_message: (::A::B::C::TestMessage::NestedMessage | ::A::B::C::TestMessage::NestedMessage::init_map)?, + "nested_message" => (::A::B::C::TestMessage::NestedMessage | ::A::B::C::TestMessage::NestedMessage::init_map)?, + } + + def initialize: (?init_map initial_value) -> void + + def []: + ("optional_int32" name) -> ::Integer + | ("optional_int64" name) -> ::Integer + | ("optional_uint32" name) -> ::Integer + | ("optional_uint64" name) -> ::Integer + | ("optional_bool" name) -> bool + | ("optional_double" name) -> ::Float + | ("optional_float" name) -> ::Float + | ("optional_string" name) -> ::String + | ("optional_bytes" name) -> ::String + | ("optional_enum" name) -> (::A::B::C::TestEnum::names | ::Integer) + | ("optional_msg" name) -> ::A::B::C::TestMessage? + | ("optional_proto2_submessage" name) -> ::A::B::C::TestImportedMessage? + | ("repeated_int32" name) -> (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) + | ("repeated_int64" name) -> (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) + | ("repeated_uint32" name) -> (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) + | ("repeated_uint64" name) -> (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) + | ("repeated_bool" name) -> ::Google::Protobuf::RepeatedField[bool, bool] + | ("repeated_double" name) -> (::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer]) + | ("repeated_float" name) -> (::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer]) + | ("repeated_string" name) -> (::Google::Protobuf::RepeatedField[::String, ::String | ::Symbol]) + | ("repeated_bytes" name) -> ::Google::Protobuf::RepeatedField[::String, ::String] + | ("repeated_enum" name) -> (::Google::Protobuf::RepeatedField[::A::B::C::TestEnum::names | ::Integer, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]) + | ("repeated_msg" name) -> ::Google::Protobuf::RepeatedField[::A::B::C::TestMessage, ::A::B::C::TestMessage] + | ("required_int32" name) -> ::Integer + | ("required_int64" name) -> ::Integer + | ("required_uint32" name) -> ::Integer + | ("required_uint64" name) -> ::Integer + | ("required_bool" name) -> bool + | ("required_double" name) -> ::Float + | ("required_float" name) -> ::Float + | ("required_string" name) -> ::String + | ("required_bytes" name) -> ::String + | ("required_enum" name) -> (::A::B::C::TestEnum::names | ::Integer) + | ("required_msg" name) -> ::A::B::C::TestMessage? + | ("oneof_int32" name) -> ::Integer + | ("oneof_int64" name) -> ::Integer + | ("oneof_uint32" name) -> ::Integer + | ("oneof_uint64" name) -> ::Integer + | ("oneof_bool" name) -> bool + | ("oneof_double" name) -> ::Float + | ("oneof_float" name) -> ::Float + | ("oneof_string" name) -> ::String + | ("oneof_bytes" name) -> ::String + | ("oneof_enum" name) -> (::A::B::C::TestEnum::names | ::Integer) + | ("oneof_msg" name) -> ::A::B::C::TestMessage? + | ("nested_message" name) -> ::A::B::C::TestMessage::NestedMessage? + + def []=: + ("optional_int32" name, (::Integer | ::Float) value) -> void + | ("optional_int64" name, (::Integer | ::Float) value) -> void + | ("optional_uint32" name, (::Integer | ::Float) value) -> void + | ("optional_uint64" name, (::Integer | ::Float) value) -> void + | ("optional_bool" name, bool value) -> void + | ("optional_double" name, (::Float | ::Integer) value) -> void + | ("optional_float" name, (::Float | ::Integer) value) -> void + | ("optional_string" name, (::String | ::Symbol) value) -> void + | ("optional_bytes" name, ::String value) -> void + | ("optional_enum" name, (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float) value) -> void + | ("optional_msg" name, ::A::B::C::TestMessage? value) -> void + | ("optional_proto2_submessage" name, ::A::B::C::TestImportedMessage? value) -> void + | ("repeated_int32" name, (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) value) -> void + | ("repeated_int64" name, (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) value) -> void + | ("repeated_uint32" name, (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) value) -> void + | ("repeated_uint64" name, (::Google::Protobuf::RepeatedField[::Integer, ::Integer | ::Float]) value) -> void + | ("repeated_bool" name, ::Google::Protobuf::RepeatedField[bool, bool] value) -> void + | ("repeated_double" name, (::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer]) value) -> void + | ("repeated_float" name, (::Google::Protobuf::RepeatedField[::Float, ::Float | ::Integer]) value) -> void + | ("repeated_string" name, (::Google::Protobuf::RepeatedField[::String, ::String | ::Symbol]) value) -> void + | ("repeated_bytes" name, ::Google::Protobuf::RepeatedField[::String, ::String] value) -> void + | ("repeated_enum" name, (::Google::Protobuf::RepeatedField[::A::B::C::TestEnum::names | ::Integer, ::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float]) value) -> void + | ("repeated_msg" name, ::Google::Protobuf::RepeatedField[::A::B::C::TestMessage, ::A::B::C::TestMessage] value) -> void + | ("required_int32" name, (::Integer | ::Float) value) -> void + | ("required_int64" name, (::Integer | ::Float) value) -> void + | ("required_uint32" name, (::Integer | ::Float) value) -> void + | ("required_uint64" name, (::Integer | ::Float) value) -> void + | ("required_bool" name, bool value) -> void + | ("required_double" name, (::Float | ::Integer) value) -> void + | ("required_float" name, (::Float | ::Integer) value) -> void + | ("required_string" name, (::String | ::Symbol) value) -> void + | ("required_bytes" name, ::String value) -> void + | ("required_enum" name, (::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float) value) -> void + | ("required_msg" name, ::A::B::C::TestMessage? value) -> void + | ("oneof_int32" name, ((::Integer | ::Float)?) value) -> void + | ("oneof_int64" name, ((::Integer | ::Float)?) value) -> void + | ("oneof_uint32" name, ((::Integer | ::Float)?) value) -> void + | ("oneof_uint64" name, ((::Integer | ::Float)?) value) -> void + | ("oneof_bool" name, bool? value) -> void + | ("oneof_double" name, ((::Float | ::Integer)?) value) -> void + | ("oneof_float" name, ((::Float | ::Integer)?) value) -> void + | ("oneof_string" name, ((::String | ::Symbol)?) value) -> void + | ("oneof_bytes" name, ::String? value) -> void + | ("oneof_enum" name, ((::A::B::C::TestEnum::names | ::A::B::C::TestEnum::strings | ::Integer | ::Float)?) value) -> void + | ("oneof_msg" name, ::A::B::C::TestMessage? value) -> void + | ("nested_message" name, ::A::B::C::TestMessage::NestedMessage? value) -> void + end + + module TestEnum + extend ::Google::Protobuf::_EnumModule + + Default: 0 + + A: 1 + + B: 2 + + C: 3 + + def self.lookup: + (0 number) -> :Default + | (1 number) -> :A + | (2 number) -> :B + | (3 number) -> :C + | (::int number) -> names? + | ... + + def self.resolve: + (:Default name) -> 0 + | (:A name) -> 1 + | (:B name) -> 2 + | (:C name) -> 3 + | (::Symbol name) -> numbers? + | ... + + type names = :Default | :A | :B | :C + + type strings = "Default" | "A" | "B" | "C" + + type numbers = 0 | 1 | 2 | 3 + end + end + end +end + +module Google + module Protobuf + class DescriptorPool + def lookup: + ("A.B.C.TestMessage" name) -> (::Google::Protobuf::Descriptor & ::Google::Protobuf::_SpecificDescriptor[singleton(::A::B::C::TestMessage)]) + | ("A.B.C.TestMessage.NestedMessage" name) -> (::Google::Protobuf::Descriptor & ::Google::Protobuf::_SpecificDescriptor[singleton(::A::B::C::TestMessage::NestedMessage)]) + | ("A.B.C.TestEnum" name) -> (::Google::Protobuf::EnumDescriptor & ::Google::Protobuf::_SpecificEnumDescriptor[singleton(::A::B::C::TestEnum)]) + | ... + end + end +end diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_pkg_explicit_legacy_pb.rbs b/src/google/protobuf/compiler/ruby/ruby_generated_pkg_explicit_legacy_pb.rbs new file mode 100644 index 0000000000000..6e424980e3ebe --- /dev/null +++ b/src/google/protobuf/compiler/ruby/ruby_generated_pkg_explicit_legacy_pb.rbs @@ -0,0 +1,43 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# This RBS interface is provided for convenience, on a best-effort basis. +# The library is the definitive source for the API contract; if the RBS file +# and the library's behavior differ, the library behavior is authoritative. +# We welcome fixes to change the RBS file to match. +# source: ruby_generated_pkg_explicit_legacy.proto + +module AA + module BB + module CC + class Four < ::Google::Protobuf::AbstractMessage + include ::Google::Protobuf::_MessageClass[::AA::BB::CC::Four] + + attr_reader another_string(): ::String + attr_writer another_string(): ::String | ::Symbol + def clear_another_string: () -> void + + type init_map = { + another_string: (::String | ::Symbol)?, + "another_string" => (::String | ::Symbol)?, + } + + def initialize: (?init_map initial_value) -> void + + def []: + ("another_string" name) -> ::String + + def []=: + ("another_string" name, (::String | ::Symbol) value) -> void + end + end + end +end + +module Google + module Protobuf + class DescriptorPool + def lookup: + ("one.two.a_three.and.Four" name) -> (::Google::Protobuf::Descriptor & ::Google::Protobuf::_SpecificDescriptor[singleton(::AA::BB::CC::Four)]) + | ... + end + end +end diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_pkg_explicit_pb.rbs b/src/google/protobuf/compiler/ruby/ruby_generated_pkg_explicit_pb.rbs new file mode 100644 index 0000000000000..ba6056177f88e --- /dev/null +++ b/src/google/protobuf/compiler/ruby/ruby_generated_pkg_explicit_pb.rbs @@ -0,0 +1,43 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# This RBS interface is provided for convenience, on a best-effort basis. +# The library is the definitive source for the API contract; if the RBS file +# and the library's behavior differ, the library behavior is authoritative. +# We welcome fixes to change the RBS file to match. +# source: ruby_generated_pkg_explicit.proto + +module A + module B + module C + class Four < ::Google::Protobuf::AbstractMessage + include ::Google::Protobuf::_MessageClass[::A::B::C::Four] + + attr_reader a_string(): ::String + attr_writer a_string(): ::String | ::Symbol + def clear_a_string: () -> void + + type init_map = { + a_string: (::String | ::Symbol)?, + "a_string" => (::String | ::Symbol)?, + } + + def initialize: (?init_map initial_value) -> void + + def []: + ("a_string" name) -> ::String + + def []=: + ("a_string" name, (::String | ::Symbol) value) -> void + end + end + end +end + +module Google + module Protobuf + class DescriptorPool + def lookup: + ("one.two.a_three.Four" name) -> (::Google::Protobuf::Descriptor & ::Google::Protobuf::_SpecificDescriptor[singleton(::A::B::C::Four)]) + | ... + end + end +end diff --git a/src/google/protobuf/compiler/ruby/ruby_generated_pkg_implicit_pb.rbs b/src/google/protobuf/compiler/ruby/ruby_generated_pkg_implicit_pb.rbs new file mode 100644 index 0000000000000..53e84cf896041 --- /dev/null +++ b/src/google/protobuf/compiler/ruby/ruby_generated_pkg_implicit_pb.rbs @@ -0,0 +1,43 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# This RBS interface is provided for convenience, on a best-effort basis. +# The library is the definitive source for the API contract; if the RBS file +# and the library's behavior differ, the library behavior is authoritative. +# We welcome fixes to change the RBS file to match. +# source: ruby_generated_pkg_implicit.proto + +module One + module Two + module AThree + class Four < ::Google::Protobuf::AbstractMessage + include ::Google::Protobuf::_MessageClass[::One::Two::AThree::Four] + + attr_reader a_string(): ::String + attr_writer a_string(): ::String | ::Symbol + def clear_a_string: () -> void + + type init_map = { + a_string: (::String | ::Symbol)?, + "a_string" => (::String | ::Symbol)?, + } + + def initialize: (?init_map initial_value) -> void + + def []: + ("a_string" name) -> ::String + + def []=: + ("a_string" name, (::String | ::Symbol) value) -> void + end + end + end +end + +module Google + module Protobuf + class DescriptorPool + def lookup: + ("one.two.a_three.Four" name) -> (::Google::Protobuf::Descriptor & ::Google::Protobuf::_SpecificDescriptor[singleton(::One::Two::AThree::Four)]) + | ... + end + end +end diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.h b/src/google/protobuf/compiler/ruby/ruby_generator.h index 6822f5a6011bf..ed7138eb9d247 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generator.h +++ b/src/google/protobuf/compiler/ruby/ruby_generator.h @@ -21,6 +21,12 @@ namespace protobuf { namespace compiler { namespace ruby { +std::string GetRequireName(absl::string_view proto_file); +std::string PackageToModule(absl::string_view name); +std::string RubifyConstant(absl::string_view name); +int GeneratePackageModules(const FileDescriptor* file, io::Printer* printer); +void EndPackageModules(int levels, io::Printer* printer); + // CodeGenerator implementation for generated Ruby protocol buffer classes. // If you create your own protocol compiler binary and you want it to support // Ruby output, you can do so by registering an instance of this