| // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| // Exceptions. See /LICENSE for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| #include "rs_bindings_from_cc/ir.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <ostream> |
| #include <string> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| #include "absl/base/nullability.h" |
| #include "absl/log/check.h" |
| #include "absl/status/status.h" |
| #include "absl/strings/cord.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "common/string_type.h" |
| #include "common/strong_int.h" |
| #include "llvm/Support/JSON.h" |
| |
| namespace crubit { |
| namespace { |
| // https://en.cppreference.com/w/cpp/utility/variant/visit |
| template <typename... Ts> |
| struct visitor : Ts... { |
| using Ts::operator()...; |
| }; |
| } // namespace |
| |
| template <class T> |
| llvm::json::Value toJSON(const T& t) { |
| return t.ToJson(); |
| } |
| |
| template <typename TTag, typename TInt> |
| llvm::json::Value toJSON(const crubit::StrongInt<TTag, TInt> strong_int) { |
| return llvm::json::Value(strong_int.value()); |
| } |
| |
| template <typename TTag> |
| llvm::json::Value toJSON(const crubit::StringType<TTag> string_type) { |
| return llvm::json::Value(string_type.value()); |
| } |
| |
| template <class T> |
| llvm::json::Value toJSON(const absl::StatusOr<T>& t) { |
| if (t.ok()) { |
| return llvm::json::Object{{"Ok", *t}}; |
| } |
| return llvm::json::Object{{"Err", std::string(t.status().message())}}; |
| } |
| |
| llvm::json::Value HeaderName::ToJson() const { |
| return llvm::json::Object{ |
| {"name", name_}, |
| }; |
| } |
| |
| llvm::json::Value LifetimeName::ToJson() const { |
| return llvm::json::Object{ |
| {"name", name}, |
| {"id", id}, |
| }; |
| } |
| |
| llvm::json::Value CcType::ToJson() const { |
| llvm::json::Object variant_object = std::visit( |
| visitor{ |
| [&](CcType::Primitive primitive) { |
| return llvm::json::Object{{"Primitive", primitive.spelling}}; |
| }, |
| [&](CcType::PointerType pointer) { |
| return llvm::json::Object{ |
| {"Pointer", |
| llvm::json::Object{ |
| { |
| "kind", |
| [&]() -> llvm::json::Value { |
| switch (pointer.kind) { |
| case PointerTypeKind::kLValueRef: |
| return "LValueRef"; |
| case PointerTypeKind::kRValueRef: |
| return "RValueRef"; |
| case PointerTypeKind::kNullable: |
| return "Nullable"; |
| case PointerTypeKind::kNonNull: |
| return "NonNull"; |
| } |
| }(), |
| }, |
| {"lifetime", pointer.lifetime}, |
| {"pointee_type", *pointer.pointee_type}, |
| }}, |
| }; |
| }, |
| [&](const CcType::FuncPointer& func_value) { |
| std::vector<llvm::json::Value> param_and_return_type_values; |
| param_and_return_type_values.reserve( |
| func_value.param_and_return_types.size()); |
| for (const CcType& type : func_value.param_and_return_types) { |
| param_and_return_type_values.push_back(type.ToJson()); |
| } |
| return llvm::json::Object{ |
| {"FuncPointer", |
| llvm::json::Object{ |
| {"non_null", func_value.non_null}, |
| { |
| "call_conv", |
| [&]() -> llvm::json::Value { |
| switch (func_value.call_conv) { |
| case CallingConv::kC: |
| return "cdecl"; |
| case CallingConv::kX86FastCall: |
| return "fastcall"; |
| case CallingConv::kX86VectorCall: |
| return "vectorcall"; |
| case CallingConv::kX864ThisCall: |
| return "thiscall"; |
| case CallingConv::kX86StdCall: |
| return "stdcall"; |
| case CallingConv::kWin64: |
| return "ms_abi"; |
| } |
| }(), |
| }, |
| {"param_and_return_types", param_and_return_type_values}, |
| }}, |
| }; |
| }, |
| [&](ItemId id) { return llvm::json::Object{{"Decl", id}}; }}, |
| variant); |
| |
| return llvm::json::Object{ |
| {"variant", std::move(variant_object)}, |
| {"is_const", is_const}, |
| {"unknown_attr", unknown_attr}, |
| }; |
| } |
| |
| namespace { |
| CcType PointerOrReferenceTo(CcType pointee_type, PointerTypeKind pointer_kind, |
| std::optional<LifetimeId> lifetime) { |
| return CcType(CcType::PointerType{ |
| .kind = pointer_kind, |
| .lifetime = lifetime, |
| .pointee_type = std::make_shared<CcType>(std::move(pointee_type)), |
| }); |
| } |
| } // namespace |
| |
| CcType CcType::PointerTo(CcType pointee_type, |
| std::optional<LifetimeId> lifetime, bool nullable) { |
| return PointerOrReferenceTo( |
| std::move(pointee_type), |
| nullable ? PointerTypeKind::kNullable : PointerTypeKind::kNonNull, |
| lifetime); |
| } |
| |
| CcType CcType::LValueReferenceTo(CcType pointee_type, |
| std::optional<LifetimeId> lifetime) { |
| return PointerOrReferenceTo(std::move(pointee_type), |
| PointerTypeKind::kLValueRef, lifetime); |
| } |
| |
| CcType CcType::RValueReferenceTo(CcType pointee_type, |
| std::optional<LifetimeId> lifetime) { |
| return PointerOrReferenceTo(std::move(pointee_type), |
| PointerTypeKind::kRValueRef, lifetime); |
| } |
| |
| llvm::json::Value Identifier::ToJson() const { |
| return llvm::json::Object{ |
| {"identifier", identifier_}, |
| }; |
| } |
| |
| llvm::json::Value IntegerConstant::ToJson() const { |
| return llvm::json::Object{ |
| {"is_negative", is_negative_}, |
| {"wrapped_value", wrapped_value_}, |
| }; |
| } |
| |
| llvm::json::Value Operator::ToJson() const { |
| return llvm::json::Object{ |
| {"name", name_}, |
| }; |
| } |
| |
| static std::string SpecialNameToString(SpecialName special_name) { |
| switch (special_name) { |
| case SpecialName::kDestructor: |
| return "Destructor"; |
| case SpecialName::kConstructor: |
| return "Constructor"; |
| } |
| } |
| |
| llvm::json::Value toJSON(const UnqualifiedIdentifier& unqualified_identifier) { |
| if (auto* id = std::get_if<Identifier>(&unqualified_identifier)) { |
| return llvm::json::Object{ |
| {"Identifier", *id}, |
| }; |
| } else if (auto* op = std::get_if<Operator>(&unqualified_identifier)) { |
| return llvm::json::Object{ |
| {"Operator", *op}, |
| }; |
| } else { |
| SpecialName special_name = std::get<SpecialName>(unqualified_identifier); |
| return llvm::json::Object{ |
| {SpecialNameToString(special_name), nullptr}, |
| }; |
| } |
| } |
| |
| llvm::json::Value FuncParam::ToJson() const { |
| return llvm::json::Object{ |
| {"type", type}, |
| {"identifier", identifier}, |
| {"unknown_attr", unknown_attr}, |
| }; |
| } |
| |
| std::ostream& operator<<(std::ostream& o, const SpecialName& special_name) { |
| return o << SpecialNameToString(special_name); |
| } |
| |
| UnqualifiedIdentifier& TranslatedUnqualifiedIdentifier::rs_identifier() { |
| if (crubit_rust_name.has_value()) { |
| return *crubit_rust_name; |
| } |
| return cc_identifier; |
| } |
| |
| Identifier& TranslatedIdentifier::rs_identifier() { |
| if (crubit_rust_name.has_value()) { |
| return *crubit_rust_name; |
| } |
| return cc_identifier; |
| } |
| |
| llvm::json::Value MemberFuncMetadata::InstanceMethodMetadata::ToJson() const { |
| const char* reference_str = nullptr; |
| switch (reference) { |
| case MemberFuncMetadata::kLValue: |
| reference_str = "LValue"; |
| break; |
| case MemberFuncMetadata::kRValue: |
| reference_str = "RValue"; |
| break; |
| case MemberFuncMetadata::kUnqualified: |
| reference_str = "Unqualified"; |
| break; |
| } |
| |
| return llvm::json::Object{ |
| {"reference", reference_str}, |
| {"is_const", is_const}, |
| {"is_virtual", is_virtual}, |
| }; |
| } |
| |
| llvm::json::Value MemberFuncMetadata::ToJson() const { |
| return llvm::json::Object{ |
| {"record_id", record_id}, |
| {"instance_method_metadata", instance_method_metadata}, |
| }; |
| } |
| |
| llvm::json::Value ExistingRustType::ToJson() const { |
| llvm::json::Object override{ |
| {"rs_name", rs_name}, |
| {"cc_name", cc_name}, |
| {"type_parameters", type_parameters}, |
| {"owning_target", owning_target}, |
| {"is_same_abi", is_same_abi}, |
| {"id", id}, |
| {"must_bind", must_bind}, |
| }; |
| if (size_align.has_value()) { |
| override.insert({"size_align", size_align->ToJson()}); |
| } |
| |
| return llvm::json::Object{ |
| {"ExistingRustType", std::move(override)}, |
| }; |
| } |
| |
| llvm::json::Value UseMod::ToJson() const { |
| llvm::json::Object use_mod{ |
| {"path", path}, |
| {"mod_name", mod_name}, |
| {"id", id}, |
| {"must_bind", must_bind}, |
| }; |
| |
| return llvm::json::Object{ |
| {"UseMod", std::move(use_mod)}, |
| }; |
| } |
| |
| static std::string SafetyAnnotationToString( |
| SafetyAnnotation safety_annotation) { |
| switch (safety_annotation) { |
| case SafetyAnnotation::kDisableUnsafe: |
| return "DisableUnsafe"; |
| case SafetyAnnotation::kUnsafe: |
| return "Unsafe"; |
| case SafetyAnnotation::kUnannotated: |
| return "Unannotated"; |
| } |
| } |
| |
| llvm::json::Value Func::ToJson() const { |
| llvm::json::Object func{ |
| {"cc_name", cc_name}, |
| {"rs_name", rs_name}, |
| {"owning_target", owning_target}, |
| {"doc_comment", doc_comment}, |
| {"mangled_name", mangled_name}, |
| {"return_type", return_type}, |
| {"params", params}, |
| {"lifetime_params", lifetime_params}, |
| {"is_inline", is_inline}, |
| {"member_func_metadata", member_func_metadata}, |
| {"is_extern_c", is_extern_c}, |
| {"is_noreturn", is_noreturn}, |
| {"is_variadic", is_variadic}, |
| {"is_consteval", is_consteval}, |
| {"nodiscard", nodiscard}, |
| {"deprecated", deprecated}, |
| {"has_c_calling_convention", has_c_calling_convention}, |
| {"is_member_or_descendant_of_class_template", |
| is_member_or_descendant_of_class_template}, |
| {"safety_annotation", SafetyAnnotationToString(safety_annotation)}, |
| {"source_loc", source_loc}, |
| {"id", id}, |
| {"enclosing_item_id", enclosing_item_id}, |
| {"adl_enclosing_record", adl_enclosing_record}, |
| {"must_bind", must_bind}, |
| }; |
| |
| return llvm::json::Object{ |
| {"Func", std::move(func)}, |
| }; |
| } |
| |
| static std::string AccessToString(AccessSpecifier access) { |
| switch (access) { |
| case kPublic: |
| return "Public"; |
| case kProtected: |
| return "Protected"; |
| case kPrivate: |
| return "Private"; |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& o, const AccessSpecifier& access) { |
| return o << AccessToString(access); |
| } |
| |
| llvm::json::Value Field::ToJson() const { |
| return llvm::json::Object{ |
| {"rust_identifier", rust_identifier}, |
| {"cpp_identifier", cpp_identifier}, |
| {"doc_comment", doc_comment}, |
| {"type", type}, |
| {"access", AccessToString(access)}, |
| {"offset", offset}, |
| {"size", size}, |
| {"unknown_attr", toJSON(unknown_attr)}, |
| {"is_no_unique_address", is_no_unique_address}, |
| {"is_bitfield", is_bitfield}, |
| {"is_inheritable", is_inheritable}, |
| }; |
| } |
| |
| llvm::json::Value toJSON(const SpecialMemberFunc& f) { |
| switch (f) { |
| case SpecialMemberFunc::kTrivial: |
| return "Trivial"; |
| case SpecialMemberFunc::kNontrivialMembers: |
| return "NontrivialMembers"; |
| case SpecialMemberFunc::kNontrivialUserDefined: |
| return "NontrivialUserDefined"; |
| case SpecialMemberFunc::kUnavailable: |
| return "Unavailable"; |
| } |
| } |
| |
| llvm::json::Value BaseClass::ToJson() const { |
| return llvm::json::Object{ |
| {"base_record_id", base_record_id}, |
| {"offset", offset}, |
| }; |
| } |
| |
| static std::string RecordTypeToString(RecordType record_type) { |
| switch (record_type) { |
| case kStruct: |
| return "Struct"; |
| case kUnion: |
| return "Union"; |
| case kClass: |
| return "Class"; |
| } |
| } |
| |
| std::ostream& operator<<(std::ostream& o, const RecordType& record_type) { |
| return o << RecordTypeToString(record_type); |
| } |
| |
| llvm::json::Value IncompleteRecord::ToJson() const { |
| llvm::json::Object record{{"cc_name", cc_name}, |
| {"rs_name", rs_name}, |
| {"id", id}, |
| {"owning_target", owning_target}, |
| {"unknown_attr", unknown_attr}, |
| {"record_type", RecordTypeToString(record_type)}, |
| {"enclosing_item_id", enclosing_item_id}, |
| {"must_bind", must_bind}}; |
| |
| return llvm::json::Object{ |
| {"IncompleteRecord", std::move(record)}, |
| }; |
| } |
| |
| llvm::json::Value SizeAlign::ToJson() const { |
| return llvm::json::Object{ |
| {"size", size}, |
| {"alignment", alignment}, |
| }; |
| } |
| |
| llvm::json::Value BridgeType::ToJson() const { |
| return std::visit( |
| visitor{ |
| [&](const BridgeType::BridgeVoidConverters& annotation) { |
| return llvm::json::Object{{ |
| "BridgeVoidConverters", |
| llvm::json::Object{ |
| {"rust_name", annotation.rust_name}, |
| {"rust_to_cpp_converter", annotation.rust_to_cpp_converter}, |
| {"cpp_to_rust_converter", annotation.cpp_to_rust_converter}, |
| }, |
| }}; |
| }, |
| [&](const BridgeType::Bridge& annotation) { |
| return llvm::json::Object{{ |
| "Bridge", |
| llvm::json::Object{ |
| {"rust_name", annotation.rust_name}, |
| {"abi_rust", annotation.abi_rust}, |
| {"abi_cpp", annotation.abi_cpp}, |
| }, |
| }}; |
| }, |
| [&](const BridgeType::StdOptional& std_optional) { |
| return llvm::json::Object{ |
| {"StdOptional", std_optional.inner_type->ToJson()}}; |
| }, |
| [&](const BridgeType::StdPair& std_pair) { |
| return llvm::json::Object{ |
| {"StdPair", llvm::json::Array{ |
| std_pair.first_type->ToJson(), |
| std_pair.second_type->ToJson(), |
| }}}; |
| }, |
| [&](const BridgeType::StdString& std_string) { |
| return llvm::json::Object{{"StdString", nullptr}}; |
| }, |
| [&](const BridgeType::ProtoMessageBridge& proto_message_bridge) { |
| return llvm::json::Object{{ |
| "ProtoMessageBridge", |
| llvm::json::Object{ |
| {"rust_name", proto_message_bridge.rust_name}, |
| {"abi_rust", proto_message_bridge.abi_rust}, |
| {"abi_cpp", proto_message_bridge.abi_cpp}, |
| }, |
| }}; |
| }, |
| }, |
| variant); |
| } |
| |
| llvm::json::Value TemplateArg::ToJson() const { |
| return llvm::json::Object{ |
| {"type", type}, |
| }; |
| } |
| |
| llvm::json::Value TemplateSpecialization::ToJson() const { |
| return llvm::json::Object{ |
| {"is_string_view", is_string_view}, |
| {"is_wstring_view", is_wstring_view}, |
| {"defining_target", defining_target}, |
| {"template_name", template_name}, |
| {"template_args", template_args}, |
| }; |
| } |
| |
| TraitImplPolarity* absl_nullable TraitDerives::Polarity( |
| absl::string_view trait) { |
| // <internal link> start |
| if (trait == "Clone") return &clone; |
| if (trait == "Copy") return © |
| if (trait == "Debug") return &debug; |
| // <internal link> end |
| return nullptr; |
| } |
| |
| static std::string TraitImplPolarityToString(TraitImplPolarity polarity) { |
| switch (polarity) { |
| case TraitImplPolarity::kNegative: |
| return "Negative"; |
| case TraitImplPolarity::kNone: |
| return "None"; |
| case TraitImplPolarity::kPositive: |
| return "Positive"; |
| } |
| } |
| |
| llvm::json::Value TraitDerives::ToJson() const { |
| return llvm::json::Object{ |
| // <internal link> start |
| {"clone", TraitImplPolarityToString(clone)}, |
| {"copy", TraitImplPolarityToString(copy)}, |
| {"debug", TraitImplPolarityToString(debug)}, |
| // <internal link> end |
| {"send", send}, |
| {"sync", sync}, |
| {"custom", custom}, |
| }; |
| } |
| |
| llvm::json::Value Record::ToJson() const { |
| std::vector<llvm::json::Value> json_item_ids; |
| json_item_ids.reserve(child_item_ids.size()); |
| for (const auto& id : child_item_ids) { |
| json_item_ids.push_back(id.value()); |
| } |
| |
| llvm::json::Object record{ |
| {"rs_name", rs_name}, |
| {"cc_name", cc_name}, |
| {"mangled_cc_name", mangled_cc_name}, |
| {"id", id}, |
| {"owning_target", owning_target}, |
| {"template_specialization", template_specialization}, |
| {"unknown_attr", unknown_attr}, |
| {"doc_comment", doc_comment}, |
| {"bridge_type", bridge_type}, |
| {"source_loc", source_loc}, |
| {"unambiguous_public_bases", unambiguous_public_bases}, |
| {"fields", fields}, |
| {"lifetime_params", lifetime_params}, |
| {"size_align", size_align.ToJson()}, |
| {"trait_derives", trait_derives.ToJson()}, |
| {"is_derived_class", is_derived_class}, |
| {"override_alignment", override_alignment}, |
| {"is_unsafe_type", is_unsafe_type}, |
| {"copy_constructor", copy_constructor}, |
| {"move_constructor", move_constructor}, |
| {"destructor", destructor}, |
| {"is_trivial_abi", is_trivial_abi}, |
| {"is_inheritable", is_inheritable}, |
| {"is_abstract", is_abstract}, |
| {"nodiscard", nodiscard}, |
| {"record_type", RecordTypeToString(record_type)}, |
| {"is_aggregate", is_aggregate}, |
| {"is_anon_record_with_typedef", is_anon_record_with_typedef}, |
| {"child_item_ids", std::move(json_item_ids)}, |
| {"enclosing_item_id", enclosing_item_id}, |
| {"must_bind", must_bind}, |
| }; |
| |
| return llvm::json::Object{ |
| {"Record", std::move(record)}, |
| }; |
| } |
| |
| llvm::json::Value Enumerator::ToJson() const { |
| return llvm::json::Object{ |
| {"identifier", identifier}, |
| {"value", value}, |
| {"unknown_attr", unknown_attr}, |
| }; |
| } |
| |
| llvm::json::Value Enum::ToJson() const { |
| llvm::json::Object enum_ir{ |
| {"cc_name", cc_name}, |
| {"rs_name", rs_name}, |
| {"id", id}, |
| {"owning_target", owning_target}, |
| {"source_loc", source_loc}, |
| {"underlying_type", underlying_type}, |
| {"enumerators", enumerators}, |
| {"unknown_attr", unknown_attr}, |
| {"enclosing_item_id", enclosing_item_id}, |
| {"must_bind", must_bind}, |
| }; |
| |
| return llvm::json::Object{ |
| {"Enum", std::move(enum_ir)}, |
| }; |
| } |
| |
| llvm::json::Value GlobalVar::ToJson() const { |
| llvm::json::Object var{ |
| {"cc_name", cc_name}, |
| {"rs_name", rs_name}, |
| {"id", id}, |
| {"owning_target", owning_target}, |
| {"source_loc", source_loc}, |
| {"mangled_name", mangled_name}, |
| {"type", type}, |
| {"unknown_attr", unknown_attr}, |
| {"enclosing_item_id", enclosing_item_id}, |
| {"must_bind", must_bind}, |
| }; |
| |
| return llvm::json::Object{ |
| {"GlobalVar", std::move(var)}, |
| }; |
| } |
| |
| llvm::json::Value TypeAlias::ToJson() const { |
| llvm::json::Object type_alias{{"cc_name", cc_name}, |
| {"rs_name", rs_name}, |
| {"id", id}, |
| {"owning_target", owning_target}, |
| {"unknown_attr", unknown_attr}, |
| {"doc_comment", doc_comment}, |
| {"underlying_type", underlying_type}, |
| {"source_loc", source_loc}, |
| {"enclosing_item_id", enclosing_item_id}, |
| {"must_bind", must_bind}}; |
| |
| return llvm::json::Object{ |
| {"TypeAlias", std::move(type_alias)}, |
| }; |
| } |
| |
| FormattedError FormattedError::FromStatus(absl::Status status) { |
| std::optional<absl::Cord> fmt_cord = |
| status.GetPayload(FormattedError::kFmtPayloadTypeUrl); |
| std::string fmt; |
| if (fmt_cord) { |
| fmt = std::string(*fmt_cord); |
| } else { |
| fmt = absl::StrCat("(unannotated `", |
| absl::StatusCodeToString(status.code()), "` status)"); |
| } |
| return FormattedError(fmt, std::string(status.message())); |
| } |
| |
| llvm::json::Value FormattedError::ToJson() const { |
| return llvm::json::Object{ |
| {"fmt", fmt_}, |
| {"message", message_}, |
| }; |
| } |
| |
| static std::string UnsupportedItemKindToString(UnsupportedItem::Kind kind) { |
| switch (kind) { |
| case UnsupportedItem::Kind::kFunc: |
| return "Func"; |
| case UnsupportedItem::Kind::kGlobalVar: |
| return "GlobalVar"; |
| case UnsupportedItem::Kind::kStruct: |
| return "Struct"; |
| case UnsupportedItem::Kind::kUnion: |
| return "Union"; |
| case UnsupportedItem::Kind::kClass: |
| return "Class"; |
| case UnsupportedItem::Kind::kEnum: |
| return "Enum"; |
| case UnsupportedItem::Kind::kTypeAlias: |
| return "TypeAlias"; |
| case UnsupportedItem::Kind::kNamespace: |
| return "Namespace"; |
| case UnsupportedItem::Kind::kConstructor: |
| return "Constructor"; |
| case UnsupportedItem::Kind::kOther: |
| return "Other"; |
| } |
| } |
| |
| llvm::json::Value UnsupportedItem::Path::ToJson() const { |
| return llvm::json::Object{ |
| {"ident", ident}, |
| {"enclosing_item_id", enclosing_item_id}, |
| }; |
| } |
| |
| llvm::json::Value UnsupportedItem::ToJson() const { |
| std::vector<llvm::json::Value> json_errors; |
| json_errors.reserve(errors.size()); |
| for (const auto& error : errors) { |
| json_errors.push_back(error.ToJson()); |
| } |
| |
| llvm::json::Object unsupported{ |
| {"name", name}, |
| {"kind", UnsupportedItemKindToString(kind)}, |
| {"path", path}, |
| {"errors", json_errors}, |
| {"source_loc", source_loc}, |
| {"id", id}, |
| {"must_bind", must_bind}, |
| }; |
| |
| return llvm::json::Object{ |
| {"UnsupportedItem", std::move(unsupported)}, |
| }; |
| } |
| |
| llvm::json::Value Comment::ToJson() const { |
| llvm::json::Object comment{ |
| {"text", text}, |
| {"id", id}, |
| {"must_bind", must_bind}, |
| }; |
| comment["id"] = id.value(); |
| return llvm::json::Object{ |
| {"Comment", std::move(comment)}, |
| }; |
| } |
| |
| llvm::json::Value Namespace::ToJson() const { |
| std::vector<llvm::json::Value> json_item_ids; |
| json_item_ids.reserve(child_item_ids.size()); |
| for (const auto& id : child_item_ids) { |
| json_item_ids.push_back(id.value()); |
| } |
| |
| llvm::json::Object ns{ |
| {"cc_name", cc_name}, |
| {"rs_name", rs_name}, |
| {"id", id}, |
| {"canonical_namespace_id", canonical_namespace_id}, |
| {"unknown_attr", unknown_attr}, |
| {"owning_target", owning_target}, |
| {"child_item_ids", std::move(json_item_ids)}, |
| {"enclosing_item_id", enclosing_item_id}, |
| {"is_inline", is_inline}, |
| {"must_bind", must_bind}, |
| }; |
| |
| return llvm::json::Object{ |
| {"Namespace", std::move(ns)}, |
| }; |
| } |
| |
| llvm::json::Value IR::ToJson() const { |
| std::vector<llvm::json::Value> json_items; |
| json_items.reserve(items.size()); |
| for (const auto& item : items) { |
| std::visit([&](auto&& item) { json_items.push_back(item.ToJson()); }, item); |
| } |
| CHECK_EQ(json_items.size(), items.size()); |
| |
| llvm::json::Object top_level_item_ids_json; |
| for (const auto& [target, item_ids] : top_level_item_ids) { |
| std::vector<llvm::json::Value> item_ids_json; |
| item_ids_json.reserve(item_ids.size()); |
| for (const auto& item_id : item_ids) { |
| item_ids_json.push_back(item_id.value()); |
| } |
| top_level_item_ids_json[target.value()] = std::move(item_ids_json); |
| } |
| |
| llvm::json::Object features_json; |
| for (const auto& [target, features] : crubit_features) { |
| std::vector<llvm::json::Value> feature_array; |
| for (const std::string& feature : features) { |
| feature_array.push_back(feature); |
| } |
| features_json[target.value()] = std::move(feature_array); |
| } |
| |
| llvm::json::Object result{ |
| {"public_headers", public_headers}, |
| {"current_target", current_target}, |
| {"items", std::move(json_items)}, |
| {"top_level_item_ids", std::move(top_level_item_ids_json)}, |
| {"crubit_features", std::move(features_json)}, |
| }; |
| if (!crate_root_path.empty()) { |
| result["crate_root_path"] = crate_root_path; |
| } |
| return std::move(result); |
| } |
| |
| std::string ItemToString(const IR::Item& item) { |
| return std::visit( |
| [&](auto&& item) { return llvm::formatv("{0}", item.ToJson()); }, item); |
| } |
| |
| void SetMustBindItem(IR::Item& item) { |
| // All IR::Item variants have a `must_bind` field. |
| std::visit([](auto& item_variant) { item_variant.must_bind = true; }, item); |
| } |
| |
| } // namespace crubit |