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