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