diff --git a/rust/test/nested.proto b/rust/test/nested.proto index 1b5b149e0c..80b0c615aa 100644 --- a/rust/test/nested.proto +++ b/rust/test/nested.proto @@ -13,6 +13,18 @@ message Outer { message Inner { optional int32 num = 1; optional bool boolean = 2; + + message SuperInner { + message DuperInner { + message EvenMoreInner { + message CantBelieveItsSoInner { + optional int32 num = 99; + } + } + } + } } optional Inner inner = 1; + optional .nest.Outer.Inner.SuperInner.DuperInner.EvenMoreInner + .CantBelieveItsSoInner deep = 2; } diff --git a/rust/test/shared/simple_nested_test.rs b/rust/test/shared/simple_nested_test.rs index 2f9dad50b4..daa654c7da 100644 --- a/rust/test/shared/simple_nested_test.rs +++ b/rust/test/shared/simple_nested_test.rs @@ -13,3 +13,13 @@ fn test_simple_nested_proto() { assert_eq!(outer_msg.inner().num(), 0); assert!(!outer_msg.inner().boolean()); } + +#[test] +fn test_deeply_nested_definition() { + let deep = nested_proto::nest::Outer_::Inner_::SuperInner_::DuperInner_::EvenMoreInner_ + ::CantBelieveItsSoInner::new(); + assert_eq!(deep.num(), 0); + + let outer_msg = Outer::new(); + assert_eq!(outer_msg.deep().num(), 0); +} diff --git a/src/google/protobuf/compiler/rust/naming.cc b/src/google/protobuf/compiler/rust/naming.cc index bb1c35a3e2..e9e24c40b0 100644 --- a/src/google/protobuf/compiler/rust/naming.cc +++ b/src/google/protobuf/compiler/rust/naming.cc @@ -8,13 +8,14 @@ #include "google/protobuf/compiler/rust/naming.h" #include +#include #include "absl/log/absl_log.h" -#include "absl/strings/match.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" +#include "absl/strings/str_split.h" #include "absl/strings/string_view.h" -#include "absl/strings/strip.h" #include "absl/strings/substitute.h" #include "google/protobuf/compiler/code_generator.h" #include "google/protobuf/compiler/rust/context.h" @@ -166,10 +167,46 @@ std::string PrimitiveRsTypeName(const FieldDescriptor& desc) { return ""; } +// Constructs a string of the Rust modules which will contain the message. +// +// Example: Given a message 'NestedMessage' which is defined in package 'x.y' +// which is inside 'ParentMessage', the message will be placed in the +// x::y::ParentMessage_ Rust module, so this function will return the string +// "x::y::ParentMessage_::". +// +// If the message has no package and no containing messages then this returns +// empty string. std::string RustModule(Context msg) { - absl::string_view package = msg.desc().file()->package(); - if (package.empty()) return ""; - return absl::StrCat("", absl::StrReplaceAll(package, {{".", "::"}})); + const Descriptor& desc = msg.desc(); + + std::vector modules; + + std::vector package_modules = + absl::StrSplit(desc.file()->package(), '.', absl::SkipEmpty()); + + modules.insert(modules.begin(), package_modules.begin(), + package_modules.end()); + + // Innermost to outermost order. + std::vector modules_from_containing_types; + const Descriptor* parent = desc.containing_type(); + while (parent != nullptr) { + modules_from_containing_types.push_back(absl::StrCat(parent->name(), "_")); + parent = parent->containing_type(); + } + + // Add the modules from containing messages (rbegin/rend to get them in outer + // to inner order). + modules.insert(modules.end(), modules_from_containing_types.rbegin(), + modules_from_containing_types.rend()); + + // If there is any modules at all, push an empty string on the end so that + // we get the trailing :: + if (!modules.empty()) { + modules.push_back(""); + } + + return absl::StrJoin(modules, "::"); } std::string RustInternalModuleName(Context file) { @@ -179,19 +216,7 @@ std::string RustInternalModuleName(Context file) { } std::string GetCrateRelativeQualifiedPath(Context msg) { - std::string name = msg.desc().full_name(); - if (msg.desc().file()->package().empty()) { - return name; - } - // when computing the relative path, we don't want the package name, so we - // strip that out - name = - std::string(absl::StripPrefix(name, msg.desc().file()->package() + ".")); - // proto nesting is marked with periods in .proto files -- this gets - // translated to delimiting via _:: in terra rust - absl::StrReplaceAll({{".", "_::"}}, &name); - - return absl::StrCat(RustModule(msg), "::", name); + return absl::StrCat(RustModule(msg), msg.desc().name()); } std::string FieldInfoComment(Context field) {