Make items that stem from decl context have a list of children item ids and generate code recursively
After this cl:
* We use a new method `Importer::GetItemIdsInSourceOrder()` to obtain a list of their children items (including comments and unsupported items, excluding children `decl`s whose `ImportDecl()` returned `std::nullopt`, as they don't have a corresponding item.)
* We generate source for items recursively, starting from `flat_ir.top_level_item_ids`.
* This required some changes to `generate_record()`, as it used to not be responsible for generating (unsupported) items for its own children, and now it is.
* Comments: We store all the comments, so we can find the relevant comments for each decl context.
* We now generate free comments that appear within a `Record`; however, comments between fields now appear out of place, after the struct definition. I will address this in a followup cl.
PiperOrigin-RevId: 440906345
diff --git a/rs_bindings_from_cc/importer_test.cc b/rs_bindings_from_cc/importer_test.cc
index 9541618..808f513 100644
--- a/rs_bindings_from_cc/importer_test.cc
+++ b/rs_bindings_from_cc/importer_test.cc
@@ -24,12 +24,14 @@
using ::testing::AllOf;
using ::testing::AnyOf;
+using ::testing::Contains;
using ::testing::Each;
using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::Not;
using ::testing::Pointee;
using ::testing::SizeIs;
+using ::testing::UnorderedElementsAre;
using ::testing::VariantWith;
using ::testing::status::StatusIs;
@@ -42,6 +44,23 @@
return std::nullopt;
}
+std::optional<IR::Item> FindItemById(const IR& ir, ItemId id) {
+ for (auto item : ir.items) {
+ if (auto* record = std::get_if<Record>(&item); record && record->id == id) {
+ return item;
+ } else if (auto* func = std::get_if<Func>(&item); func && func->id == id) {
+ return item;
+ } else if (auto* comment = std::get_if<Comment>(&item);
+ comment && comment->id == id) {
+ return item;
+ } else if (auto* unsupported = std::get_if<UnsupportedItem>(&item);
+ unsupported && unsupported->id == id) {
+ return item;
+ }
+ }
+ return std::nullopt;
+}
+
template <typename T>
UnqualifiedIdentifier GetName(const T& x) {
return x.identifier;
@@ -111,6 +130,14 @@
return false;
}
+// Matches text for comments.
+MATCHER_P(TextIs, text, "") {
+ if (arg.text == text) return true;
+
+ *result_listener << "actual text: '" << arg.text << "'";
+ return false;
+}
+
// Matches an RsType or CcType that has the given decl_id.
MATCHER_P(DeclIdIs, decl_id, "") {
if (arg.decl_id.hasValue() && *arg.decl_id == decl_id) return true;
@@ -301,7 +328,7 @@
TEST(ImporterTest, FuncWithVoidReturnType) {
ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("void Foo();"));
EXPECT_THAT(ItemsWithoutBuiltins(ir),
- ElementsAre(VariantWith<Func>(
+ UnorderedElementsAre(VariantWith<Func>(
AllOf(IdentifierIs("Foo"), MangledNameIs("_Z3Foov"),
ReturnType(IsVoid()), ParamsAre()))));
}
@@ -310,7 +337,7 @@
ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("void Foo(); void Bar();"));
EXPECT_THAT(
ItemsWithoutBuiltins(ir),
- ElementsAre(
+ UnorderedElementsAre(
VariantWith<Func>(AllOf(IdentifierIs("Foo"), MangledNameIs("_Z3Foov"),
ReturnType(IsVoid()), ParamsAre())),
VariantWith<Func>(AllOf(IdentifierIs("Bar"), MangledNameIs("_Z3Barv"),
@@ -331,35 +358,36 @@
BazelLabel{"//two_funcs:one_target"}},
}));
EXPECT_THAT(ItemsWithoutBuiltins(ir),
- ElementsAre(VariantWith<Func>(IdentifierIs("Foo")),
- VariantWith<Func>(IdentifierIs("Bar"))));
+ UnorderedElementsAre(VariantWith<Func>(IdentifierIs("Foo")),
+ VariantWith<Func>(IdentifierIs("Bar"))));
}
TEST(ImporterTest, NonInlineFunc) {
ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("void Foo() {}"));
EXPECT_THAT(ItemsWithoutBuiltins(ir),
- ElementsAre(VariantWith<Func>(
+ UnorderedElementsAre(VariantWith<Func>(
AllOf(IdentifierIs("Foo"), Not(IsInline())))));
}
TEST(ImporterTest, InlineFunc) {
ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("inline void Foo() {}"));
- EXPECT_THAT(
- ItemsWithoutBuiltins(ir),
- ElementsAre(VariantWith<Func>(AllOf(IdentifierIs("Foo"), IsInline()))));
+ EXPECT_THAT(ItemsWithoutBuiltins(ir),
+ UnorderedElementsAre(
+ VariantWith<Func>(AllOf(IdentifierIs("Foo"), IsInline()))));
}
TEST(ImporterTest, FuncJustOnce) {
ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("void Foo(); void Foo();"));
- EXPECT_THAT(ItemsWithoutBuiltins(ir),
- ElementsAre(VariantWith<Func>(AllOf(IdentifierIs("Foo")))));
+ EXPECT_THAT(
+ ItemsWithoutBuiltins(ir),
+ UnorderedElementsAre(VariantWith<Func>(AllOf(IdentifierIs("Foo")))));
}
TEST(ImporterTest, TestImportPointerFunc) {
ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("int* Foo(int* a);"));
EXPECT_THAT(ItemsWithoutBuiltins(ir),
- ElementsAre(VariantWith<Func>(AllOf(
+ UnorderedElementsAre(VariantWith<Func>(AllOf(
ReturnType(IsIntPtr()), ParamsAre(ParamType(IsIntPtr()))))));
}
@@ -383,7 +411,7 @@
ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc("int& Foo(int& a);"));
EXPECT_THAT(ItemsWithoutBuiltins(ir),
- ElementsAre(VariantWith<Func>(AllOf(
+ UnorderedElementsAre(VariantWith<Func>(AllOf(
ReturnType(IsIntRef()), ParamsAre(ParamType(IsIntRef()))))));
}
@@ -776,5 +804,73 @@
EXPECT_THAT(records, Each(Pointee(Not(IsTrivialAbi()))));
}
+TEST(ImporterTest, TopLevelItemIds) {
+ absl::string_view file = R"cc(
+ struct TopLevelStruct {};
+ // Top level comment
+
+ // Function comment
+ void top_level_func();
+ namespace top_level_namespace {
+ struct Nested {};
+ // free nested comment
+
+ // nested_func comment
+ void nested_func();
+ } // namespace top_level_namespace
+ )cc";
+ ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc(file));
+
+ std::vector<IR::Item> items;
+ for (const auto& id : ir.top_level_item_ids) {
+ auto item = FindItemById(ir, id);
+ ASSERT_TRUE(item.has_value());
+ items.push_back(*item);
+ }
+
+ EXPECT_THAT(ir.top_level_item_ids, SizeIs(5));
+ EXPECT_THAT(
+ items,
+ ElementsAre(
+ VariantWith<Record>(RsNameIs("TopLevelStruct")),
+ VariantWith<Comment>(TextIs("Top level comment")),
+ VariantWith<Func>(IdentifierIs("top_level_func")),
+ VariantWith<UnsupportedItem>(NameIs("top_level_namespace")),
+ VariantWith<Comment>(TextIs("namespace top_level_namespace"))));
+}
+
+TEST(ImporterTest, RecordItemIds) {
+ absl::string_view file = R"cc(
+ struct TopLevelStruct {
+ // A free comment
+
+ // foo comment
+ int foo;
+
+ int bar();
+ struct Nested {};
+ int baz();
+ };
+ )cc";
+ ASSERT_OK_AND_ASSIGN(IR ir, IrFromCc(file));
+
+ std::vector<const Record*> records = ir.get_items_if<Record>();
+ EXPECT_THAT(records, SizeIs(1));
+
+ std::vector<IR::Item> items;
+ for (const auto& id : records[0]->child_item_ids) {
+ auto item = FindItemById(ir, id);
+ ASSERT_TRUE(item.has_value());
+ items.push_back(*item);
+ }
+
+ EXPECT_THAT(items,
+ AllOf(Contains(VariantWith<Comment>(TextIs("A free comment"))),
+ Contains(VariantWith<Func>(IdentifierIs("bar"))),
+ Contains(VariantWith<UnsupportedItem>(
+ NameIs("TopLevelStruct::Nested"))),
+ Contains(VariantWith<Func>(IdentifierIs("baz")))));
+}
+
} // namespace
} // namespace crubit