#include "Plugins/ExpressionParser/Clang/CxxModuleHandler.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "clang/Sema/Lookup.h"
#include "llvm/Support/Error.h"
#include <optional>
using namespace lldb_private;
using namespace clang;
CxxModuleHandler::CxxModuleHandler(ASTImporter &importer, ASTContext *target)
: m_importer(&importer),
m_sema(TypeSystemClang::GetASTContext(target)->getSema()) {
std::initializer_list<const char *> supported_names = {
"array",
"deque",
"forward_list",
"list",
"queue",
"stack",
"vector",
"shared_ptr",
"unique_ptr",
"weak_ptr",
"move_iterator",
"__wrap_iter",
"allocator",
"pair",
};
m_supported_templates.insert(supported_names.begin(), supported_names.end());
}
static void makeScopes(Sema &sema, DeclContext *ctxt,
std::vector<Scope *> &result) {
if (auto parent = ctxt->getParent()) {
makeScopes(sema, parent, result);
Scope *scope =
new Scope(result.back(), Scope::DeclScope, sema.getDiagnostics());
scope->setEntity(ctxt);
result.push_back(scope);
} else
result.push_back(sema.TUScope);
}
static std::unique_ptr<LookupResult>
emulateLookupInCtxt(Sema &sema, llvm::StringRef name, DeclContext *ctxt) {
IdentifierInfo &ident = sema.getASTContext().Idents.get(name);
std::unique_ptr<LookupResult> lookup_result;
lookup_result = std::make_unique<LookupResult>(sema, DeclarationName(&ident),
SourceLocation(),
Sema::LookupOrdinaryName);
std::vector<Scope *> scopes;
makeScopes(sema, ctxt, scopes);
sema.LookupName(*lookup_result, scopes.back());
for (Scope *s : scopes)
if (s->getDepth() != 0)
delete s;
return lookup_result;
}
struct MissingDeclContext : public llvm::ErrorInfo<MissingDeclContext> {
static char ID;
MissingDeclContext(DeclContext *context, std::string error)
: m_context(context), m_error(error) {}
DeclContext *m_context;
std::string m_error;
void log(llvm::raw_ostream &OS) const override {
OS << llvm::formatv("error when reconstructing context of kind {0}:{1}",
m_context->getDeclKindName(), m_error);
}
std::error_code convertToErrorCode() const override {
return llvm::inconvertibleErrorCode();
}
};
char MissingDeclContext::ID = 0;
static llvm::Expected<DeclContext *>
getEqualLocalDeclContext(Sema &sema, DeclContext *foreign_ctxt) {
while (foreign_ctxt && foreign_ctxt->isInlineNamespace())
foreign_ctxt = foreign_ctxt->getParent();
if (foreign_ctxt->isTranslationUnit())
return sema.getASTContext().getTranslationUnitDecl();
llvm::Expected<DeclContext *> parent =
getEqualLocalDeclContext(sema, foreign_ctxt->getParent());
if (!parent)
return parent;
if (foreign_ctxt->isNamespace()) {
NamedDecl *ns = llvm::cast<NamedDecl>(foreign_ctxt);
llvm::StringRef ns_name = ns->getName();
auto lookup_result = emulateLookupInCtxt(sema, ns_name, *parent);
for (NamedDecl *named_decl : *lookup_result) {
if (DeclContext *DC = llvm::dyn_cast<DeclContext>(named_decl))
return DC->getPrimaryContext();
}
return llvm::make_error<MissingDeclContext>(
foreign_ctxt,
"Couldn't find namespace " + ns->getQualifiedNameAsString());
}
return llvm::make_error<MissingDeclContext>(foreign_ctxt, "Unknown context ");
}
static bool templateArgsAreSupported(ArrayRef<TemplateArgument> a) {
for (const TemplateArgument &arg : a) {
switch (arg.getKind()) {
case TemplateArgument::Type:
case TemplateArgument::Integral:
break;
default:
return false;
}
}
return true;
}
template <typename T, typename... Args>
T *createDecl(ASTImporter &importer, Decl *from_d, Args &&... args) {
T *to_d = T::Create(std::forward<Args>(args)...);
importer.RegisterImportedDecl(from_d, to_d);
return to_d;
}
std::optional<Decl *> CxxModuleHandler::tryInstantiateStdTemplate(Decl *d) {
Log *log = GetLog(LLDBLog::Expressions);
auto td = dyn_cast<ClassTemplateSpecializationDecl>(d);
if (!td)
return std::nullopt;
if (!td->getDeclContext()->isStdNamespace())
return std::nullopt;
if (!m_supported_templates.contains(td->getName()))
return std::nullopt;
auto &foreign_args = td->getTemplateInstantiationArgs();
if (!templateArgsAreSupported(foreign_args.asArray()))
return std::nullopt;
llvm::Expected<DeclContext *> to_context =
getEqualLocalDeclContext(*m_sema, td->getDeclContext());
if (!to_context) {
LLDB_LOG_ERROR(log, to_context.takeError(),
"Got error while searching equal local DeclContext for decl "
"'{1}':\n{0}",
td->getName());
return std::nullopt;
}
std::unique_ptr<LookupResult> lookup =
emulateLookupInCtxt(*m_sema, td->getName(), *to_context);
ClassTemplateDecl *new_class_template = nullptr;
for (auto LD : *lookup) {
if ((new_class_template = dyn_cast<ClassTemplateDecl>(LD)))
break;
}
if (!new_class_template)
return std::nullopt;
llvm::SmallVector<TemplateArgument, 4> imported_args;
for (const TemplateArgument &arg : foreign_args.asArray()) {
switch (arg.getKind()) {
case TemplateArgument::Type: {
llvm::Expected<QualType> type = m_importer->Import(arg.getAsType());
if (!type) {
LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
return std::nullopt;
}
imported_args.push_back(
TemplateArgument(*type, false, arg.getIsDefaulted()));
break;
}
case TemplateArgument::Integral: {
llvm::APSInt integral = arg.getAsIntegral();
llvm::Expected<QualType> type =
m_importer->Import(arg.getIntegralType());
if (!type) {
LLDB_LOG_ERROR(log, type.takeError(), "Couldn't import type: {0}");
return std::nullopt;
}
imported_args.push_back(TemplateArgument(d->getASTContext(), integral,
*type, arg.getIsDefaulted()));
break;
}
default:
assert(false && "templateArgsAreSupported not updated?");
}
}
void *InsertPos = nullptr;
ClassTemplateSpecializationDecl *result =
new_class_template->findSpecialization(imported_args, InsertPos);
if (result) {
m_importer->RegisterImportedDecl(d, result);
return result;
}
result = createDecl<ClassTemplateSpecializationDecl>(
*m_importer, d, m_sema->getASTContext(),
new_class_template->getTemplatedDecl()->getTagKind(),
new_class_template->getDeclContext(),
new_class_template->getTemplatedDecl()->getLocation(),
new_class_template->getLocation(), new_class_template, imported_args,
nullptr);
new_class_template->AddSpecialization(result, InsertPos);
if (new_class_template->isOutOfLine())
result->setLexicalDeclContext(
new_class_template->getLexicalDeclContext());
return result;
}
std::optional<Decl *> CxxModuleHandler::Import(Decl *d) {
if (!isValid())
return {};
return tryInstantiateStdTemplate(d);
}