1 //===- unittest/AST/ASTImporterFixtures.h - 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 /// Fixture classes for testing the ASTImporter.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H
15 #define LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H
16 
17 #include "gmock/gmock.h"
18 
19 #include "clang/AST/ASTImporter.h"
20 #include "clang/AST/ASTImporterSharedState.h"
21 #include "clang/Frontend/ASTUnit.h"
22 #include "clang/Testing/CommandLineArgs.h"
23 #include "llvm/Support/Error.h"
24 #include "llvm/Support/ErrorHandling.h"
25 
26 #include "DeclMatcher.h"
27 
28 #include <sstream>
29 
30 namespace clang {
31 
32 class ASTImporter;
33 class ASTImporterSharedState;
34 class ASTUnit;
35 
36 namespace ast_matchers {
37 
38 const StringRef DeclToImportID = "declToImport";
39 const StringRef DeclToVerifyID = "declToVerify";
40 
41 // Creates a virtual file and assigns that to the context of given AST. If the
42 // file already exists then the file will not be created again as a duplicate.
43 void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
44                                std::unique_ptr<llvm::MemoryBuffer> &&Buffer);
45 
46 void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
47                                StringRef Code);
48 
49 // Common base for the different families of ASTImporter tests that are
50 // parameterized on the compiler options which may result a different AST. E.g.
51 // -fms-compatibility or -fdelayed-template-parsing.
52 class CompilerOptionSpecificTest : public ::testing::Test {
53 protected:
54   // Return the extra arguments appended to runtime options at compilation.
getExtraArgs()55   virtual std::vector<std::string> getExtraArgs() const { return {}; }
56 
57   // Returns the argument vector used for a specific language option, this set
58   // can be tweaked by the test parameters.
59   std::vector<std::string>
getCommandLineArgsForLanguage(TestLanguage Lang)60   getCommandLineArgsForLanguage(TestLanguage Lang) const {
61     std::vector<std::string> Args = getCommandLineArgsForTesting(Lang);
62     std::vector<std::string> ExtraArgs = getExtraArgs();
63     for (const auto &Arg : ExtraArgs) {
64       Args.push_back(Arg);
65     }
66     return Args;
67   }
68 };
69 
70 const auto DefaultTestArrayForRunOptions =
71     std::array<std::vector<std::string>, 4>{
72         {std::vector<std::string>(),
73          std::vector<std::string>{"-fdelayed-template-parsing"},
74          std::vector<std::string>{"-fms-compatibility"},
75          std::vector<std::string>{"-fdelayed-template-parsing",
76                                   "-fms-compatibility"}}};
77 
78 const auto DefaultTestValuesForRunOptions =
79     ::testing::ValuesIn(DefaultTestArrayForRunOptions);
80 
81 // This class provides generic methods to write tests which can check internal
82 // attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also,
83 // this fixture makes it possible to import from several "From" contexts.
84 class ASTImporterTestBase : public CompilerOptionSpecificTest {
85 
86   const char *const InputFileName = "input.cc";
87   const char *const OutputFileName = "output.cc";
88 
89 public:
90   /// Allocates an ASTImporter (or one of its subclasses).
91   typedef std::function<ASTImporter *(
92       ASTContext &, FileManager &, ASTContext &, FileManager &, bool,
93       const std::shared_ptr<ASTImporterSharedState> &SharedState)>
94       ImporterConstructor;
95 
96   // ODR handling type for the AST importer.
97   ASTImporter::ODRHandlingType ODRHandling;
98 
99   // The lambda that constructs the ASTImporter we use in this test.
100   ImporterConstructor Creator;
101 
102 private:
103   // Buffer for the To context, must live in the test scope.
104   std::string ToCode;
105 
106   // Represents a "From" translation unit and holds an importer object which we
107   // use to import from this translation unit.
108   struct TU {
109     // Buffer for the context, must live in the test scope.
110     std::string Code;
111     std::string FileName;
112     std::unique_ptr<ASTUnit> Unit;
113     TranslationUnitDecl *TUDecl = nullptr;
114     std::unique_ptr<ASTImporter> Importer;
115     ImporterConstructor Creator;
116     ASTImporter::ODRHandlingType ODRHandling;
117 
118     TU(StringRef Code, StringRef FileName, std::vector<std::string> Args,
119        ImporterConstructor C = ImporterConstructor(),
120        ASTImporter::ODRHandlingType ODRHandling =
121            ASTImporter::ODRHandlingType::Conservative);
122     ~TU();
123 
124     void
125     lazyInitImporter(const std::shared_ptr<ASTImporterSharedState> &SharedState,
126                      ASTUnit *ToAST);
127     Decl *import(const std::shared_ptr<ASTImporterSharedState> &SharedState,
128                  ASTUnit *ToAST, Decl *FromDecl);
129     llvm::Expected<Decl *>
130     importOrError(const std::shared_ptr<ASTImporterSharedState> &SharedState,
131                   ASTUnit *ToAST, Decl *FromDecl);
132     QualType import(const std::shared_ptr<ASTImporterSharedState> &SharedState,
133                     ASTUnit *ToAST, QualType FromType);
134   };
135 
136   // We may have several From contexts and related translation units. In each
137   // AST, the buffers for the source are handled via references and are set
138   // during the creation of the AST. These references must point to a valid
139   // buffer until the AST is alive. Thus, we must use a list in order to avoid
140   // moving of the stored objects because that would mean breaking the
141   // references in the AST. By using a vector a move could happen when the
142   // vector is expanding, with the list we won't have these issues.
143   std::list<TU> FromTUs;
144 
145   // Initialize the shared state if not initialized already.
146   void lazyInitSharedState(TranslationUnitDecl *ToTU);
147 
148   void lazyInitToAST(TestLanguage ToLang, StringRef ToSrcCode,
149                      StringRef FileName);
150 
151 protected:
152   std::shared_ptr<ASTImporterSharedState> SharedStatePtr;
153 
154 public:
155   // We may have several From context but only one To context.
156   std::unique_ptr<ASTUnit> ToAST;
157 
158   // Returns with the TU associated with the given Decl.
159   TU *findFromTU(Decl *From);
160 
161   // Creates an AST both for the From and To source code and imports the Decl
162   // of the identifier into the To context.
163   // Must not be called more than once within the same test.
164   std::tuple<Decl *, Decl *>
165   getImportedDecl(StringRef FromSrcCode, TestLanguage FromLang,
166                   StringRef ToSrcCode, TestLanguage ToLang,
167                   StringRef Identifier = DeclToImportID);
168 
169   // Creates a TU decl for the given source code which can be used as a From
170   // context.  May be called several times in a given test (with different file
171   // name).
172   TranslationUnitDecl *getTuDecl(StringRef SrcCode, TestLanguage Lang,
173                                  StringRef FileName = "input.cc");
174 
175   // Creates the To context with the given source code and returns the TU decl.
176   TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, TestLanguage ToLang);
177 
178   // Import the given Decl into the ToCtx.
179   // May be called several times in a given test.
180   // The different instances of the param From may have different ASTContext.
181   Decl *Import(Decl *From, TestLanguage ToLang);
182 
Import(DeclT * From,TestLanguage Lang)183   template <class DeclT> DeclT *Import(DeclT *From, TestLanguage Lang) {
184     return cast_or_null<DeclT>(Import(cast<Decl>(From), Lang));
185   }
186 
187   // Import the given Decl into the ToCtx.
188   // Same as Import but returns the result of the import which can be an error.
189   llvm::Expected<Decl *> importOrError(Decl *From, TestLanguage ToLang);
190 
191   QualType ImportType(QualType FromType, Decl *TUDecl, TestLanguage ToLang);
192 
ASTImporterTestBase()193   ASTImporterTestBase()
194       : ODRHandling(ASTImporter::ODRHandlingType::Conservative) {}
195   ~ASTImporterTestBase();
196 };
197 
198 class ASTImporterOptionSpecificTestBase
199     : public ASTImporterTestBase,
200       public ::testing::WithParamInterface<std::vector<std::string>> {
201 protected:
getExtraArgs()202   std::vector<std::string> getExtraArgs() const override { return GetParam(); }
203 };
204 
205 template <class T>
isSuccess(llvm::Expected<T> & ValOrErr)206 ::testing::AssertionResult isSuccess(llvm::Expected<T> &ValOrErr) {
207   if (ValOrErr)
208     return ::testing::AssertionSuccess() << "Expected<> contains no error.";
209   else
210     return ::testing::AssertionFailure()
211            << "Expected<> contains error: " << toString(ValOrErr.takeError());
212 }
213 
214 template <class T>
isImportError(llvm::Expected<T> & ValOrErr,ImportError::ErrorKind Kind)215 ::testing::AssertionResult isImportError(llvm::Expected<T> &ValOrErr,
216                                          ImportError::ErrorKind Kind) {
217   if (ValOrErr) {
218     return ::testing::AssertionFailure() << "Expected<> is expected to contain "
219                                             "error but does contain value \""
220                                          << (*ValOrErr) << "\"";
221   } else {
222     std::ostringstream OS;
223     bool Result = false;
224     auto Err = llvm::handleErrors(
225         ValOrErr.takeError(), [&OS, &Result, Kind](clang::ImportError &IE) {
226           if (IE.Error == Kind) {
227             Result = true;
228             OS << "Expected<> contains an ImportError " << IE.toString();
229           } else {
230             OS << "Expected<> contains an ImportError " << IE.toString()
231                << " instead of kind " << Kind;
232           }
233         });
234     if (Err) {
235       OS << "Expected<> contains unexpected error: "
236          << toString(std::move(Err));
237     }
238     if (Result)
239       return ::testing::AssertionSuccess() << OS.str();
240     else
241       return ::testing::AssertionFailure() << OS.str();
242   }
243 }
244 
245 } // end namespace ast_matchers
246 } // end namespace clang
247 
248 #endif
249