blob: 3d3295a79f28fa26ffd99cd93b28422276a91363 [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/type_map_override.h"
#include <optional>
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "common/status_macros.h"
#include "rs_bindings_from_cc/ir.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Attrs.inc"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Type.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/StringRef.h"
namespace crubit {
namespace {
// Copied from lifetime_annotations/type_lifetimes.cc, which is expected to move
// into ClangTidy. See:
// https://discourse.llvm.org/t/rfc-lifetime-annotations-for-c/61377
absl::StatusOr<absl::string_view> EvaluateAsStringLiteral(
const clang::Expr& expr, const clang::ASTContext& ast_context) {
auto error = []() {
return absl::InvalidArgumentError(
"cannot evaluate argument as a string literal");
};
clang::Expr::EvalResult eval_result;
if (!expr.EvaluateAsConstantExpr(eval_result, ast_context) ||
!eval_result.Val.isLValue()) {
return error();
}
const auto* eval_result_expr =
eval_result.Val.getLValueBase().dyn_cast<const clang::Expr*>();
if (!eval_result_expr) {
return error();
}
const auto* string_literal =
clang::dyn_cast<clang::StringLiteral>(eval_result_expr);
if (!string_literal) {
return error();
}
return {string_literal->getString()};
}
// Gets the requested attribute for `decl`.
// `decl` must not be null.
absl::StatusOr<const clang::AnnotateAttr*> GetAnnotateAttr(
const clang::Decl* decl, absl::string_view attribute) {
const clang::AnnotateAttr* found_attr = nullptr;
for (clang::AnnotateAttr* attr :
decl->specific_attrs<clang::AnnotateAttr>()) {
if (attr->getAnnotation() != llvm::StringRef(attribute)) continue;
if (found_attr != nullptr)
return absl::InvalidArgumentError(
absl::StrCat("Only one `", attribute,
"` attribute may be placed on a declaration."));
found_attr = attr;
}
return found_attr;
}
// Gets the crubit_internal_rust_type attribute for `decl`.
// `decl` must not be null.
absl::StatusOr<std::optional<absl::string_view>> GetRustTypeAttribute(
const clang::Decl* decl) {
CRUBIT_ASSIGN_OR_RETURN(const clang::AnnotateAttr* attr,
GetAnnotateAttr(decl, "crubit_internal_rust_type"));
if (attr == nullptr) return std::nullopt;
if (attr->args_size() != 1)
return absl::InvalidArgumentError(
"The `crubit_internal_rust_type` attribute requires a single "
"string literal "
"argument, the Rust type.");
const clang::Expr& arg = **attr->args_begin();
return EvaluateAsStringLiteral(arg, decl->getASTContext());
}
// Gets the crubit_internal_same_abi attribute for `decl`.
// If the attribute is specified, returns true. If it's unspecified, returns
// false. If the attribute is malformed, returns a bad status.
//
// `decl` must not be null.
absl::StatusOr<bool> GetIsSameAbiAttribute(const clang::Decl* decl) {
CRUBIT_ASSIGN_OR_RETURN(const clang::AnnotateAttr* attr,
GetAnnotateAttr(decl, "crubit_internal_same_abi"));
if (attr != nullptr && attr->args_size() != 0)
return absl::InvalidArgumentError(
"The `crubit_internal_same_abi` attribute takes no arguments.");
return attr != nullptr;
}
} // namespace
std::optional<IR::Item> TypeMapOverrideImporter::Import(
clang::TypeDecl* type_decl) {
absl::StatusOr<std::optional<absl::string_view>> rust_type =
GetRustTypeAttribute(type_decl);
if (!rust_type.ok()) {
return ictx_.ImportUnsupportedItem(
type_decl, absl::StrCat("Invalid crubit_internal_rust_type attribute: ",
rust_type.status().message()));
}
if (!rust_type->has_value()) {
return std::nullopt;
}
absl::StatusOr<bool> is_same_abi = GetIsSameAbiAttribute(type_decl);
if (!is_same_abi.ok()) {
return ictx_.ImportUnsupportedItem(
type_decl,
absl::StrCat("Invalid crubit_internal_is_same_abi attribute: ",
is_same_abi.status().message()));
}
auto rs_name = std::string(**rust_type);
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 std::nullopt;
std::string cc_name = cc_qualtype.getAsString();
ictx_.MarkAsSuccessfullyImported(type_decl);
std::optional<SizeAlign> size_align;
if (!cpp_type->isIncompleteType()) {
size_align = SizeAlign{
.size = context.getTypeSizeInChars(cpp_type).getQuantity(),
.alignment = context.getTypeAlignInChars(cpp_type).getQuantity(),
};
}
return TypeMapOverride{
.rs_name = std::move(rs_name),
.cc_name = std::move(cc_name),
.owning_target = ictx_.GetOwningTarget(type_decl),
.size_align = std::move(size_align),
.is_same_abi = *is_same_abi,
.id = ictx_.GenerateItemId(type_decl),
};
}
} // namespace crubit