| //===- 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, ArgVector Args, |
| ImporterConstructor C, |
| ASTImporter::ODRHandlingType ODRHandling) |
| : Code(Code), FileName(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(Language ToLang, StringRef ToSrcCode, |
| StringRef FileName) { |
| if (ToAST) |
| return; |
| ArgVector ToArgs = getArgVectorForLanguage(ToLang); |
| // Source code must be a valid live buffer through the tests lifetime. |
| ToCode = 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, Language FromLang, |
| StringRef ToSrcCode, Language ToLang, |
| StringRef Identifier) { |
| ArgVector FromArgs = getArgVectorForLanguage(FromLang), |
| ToArgs = getArgVectorForLanguage(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, |
| Language Lang, |
| StringRef FileName) { |
| assert(llvm::find_if(FromTUs, [FileName](const TU &E) { |
| return E.FileName == FileName; |
| }) == FromTUs.end()); |
| |
| ArgVector Args = getArgVectorForLanguage(Lang); |
| FromTUs.emplace_back(SrcCode, FileName, Args, Creator, ODRHandling); |
| TU &Tu = FromTUs.back(); |
| |
| return Tu.TUDecl; |
| } |
| |
| TranslationUnitDecl *ASTImporterTestBase::getToTuDecl(StringRef ToSrcCode, |
| Language ToLang) { |
| ArgVector ToArgs = getArgVectorForLanguage(ToLang); |
| assert(!ToAST); |
| lazyInitToAST(ToLang, ToSrcCode, OutputFileName); |
| return ToAST->getASTContext().getTranslationUnitDecl(); |
| } |
| |
| Decl *ASTImporterTestBase::Import(Decl *From, Language 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, |
| Language 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, |
| Language 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 |