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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 /// \file
10 /// 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
21 namespace clang {
22 namespace ast_matchers {
23
createVirtualFileIfNeeded(ASTUnit * ToAST,StringRef FileName,std::unique_ptr<llvm::MemoryBuffer> && Buffer)24 void 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
createVirtualFileIfNeeded(ASTUnit * ToAST,StringRef FileName,StringRef Code)35 void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
36 StringRef Code) {
37 return createVirtualFileIfNeeded(ToAST, FileName,
38 llvm::MemoryBuffer::getMemBuffer(Code));
39 }
40
TU(StringRef Code,StringRef FileName,std::vector<std::string> Args,ImporterConstructor C,ASTImporter::ODRHandlingType ODRHandling)41 ASTImporterTestBase::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 a
52 // 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
~TU()63 ASTImporterTestBase::TU::~TU() {}
64
lazyInitImporter(const std::shared_ptr<ASTImporterSharedState> & SharedState,ASTUnit * ToAST)65 void 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
import(const std::shared_ptr<ASTImporterSharedState> & SharedState,ASTUnit * ToAST,Decl * FromDecl)79 Decl *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
importOrError(const std::shared_ptr<ASTImporterSharedState> & SharedState,ASTUnit * ToAST,Decl * FromDecl)91 llvm::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
import(const std::shared_ptr<ASTImporterSharedState> & SharedState,ASTUnit * ToAST,QualType FromType)98 QualType 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
lazyInitSharedState(TranslationUnitDecl * ToTU)110 void ASTImporterTestBase::lazyInitSharedState(TranslationUnitDecl *ToTU) {
111 assert(ToTU);
112 if (!SharedStatePtr)
113 SharedStatePtr = std::make_shared<ASTImporterSharedState>(*ToTU);
114 }
115
lazyInitToAST(TestLanguage ToLang,StringRef ToSrcCode,StringRef FileName)116 void 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
findFromTU(Decl * From)130 ASTImporterTestBase::TU *ASTImporterTestBase::findFromTU(Decl *From) {
131 // Create a virtual file in the To Ctx which corresponds to the file from
132 // which we want to import the `From` Decl. Without this source locations
133 // 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
getImportedDecl(StringRef FromSrcCode,TestLanguage FromLang,StringRef ToSrcCode,TestLanguage ToLang,StringRef Identifier)141 std::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
getTuDecl(StringRef SrcCode,TestLanguage Lang,StringRef FileName)173 TranslationUnitDecl *ASTImporterTestBase::getTuDecl(StringRef SrcCode,
174 TestLanguage Lang,
175 StringRef FileName) {
176 assert(llvm::find_if(FromTUs, [FileName](const TU &E) {
177 return E.FileName == FileName;
178 }) == FromTUs.end());
179
180 std::vector<std::string> Args = getCommandLineArgsForLanguage(Lang);
181 FromTUs.emplace_back(SrcCode, FileName, Args, Creator, ODRHandling);
182 TU &Tu = FromTUs.back();
183
184 return Tu.TUDecl;
185 }
186
getToTuDecl(StringRef ToSrcCode,TestLanguage ToLang)187 TranslationUnitDecl *ASTImporterTestBase::getToTuDecl(StringRef ToSrcCode,
188 TestLanguage ToLang) {
189 std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(ToLang);
190 assert(!ToAST);
191 lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
192 return ToAST->getASTContext().getTranslationUnitDecl();
193 }
194
Import(Decl * From,TestLanguage ToLang)195 Decl *ASTImporterTestBase::Import(Decl *From, TestLanguage ToLang) {
196 lazyInitToAST(ToLang, "", OutputFileName);
197 TU *FromTU = findFromTU(From);
198 assert(SharedStatePtr);
199 Decl *To = FromTU->import(SharedStatePtr, ToAST.get(), From);
200 return To;
201 }
202
importOrError(Decl * From,TestLanguage ToLang)203 llvm::Expected<Decl *> ASTImporterTestBase::importOrError(Decl *From,
204 TestLanguage ToLang) {
205 lazyInitToAST(ToLang, "", OutputFileName);
206 TU *FromTU = findFromTU(From);
207 assert(SharedStatePtr);
208 llvm::Expected<Decl *> To =
209 FromTU->importOrError(SharedStatePtr, ToAST.get(), From);
210 return To;
211 }
212
ImportType(QualType FromType,Decl * TUDecl,TestLanguage ToLang)213 QualType ASTImporterTestBase::ImportType(QualType FromType, Decl *TUDecl,
214 TestLanguage ToLang) {
215 lazyInitToAST(ToLang, "", OutputFileName);
216 TU *FromTU = findFromTU(TUDecl);
217 assert(SharedStatePtr);
218 return FromTU->import(SharedStatePtr, ToAST.get(), FromType);
219 }
220
~ASTImporterTestBase()221 ASTImporterTestBase::~ASTImporterTestBase() {
222 if (!::testing::Test::HasFailure())
223 return;
224
225 for (auto &Tu : FromTUs) {
226 assert(Tu.Unit);
227 llvm::errs() << "FromAST:\n";
228 Tu.Unit->getASTContext().getTranslationUnitDecl()->dump();
229 llvm::errs() << "\n";
230 }
231 if (ToAST) {
232 llvm::errs() << "ToAST:\n";
233 ToAST->getASTContext().getTranslationUnitDecl()->dump();
234 }
235 }
236
237 } // end namespace ast_matchers
238 } // end namespace clang
239