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 | |
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(Path: FileName, ModificationTime: 0, Buffer: std::move(Buffer)); |
33 | } |
34 | |
35 | void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, |
36 | StringRef Code) { |
37 | return createVirtualFileIfNeeded(ToAST, FileName, |
38 | Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: Code)); |
39 | } |
40 | |
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(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 | |
63 | ASTImporterTestBase::TU::~TU() {} |
64 | |
65 | void 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 | |
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(FromD: FromDecl)) |
84 | return *ImportedOrErr; |
85 | else { |
86 | llvm::consumeError(Err: ImportedOrErr.takeError()); |
87 | return nullptr; |
88 | } |
89 | } |
90 | |
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(FromD: FromDecl); |
96 | } |
97 | |
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(FromT: FromType)) |
103 | return *ImportedOrErr; |
104 | else { |
105 | llvm::consumeError(Err: ImportedOrErr.takeError()); |
106 | return QualType{}; |
107 | } |
108 | } |
109 | |
110 | void ASTImporterTestBase::lazyInitSharedState(TranslationUnitDecl *ToTU) { |
111 | assert(ToTU); |
112 | if (!SharedStatePtr) |
113 | SharedStatePtr = std::make_shared<ASTImporterSharedState>(args&: *ToTU); |
114 | } |
115 | |
116 | void 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 | |
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(Range&: FromTUs, P: [From](const TU &E) { |
135 | return E.TUDecl == From->getTranslationUnitDecl(); |
136 | }); |
137 | assert(It != FromTUs.end()); |
138 | return &*It; |
139 | } |
140 | |
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(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 | |
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(args&: SrcCode, args&: FileName, args&: Args, args&: Creator, args&: ODRHandling); |
182 | TU &Tu = FromTUs.back(); |
183 | |
184 | return Tu.TUDecl; |
185 | } |
186 | |
187 | TranslationUnitDecl *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 | |
195 | Decl *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 | |
203 | llvm::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 | |
213 | QualType 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 | |
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 | |