C++ IR: Add access specifiers to fields and populate in AstVisitor. The access specifiers aren't being serialized to JSON yet, so no need to change the JSON side (that will happen in a followup CL). PiperOrigin-RevId: 397254542
diff --git a/rs_bindings_from_cc/ast_visitor.cc b/rs_bindings_from_cc/ast_visitor.cc index 56640b5..f9d9bf6 100644 --- a/rs_bindings_from_cc/ast_visitor.cc +++ b/rs_bindings_from_cc/ast_visitor.cc
@@ -14,8 +14,10 @@ #include "third_party/absl/strings/string_view.h" #include "third_party/llvm/llvm-project/clang/include/clang/AST/ASTContext.h" #include "third_party/llvm/llvm-project/clang/include/clang/AST/Decl.h" +#include "third_party/llvm/llvm-project/clang/include/clang/AST/DeclCXX.h" #include "third_party/llvm/llvm-project/clang/include/clang/AST/Mangle.h" #include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h" +#include "third_party/llvm/llvm-project/clang/include/clang/Basic/Specifiers.h" #include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Casting.h" namespace rs_bindings_from_cc { @@ -54,11 +56,40 @@ return true; } +static AccessSpecifier TranslateAccessSpecifier(clang::AccessSpecifier access) { + switch (access) { + case clang::AS_public: + return kPublic; + case clang::AS_protected: + return kProtected; + case clang::AS_private: + return kPrivate; + case clang::AS_none: + // We should never be encoding a "none" access specifier in IR. + assert(false); + // We have to return something. Conservatively return private so we don't + // inadvertently make a private member variable accessible in Rust. + return kPrivate; + } +} + bool AstVisitor::VisitRecordDecl(clang::RecordDecl* record_decl) { std::vector<Field> fields; + clang::AccessSpecifier default_access = clang::AS_public; + if (const auto* cxx_record_decl = + clang::dyn_cast<clang::CXXRecordDecl>(record_decl)) { + if (cxx_record_decl->isClass()) { + default_access = clang::AS_private; + } + } for (const clang::FieldDecl* field_decl : record_decl->fields()) { + clang::AccessSpecifier access = field_decl->getAccess(); + if (access == clang::AS_none) { + access = default_access; + } fields.push_back({.identifier = GetTranslatedName(field_decl), - .type = ConvertType(field_decl->getType())}); + .type = ConvertType(field_decl->getType()), + .access = TranslateAccessSpecifier(access)}); } ir_.records.push_back({GetTranslatedName(record_decl), std::move(fields)}); return true;
diff --git a/rs_bindings_from_cc/ast_visitor_test.cc b/rs_bindings_from_cc/ast_visitor_test.cc index 12a8970..2f23bd9 100644 --- a/rs_bindings_from_cc/ast_visitor_test.cc +++ b/rs_bindings_from_cc/ast_visitor_test.cc
@@ -191,5 +191,49 @@ EXPECT_EQ(second.type.rs_name, "i32"); } +TEST(AstVisitorTest, MemberVariableAccessSpecifiers) { + IR ir = ImportCode({R"( + struct SomeStruct { + int default_access_int; + public: + int public_int; + protected: + int protected_int; + private: + int private_int; + }; + + class SomeClass { + int default_access_int; + }; + )"}, + {}); + EXPECT_THAT(ir.functions, SizeIs(0)); + ASSERT_THAT(ir.records, SizeIs(2)); + + Record some_struct = ir.records[0]; + EXPECT_EQ(some_struct.Ident().Ident(), "SomeStruct"); + ASSERT_THAT(some_struct.Fields(), SizeIs(4)); + Field field0 = some_struct.Fields()[0]; + EXPECT_EQ(field0.identifier.Ident(), "default_access_int"); + EXPECT_EQ(field0.access, kPublic); + Field field1 = some_struct.Fields()[1]; + EXPECT_EQ(field1.identifier.Ident(), "public_int"); + EXPECT_EQ(field1.access, kPublic); + Field field2 = some_struct.Fields()[2]; + EXPECT_EQ(field2.identifier.Ident(), "protected_int"); + EXPECT_EQ(field2.access, kProtected); + Field field3 = some_struct.Fields()[3]; + EXPECT_EQ(field3.identifier.Ident(), "private_int"); + EXPECT_EQ(field3.access, kPrivate); + + Record some_class = ir.records[1]; + EXPECT_EQ(some_class.Ident().Ident(), "SomeClass"); + ASSERT_THAT(some_class.Fields(), SizeIs(1)); + field0 = some_class.Fields()[0]; + EXPECT_EQ(field0.identifier.Ident(), "default_access_int"); + EXPECT_EQ(field0.access, kPrivate); +} + } // namespace } // namespace rs_bindings_from_cc
diff --git a/rs_bindings_from_cc/ir.h b/rs_bindings_from_cc/ir.h index fe958d0..89d05c3 100644 --- a/rs_bindings_from_cc/ir.h +++ b/rs_bindings_from_cc/ir.h
@@ -118,12 +118,20 @@ bool is_inline; }; +// Access specifier for a member or base class. +enum AccessSpecifier { + kPublic, + kProtected, + kPrivate, +}; + // A field (non-static member variable) of a record. struct Field { nlohmann::json ToJson() const; Identifier identifier; Type type; + AccessSpecifier access; }; // A record (struct, class, union).