brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.6 KiB · 2af69c6 Raw
475 lines · c
1//===- unittest/AST/ASTImporterFixtures.h - AST unit test support ---------===//2//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-exception6//7//===----------------------------------------------------------------------===//8//9/// \file10/// Fixture classes for testing the ASTImporter.11//12//===----------------------------------------------------------------------===//13 14#ifndef LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H15#define LLVM_CLANG_UNITTESTS_AST_IMPORTER_FIXTURES_H16 17#include "gmock/gmock.h"18 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"25 26#include "DeclMatcher.h"27#include "MatchVerifier.h"28 29#include <sstream>30 31namespace clang {32 33class ASTImporter;34class ASTImporterSharedState;35class ASTUnit;36 37namespace ast_matchers {38 39const StringRef DeclToImportID = "declToImport";40const StringRef DeclToVerifyID = "declToVerify";41 42// Creates a virtual file and assigns that to the context of given AST. If the43// file already exists then the file will not be created again as a duplicate.44void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,45                               std::unique_ptr<llvm::MemoryBuffer> &&Buffer);46 47void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,48                               StringRef Code);49 50// Common base for the different families of ASTImporter tests that are51// parameterized on the compiler options which may result a different AST. E.g.52// -fms-compatibility or -fdelayed-template-parsing.53class CompilerOptionSpecificTest : public ::testing::Test {54protected:55  // Return the extra arguments appended to runtime options at compilation.56  virtual std::vector<std::string> getExtraArgs() const { return {}; }57 58  // Returns the argument vector used for a specific language option, this set59  // 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) {65      Args.push_back(Arg);66    }67    return Args;68  }69};70 71const 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"}}};78 79const auto DefaultTestValuesForRunOptions =80    ::testing::ValuesIn(DefaultTestArrayForRunOptions);81 82// This class provides generic methods to write tests which can check internal83// attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also,84// this fixture makes it possible to import from several "From" contexts.85class ASTImporterTestBase : public CompilerOptionSpecificTest {86 87  const char *const InputFileName = "input.cc";88  const char *const OutputFileName = "output.cc";89 90public: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)>95      ImporterConstructor;96 97  // ODR handling type for the AST importer.98  ASTImporter::ODRHandlingType ODRHandling;99 100  // The lambda that constructs the ASTImporter we use in this test.101  ImporterConstructor Creator;102 103private:104  // Buffer for the To context, must live in the test scope.105  std::string ToCode;106 107  // Represents a "From" translation unit and holds an importer object which we108  // use to import from this translation unit.109  struct TU {110    // Buffer for the context, must live in the test scope.111    std::string Code;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;118 119    TU(StringRef Code, StringRef FileName, std::vector<std::string> Args,120       ImporterConstructor C = ImporterConstructor(),121       ASTImporter::ODRHandlingType ODRHandling =122           ASTImporter::ODRHandlingType::Conservative);123    ~TU();124 125    void126    lazyInitImporter(const std::shared_ptr<ASTImporterSharedState> &SharedState,127                     ASTUnit *ToAST);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);135  };136 137  // We may have several From contexts and related translation units. In each138  // AST, the buffers for the source are handled via references and are set139  // during the creation of the AST. These references must point to a valid140  // buffer until the AST is alive. Thus, we must use a list in order to avoid141  // moving of the stored objects because that would mean breaking the142  // references in the AST. By using a vector a move could happen when the143  // vector is expanding, with the list we won't have these issues.144  std::list<TU> FromTUs;145 146  // Initialize the shared state if not initialized already.147  void lazyInitSharedState(TranslationUnitDecl *ToTU);148 149  void lazyInitToAST(TestLanguage ToLang, StringRef ToSrcCode,150                     StringRef FileName);151 152protected:153  std::shared_ptr<ASTImporterSharedState> SharedStatePtr;154 155public:156  // We may have several From context but only one To context.157  std::unique_ptr<ASTUnit> ToAST;158 159  // Returns with the TU associated with the given Decl.160  TU *findFromTU(Decl *From);161 162  // Creates an AST both for the From and To source code and imports the Decl163  // 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);169 170  // Creates a TU decl for the given source code which can be used as a From171  // context.  May be called several times in a given test (with different file172  // name).173  TranslationUnitDecl *getTuDecl(StringRef SrcCode, TestLanguage Lang,174                                 StringRef FileName = "input.cc");175 176  // Creates the To context with the given source code and returns the TU decl.177  TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, TestLanguage ToLang);178 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);183 184  template <class DeclT> DeclT *Import(DeclT *From, TestLanguage Lang) {185    return cast_or_null<DeclT>(Import(cast<Decl>(From), Lang));186  }187 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);191 192  QualType ImportType(QualType FromType, Decl *TUDecl, TestLanguage ToLang);193 194  ASTImporterTestBase()195      : ODRHandling(ASTImporter::ODRHandlingType::Conservative) {}196  ~ASTImporterTestBase();197};198 199class ASTImporterOptionSpecificTestBase200    : public ASTImporterTestBase,201      public ::testing::WithParamInterface<std::vector<std::string>> {202protected:203  std::vector<std::string> getExtraArgs() const override { return GetParam(); }204};205 206// Base class for those tests which use the family of `testImport` functions.207class TestImportBase208    : public CompilerOptionSpecificTest,209      public ::testing::WithParamInterface<std::vector<std::string>> {210 211  template <typename NodeType>212  llvm::Expected<NodeType> importNode(ASTUnit *From, ASTUnit *To,213                                      ASTImporter &Importer, NodeType Node) {214    ASTContext &ToCtx = To->getASTContext();215 216    // Add 'From' file to virtual file system so importer can 'find' it217    // while importing SourceLocations. It is safe to add same file multiple218    // times - it just isn't replaced.219    StringRef FromFileName = From->getMainFileName();220    createVirtualFileIfNeeded(To, FromFileName,221                              From->getBufferForFile(FromFileName));222 223    auto Imported = Importer.Import(Node);224 225    if (Imported) {226      // This should dump source locations and assert if some source locations227      // were not imported.228      SmallString<1024> ImportChecker;229      llvm::raw_svector_ostream ToNothing(ImportChecker);230      ToCtx.getTranslationUnitDecl()->print(ToNothing);231 232      // This traverses the AST to catch certain bugs like poorly or not233      // implemented subtrees.234      (*Imported)->dump(ToNothing);235    }236 237    return Imported;238  }239 240  template <typename NodeType>241  testing::AssertionResult242  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";250 251    std::unique_ptr<ASTUnit> FromAST = tooling::buildASTFromCodeWithArgs(252                                 FromCode, FromArgs, InputFileName),253                             ToAST = tooling::buildASTFromCodeWithArgs(254                                 ToCode, ToArgs, OutputFileName);255 256    ASTContext &FromCtx = FromAST->getASTContext(),257               &ToCtx = ToAST->getASTContext();258 259    ASTImporter Importer(ToCtx, ToAST->getFileManager(), FromCtx,260                         FromAST->getFileManager(), false);261 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!";268 269    auto ToImport = selectFirst<NodeType>(DeclToImportID, FoundNodes);270    if (!ToImport)271      return testing::AssertionFailure() << "Node type mismatch!";272 273    // The node being imported should match in the same way as274    // the result node.275    internal::BindableMatcher<NodeType> WrapperMatcher(VerificationMatcher);276    EXPECT_TRUE(Verifier.match(ToImport, WrapperMatcher));277 278    auto Imported = importNode(FromAST.get(), ToAST.get(), Importer, ToImport);279    if (!Imported) {280      std::string ErrorText;281      handleAllErrors(Imported.takeError(),282                      [&ErrorText](const ASTImportError &Err) {283                        ErrorText = Err.message();284                      });285      return testing::AssertionFailure()286             << "Import failed, error: \"" << ErrorText << "\"!";287    }288 289    return Verifier.match(*Imported, WrapperMatcher);290  }291 292  template <typename NodeType>293  testing::AssertionResult294  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) {299    return testImport(300        FromCode, FromArgs, ToCode, ToArgs, Verifier,301        translationUnitDecl(302            has(namedDecl(hasName(DeclToImportID)).bind(DeclToImportID))),303        VerificationMatcher);304  }305 306protected:307  std::vector<std::string> getExtraArgs() const override { return GetParam(); }308 309public:310  /// Test how AST node named "declToImport" located in the translation unit311  /// 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);320    EXPECT_TRUE(321        testImport(FromCode, FromArgs, ToCode, ToArgs, Verifier, AMatcher));322  }323 324  struct ImportAction {325    StringRef FromFilename;326    StringRef ToFilename;327    // FIXME: Generalize this to support other node kinds.328    internal::BindableMatcher<Decl> ImportPredicate;329 330    ImportAction(StringRef FromFilename, StringRef ToFilename,331                 DeclarationMatcher ImportPredicate)332        : FromFilename(FromFilename), ToFilename(ToFilename),333          ImportPredicate(ImportPredicate) {}334 335    ImportAction(StringRef FromFilename, StringRef ToFilename,336                 const std::string &DeclName)337        : FromFilename(FromFilename), ToFilename(ToFilename),338          ImportPredicate(namedDecl(hasName(DeclName))) {}339  };340 341  using SingleASTUnit = std::unique_ptr<ASTUnit>;342  using AllASTUnits = llvm::StringMap<SingleASTUnit>;343 344  struct CodeEntry {345    std::string CodeSample;346    TestLanguage Lang;347  };348 349  using CodeFiles = llvm::StringMap<CodeEntry>;350 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());356    return AST;357  }358 359  /// Test an arbitrary sequence of imports for a set of given in-memory files.360  /// The verification is done by running VerificationMatcher against a361  /// specified AST node inside of one of given files.362  /// \param CodeSamples Map whose key is the file name and the value is the363  /// file content.364  /// \param ImportActions Sequence of imports. Each import in sequence365  /// specifies "from file" and "to file" and a matcher that is used for366  /// searching a declaration for import in "from file".367  /// \param FileForFinalCheck Name of virtual file for which the final check is368  /// applied.369  /// \param FinalSelectPredicate Matcher that specifies the AST node in the370  /// FileForFinalCheck for which the verification will be done.371  /// \param VerificationMatcher Matcher that will be used for verification372  /// 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) {378    AllASTUnits AllASTs;379    using ImporterKey = std::pair<const ASTUnit *, const ASTUnit *>;380    llvm::DenseMap<ImporterKey, std::unique_ptr<ASTImporter>> Importers;381 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());387      }388    };389 390    for (const ImportAction &Action : ImportActions) {391      StringRef FromFile = Action.FromFilename, ToFile = Action.ToFilename;392      GenASTsIfNeeded(FromFile);393      GenASTsIfNeeded(ToFile);394 395      ASTUnit *From = AllASTs[FromFile].get();396      ASTUnit *To = AllASTs[ToFile].get();397 398      // Create a new importer if needed.399      std::unique_ptr<ASTImporter> &ImporterRef = Importers[{From, To}];400      if (!ImporterRef)401        ImporterRef.reset(new ASTImporter(402            To->getASTContext(), To->getFileManager(), From->getASTContext(),403            From->getFileManager(), false));404 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));412      if (!Imported)413        llvm::consumeError(Imported.takeError());414    }415 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)));424  }425};426 427template <typename T> RecordDecl *getRecordDecl(T *D) {428  return D->getType()->getAsRecordDecl();429}430 431template <class T>432::testing::AssertionResult isSuccess(llvm::Expected<T> &ValOrErr) {433  if (ValOrErr)434    return ::testing::AssertionSuccess() << "Expected<> contains no error.";435  else436    return ::testing::AssertionFailure()437           << "Expected<> contains error: " << toString(ValOrErr.takeError());438}439 440template <class T>441::testing::AssertionResult isImportError(llvm::Expected<T> &ValOrErr,442                                         ASTImportError::ErrorKind Kind) {443  if (ValOrErr) {444    return ::testing::AssertionFailure() << "Expected<> is expected to contain "445                                            "error but does contain value \""446                                         << (*ValOrErr) << "\"";447  } else {448    std::ostringstream OS;449    bool Result = false;450    auto Err = llvm::handleErrors(451        ValOrErr.takeError(), [&OS, &Result, Kind](clang::ASTImportError &IE) {452          if (IE.Error == Kind) {453            Result = true;454            OS << "Expected<> contains an ImportError " << IE.toString();455          } else {456            OS << "Expected<> contains an ImportError " << IE.toString()457               << " instead of kind " << Kind;458          }459        });460    if (Err) {461      OS << "Expected<> contains unexpected error: "462         << toString(std::move(Err));463    }464    if (Result)465      return ::testing::AssertionSuccess() << OS.str();466    else467      return ::testing::AssertionFailure() << OS.str();468  }469}470 471} // end namespace ast_matchers472} // end namespace clang473 474#endif475