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