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
.empty())
264 return testing::AssertionFailure() << "No node was found!";
265 if (FoundNodes
.size() != 1)
266 return testing::AssertionFailure()
267 << "Multiple potential nodes were found!";
269 auto ToImport
= selectFirst
<NodeType
>(DeclToImportID
, FoundNodes
);
271 return testing::AssertionFailure() << "Node type mismatch!";
273 // The node being imported should match in the same way as
275 internal::BindableMatcher
<NodeType
> WrapperMatcher(VerificationMatcher
);
276 EXPECT_TRUE(Verifier
.match(ToImport
, WrapperMatcher
));
278 auto Imported
= importNode(FromAST
.get(), ToAST
.get(), Importer
, ToImport
);
280 std::string ErrorText
;
281 handleAllErrors(Imported
.takeError(),
282 [&ErrorText
](const ASTImportError
&Err
) {
283 ErrorText
= Err
.message();
285 return testing::AssertionFailure()
286 << "Import failed, error: \"" << ErrorText
<< "\"!";
289 return Verifier
.match(*Imported
, WrapperMatcher
);
292 template <typename NodeType
>
293 testing::AssertionResult
294 testImport(const std::string
&FromCode
,
295 const std::vector
<std::string
> &FromArgs
,
296 const std::string
&ToCode
, const std::vector
<std::string
> &ToArgs
,
297 MatchVerifier
<NodeType
> &Verifier
,
298 const internal::BindableMatcher
<NodeType
> &VerificationMatcher
) {
300 FromCode
, FromArgs
, ToCode
, ToArgs
, Verifier
,
302 has(namedDecl(hasName(DeclToImportID
)).bind(DeclToImportID
))),
303 VerificationMatcher
);
307 std::vector
<std::string
> getExtraArgs() const override
{ return GetParam(); }
310 /// Test how AST node named "declToImport" located in the translation unit
311 /// of "FromCode" virtual file is imported to "ToCode" virtual file.
312 /// The verification is done by running AMatcher over the imported node.
313 template <typename NodeType
, typename MatcherType
>
314 void testImport(const std::string
&FromCode
, TestLanguage FromLang
,
315 const std::string
&ToCode
, TestLanguage ToLang
,
316 MatchVerifier
<NodeType
> &Verifier
,
317 const MatcherType
&AMatcher
) {
318 std::vector
<std::string
> FromArgs
= getCommandLineArgsForLanguage(FromLang
);
319 std::vector
<std::string
> ToArgs
= getCommandLineArgsForLanguage(ToLang
);
321 testImport(FromCode
, FromArgs
, ToCode
, ToArgs
, Verifier
, AMatcher
));
324 struct ImportAction
{
325 StringRef FromFilename
;
326 StringRef ToFilename
;
327 // FIXME: Generalize this to support other node kinds.
328 internal::BindableMatcher
<Decl
> ImportPredicate
;
330 ImportAction(StringRef FromFilename
, StringRef ToFilename
,
331 DeclarationMatcher ImportPredicate
)
332 : FromFilename(FromFilename
), ToFilename(ToFilename
),
333 ImportPredicate(ImportPredicate
) {}
335 ImportAction(StringRef FromFilename
, StringRef ToFilename
,
336 const std::string
&DeclName
)
337 : FromFilename(FromFilename
), ToFilename(ToFilename
),
338 ImportPredicate(namedDecl(hasName(DeclName
))) {}
341 using SingleASTUnit
= std::unique_ptr
<ASTUnit
>;
342 using AllASTUnits
= llvm::StringMap
<SingleASTUnit
>;
345 std::string CodeSample
;
349 using CodeFiles
= llvm::StringMap
<CodeEntry
>;
351 /// Builds an ASTUnit for one potential compile options set.
352 SingleASTUnit
createASTUnit(StringRef FileName
, const CodeEntry
&CE
) const {
353 std::vector
<std::string
> Args
= getCommandLineArgsForLanguage(CE
.Lang
);
354 auto AST
= tooling::buildASTFromCodeWithArgs(CE
.CodeSample
, Args
, FileName
);
355 EXPECT_TRUE(AST
.get());
359 /// Test an arbitrary sequence of imports for a set of given in-memory files.
360 /// The verification is done by running VerificationMatcher against a
361 /// specified AST node inside of one of given files.
362 /// \param CodeSamples Map whose key is the file name and the value is the
364 /// \param ImportActions Sequence of imports. Each import in sequence
365 /// specifies "from file" and "to file" and a matcher that is used for
366 /// searching a declaration for import in "from file".
367 /// \param FileForFinalCheck Name of virtual file for which the final check is
369 /// \param FinalSelectPredicate Matcher that specifies the AST node in the
370 /// FileForFinalCheck for which the verification will be done.
371 /// \param VerificationMatcher Matcher that will be used for verification
372 /// after all imports in sequence are done.
373 void testImportSequence(const CodeFiles
&CodeSamples
,
374 const std::vector
<ImportAction
> &ImportActions
,
375 StringRef FileForFinalCheck
,
376 internal::BindableMatcher
<Decl
> FinalSelectPredicate
,
377 internal::BindableMatcher
<Decl
> VerificationMatcher
) {
379 using ImporterKey
= std::pair
<const ASTUnit
*, const ASTUnit
*>;
380 llvm::DenseMap
<ImporterKey
, std::unique_ptr
<ASTImporter
>> Importers
;
382 auto GenASTsIfNeeded
= [this, &AllASTs
, &CodeSamples
](StringRef Filename
) {
383 if (!AllASTs
.count(Filename
)) {
384 auto Found
= CodeSamples
.find(Filename
);
385 assert(Found
!= CodeSamples
.end() && "Wrong file for import!");
386 AllASTs
[Filename
] = createASTUnit(Filename
, Found
->getValue());
390 for (const ImportAction
&Action
: ImportActions
) {
391 StringRef FromFile
= Action
.FromFilename
, ToFile
= Action
.ToFilename
;
392 GenASTsIfNeeded(FromFile
);
393 GenASTsIfNeeded(ToFile
);
395 ASTUnit
*From
= AllASTs
[FromFile
].get();
396 ASTUnit
*To
= AllASTs
[ToFile
].get();
398 // Create a new importer if needed.
399 std::unique_ptr
<ASTImporter
> &ImporterRef
= Importers
[{From
, To
}];
401 ImporterRef
.reset(new ASTImporter(
402 To
->getASTContext(), To
->getFileManager(), From
->getASTContext(),
403 From
->getFileManager(), false));
405 // Find the declaration and import it.
406 auto FoundDecl
= match(Action
.ImportPredicate
.bind(DeclToImportID
),
407 From
->getASTContext());
408 EXPECT_TRUE(FoundDecl
.size() == 1);
409 const Decl
*ToImport
= selectFirst
<Decl
>(DeclToImportID
, FoundDecl
);
410 auto Imported
= importNode(From
, To
, *ImporterRef
, ToImport
);
411 EXPECT_TRUE(static_cast<bool>(Imported
));
413 llvm::consumeError(Imported
.takeError());
416 // Find the declaration and import it.
417 auto FoundDecl
= match(FinalSelectPredicate
.bind(DeclToVerifyID
),
418 AllASTs
[FileForFinalCheck
]->getASTContext());
419 EXPECT_TRUE(FoundDecl
.size() == 1);
420 const Decl
*ToVerify
= selectFirst
<Decl
>(DeclToVerifyID
, FoundDecl
);
421 MatchVerifier
<Decl
> Verifier
;
422 EXPECT_TRUE(Verifier
.match(
423 ToVerify
, internal::BindableMatcher
<Decl
>(VerificationMatcher
)));
427 template <typename T
> RecordDecl
*getRecordDecl(T
*D
) {
428 auto *ET
= cast
<ElaboratedType
>(D
->getType().getTypePtr());
429 return cast
<RecordType
>(ET
->getNamedType().getTypePtr())->getDecl();
433 ::testing::AssertionResult
isSuccess(llvm::Expected
<T
> &ValOrErr
) {
435 return ::testing::AssertionSuccess() << "Expected<> contains no error.";
437 return ::testing::AssertionFailure()
438 << "Expected<> contains error: " << toString(ValOrErr
.takeError());
442 ::testing::AssertionResult
isImportError(llvm::Expected
<T
> &ValOrErr
,
443 ASTImportError::ErrorKind Kind
) {
445 return ::testing::AssertionFailure() << "Expected<> is expected to contain "
446 "error but does contain value \""
447 << (*ValOrErr
) << "\"";
449 std::ostringstream OS
;
451 auto Err
= llvm::handleErrors(
452 ValOrErr
.takeError(), [&OS
, &Result
, Kind
](clang::ASTImportError
&IE
) {
453 if (IE
.Error
== Kind
) {
455 OS
<< "Expected<> contains an ImportError " << IE
.toString();
457 OS
<< "Expected<> contains an ImportError " << IE
.toString()
458 << " instead of kind " << Kind
;
462 OS
<< "Expected<> contains unexpected error: "
463 << toString(std::move(Err
));
466 return ::testing::AssertionSuccess() << OS
.str();
468 return ::testing::AssertionFailure() << OS
.str();
472 } // end namespace ast_matchers
473 } // end namespace clang