| // 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/importers/enum.h" |
| |
| #include <optional> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/algorithm/container.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "lifetime_annotations/type_lifetimes.h" |
| #include "rs_bindings_from_cc/ast_util.h" |
| #include "rs_bindings_from_cc/ir.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/Type.h" |
| #include "clang/Basic/LLVM.h" |
| |
| namespace crubit { |
| |
| std::optional<IR::Item> EnumDeclImporter::Import(clang::EnumDecl* enum_decl) { |
| if (enum_decl->getName().empty()) { |
| // TODO(b/208945197): This corresponds to an unnamed enum declaration like |
| // `enum { kFoo = 1 }`, which only exists to provide constants into the |
| // surrounding scope and doesn't actually introduce an enum namespace. It |
| // seems like it should probably be handled with other constants. |
| return ictx_.ImportUnsupportedItem(enum_decl, |
| "Unnamed enums are not supported yet"); |
| } |
| absl::StatusOr<Identifier> enum_name = |
| ictx_.GetTranslatedIdentifier(enum_decl); |
| if (!enum_name.ok()) { |
| return ictx_.ImportUnsupportedItem( |
| enum_decl, absl::StrCat("Enum name is not supported: ", |
| enum_name.status().message())); |
| } |
| |
| clang::QualType cc_type = enum_decl->getIntegerType(); |
| if (cc_type.isNull()) { |
| // According to https://clang.llvm.org/doxygen/classclang_1_1EnumDecl.html, |
| // getIntegerType "returns a null QualType for an enum forward definition |
| // with no fixed underlying type." The same page implies that this can't |
| // occur in C++ nor in standard C, but clang supports enums like this |
| // in C "as an extension". |
| return ictx_.ImportUnsupportedItem( |
| enum_decl, |
| "Forward declared enums without type specifiers are not supported"); |
| } |
| const clang::tidy::lifetimes::ValueLifetimes* no_lifetimes = nullptr; |
| absl::StatusOr<MappedType> type = |
| ictx_.ConvertQualType(cc_type, no_lifetimes, std::nullopt); |
| if (!type.ok()) { |
| return ictx_.ImportUnsupportedItem(enum_decl, type.status().ToString()); |
| } |
| |
| std::vector<Enumerator> enumerators; |
| enumerators.reserve(absl::c_distance(enum_decl->enumerators())); |
| for (clang::EnumConstantDecl* enumerator : enum_decl->enumerators()) { |
| absl::StatusOr<Identifier> enumerator_name = |
| ictx_.GetTranslatedIdentifier(enumerator); |
| if (!enumerator_name.ok()) { |
| // It's not clear that this case is possible |
| return ictx_.ImportUnsupportedItem( |
| enum_decl, absl::StrCat("Enumerator name is not supported: ", |
| enumerator_name.status().message())); |
| } |
| |
| enumerators.push_back(Enumerator{ |
| .identifier = *enumerator_name, |
| .value = IntegerConstant(enumerator->getInitVal()), |
| .unknown_attr = CollectUnknownAttrs(*enumerator), |
| }); |
| } |
| |
| auto enclosing_item_id = ictx_.GetEnclosingItemId(enum_decl); |
| if (!enclosing_item_id.ok()) { |
| return ictx_.ImportUnsupportedItem( |
| enum_decl, std::string(enclosing_item_id.status().message())); |
| } |
| |
| ictx_.MarkAsSuccessfullyImported(enum_decl); |
| return Enum{ |
| .identifier = *enum_name, |
| .id = ictx_.GenerateItemId(enum_decl), |
| .owning_target = ictx_.GetOwningTarget(enum_decl), |
| .source_loc = ictx_.ConvertSourceLocation(enum_decl->getBeginLoc()), |
| .underlying_type = *std::move(type), |
| .enumerators = enum_decl->isCompleteDefinition() |
| ? std::make_optional(std::move(enumerators)) |
| : std::nullopt, |
| .unknown_attr = CollectUnknownAttrs(*enum_decl), |
| .enclosing_item_id = *std::move(enclosing_item_id), |
| }; |
| } |
| |
| } // namespace crubit |