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
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(Path: FileName, ModificationTime: 0, Buffer: std::move(Buffer));
33}
34
35void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
36 StringRef Code) {
37 return createVirtualFileIfNeeded(ToAST, FileName,
38 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: 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(Code: this->Code, Args, FileName: 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
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(p: 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(FromD: FromDecl))
84 return *ImportedOrErr;
85 else {
86 llvm::consumeError(Err: 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(FromD: 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(FromT: FromType))
103 return *ImportedOrErr;
104 else {
105 llvm::consumeError(Err: 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>(args&: *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(Lang: 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(Code: ToCode, Args: ToArgs, FileName);
126 ToAST->enableSourceFileDiagnostics();
127 lazyInitSharedState(ToTU: 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 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(Range&: FromTUs, P: [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(Lang: FromLang);
145 std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(Lang: ToLang);
146
147 FromTUs.emplace_back(args&: FromSrcCode, args: InputFileName, args&: FromArgs, args&: Creator,
148 args&: ODRHandling);
149 TU &FromTU = FromTUs.back();
150
151 assert(!ToAST);
152 lazyInitToAST(ToLang, ToSrcCode, FileName: OutputFileName);
153
154 ASTContext &FromCtx = FromTU.Unit->getASTContext();
155
156 IdentifierInfo *ImportedII = &FromCtx.Idents.get(Name: 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(args&: *FoundDecls.begin(), args&: Imported);
171}
172
173TranslationUnitDecl *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(args&: SrcCode, args&: FileName, args&: Args, args&: Creator, args&: ODRHandling);
182 TU &Tu = FromTUs.back();
183
184 return Tu.TUDecl;
185}
186
187TranslationUnitDecl *ASTImporterTestBase::getToTuDecl(StringRef ToSrcCode,
188 TestLanguage ToLang) {
189 std::vector<std::string> ToArgs = getCommandLineArgsForLanguage(Lang: ToLang);
190 assert(!ToAST);
191 lazyInitToAST(ToLang, ToSrcCode, FileName: OutputFileName);
192 return ToAST->getASTContext().getTranslationUnitDecl();
193}
194
195Decl *ASTImporterTestBase::Import(Decl *From, TestLanguage ToLang) {
196 lazyInitToAST(ToLang, ToSrcCode: "", FileName: OutputFileName);
197 TU *FromTU = findFromTU(From);
198 assert(SharedStatePtr);
199 Decl *To = FromTU->import(SharedState: SharedStatePtr, ToAST: ToAST.get(), FromDecl: From);
200 return To;
201}
202
203llvm::Expected<Decl *> ASTImporterTestBase::importOrError(Decl *From,
204 TestLanguage ToLang) {
205 lazyInitToAST(ToLang, ToSrcCode: "", FileName: OutputFileName);
206 TU *FromTU = findFromTU(From);
207 assert(SharedStatePtr);
208 llvm::Expected<Decl *> To =
209 FromTU->importOrError(SharedState: SharedStatePtr, ToAST: ToAST.get(), FromDecl: From);
210 return To;
211}
212
213QualType ASTImporterTestBase::ImportType(QualType FromType, Decl *TUDecl,
214 TestLanguage ToLang) {
215 lazyInitToAST(ToLang, ToSrcCode: "", FileName: OutputFileName);
216 TU *FromTU = findFromTU(From: TUDecl);
217 assert(SharedStatePtr);
218 return FromTU->import(SharedState: SharedStatePtr, ToAST: ToAST.get(), FromType);
219}
220
221ASTImporterTestBase::~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

source code of clang/unittests/AST/ASTImporterFixtures.cpp