blob: 1287d7554f5e95ceccbf893a22902b398c08441d [file] [log] [blame] [edit]
// 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 <string>
#include <utility>
#include <vector>
#include "absl/algorithm/container.h"
#include "absl/status/statusor.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, std::nullopt,
FormattedError::Static("Unnamed enums are not supported yet"));
}
absl::StatusOr<TranslatedIdentifier> enum_name =
ictx_.GetTranslatedIdentifier(enum_decl);
if (!enum_name.ok()) {
return ictx_.ImportUnsupportedItem(
*enum_decl, std::nullopt,
FormattedError::PrefixedStrCat("Enum name is not supported",
enum_name.status().message()));
}
auto enclosing_item_id = ictx_.GetEnclosingItemId(enum_decl);
if (!enclosing_item_id.ok()) {
return ictx_.ImportUnsupportedItem(
*enum_decl, std::nullopt,
FormattedError::FromStatus(std::move(enclosing_item_id.status())));
}
// Reports an unsupported enum with the given error.
//
// This is preferred to invoking `ImportUnsupportedItem` directly because it
// ensures that the path is set correctly. Note that this cannot be used above
// because the enclosing item ID and translated name are not yet available.
auto unsupported = [this, &enum_name, &enclosing_item_id,
enum_decl](FormattedError error) {
return ictx_.ImportUnsupportedItem(
*enum_decl,
UnsupportedItem::Path{.ident = (*enum_name).rs_identifier(),
.enclosing_item_id = *enclosing_item_id},
error);
};
clang::QualType cpp_type = enum_decl->getIntegerType();
if (cpp_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 unsupported(
FormattedError::Static("Forward declared enums without type "
"specifiers are not supported"));
}
const clang::tidy::lifetimes::ValueLifetimes* no_lifetimes = nullptr;
absl::StatusOr<CcType> type = ictx_.ConvertQualType(cpp_type, no_lifetimes);
if (!type.ok()) {
return unsupported(FormattedError::FromStatus(std::move(type.status())));
}
std::vector<Enumerator> enumerators;
enumerators.reserve(absl::c_distance(enum_decl->enumerators()));
for (clang::EnumConstantDecl* enumerator : enum_decl->enumerators()) {
absl::StatusOr<TranslatedIdentifier> enumerator_name =
ictx_.GetTranslatedIdentifier(enumerator);
if (!enumerator_name.ok()) {
// It's not clear that this case is possible
return unsupported(
FormattedError::PrefixedStrCat("Enumerator name is not supported",
enumerator_name.status().message()));
}
absl::StatusOr<std::optional<std::string>> unknown_attr =
CollectUnknownAttrs(*enumerator);
if (!unknown_attr.ok()) {
return unsupported(
FormattedError::FromStatus(std::move(unknown_attr.status())));
}
enumerators.push_back(Enumerator{
.identifier = (*enumerator_name).rs_identifier(),
.value = IntegerConstant(enumerator->getInitVal()),
.unknown_attr = std::move(*unknown_attr),
});
}
absl::StatusOr<std::optional<std::string>> unknown_attr =
CollectUnknownAttrs(*enum_decl);
if (!unknown_attr.ok()) {
return unsupported(
FormattedError::FromStatus(std::move(unknown_attr.status())));
}
if (ictx_.IsFromProtoTarget(*enum_decl)) {
// TODO(b/406221412): Proto enums aren't at the expected location.
return unsupported(
FormattedError::Static("b/406221412: Proto enums are not supported"));
}
ictx_.MarkAsSuccessfullyImported(enum_decl);
return Enum{
.cc_name = (*enum_name).cc_identifier,
.rs_name = (*enum_name).rs_identifier(),
.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 = std::move(*unknown_attr),
.enclosing_item_id = *std::move(enclosing_item_id),
};
}
} // namespace crubit