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