diff --git a/rs_bindings_from_cc/BUILD b/rs_bindings_from_cc/BUILD index 2e218776c..ebfb49552 100644 --- a/rs_bindings_from_cc/BUILD +++ b/rs_bindings_from_cc/BUILD @@ -271,6 +271,7 @@ cc_library( deps = [ "cc_ir", ":bazel_types", + ":ir_cc_proto", "//lifetime_annotations", "//lifetime_annotations:type_lifetimes", "@abseil-cpp//absl/container:flat_hash_map", @@ -323,6 +324,7 @@ cc_library( ":bazel_types", ":cc_ir", ":decl_importer", + ":ir_cc_proto", ":recording_diagnostic_consumer", ":type_map", "//common:annotation_reader", diff --git a/rs_bindings_from_cc/decl_importer.h b/rs_bindings_from_cc/decl_importer.h index 61b36e373..5e0e4384c 100644 --- a/rs_bindings_from_cc/decl_importer.h +++ b/rs_bindings_from_cc/decl_importer.h @@ -21,6 +21,7 @@ #include "lifetime_annotations/type_lifetimes.h" #include "rs_bindings_from_cc/bazel_types.h" #include "rs_bindings_from_cc/ir.h" +#include "rs_bindings_from_cc/ir.pb.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclTemplate.h" @@ -34,6 +35,8 @@ namespace crubit { +namespace ir_proto = rs_bindings_from_cc::ir_proto::flat; + // Top-level parameters as well as return value of an importer invocation. class Invocation { public: @@ -88,6 +91,7 @@ class Invocation { const std::optional> do_not_bind_allowlist_; // The main output of the import process + ir_proto::IRProto ir_proto_; IR ir_; // Transient map of top level items used to build the tree. @@ -180,9 +184,37 @@ class ImportContext { /*is_hard_error=*/false); } + virtual std::unique_ptr ImportUnsupportedItemToProto( + const clang::Decl& decl, std::optional path, + std::vector errors, bool is_hard_error) { + IR::Item legacy_item = ImportUnsupportedItem( + decl, std::move(path), std::move(errors), is_hard_error); + auto proto_item = std::make_unique(); + *proto_item = crubit::ToFlatProto(legacy_item); + return proto_item; + } + + std::unique_ptr ImportUnsupportedItemToProto( + const clang::Decl& decl, std::optional path, + std::vector errors) { + return ImportUnsupportedItemToProto(decl, std::move(path), + std::move(errors), + /*is_hard_error=*/false); + } + + std::unique_ptr HardErrorToProto(const clang::Decl& decl, + FormattedError error) { + return ImportUnsupportedItemToProto(decl, std::nullopt, {std::move(error)}, + /*is_hard_error=*/true); + } + // Imports a decl and creates an IR item (or error messages). This allows // importers to recursively delegate to other importers. // Does not use or update the cache. + // TODO(deprecate-cpp-ir): Add virtual absl::StatusOr + // ImportDeclToProto(clang::Decl* decl, bool must_bind) here (CL 1). + virtual absl::StatusOr> ImportDeclToProto( + clang::Decl* decl, bool must_bind) = 0; virtual std::optional ImportDecl(clang::Decl* decl) = 0; // Returns the Item of a Decl, importing it first if necessary. @@ -383,6 +415,19 @@ class DeclImporter { // be attempted, return UnsupportedItem. virtual std::optional ImportDecl(clang::Decl*, bool must_bind) = 0; + // Converts a decl to a proto IR item on the heap. Default + // implementation falls back to ImportDecl and converts via ToFlatProto. + virtual absl::StatusOr> ImportDeclToProto( + clang::Decl* decl, bool must_bind) { + std::optional legacy_item = ImportDecl(decl, must_bind); + if (!legacy_item.has_value()) { + return nullptr; + } + auto proto_item = std::make_unique(); + *proto_item = crubit::ToFlatProto(*legacy_item); + return proto_item; + } + protected: ImportContext& ictx_; }; @@ -402,7 +447,26 @@ class DeclImporterBase : public DeclImporter { must_bind_ = must_bind; return Import(typed_decl); } - virtual std::optional Import(D*) = 0; + // TODO(b/532184858): Remove Import once all importers are migrated. + virtual std::optional Import(D*) { return std::nullopt; } + + absl::StatusOr> ImportDeclToProto( + clang::Decl* decl, bool must_bind) override { + auto* typed_decl = clang::dyn_cast(decl); + if (typed_decl == nullptr) return nullptr; + must_bind_ = must_bind; + return ImportToProto(typed_decl); + } + // TODO(b/532184858): Remove ToFlatProto fallback once all importers override + // this function. + virtual absl::StatusOr> ImportToProto( + D* decl) { + std::optional legacy_item = Import(decl); + if (!legacy_item.has_value()) return nullptr; + auto proto_item = std::make_unique(); + *proto_item = crubit::ToFlatProto(*legacy_item); + return proto_item; + } // A property of the current decl being imported. // This is used to avoid re-parsing the annotation. diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc index 36e5c4ecd..1066506e5 100644 --- a/rs_bindings_from_cc/importer.cc +++ b/rs_bindings_from_cc/importer.cc @@ -87,6 +87,9 @@ #include "llvm/Support/raw_ostream.h" namespace crubit { + +namespace ir_proto = rs_bindings_from_cc::ir_proto::flat; + namespace { constexpr absl::string_view kTypeStatusPayloadUrl = @@ -636,8 +639,8 @@ ItemId Importer::GenerateItemId(const clang::Decl* decl) const { bool Importer::IsUnsupportedAndAlien(ItemId item_id) const { auto it = import_cache_.find(reinterpret_cast(item_id.value())); - return it != import_cache_.end() && it->second.has_value() && - std::holds_alternative(*it->second) && + return it != import_cache_.end() && it->second.legacy_item.has_value() && + std::holds_alternative(*it->second.legacy_item) && !IsFromCurrentTarget(it->first); } @@ -942,14 +945,15 @@ void Importer::Import(clang::TranslationUnitDecl* translation_unit_decl) { // class A { class B; }; // declares A::B // class A::B { ... }; // defines A::B std::vector> ordered_children; - for (const auto& [decl, item] : import_cache_) { - if (!item.has_value()) continue; + for (const auto& [decl, entry] : import_cache_) { + if (!entry.legacy_item.has_value()) continue; if (auto* parent_record_decl = llvm::dyn_cast(decl->getDeclContext())) { auto parent_it = import_cache_.find(parent_record_decl); - if (parent_it != import_cache_.end() && parent_it->second.has_value()) { + if (parent_it != import_cache_.end() && + parent_it->second.legacy_item.has_value()) { if (auto* parent_item = - std::get_if(&(parent_it->second.value()))) { + std::get_if(&(parent_it->second.legacy_item.value()))) { ordered_children.push_back({GetSourceOrderKey(decl), decl}); } } @@ -963,7 +967,8 @@ void Importer::Import(clang::TranslationUnitDecl* translation_unit_decl) { auto* parent_record_decl = llvm::dyn_cast(decl->getDeclContext()); auto parent_it = import_cache_.find(parent_record_decl); - auto* parent_item = std::get_if(&(parent_it->second.value())); + auto* parent_item = + std::get_if(&(parent_it->second.legacy_item.value())); auto child_id = GenerateItemId(decl); auto& parent_child_ids = invocation_.child_item_ids_[parent_item->id]; @@ -973,11 +978,12 @@ void Importer::Import(clang::TranslationUnitDecl* translation_unit_decl) { } } - for (const auto& [decl, item] : import_cache_) { - if (!item.has_value() || IsUnsupportedAndAlien(GenerateItemId(decl))) { + for (const auto& [decl, entry] : import_cache_) { + if (!entry.legacy_item.has_value() || + IsUnsupportedAndAlien(GenerateItemId(decl))) { continue; } - ordered_items.push_back({GetSourceOrderKey(decl), *item}); + ordered_items.push_back({GetSourceOrderKey(decl), *entry.legacy_item}); } llvm::stable_sort(ordered_items, SourceLocationComparator(sm)); @@ -1018,7 +1024,10 @@ void Importer::ImportDeclsFromDeclContext( std::optional Importer::GetDeclItem(clang::Decl* decl) { if (auto it = import_cache_.find(decl); it != import_cache_.end()) { - return it->second; + if (it->second.status == ItemCacheEntry::Status::kInProgress) { + return std::nullopt; + } + return it->second.legacy_item; } // Here, we need to be careful. Recursive imports break cycles as follows: // an item which may, in the process of being imported, then import itself, @@ -1054,8 +1063,14 @@ std::optional Importer::GetDeclItem(clang::Decl* decl) { // Note: insert_or_assign, not insert, in case a record, so as to overwrite // any null entries introduced by cycles. - std::optional result = ImportDecl(decl); - auto [it, inserted] = import_cache_.try_emplace(decl, result); + ItemId id = GenerateItemId(decl); + auto [it, inserted] = import_cache_.try_emplace( + decl, ItemCacheEntry{ + .status = ItemCacheEntry::Status::kInProgress, + .id = id, + .legacy_item = std::nullopt, + .proto_item = nullptr, + }); if (!inserted) { // TODO(jeanpierreda): Fix and promote to CHECK. // At least one cycle occurs with Typedef, where a typedef will import @@ -1066,14 +1081,31 @@ std::optional Importer::GetDeclItem(clang::Decl* decl) { // // Alternatively, maybe it's sufficient to check that they're _equal_. // It's not a bug at all to import it twice if it has no effect. - LOG_IF(INFO, !it->second.has_value()) + LOG_IF(INFO, !it->second.legacy_item.has_value()) << "re-entrant import discovered, where the re-entrant import had a " "non-null value." - << "\n trying to import a " << decl->getDeclKindName() - << "\n present entry: " << ItemToString(it->second) - << "\n was going to be inserted: " << ItemToString(result); - it->second = result; + << "\n trying to import a " << decl->getDeclKindName(); + } + + std::optional result = ImportDecl(decl); + + std::unique_ptr proto_item = nullptr; + if (result.has_value()) { + proto_item = std::make_unique(); + *proto_item = crubit::ToFlatProto(*result); } + + ItemCacheEntry::Status entry_status = result.has_value() + ? ItemCacheEntry::Status::kCompleted + : ItemCacheEntry::Status::kFailed; + + import_cache_[decl] = ItemCacheEntry{ + .status = entry_status, + .id = id, + .legacy_item = result, + .proto_item = std::move(proto_item), + }; + if (auto* record_decl = clang::dyn_cast(decl)) { // TODO(forster): Should we even visit the nested decl if we couldn't // import the parent? For now we have tests that check that we generate @@ -1136,9 +1168,10 @@ std::optional Importer::ImportDecl(clang::Decl* decl) { if (IsTransitivelyInPrivate(decl)) { if (*must_bind) { - return HardError(*decl, - FormattedError::Static( - "Private declarations cannot receive bindings")); + return HardError( + *decl, + FormattedError::Static("Items in private sections or classes are not " + "supported, but marked with must_bind")); } return std::nullopt; } @@ -1202,11 +1235,72 @@ std::optional Importer::ImportDecl(clang::Decl* decl) { return std::nullopt; } +absl::StatusOr> Importer::ImportDeclToProto( + clang::Decl* decl, bool must_bind) { + if (IsTransitivelyInPrivate(decl)) { + if (must_bind) { + return HardErrorToProto( + *decl, + FormattedError::Static("Items in private sections or classes are not " + "supported, but marked with must_bind")); + } + return nullptr; + } + + const absl::StatusOr do_not_bind = + HasAnnotationWithoutArgs(*decl, "crubit_do_not_bind"); + if (!do_not_bind.ok()) { + return HardErrorToProto(*decl, + FormattedError::FromStatus(do_not_bind.status())); + } + if (*do_not_bind) { + if (must_bind) { + return HardErrorToProto( + *decl, FormattedError::Static("Conflicting CRUBIT_MUST_BIND and " + "CRUBIT_DO_NOT_BIND annotations")); + } + const std::optional>& + do_not_bind_allowlist = invocation_.do_not_bind_allowlist_; + const clang::NamedDecl* named_decl = + clang::dyn_cast(decl); + if (named_decl && !clang::isa(decl) && + do_not_bind_allowlist.has_value()) { + std::string decl_name = named_decl->getQualifiedNameAsString(); + if (!do_not_bind_allowlist->contains(decl_name)) { + return HardErrorToProto( + *decl, FormattedError::PrefixedStrCat( + "CRUBIT_DO_NOT_BIND annotation on non-allowlisted decl", + std::move(decl_name), + "\nOmitted bindings must be pre-registered using " + "`do_not_bind_allowlist`")); + } + } + return nullptr; + } + + for (auto& decl_importer : decl_importers_) { + CRUBIT_ASSIGN_OR_RETURN(std::unique_ptr item, + decl_importer->ImportDeclToProto(decl, must_bind)); + if (item != nullptr) { + return item; + } + } + + if (must_bind) { + return HardErrorToProto( + *decl, + FormattedError::Static( + "No importer found for decl with CRUBIT_MUST_BIND annotation")); + } + + return nullptr; +} + std::optional Importer::GetImportedItem( const clang::Decl* decl) const { auto it = import_cache_.find(decl); if (it != import_cache_.end()) { - return it->second; + return it->second.legacy_item; } return std::nullopt; } @@ -1377,7 +1471,7 @@ bool Importer::RefersToOwnedDefinitionImpl( } bool Importer::IsFromProtoTarget(const clang::Decl& decl) const { - // TODO(b/b/441343672): This is probably not a good way to detect if something + // TODO(b/441343672): This is probably not a good way to detect if something // is from a proto target, and we should do something more durable. clang::SourceManager& source_manager = ctx_.getSourceManager(); std::optional filename = @@ -1608,6 +1702,22 @@ IR::Item Importer::HardError(const clang::Decl& decl, FormattedError error) { /*is_hard_error=*/true); } +std::unique_ptr Importer::HardErrorToProto( + const clang::Decl& decl, FormattedError error) { + return ImportUnsupportedItemToProto(decl, std::nullopt, {std::move(error)}, + /*is_hard_error=*/true); +} + +std::unique_ptr Importer::ImportUnsupportedItemToProto( + const clang::Decl& original_decl, std::optional path, + std::vector errors, bool is_hard_error) { + IR::Item legacy_item = ImportUnsupportedItem( + original_decl, std::move(path), std::move(errors), is_hard_error); + auto proto_item = std::make_unique(); + *proto_item = crubit::ToFlatProto(legacy_item); + return proto_item; +} + IR::Item Importer::ImportUnsupportedItem( const clang::Decl& original_decl, std::optional path, std::vector errors, bool is_hard_error) { diff --git a/rs_bindings_from_cc/importer.h b/rs_bindings_from_cc/importer.h index f326f9a2e..959faafb2 100644 --- a/rs_bindings_from_cc/importer.h +++ b/rs_bindings_from_cc/importer.h @@ -21,6 +21,7 @@ #include "rs_bindings_from_cc/bazel_types.h" #include "rs_bindings_from_cc/decl_importer.h" #include "rs_bindings_from_cc/ir.h" +#include "rs_bindings_from_cc/ir.pb.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" @@ -32,6 +33,18 @@ namespace crubit { +namespace ir_proto = rs_bindings_from_cc::ir_proto::flat; + +// Stateful entry to prevent re-entrant imports, and to track the underlying +// proto item we should store the AST node in. +struct ItemCacheEntry { + enum class Status { kInProgress, kCompleted, kFailed, kUnsupported }; + Status status = Status::kCompleted; + ItemId id = ItemId(0); + std::optional legacy_item; + std::unique_ptr proto_item; +}; + // Iterates over the AST created from the invocation's entry headers and // creates an intermediate representation of the import (`IR`) into the // invocation object. @@ -48,11 +61,18 @@ class Importer final : public ImportContext { void ImportDeclsFromDeclContext( const clang::DeclContext* decl_context) override; IR::Item HardError(const clang::Decl& decl, FormattedError error) override; + std::unique_ptr HardErrorToProto(const clang::Decl& decl, + FormattedError error); IR::Item ImportUnsupportedItem(const clang::Decl& decl, std::optional path, std::vector errors, bool is_hard_error) override; + std::unique_ptr ImportUnsupportedItemToProto( + const clang::Decl& decl, std::optional path, + std::vector errors, bool is_hard_error) override; std::optional ImportDecl(clang::Decl* decl) override; + absl::StatusOr> ImportDeclToProto( + clang::Decl* decl, bool must_bind) override; std::optional GetImportedItem( const clang::Decl* decl) const override; @@ -198,8 +218,7 @@ class Importer final : public ImportContext { // to successfully match a decl "wins", and no other importers are tried. std::vector> decl_importers_; std::unique_ptr mangler_; - absl::flat_hash_map> - import_cache_; + absl::flat_hash_map import_cache_; absl::flat_hash_set class_template_instantiations_; std::vector comments_; diff --git a/rs_bindings_from_cc/importers/BUILD b/rs_bindings_from_cc/importers/BUILD index d4ea5bbcf..a1a68f234 100644 --- a/rs_bindings_from_cc/importers/BUILD +++ b/rs_bindings_from_cc/importers/BUILD @@ -101,11 +101,14 @@ cc_library( srcs = ["friend.cc"], hdrs = ["friend.h"], deps = [ + "//common:status_macros", "//rs_bindings_from_cc:cc_ir", "//rs_bindings_from_cc:decl_importer", "@abseil-cpp//absl/log:check", + "@abseil-cpp//absl/status:statusor", "@llvm-project//clang:ast", "@llvm-project//clang:basic", + "@llvm-project//llvm:Support", ], ) @@ -160,6 +163,7 @@ cc_library( "//rs_bindings_from_cc:cc_ir", "//rs_bindings_from_cc:decl_importer", "@abseil-cpp//absl/log:check", + "@abseil-cpp//absl/status:statusor", "@llvm-project//clang:ast", ], ) diff --git a/rs_bindings_from_cc/importers/enum_constant.cc b/rs_bindings_from_cc/importers/enum_constant.cc index 0aa793263..80e75d2af 100644 --- a/rs_bindings_from_cc/importers/enum_constant.cc +++ b/rs_bindings_from_cc/importers/enum_constant.cc @@ -4,6 +4,7 @@ #include "rs_bindings_from_cc/importers/enum_constant.h" +#include #include #include #include @@ -98,4 +99,97 @@ std::optional EnumConstantDeclImporter::Import( }; } +absl::StatusOr> +EnumConstantDeclImporter::ImportToProto( + clang::EnumConstantDecl* enum_constant_decl) { + absl::StatusOr enumerator_name = + ictx_.GetTranslatedIdentifier(enum_constant_decl); + if (!enumerator_name.ok()) { + return ictx_.ImportUnsupportedItemToProto( + *enum_constant_decl, std::nullopt, + {FormattedError::PrefixedStrCat("Enumerator name is not supported", + enumerator_name.status().message())}); + } + + auto enclosing_item_id = ictx_.GetEnclosingItemId(enum_constant_decl); + if (!enclosing_item_id.ok()) { + return ictx_.ImportUnsupportedItemToProto( + *enum_constant_decl, std::nullopt, + {FormattedError::FromStatus(std::move(enclosing_item_id.status()))}); + } + + const auto* enum_decl = + llvm::cast(enum_constant_decl->getDeclContext()); + clang::QualType cpp_type = enum_decl->getIntegerType(); + if (cpp_type.isNull()) { + return ictx_.ImportUnsupportedItemToProto( + *enum_constant_decl, std::nullopt, + {FormattedError::Static("Enumerator's enum has no underlying type")}); + } + + absl::StatusOr type = + ictx_.ConvertQualType(cpp_type, nullptr, /*nullable=*/true, + ictx_.AreAssumedLifetimesEnabledForTarget( + ictx_.GetOwningTarget(enum_constant_decl))); + if (!type.ok()) { + return ictx_.ImportUnsupportedItemToProto( + *enum_constant_decl, std::nullopt, + {FormattedError::FromStatus(std::move(type.status()))}); + } + + std::optional deprecated; + absl::StatusOr> unknown_attr = CollectUnknownAttrs( + *enum_constant_decl, [&deprecated](const clang::Attr& attr) { + if (auto* deprecated_attr = + clang::dyn_cast(&attr)) { + deprecated.emplace(deprecated_attr->getMessage()); + return true; + } + return false; + }); + if (!unknown_attr.ok()) { + return ictx_.ImportUnsupportedItemToProto( + *enum_constant_decl, std::nullopt, + {FormattedError::FromStatus(std::move(unknown_attr.status()))}); + } + + ictx_.MarkAsSuccessfullyImported(enum_constant_decl); + absl::StatusOr value = + IntegerConstant::FromAPValue(enum_constant_decl->getInitVal()); + if (!value.ok()) { + return ictx_.ImportUnsupportedItemToProto( + *enum_constant_decl, std::nullopt, + {FormattedError::FromStatus(std::move(value.status()))}); + } + + auto item = std::make_unique(); + ir_proto::Constant* constant = item->mutable_constant(); + + *constant->mutable_value() = value->ToFlatProto(); + *constant->mutable_cc_name() = enumerator_name->cc_identifier.ToFlatProto(); + *constant->mutable_rs_name() = enumerator_name->rs_identifier().ToFlatProto(); + constant->set_unique_name(ictx_.GetUniqueName(*enum_constant_decl)); + constant->set_id(ictx_.GenerateItemId(enum_constant_decl).value()); + constant->set_owning_target( + ictx_.GetOwningTarget(enum_constant_decl).value()); + constant->set_source_loc( + ictx_.ConvertSourceLocation(enum_constant_decl->getBeginLoc(), nullptr)); + *constant->mutable_type() = type->ToFlatProto(); + if (unknown_attr->has_value()) { + constant->set_unknown_attr(**unknown_attr); + } + if (enclosing_item_id->has_value()) { + constant->set_enclosing_item_id((*enclosing_item_id)->value()); + } + constant->set_must_bind(must_bind_); + if (deprecated) { + constant->set_deprecated(*deprecated); + } + if (auto doc_comment = ictx_.GetComment(enum_constant_decl)) { + constant->set_doc_comment(*doc_comment); + } + + return item; +} + } // namespace crubit diff --git a/rs_bindings_from_cc/importers/enum_constant.h b/rs_bindings_from_cc/importers/enum_constant.h index 1cc226fb7..71e8255cc 100644 --- a/rs_bindings_from_cc/importers/enum_constant.h +++ b/rs_bindings_from_cc/importers/enum_constant.h @@ -5,8 +5,10 @@ #ifndef CRUBIT_RS_BINDINGS_FROM_CC_IMPORTERS_ENUM_CONSTANT_H_ #define CRUBIT_RS_BINDINGS_FROM_CC_IMPORTERS_ENUM_CONSTANT_H_ +#include #include +#include "absl/status/statusor.h" #include "rs_bindings_from_cc/decl_importer.h" #include "rs_bindings_from_cc/ir.h" #include "clang/AST/Decl.h" @@ -22,6 +24,8 @@ class EnumConstantDeclImporter explicit EnumConstantDeclImporter(ImportContext& context) : DeclImporterBase(context) {} std::optional Import(clang::EnumConstantDecl*) override; + absl::StatusOr> ImportToProto( + clang::EnumConstantDecl*) override; }; } // namespace crubit diff --git a/rs_bindings_from_cc/importers/existing_rust_type.cc b/rs_bindings_from_cc/importers/existing_rust_type.cc index 25de2e14e..0b5245744 100644 --- a/rs_bindings_from_cc/importers/existing_rust_type.cc +++ b/rs_bindings_from_cc/importers/existing_rust_type.cc @@ -4,6 +4,7 @@ #include "rs_bindings_from_cc/importers/existing_rust_type.h" +#include #include #include #include @@ -305,4 +306,64 @@ std::optional ExistingRustTypeImporter::Import( }; } +absl::StatusOr> +ExistingRustTypeImporter::ImportToProto(clang::TypeDecl* type_decl) { + absl::StatusOr> opt_attr = + GetCrubitInternalRustTypeAttr(ictx_, *type_decl); + if (!opt_attr.ok()) { + return ictx_.HardErrorToProto( + *type_decl, FormattedError::PrefixedStrCat( + "Invalid CRUBIT_INTERNAL_RUST_TYPE attribute", + std::move(opt_attr).status().message())); + } + if (!opt_attr->has_value()) { + return nullptr; + } + auto [format_string, format_args] = **std::move(opt_attr); + absl::StatusOr is_same_abi = GetIsSameAbiAttribute(type_decl); + if (!is_same_abi.ok()) { + return ictx_.HardErrorToProto( + *type_decl, FormattedError::PrefixedStrCat( + "Invalid crubit_internal_is_same_abi attribute", + is_same_abi.status().message())); + } + + clang::ASTContext& context = type_decl->getASTContext(); + clang::QualType cc_qualtype = context.getTypeDeclType(type_decl); + const clang::Type* cpp_type = cc_qualtype.getTypePtr(); + if (cpp_type == nullptr) return nullptr; + + clang::PrintingPolicy policy(context.getLangOpts()); + policy.SuppressTagKeyword = true; + std::string cc_name = cc_qualtype.getAsString(policy); + + ictx_.MarkAsSuccessfullyImported(type_decl); + + auto item = std::make_unique(); + ir_proto::ExistingRustType* existing_rust_type = + item->mutable_existing_rust_type(); + + existing_rust_type->set_rs_name(std::move(format_string)); + existing_rust_type->set_cc_name(std::move(cc_name)); + existing_rust_type->set_unique_name(ictx_.GetUniqueName(*type_decl)); + for (const auto& arg : format_args) { + *existing_rust_type->add_template_args() = arg.ToFlatProto(); + } + existing_rust_type->set_owning_target( + ictx_.GetOwningTarget(type_decl).value()); + if (!cpp_type->isIncompleteType()) { + ir_proto::SizeAlign* sa = existing_rust_type->mutable_size_align(); + sa->set_size(context.getTypeSizeInChars(cpp_type).getQuantity()); + sa->set_alignment(context.getTypeAlignInChars(cpp_type).getQuantity()); + } + existing_rust_type->set_is_same_abi(*is_same_abi); + existing_rust_type->set_id(ictx_.GenerateItemId(type_decl).value()); + existing_rust_type->set_must_bind(must_bind_); + existing_rust_type->set_impl_debug(ictx_.IsRecordImplDebugEnabledForTarget( + ictx_.GetOwningTarget(type_decl)) && + ictx_.ImplementsCoreFmtDebug(*type_decl)); + + return item; +} + } // namespace crubit diff --git a/rs_bindings_from_cc/importers/existing_rust_type.h b/rs_bindings_from_cc/importers/existing_rust_type.h index 6483cb750..40329f6c0 100644 --- a/rs_bindings_from_cc/importers/existing_rust_type.h +++ b/rs_bindings_from_cc/importers/existing_rust_type.h @@ -5,8 +5,10 @@ #ifndef CRUBIT_RS_BINDINGS_FROM_CC_IMPORTERS_TYPE_DECL_H_ #define CRUBIT_RS_BINDINGS_FROM_CC_IMPORTERS_TYPE_DECL_H_ +#include #include +#include "absl/status/statusor.h" #include "rs_bindings_from_cc/decl_importer.h" #include "rs_bindings_from_cc/ir.h" #include "clang/AST/Decl.h" @@ -19,6 +21,8 @@ class ExistingRustTypeImporter final explicit ExistingRustTypeImporter(ImportContext& context) : DeclImporterBase(context) {} std::optional Import(clang::TypeDecl*) override; + absl::StatusOr> ImportToProto( + clang::TypeDecl*) override; }; } // namespace crubit diff --git a/rs_bindings_from_cc/importers/friend.cc b/rs_bindings_from_cc/importers/friend.cc index 5ea208439..f548e9ea6 100644 --- a/rs_bindings_from_cc/importers/friend.cc +++ b/rs_bindings_from_cc/importers/friend.cc @@ -4,15 +4,19 @@ #include "rs_bindings_from_cc/importers/friend.h" +#include #include #include #include "absl/log/check.h" +#include "absl/status/statusor.h" +#include "common/status_macros.h" #include "rs_bindings_from_cc/ir.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" #include "clang/Basic/LLVM.h" +#include "llvm/ADT/STLExtras.h" namespace crubit { @@ -72,4 +76,49 @@ std::optional FriendDeclImporter::Import( return result; } +absl::StatusOr> +FriendDeclImporter::ImportToProto(clang::FriendDecl* friend_decl) { + if (!ictx_.IsFromCurrentTarget(friend_decl)) return nullptr; + + clang::NamedDecl* named_decl = clang::dyn_cast_or_null( + friend_decl->getFriendDecl()); + if (!named_decl || named_decl != named_decl->getCanonicalDecl()) { + return nullptr; + } + + clang::DeclContext* decl_context = friend_decl->getDeclContext(); + if (!decl_context) { + return ictx_.ImportUnsupportedItemToProto( + *named_decl, std::nullopt, + {FormattedError::Static("DeclContext was unexpectedly null")}); + } + clang::CXXRecordDecl* enclosing_record_decl = + clang::dyn_cast(decl_context); + if (!enclosing_record_decl) { + return ictx_.ImportUnsupportedItemToProto( + *named_decl, std::nullopt, + {FormattedError::Static( + "DeclContext was unexpectedly not a CXXRecordDecl")}); + } + + if (llvm::any_of(named_decl->redecls(), + [named_decl](const clang::Decl* redecl) { + return redecl != named_decl && + !redecl->getLexicalDeclContext()->isRecord(); + })) { + return nullptr; + } + + CRUBIT_ASSIGN_OR_RETURN(std::unique_ptr item, + ictx_.ImportDeclToProto(named_decl, must_bind_)); + if (item == nullptr || item->has_unsupported_item() || !item->has_func()) { + return nullptr; + } + + item->mutable_func()->set_id(ictx_.GenerateItemId(friend_decl).value()); + item->mutable_func()->set_adl_enclosing_record( + ictx_.GenerateItemId(enclosing_record_decl).value()); + return item; +} + } // namespace crubit diff --git a/rs_bindings_from_cc/importers/friend.h b/rs_bindings_from_cc/importers/friend.h index 8202038ca..bf7c58bd2 100644 --- a/rs_bindings_from_cc/importers/friend.h +++ b/rs_bindings_from_cc/importers/friend.h @@ -5,8 +5,10 @@ #ifndef CRUBIT_RS_BINDINGS_FROM_CC_IMPORTERS_FRIEND_H_ #define CRUBIT_RS_BINDINGS_FROM_CC_IMPORTERS_FRIEND_H_ +#include #include +#include "absl/status/statusor.h" #include "rs_bindings_from_cc/decl_importer.h" #include "rs_bindings_from_cc/ir.h" #include "clang/AST/DeclFriend.h" @@ -19,6 +21,8 @@ class FriendDeclImporter : public DeclImporterBase { explicit FriendDeclImporter(ImportContext& context) : DeclImporterBase(context) {} std::optional Import(clang::FriendDecl*) override; + absl::StatusOr> ImportToProto( + clang::FriendDecl*) override; }; } // namespace crubit diff --git a/rs_bindings_from_cc/importers/namespace.cc b/rs_bindings_from_cc/importers/namespace.cc index 3c01d078f..3767d8fcb 100644 --- a/rs_bindings_from_cc/importers/namespace.cc +++ b/rs_bindings_from_cc/importers/namespace.cc @@ -5,11 +5,13 @@ #include "rs_bindings_from_cc/importers/namespace.h" #include +#include #include #include #include #include "absl/log/check.h" +#include "absl/status/statusor.h" #include "rs_bindings_from_cc/ast_util.h" #include "rs_bindings_from_cc/ir.h" #include "clang/AST/Attr.h" @@ -83,4 +85,77 @@ std::optional NamespaceDeclImporter::Import( .doc_comment = ictx_.GetComment(namespace_decl)}; } +absl::StatusOr> +NamespaceDeclImporter::ImportToProto(clang::NamespaceDecl* namespace_decl) { + if (namespace_decl->isAnonymousNamespace()) { + return ictx_.ImportUnsupportedItemToProto( + *namespace_decl, std::nullopt, + {FormattedError::Static("Anonymous namespaces are not yet supported")}); + } + + absl::StatusOr identifier = + ictx_.GetTranslatedIdentifier(namespace_decl); + if (!identifier.ok()) { + return ictx_.ImportUnsupportedItemToProto( + *namespace_decl, std::nullopt, + {FormattedError::PrefixedStrCat("Namespace name is not supported", + identifier.status().message())}); + } + + ictx_.ImportDeclsFromDeclContext(namespace_decl); + auto item_ids = ictx_.GetItemIdsInSourceOrder(namespace_decl); + auto enclosing_item_id = ictx_.GetEnclosingItemId(namespace_decl); + if (!enclosing_item_id.ok()) { + return ictx_.ImportUnsupportedItemToProto( + *namespace_decl, std::nullopt, + {FormattedError::FromStatus(std::move(enclosing_item_id.status()))}); + } + + std::optional deprecated; + absl::StatusOr> unknown_attr = + CollectUnknownAttrs(*namespace_decl, [&](const clang::Attr& attr) { + if (auto* deprecated_attr = + clang::dyn_cast(&attr)) { + deprecated.emplace(deprecated_attr->getMessage()); + return true; + } + return false; + }); + if (!unknown_attr.ok()) { + return ictx_.ImportUnsupportedItemToProto( + *namespace_decl, std::nullopt, + {FormattedError::FromStatus(std::move(unknown_attr.status()))}); + } + + ItemId id = ictx_.GenerateItemId(namespace_decl); + ictx_.invocation_.child_item_ids_[id] = std::move(item_ids); + + auto item = std::make_unique(); + ir_proto::Namespace* ns = item->mutable_namespace_decl(); + + *ns->mutable_cc_name() = identifier->cc_identifier.ToFlatProto(); + *ns->mutable_rs_name() = identifier->cc_identifier.ToFlatProto(); + ns->set_unique_name(ictx_.GetUniqueName(*namespace_decl)); + ns->set_id(id.value()); + ns->set_canonical_namespace_id( + ictx_.GenerateItemId(namespace_decl->getCanonicalDecl()).value()); + if (unknown_attr->has_value()) { + ns->set_unknown_attr(**unknown_attr); + } + ns->set_owning_target(ictx_.GetOwningTarget(namespace_decl).value()); + if (enclosing_item_id->has_value()) { + ns->set_enclosing_item_id((*enclosing_item_id)->value()); + } + ns->set_is_inline(namespace_decl->isInline()); + ns->set_must_bind(must_bind_); + if (deprecated) { + ns->set_deprecated(*deprecated); + } + if (auto doc_comment = ictx_.GetComment(namespace_decl)) { + ns->set_doc_comment(*doc_comment); + } + + return item; +} + } // namespace crubit diff --git a/rs_bindings_from_cc/importers/namespace.h b/rs_bindings_from_cc/importers/namespace.h index bb54cc4e4..f62582148 100644 --- a/rs_bindings_from_cc/importers/namespace.h +++ b/rs_bindings_from_cc/importers/namespace.h @@ -5,8 +5,10 @@ #ifndef CRUBIT_RS_BINDINGS_FROM_CC_IMPORTERS_NAMESPACE_H_ #define CRUBIT_RS_BINDINGS_FROM_CC_IMPORTERS_NAMESPACE_H_ +#include #include +#include "absl/status/statusor.h" #include "rs_bindings_from_cc/decl_importer.h" #include "rs_bindings_from_cc/ir.h" #include "clang/AST/Decl.h" @@ -19,6 +21,8 @@ class NamespaceDeclImporter : public DeclImporterBase { explicit NamespaceDeclImporter(ImportContext& context) : DeclImporterBase(context) {} std::optional Import(clang::NamespaceDecl*) override; + absl::StatusOr> ImportToProto( + clang::NamespaceDecl* namespace_decl) override; }; } // namespace crubit