//===- 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