238 lines · cpp
1//===- unittest/AST/ASTImporterFixtures.cpp - AST unit test support -------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9/// \file10/// Implementation of fixture classes for testing the ASTImporter.11//12//===----------------------------------------------------------------------===//13 14#include "ASTImporterFixtures.h"15 16#include "clang/AST/ASTImporter.h"17#include "clang/AST/ASTImporterSharedState.h"18#include "clang/Frontend/ASTUnit.h"19#include "clang/Tooling/Tooling.h"20 21namespace clang {22namespace ast_matchers {23 24void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,25 std::unique_ptr<llvm::MemoryBuffer> &&Buffer) {26 assert(ToAST);27 ASTContext &ToCtx = ToAST->getASTContext();28 auto *OFS = static_cast<llvm::vfs::OverlayFileSystem *>(29 &ToCtx.getSourceManager().getFileManager().getVirtualFileSystem());30 auto *MFS = static_cast<llvm::vfs::InMemoryFileSystem *>(31 OFS->overlays_begin()->get());32 MFS->addFile(FileName, 0, std::move(Buffer));33}34 35void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,36 StringRef Code) {37 return createVirtualFileIfNeeded(ToAST, FileName,38 llvm::MemoryBuffer::getMemBuffer(Code));39}40 41ASTImporterTestBase::TU::TU(StringRef Code, StringRef FileName,42 std::vector<std::string> Args,43 ImporterConstructor C,44 ASTImporter::ODRHandlingType ODRHandling)45 : Code(std::string(Code)), FileName(std::string(FileName)),46 Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args, this->FileName)),47 TUDecl(Unit->getASTContext().getTranslationUnitDecl()), Creator(C),48 ODRHandling(ODRHandling) {49 Unit->enableSourceFileDiagnostics();50 51 // If the test doesn't need a specific ASTImporter, we just create a52 // normal ASTImporter with it.53 if (!Creator)54 Creator = [](ASTContext &ToContext, FileManager &ToFileManager,55 ASTContext &FromContext, FileManager &FromFileManager,56 bool MinimalImport,57 const std::shared_ptr<ASTImporterSharedState> &SharedState) {58 return new ASTImporter(ToContext, ToFileManager, FromContext,59 FromFileManager, MinimalImport, SharedState);60 };61}62 63ASTImporterTestBase::TU::~TU() {}64 65void ASTImporterTestBase::TU::lazyInitImporter(66 const std::shared_ptr<ASTImporterSharedState> &SharedState,67 ASTUnit *ToAST) {68 assert(ToAST);69 if (!Importer) {70 Importer.reset(Creator(ToAST->getASTContext(), ToAST->getFileManager(),71 Unit->getASTContext(), Unit->getFileManager(), false,72 SharedState));73 Importer->setODRHandling(ODRHandling);74 }75 assert(&ToAST->getASTContext() == &Importer->getToContext());76 createVirtualFileIfNeeded(ToAST, FileName, Code);77}78 79Decl *ASTImporterTestBase::TU::import(80 const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,81 Decl *FromDecl) {82 lazyInitImporter(SharedState, ToAST);83 if (auto ImportedOrErr = Importer->Import(FromDecl))84 return *ImportedOrErr;85 else {86 llvm::consumeError(ImportedOrErr.takeError());87 return nullptr;88 }89}90 91llvm::Expected<Decl *> ASTImporterTestBase::TU::importOrError(92 const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,93 Decl *FromDecl) {94 lazyInitImporter(SharedState, ToAST);95 return Importer->Import(FromDecl);96}97 98QualType ASTImporterTestBase::TU::import(99 const std::shared_ptr<ASTImporterSharedState> &SharedState, ASTUnit *ToAST,100 QualType FromType) {101 lazyInitImporter(SharedState, ToAST);102 if (auto ImportedOrErr = Importer->Import(FromType))103 return *ImportedOrErr;104 else {105 llvm::consumeError(ImportedOrErr.takeError());106 return QualType{};107 }108}109 110void ASTImporterTestBase::lazyInitSharedState(TranslationUnitDecl *ToTU) {111 assert(ToTU);112 if (!SharedStatePtr)113 SharedStatePtr = std::make_shared<ASTImporterSharedState>(*ToTU);114}115 116void ASTImporterTestBase::lazyInitToAST(TestLanguage ToLang,117 StringRef ToSrcCode,118 StringRef FileName) {119 if (ToAST)120 return;121 std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(ToLang);122 // Source code must be a valid live buffer through the tests lifetime.123 ToCode = std::string(ToSrcCode);124 // Build the AST from an empty file.125 ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName);126 ToAST->enableSourceFileDiagnostics();127 lazyInitSharedState(ToAST->getASTContext().getTranslationUnitDecl());128}129 130ASTImporterTestBase::TU *ASTImporterTestBase::findFromTU(Decl *From) {131 // Create a virtual file in the To Ctx which corresponds to the file from132 // which we want to import the `From` Decl. Without this source locations133 // will be invalid in the ToCtx.134 auto It = llvm::find_if(FromTUs, [From](const TU &E) {135 return E.TUDecl == From->getTranslationUnitDecl();136 });137 assert(It != FromTUs.end());138 return &*It;139}140 141std::tuple<Decl *, Decl *> ASTImporterTestBase::getImportedDecl(142 StringRef FromSrcCode, TestLanguage FromLang, StringRef ToSrcCode,143 TestLanguage ToLang, StringRef Identifier) {144 std::vector<std::string> FromArgs = getCommandLineArgsForLanguage(FromLang);145 std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(ToLang);146 147 FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs, Creator,148 ODRHandling);149 TU &FromTU = FromTUs.back();150 151 assert(!ToAST);152 lazyInitToAST(ToLang, ToSrcCode, OutputFileName);153 154 ASTContext &FromCtx = FromTU.Unit->getASTContext();155 156 IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier);157 assert(ImportedII && "Declaration with the given identifier "158 "should be specified in test!");159 DeclarationName ImportDeclName(ImportedII);160 SmallVector<NamedDecl *, 1> FoundDecls;161 FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName,162 FoundDecls);163 164 assert(FoundDecls.size() == 1);165 166 Decl *Imported =167 FromTU.import(SharedStatePtr, ToAST.get(), FoundDecls.front());168 169 assert(Imported);170 return std::make_tuple(*FoundDecls.begin(), Imported);171}172 173TranslationUnitDecl *ASTImporterTestBase::getTuDecl(StringRef SrcCode,174 TestLanguage Lang,175 StringRef FileName) {176 assert(llvm::none_of(177 FromTUs, [FileName](const TU &E) { return E.FileName == FileName; }));178 179 std::vector<std::string> Args = getCommandLineArgsForLanguage(Lang);180 FromTUs.emplace_back(SrcCode, FileName, Args, Creator, ODRHandling);181 TU &Tu = FromTUs.back();182 183 return Tu.TUDecl;184}185 186TranslationUnitDecl *ASTImporterTestBase::getToTuDecl(StringRef ToSrcCode,187 TestLanguage ToLang) {188 std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(ToLang);189 assert(!ToAST);190 lazyInitToAST(ToLang, ToSrcCode, OutputFileName);191 return ToAST->getASTContext().getTranslationUnitDecl();192}193 194Decl *ASTImporterTestBase::Import(Decl *From, TestLanguage ToLang) {195 lazyInitToAST(ToLang, "", OutputFileName);196 TU *FromTU = findFromTU(From);197 assert(SharedStatePtr);198 Decl *To = FromTU->import(SharedStatePtr, ToAST.get(), From);199 return To;200}201 202llvm::Expected<Decl *> ASTImporterTestBase::importOrError(Decl *From,203 TestLanguage ToLang) {204 lazyInitToAST(ToLang, "", OutputFileName);205 TU *FromTU = findFromTU(From);206 assert(SharedStatePtr);207 llvm::Expected<Decl *> To =208 FromTU->importOrError(SharedStatePtr, ToAST.get(), From);209 return To;210}211 212QualType ASTImporterTestBase::ImportType(QualType FromType, Decl *TUDecl,213 TestLanguage ToLang) {214 lazyInitToAST(ToLang, "", OutputFileName);215 TU *FromTU = findFromTU(TUDecl);216 assert(SharedStatePtr);217 return FromTU->import(SharedStatePtr, ToAST.get(), FromType);218}219 220ASTImporterTestBase::~ASTImporterTestBase() {221 if (!::testing::Test::HasFailure())222 return;223 224 for (auto &Tu : FromTUs) {225 assert(Tu.Unit);226 llvm::errs() << "FromAST:\n";227 Tu.Unit->getASTContext().getTranslationUnitDecl()->dump();228 llvm::errs() << "\n";229 }230 if (ToAST) {231 llvm::errs() << "ToAST:\n";232 ToAST->getASTContext().getTranslationUnitDecl()->dump();233 }234}235 236} // end namespace ast_matchers237} // end namespace clang238