diff --git a/clang/unittests/AST/ASTImporterFixtures.h b/clang/unittests/AST/ASTImporterFixtures.h --- a/clang/unittests/AST/ASTImporterFixtures.h +++ b/clang/unittests/AST/ASTImporterFixtures.h @@ -24,6 +24,7 @@ #include "llvm/Support/ErrorHandling.h" #include "DeclMatcher.h" +#include "MatchVerifier.h" #include @@ -202,6 +203,229 @@ std::vector getExtraArgs() const override { return GetParam(); } }; +// Base class for those tests which use the family of `testImport` functions. +class TestImportBase + : public CompilerOptionSpecificTest, + public ::testing::WithParamInterface> { + + template + llvm::Expected importNode(ASTUnit *From, ASTUnit *To, + ASTImporter &Importer, NodeType Node) { + ASTContext &ToCtx = To->getASTContext(); + + // Add 'From' file to virtual file system so importer can 'find' it + // while importing SourceLocations. It is safe to add same file multiple + // times - it just isn't replaced. + StringRef FromFileName = From->getMainFileName(); + createVirtualFileIfNeeded(To, FromFileName, + From->getBufferForFile(FromFileName)); + + auto Imported = Importer.Import(Node); + + if (Imported) { + // This should dump source locations and assert if some source locations + // were not imported. + SmallString<1024> ImportChecker; + llvm::raw_svector_ostream ToNothing(ImportChecker); + ToCtx.getTranslationUnitDecl()->print(ToNothing); + + // This traverses the AST to catch certain bugs like poorly or not + // implemented subtrees. + (*Imported)->dump(ToNothing); + } + + return Imported; + } + + template + testing::AssertionResult + testImport(const std::string &FromCode, + const std::vector &FromArgs, + const std::string &ToCode, const std::vector &ToArgs, + MatchVerifier &Verifier, + const internal::BindableMatcher &SearchMatcher, + const internal::BindableMatcher &VerificationMatcher) { + const char *const InputFileName = "input.cc"; + const char *const OutputFileName = "output.cc"; + + std::unique_ptr FromAST = tooling::buildASTFromCodeWithArgs( + FromCode, FromArgs, InputFileName), + ToAST = tooling::buildASTFromCodeWithArgs( + ToCode, ToArgs, OutputFileName); + + ASTContext &FromCtx = FromAST->getASTContext(), + &ToCtx = ToAST->getASTContext(); + + ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx, + FromAST->getFileManager(), false); + + auto FoundNodes = match(SearchMatcher, FromCtx); + if (FoundNodes.size() != 1) + return testing::AssertionFailure() + << "Multiple potential nodes were found!"; + + auto ToImport = selectFirst(DeclToImportID, FoundNodes); + if (!ToImport) + return testing::AssertionFailure() << "Node type mismatch!"; + + // Sanity check: the node being imported should match in the same way as + // the result node. + internal::BindableMatcher WrapperMatcher(VerificationMatcher); + EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher)); + + auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport); + if (!Imported) { + std::string ErrorText; + handleAllErrors( + Imported.takeError(), + [&ErrorText](const ImportError &Err) { ErrorText = Err.message(); }); + return testing::AssertionFailure() + << "Import failed, error: \"" << ErrorText << "\"!"; + } + + return Verifier.match(*Imported, WrapperMatcher); + } + + template + testing::AssertionResult + testImport(const std::string &FromCode, + const std::vector &FromArgs, + const std::string &ToCode, const std::vector &ToArgs, + MatchVerifier &Verifier, + const internal::BindableMatcher &VerificationMatcher) { + return testImport( + FromCode, FromArgs, ToCode, ToArgs, Verifier, + translationUnitDecl( + has(namedDecl(hasName(DeclToImportID)).bind(DeclToImportID))), + VerificationMatcher); + } + +protected: + std::vector getExtraArgs() const override { return GetParam(); } + +public: + /// Test how AST node named "declToImport" located in the translation unit + /// of "FromCode" virtual file is imported to "ToCode" virtual file. + /// The verification is done by running AMatcher over the imported node. + template + void testImport(const std::string &FromCode, TestLanguage FromLang, + const std::string &ToCode, TestLanguage ToLang, + MatchVerifier &Verifier, + const MatcherType &AMatcher) { + std::vector FromArgs = getCommandLineArgsForLanguage(FromLang); + std::vector ToArgs = getCommandLineArgsForLanguage(ToLang); + EXPECT_TRUE( + testImport(FromCode, FromArgs, ToCode, ToArgs, Verifier, AMatcher)); + } + + struct ImportAction { + StringRef FromFilename; + StringRef ToFilename; + // FIXME: Generalize this to support other node kinds. + internal::BindableMatcher ImportPredicate; + + ImportAction(StringRef FromFilename, StringRef ToFilename, + DeclarationMatcher ImportPredicate) + : FromFilename(FromFilename), ToFilename(ToFilename), + ImportPredicate(ImportPredicate) {} + + ImportAction(StringRef FromFilename, StringRef ToFilename, + const std::string &DeclName) + : FromFilename(FromFilename), ToFilename(ToFilename), + ImportPredicate(namedDecl(hasName(DeclName))) {} + }; + + using SingleASTUnit = std::unique_ptr; + using AllASTUnits = llvm::StringMap; + + struct CodeEntry { + std::string CodeSample; + TestLanguage Lang; + }; + + using CodeFiles = llvm::StringMap; + + /// Builds an ASTUnit for one potential compile options set. + SingleASTUnit createASTUnit(StringRef FileName, const CodeEntry &CE) const { + std::vector Args = getCommandLineArgsForLanguage(CE.Lang); + auto AST = tooling::buildASTFromCodeWithArgs(CE.CodeSample, Args, FileName); + EXPECT_TRUE(AST.get()); + return AST; + } + + /// Test an arbitrary sequence of imports for a set of given in-memory files. + /// The verification is done by running VerificationMatcher against a + /// specified AST node inside of one of given files. + /// \param CodeSamples Map whose key is the file name and the value is the + /// file content. + /// \param ImportActions Sequence of imports. Each import in sequence + /// specifies "from file" and "to file" and a matcher that is used for + /// searching a declaration for import in "from file". + /// \param FileForFinalCheck Name of virtual file for which the final check is + /// applied. + /// \param FinalSelectPredicate Matcher that specifies the AST node in the + /// FileForFinalCheck for which the verification will be done. + /// \param VerificationMatcher Matcher that will be used for verification + /// after all imports in sequence are done. + void testImportSequence(const CodeFiles &CodeSamples, + const std::vector &ImportActions, + StringRef FileForFinalCheck, + internal::BindableMatcher FinalSelectPredicate, + internal::BindableMatcher VerificationMatcher) { + AllASTUnits AllASTs; + using ImporterKey = std::pair; + llvm::DenseMap> Importers; + + auto GenASTsIfNeeded = [this, &AllASTs, &CodeSamples](StringRef Filename) { + if (!AllASTs.count(Filename)) { + auto Found = CodeSamples.find(Filename); + assert(Found != CodeSamples.end() && "Wrong file for import!"); + AllASTs[Filename] = createASTUnit(Filename, Found->getValue()); + } + }; + + for (const ImportAction &Action : ImportActions) { + StringRef FromFile = Action.FromFilename, ToFile = Action.ToFilename; + GenASTsIfNeeded(FromFile); + GenASTsIfNeeded(ToFile); + + ASTUnit *From = AllASTs[FromFile].get(); + ASTUnit *To = AllASTs[ToFile].get(); + + // Create a new importer if needed. + std::unique_ptr &ImporterRef = Importers[{From, To}]; + if (!ImporterRef) + ImporterRef.reset(new ASTImporter( + To->getASTContext(), To->getFileManager(), From->getASTContext(), + From->getFileManager(), false)); + + // Find the declaration and import it. + auto FoundDecl = match(Action.ImportPredicate.bind(DeclToImportID), + From->getASTContext()); + EXPECT_TRUE(FoundDecl.size() == 1); + const Decl *ToImport = selectFirst(DeclToImportID, FoundDecl); + auto Imported = importNode(From, To, *ImporterRef, ToImport); + EXPECT_TRUE(static_cast(Imported)); + if (!Imported) + llvm::consumeError(Imported.takeError()); + } + + // Find the declaration and import it. + auto FoundDecl = match(FinalSelectPredicate.bind(DeclToVerifyID), + AllASTs[FileForFinalCheck]->getASTContext()); + EXPECT_TRUE(FoundDecl.size() == 1); + const Decl *ToVerify = selectFirst(DeclToVerifyID, FoundDecl); + MatchVerifier Verifier; + EXPECT_TRUE(Verifier.match( + ToVerify, internal::BindableMatcher(VerificationMatcher))); + } +}; + +template RecordDecl *getRecordDecl(T *D) { + auto *ET = cast(D->getType().getTypePtr()); + return cast(ET->getNamedType().getTypePtr())->getDecl(); +} + template ::testing::AssertionResult isSuccess(llvm::Expected &ValOrErr) { if (ValOrErr) diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -18,7 +18,6 @@ #include "gtest/gtest.h" #include "ASTImporterFixtures.h" -#include "MatchVerifier.h" namespace clang { namespace ast_matchers { @@ -27,230 +26,6 @@ using internal::BindableMatcher; using llvm::StringMap; -// Base class for those tests which use the family of `testImport` functions. -class TestImportBase - : public CompilerOptionSpecificTest, - public ::testing::WithParamInterface> { - - template - llvm::Expected importNode(ASTUnit *From, ASTUnit *To, - ASTImporter &Importer, NodeType Node) { - ASTContext &ToCtx = To->getASTContext(); - - // Add 'From' file to virtual file system so importer can 'find' it - // while importing SourceLocations. It is safe to add same file multiple - // times - it just isn't replaced. - StringRef FromFileName = From->getMainFileName(); - createVirtualFileIfNeeded(To, FromFileName, - From->getBufferForFile(FromFileName)); - - auto Imported = Importer.Import(Node); - - if (Imported) { - // This should dump source locations and assert if some source locations - // were not imported. - SmallString<1024> ImportChecker; - llvm::raw_svector_ostream ToNothing(ImportChecker); - ToCtx.getTranslationUnitDecl()->print(ToNothing); - - // This traverses the AST to catch certain bugs like poorly or not - // implemented subtrees. - (*Imported)->dump(ToNothing); - } - - return Imported; - } - - template - testing::AssertionResult - testImport(const std::string &FromCode, - const std::vector &FromArgs, - const std::string &ToCode, const std::vector &ToArgs, - MatchVerifier &Verifier, - const BindableMatcher &SearchMatcher, - const BindableMatcher &VerificationMatcher) { - const char *const InputFileName = "input.cc"; - const char *const OutputFileName = "output.cc"; - - std::unique_ptr FromAST = tooling::buildASTFromCodeWithArgs( - FromCode, FromArgs, InputFileName), - ToAST = tooling::buildASTFromCodeWithArgs( - ToCode, ToArgs, OutputFileName); - - ASTContext &FromCtx = FromAST->getASTContext(), - &ToCtx = ToAST->getASTContext(); - - ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx, - FromAST->getFileManager(), false); - - auto FoundNodes = match(SearchMatcher, FromCtx); - if (FoundNodes.size() != 1) - return testing::AssertionFailure() - << "Multiple potential nodes were found!"; - - auto ToImport = selectFirst(DeclToImportID, FoundNodes); - if (!ToImport) - return testing::AssertionFailure() << "Node type mismatch!"; - - // Sanity check: the node being imported should match in the same way as - // the result node. - BindableMatcher WrapperMatcher(VerificationMatcher); - EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher)); - - auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport); - if (!Imported) { - std::string ErrorText; - handleAllErrors( - Imported.takeError(), - [&ErrorText](const ImportError &Err) { ErrorText = Err.message(); }); - return testing::AssertionFailure() - << "Import failed, error: \"" << ErrorText << "\"!"; - } - - return Verifier.match(*Imported, WrapperMatcher); - } - - template - testing::AssertionResult - testImport(const std::string &FromCode, - const std::vector &FromArgs, - const std::string &ToCode, const std::vector &ToArgs, - MatchVerifier &Verifier, - const BindableMatcher &VerificationMatcher) { - return testImport( - FromCode, FromArgs, ToCode, ToArgs, Verifier, - translationUnitDecl( - has(namedDecl(hasName(DeclToImportID)).bind(DeclToImportID))), - VerificationMatcher); - } - -protected: - std::vector getExtraArgs() const override { return GetParam(); } - -public: - - /// Test how AST node named "declToImport" located in the translation unit - /// of "FromCode" virtual file is imported to "ToCode" virtual file. - /// The verification is done by running AMatcher over the imported node. - template - void testImport(const std::string &FromCode, TestLanguage FromLang, - const std::string &ToCode, TestLanguage ToLang, - MatchVerifier &Verifier, - const MatcherType &AMatcher) { - std::vector FromArgs = getCommandLineArgsForLanguage(FromLang); - std::vector ToArgs = getCommandLineArgsForLanguage(ToLang); - EXPECT_TRUE( - testImport(FromCode, FromArgs, ToCode, ToArgs, Verifier, AMatcher)); - } - - struct ImportAction { - StringRef FromFilename; - StringRef ToFilename; - // FIXME: Generalize this to support other node kinds. - BindableMatcher ImportPredicate; - - ImportAction(StringRef FromFilename, StringRef ToFilename, - DeclarationMatcher ImportPredicate) - : FromFilename(FromFilename), ToFilename(ToFilename), - ImportPredicate(ImportPredicate) {} - - ImportAction(StringRef FromFilename, StringRef ToFilename, - const std::string &DeclName) - : FromFilename(FromFilename), ToFilename(ToFilename), - ImportPredicate(namedDecl(hasName(DeclName))) {} - }; - - using SingleASTUnit = std::unique_ptr; - using AllASTUnits = StringMap; - - struct CodeEntry { - std::string CodeSample; - TestLanguage Lang; - }; - - using CodeFiles = StringMap; - - /// Builds an ASTUnit for one potential compile options set. - SingleASTUnit createASTUnit(StringRef FileName, const CodeEntry &CE) const { - std::vector Args = getCommandLineArgsForLanguage(CE.Lang); - auto AST = tooling::buildASTFromCodeWithArgs(CE.CodeSample, Args, FileName); - EXPECT_TRUE(AST.get()); - return AST; - } - - /// Test an arbitrary sequence of imports for a set of given in-memory files. - /// The verification is done by running VerificationMatcher against a - /// specified AST node inside of one of given files. - /// \param CodeSamples Map whose key is the file name and the value is the - /// file content. - /// \param ImportActions Sequence of imports. Each import in sequence - /// specifies "from file" and "to file" and a matcher that is used for - /// searching a declaration for import in "from file". - /// \param FileForFinalCheck Name of virtual file for which the final check is - /// applied. - /// \param FinalSelectPredicate Matcher that specifies the AST node in the - /// FileForFinalCheck for which the verification will be done. - /// \param VerificationMatcher Matcher that will be used for verification - /// after all imports in sequence are done. - void testImportSequence(const CodeFiles &CodeSamples, - const std::vector &ImportActions, - StringRef FileForFinalCheck, - BindableMatcher FinalSelectPredicate, - BindableMatcher VerificationMatcher) { - AllASTUnits AllASTs; - using ImporterKey = std::pair; - llvm::DenseMap> Importers; - - auto GenASTsIfNeeded = [this, &AllASTs, &CodeSamples](StringRef Filename) { - if (!AllASTs.count(Filename)) { - auto Found = CodeSamples.find(Filename); - assert(Found != CodeSamples.end() && "Wrong file for import!"); - AllASTs[Filename] = createASTUnit(Filename, Found->getValue()); - } - }; - - for (const ImportAction &Action : ImportActions) { - StringRef FromFile = Action.FromFilename, ToFile = Action.ToFilename; - GenASTsIfNeeded(FromFile); - GenASTsIfNeeded(ToFile); - - ASTUnit *From = AllASTs[FromFile].get(); - ASTUnit *To = AllASTs[ToFile].get(); - - // Create a new importer if needed. - std::unique_ptr &ImporterRef = Importers[{From, To}]; - if (!ImporterRef) - ImporterRef.reset(new ASTImporter( - To->getASTContext(), To->getFileManager(), From->getASTContext(), - From->getFileManager(), false)); - - // Find the declaration and import it. - auto FoundDecl = match(Action.ImportPredicate.bind(DeclToImportID), - From->getASTContext()); - EXPECT_TRUE(FoundDecl.size() == 1); - const Decl *ToImport = selectFirst(DeclToImportID, FoundDecl); - auto Imported = importNode(From, To, *ImporterRef, ToImport); - EXPECT_TRUE(static_cast(Imported)); - if (!Imported) - llvm::consumeError(Imported.takeError()); - } - - // Find the declaration and import it. - auto FoundDecl = match(FinalSelectPredicate.bind(DeclToVerifyID), - AllASTs[FileForFinalCheck]->getASTContext()); - EXPECT_TRUE(FoundDecl.size() == 1); - const Decl *ToVerify = selectFirst(DeclToVerifyID, FoundDecl); - MatchVerifier Verifier; - EXPECT_TRUE( - Verifier.match(ToVerify, BindableMatcher(VerificationMatcher))); - } -}; - -template RecordDecl *getRecordDecl(T *D) { - auto *ET = cast(D->getType().getTypePtr()); - return cast(ET->getNamedType().getTypePtr())->getDecl(); -} - static const RecordDecl *getRecordDeclOfFriend(FriendDecl *FD) { QualType Ty = FD->getFriendType()->getType().getCanonicalType(); return cast(Ty)->getDecl();