blob: 5026b3317c02e33586009562d4c5200dae79dc33 [file] [log] [blame]
// 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