1 //===- unittest/AST/ASTImporterFixtures.h - AST unit test support ---------===//
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
7 //===----------------------------------------------------------------------===//
10 /// Fixture classes for testing the ASTImporter.
12 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H
15 #define LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H
17 #include "gmock/gmock.h"
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"
26 #include "DeclMatcher.h"
27 #include "MatchVerifier.h"
34 class ASTImporterSharedState
;
37 namespace ast_matchers
{
39 const StringRef DeclToImportID
= "declToImport";
40 const StringRef DeclToVerifyID
= "declToVerify";
42 // Creates a virtual file and assigns that to the context of given AST. If the
43 // file already exists then the file will not be created again as a duplicate.
44 void createVirtualFileIfNeeded(ASTUnit
*ToAST
, StringRef FileName
,
45 std::unique_ptr
<llvm::MemoryBuffer
> &&Buffer
);
47 void createVirtualFileIfNeeded(ASTUnit
*ToAST
, StringRef FileName
,
50 // Common base for the different families of ASTImporter tests that are
51 // parameterized on the compiler options which may result a different AST. E.g.
52 // -fms-compatibility or -fdelayed-template-parsing.
53 class CompilerOptionSpecificTest
: public ::testing::Test
{
55 // Return the extra arguments appended to runtime options at compilation.
56 virtual std::vector
<std::string
> getExtraArgs() const { return {}; }
58 // Returns the argument vector used for a specific language option, this set
59 // can be tweaked by the test parameters.
60 std::vector
<std::string
>
61 getCommandLineArgsForLanguage(TestLanguage Lang
) const {
62 std::vector
<std::string
> Args
= getCommandLineArgsForTesting(Lang
);
63 std::vector
<std::string
> ExtraArgs
= getExtraArgs();
64 for (const auto &Arg
: ExtraArgs
) {
71 const auto DefaultTestArrayForRunOptions
=
72 std::array
<std::vector
<std::string
>, 4>{
73 {std::vector
<std::string
>(),
74 std::vector
<std::string
>{"-fdelayed-template-parsing"},
75 std::vector
<std::string
>{"-fms-compatibility"},
76 std::vector
<std::string
>{"-fdelayed-template-parsing",
77 "-fms-compatibility"}}};
79 const auto DefaultTestValuesForRunOptions
=
80 ::testing::ValuesIn(DefaultTestArrayForRunOptions
);
82 // This class provides generic methods to write tests which can check internal
83 // attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also,
84 // this fixture makes it possible to import from several "From" contexts.
85 class ASTImporterTestBase
: public CompilerOptionSpecificTest
{
87 const char *const InputFileName
= "input.cc";
88 const char *const OutputFileName
= "output.cc";
91 /// Allocates an ASTImporter (or one of its subclasses).
92 typedef std::function
<ASTImporter
*(
93 ASTContext
&, FileManager
&, ASTContext
&, FileManager
&, bool,
94 const std::shared_ptr
<ASTImporterSharedState
> &SharedState
)>
97 // ODR handling type for the AST importer.
98 ASTImporter::ODRHandlingType ODRHandling
;
100 // The lambda that constructs the ASTImporter we use in this test.
101 ImporterConstructor Creator
;
104 // Buffer for the To context, must live in the test scope.
107 // Represents a "From" translation unit and holds an importer object which we
108 // use to import from this translation unit.
110 // Buffer for the context, must live in the test scope.
112 std::string FileName
;
113 std::unique_ptr
<ASTUnit
> Unit
;
114 TranslationUnitDecl
*TUDecl
= nullptr;
115 std::unique_ptr
<ASTImporter
> Importer
;
116 ImporterConstructor Creator
;
117 ASTImporter::ODRHandlingType ODRHandling
;
119 TU(StringRef Code
, StringRef FileName
, std::vector
<std::string
> Args
,
120 ImporterConstructor C
= ImporterConstructor(),
121 ASTImporter::ODRHandlingType ODRHandling
=
122 ASTImporter::ODRHandlingType::Conservative
);
126 lazyInitImporter(const std::shared_ptr
<ASTImporterSharedState
> &SharedState
,
128 Decl
*import(const std::shared_ptr
<ASTImporterSharedState
> &SharedState
,
129 ASTUnit
*ToAST
, Decl
*FromDecl
);
130 llvm::Expected
<Decl
*>
131 importOrError(const std::shared_ptr
<ASTImporterSharedState
> &SharedState
,
132 ASTUnit
*ToAST
, Decl
*FromDecl
);
133 QualType
import(const std::shared_ptr
<ASTImporterSharedState
> &SharedState
,
134 ASTUnit
*ToAST
, QualType FromType
);
137 // We may have several From contexts and related translation units. In each
138 // AST, the buffers for the source are handled via references and are set
139 // during the creation of the AST. These references must point to a valid
140 // buffer until the AST is alive. Thus, we must use a list in order to avoid
141 // moving of the stored objects because that would mean breaking the
142 // references in the AST. By using a vector a move could happen when the
143 // vector is expanding, with the list we won't have these issues.
144 std::list
<TU
> FromTUs
;
146 // Initialize the shared state if not initialized already.
147 void lazyInitSharedState(TranslationUnitDecl
*ToTU
);
149 void lazyInitToAST(TestLanguage ToLang
, StringRef ToSrcCode
,
153 std::shared_ptr
<ASTImporterSharedState
> SharedStatePtr
;
156 // We may have several From context but only one To context.
157 std::unique_ptr
<ASTUnit
> ToAST
;
159 // Returns with the TU associated with the given Decl.
160 TU
*findFromTU(Decl
*From
);
162 // Creates an AST both for the From and To source code and imports the Decl
163 // of the identifier into the To context.
164 // Must not be called more than once within the same test.
165 std::tuple
<Decl
*, Decl
*>
166 getImportedDecl(StringRef FromSrcCode
, TestLanguage FromLang
,
167 StringRef ToSrcCode
, TestLanguage ToLang
,
168 StringRef Identifier
= DeclToImportID
);
170 // Creates a TU decl for the given source code which can be used as a From
171 // context. May be called several times in a given test (with different file
173 TranslationUnitDecl
*getTuDecl(StringRef SrcCode
, TestLanguage Lang
,
174 StringRef FileName
= "input.cc");
176 // Creates the To context with the given source code and returns the TU decl.
177 TranslationUnitDecl
*getToTuDecl(StringRef ToSrcCode
, TestLanguage ToLang
);
179 // Import the given Decl into the ToCtx.
180 // May be called several times in a given test.
181 // The different instances of the param From may have different ASTContext.
182 Decl
*Import(Decl
*From
, TestLanguage ToLang
);
184 template <class DeclT
> DeclT
*Import(DeclT
*From
, TestLanguage Lang
) {
185 return cast_or_null
<DeclT
>(Import(cast
<Decl
>(From
), Lang
));
188 // Import the given Decl into the ToCtx.
189 // Same as Import but returns the result of the import which can be an error.
190 llvm::Expected
<Decl
*> importOrError(Decl
*From
, TestLanguage ToLang
);
192 QualType
ImportType(QualType FromType
, Decl
*TUDecl
, TestLanguage ToLang
);
194 ASTImporterTestBase()
195 : ODRHandling(ASTImporter::ODRHandlingType::Conservative
) {}
196 ~ASTImporterTestBase();
199 class ASTImporterOptionSpecificTestBase
200 : public ASTImporterTestBase
,
201 public ::testing::WithParamInterface
<std::vector
<std::string
>> {
203 std::vector
<std::string
> getExtraArgs() const override
{ return GetParam(); }
206 // Base class for those tests which use the family of `testImport` functions.
208 : public CompilerOptionSpecificTest
,
209 public ::testing::WithParamInterface
<std::vector
<std::string
>> {
211 template <typename NodeType
>
212 llvm::Expected
<NodeType
> importNode(ASTUnit
*From
, ASTUnit
*To
,
213 ASTImporter
&Importer
, NodeType Node
) {
214 ASTContext
&ToCtx
= To
->getASTContext();
216 // Add 'From' file to virtual file system so importer can 'find' it
217 // while importing SourceLocations. It is safe to add same file multiple
218 // times - it just isn't replaced.
219 StringRef FromFileName
= From
->getMainFileName();
220 createVirtualFileIfNeeded(To
, FromFileName
,
221 From
->getBufferForFile(FromFileName
));
223 auto Imported
= Importer
.Import(Node
);
226 // This should dump source locations and assert if some source locations
227 // were not imported.
228 SmallString
<1024> ImportChecker
;
229 llvm::raw_svector_ostream
ToNothing(ImportChecker
);
230 ToCtx
.getTranslationUnitDecl()->print(ToNothing
);
232 // This traverses the AST to catch certain bugs like poorly or not
233 // implemented subtrees.
234 (*Imported
)->dump(ToNothing
);
240 template <typename NodeType
>
241 testing::AssertionResult
242 testImport(const std::string
&FromCode
,
243 const std::vector
<std::string
> &FromArgs
,
244 const std::string
&ToCode
, const std::vector
<std::string
> &ToArgs
,
245 MatchVerifier
<NodeType
> &Verifier
,
246 const internal::BindableMatcher
<NodeType
> &SearchMatcher
,
247 const internal::BindableMatcher
<NodeType
> &VerificationMatcher
) {
248 const char *const InputFileName
= "input.cc";
249 const char *const OutputFileName
= "output.cc";
251 std::unique_ptr
<ASTUnit
> FromAST
= tooling::buildASTFromCodeWithArgs(
252 FromCode
, FromArgs
, InputFileName
),
253 ToAST
= tooling::buildASTFromCodeWithArgs(
254 ToCode
, ToArgs
, OutputFileName
);
256 ASTContext
&FromCtx
= FromAST
->getASTContext(),
257 &ToCtx
= ToAST
->getASTContext();
259 ASTImporter
Importer(ToCtx
, ToAST
->getFileManager(), FromCtx
,
260 FromAST
->getFileManager(), false);
262 auto FoundNodes
= match(SearchMatcher
, FromCtx
);
263 if (FoundNodes
.size() != 1)
264 return testing::AssertionFailure()
265 << "Multiple potential nodes were found!";
267 auto ToImport
= selectFirst
<NodeType
>(DeclToImportID
, FoundNodes
);
269 return testing::AssertionFailure() << "Node type mismatch!";
271 // Sanity check: the node being imported should match in the same way as
273 internal::BindableMatcher
<NodeType
> WrapperMatcher(VerificationMatcher
);
274 EXPECT_TRUE(Verifier
.match(ToImport
, WrapperMatcher
));
276 auto Imported
= importNode(FromAST
.get(), ToAST
.get(), Importer
, ToImport
);
278 std::string ErrorText
;
280 Imported
.takeError(),
281 [&ErrorText
](const ImportError
&Err
) { ErrorText
= Err
.message(); });
282 return testing::AssertionFailure()
283 << "Import failed, error: \"" << ErrorText
<< "\"!";
286 return Verifier
.match(*Imported
, WrapperMatcher
);
289 template <typename NodeType
>
290 testing::AssertionResult
291 testImport(const std::string
&FromCode
,
292 const std::vector
<std::string
> &FromArgs
,
293 const std::string
&ToCode
, const std::vector
<std::string
> &ToArgs
,
294 MatchVerifier
<NodeType
> &Verifier
,
295 const internal::BindableMatcher
<NodeType
> &VerificationMatcher
) {
297 FromCode
, FromArgs
, ToCode
, ToArgs
, Verifier
,
299 has(namedDecl(hasName(DeclToImportID
)).bind(DeclToImportID
))),
300 VerificationMatcher
);
304 std::vector
<std::string
> getExtraArgs() const override
{ return GetParam(); }
307 /// Test how AST node named "declToImport" located in the translation unit
308 /// of "FromCode" virtual file is imported to "ToCode" virtual file.
309 /// The verification is done by running AMatcher over the imported node.
310 template <typename NodeType
, typename MatcherType
>
311 void testImport(const std::string
&FromCode
, TestLanguage FromLang
,
312 const std::string
&ToCode
, TestLanguage ToLang
,
313 MatchVerifier
<NodeType
> &Verifier
,
314 const MatcherType
&AMatcher
) {
315 std::vector
<std::string
> FromArgs
= getCommandLineArgsForLanguage(FromLang
);
316 std::vector
<std::string
> ToArgs
= getCommandLineArgsForLanguage(ToLang
);
318 testImport(FromCode
, FromArgs
, ToCode
, ToArgs
, Verifier
, AMatcher
));
321 struct ImportAction
{
322 StringRef FromFilename
;
323 StringRef ToFilename
;
324 // FIXME: Generalize this to support other node kinds.
325 internal::BindableMatcher
<Decl
> ImportPredicate
;
327 ImportAction(StringRef FromFilename
, StringRef ToFilename
,
328 DeclarationMatcher ImportPredicate
)
329 : FromFilename(FromFilename
), ToFilename(ToFilename
),
330 ImportPredicate(ImportPredicate
) {}
332 ImportAction(StringRef FromFilename
, StringRef ToFilename
,
333 const std::string
&DeclName
)
334 : FromFilename(FromFilename
), ToFilename(ToFilename
),
335 ImportPredicate(namedDecl(hasName(DeclName
))) {}
338 using SingleASTUnit
= std::unique_ptr
<ASTUnit
>;
339 using AllASTUnits
= llvm::StringMap
<SingleASTUnit
>;
342 std::string CodeSample
;
346 using CodeFiles
= llvm::StringMap
<CodeEntry
>;
348 /// Builds an ASTUnit for one potential compile options set.
349 SingleASTUnit
createASTUnit(StringRef FileName
, const CodeEntry
&CE
) const {
350 std::vector
<std::string
> Args
= getCommandLineArgsForLanguage(CE
.Lang
);
351 auto AST
= tooling::buildASTFromCodeWithArgs(CE
.CodeSample
, Args
, FileName
);
352 EXPECT_TRUE(AST
.get());
356 /// Test an arbitrary sequence of imports for a set of given in-memory files.
357 /// The verification is done by running VerificationMatcher against a
358 /// specified AST node inside of one of given files.
359 /// \param CodeSamples Map whose key is the file name and the value is the
361 /// \param ImportActions Sequence of imports. Each import in sequence
362 /// specifies "from file" and "to file" and a matcher that is used for
363 /// searching a declaration for import in "from file".
364 /// \param FileForFinalCheck Name of virtual file for which the final check is
366 /// \param FinalSelectPredicate Matcher that specifies the AST node in the
367 /// FileForFinalCheck for which the verification will be done.
368 /// \param VerificationMatcher Matcher that will be used for verification
369 /// after all imports in sequence are done.
370 void testImportSequence(const CodeFiles
&CodeSamples
,
371 const std::vector
<ImportAction
> &ImportActions
,
372 StringRef FileForFinalCheck
,
373 internal::BindableMatcher
<Decl
> FinalSelectPredicate
,
374 internal::BindableMatcher
<Decl
> VerificationMatcher
) {
376 using ImporterKey
= std::pair
<const ASTUnit
*, const ASTUnit
*>;
377 llvm::DenseMap
<ImporterKey
, std::unique_ptr
<ASTImporter
>> Importers
;
379 auto GenASTsIfNeeded
= [this, &AllASTs
, &CodeSamples
](StringRef Filename
) {
380 if (!AllASTs
.count(Filename
)) {
381 auto Found
= CodeSamples
.find(Filename
);
382 assert(Found
!= CodeSamples
.end() && "Wrong file for import!");
383 AllASTs
[Filename
] = createASTUnit(Filename
, Found
->getValue());
387 for (const ImportAction
&Action
: ImportActions
) {
388 StringRef FromFile
= Action
.FromFilename
, ToFile
= Action
.ToFilename
;
389 GenASTsIfNeeded(FromFile
);
390 GenASTsIfNeeded(ToFile
);
392 ASTUnit
*From
= AllASTs
[FromFile
].get();
393 ASTUnit
*To
= AllASTs
[ToFile
].get();
395 // Create a new importer if needed.
396 std::unique_ptr
<ASTImporter
> &ImporterRef
= Importers
[{From
, To
}];
398 ImporterRef
.reset(new ASTImporter(
399 To
->getASTContext(), To
->getFileManager(), From
->getASTContext(),
400 From
->getFileManager(), false));
402 // Find the declaration and import it.
403 auto FoundDecl
= match(Action
.ImportPredicate
.bind(DeclToImportID
),
404 From
->getASTContext());
405 EXPECT_TRUE(FoundDecl
.size() == 1);
406 const Decl
*ToImport
= selectFirst
<Decl
>(DeclToImportID
, FoundDecl
);
407 auto Imported
= importNode(From
, To
, *ImporterRef
, ToImport
);
408 EXPECT_TRUE(static_cast<bool>(Imported
));
410 llvm::consumeError(Imported
.takeError());
413 // Find the declaration and import it.
414 auto FoundDecl
= match(FinalSelectPredicate
.bind(DeclToVerifyID
),
415 AllASTs
[FileForFinalCheck
]->getASTContext());
416 EXPECT_TRUE(FoundDecl
.size() == 1);
417 const Decl
*ToVerify
= selectFirst
<Decl
>(DeclToVerifyID
, FoundDecl
);
418 MatchVerifier
<Decl
> Verifier
;
419 EXPECT_TRUE(Verifier
.match(
420 ToVerify
, internal::BindableMatcher
<Decl
>(VerificationMatcher
)));
424 template <typename T
> RecordDecl
*getRecordDecl(T
*D
) {
425 auto *ET
= cast
<ElaboratedType
>(D
->getType().getTypePtr());
426 return cast
<RecordType
>(ET
->getNamedType().getTypePtr())->getDecl();
430 ::testing::AssertionResult
isSuccess(llvm::Expected
<T
> &ValOrErr
) {
432 return ::testing::AssertionSuccess() << "Expected<> contains no error.";
434 return ::testing::AssertionFailure()
435 << "Expected<> contains error: " << toString(ValOrErr
.takeError());
439 ::testing::AssertionResult
isImportError(llvm::Expected
<T
> &ValOrErr
,
440 ImportError::ErrorKind Kind
) {
442 return ::testing::AssertionFailure() << "Expected<> is expected to contain "
443 "error but does contain value \""
444 << (*ValOrErr
) << "\"";
446 std::ostringstream OS
;
448 auto Err
= llvm::handleErrors(
449 ValOrErr
.takeError(), [&OS
, &Result
, Kind
](clang::ImportError
&IE
) {
450 if (IE
.Error
== Kind
) {
452 OS
<< "Expected<> contains an ImportError " << IE
.toString();
454 OS
<< "Expected<> contains an ImportError " << IE
.toString()
455 << " instead of kind " << Kind
;
459 OS
<< "Expected<> contains unexpected error: "
460 << toString(std::move(Err
));
463 return ::testing::AssertionSuccess() << OS
.str();
465 return ::testing::AssertionFailure() << OS
.str();
469 } // end namespace ast_matchers
470 } // end namespace clang