239 lines
8.6 KiB
C++
239 lines
8.6 KiB
C++
|
//===- unittest/AST/ASTImporterFixtures.cpp - AST unit test support -------===//
|
||
|
//
|
||
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||
|
// See https://llvm.org/LICENSE.txt for license information.
|
||
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
//
|
||
|
/// \file
|
||
|
/// Implementation of fixture classes for testing the ASTImporter.
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "ASTImporterFixtures.h"
|
||
|
|
||
|
#include "clang/AST/ASTImporter.h"
|
||
|
#include "clang/AST/ASTImporterSharedState.h"
|
||
|
#include "clang/Frontend/ASTUnit.h"
|
||
|
#include "clang/Tooling/Tooling.h"
|
||
|
|
||
|
namespace clang {
|
||
|
namespace ast_matchers {
|
||
|
|
||
|
void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
|
||
|
std::unique_ptr<llvm::MemoryBuffer> &&Buffer) {
|
||
|
assert(ToAST);
|
||
|
ASTContext &ToCtx = ToAST->getASTContext();
|
||
|
auto *OFS = static_cast<llvm::vfs::OverlayFileSystem *>(
|
||
|
&ToCtx.getSourceManager().getFileManager().getVirtualFileSystem());
|
||
|
auto *MFS = static_cast<llvm::vfs::InMemoryFileSystem *>(
|
||
|
OFS->overlays_begin()->get());
|
||
|
MFS->addFile(FileName, 0, std::move(Buffer));
|
||
|
}
|
||
|
|
||
|
void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
|
||
|
StringRef Code) {
|
||
|
return createVirtualFileIfNeeded(ToAST, FileName,
|
||
|
llvm::MemoryBuffer::getMemBuffer(Code));
|
||
|
}
|
||
|
|
||
|
ASTImporterTestBase::TU::TU(StringRef Code, StringRef FileName,
|
||
|
std::vector<std::string> Args,
|
||
|
ImporterConstructor C,
|
||
|
ASTImporter::ODRHandlingType ODRHandling)
|
||
|
: Code(std::string(Code)), FileName(std::string(FileName)),
|
||
|
Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args, this->FileName)),
|
||
|
TUDecl(Unit->getASTContext().getTranslationUnitDecl()), Creator(C),
|
||
|
ODRHandling(ODRHandling) {
|
||
|
Unit->enableSourceFileDiagnostics();
|
||
|
|
||
|
// If the test doesn't need a specific ASTImporter, we just create a
|
||
|
// normal ASTImporter with it.
|
||
|
if (!Creator)
|
||
|
Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
|
||
|
ASTContext &FromContext, FileManager &FromFileManager,
|
||
|
bool MinimalImport,
|
||
|
const std::shared_ptr<ASTImporterSharedState> &SharedState) {
|
||
|
return new ASTImporter(ToContext, ToFileManager, FromContext,
|
||
|
FromFileManager, MinimalImport, SharedState);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
ASTImporterTestBase::TU::~TU() {}
|
||
|
|
||
|
void ASTImporterTestBase::TU::lazyInitImporter(
|
||
|
const std::shared_ptr<ASTImporterSharedState> &SharedState,
|
||
|
ASTUnit *ToAST) {
|
||
|
assert(ToAST);
|
||
|
if (!Importer) {
|
||
|
Importer.reset(Creator(ToAST->getASTContext(), ToAST->getFileManager(),
|
||
|
Unit->getASTContext(), Unit->getFileManager(), false,
|
||
|
SharedState));
|
||
|
Importer->setODRHandling(ODRHandling);
|
||
|
}
|
||
|
assert(&ToAST->getASTContext() == &Importer->getToContext());
|
||
|
createVirtualFileIfNeeded(ToAST, FileName, Code);
|
||
|
}
|
||
|
|
||
|
Decl *ASTImporterTestBase::TU::import(
|
||
|
const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,
|
||
|
Decl *FromDecl) {
|
||
|
lazyInitImporter(SharedState, ToAST);
|
||
|
if (auto ImportedOrErr = Importer->Import(FromDecl))
|
||
|
return *ImportedOrErr;
|
||
|
else {
|
||
|
llvm::consumeError(ImportedOrErr.takeError());
|
||
|
return nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
llvm::Expected<Decl *> ASTImporterTestBase::TU::importOrError(
|
||
|
const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,
|
||
|
Decl *FromDecl) {
|
||
|
lazyInitImporter(SharedState, ToAST);
|
||
|
return Importer->Import(FromDecl);
|
||
|
}
|
||
|
|
||
|
QualType ASTImporterTestBase::TU::import(
|
||
|
const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,
|
||
|
QualType FromType) {
|
||
|
lazyInitImporter(SharedState, ToAST);
|
||
|
if (auto ImportedOrErr = Importer->Import(FromType))
|
||
|
return *ImportedOrErr;
|
||
|
else {
|
||
|
llvm::consumeError(ImportedOrErr.takeError());
|
||
|
return QualType{};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ASTImporterTestBase::lazyInitSharedState(TranslationUnitDecl *ToTU) {
|
||
|
assert(ToTU);
|
||
|
if (!SharedStatePtr)
|
||
|
SharedStatePtr = std::make_shared<ASTImporterSharedState>(*ToTU);
|
||
|
}
|
||
|
|
||
|
void ASTImporterTestBase::lazyInitToAST(TestLanguage ToLang,
|
||
|
StringRef ToSrcCode,
|
||
|
StringRef FileName) {
|
||
|
if (ToAST)
|
||
|
return;
|
||
|
std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(ToLang);
|
||
|
// Source code must be a valid live buffer through the tests lifetime.
|
||
|
ToCode = std::string(ToSrcCode);
|
||
|
// Build the AST from an empty file.
|
||
|
ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName);
|
||
|
ToAST->enableSourceFileDiagnostics();
|
||
|
lazyInitSharedState(ToAST->getASTContext().getTranslationUnitDecl());
|
||
|
}
|
||
|
|
||
|
ASTImporterTestBase::TU *ASTImporterTestBase::findFromTU(Decl *From) {
|
||
|
// Create a virtual file in the To Ctx which corresponds to the file from
|
||
|
// which we want to import the `From` Decl. Without this source locations
|
||
|
// will be invalid in the ToCtx.
|
||
|
auto It = llvm::find_if(FromTUs, [From](const TU &E) {
|
||
|
return E.TUDecl == From->getTranslationUnitDecl();
|
||
|
});
|
||
|
assert(It != FromTUs.end());
|
||
|
return &*It;
|
||
|
}
|
||
|
|
||
|
std::tuple<Decl *, Decl *> ASTImporterTestBase::getImportedDecl(
|
||
|
StringRef FromSrcCode, TestLanguage FromLang, StringRef ToSrcCode,
|
||
|
TestLanguage ToLang, StringRef Identifier) {
|
||
|
std::vector<std::string> FromArgs = getCommandLineArgsForLanguage(FromLang);
|
||
|
std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(ToLang);
|
||
|
|
||
|
FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs, Creator,
|
||
|
ODRHandling);
|
||
|
TU &FromTU = FromTUs.back();
|
||
|
|
||
|
assert(!ToAST);
|
||
|
lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
|
||
|
|
||
|
ASTContext &FromCtx = FromTU.Unit->getASTContext();
|
||
|
|
||
|
IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier);
|
||
|
assert(ImportedII && "Declaration with the given identifier "
|
||
|
"should be specified in test!");
|
||
|
DeclarationName ImportDeclName(ImportedII);
|
||
|
SmallVector<NamedDecl *, 1> FoundDecls;
|
||
|
FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName,
|
||
|
FoundDecls);
|
||
|
|
||
|
assert(FoundDecls.size() == 1);
|
||
|
|
||
|
Decl *Imported =
|
||
|
FromTU.import(SharedStatePtr, ToAST.get(), FoundDecls.front());
|
||
|
|
||
|
assert(Imported);
|
||
|
return std::make_tuple(*FoundDecls.begin(), Imported);
|
||
|
}
|
||
|
|
||
|
TranslationUnitDecl *ASTImporterTestBase::getTuDecl(StringRef SrcCode,
|
||
|
TestLanguage Lang,
|
||
|
StringRef FileName) {
|
||
|
assert(llvm::find_if(FromTUs, [FileName](const TU &E) {
|
||
|
return E.FileName == FileName;
|
||
|
}) == FromTUs.end());
|
||
|
|
||
|
std::vector<std::string> Args = getCommandLineArgsForLanguage(Lang);
|
||
|
FromTUs.emplace_back(SrcCode, FileName, Args, Creator, ODRHandling);
|
||
|
TU &Tu = FromTUs.back();
|
||
|
|
||
|
return Tu.TUDecl;
|
||
|
}
|
||
|
|
||
|
TranslationUnitDecl *ASTImporterTestBase::getToTuDecl(StringRef ToSrcCode,
|
||
|
TestLanguage ToLang) {
|
||
|
std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(ToLang);
|
||
|
assert(!ToAST);
|
||
|
lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
|
||
|
return ToAST->getASTContext().getTranslationUnitDecl();
|
||
|
}
|
||
|
|
||
|
Decl *ASTImporterTestBase::Import(Decl *From, TestLanguage ToLang) {
|
||
|
lazyInitToAST(ToLang, "", OutputFileName);
|
||
|
TU *FromTU = findFromTU(From);
|
||
|
assert(SharedStatePtr);
|
||
|
Decl *To = FromTU->import(SharedStatePtr, ToAST.get(), From);
|
||
|
return To;
|
||
|
}
|
||
|
|
||
|
llvm::Expected<Decl *> ASTImporterTestBase::importOrError(Decl *From,
|
||
|
TestLanguage ToLang) {
|
||
|
lazyInitToAST(ToLang, "", OutputFileName);
|
||
|
TU *FromTU = findFromTU(From);
|
||
|
assert(SharedStatePtr);
|
||
|
llvm::Expected<Decl *> To =
|
||
|
FromTU->importOrError(SharedStatePtr, ToAST.get(), From);
|
||
|
return To;
|
||
|
}
|
||
|
|
||
|
QualType ASTImporterTestBase::ImportType(QualType FromType, Decl *TUDecl,
|
||
|
TestLanguage ToLang) {
|
||
|
lazyInitToAST(ToLang, "", OutputFileName);
|
||
|
TU *FromTU = findFromTU(TUDecl);
|
||
|
assert(SharedStatePtr);
|
||
|
return FromTU->import(SharedStatePtr, ToAST.get(), FromType);
|
||
|
}
|
||
|
|
||
|
ASTImporterTestBase::~ASTImporterTestBase() {
|
||
|
if (!::testing::Test::HasFailure())
|
||
|
return;
|
||
|
|
||
|
for (auto &Tu : FromTUs) {
|
||
|
assert(Tu.Unit);
|
||
|
llvm::errs() << "FromAST:\n";
|
||
|
Tu.Unit->getASTContext().getTranslationUnitDecl()->dump();
|
||
|
llvm::errs() << "\n";
|
||
|
}
|
||
|
if (ToAST) {
|
||
|
llvm::errs() << "ToAST:\n";
|
||
|
ToAST->getASTContext().getTranslationUnitDecl()->dump();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // end namespace ast_matchers
|
||
|
} // end namespace clang
|