pw_protobuf: Allow self-referencing protos
Fixes codegen to support proto messages that reference themselves in
their definition.
Change-Id: I0d9429d3568f088c22ac715e2af2030fab1755b3
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/42020
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Armando Montanez <amontanez@google.com>
diff --git a/pw_protobuf/codegen_test.cc b/pw_protobuf/codegen_test.cc
index faceb51..b62ce14 100644
--- a/pw_protobuf/codegen_test.cc
+++ b/pw_protobuf/codegen_test.cc
@@ -174,6 +174,53 @@
0);
}
+TEST(Codegen, RecursiveSubmessage) {
+ std::byte encode_buffer[512];
+ NestedEncoder<20, 20> encoder(encode_buffer);
+
+ Crate::Encoder biggest_crate(&encoder);
+ biggest_crate.WriteName("Huge crate");
+
+ {
+ Crate::Encoder medium_crate = biggest_crate.GetSmallerCratesEncoder();
+ medium_crate.WriteName("Medium crate");
+ {
+ Crate::Encoder small_crate = medium_crate.GetSmallerCratesEncoder();
+ small_crate.WriteName("Small crate");
+ }
+ {
+ Crate::Encoder tiny_crate = medium_crate.GetSmallerCratesEncoder();
+ tiny_crate.WriteName("Tiny crate");
+ }
+ }
+
+ // clang-format off
+ constexpr uint8_t expected_proto[] = {
+ // crate.name
+ 0x0a, 0x0a, 'H', 'u', 'g', 'e', ' ', 'c', 'r', 'a', 't', 'e',
+ // crate.smaller_crate[0]
+ 0x12, 0x2b,
+ // crate.smaller_crate[0].name
+ 0x0a, 0x0c, 'M', 'e', 'd', 'i', 'u', 'm', ' ', 'c', 'r', 'a', 't', 'e',
+ // crate.smaller_crate[0].smaller_crate[0]
+ 0x12, 0x0d,
+ // crate.smaller_crate[0].smaller_crate[0].name
+ 0x0a, 0x0b, 'S', 'm', 'a', 'l', 'l', ' ', 'c', 'r', 'a', 't', 'e',
+ // crate.smaller_crate[0].smaller_crate[1]
+ 0x12, 0x0c,
+ // crate.smaller_crate[0].smaller_crate[1].name
+ 0x0a, 0x0a, 'T', 'i', 'n', 'y', ' ', 'c', 'r', 'a', 't', 'e',
+ };
+ // clang-format on
+
+ Result result = encoder.Encode();
+ ASSERT_EQ(result.status(), OkStatus());
+ EXPECT_EQ(result.value().size(), sizeof(expected_proto));
+ EXPECT_EQ(std::memcmp(
+ result.value().data(), expected_proto, sizeof(expected_proto)),
+ 0);
+}
+
TEST(CodegenRepeated, NonPackedScalar) {
std::byte encode_buffer[32];
NestedEncoder encoder(encode_buffer);
diff --git a/pw_protobuf/pw_protobuf_test_protos/full_test.proto b/pw_protobuf/pw_protobuf_test_protos/full_test.proto
index 7262d32..b8a80fc 100644
--- a/pw_protobuf/pw_protobuf_test_protos/full_test.proto
+++ b/pw_protobuf/pw_protobuf_test_protos/full_test.proto
@@ -114,6 +114,12 @@
repeated KeyValuePair attributes = 4;
}
+// Ensure recursive submessages work.
+message Crate {
+ string name = 1;
+ repeated Crate smaller_crates = 2;
+}
+
// This might be useful.
message KeyValuePair {
string key = 1;
diff --git a/pw_protobuf/py/pw_protobuf/codegen_pwpb.py b/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
index faf6bb9..e0b9293 100644
--- a/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
+++ b/pw_protobuf/py/pw_protobuf/codegen_pwpb.py
@@ -117,9 +117,15 @@
scope = self._root if from_root else self._scope
type_node = self._field.type_node()
assert type_node is not None
+
+ # If a class method is referencing its class, the namespace provided
+ # must be from the root or it will be empty.
+ if type_node == scope:
+ scope = self._root
+
ancestor = scope.common_ancestor(type_node)
namespace = type_node.cpp_namespace(ancestor)
- assert namespace is not None
+ assert namespace
return namespace