blob: 13fe0caf185d1122914db5830f927ffdfd115c25 [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
#ifndef CRUBIT_RS_BINDINGS_FROM_CC_DECL_IMPORTER_H_
#define CRUBIT_RS_BINDINGS_FROM_CC_DECL_IMPORTER_H_
#include "absl/container/flat_hash_map.h"
#include "absl/log/check.h"
#include "absl/status/statusor.h"
#include "lifetime_annotations/lifetime_annotations.h"
#include "rs_bindings_from_cc/bazel_types.h"
#include "rs_bindings_from_cc/ir.h"
namespace crubit {
// Top-level parameters as well as return value of an importer invocation.
class Invocation {
public:
Invocation(BazelLabel target, absl::Span<const HeaderName> entry_headers,
const absl::flat_hash_map<const HeaderName, const BazelLabel>&
header_targets)
: target_(target),
entry_headers_(entry_headers),
lifetime_context_(std::make_shared<
clang::tidy::lifetimes::LifetimeAnnotationContext>()),
header_targets_(header_targets) {
// Caller should verify that the inputs are non-empty.
CHECK(!entry_headers_.empty());
CHECK(!header_targets_.empty());
ir_.used_headers.insert(ir_.used_headers.end(), entry_headers_.begin(),
entry_headers.end());
ir_.current_target = target_;
}
// Returns the target of a header, if any.
std::optional<BazelLabel> header_target(const HeaderName header) const {
auto it = header_targets_.find(header);
return (it != header_targets_.end()) ? std::optional(it->second)
: std::nullopt;
}
// The main target from which we are importing.
const BazelLabel target_;
// The headers from which the import starts (a collection of
// paths in the format suitable for a google3-relative quote include).
const absl::Span<const HeaderName> entry_headers_;
const std::shared_ptr<clang::tidy::lifetimes::LifetimeAnnotationContext>
lifetime_context_;
// The main output of the import process
IR ir_;
private:
const absl::flat_hash_map<const HeaderName, const BazelLabel>&
header_targets_;
};
// Converts primitive types like `std::usize` or `int64_t` into their Rust
// equivalents.
std::optional<absl::string_view> MapKnownCcTypeToRsType(
absl::string_view cc_type);
// Explicitly defined interface that defines how `DeclImporter`s are allowed to
// interface with the global state of the importer.
class ImportContext {
public:
ImportContext(Invocation& invocation, clang::ASTContext& ctx,
clang::Sema& sema)
: invocation_(invocation), ctx_(ctx), sema_(sema) {}
virtual ~ImportContext() {}
// Imports all decls contained in a `DeclContext`.
virtual void ImportDeclsFromDeclContext(
const clang::DeclContext* decl_context) = 0;
// Imports an unsupported item with a single error message.
virtual IR::Item ImportUnsupportedItem(const clang::Decl* decl,
std::string error) = 0;
// Imports an unsupported item with multiple error messages.
virtual IR::Item ImportUnsupportedItem(const clang::Decl* decl,
std::set<std::string> errors) = 0;
// Imports a decl and creates an IR item (or error messages). This allows
// importers to recursively delegate to other importers.
// Does not use or update the cache.
virtual std::optional<IR::Item> ImportDecl(clang::Decl* decl) = 0;
virtual std::optional<IR::Item> GetImportedItem(const clang::Decl* decl) = 0;
// Returns the item ids of the children and comments of the given decl in
// source order. This method assumes that the children decls have already been
// imported.
virtual std::vector<ItemId> GetItemIdsInSourceOrder(clang::Decl* decl) = 0;
// Mangles the name of a named decl.
virtual std::string GetMangledName(
const clang::NamedDecl* named_decl) const = 0;
// Returs the label of the target that contains a decl.
virtual BazelLabel GetOwningTarget(const clang::Decl* decl) const = 0;
// Checks if the given decl belongs to the current target. Does not look into
// other redeclarations of the decl.
virtual bool IsFromCurrentTarget(const clang::Decl* decl) const = 0;
// Gets an IR UnqualifiedIdentifier for the named decl.
//
// If the decl's name is an identifier, this returns that identifier as-is.
//
// If the decl is a special member function or operator overload, this returns
// a SpecialName.
//
// If the translated name is not yet implemented, this returns null.
virtual std::optional<UnqualifiedIdentifier> GetTranslatedName(
const clang::NamedDecl* named_decl) const = 0;
// GetTranslatedName, but only for identifier names. This is the common case.
virtual std::optional<Identifier> GetTranslatedIdentifier(
const clang::NamedDecl* named_decl) const = 0;
// Gets the doc comment of the declaration.
virtual llvm::Optional<std::string> GetComment(
const clang::Decl* decl) const = 0;
// Converts a Clang source location to IR.
virtual SourceLoc ConvertSourceLocation(clang::SourceLocation loc) const = 0;
// Converts the Clang type `qual_type` into an equivalent `MappedType`.
// Lifetimes for the type can optionally be specified using `lifetimes`.
// If `qual_type` is a pointer type, `nullable` specifies whether the
// pointer can be null.
// TODO(b/209390498): Currently, we're able to specify nullability only for
// top-level pointers. Extend this so that we can specify nullability for
// all pointers contained in `qual_type`, in the same way that `lifetimes`
// specifies lifetimes for all these pointers. Once this is done, make sure
// that all callers pass in the appropriate information, derived from
// nullability annotations.
virtual absl::StatusOr<MappedType> ConvertQualType(
clang::QualType qual_type,
std::optional<clang::tidy::lifetimes::ValueLifetimes>& lifetimes,
bool nullable = true) = 0;
// Marks `decl` as successfully imported. Other pieces of code can check
// HasBeenAlreadySuccessfullyImported to avoid introducing dangling ItemIds
// that refer to an unimportable `decl`.
virtual void MarkAsSuccessfullyImported(const clang::TypeDecl* decl) = 0;
// Returns whether the `decl` has been already successfully imported (maybe
// partially - e.g. CXXRecordDeclImporter::Import marks the import as success
// before importing the fields, because the latter cannot fail). See also
// MarkAsSuccessfullyImported.
virtual bool HasBeenAlreadySuccessfullyImported(
const clang::TypeDecl* decl) const = 0;
// Adds an asssociation from anonymous structs/unionts to their typedef names.
virtual void AddAnonDeclTypedefName(clang::Decl* record,
absl::string_view name) = 0;
Invocation& invocation_;
clang::ASTContext& ctx_;
clang::Sema& sema_;
};
// Interface for components that can import decls of a certain category.
class DeclImporter {
public:
DeclImporter(ImportContext& ictx) : ictx_(ictx) {}
virtual ~DeclImporter() {}
// Determines whether this importer is autoritative for a decl. This does not
// imply that the import will be succesful.
virtual bool CanImport(clang::Decl*) = 0;
// Returns an IR item for a decl, or `std::nullopt` if importing failed.
// This member function may only be called after `CanImport` returned `true`.
virtual std::optional<IR::Item> ImportDecl(clang::Decl*) = 0;
protected:
ImportContext& ictx_;
};
// Common implementation for defining `DeclImporter`s that determine their
// applicability by the dynamic type of the decl.
template <typename D>
class DeclImporterBase : public DeclImporter {
public:
DeclImporterBase(ImportContext& context) : DeclImporter(context) {}
protected:
bool CanImport(clang::Decl* decl) { return clang::isa<D>(decl); }
std::optional<IR::Item> ImportDecl(clang::Decl* decl) {
return Import(clang::cast<D>(decl));
}
virtual std::optional<IR::Item> Import(D*) = 0;
};
} // namespace crubit
#endif // CRUBIT_RS_BINDINGS_FROM_CC_DECL_IMPORTER_H_