| //===- unittest/AST/ASTImporterFixtures.h - AST unit test support ---------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| /// \file |
| /// Fixture classes for testing the ASTImporter. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H |
| #define LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H |
| |
| #include "gmock/gmock.h" |
| |
| #include "clang/AST/ASTImporter.h" |
| #include "clang/AST/ASTImporterSharedState.h" |
| #include "clang/Frontend/ASTUnit.h" |
| #include "llvm/Support/Error.h" |
| #include "llvm/Support/ErrorHandling.h" |
| |
| #include "DeclMatcher.h" |
| #include "Language.h" |
| |
| #include <sstream> |
| |
| namespace clang { |
| |
| class ASTImporter; |
| class ASTImporterSharedState; |
| class ASTUnit; |
| |
| namespace ast_matchers { |
| |
| const StringRef DeclToImportID = "declToImport"; |
| const StringRef DeclToVerifyID = "declToVerify"; |
| |
| // Creates a virtual file and assigns that to the context of given AST. If the |
| // file already exists then the file will not be created again as a duplicate. |
| void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, |
| std::unique_ptr<llvm::MemoryBuffer> &&Buffer); |
| |
| void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName, |
| StringRef Code); |
| |
| // Common base for the different families of ASTImporter tests that are |
| // parameterized on the compiler options which may result a different AST. E.g. |
| // -fms-compatibility or -fdelayed-template-parsing. |
| class CompilerOptionSpecificTest : public ::testing::Test { |
| protected: |
| // Return the extra arguments appended to runtime options at compilation. |
| virtual ArgVector getExtraArgs() const { return ArgVector(); } |
| |
| // Returns the argument vector used for a specific language option, this set |
| // can be tweaked by the test parameters. |
| ArgVector getArgVectorForLanguage(Language Lang) const { |
| ArgVector Args = getBasicRunOptionsForLanguage(Lang); |
| ArgVector ExtraArgs = getExtraArgs(); |
| for (const auto &Arg : ExtraArgs) { |
| Args.push_back(Arg); |
| } |
| return Args; |
| } |
| }; |
| |
| const auto DefaultTestValuesForRunOptions = ::testing::Values( |
| ArgVector(), ArgVector{"-fdelayed-template-parsing"}, |
| ArgVector{"-fms-compatibility"}, |
| ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"}); |
| |
| // This class provides generic methods to write tests which can check internal |
| // attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also, |
| // this fixture makes it possible to import from several "From" contexts. |
| class ASTImporterTestBase : public CompilerOptionSpecificTest { |
| |
| const char *const InputFileName = "input.cc"; |
| const char *const OutputFileName = "output.cc"; |
| |
| public: |
| /// Allocates an ASTImporter (or one of its subclasses). |
| typedef std::function<ASTImporter *( |
| ASTContext &, FileManager &, ASTContext &, FileManager &, bool, |
| const std::shared_ptr<ASTImporterSharedState> &SharedState)> |
| ImporterConstructor; |
| |
| // ODR handling type for the AST importer. |
| ASTImporter::ODRHandlingType ODRHandling; |
| |
| // The lambda that constructs the ASTImporter we use in this test. |
| ImporterConstructor Creator; |
| |
| private: |
| // Buffer for the To context, must live in the test scope. |
| std::string ToCode; |
| |
| // Represents a "From" translation unit and holds an importer object which we |
| // use to import from this translation unit. |
| struct TU { |
| // Buffer for the context, must live in the test scope. |
| std::string Code; |
| std::string FileName; |
| std::unique_ptr<ASTUnit> Unit; |
| TranslationUnitDecl *TUDecl = nullptr; |
| std::unique_ptr<ASTImporter> Importer; |
| ImporterConstructor Creator; |
| ASTImporter::ODRHandlingType ODRHandling; |
| |
| TU(StringRef Code, StringRef FileName, ArgVector Args, |
| ImporterConstructor C = ImporterConstructor(), |
| ASTImporter::ODRHandlingType ODRHandling = |
| ASTImporter::ODRHandlingType::Conservative); |
| ~TU(); |
| |
| void |
| lazyInitImporter(const std::shared_ptr<ASTImporterSharedState> &SharedState, |
| ASTUnit *ToAST); |
| Decl *import(const std::shared_ptr<ASTImporterSharedState> &SharedState, |
| ASTUnit *ToAST, Decl *FromDecl); |
| llvm::Expected<Decl *> |
| importOrError(const std::shared_ptr<ASTImporterSharedState> &SharedState, |
| ASTUnit *ToAST, Decl *FromDecl); |
| QualType import(const std::shared_ptr<ASTImporterSharedState> &SharedState, |
| ASTUnit *ToAST, QualType FromType); |
| }; |
| |
| // We may have several From contexts and related translation units. In each |
| // AST, the buffers for the source are handled via references and are set |
| // during the creation of the AST. These references must point to a valid |
| // buffer until the AST is alive. Thus, we must use a list in order to avoid |
| // moving of the stored objects because that would mean breaking the |
| // references in the AST. By using a vector a move could happen when the |
| // vector is expanding, with the list we won't have these issues. |
| std::list<TU> FromTUs; |
| |
| // Initialize the shared state if not initialized already. |
| void lazyInitSharedState(TranslationUnitDecl *ToTU); |
| |
| void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName); |
| |
| protected: |
| std::shared_ptr<ASTImporterSharedState> SharedStatePtr; |
| |
| public: |
| // We may have several From context but only one To context. |
| std::unique_ptr<ASTUnit> ToAST; |
| |
| // Returns with the TU associated with the given Decl. |
| TU *findFromTU(Decl *From); |
| |
| // Creates an AST both for the From and To source code and imports the Decl |
| // of the identifier into the To context. |
| // Must not be called more than once within the same test. |
| std::tuple<Decl *, Decl *> |
| getImportedDecl(StringRef FromSrcCode, Language FromLang, StringRef ToSrcCode, |
| Language ToLang, StringRef Identifier = DeclToImportID); |
| |
| // Creates a TU decl for the given source code which can be used as a From |
| // context. May be called several times in a given test (with different file |
| // name). |
| TranslationUnitDecl *getTuDecl(StringRef SrcCode, Language Lang, |
| StringRef FileName = "input.cc"); |
| |
| // Creates the To context with the given source code and returns the TU decl. |
| TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang); |
| |
| // Import the given Decl into the ToCtx. |
| // May be called several times in a given test. |
| // The different instances of the param From may have different ASTContext. |
| Decl *Import(Decl *From, Language ToLang); |
| |
| template <class DeclT> DeclT *Import(DeclT *From, Language Lang) { |
| return cast_or_null<DeclT>(Import(cast<Decl>(From), Lang)); |
| } |
| |
| // Import the given Decl into the ToCtx. |
| // Same as Import but returns the result of the import which can be an error. |
| llvm::Expected<Decl *> importOrError(Decl *From, Language ToLang); |
| |
| QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang); |
| |
| ASTImporterTestBase() |
| : ODRHandling(ASTImporter::ODRHandlingType::Conservative) {} |
| ~ASTImporterTestBase(); |
| }; |
| |
| class ASTImporterOptionSpecificTestBase |
| : public ASTImporterTestBase, |
| public ::testing::WithParamInterface<ArgVector> { |
| protected: |
| ArgVector getExtraArgs() const override { return GetParam(); } |
| }; |
| |
| template <class T> |
| ::testing::AssertionResult isSuccess(llvm::Expected<T> &ValOrErr) { |
| if (ValOrErr) |
| return ::testing::AssertionSuccess() << "Expected<> contains no error."; |
| else |
| return ::testing::AssertionFailure() |
| << "Expected<> contains error: " << toString(ValOrErr.takeError()); |
| } |
| |
| template <class T> |
| ::testing::AssertionResult isImportError(llvm::Expected<T> &ValOrErr, |
| ImportError::ErrorKind Kind) { |
| if (ValOrErr) { |
| return ::testing::AssertionFailure() << "Expected<> is expected to contain " |
| "error but does contain value \"" |
| << (*ValOrErr) << "\""; |
| } else { |
| std::ostringstream OS; |
| bool Result = false; |
| auto Err = llvm::handleErrors( |
| ValOrErr.takeError(), [&OS, &Result, Kind](clang::ImportError &IE) { |
| if (IE.Error == Kind) { |
| Result = true; |
| OS << "Expected<> contains an ImportError " << IE.toString(); |
| } else { |
| OS << "Expected<> contains an ImportError " << IE.toString() |
| << " instead of kind " << Kind; |
| } |
| }); |
| if (Err) { |
| OS << "Expected<> contains unexpected error: " |
| << toString(std::move(Err)); |
| } |
| if (Result) |
| return ::testing::AssertionSuccess() << OS.str(); |
| else |
| return ::testing::AssertionFailure() << OS.str(); |
| } |
| } |
| |
| } // end namespace ast_matchers |
| } // end namespace clang |
| |
| #endif |