//===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Tests for the correct import of AST nodes from one AST context to another.
//
//===----------------------------------------------------------------------===//

#include "clang/ASTMatchers/ASTMatchers.h"
#include "llvm/ADT/StringMap.h"

#include "clang/AST/DeclContextInternals.h"
#include "gtest/gtest.h"

#include "ASTImporterFixtures.h"
#include "MatchVerifier.h"

namespace clang {
namespace ast_matchers {

using internal::Matcher;
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<ArgVector> {

  template <typename NodeType>
  llvm::Expected<NodeType> 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 <typename NodeType>
  testing::AssertionResult
  testImport(const std::string &FromCode, const ArgVector &FromArgs,
             const std::string &ToCode, const ArgVector &ToArgs,
             MatchVerifier<NodeType> &Verifier,
             const BindableMatcher<NodeType> &SearchMatcher,
             const BindableMatcher<NodeType> &VerificationMatcher) {
    const char *const InputFileName = "input.cc";
    const char *const OutputFileName = "output.cc";

    std::unique_ptr<ASTUnit> 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<NodeType>(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<NodeType> 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 <typename NodeType>
  testing::AssertionResult
  testImport(const std::string &FromCode, const ArgVector &FromArgs,
             const std::string &ToCode, const ArgVector &ToArgs,
             MatchVerifier<NodeType> &Verifier,
             const BindableMatcher<NodeType> &VerificationMatcher) {
    return testImport(
        FromCode, FromArgs, ToCode, ToArgs, Verifier,
        translationUnitDecl(
            has(namedDecl(hasName(DeclToImportID)).bind(DeclToImportID))),
        VerificationMatcher);
  }

protected:
  ArgVector 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 <typename NodeType, typename MatcherType>
  void testImport(const std::string &FromCode, Language FromLang,
                  const std::string &ToCode, Language ToLang,
                  MatchVerifier<NodeType> &Verifier,
                  const MatcherType &AMatcher) {
    ArgVector FromArgs = getArgVectorForLanguage(FromLang),
              ToArgs = getArgVectorForLanguage(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<Decl> 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<ASTUnit>;
  using AllASTUnits = StringMap<SingleASTUnit>;

  struct CodeEntry {
    std::string CodeSample;
    Language Lang;
  };

  using CodeFiles = StringMap<CodeEntry>;

  /// Builds an ASTUnit for one potential compile options set.
  SingleASTUnit createASTUnit(StringRef FileName, const CodeEntry &CE) const {
    ArgVector Args = getArgVectorForLanguage(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<ImportAction> &ImportActions,
                          StringRef FileForFinalCheck,
                          BindableMatcher<Decl> FinalSelectPredicate,
                          BindableMatcher<Decl> VerificationMatcher) {
    AllASTUnits AllASTs;
    using ImporterKey = std::pair<const ASTUnit *, const ASTUnit *>;
    llvm::DenseMap<ImporterKey, std::unique_ptr<ASTImporter>> 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<ASTImporter> &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<Decl>(DeclToImportID, FoundDecl);
      auto Imported = importNode(From, To, *ImporterRef, ToImport);
      EXPECT_TRUE(static_cast<bool>(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<Decl>(DeclToVerifyID, FoundDecl);
    MatchVerifier<Decl> Verifier;
    EXPECT_TRUE(
        Verifier.match(ToVerify, BindableMatcher<Decl>(VerificationMatcher)));
  }
};

template <typename T> RecordDecl *getRecordDecl(T *D) {
  auto *ET = cast<ElaboratedType>(D->getType().getTypePtr());
  return cast<RecordType>(ET->getNamedType().getTypePtr())->getDecl();
}

static const RecordDecl *getRecordDeclOfFriend(FriendDecl *FD) {
  QualType Ty = FD->getFriendType()->getType().getCanonicalType();
  return cast<RecordType>(Ty)->getDecl();
}

struct ImportExpr : TestImportBase {};
struct ImportType : TestImportBase {};
struct ImportDecl : TestImportBase {};

struct CanonicalRedeclChain : ASTImporterOptionSpecificTestBase {};

TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers) {
  Decl *FromTU = getTuDecl("void f();", Lang_CXX);
  auto Pattern = functionDecl(hasName("f"));
  auto *D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);

  auto Redecls = getCanonicalForwardRedeclChain(D0);
  ASSERT_EQ(Redecls.size(), 1u);
  EXPECT_EQ(D0, Redecls[0]);
}

TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers2) {
  Decl *FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX);
  auto Pattern = functionDecl(hasName("f"));
  auto *D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
  auto *D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
  FunctionDecl *D1 = D2->getPreviousDecl();

  auto Redecls = getCanonicalForwardRedeclChain(D0);
  ASSERT_EQ(Redecls.size(), 3u);
  EXPECT_EQ(D0, Redecls[0]);
  EXPECT_EQ(D1, Redecls[1]);
  EXPECT_EQ(D2, Redecls[2]);
}

TEST_P(CanonicalRedeclChain, ShouldBeSameForAllDeclInTheChain) {
  Decl *FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX);
  auto Pattern = functionDecl(hasName("f"));
  auto *D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
  auto *D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
  FunctionDecl *D1 = D2->getPreviousDecl();

  auto RedeclsD0 = getCanonicalForwardRedeclChain(D0);
  auto RedeclsD1 = getCanonicalForwardRedeclChain(D1);
  auto RedeclsD2 = getCanonicalForwardRedeclChain(D2);

  EXPECT_THAT(RedeclsD0, ::testing::ContainerEq(RedeclsD1));
  EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2));
}

namespace {
struct RedirectingImporter : public ASTImporter {
  using ASTImporter::ASTImporter;

protected:
  llvm::Expected<Decl *> ImportImpl(Decl *FromD) override {
    auto *ND = dyn_cast<NamedDecl>(FromD);
    if (!ND || ND->getName() != "shouldNotBeImported")
      return ASTImporter::ImportImpl(FromD);
    for (Decl *D : getToContext().getTranslationUnitDecl()->decls()) {
      if (auto *ND = dyn_cast<NamedDecl>(D))
        if (ND->getName() == "realDecl") {
          RegisterImportedDecl(FromD, ND);
          return ND;
        }
    }
    return ASTImporter::ImportImpl(FromD);
  }
};

} // namespace

struct RedirectingImporterTest : ASTImporterOptionSpecificTestBase {
  RedirectingImporterTest() {
    Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
                 ASTContext &FromContext, FileManager &FromFileManager,
                 bool MinimalImport,
                 const std::shared_ptr<ASTImporterSharedState> &SharedState) {
      return new RedirectingImporter(ToContext, ToFileManager, FromContext,
                                     FromFileManager, MinimalImport,
                                     SharedState);
    };
  }
};

// Test that an ASTImporter subclass can intercept an import call.
TEST_P(RedirectingImporterTest, InterceptImport) {
  Decl *From, *To;
  std::tie(From, To) =
      getImportedDecl("class shouldNotBeImported {};", Lang_CXX,
                      "class realDecl {};", Lang_CXX, "shouldNotBeImported");
  auto *Imported = cast<CXXRecordDecl>(To);
  EXPECT_EQ(Imported->getQualifiedNameAsString(), "realDecl");

  // Make sure our importer prevented the importing of the decl.
  auto *ToTU = Imported->getTranslationUnitDecl();
  auto Pattern = functionDecl(hasName("shouldNotBeImported"));
  unsigned count =
      DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern);
  EXPECT_EQ(0U, count);
}

// Test that when we indirectly import a declaration the custom ASTImporter
// is still intercepting the import.
TEST_P(RedirectingImporterTest, InterceptIndirectImport) {
  Decl *From, *To;
  std::tie(From, To) =
      getImportedDecl("class shouldNotBeImported {};"
                      "class F { shouldNotBeImported f; };",
                      Lang_CXX, "class realDecl {};", Lang_CXX, "F");

  // Make sure our ASTImporter prevented the importing of the decl.
  auto *ToTU = To->getTranslationUnitDecl();
  auto Pattern = functionDecl(hasName("shouldNotBeImported"));
  unsigned count =
      DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern);
  EXPECT_EQ(0U, count);
}

struct ImportPath : ASTImporterOptionSpecificTestBase {
  Decl *FromTU;
  FunctionDecl *D0, *D1, *D2;
  ImportPath() {
    FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX);
    auto Pattern = functionDecl(hasName("f"));
    D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
    D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
    D1 = D2->getPreviousDecl();
  }
};

TEST_P(ImportPath, Push) {
  ASTImporter::ImportPathTy path;
  path.push(D0);
  EXPECT_FALSE(path.hasCycleAtBack());
}

TEST_P(ImportPath, SmallCycle) {
  ASTImporter::ImportPathTy path;
  path.push(D0);
  path.push(D0);
  EXPECT_TRUE(path.hasCycleAtBack());
  path.pop();
  EXPECT_FALSE(path.hasCycleAtBack());
  path.push(D0);
  EXPECT_TRUE(path.hasCycleAtBack());
}

TEST_P(ImportPath, GetSmallCycle) {
  ASTImporter::ImportPathTy path;
  path.push(D0);
  path.push(D0);
  EXPECT_TRUE(path.hasCycleAtBack());
  std::array<Decl* ,2> Res;
  int i = 0;
  for (Decl *Di : path.getCycleAtBack()) {
    Res[i++] = Di;
  }
  ASSERT_EQ(i, 2);
  EXPECT_EQ(Res[0], D0);
  EXPECT_EQ(Res[1], D0);
}

TEST_P(ImportPath, GetCycle) {
  ASTImporter::ImportPathTy path;
  path.push(D0);
  path.push(D1);
  path.push(D2);
  path.push(D0);
  EXPECT_TRUE(path.hasCycleAtBack());
  std::array<Decl* ,4> Res;
  int i = 0;
  for (Decl *Di : path.getCycleAtBack()) {
    Res[i++] = Di;
  }
  ASSERT_EQ(i, 4);
  EXPECT_EQ(Res[0], D0);
  EXPECT_EQ(Res[1], D2);
  EXPECT_EQ(Res[2], D1);
  EXPECT_EQ(Res[3], D0);
}

TEST_P(ImportPath, CycleAfterCycle) {
  ASTImporter::ImportPathTy path;
  path.push(D0);
  path.push(D1);
  path.push(D0);
  path.push(D1);
  path.push(D2);
  path.push(D0);
  EXPECT_TRUE(path.hasCycleAtBack());
  std::array<Decl* ,4> Res;
  int i = 0;
  for (Decl *Di : path.getCycleAtBack()) {
    Res[i++] = Di;
  }
  ASSERT_EQ(i, 4);
  EXPECT_EQ(Res[0], D0);
  EXPECT_EQ(Res[1], D2);
  EXPECT_EQ(Res[2], D1);
  EXPECT_EQ(Res[3], D0);

  path.pop();
  path.pop();
  path.pop();
  EXPECT_TRUE(path.hasCycleAtBack());
  i = 0;
  for (Decl *Di : path.getCycleAtBack()) {
    Res[i++] = Di;
  }
  ASSERT_EQ(i, 3);
  EXPECT_EQ(Res[0], D0);
  EXPECT_EQ(Res[1], D1);
  EXPECT_EQ(Res[2], D0);

  path.pop();
  EXPECT_FALSE(path.hasCycleAtBack());
}

TEST_P(ImportExpr, ImportStringLiteral) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { (void)\"foo\"; }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          stringLiteral(hasType(asString("const char [4]"))))));
  testImport(
      "void declToImport() { (void)L\"foo\"; }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          stringLiteral(hasType(asString("const wchar_t [4]"))))));
  testImport(
      "void declToImport() { (void) \"foo\" \"bar\"; }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          stringLiteral(hasType(asString("const char [7]"))))));
}

TEST_P(ImportExpr, ImportChooseExpr) {
  MatchVerifier<Decl> Verifier;

  // This case tests C code that is not condition-dependent and has a true
  // condition.
  testImport(
    "void declToImport() { (void)__builtin_choose_expr(1, 2, 3); }",
    Lang_C, "", Lang_C, Verifier,
    functionDecl(hasDescendant(chooseExpr())));
}

TEST_P(ImportExpr, ImportGNUNullExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { (void)__null; }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(gnuNullExpr(hasType(isInteger())))));
}

TEST_P(ImportExpr, ImportCXXNullPtrLiteralExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { (void)nullptr; }",
      Lang_CXX11, "", Lang_CXX11, Verifier,
      functionDecl(hasDescendant(cxxNullPtrLiteralExpr())));
}


TEST_P(ImportExpr, ImportFloatinglLiteralExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { (void)1.0; }",
      Lang_C, "", Lang_C, Verifier,
      functionDecl(hasDescendant(
          floatLiteral(equals(1.0), hasType(asString("double"))))));
  testImport(
      "void declToImport() { (void)1.0e-5f; }",
      Lang_C, "", Lang_C, Verifier,
      functionDecl(hasDescendant(
          floatLiteral(equals(1.0e-5f), hasType(asString("float"))))));
}

TEST_P(ImportExpr, ImportImaginaryLiteralExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { (void)1.0i; }",
      Lang_CXX14, "", Lang_CXX14, Verifier,
      functionDecl(hasDescendant(imaginaryLiteral())));
}

TEST_P(ImportExpr, ImportCompoundLiteralExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() {"
      "  struct s { int x; long y; unsigned z; }; "
      "  (void)(struct s){ 42, 0L, 1U }; }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          compoundLiteralExpr(
              hasType(asString("struct s")),
              has(initListExpr(
                  hasType(asString("struct s")),
                  has(integerLiteral(
                      equals(42), hasType(asString("int")))),
                  has(integerLiteral(
                      equals(0), hasType(asString("long")))),
                  has(integerLiteral(
                      equals(1), hasType(asString("unsigned int"))))))))));
}

TEST_P(ImportExpr, ImportCXXThisExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "class declToImport { void f() { (void)this; } };",
      Lang_CXX, "", Lang_CXX, Verifier,
      cxxRecordDecl(
          hasMethod(
              hasDescendant(
                  cxxThisExpr(
                      hasType(
                          asString("class declToImport *")))))));
}

TEST_P(ImportExpr, ImportAtomicExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { int *ptr; __atomic_load_n(ptr, 1); }",
      Lang_C, "", Lang_C, Verifier,
      functionDecl(hasDescendant(
          atomicExpr(
              has(ignoringParenImpCasts(
                  declRefExpr(hasDeclaration(varDecl(hasName("ptr"))),
                      hasType(asString("int *"))))),
              has(integerLiteral(equals(1), hasType(asString("int"))))))));
}

TEST_P(ImportExpr, ImportLabelDeclAndAddrLabelExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { loop: goto loop; (void)&&loop; }",
      Lang_C, "", Lang_C, Verifier,
      functionDecl(
          hasDescendant(
              labelStmt(hasDeclaration(labelDecl(hasName("loop"))))),
          hasDescendant(
              addrLabelExpr(hasDeclaration(labelDecl(hasName("loop")))))));
}

AST_MATCHER_P(TemplateDecl, hasTemplateDecl,
              internal::Matcher<NamedDecl>, InnerMatcher) {
  const NamedDecl *Template = Node.getTemplatedDecl();
  return Template && InnerMatcher.matches(*Template, Finder, Builder);
}

TEST_P(ImportExpr, ImportParenListExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template<typename T> class dummy { void f() { dummy X(*this); } };"
      "typedef dummy<int> declToImport;"
      "template class dummy<int>;",
      Lang_CXX, "", Lang_CXX, Verifier,
      typedefDecl(hasType(templateSpecializationType(
          hasDeclaration(classTemplateSpecializationDecl(hasSpecializedTemplate(
              classTemplateDecl(hasTemplateDecl(cxxRecordDecl(hasMethod(allOf(
                  hasName("f"),
                  hasBody(compoundStmt(has(declStmt(hasSingleDecl(
                      varDecl(hasInitializer(parenListExpr(has(unaryOperator(
                          hasOperatorName("*"),
                          hasUnaryOperand(cxxThisExpr())))))))))))))))))))))));
}

TEST_P(ImportExpr, ImportSwitch) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { int b; switch (b) { case 1: break; } }",
      Lang_C, "", Lang_C, Verifier,
      functionDecl(hasDescendant(
          switchStmt(has(compoundStmt(has(caseStmt())))))));
}

TEST_P(ImportExpr, ImportStmtExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
    "void declToImport() { int b; int a = b ?: 1; int C = ({int X=4; X;}); }",
    Lang_C, "", Lang_C, Verifier,
    functionDecl(hasDescendant(
        varDecl(
            hasName("C"),
            hasType(asString("int")),
            hasInitializer(
                stmtExpr(
                    hasAnySubstatement(declStmt(hasSingleDecl(
                        varDecl(
                            hasName("X"),
                            hasType(asString("int")),
                            hasInitializer(
                                integerLiteral(equals(4))))))),
                    hasDescendant(
                        implicitCastExpr())))))));
}

TEST_P(ImportExpr, ImportConditionalOperator) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { (void)(true ? 1 : -5); }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          conditionalOperator(
              hasCondition(cxxBoolLiteral(equals(true))),
              hasTrueExpression(integerLiteral(equals(1))),
              hasFalseExpression(
                  unaryOperator(hasUnaryOperand(integerLiteral(equals(5))))))
          )));
}

TEST_P(ImportExpr, ImportBinaryConditionalOperator) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { (void)(1 ?: -5); }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          binaryConditionalOperator(
              hasCondition(
                  implicitCastExpr(
                      hasSourceExpression(opaqueValueExpr(
                          hasSourceExpression(integerLiteral(equals(1))))),
                      hasType(booleanType()))),
              hasTrueExpression(
                  opaqueValueExpr(
                      hasSourceExpression(integerLiteral(equals(1))))),
              hasFalseExpression(
                  unaryOperator(
                      hasOperatorName("-"),
                      hasUnaryOperand(integerLiteral(equals(5)))))))));
}

TEST_P(ImportExpr, ImportDesignatedInitExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() {"
      "  struct point { double x; double y; };"
      "  struct point ptarray[10] = "
      "{ [2].y = 1.0, [2].x = 2.0, [0].x = 1.0 }; }",
      Lang_C, "", Lang_C, Verifier,
      functionDecl(hasDescendant(
          initListExpr(
              has(designatedInitExpr(
                  designatorCountIs(2),
                  hasDescendant(floatLiteral(equals(1.0))),
                  hasDescendant(integerLiteral(equals(2))))),
              has(designatedInitExpr(
                  designatorCountIs(2),
                  hasDescendant(floatLiteral(equals(2.0))),
                  hasDescendant(integerLiteral(equals(2))))),
              has(designatedInitExpr(
                  designatorCountIs(2),
                  hasDescendant(floatLiteral(equals(1.0))),
                  hasDescendant(integerLiteral(equals(0)))))))));
}

TEST_P(ImportExpr, ImportPredefinedExpr) {
  MatchVerifier<Decl> Verifier;
  // __func__ expands as StringLiteral("declToImport")
  testImport(
      "void declToImport() { (void)__func__; }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          predefinedExpr(
              hasType(
                  asString("const char [13]")),
              has(stringLiteral(hasType(
                  asString("const char [13]"))))))));
}

TEST_P(ImportExpr, ImportInitListExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() {"
      "  struct point { double x; double y; };"
      "  point ptarray[10] = { [2].y = 1.0, [2].x = 2.0,"
      "                        [0].x = 1.0 }; }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          initListExpr(
              has(
                  cxxConstructExpr(
                  requiresZeroInitialization())),
              has(
                  initListExpr(
                      hasType(asString("struct point")),
                      has(floatLiteral(equals(1.0))),
                      has(implicitValueInitExpr(
                          hasType(asString("double")))))),
              has(
                  initListExpr(
                      hasType(asString("struct point")),
                      has(floatLiteral(equals(2.0))),
                      has(floatLiteral(equals(1.0)))))))));
}


const internal::VariadicDynCastAllOfMatcher<Expr, VAArgExpr> vaArgExpr;

TEST_P(ImportExpr, ImportVAArgExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport(__builtin_va_list list, ...) {"
      "  (void)__builtin_va_arg(list, int); }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          cStyleCastExpr(hasSourceExpression(vaArgExpr())))));
}

TEST_P(ImportExpr, CXXTemporaryObjectExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "struct C {};"
      "void declToImport() { C c = C(); }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          exprWithCleanups(has(cxxConstructExpr(
              has(materializeTemporaryExpr(has(implicitCastExpr(
                  has(cxxTemporaryObjectExpr())))))))))));
}

TEST_P(ImportType, ImportAtomicType) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { typedef _Atomic(int) a_int; }",
      Lang_CXX11, "", Lang_CXX11, Verifier,
      functionDecl(hasDescendant(typedefDecl(has(atomicType())))));
}

TEST_P(ImportDecl, ImportFunctionTemplateDecl) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template <typename T> void declToImport() { };",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionTemplateDecl());
}

TEST_P(ImportExpr, ImportCXXDependentScopeMemberExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template <typename T> struct C { T t; };"
      "template <typename T> void declToImport() {"
      "  C<T> d;"
      "  (void)d.t;"
      "}"
      "void instantiate() { declToImport<int>(); }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionTemplateDecl(hasDescendant(
          cStyleCastExpr(has(cxxDependentScopeMemberExpr())))));
  testImport(
      "template <typename T> struct C { T t; };"
      "template <typename T> void declToImport() {"
      "  C<T> d;"
      "  (void)(&d)->t;"
      "}"
      "void instantiate() { declToImport<int>(); }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionTemplateDecl(hasDescendant(
          cStyleCastExpr(has(cxxDependentScopeMemberExpr())))));
}

TEST_P(ImportType, ImportTypeAliasTemplate) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template <int K>"
      "struct dummy { static const int i = K; };"
      "template <int K> using dummy2 = dummy<K>;"
      "int declToImport() { return dummy2<3>::i; }",
      Lang_CXX11, "", Lang_CXX11, Verifier,
      functionDecl(
          hasDescendant(implicitCastExpr(has(declRefExpr()))),
          unless(hasAncestor(translationUnitDecl(has(typeAliasDecl()))))));
}

const internal::VariadicDynCastAllOfMatcher<Decl, VarTemplateSpecializationDecl>
    varTemplateSpecializationDecl;

TEST_P(ImportDecl, ImportVarTemplate) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template <typename T>"
      "T pi = T(3.1415926535897932385L);"
      "void declToImport() { (void)pi<int>; }",
      Lang_CXX14, "", Lang_CXX14, Verifier,
      functionDecl(
          hasDescendant(declRefExpr(to(varTemplateSpecializationDecl()))),
          unless(hasAncestor(translationUnitDecl(has(varDecl(
              hasName("pi"), unless(varTemplateSpecializationDecl()))))))));
}

TEST_P(ImportType, ImportPackExpansion) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template <typename... Args>"
      "struct dummy {"
      "  dummy(Args... args) {}"
      "  static const int i = 4;"
      "};"
      "int declToImport() { return dummy<int>::i; }",
      Lang_CXX11, "", Lang_CXX11, Verifier,
      functionDecl(hasDescendant(
          returnStmt(has(implicitCastExpr(has(declRefExpr())))))));
}

const internal::VariadicDynCastAllOfMatcher<Type,
                                            DependentTemplateSpecializationType>
    dependentTemplateSpecializationType;

TEST_P(ImportType, ImportDependentTemplateSpecialization) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template<typename T>"
      "struct A;"
      "template<typename T>"
      "struct declToImport {"
      "  typename A<T>::template B<T> a;"
      "};",
      Lang_CXX, "", Lang_CXX, Verifier,
      classTemplateDecl(has(cxxRecordDecl(has(
          fieldDecl(hasType(dependentTemplateSpecializationType())))))));
}

const internal::VariadicDynCastAllOfMatcher<Stmt, SizeOfPackExpr>
    sizeOfPackExpr;

TEST_P(ImportExpr, ImportSizeOfPackExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template <typename... Ts>"
      "void declToImport() {"
      "  const int i = sizeof...(Ts);"
      "};"
      "void g() { declToImport<int>(); }",
      Lang_CXX11, "", Lang_CXX11, Verifier,
          functionTemplateDecl(hasDescendant(sizeOfPackExpr())));
  testImport(
      "template <typename... Ts>"
      "using X = int[sizeof...(Ts)];"
      "template <typename... Us>"
      "struct Y {"
      "  X<Us..., int, double, int, Us...> f;"
      "};"
      "Y<float, int> declToImport;",
      Lang_CXX11, "", Lang_CXX11, Verifier,
      varDecl(hasType(classTemplateSpecializationDecl(has(fieldDecl(hasType(
          hasUnqualifiedDesugaredType(constantArrayType(hasSize(7))))))))));
}

/// \brief Matches __builtin_types_compatible_p:
/// GNU extension to check equivalent types
/// Given
/// \code
///   __builtin_types_compatible_p(int, int)
/// \endcode
//  will generate TypeTraitExpr <...> 'int'
const internal::VariadicDynCastAllOfMatcher<Stmt, TypeTraitExpr> typeTraitExpr;

TEST_P(ImportExpr, ImportTypeTraitExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "void declToImport() { "
      "  (void)__builtin_types_compatible_p(int, int);"
      "}",
      Lang_C, "", Lang_C, Verifier,
      functionDecl(hasDescendant(typeTraitExpr(hasType(asString("int"))))));
}

const internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> cxxTypeidExpr;

TEST_P(ImportExpr, ImportCXXTypeidExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "namespace std { class type_info {}; }"
      "void declToImport() {"
      "  int x;"
      "  auto a = typeid(int); auto b = typeid(x);"
      "}",
      Lang_CXX11, "", Lang_CXX11, Verifier,
      functionDecl(
          hasDescendant(varDecl(
              hasName("a"), hasInitializer(hasDescendant(cxxTypeidExpr())))),
          hasDescendant(varDecl(
              hasName("b"), hasInitializer(hasDescendant(cxxTypeidExpr()))))));
}

TEST_P(ImportExpr, ImportTypeTraitExprValDep) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template<typename T> struct declToImport {"
      "  void m() { (void)__is_pod(T); }"
      "};"
      "void f() { declToImport<int>().m(); }",
      Lang_CXX11, "", Lang_CXX11, Verifier,
      classTemplateDecl(has(cxxRecordDecl(has(
          functionDecl(hasDescendant(
              typeTraitExpr(hasType(booleanType())))))))));
}

TEST_P(ImportDecl, ImportRecordDeclInFunc) {
  MatchVerifier<Decl> Verifier;
  testImport("int declToImport() { "
             "  struct data_t {int a;int b;};"
             "  struct data_t d;"
             "  return 0;"
             "}",
             Lang_C, "", Lang_C, Verifier,
             functionDecl(hasBody(compoundStmt(
                 has(declStmt(hasSingleDecl(varDecl(hasName("d")))))))));
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordTypeInFunc) {
  Decl *FromTU = getTuDecl("int declToImport() { "
                           "  struct data_t {int a;int b;};"
                           "  struct data_t d;"
                           "  return 0;"
                           "}",
                           Lang_C, "input.c");
  auto *FromVar =
      FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("d")));
  ASSERT_TRUE(FromVar);
  auto ToType =
      ImportType(FromVar->getType().getCanonicalType(), FromVar, Lang_C);
  EXPECT_FALSE(ToType.isNull());
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordDeclInFuncParams) {
  // This construct is not supported by ASTImporter.
  Decl *FromTU = getTuDecl(
      "int declToImport(struct data_t{int a;int b;} ***d){ return 0; }",
      Lang_C, "input.c");
  auto *From = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("declToImport")));
  ASSERT_TRUE(From);
  auto *To = Import(From, Lang_C);
  EXPECT_EQ(To, nullptr);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordDeclInFuncFromMacro) {
  Decl *FromTU = getTuDecl(
      "#define NONAME_SIZEOF(type) sizeof(struct{type *dummy;}) \n"
      "int declToImport(){ return NONAME_SIZEOF(int); }",
      Lang_C, "input.c");
  auto *From = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("declToImport")));
  ASSERT_TRUE(From);
  auto *To = Import(From, Lang_C);
  ASSERT_TRUE(To);
  EXPECT_TRUE(MatchVerifier<FunctionDecl>().match(
      To, functionDecl(hasName("declToImport"),
                       hasDescendant(unaryExprOrTypeTraitExpr()))));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportRecordDeclInFuncParamsFromMacro) {
  // This construct is not supported by ASTImporter.
  Decl *FromTU = getTuDecl(
      "#define PAIR_STRUCT(type) struct data_t{type a;type b;} \n"
      "int declToImport(PAIR_STRUCT(int) ***d){ return 0; }",
      Lang_C, "input.c");
  auto *From = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("declToImport")));
  ASSERT_TRUE(From);
  auto *To = Import(From, Lang_C);
  EXPECT_EQ(To, nullptr);
}

const internal::VariadicDynCastAllOfMatcher<Expr, CXXPseudoDestructorExpr>
    cxxPseudoDestructorExpr;

TEST_P(ImportExpr, ImportCXXPseudoDestructorExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "typedef int T;"
      "void declToImport(int *p) {"
      "  T t;"
      "  p->T::~T();"
      "}",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(
          callExpr(has(cxxPseudoDestructorExpr())))));
}

TEST_P(ImportDecl, ImportUsingDecl) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "namespace foo { int bar; }"
      "void declToImport() { using foo::bar; }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionDecl(hasDescendant(usingDecl())));
}

/// \brief Matches shadow declarations introduced into a scope by a
///        (resolved) using declaration.
///
/// Given
/// \code
///   namespace n { int f; }
///   namespace declToImport { using n::f; }
/// \endcode
/// usingShadowDecl()
///   matches \code f \endcode
const internal::VariadicDynCastAllOfMatcher<Decl,
                                            UsingShadowDecl> usingShadowDecl;

TEST_P(ImportDecl, ImportUsingShadowDecl) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "namespace foo { int bar; }"
      "namespace declToImport { using foo::bar; }",
      Lang_CXX, "", Lang_CXX, Verifier,
      namespaceDecl(has(usingShadowDecl())));
}

TEST_P(ImportExpr, ImportUnresolvedLookupExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template<typename T> int foo();"
      "template <typename T> void declToImport() {"
      "  (void)::foo<T>;"
      "  (void)::template foo<T>;"
      "}"
      "void instantiate() { declToImport<int>(); }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionTemplateDecl(hasDescendant(unresolvedLookupExpr())));
}

TEST_P(ImportExpr, ImportCXXUnresolvedConstructExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template <typename T> struct C { T t; };"
      "template <typename T> void declToImport() {"
      "  C<T> d;"
      "  d.t = T();"
      "}"
      "void instantiate() { declToImport<int>(); }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionTemplateDecl(hasDescendant(
          binaryOperator(has(cxxUnresolvedConstructExpr())))));
  testImport(
      "template <typename T> struct C { T t; };"
      "template <typename T> void declToImport() {"
      "  C<T> d;"
      "  (&d)->t = T();"
      "}"
      "void instantiate() { declToImport<int>(); }",
      Lang_CXX, "", Lang_CXX, Verifier,
          functionTemplateDecl(hasDescendant(
              binaryOperator(has(cxxUnresolvedConstructExpr())))));
}

/// Check that function "declToImport()" (which is the templated function
/// for corresponding FunctionTemplateDecl) is not added into DeclContext.
/// Same for class template declarations.
TEST_P(ImportDecl, ImportTemplatedDeclForTemplate) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template <typename T> void declToImport() { T a = 1; }"
      "void instantiate() { declToImport<int>(); }",
      Lang_CXX, "", Lang_CXX, Verifier,
      functionTemplateDecl(hasAncestor(translationUnitDecl(
          unless(has(functionDecl(hasName("declToImport"))))))));
  testImport(
      "template <typename T> struct declToImport { T t; };"
      "void instantiate() { declToImport<int>(); }",
      Lang_CXX, "", Lang_CXX, Verifier,
      classTemplateDecl(hasAncestor(translationUnitDecl(
          unless(has(cxxRecordDecl(hasName("declToImport"))))))));
}

TEST_P(ImportDecl, ImportClassTemplatePartialSpecialization) {
  MatchVerifier<Decl> Verifier;
  auto Code =
      R"s(
      struct declToImport {
        template <typename T0> struct X;
        template <typename T0> struct X<T0 *> {};
      };
      )s";
  testImport(Code, Lang_CXX, "", Lang_CXX, Verifier,
             recordDecl(has(classTemplateDecl()),
                        has(classTemplateSpecializationDecl())));
}

TEST_P(ImportExpr, CXXOperatorCallExpr) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "class declToImport {"
      "  void f() { *this = declToImport(); }"
      "};",
      Lang_CXX, "", Lang_CXX, Verifier,
      cxxRecordDecl(has(cxxMethodDecl(hasDescendant(
          cxxOperatorCallExpr())))));
}

TEST_P(ImportExpr, DependentSizedArrayType) {
  MatchVerifier<Decl> Verifier;
  testImport(
      "template<typename T, int Size> class declToImport {"
      "  T data[Size];"
      "};",
      Lang_CXX, "", Lang_CXX, Verifier,
      classTemplateDecl(has(cxxRecordDecl(
          has(fieldDecl(hasType(dependentSizedArrayType())))))));
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportBeginLocOfDeclRefExpr) {
  Decl *FromTU = getTuDecl(
      "class A { public: static int X; }; void f() { (void)A::X; }", Lang_CXX);
  auto From = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));
  ASSERT_TRUE(From);
  ASSERT_TRUE(
      cast<CStyleCastExpr>(cast<CompoundStmt>(From->getBody())->body_front())
          ->getSubExpr()
          ->getBeginLoc()
          .isValid());
  FunctionDecl *To = Import(From, Lang_CXX);
  ASSERT_TRUE(To);
  ASSERT_TRUE(
      cast<CStyleCastExpr>(cast<CompoundStmt>(To->getBody())->body_front())
          ->getSubExpr()
          ->getBeginLoc()
          .isValid());
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportOfTemplatedDeclOfClassTemplateDecl) {
  Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX);
  auto From =
      FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl());
  ASSERT_TRUE(From);
  auto To = cast<ClassTemplateDecl>(Import(From, Lang_CXX));
  ASSERT_TRUE(To);
  Decl *ToTemplated = To->getTemplatedDecl();
  Decl *ToTemplated1 = Import(From->getTemplatedDecl(), Lang_CXX);
  EXPECT_TRUE(ToTemplated1);
  EXPECT_EQ(ToTemplated1, ToTemplated);
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportOfTemplatedDeclOfFunctionTemplateDecl) {
  Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX);
  auto From = FirstDeclMatcher<FunctionTemplateDecl>().match(
      FromTU, functionTemplateDecl());
  ASSERT_TRUE(From);
  auto To = cast<FunctionTemplateDecl>(Import(From, Lang_CXX));
  ASSERT_TRUE(To);
  Decl *ToTemplated = To->getTemplatedDecl();
  Decl *ToTemplated1 = Import(From->getTemplatedDecl(), Lang_CXX);
  EXPECT_TRUE(ToTemplated1);
  EXPECT_EQ(ToTemplated1, ToTemplated);
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportOfTemplatedDeclShouldImportTheClassTemplateDecl) {
  Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX);
  auto FromFT =
      FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl());
  ASSERT_TRUE(FromFT);

  auto ToTemplated =
      cast<CXXRecordDecl>(Import(FromFT->getTemplatedDecl(), Lang_CXX));
  EXPECT_TRUE(ToTemplated);
  auto ToTU = ToTemplated->getTranslationUnitDecl();
  auto ToFT =
      FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, classTemplateDecl());
  EXPECT_TRUE(ToFT);
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportOfTemplatedDeclShouldImportTheFunctionTemplateDecl) {
  Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX);
  auto FromFT = FirstDeclMatcher<FunctionTemplateDecl>().match(
      FromTU, functionTemplateDecl());
  ASSERT_TRUE(FromFT);

  auto ToTemplated =
      cast<FunctionDecl>(Import(FromFT->getTemplatedDecl(), Lang_CXX));
  EXPECT_TRUE(ToTemplated);
  auto ToTU = ToTemplated->getTranslationUnitDecl();
  auto ToFT = FirstDeclMatcher<FunctionTemplateDecl>().match(
      ToTU, functionTemplateDecl());
  EXPECT_TRUE(ToFT);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportCorrectTemplatedDecl) {
  auto Code =
        R"(
        namespace x {
          template<class X> struct S1{};
          template<class X> struct S2{};
          template<class X> struct S3{};
        }
        )";
  Decl *FromTU = getTuDecl(Code, Lang_CXX);
  auto FromNs =
      FirstDeclMatcher<NamespaceDecl>().match(FromTU, namespaceDecl());
  auto ToNs = cast<NamespaceDecl>(Import(FromNs, Lang_CXX));
  ASSERT_TRUE(ToNs);
  auto From =
      FirstDeclMatcher<ClassTemplateDecl>().match(FromTU,
                                                  classTemplateDecl(
                                                      hasName("S2")));
  auto To =
      FirstDeclMatcher<ClassTemplateDecl>().match(ToNs,
                                                  classTemplateDecl(
                                                      hasName("S2")));
  ASSERT_TRUE(From);
  ASSERT_TRUE(To);
  auto ToTemplated = To->getTemplatedDecl();
  auto ToTemplated1 =
      cast<CXXRecordDecl>(Import(From->getTemplatedDecl(), Lang_CXX));
  EXPECT_TRUE(ToTemplated1);
  ASSERT_EQ(ToTemplated1, ToTemplated);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportChooseExpr) {
  // This tests the import of isConditionTrue directly to make sure the importer
  // gets it right.
  Decl *From, *To;
  std::tie(From, To) = getImportedDecl(
    "void declToImport() { (void)__builtin_choose_expr(1, 0, 1); }",
    Lang_C, "", Lang_C);

  auto ToResults = match(chooseExpr().bind("choose"), To->getASTContext());
  auto FromResults = match(chooseExpr().bind("choose"), From->getASTContext());

  const ChooseExpr *FromChooseExpr =
      selectFirst<ChooseExpr>("choose", FromResults);
  ASSERT_TRUE(FromChooseExpr);

  const ChooseExpr *ToChooseExpr = selectFirst<ChooseExpr>("choose", ToResults);
  ASSERT_TRUE(ToChooseExpr);

  EXPECT_EQ(FromChooseExpr->isConditionTrue(), ToChooseExpr->isConditionTrue());
  EXPECT_EQ(FromChooseExpr->isConditionDependent(),
            ToChooseExpr->isConditionDependent());
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportFunctionWithBackReferringParameter) {
  Decl *From, *To;
  std::tie(From, To) = getImportedDecl(
      R"(
      template <typename T> struct X {};

      void declToImport(int y, X<int> &x) {}

      template <> struct X<int> {
        void g() {
          X<int> x;
          declToImport(0, x);
        }
      };
      )",
      Lang_CXX, "", Lang_CXX);

  MatchVerifier<Decl> Verifier;
  auto Matcher = functionDecl(hasName("declToImport"),
                              parameterCountIs(2),
                              hasParameter(0, hasName("y")),
                              hasParameter(1, hasName("x")),
                              hasParameter(1, hasType(asString("X<int> &"))));
  ASSERT_TRUE(Verifier.match(From, Matcher));
  EXPECT_TRUE(Verifier.match(To, Matcher));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       TUshouldNotContainTemplatedDeclOfFunctionTemplates) {
  Decl *From, *To;
  std::tie(From, To) =
      getImportedDecl("template <typename T> void declToImport() { T a = 1; }"
                      "void instantiate() { declToImport<int>(); }",
                      Lang_CXX, "", Lang_CXX);

  auto Check = [](Decl *D) -> bool {
    auto TU = D->getTranslationUnitDecl();
    for (auto Child : TU->decls()) {
      if (auto *FD = dyn_cast<FunctionDecl>(Child)) {
        if (FD->getNameAsString() == "declToImport") {
          GTEST_NONFATAL_FAILURE_(
              "TU should not contain any FunctionDecl with name declToImport");
          return false;
        }
      }
    }
    return true;
  };

  ASSERT_TRUE(Check(From));
  EXPECT_TRUE(Check(To));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       TUshouldNotContainTemplatedDeclOfClassTemplates) {
  Decl *From, *To;
  std::tie(From, To) =
      getImportedDecl("template <typename T> struct declToImport { T t; };"
                      "void instantiate() { declToImport<int>(); }",
                      Lang_CXX, "", Lang_CXX);

  auto Check = [](Decl *D) -> bool {
    auto TU = D->getTranslationUnitDecl();
    for (auto Child : TU->decls()) {
      if (auto *RD = dyn_cast<CXXRecordDecl>(Child)) {
        if (RD->getNameAsString() == "declToImport") {
          GTEST_NONFATAL_FAILURE_(
              "TU should not contain any CXXRecordDecl with name declToImport");
          return false;
        }
      }
    }
    return true;
  };

  ASSERT_TRUE(Check(From));
  EXPECT_TRUE(Check(To));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       TUshouldNotContainTemplatedDeclOfTypeAlias) {
  Decl *From, *To;
  std::tie(From, To) =
      getImportedDecl(
          "template <typename T> struct X {};"
          "template <typename T> using declToImport = X<T>;"
          "void instantiate() { declToImport<int> a; }",
                      Lang_CXX11, "", Lang_CXX11);

  auto Check = [](Decl *D) -> bool {
    auto TU = D->getTranslationUnitDecl();
    for (auto Child : TU->decls()) {
      if (auto *AD = dyn_cast<TypeAliasDecl>(Child)) {
        if (AD->getNameAsString() == "declToImport") {
          GTEST_NONFATAL_FAILURE_(
              "TU should not contain any TypeAliasDecl with name declToImport");
          return false;
        }
      }
    }
    return true;
  };

  ASSERT_TRUE(Check(From));
  EXPECT_TRUE(Check(To));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       TUshouldNotContainClassTemplateSpecializationOfImplicitInstantiation) {

  Decl *From, *To;
  std::tie(From, To) = getImportedDecl(
      R"(
      template<class T>
      class Base {};
      class declToImport : public Base<declToImport> {};
      )",
      Lang_CXX, "", Lang_CXX);

  // Check that the ClassTemplateSpecializationDecl is NOT the child of the TU.
  auto Pattern =
      translationUnitDecl(unless(has(classTemplateSpecializationDecl())));
  ASSERT_TRUE(
      MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
  EXPECT_TRUE(
      MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));

  // Check that the ClassTemplateSpecializationDecl is the child of the
  // ClassTemplateDecl.
  Pattern = translationUnitDecl(has(classTemplateDecl(
      hasName("Base"), has(classTemplateSpecializationDecl()))));
  ASSERT_TRUE(
      MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
  EXPECT_TRUE(
      MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
}

AST_MATCHER_P(RecordDecl, hasFieldOrder, std::vector<StringRef>, Order) {
  size_t Index = 0;
  for (Decl *D : Node.decls()) {
    if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) {
      auto *ND = cast<NamedDecl>(D);
      if (Index == Order.size())
        return false;
      if (ND->getName() != Order[Index])
        return false;
      ++Index;
    }
  }
  return Index == Order.size();
}

TEST_P(ASTImporterOptionSpecificTestBase,
       TUshouldContainClassTemplateSpecializationOfExplicitInstantiation) {
  Decl *From, *To;
  std::tie(From, To) = getImportedDecl(
      R"(
      namespace NS {
        template<class T>
        class X {};
        template class X<int>;
      }
      )",
      Lang_CXX, "", Lang_CXX, "NS");

  // Check that the ClassTemplateSpecializationDecl is NOT the child of the
  // ClassTemplateDecl.
  auto Pattern = namespaceDecl(has(classTemplateDecl(
      hasName("X"), unless(has(classTemplateSpecializationDecl())))));
  ASSERT_TRUE(MatchVerifier<Decl>{}.match(From, Pattern));
  EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern));

  // Check that the ClassTemplateSpecializationDecl is the child of the
  // NamespaceDecl.
  Pattern = namespaceDecl(has(classTemplateSpecializationDecl(hasName("X"))));
  ASSERT_TRUE(MatchVerifier<Decl>{}.match(From, Pattern));
  EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       CXXRecordDeclFieldsShouldBeInCorrectOrder) {
  Decl *From, *To;
  std::tie(From, To) =
      getImportedDecl(
          "struct declToImport { int a; int b; };",
                      Lang_CXX11, "", Lang_CXX11);

  MatchVerifier<Decl> Verifier;
  ASSERT_TRUE(Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b"}))));
  EXPECT_TRUE(Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b"}))));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       CXXRecordDeclFieldOrderShouldNotDependOnImportOrder) {
  Decl *From, *To;
  std::tie(From, To) = getImportedDecl(
      // The original recursive algorithm of ASTImporter first imports 'c' then
      // 'b' and lastly 'a'.  Therefore we must restore the order somehow.
      R"s(
      struct declToImport {
          int a = c + b;
          int b = 1;
          int c = 2;
      };
      )s",
      Lang_CXX11, "", Lang_CXX11);

  MatchVerifier<Decl> Verifier;
  ASSERT_TRUE(
      Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b", "c"}))));
  EXPECT_TRUE(
      Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b", "c"}))));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       CXXRecordDeclFieldAndIndirectFieldOrder) {
  Decl *From, *To;
  std::tie(From, To) = getImportedDecl(
      // First field is "a", then the field for unnamed union, then "b" and "c"
      // from it (indirect fields), then "d".
      R"s(
      struct declToImport {
        int a = d;
        union { 
          int b;
          int c;
        };
        int d;
      };
      )s",
      Lang_CXX11, "", Lang_CXX11);

  MatchVerifier<Decl> Verifier;
  ASSERT_TRUE(Verifier.match(
      From, cxxRecordDecl(hasFieldOrder({"a", "", "b", "c", "d"}))));
  EXPECT_TRUE(Verifier.match(
      To, cxxRecordDecl(hasFieldOrder({"a", "", "b", "c", "d"}))));
}

TEST_P(ASTImporterOptionSpecificTestBase, ShouldImportImplicitCXXRecordDecl) {
  Decl *From, *To;
  std::tie(From, To) = getImportedDecl(
      R"(
      struct declToImport {
      };
      )",
      Lang_CXX, "", Lang_CXX);

  MatchVerifier<Decl> Verifier;
  // Match the implicit Decl.
  auto Matcher = cxxRecordDecl(has(cxxRecordDecl()));
  ASSERT_TRUE(Verifier.match(From, Matcher));
  EXPECT_TRUE(Verifier.match(To, Matcher));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ShouldImportImplicitCXXRecordDeclOfClassTemplate) {
  Decl *From, *To;
  std::tie(From, To) = getImportedDecl(
      R"(
      template <typename U>
      struct declToImport {
      };
      )",
      Lang_CXX, "", Lang_CXX);

  MatchVerifier<Decl> Verifier;
  // Match the implicit Decl.
  auto Matcher = classTemplateDecl(has(cxxRecordDecl(has(cxxRecordDecl()))));
  ASSERT_TRUE(Verifier.match(From, Matcher));
  EXPECT_TRUE(Verifier.match(To, Matcher));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ShouldImportImplicitCXXRecordDeclOfClassTemplateSpecializationDecl) {
  Decl *From, *To;
  std::tie(From, To) = getImportedDecl(
      R"(
      template<class T>
      class Base {};
      class declToImport : public Base<declToImport> {};
      )",
      Lang_CXX, "", Lang_CXX);

  auto hasImplicitClass = has(cxxRecordDecl());
  auto Pattern = translationUnitDecl(has(classTemplateDecl(
      hasName("Base"),
      has(classTemplateSpecializationDecl(hasImplicitClass)))));
  ASSERT_TRUE(
      MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
  EXPECT_TRUE(
      MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
}

TEST_P(ASTImporterOptionSpecificTestBase, IDNSOrdinary) {
  Decl *From, *To;
  std::tie(From, To) =
      getImportedDecl("void declToImport() {}", Lang_CXX, "", Lang_CXX);

  MatchVerifier<Decl> Verifier;
  auto Matcher = functionDecl();
  ASSERT_TRUE(Verifier.match(From, Matcher));
  EXPECT_TRUE(Verifier.match(To, Matcher));
  EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace());
}

TEST_P(ASTImporterOptionSpecificTestBase, IDNSOfNonmemberOperator) {
  Decl *FromTU = getTuDecl(
      R"(
      struct X {};
      void operator<<(int, X);
      )",
      Lang_CXX);
  Decl *From = LastDeclMatcher<Decl>{}.match(FromTU, functionDecl());
  const Decl *To = Import(From, Lang_CXX);
  EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace());
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ShouldImportMembersOfClassTemplateSpecializationDecl) {
  Decl *From, *To;
  std::tie(From, To) = getImportedDecl(
      R"(
      template<class T>
      class Base { int a; };
      class declToImport : Base<declToImport> {};
      )",
      Lang_CXX, "", Lang_CXX);

  auto Pattern = translationUnitDecl(has(classTemplateDecl(
      hasName("Base"),
      has(classTemplateSpecializationDecl(has(fieldDecl(hasName("a"))))))));
  ASSERT_TRUE(
      MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
  EXPECT_TRUE(
      MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportDefinitionOfClassTemplateAfterFwdDecl) {
  {
    Decl *FromTU = getTuDecl(
        R"(
            template <typename T>
            struct B;
            )",
        Lang_CXX, "input0.cc");
    auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
        FromTU, classTemplateDecl(hasName("B")));

    Import(FromD, Lang_CXX);
  }

  {
    Decl *FromTU = getTuDecl(
        R"(
            template <typename T>
            struct B {
              void f();
            };
            )",
        Lang_CXX, "input1.cc");
    FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("f")));
    Import(FromD, Lang_CXX);
    auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match(
        FromTU, classTemplateDecl(hasName("B")));
    auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX));
    EXPECT_TRUE(ToCTD->isThisDeclarationADefinition());
  }
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportDefinitionOfClassTemplateIfThereIsAnExistingFwdDeclAndDefinition) {
  Decl *ToTU = getToTuDecl(
      R"(
      template <typename T>
      struct B {
        void f();
      };

      template <typename T>
      struct B;
      )",
      Lang_CXX);
  ASSERT_EQ(1u, DeclCounterWithPredicate<ClassTemplateDecl>(
                    [](const ClassTemplateDecl *T) {
                      return T->isThisDeclarationADefinition();
                    })
                    .match(ToTU, classTemplateDecl()));

  Decl *FromTU = getTuDecl(
      R"(
      template <typename T>
      struct B {
        void f();
      };
      )",
      Lang_CXX, "input1.cc");
  ClassTemplateDecl *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
      FromTU, classTemplateDecl(hasName("B")));

  Import(FromD, Lang_CXX);

  // We should have only one definition.
  EXPECT_EQ(1u, DeclCounterWithPredicate<ClassTemplateDecl>(
                    [](const ClassTemplateDecl *T) {
                      return T->isThisDeclarationADefinition();
                    })
                    .match(ToTU, classTemplateDecl()));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportDefinitionOfClassIfThereIsAnExistingFwdDeclAndDefinition) {
  Decl *ToTU = getToTuDecl(
      R"(
      struct B {
        void f();
      };

      struct B;
      )",
      Lang_CXX);
  ASSERT_EQ(2u, DeclCounter<CXXRecordDecl>().match(
                    ToTU, cxxRecordDecl(unless(isImplicit()))));

  Decl *FromTU = getTuDecl(
      R"(
      struct B {
        void f();
      };
      )",
      Lang_CXX, "input1.cc");
  auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("B")));

  Import(FromD, Lang_CXX);

  EXPECT_EQ(2u, DeclCounter<CXXRecordDecl>().match(
                    ToTU, cxxRecordDecl(unless(isImplicit()))));
}

static void CompareSourceLocs(FullSourceLoc Loc1, FullSourceLoc Loc2) {
  EXPECT_EQ(Loc1.getExpansionLineNumber(), Loc2.getExpansionLineNumber());
  EXPECT_EQ(Loc1.getExpansionColumnNumber(), Loc2.getExpansionColumnNumber());
  EXPECT_EQ(Loc1.getSpellingLineNumber(), Loc2.getSpellingLineNumber());
  EXPECT_EQ(Loc1.getSpellingColumnNumber(), Loc2.getSpellingColumnNumber());
}
static void CompareSourceRanges(SourceRange Range1, SourceRange Range2,
                                SourceManager &SM1, SourceManager &SM2) {
  CompareSourceLocs(FullSourceLoc{ Range1.getBegin(), SM1 },
                    FullSourceLoc{ Range2.getBegin(), SM2 });
  CompareSourceLocs(FullSourceLoc{ Range1.getEnd(), SM1 },
                    FullSourceLoc{ Range2.getEnd(), SM2 });
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportSourceLocs) {
  Decl *FromTU = getTuDecl(
      R"(
      #define MFOO(arg) arg = arg + 1

      void foo() {
        int a = 5;
        MFOO(a);
      }
      )",
      Lang_CXX);
  auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
  auto ToD = Import(FromD, Lang_CXX);

  auto ToLHS = LastDeclMatcher<DeclRefExpr>().match(ToD, declRefExpr());
  auto FromLHS = LastDeclMatcher<DeclRefExpr>().match(FromTU, declRefExpr());
  auto ToRHS = LastDeclMatcher<IntegerLiteral>().match(ToD, integerLiteral());
  auto FromRHS =
      LastDeclMatcher<IntegerLiteral>().match(FromTU, integerLiteral());

  SourceManager &ToSM = ToAST->getASTContext().getSourceManager();
  SourceManager &FromSM = FromD->getASTContext().getSourceManager();
  CompareSourceRanges(ToD->getSourceRange(), FromD->getSourceRange(), ToSM,
                      FromSM);
  CompareSourceRanges(ToLHS->getSourceRange(), FromLHS->getSourceRange(), ToSM,
                      FromSM);
  CompareSourceRanges(ToRHS->getSourceRange(), FromRHS->getSourceRange(), ToSM,
                      FromSM);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportNestedMacro) {
  Decl *FromTU = getTuDecl(
      R"(
      #define FUNC_INT void declToImport
      #define FUNC FUNC_INT
      FUNC(int a);
      )",
      Lang_CXX);
  auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
  auto ToD = Import(FromD, Lang_CXX);

  SourceManager &ToSM = ToAST->getASTContext().getSourceManager();
  SourceManager &FromSM = FromD->getASTContext().getSourceManager();
  CompareSourceRanges(ToD->getSourceRange(), FromD->getSourceRange(), ToSM,
                      FromSM);
}

TEST_P(
    ASTImporterOptionSpecificTestBase,
    ImportDefinitionOfClassTemplateSpecIfThereIsAnExistingFwdDeclAndDefinition) {
  Decl *ToTU = getToTuDecl(
      R"(
      template <typename T>
      struct B;

      template <>
      struct B<int> {};

      template <>
      struct B<int>;
      )",
      Lang_CXX);
  // We should have only one definition.
  ASSERT_EQ(1u, DeclCounterWithPredicate<ClassTemplateSpecializationDecl>(
                    [](const ClassTemplateSpecializationDecl *T) {
                      return T->isThisDeclarationADefinition();
                    })
                    .match(ToTU, classTemplateSpecializationDecl()));

  Decl *FromTU = getTuDecl(
      R"(
      template <typename T>
      struct B;

      template <>
      struct B<int> {};
      )",
      Lang_CXX, "input1.cc");
  auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      FromTU, classTemplateSpecializationDecl(hasName("B")));

  Import(FromD, Lang_CXX);

  // We should have only one definition.
  EXPECT_EQ(1u, DeclCounterWithPredicate<ClassTemplateSpecializationDecl>(
                    [](const ClassTemplateSpecializationDecl *T) {
                      return T->isThisDeclarationADefinition();
                    })
                    .match(ToTU, classTemplateSpecializationDecl()));
}

TEST_P(ASTImporterOptionSpecificTestBase, ObjectsWithUnnamedStructType) {
  Decl *FromTU = getTuDecl(
      R"(
      struct { int a; int b; } object0 = { 2, 3 };
      struct { int x; int y; int z; } object1;
      )",
      Lang_CXX, "input0.cc");

  auto *Obj0 =
      FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("object0")));
  auto *From0 = getRecordDecl(Obj0);
  auto *Obj1 =
      FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("object1")));
  auto *From1 = getRecordDecl(Obj1);

  auto *To0 = Import(From0, Lang_CXX);
  auto *To1 = Import(From1, Lang_CXX);

  EXPECT_TRUE(To0);
  EXPECT_TRUE(To1);
  EXPECT_NE(To0, To1);
  EXPECT_NE(To0->getCanonicalDecl(), To1->getCanonicalDecl());
}

TEST_P(ASTImporterOptionSpecificTestBase, AnonymousRecords) {
  auto *Code =
      R"(
      struct X {
        struct { int a; };
        struct { int b; };
      };
      )";
  Decl *FromTU0 = getTuDecl(Code, Lang_C, "input0.c");

  Decl *FromTU1 = getTuDecl(Code, Lang_C, "input1.c");

  auto *X0 =
      FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
  auto *X1 =
      FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
  Import(X0, Lang_C);
  Import(X1, Lang_C);

  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  // We expect no (ODR) warning during the import.
  EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
  EXPECT_EQ(1u,
            DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
}

TEST_P(ASTImporterOptionSpecificTestBase, AnonymousRecordsReversed) {
  Decl *FromTU0 = getTuDecl(
      R"(
      struct X {
        struct { int a; };
        struct { int b; };
      };
      )",
      Lang_C, "input0.c");

  Decl *FromTU1 = getTuDecl(
      R"(
      struct X { // reversed order
        struct { int b; };
        struct { int a; };
      };
      )",
      Lang_C, "input1.c");

  auto *X0 =
      FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
  auto *X1 =
      FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
  Import(X0, Lang_C);
  Import(X1, Lang_C);

  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  // We expect one (ODR) warning during the import.
  EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
  EXPECT_EQ(1u,
            DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag) {
  auto Pattern = varDecl(hasName("x"));
  VarDecl *Imported1;
  {
    Decl *FromTU = getTuDecl("extern int x;", Lang_CXX, "input0.cc");
    auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
    Imported1 = cast<VarDecl>(Import(FromD, Lang_CXX));
  }
  VarDecl *Imported2;
  {
    Decl *FromTU = getTuDecl("int x;", Lang_CXX, "input1.cc");
    auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
    Imported2 = cast<VarDecl>(Import(FromD, Lang_CXX));
  }
  EXPECT_EQ(Imported1->getCanonicalDecl(), Imported2->getCanonicalDecl());
  EXPECT_FALSE(Imported2->isUsed(false));
  {
    Decl *FromTU =
        getTuDecl("extern int x; int f() { return x; }", Lang_CXX, "input2.cc");
    auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("f")));
    Import(FromD, Lang_CXX);
  }
  EXPECT_TRUE(Imported2->isUsed(false));
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag2) {
  auto Pattern = varDecl(hasName("x"));
  VarDecl *ExistingD;
  {
    Decl *ToTU = getToTuDecl("int x = 1;", Lang_CXX);
    ExistingD = FirstDeclMatcher<VarDecl>().match(ToTU, Pattern);
  }
  EXPECT_FALSE(ExistingD->isUsed(false));
  {
    Decl *FromTU = getTuDecl(
        "int x = 1; int f() { return x; }", Lang_CXX, "input1.cc");
    auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("f")));
    Import(FromD, Lang_CXX);
  }
  EXPECT_TRUE(ExistingD->isUsed(false));
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag3) {
  auto Pattern = varDecl(hasName("a"));
  VarDecl *ExistingD;
  {
    Decl *ToTU = getToTuDecl(
        R"(
        struct A {
          static const int a = 1;
        };
        )", Lang_CXX);
    ExistingD = FirstDeclMatcher<VarDecl>().match(ToTU, Pattern);
  }
  EXPECT_FALSE(ExistingD->isUsed(false));
  {
    Decl *FromTU = getTuDecl(
        R"(
        struct A {
          static const int a = 1;
        };
        const int *f() { return &A::a; } // requires storage,
                                         // thus used flag will be set
        )", Lang_CXX, "input1.cc");
    auto *FromFunD = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("f")));
    auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
    ASSERT_TRUE(FromD->isUsed(false));
    Import(FromFunD, Lang_CXX);
  }
  EXPECT_TRUE(ExistingD->isUsed(false));
}

TEST_P(ASTImporterOptionSpecificTestBase, ReimportWithUsedFlag) {
  auto Pattern = varDecl(hasName("x"));

  Decl *FromTU = getTuDecl("int x;", Lang_CXX, "input0.cc");
  auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);

  auto *Imported1 = cast<VarDecl>(Import(FromD, Lang_CXX));

  ASSERT_FALSE(Imported1->isUsed(false));

  FromD->setIsUsed();
  auto *Imported2 = cast<VarDecl>(Import(FromD, Lang_CXX));

  EXPECT_EQ(Imported1, Imported2);
  EXPECT_TRUE(Imported2->isUsed(false));
}

struct ImportFunctions : ASTImporterOptionSpecificTestBase {};

TEST_P(ImportFunctions, ImportPrototypeOfRecursiveFunction) {
  Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
  auto Pattern = functionDecl(hasName("f"));
  auto *From =
      FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Proto

  Decl *ImportedD = Import(From, Lang_CXX);
  Decl *ToTU = ImportedD->getTranslationUnitDecl();

  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
  auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
  auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
  EXPECT_TRUE(ImportedD == To0);
  EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
  EXPECT_TRUE(To1->doesThisDeclarationHaveABody());
  EXPECT_EQ(To1->getPreviousDecl(), To0);
}

TEST_P(ImportFunctions, ImportDefinitionOfRecursiveFunction) {
  Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX);
  auto Pattern = functionDecl(hasName("f"));
  auto *From =
      LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Def

  Decl *ImportedD = Import(From, Lang_CXX);
  Decl *ToTU = ImportedD->getTranslationUnitDecl();

  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
  auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
  auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
  EXPECT_TRUE(ImportedD == To1);
  EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
  EXPECT_TRUE(To1->doesThisDeclarationHaveABody());
  EXPECT_EQ(To1->getPreviousDecl(), To0);
}

TEST_P(ImportFunctions, OverriddenMethodsShouldBeImported) {
  auto Code =
      R"(
      struct B { virtual void f(); };
      void B::f() {}
      struct D : B { void f(); };
      )";
  auto Pattern =
      cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
  Decl *FromTU = getTuDecl(Code, Lang_CXX);
  CXXMethodDecl *Proto =
      FirstDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern);

  ASSERT_EQ(Proto->size_overridden_methods(), 1u);
  CXXMethodDecl *To = cast<CXXMethodDecl>(Import(Proto, Lang_CXX));
  EXPECT_EQ(To->size_overridden_methods(), 1u);
}

TEST_P(ImportFunctions, VirtualFlagShouldBePreservedWhenImportingPrototype) {
  auto Code =
      R"(
      struct B { virtual void f(); };
      void B::f() {}
      )";
  auto Pattern =
      cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
  Decl *FromTU = getTuDecl(Code, Lang_CXX);
  CXXMethodDecl *Proto =
      FirstDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern);
  CXXMethodDecl *Def = LastDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern);

  ASSERT_TRUE(Proto->isVirtual());
  ASSERT_TRUE(Def->isVirtual());
  CXXMethodDecl *To = cast<CXXMethodDecl>(Import(Proto, Lang_CXX));
  EXPECT_TRUE(To->isVirtual());
}

TEST_P(ImportFunctions,
       ImportDefinitionIfThereIsAnExistingDefinitionAndFwdDecl) {
  Decl *ToTU = getToTuDecl(
      R"(
      void f() {}
      void f();
      )",
      Lang_CXX);
  ASSERT_EQ(1u,
            DeclCounterWithPredicate<FunctionDecl>([](const FunctionDecl *FD) {
              return FD->doesThisDeclarationHaveABody();
            }).match(ToTU, functionDecl()));

  Decl *FromTU = getTuDecl("void f() {}", Lang_CXX, "input0.cc");
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());

  Import(FromD, Lang_CXX);

  EXPECT_EQ(1u,
            DeclCounterWithPredicate<FunctionDecl>([](const FunctionDecl *FD) {
              return FD->doesThisDeclarationHaveABody();
            }).match(ToTU, functionDecl()));
}

TEST_P(ImportFunctions, ImportOverriddenMethodTwice) {
  auto Code =
      R"(
      struct B { virtual void f(); };
      struct D:B { void f(); };
      )";
  auto BFP =
      cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
  auto DFP =
      cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));

  Decl *FromTU0 = getTuDecl(Code, Lang_CXX);
  auto *DF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
  Import(DF, Lang_CXX);

  Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc");
  auto *BF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP);
  Import(BF, Lang_CXX);

  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();

  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
}

TEST_P(ImportFunctions, ImportOverriddenMethodTwiceDefinitionFirst) {
  auto CodeWithoutDef =
      R"(
      struct B { virtual void f(); };
      struct D:B { void f(); };
      )";
  auto CodeWithDef =
      R"(
    struct B { virtual void f(){}; };
    struct D:B { void f(){}; };
  )";
  auto BFP =
      cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
  auto DFP =
      cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
  auto BFDefP = cxxMethodDecl(
      hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
  auto DFDefP = cxxMethodDecl(
      hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition());
  auto FDefAllP = cxxMethodDecl(hasName("f"), isDefinition());

  {
    Decl *FromTU = getTuDecl(CodeWithDef, Lang_CXX, "input0.cc");
    auto *FromD = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, DFP);
    Import(FromD, Lang_CXX);
  }
  {
    Decl *FromTU = getTuDecl(CodeWithoutDef, Lang_CXX, "input1.cc");
    auto *FromB = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, BFP);
    Import(FromB, Lang_CXX);
  }

  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();

  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 1u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 1u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FDefAllP), 2u);
}

TEST_P(ImportFunctions, ImportOverriddenMethodTwiceOutOfClassDef) {
  auto Code =
      R"(
      struct B { virtual void f(); };
      struct D:B { void f(); };
      void B::f(){};
      )";

  auto BFP =
      cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
  auto BFDefP = cxxMethodDecl(
      hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
  auto DFP = cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))),
                           unless(isDefinition()));

  Decl *FromTU0 = getTuDecl(Code, Lang_CXX);
  auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
  Import(D, Lang_CXX);

  Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc");
  auto *B = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP);
  Import(B, Lang_CXX);

  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();

  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u);

  auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match(
      ToTU, cxxRecordDecl(hasName("B")));
  auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP);
  auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match(
      ToTU, cxxMethodDecl(hasName("f"), isDefinition()));

  // The definition should be out-of-class.
  EXPECT_NE(ToBFInClass, ToBFOutOfClass);
  EXPECT_NE(ToBFInClass->getLexicalDeclContext(),
            ToBFOutOfClass->getLexicalDeclContext());
  EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB);
  EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU);

  // Check that the redecl chain is intact.
  EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass);
}

TEST_P(ImportFunctions,
       ImportOverriddenMethodTwiceOutOfClassDefInSeparateCode) {
  auto CodeTU0 =
      R"(
      struct B { virtual void f(); };
      struct D:B { void f(); };
      )";
  auto CodeTU1 =
      R"(
      struct B { virtual void f(); };
      struct D:B { void f(); };
      void B::f(){}
      void D::f(){}
      void foo(B &b, D &d) { b.f(); d.f(); }
      )";

  auto BFP =
      cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
  auto BFDefP = cxxMethodDecl(
      hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
  auto DFP =
      cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
  auto DFDefP = cxxMethodDecl(
      hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition());
  auto FooDef = functionDecl(hasName("foo"));

  {
    Decl *FromTU0 = getTuDecl(CodeTU0, Lang_CXX, "input0.cc");
    auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
    Import(D, Lang_CXX);
  }

  {
    Decl *FromTU1 = getTuDecl(CodeTU1, Lang_CXX, "input1.cc");
    auto *Foo = FirstDeclMatcher<FunctionDecl>().match(FromTU1, FooDef);
    Import(Foo, Lang_CXX);
  }

  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();

  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 0u);

  auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match(
      ToTU, cxxRecordDecl(hasName("B")));
  auto *ToD = FirstDeclMatcher<CXXRecordDecl>().match(
      ToTU, cxxRecordDecl(hasName("D")));
  auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP);
  auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match(
      ToTU, cxxMethodDecl(hasName("f"), isDefinition()));
  auto *ToDFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, DFP);
  auto *ToDFOutOfClass = LastDeclMatcher<CXXMethodDecl>().match(
      ToTU, cxxMethodDecl(hasName("f"), isDefinition()));

  // The definition should be out-of-class.
  EXPECT_NE(ToBFInClass, ToBFOutOfClass);
  EXPECT_NE(ToBFInClass->getLexicalDeclContext(),
            ToBFOutOfClass->getLexicalDeclContext());
  EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB);
  EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU);

  EXPECT_NE(ToDFInClass, ToDFOutOfClass);
  EXPECT_NE(ToDFInClass->getLexicalDeclContext(),
            ToDFOutOfClass->getLexicalDeclContext());
  EXPECT_EQ(ToDFOutOfClass->getDeclContext(), ToD);
  EXPECT_EQ(ToDFOutOfClass->getLexicalDeclContext(), ToTU);

  // Check that the redecl chain is intact.
  EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass);
  EXPECT_EQ(ToDFOutOfClass->getPreviousDecl(), ToDFInClass);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportVariableChainInC) {
    std::string Code = "static int v; static int v = 0;";
    auto Pattern = varDecl(hasName("v"));

    TranslationUnitDecl *FromTu = getTuDecl(Code, Lang_C, "input0.c");

    auto *From0 = FirstDeclMatcher<VarDecl>().match(FromTu, Pattern);
    auto *From1 = LastDeclMatcher<VarDecl>().match(FromTu, Pattern);

    auto *To0 = Import(From0, Lang_C);
    auto *To1 = Import(From1, Lang_C);

    EXPECT_TRUE(To0);
    ASSERT_TRUE(To1);
    EXPECT_NE(To0, To1);
    EXPECT_EQ(To1->getPreviousDecl(), To0);
}

TEST_P(ImportFunctions, ImportFromDifferentScopedAnonNamespace) {
  TranslationUnitDecl *FromTu = getTuDecl(
      "namespace NS0 { namespace { void f(); } }"
      "namespace NS1 { namespace { void f(); } }",
      Lang_CXX, "input0.cc");
  auto Pattern = functionDecl(hasName("f"));

  auto *FromF0 = FirstDeclMatcher<FunctionDecl>().match(FromTu, Pattern);
  auto *FromF1 = LastDeclMatcher<FunctionDecl>().match(FromTu, Pattern);

  auto *ToF0 = Import(FromF0, Lang_CXX);
  auto *ToF1 = Import(FromF1, Lang_CXX);

  EXPECT_TRUE(ToF0);
  ASSERT_TRUE(ToF1);
  EXPECT_NE(ToF0, ToF1);
  EXPECT_FALSE(ToF1->getPreviousDecl());
}

TEST_P(ImportFunctions, ImportFunctionFromUnnamedNamespace) {
  {
    Decl *FromTU = getTuDecl("namespace { void f() {} } void g0() { f(); }",
                             Lang_CXX, "input0.cc");
    auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("g0")));

    Import(FromD, Lang_CXX);
  }
  {
    Decl *FromTU =
        getTuDecl("namespace { void f() { int a; } } void g1() { f(); }",
                  Lang_CXX, "input1.cc");
    auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("g1")));
    Import(FromD, Lang_CXX);
  }

  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))),
            2u);
}

TEST_P(ImportFunctions, ImportImplicitFunctionsInLambda) {
  Decl *FromTU = getTuDecl(
      R"(
      void foo() {
        (void)[]() { ; };
      }
      )",
      Lang_CXX11);
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("foo")));
  auto *ToD = Import(FromD, Lang_CXX);
  EXPECT_TRUE(ToD);
  CXXRecordDecl *LambdaRec =
      cast<LambdaExpr>(cast<CStyleCastExpr>(
                           *cast<CompoundStmt>(ToD->getBody())->body_begin())
                           ->getSubExpr())
          ->getLambdaClass();
  EXPECT_TRUE(LambdaRec->getDestructor());
}

TEST_P(ImportFunctions,
       CallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) {
  Decl *FromTU = getTuDecl(
      R"(
      struct X {
        template <typename T>
        void foo(){}
      };
      void f() {
        X x;
        x.foo<int>();
      }
      )",
      Lang_CXX);
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));
  auto *ToD = Import(FromD, Lang_CXX);
  EXPECT_TRUE(ToD);
  EXPECT_TRUE(MatchVerifier<FunctionDecl>().match(
      ToD, functionDecl(hasName("f"), hasDescendant(declRefExpr()))));
}

TEST_P(ImportFunctions,
       DependentCallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) {
  Decl *FromTU = getTuDecl(
      R"(
      struct X {
        template <typename T>
        void foo(){}
      };
      template <typename T>
      void f() {
        X x;
        x.foo<T>();
      }
      void g() {
        f<int>();
      }
      )",
      Lang_CXX);
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("g")));
  auto *ToD = Import(FromD, Lang_CXX);
  EXPECT_TRUE(ToD);
  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  EXPECT_TRUE(MatchVerifier<TranslationUnitDecl>().match(
      ToTU, translationUnitDecl(hasDescendant(
                functionDecl(hasName("f"), hasDescendant(declRefExpr()))))));
}

struct ImportFunctionTemplates : ASTImporterOptionSpecificTestBase {};

TEST_P(ImportFunctionTemplates, ImportFunctionTemplateInRecordDeclTwice) {
  auto Code =
      R"(
      class X {
        template <class T>
        void f(T t);
      };
      )";
  Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc");
  auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match(
      FromTU1, functionTemplateDecl(hasName("f")));
  auto *ToD1 = Import(FromD1, Lang_CXX);
  Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc");
  auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match(
      FromTU2, functionTemplateDecl(hasName("f")));
  auto *ToD2 = Import(FromD2, Lang_CXX);
  EXPECT_EQ(ToD1, ToD2);
}

TEST_P(ImportFunctionTemplates,
       ImportFunctionTemplateWithDefInRecordDeclTwice) {
  auto Code =
      R"(
      class X {
        template <class T>
        void f(T t);
      };
      template <class T>
      void X::f(T t) {};
      )";
  Decl *FromTU1 = getTuDecl(Code, Lang_CXX, "input1.cc");
  auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match(
      FromTU1, functionTemplateDecl(hasName("f")));
  auto *ToD1 = Import(FromD1, Lang_CXX);
  Decl *FromTU2 = getTuDecl(Code, Lang_CXX, "input2.cc");
  auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match(
      FromTU2, functionTemplateDecl(hasName("f")));
  auto *ToD2 = Import(FromD2, Lang_CXX);
  EXPECT_EQ(ToD1, ToD2);
}

TEST_P(ImportFunctionTemplates,
       ImportFunctionWhenThereIsAFunTemplateWithSameName) {
  getToTuDecl(
      R"(
      template <typename T>
      void foo(T) {}
      void foo();
      )",
      Lang_CXX);
  Decl *FromTU = getTuDecl("void foo();", Lang_CXX);
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("foo")));
  auto *ImportedD = Import(FromD, Lang_CXX);
  EXPECT_TRUE(ImportedD);
}

TEST_P(ImportFunctionTemplates,
       ImportConstructorWhenThereIsAFunTemplateWithSameName) {
  auto Code =
      R"(
      struct Foo {
        template <typename T>
        Foo(T) {}
        Foo();
      };
      )";
  getToTuDecl(Code, Lang_CXX);
  Decl *FromTU = getTuDecl(Code, Lang_CXX);
  auto *FromD =
      LastDeclMatcher<CXXConstructorDecl>().match(FromTU, cxxConstructorDecl());
  auto *ImportedD = Import(FromD, Lang_CXX);
  EXPECT_TRUE(ImportedD);
}

TEST_P(ImportFunctionTemplates,
       ImportOperatorWhenThereIsAFunTemplateWithSameName) {
  getToTuDecl(
      R"(
      template <typename T>
      void operator<(T,T) {}
      struct X{};
      void operator<(X, X);
      )",
      Lang_CXX);
  Decl *FromTU = getTuDecl(
      R"(
      struct X{};
      void operator<(X, X);
      )",
      Lang_CXX);
  auto *FromD = LastDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasOverloadedOperatorName("<")));
  auto *ImportedD = Import(FromD, Lang_CXX);
  EXPECT_TRUE(ImportedD);
}

struct ImportFriendFunctions : ImportFunctions {};

TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto) {
  auto Pattern = functionDecl(hasName("f"));

  Decl *FromTU = getTuDecl("struct X { friend void f(); };"
                           "void f();",
                           Lang_CXX,
                           "input0.cc");
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);

  auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
  EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
  auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
  EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody());
  EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
}

TEST_P(ImportFriendFunctions,
       ImportFriendFunctionRedeclChainProto_OutOfClassProtoFirst) {
  auto Pattern = functionDecl(hasName("f"));

  Decl *FromTU = getTuDecl("void f();"
                           "struct X { friend void f(); };",
                           Lang_CXX, "input0.cc");
  auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);

  auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
  EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
  auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
  EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody());
  EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
}

TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainDef) {
  auto Pattern = functionDecl(hasName("f"));

  Decl *FromTU = getTuDecl("struct X { friend void f(){} };"
                           "void f();",
                           Lang_CXX,
                           "input0.cc");
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);

  auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
  EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody());
  auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
  EXPECT_FALSE(ToFD->doesThisDeclarationHaveABody());
  EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
}

TEST_P(ImportFriendFunctions,
       ImportFriendFunctionRedeclChainDef_OutOfClassDef) {
  auto Pattern = functionDecl(hasName("f"));

  Decl *FromTU = getTuDecl("struct X { friend void f(); };"
                           "void f(){}",
                           Lang_CXX, "input0.cc");
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);

  auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
  EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
  auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
  EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody());
  EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
}

TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainDefWithClass) {
  auto Pattern = functionDecl(hasName("f"));

  Decl *FromTU = getTuDecl(
      R"(
        class X;
        void f(X *x){}
        class X{
        friend void f(X *x);
        };
      )",
      Lang_CXX, "input0.cc");
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);

  auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
  EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody());
  auto *InClassFD = cast<FunctionDecl>(FirstDeclMatcher<FriendDecl>()
                                              .match(ToTU, friendDecl())
                                              ->getFriendDecl());
  EXPECT_FALSE(InClassFD->doesThisDeclarationHaveABody());
  EXPECT_EQ(InClassFD->getPreviousDecl(), ImportedD);
  // The parameters must refer the same type
  EXPECT_EQ((*InClassFD->param_begin())->getOriginalType(),
            (*ImportedD->param_begin())->getOriginalType());
}

TEST_P(ImportFriendFunctions,
       ImportFriendFunctionRedeclChainDefWithClass_ImportTheProto) {
  auto Pattern = functionDecl(hasName("f"));

  Decl *FromTU = getTuDecl(
      R"(
        class X;
        void f(X *x){}
        class X{
        friend void f(X *x);
        };
      )",
      Lang_CXX, "input0.cc");
  auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);

  auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
  EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
  auto *OutOfClassFD = FirstDeclMatcher<FunctionDecl>().match(
      ToTU, functionDecl(unless(hasParent(friendDecl()))));

  EXPECT_TRUE(OutOfClassFD->doesThisDeclarationHaveABody());
  EXPECT_EQ(ImportedD->getPreviousDecl(), OutOfClassFD);
  // The parameters must refer the same type
  EXPECT_EQ((*OutOfClassFD->param_begin())->getOriginalType(),
            (*ImportedD->param_begin())->getOriginalType());
}

TEST_P(ImportFriendFunctions, ImportFriendFunctionFromMultipleTU) {
  auto Pattern = functionDecl(hasName("f"));

  FunctionDecl *ImportedD;
  {
    Decl *FromTU =
        getTuDecl("struct X { friend void f(){} };", Lang_CXX, "input0.cc");
    auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
    ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
  }
  FunctionDecl *ImportedD1;
  {
    Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
    auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
    ImportedD1 = cast<FunctionDecl>(Import(FromD, Lang_CXX));
  }

  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
  EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody());
  EXPECT_FALSE(ImportedD1->doesThisDeclarationHaveABody());
  EXPECT_EQ(ImportedD1->getPreviousDecl(), ImportedD);
}

TEST_P(ImportFriendFunctions, Lookup) {
  auto FunctionPattern = functionDecl(hasName("f"));
  auto ClassPattern = cxxRecordDecl(hasName("X"));

  TranslationUnitDecl *FromTU =
      getTuDecl("struct X { friend void f(); };", Lang_CXX, "input0.cc");
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
  ASSERT_TRUE(FromD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  ASSERT_FALSE(FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary));
  {
    auto FromName = FromD->getDeclName();
    auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
    auto LookupRes = Class->noload_lookup(FromName);
    ASSERT_EQ(LookupRes.size(), 0u);
    LookupRes = FromTU->noload_lookup(FromName);
    ASSERT_EQ(LookupRes.size(), 1u);
  }

  auto *ToD = cast<FunctionDecl>(Import(FromD, Lang_CXX));
  auto ToName = ToD->getDeclName();

  TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
  auto LookupRes = Class->noload_lookup(ToName);
  EXPECT_EQ(LookupRes.size(), 0u);
  LookupRes = ToTU->noload_lookup(ToName);
  EXPECT_EQ(LookupRes.size(), 1u);

  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 1u);
  auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
  EXPECT_TRUE(To0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  EXPECT_FALSE(To0->isInIdentifierNamespace(Decl::IDNS_Ordinary));
}

TEST_P(ImportFriendFunctions, LookupWithProtoAfter) {
  auto FunctionPattern = functionDecl(hasName("f"));
  auto ClassPattern = cxxRecordDecl(hasName("X"));

  TranslationUnitDecl *FromTU = getTuDecl(
      "struct X { friend void f(); };"
      // This proto decl makes f available to normal
      // lookup, otherwise it is hidden.
      // Normal C++ lookup (implemented in
      // `clang::Sema::CppLookupName()` and in `LookupDirect()`)
      // returns the found `NamedDecl` only if the set IDNS is matched
      "void f();",
      Lang_CXX, "input0.cc");
  auto *FromFriend =
      FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
  auto *FromNormal =
      LastDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
  ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  ASSERT_FALSE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
  ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));

  auto FromName = FromFriend->getDeclName();
  auto *FromClass =
      FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
  auto LookupRes = FromClass->noload_lookup(FromName);
  ASSERT_EQ(LookupRes.size(), 0u);
  LookupRes = FromTU->noload_lookup(FromName);
  ASSERT_EQ(LookupRes.size(), 1u);

  auto *ToFriend = cast<FunctionDecl>(Import(FromFriend, Lang_CXX));
  auto ToName = ToFriend->getDeclName();

  TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
  LookupRes = ToClass->noload_lookup(ToName);
  EXPECT_EQ(LookupRes.size(), 0u);
  LookupRes = ToTU->noload_lookup(ToName);
  // Test is disabled because this result is 2.
  EXPECT_EQ(LookupRes.size(), 1u);

  ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 2u);
  ToFriend = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
  auto *ToNormal = LastDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
  EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  EXPECT_FALSE(ToFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
  EXPECT_FALSE(ToNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  EXPECT_TRUE(ToNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
}

TEST_P(ImportFriendFunctions, LookupWithProtoBefore) {
  auto FunctionPattern = functionDecl(hasName("f"));
  auto ClassPattern = cxxRecordDecl(hasName("X"));

  TranslationUnitDecl *FromTU = getTuDecl(
      "void f();"
      "struct X { friend void f(); };",
      Lang_CXX, "input0.cc");
  auto *FromNormal =
      FirstDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
  auto *FromFriend =
      LastDeclMatcher<FunctionDecl>().match(FromTU, FunctionPattern);
  ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
  ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));

  auto FromName = FromNormal->getDeclName();
  auto *FromClass =
      FirstDeclMatcher<CXXRecordDecl>().match(FromTU, ClassPattern);
  auto LookupRes = FromClass->noload_lookup(FromName);
  ASSERT_EQ(LookupRes.size(), 0u);
  LookupRes = FromTU->noload_lookup(FromName);
  ASSERT_EQ(LookupRes.size(), 1u);

  auto *ToNormal = cast<FunctionDecl>(Import(FromNormal, Lang_CXX));
  auto ToName = ToNormal->getDeclName();
  TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();

  auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
  LookupRes = ToClass->noload_lookup(ToName);
  EXPECT_EQ(LookupRes.size(), 0u);
  LookupRes = ToTU->noload_lookup(ToName);
  EXPECT_EQ(LookupRes.size(), 1u);

  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 2u);
  ToNormal = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
  auto *ToFriend = LastDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
  EXPECT_FALSE(ToNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  EXPECT_TRUE(ToNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary));
  EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary));
}

TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) {
  auto Pattern = functionDecl(hasName("f"));

  TranslationUnitDecl *FromNormalTU =
      getTuDecl("void f();", Lang_CXX, "input0.cc");
  auto *FromNormalF =
      FirstDeclMatcher<FunctionDecl>().match(FromNormalTU, Pattern);
  TranslationUnitDecl *FromFriendTU =
      getTuDecl("class X { friend void f(); };", Lang_CXX, "input1.cc");
  auto *FromFriendF =
      FirstDeclMatcher<FunctionDecl>().match(FromFriendTU, Pattern);
  auto FromNormalName = FromNormalF->getDeclName();
  auto FromFriendName = FromFriendF->getDeclName();

  ASSERT_TRUE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
  ASSERT_FALSE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  ASSERT_FALSE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
  ASSERT_TRUE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  auto LookupRes = FromNormalTU->noload_lookup(FromNormalName);
  ASSERT_EQ(LookupRes.size(), 1u);
  LookupRes = FromFriendTU->noload_lookup(FromFriendName);
  ASSERT_EQ(LookupRes.size(), 1u);

  auto *ToNormalF = cast<FunctionDecl>(Import(FromNormalF, Lang_CXX));
  TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  auto ToName = ToNormalF->getDeclName();
  EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
  EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
  LookupRes = ToTU->noload_lookup(ToName);
  EXPECT_EQ(LookupRes.size(), 1u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);

  auto *ToFriendF = cast<FunctionDecl>(Import(FromFriendF, Lang_CXX));
  LookupRes = ToTU->noload_lookup(ToName);
  EXPECT_EQ(LookupRes.size(), 1u);
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);

  EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
  EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));

  EXPECT_TRUE(ToFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary));
  EXPECT_TRUE(ToFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
}

TEST_P(ImportFriendFunctions, ImportFriendList) {
  TranslationUnitDecl *FromTU = getTuDecl(
      "struct X { friend void f(); };"
      "void f();",
      Lang_CXX, "input0.cc");
  auto *FromFriendF = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));

  auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("X")));
  auto *FromFriend = FirstDeclMatcher<FriendDecl>().match(FromTU, friendDecl());
  auto FromFriends = FromClass->friends();
  unsigned int FrN = 0;
  for (auto Fr : FromFriends) {
    ASSERT_EQ(Fr, FromFriend);
    ++FrN;
  }
  ASSERT_EQ(FrN, 1u);

  Import(FromFriendF, Lang_CXX);
  TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(
      ToTU, cxxRecordDecl(hasName("X")));
  auto *ToFriend = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
  auto ToFriends = ToClass->friends();
  FrN = 0;
  for (auto Fr : ToFriends) {
    EXPECT_EQ(Fr, ToFriend);
    ++FrN;
  }
  EXPECT_EQ(FrN, 1u);
}

AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher<TypedefNameDecl>,
              InnerMatcher) {
  if (auto *Typedef = Node.getTypedefNameForAnonDecl())
    return InnerMatcher.matches(*Typedef, Finder, Builder);
  return false;
}

TEST_P(ImportDecl, ImportEnumSequential) {
  CodeFiles Samples{{"main.c",
                     {"void foo();"
                      "void moo();"
                      "int main() { foo(); moo(); }",
                      Lang_C}},

                    {"foo.c",
                     {"typedef enum { THING_VALUE } thing_t;"
                      "void conflict(thing_t type);"
                      "void foo() { (void)THING_VALUE; }"
                      "void conflict(thing_t type) {}",
                      Lang_C}},

                    {"moo.c",
                     {"typedef enum { THING_VALUE } thing_t;"
                      "void conflict(thing_t type);"
                      "void moo() { conflict(THING_VALUE); }",
                      Lang_C}}};

  auto VerificationMatcher =
      enumDecl(has(enumConstantDecl(hasName("THING_VALUE"))),
               hasTypedefForAnonDecl(hasName("thing_t")));

  ImportAction ImportFoo{"foo.c", "main.c", functionDecl(hasName("foo"))},
      ImportMoo{"moo.c", "main.c", functionDecl(hasName("moo"))};

  testImportSequence(
      Samples, {ImportFoo, ImportMoo}, // "foo", them "moo".
      // Just check that there is only one enum decl in the result AST.
      "main.c", enumDecl(), VerificationMatcher);

  // For different import order, result should be the same.
  testImportSequence(
      Samples, {ImportMoo, ImportFoo}, // "moo", them "foo".
      // Check that there is only one enum decl in the result AST.
      "main.c", enumDecl(), VerificationMatcher);
}

TEST_P(ImportDecl, ImportFieldOrder) {
  MatchVerifier<Decl> Verifier;
  testImport("struct declToImport {"
             "  int b = a + 2;"
             "  int a = 5;"
             "};",
             Lang_CXX11, "", Lang_CXX11, Verifier,
             recordDecl(hasFieldOrder({"b", "a"})));
}

const internal::VariadicDynCastAllOfMatcher<Expr, DependentScopeDeclRefExpr>
    dependentScopeDeclRefExpr;

TEST_P(ImportExpr, DependentScopeDeclRefExpr) {
  MatchVerifier<Decl> Verifier;
  testImport("template <typename T> struct S { static T foo; };"
             "template <typename T> void declToImport() {"
             "  (void) S<T>::foo;"
             "}"
             "void instantiate() { declToImport<int>(); }"
             "template <typename T> T S<T>::foo;",
             Lang_CXX11, "", Lang_CXX11, Verifier,
             functionTemplateDecl(has(functionDecl(has(compoundStmt(
                 has(cStyleCastExpr(has(dependentScopeDeclRefExpr())))))))));

  testImport("template <typename T> struct S {"
             "template<typename S> static void foo(){};"
             "};"
             "template <typename T> void declToImport() {"
             "  S<T>::template foo<T>();"
             "}"
             "void instantiate() { declToImport<int>(); }",
             Lang_CXX11, "", Lang_CXX11, Verifier,
             functionTemplateDecl(has(functionDecl(has(compoundStmt(
                 has(callExpr(has(dependentScopeDeclRefExpr())))))))));
}

const internal::VariadicDynCastAllOfMatcher<Type, DependentNameType>
    dependentNameType;

TEST_P(ImportExpr, DependentNameType) {
  MatchVerifier<Decl> Verifier;
  testImport("template <typename T> struct declToImport {"
             "  typedef typename T::type dependent_name;"
             "};",
             Lang_CXX11, "", Lang_CXX11, Verifier,
             classTemplateDecl(has(
                 cxxRecordDecl(has(typedefDecl(has(dependentNameType())))))));
}

TEST_P(ImportExpr, UnresolvedMemberExpr) {
  MatchVerifier<Decl> Verifier;
  testImport("struct S { template <typename T> void mem(); };"
             "template <typename U> void declToImport() {"
             "  S s;"
             "  s.mem<U>();"
             "}"
             "void instantiate() { declToImport<int>(); }",
             Lang_CXX11, "", Lang_CXX11, Verifier,
             functionTemplateDecl(has(functionDecl(has(
                 compoundStmt(has(callExpr(has(unresolvedMemberExpr())))))))));
}

class ImportImplicitMethods : public ASTImporterOptionSpecificTestBase {
public:
  static constexpr auto DefaultCode = R"(
      struct A { int x; };
      void f() {
        A a;
        A a1(a);
        A a2(A{});
        a = a1;
        a = A{};
        a.~A();
      })";

  template <typename MatcherType>
  void testImportOf(
      const MatcherType &MethodMatcher, const char *Code = DefaultCode) {
    test(MethodMatcher, Code, /*ExpectedCount=*/1u);
  }

  template <typename MatcherType>
  void testNoImportOf(
      const MatcherType &MethodMatcher, const char *Code = DefaultCode) {
    test(MethodMatcher, Code, /*ExpectedCount=*/0u);
  }

private:
  template <typename MatcherType>
  void test(const MatcherType &MethodMatcher,
      const char *Code, unsigned int ExpectedCount) {
    auto ClassMatcher = cxxRecordDecl(unless(isImplicit()));

    Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
    auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(
        ToTU, ClassMatcher);

    ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 1u);

    {
      CXXMethodDecl *Method =
          FirstDeclMatcher<CXXMethodDecl>().match(ToClass, MethodMatcher);
      ToClass->removeDecl(Method);
      SharedStatePtr->getLookupTable()->remove(Method);
    }

    ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 0u);

    Decl *ImportedClass = nullptr;
    {
      Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input1.cc");
      auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
          FromTU, ClassMatcher);
      ImportedClass = Import(FromClass, Lang_CXX11);
    }

    EXPECT_EQ(ToClass, ImportedClass);
    EXPECT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher),
        ExpectedCount);
  }
};

TEST_P(ImportImplicitMethods, DefaultConstructor) {
  testImportOf(cxxConstructorDecl(isDefaultConstructor()));
}

TEST_P(ImportImplicitMethods, CopyConstructor) {
  testImportOf(cxxConstructorDecl(isCopyConstructor()));
}

TEST_P(ImportImplicitMethods, MoveConstructor) {
  testImportOf(cxxConstructorDecl(isMoveConstructor()));
}

TEST_P(ImportImplicitMethods, Destructor) {
  testImportOf(cxxDestructorDecl());
}

TEST_P(ImportImplicitMethods, CopyAssignment) {
  testImportOf(cxxMethodDecl(isCopyAssignmentOperator()));
}

TEST_P(ImportImplicitMethods, MoveAssignment) {
  testImportOf(cxxMethodDecl(isMoveAssignmentOperator()));
}

TEST_P(ImportImplicitMethods, DoNotImportUserProvided) {
  auto Code = R"(
      struct A { A() { int x; } };
      )";
  testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code);
}

TEST_P(ImportImplicitMethods, DoNotImportDefault) {
  auto Code = R"(
      struct A { A() = default; };
      )";
  testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code);
}

TEST_P(ImportImplicitMethods, DoNotImportDeleted) {
  auto Code = R"(
      struct A { A() = delete; };
      )";
  testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code);
}

TEST_P(ImportImplicitMethods, DoNotImportOtherMethod) {
  auto Code = R"(
      struct A { void f() { } };
      )";
  testNoImportOf(cxxMethodDecl(hasName("f")), Code);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentRecord) {
  Decl *ToR1;
  {
    Decl *FromTU = getTuDecl(
        "struct A { };", Lang_CXX, "input0.cc");
    auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
        FromTU, cxxRecordDecl(hasName("A")));

    ToR1 = Import(FromR, Lang_CXX);
  }

  Decl *ToR2;
  {
    Decl *FromTU = getTuDecl(
        "struct A { };", Lang_CXX, "input1.cc");
    auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
        FromTU, cxxRecordDecl(hasName("A")));

    ToR2 = Import(FromR, Lang_CXX);
  }

  EXPECT_EQ(ToR1, ToR2);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentRecord) {
  Decl *ToR1;
  {
    Decl *FromTU = getTuDecl(
        "struct A { int x; };", Lang_CXX, "input0.cc");
    auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
        FromTU, cxxRecordDecl(hasName("A")));
    ToR1 = Import(FromR, Lang_CXX);
  }
  Decl *ToR2;
  {
    Decl *FromTU = getTuDecl(
        "struct A { unsigned x; };", Lang_CXX, "input1.cc");
    auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
        FromTU, cxxRecordDecl(hasName("A")));
    ToR2 = Import(FromR, Lang_CXX);
  }
  EXPECT_NE(ToR1, ToR2);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentField) {
  Decl *ToF1;
  {
    Decl *FromTU = getTuDecl(
        "struct A { int x; };", Lang_CXX, "input0.cc");
    auto *FromF = FirstDeclMatcher<FieldDecl>().match(
        FromTU, fieldDecl(hasName("x")));
    ToF1 = Import(FromF, Lang_CXX);
  }
  Decl *ToF2;
  {
    Decl *FromTU = getTuDecl(
        "struct A { int x; };", Lang_CXX, "input1.cc");
    auto *FromF = FirstDeclMatcher<FieldDecl>().match(
        FromTU, fieldDecl(hasName("x")));
    ToF2 = Import(FromF, Lang_CXX);
  }
  EXPECT_EQ(ToF1, ToF2);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentField) {
  Decl *ToF1;
  {
    Decl *FromTU = getTuDecl(
        "struct A { int x; };", Lang_CXX, "input0.cc");
    auto *FromF = FirstDeclMatcher<FieldDecl>().match(
        FromTU, fieldDecl(hasName("x")));
    ToF1 = Import(FromF, Lang_CXX);
  }
  Decl *ToF2;
  {
    Decl *FromTU = getTuDecl(
        "struct A { unsigned x; };", Lang_CXX, "input1.cc");
    auto *FromF = FirstDeclMatcher<FieldDecl>().match(
        FromTU, fieldDecl(hasName("x")));
    ToF2 = Import(FromF, Lang_CXX);
  }
  EXPECT_NE(ToF1, ToF2);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentMethod) {
  Decl *ToM1;
  {
    Decl *FromTU = getTuDecl(
        "struct A { void x(); }; void A::x() { }", Lang_CXX, "input0.cc");
    auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("x"), isDefinition()));
    ToM1 = Import(FromM, Lang_CXX);
  }
  Decl *ToM2;
  {
    Decl *FromTU = getTuDecl(
        "struct A { void x(); }; void A::x() { }", Lang_CXX, "input1.cc");
    auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("x"), isDefinition()));
    ToM2 = Import(FromM, Lang_CXX);
  }
  EXPECT_EQ(ToM1, ToM2);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentMethod) {
  Decl *ToM1;
  {
    Decl *FromTU = getTuDecl(
        "struct A { void x(); }; void A::x() { }",
        Lang_CXX, "input0.cc");
    auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("x"), isDefinition()));
    ToM1 = Import(FromM, Lang_CXX);
  }
  Decl *ToM2;
  {
    Decl *FromTU = getTuDecl(
        "struct A { void x() const; }; void A::x() const { }",
        Lang_CXX, "input1.cc");
    auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("x"), isDefinition()));
    ToM2 = Import(FromM, Lang_CXX);
  }
  EXPECT_NE(ToM1, ToM2);
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportUnnamedStructsWithRecursingField) {
  Decl *FromTU = getTuDecl(
      R"(
      struct A {
        struct {
          struct A *next;
        } entry0;
        struct {
          struct A *next;
        } entry1;
      };
      )",
      Lang_C, "input0.cc");
  auto *From =
      FirstDeclMatcher<RecordDecl>().match(FromTU, recordDecl(hasName("A")));

  Import(From, Lang_C);

  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  auto *Entry0 =
      FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry0")));
  auto *Entry1 =
      FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry1")));
  auto *R0 = getRecordDecl(Entry0);
  auto *R1 = getRecordDecl(Entry1);
  EXPECT_NE(R0, R1);
  EXPECT_TRUE(MatchVerifier<RecordDecl>().match(
      R0, recordDecl(has(fieldDecl(hasName("next"))))));
  EXPECT_TRUE(MatchVerifier<RecordDecl>().match(
      R1, recordDecl(has(fieldDecl(hasName("next"))))));
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportUnnamedFieldsInCorrectOrder) {
  Decl *FromTU = getTuDecl(
      R"(
      void f(int X, int Y, bool Z) {
        (void)[X, Y, Z] { (void)Z; };
      }
      )",
      Lang_CXX11, "input0.cc");
  auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));
  auto *ToF = cast_or_null<FunctionDecl>(Import(FromF, Lang_CXX11));
  EXPECT_TRUE(ToF);

  CXXRecordDecl *FromLambda =
      cast<LambdaExpr>(cast<CStyleCastExpr>(cast<CompoundStmt>(
          FromF->getBody())->body_front())->getSubExpr())->getLambdaClass();

  auto *ToLambda = cast_or_null<CXXRecordDecl>(Import(FromLambda, Lang_CXX11));
  EXPECT_TRUE(ToLambda);

  // Check if the fields of the lambda class are imported in correct order.
  unsigned FromIndex = 0u;
  for (auto *FromField : FromLambda->fields()) {
    ASSERT_FALSE(FromField->getDeclName());
    auto *ToField = cast_or_null<FieldDecl>(Import(FromField, Lang_CXX11));
    EXPECT_TRUE(ToField);
    Optional<unsigned> ToIndex = ASTImporter::getFieldIndex(ToField);
    EXPECT_TRUE(ToIndex);
    EXPECT_EQ(*ToIndex, FromIndex);
    ++FromIndex;
  }

  EXPECT_EQ(FromIndex, 3u);
}

TEST_P(ASTImporterOptionSpecificTestBase,
       MergeFieldDeclsOfClassTemplateSpecialization) {
  std::string ClassTemplate =
      R"(
      template <typename T>
      struct X {
          int a{0}; // FieldDecl with InitListExpr
          X(char) : a(3) {}     // (1)
          X(int) {}             // (2)
      };
      )";
  Decl *ToTU = getToTuDecl(ClassTemplate +
      R"(
      void foo() {
          // ClassTemplateSpec with ctor (1): FieldDecl without InitlistExpr
          X<char> xc('c');
      }
      )", Lang_CXX11);
  auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      ToTU, classTemplateSpecializationDecl(hasName("X")));
  // FieldDecl without InitlistExpr:
  auto *ToField = *ToSpec->field_begin();
  ASSERT_TRUE(ToField);
  ASSERT_FALSE(ToField->getInClassInitializer());
  Decl *FromTU = getTuDecl(ClassTemplate +
      R"(
      void bar() {
          // ClassTemplateSpec with ctor (2): FieldDecl WITH InitlistExpr
          X<char> xc(1);
      }
      )", Lang_CXX11);
  auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      FromTU, classTemplateSpecializationDecl(hasName("X")));
  // FieldDecl with InitlistExpr:
  auto *FromField = *FromSpec->field_begin();
  ASSERT_TRUE(FromField);
  ASSERT_TRUE(FromField->getInClassInitializer());

  auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
  ASSERT_TRUE(ImportedSpec);
  EXPECT_EQ(ImportedSpec, ToSpec);
  // After the import, the FieldDecl has to be merged, thus it should have the
  // InitListExpr.
  EXPECT_TRUE(ToField->getInClassInitializer());
}

TEST_P(ASTImporterOptionSpecificTestBase,
       MergeFunctionOfClassTemplateSpecialization) {
  std::string ClassTemplate =
      R"(
      template <typename T>
      struct X {
        void f() {}
        void g() {}
      };
      )";
  Decl *ToTU = getToTuDecl(ClassTemplate +
      R"(
      void foo() {
          X<char> x;
          x.f();
      }
      )", Lang_CXX11);
  Decl *FromTU = getTuDecl(ClassTemplate +
      R"(
      void bar() {
          X<char> x;
          x.g();
      }
      )", Lang_CXX11);
  auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      FromTU, classTemplateSpecializationDecl(hasName("X")));
  auto FunPattern = functionDecl(hasName("g"),
                         hasParent(classTemplateSpecializationDecl()));
  auto *FromFun =
      FirstDeclMatcher<FunctionDecl>().match(FromTU, FunPattern);
  auto *ToFun =
      FirstDeclMatcher<FunctionDecl>().match(ToTU, FunPattern);
  ASSERT_TRUE(FromFun->hasBody());
  ASSERT_FALSE(ToFun->hasBody());
  auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
  ASSERT_TRUE(ImportedSpec);
  auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      ToTU, classTemplateSpecializationDecl(hasName("X")));
  EXPECT_EQ(ImportedSpec, ToSpec);
  EXPECT_TRUE(ToFun->hasBody());
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ODRViolationOfClassTemplateSpecializationsShouldBeReported) {
  std::string ClassTemplate =
      R"(
      template <typename T>
      struct X {};
      )";
  Decl *ToTU = getToTuDecl(ClassTemplate +
                               R"(
      template <>
      struct X<char> {
          int a;
      };
      void foo() {
          X<char> x;
      }
      )",
                           Lang_CXX11);
  Decl *FromTU = getTuDecl(ClassTemplate +
                               R"(
      template <>
      struct X<char> {
          int b;
      };
      void foo() {
          X<char> x;
      }
      )",
                           Lang_CXX11);
  auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      FromTU, classTemplateSpecializationDecl(hasName("X")));
  auto *ImportedSpec = Import(FromSpec, Lang_CXX11);

  // We expect one (ODR) warning during the import.
  EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());

  // The second specialization is different from the first, thus it violates
  // ODR, consequently we expect to keep the first specialization only, which is
  // already in the "To" context.
  EXPECT_FALSE(ImportedSpec);
  EXPECT_EQ(1u,
            DeclCounter<ClassTemplateSpecializationDecl>().match(
                ToTU, classTemplateSpecializationDecl(hasName("X"))));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       MergeCtorOfClassTemplateSpecialization) {
  std::string ClassTemplate =
      R"(
      template <typename T>
      struct X {
          X(char) {}
          X(int) {}
      };
      )";
  Decl *ToTU = getToTuDecl(ClassTemplate +
      R"(
      void foo() {
          X<char> x('c');
      }
      )", Lang_CXX11);
  Decl *FromTU = getTuDecl(ClassTemplate +
      R"(
      void bar() {
          X<char> x(1);
      }
      )", Lang_CXX11);
  auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      FromTU, classTemplateSpecializationDecl(hasName("X")));
  // Match the void(int) ctor.
  auto CtorPattern =
      cxxConstructorDecl(hasParameter(0, varDecl(hasType(asString("int")))),
                         hasParent(classTemplateSpecializationDecl()));
  auto *FromCtor =
      FirstDeclMatcher<CXXConstructorDecl>().match(FromTU, CtorPattern);
  auto *ToCtor =
      FirstDeclMatcher<CXXConstructorDecl>().match(ToTU, CtorPattern);
  ASSERT_TRUE(FromCtor->hasBody());
  ASSERT_FALSE(ToCtor->hasBody());
  auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
  ASSERT_TRUE(ImportedSpec);
  auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      ToTU, classTemplateSpecializationDecl(hasName("X")));
  EXPECT_EQ(ImportedSpec, ToSpec);
  EXPECT_TRUE(ToCtor->hasBody());
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ClassTemplatePartialSpecializationsShouldNotBeDuplicated) {
  auto Code =
      R"(
    // primary template
    template<class T1, class T2, int I>
    class A {};

    // partial specialization
    template<class T, int I>
    class A<T, T*, I> {};
    )";
  Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
  Decl *FromTU = getTuDecl(Code, Lang_CXX11);
  auto *FromSpec =
      FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
          FromTU, classTemplatePartialSpecializationDecl());
  auto *ToSpec =
      FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
          ToTU, classTemplatePartialSpecializationDecl());

  auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
  EXPECT_EQ(ImportedSpec, ToSpec);
  EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match(
                    ToTU, classTemplatePartialSpecializationDecl()));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ClassTemplateSpecializationsShouldNotBeDuplicated) {
  auto Code =
      R"(
    // primary template
    template<class T1, class T2, int I>
    class A {};

    // full specialization
    template<>
    class A<int, int, 1> {};
    )";
  Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
  Decl *FromTU = getTuDecl(Code, Lang_CXX11);
  auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      FromTU, classTemplateSpecializationDecl());
  auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      ToTU, classTemplateSpecializationDecl());

  auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
  EXPECT_EQ(ImportedSpec, ToSpec);
  EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
                   ToTU, classTemplateSpecializationDecl()));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ClassTemplateFullAndPartialSpecsShouldNotBeMixed) {
  std::string PrimaryTemplate =
      R"(
    template<class T1, class T2, int I>
    class A {};
    )";
  auto PartialSpec =
      R"(
    template<class T, int I>
    class A<T, T*, I> {};
    )";
  auto FullSpec =
      R"(
    template<>
    class A<int, int, 1> {};
    )";
  Decl *ToTU = getToTuDecl(PrimaryTemplate + FullSpec, Lang_CXX11);
  Decl *FromTU = getTuDecl(PrimaryTemplate + PartialSpec, Lang_CXX11);
  auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      FromTU, classTemplateSpecializationDecl());

  auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
  EXPECT_TRUE(ImportedSpec);
  // Check the number of partial specializations.
  EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match(
                    ToTU, classTemplatePartialSpecializationDecl()));
  // Check the number of full specializations.
  EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
                    ToTU, classTemplateSpecializationDecl(
                              unless(classTemplatePartialSpecializationDecl()))));
}

TEST_P(ASTImporterOptionSpecificTestBase,
       InitListExprValueKindShouldBeImported) {
  Decl *TU = getTuDecl(
      R"(
      const int &init();
      void foo() { const int &a{init()}; }
      )", Lang_CXX11, "input0.cc");
  auto *FromD = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("a")));
  ASSERT_TRUE(FromD->getAnyInitializer());
  auto *InitExpr = FromD->getAnyInitializer();
  ASSERT_TRUE(InitExpr);
  ASSERT_TRUE(InitExpr->isGLValue());

  auto *ToD = Import(FromD, Lang_CXX11);
  EXPECT_TRUE(ToD);
  auto *ToInitExpr = cast<VarDecl>(ToD)->getAnyInitializer();
  EXPECT_TRUE(ToInitExpr);
  EXPECT_TRUE(ToInitExpr->isGLValue());
}

struct ImportVariables : ASTImporterOptionSpecificTestBase {};

TEST_P(ImportVariables, ImportOfOneDeclBringsInTheWholeChain) {
  Decl *FromTU = getTuDecl(
      R"(
      struct A {
        static const int a = 1 + 2;
      };
      const int A::a;
      )", Lang_CXX, "input1.cc");

  auto *FromDWithInit = FirstDeclMatcher<VarDecl>().match(
      FromTU, varDecl(hasName("a"))); // Decl with init
  auto *FromDWithDef = LastDeclMatcher<VarDecl>().match(
      FromTU, varDecl(hasName("a"))); // Decl with definition
  ASSERT_NE(FromDWithInit, FromDWithDef);
  ASSERT_EQ(FromDWithDef->getPreviousDecl(), FromDWithInit);

  auto *ToD0 = cast<VarDecl>(Import(FromDWithInit, Lang_CXX11));
  auto *ToD1 = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11));
  ASSERT_TRUE(ToD0);
  ASSERT_TRUE(ToD1);
  EXPECT_NE(ToD0, ToD1);
  EXPECT_EQ(ToD1->getPreviousDecl(), ToD0);
}

TEST_P(ImportVariables, InitAndDefinitionAreInDifferentTUs) {
  auto StructA =
      R"(
      struct A {
        static const int a = 1 + 2;
      };
      )";
  Decl *ToTU = getToTuDecl(StructA, Lang_CXX);
  Decl *FromTU = getTuDecl(std::string(StructA) + "const int A::a;", Lang_CXX,
                           "input1.cc");

  auto *FromDWithInit = FirstDeclMatcher<VarDecl>().match(
      FromTU, varDecl(hasName("a"))); // Decl with init
  auto *FromDWithDef = LastDeclMatcher<VarDecl>().match(
      FromTU, varDecl(hasName("a"))); // Decl with definition
  ASSERT_EQ(FromDWithInit, FromDWithDef->getPreviousDecl());
  ASSERT_TRUE(FromDWithInit->getInit());
  ASSERT_FALSE(FromDWithInit->isThisDeclarationADefinition());
  ASSERT_TRUE(FromDWithDef->isThisDeclarationADefinition());
  ASSERT_FALSE(FromDWithDef->getInit());

  auto *ToD = FirstDeclMatcher<VarDecl>().match(
      ToTU, varDecl(hasName("a"))); // Decl with init
  ASSERT_TRUE(ToD->getInit());
  ASSERT_FALSE(ToD->getDefinition());

  auto *ImportedD = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11));
  EXPECT_TRUE(ImportedD->getAnyInitializer());
  EXPECT_TRUE(ImportedD->getDefinition());
}

TEST_P(ImportVariables, InitAndDefinitionAreInTheFromContext) {
  auto StructA =
      R"(
      struct A {
        static const int a;
      };
      )";
  Decl *ToTU = getToTuDecl(StructA, Lang_CXX);
  Decl *FromTU = getTuDecl(std::string(StructA) + "const int A::a = 1 + 2;",
                           Lang_CXX, "input1.cc");

  auto *FromDDeclarationOnly = FirstDeclMatcher<VarDecl>().match(
      FromTU, varDecl(hasName("a")));
  auto *FromDWithDef = LastDeclMatcher<VarDecl>().match(
      FromTU, varDecl(hasName("a"))); // Decl with definition and with init.
  ASSERT_EQ(FromDDeclarationOnly, FromDWithDef->getPreviousDecl());
  ASSERT_FALSE(FromDDeclarationOnly->getInit());
  ASSERT_FALSE(FromDDeclarationOnly->isThisDeclarationADefinition());
  ASSERT_TRUE(FromDWithDef->isThisDeclarationADefinition());
  ASSERT_TRUE(FromDWithDef->getInit());

  auto *ToD = FirstDeclMatcher<VarDecl>().match(
      ToTU, varDecl(hasName("a")));
  ASSERT_FALSE(ToD->getInit());
  ASSERT_FALSE(ToD->getDefinition());

  auto *ImportedD = cast<VarDecl>(Import(FromDWithDef, Lang_CXX11));
  EXPECT_TRUE(ImportedD->getAnyInitializer());
  EXPECT_TRUE(ImportedD->getDefinition());
}

struct ImportClasses : ASTImporterOptionSpecificTestBase {};

TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContext) {
  Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_C);
  Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
  auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
  auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
  auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);

  Decl *ImportedDef = Import(FromDef, Lang_C);

  EXPECT_NE(ImportedDef, ToProto);
  EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
  auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
  EXPECT_TRUE(ImportedDef == ToDef);
  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
  EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
}

TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContextCXX) {
  Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_CXX);
  Decl *FromTU1 = getTuDecl("struct X {};", Lang_CXX, "input1.cc");
  auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
  auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
  auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);

  Decl *ImportedDef = Import(FromDef, Lang_CXX);

  EXPECT_NE(ImportedDef, ToProto);
  EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
  auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
  EXPECT_TRUE(ImportedDef == ToDef);
  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
  EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
}

TEST_P(ImportClasses, ImportNestedPrototypeThenDefinition) {
  Decl *FromTU0 = getTuDecl("struct A { struct X *Xp; };", Lang_C, "input0.cc");
  Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
  auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
  auto FromProto = FirstDeclMatcher<RecordDecl>().match(FromTU0, Pattern);
  auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);

  Decl *ImportedProto = Import(FromProto, Lang_C);
  Decl *ImportedDef = Import(FromDef, Lang_C);
  Decl *ToTU = ImportedDef->getTranslationUnitDecl();

  EXPECT_NE(ImportedDef, ImportedProto);
  EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
  auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
  auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
  EXPECT_TRUE(ImportedDef == ToDef);
  EXPECT_TRUE(ImportedProto == ToProto);
  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
  EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
}


struct ImportFriendClasses : ASTImporterOptionSpecificTestBase {};

TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) {
  Decl *FromTU = getTuDecl(
      R"(
      class A {
        template <int I> class F {};
        class X {
          template <int I> friend class F;
        };
      };
      )",
      Lang_CXX, "input0.cc");

  auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
  auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("F")));

  ASSERT_TRUE(FromClass);
  ASSERT_TRUE(FromFriendClass);
  ASSERT_NE(FromClass, FromFriendClass);
  ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
  ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
  ASSERT_EQ(FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
            FromClass->getDescribedClassTemplate());

  auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
  auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));

  EXPECT_TRUE(ToClass);
  EXPECT_TRUE(ToFriendClass);
  EXPECT_NE(ToClass, ToFriendClass);
  EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
  EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
  EXPECT_EQ(ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
            ToClass->getDescribedClassTemplate());
}

TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClass) {
  Decl *FromTu = getTuDecl(
      R"(
      class declToImport {
        friend class declToImport;
      };
      )",
      Lang_CXX, "input.cc");

  auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTu, cxxRecordDecl(hasName("declToImport")));
  auto *ToD = Import(FromD, Lang_CXX);
  auto Pattern = cxxRecordDecl(has(friendDecl()));
  ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
  EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
}

TEST_P(ImportFriendClasses, UndeclaredFriendClassShouldNotBeVisible) {
  Decl *FromTu = getTuDecl("class X { friend class Y; };", Lang_CXX, "from.cc");
  auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTu, cxxRecordDecl(hasName("X")));
  auto *FromFriend = FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
  RecordDecl *FromRecordOfFriend =
      const_cast<RecordDecl *>(getRecordDeclOfFriend(FromFriend));

  ASSERT_EQ(FromRecordOfFriend->getDeclContext(), cast<DeclContext>(FromTu));
  ASSERT_EQ(FromRecordOfFriend->getLexicalDeclContext(),
            cast<DeclContext>(FromX));
  ASSERT_FALSE(
      FromRecordOfFriend->getDeclContext()->containsDecl(FromRecordOfFriend));
  ASSERT_FALSE(FromRecordOfFriend->getLexicalDeclContext()->containsDecl(
      FromRecordOfFriend));
  ASSERT_FALSE(FromRecordOfFriend->getLookupParent()
                   ->lookup(FromRecordOfFriend->getDeclName())
                   .empty());

  auto *ToX = Import(FromX, Lang_CXX);
  ASSERT_TRUE(ToX);

  Decl *ToTu = ToX->getTranslationUnitDecl();
  auto *ToFriend = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
  RecordDecl *ToRecordOfFriend =
      const_cast<RecordDecl *>(getRecordDeclOfFriend(ToFriend));

  ASSERT_EQ(ToRecordOfFriend->getDeclContext(), cast<DeclContext>(ToTu));
  ASSERT_EQ(ToRecordOfFriend->getLexicalDeclContext(), cast<DeclContext>(ToX));
  EXPECT_FALSE(
      ToRecordOfFriend->getDeclContext()->containsDecl(ToRecordOfFriend));
  EXPECT_FALSE(ToRecordOfFriend->getLexicalDeclContext()->containsDecl(
      ToRecordOfFriend));
  EXPECT_FALSE(ToRecordOfFriend->getLookupParent()
                   ->lookup(ToRecordOfFriend->getDeclName())
                   .empty());
}

TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) {
  Decl *FromTu = getTuDecl(
      R"(
      template<class A> class declToImport {
        template<class A1> friend class declToImport;
      };
      )",
      Lang_CXX, "input.cc");

  auto *FromD =
      FirstDeclMatcher<ClassTemplateDecl>().match(FromTu, classTemplateDecl());
  auto *ToD = Import(FromD, Lang_CXX);

  auto Pattern = classTemplateDecl(
      has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
  ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
  EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));

  auto *Class =
      FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
  auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
  EXPECT_NE(Friend->getFriendDecl(), Class);
  EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
}

TEST_P(ImportFriendClasses, ProperPrevDeclForClassTemplateDecls) {
  auto Pattern = classTemplateSpecializationDecl(hasName("X"));

  ClassTemplateSpecializationDecl *Imported1;
  {
    Decl *FromTU = getTuDecl("template<class T> class X;"
                             "struct Y { friend class X<int>; };",
                             Lang_CXX, "input0.cc");
    auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
        FromTU, Pattern);

    Imported1 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
  }
  ClassTemplateSpecializationDecl *Imported2;
  {
    Decl *FromTU = getTuDecl("template<class T> class X;"
                             "template<> class X<int>{};"
                             "struct Z { friend class X<int>; };",
                             Lang_CXX, "input1.cc");
    auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
        FromTU, Pattern);

    Imported2 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
  }

  Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(ToTU, Pattern),
            2u);
  ASSERT_TRUE(Imported2->getPreviousDecl());
  EXPECT_EQ(Imported2->getPreviousDecl(), Imported1);
}

TEST_P(ImportFriendClasses, TypeForDeclShouldBeSetInTemplated) {
  Decl *FromTU0 = getTuDecl(
      R"(
      class X {
        class Y;
      };
      class X::Y {
        template <typename T>
        friend class F; // The decl context of F is the global namespace.
      };
      )",
      Lang_CXX, "input0.cc");
  auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
      FromTU0, classTemplateDecl(hasName("F")));
  auto *Imported0 = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
  Decl *FromTU1 = getTuDecl(
      R"(
      template <typename T>
      class F {};
      )",
      Lang_CXX, "input1.cc");
  auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
      FromTU1, classTemplateDecl(hasName("F")));
  auto *Imported1 = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
  EXPECT_EQ(Imported0->getTemplatedDecl()->getTypeForDecl(),
            Imported1->getTemplatedDecl()->getTypeForDecl());
}

TEST_P(ImportFriendClasses, DeclsFromFriendsShouldBeInRedeclChains) {
  Decl *From, *To;
  std::tie(From, To) =
      getImportedDecl("class declToImport {};", Lang_CXX,
                      "class Y { friend class declToImport; };", Lang_CXX);
  auto *Imported = cast<CXXRecordDecl>(To);

  EXPECT_TRUE(Imported->getPreviousDecl());
}

TEST_P(ImportFriendClasses,
       ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) {
  Decl *ToTU = getToTuDecl(
      R"(
      class X {
        class Y;
      };
      class X::Y {
        template <typename T>
        friend class F; // The decl context of F is the global namespace.
      };
      )",
      Lang_CXX);
  auto *ToDecl = FirstDeclMatcher<ClassTemplateDecl>().match(
      ToTU, classTemplateDecl(hasName("F")));
  Decl *FromTU = getTuDecl(
      R"(
      template <typename T>
      class F {};
      )",
      Lang_CXX, "input0.cc");
  auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
      FromTU, classTemplateDecl(hasName("F")));
  auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
  EXPECT_TRUE(ImportedDef->getPreviousDecl());
  EXPECT_EQ(ToDecl, ImportedDef->getPreviousDecl());
  EXPECT_EQ(ToDecl->getTemplatedDecl(),
            ImportedDef->getTemplatedDecl()->getPreviousDecl());
}

TEST_P(ImportFriendClasses,
       ImportOfClassTemplateDefinitionAndFwdFriendShouldBeLinked) {
  Decl *FromTU0 = getTuDecl(
      R"(
      class X {
        class Y;
      };
      class X::Y {
        template <typename T>
        friend class F; // The decl context of F is the global namespace.
      };
      )",
      Lang_CXX, "input0.cc");
  auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
      FromTU0, classTemplateDecl(hasName("F")));
  auto *ImportedFwd = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
  Decl *FromTU1 = getTuDecl(
      R"(
      template <typename T>
      class F {};
      )",
      Lang_CXX, "input1.cc");
  auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
      FromTU1, classTemplateDecl(hasName("F")));
  auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
  EXPECT_TRUE(ImportedDef->getPreviousDecl());
  EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
  EXPECT_EQ(ImportedFwd->getTemplatedDecl(),
            ImportedDef->getTemplatedDecl()->getPreviousDecl());
}

TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) {
  Decl *FromTU0 = getTuDecl(
      R"(
      class X {
        class Y;
      };
      class X::Y {
        friend class F; // The decl context of F is the global namespace.
      };
      )",
      Lang_CXX, "input0.cc");
  auto *Friend = FirstDeclMatcher<FriendDecl>().match(FromTU0, friendDecl());
  QualType FT = Friend->getFriendType()->getType();
  FT = FromTU0->getASTContext().getCanonicalType(FT);
  auto *Fwd = cast<TagType>(FT)->getDecl();
  auto *ImportedFwd = Import(Fwd, Lang_CXX);
  Decl *FromTU1 = getTuDecl(
      R"(
      class F {};
      )",
      Lang_CXX, "input1.cc");
  auto *Definition = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU1, cxxRecordDecl(hasName("F")));
  auto *ImportedDef = Import(Definition, Lang_CXX);
  EXPECT_TRUE(ImportedDef->getPreviousDecl());
  EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
}

TEST_P(ASTImporterOptionSpecificTestBase, FriendFunInClassTemplate) {
  auto *Code = R"(
  template <class T>
  struct X {
    friend void foo(){}
  };
      )";
  TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
  auto *ToFoo = FirstDeclMatcher<FunctionDecl>().match(
      ToTU, functionDecl(hasName("foo")));

  TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX, "input.cc");
  auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("foo")));
  auto *ImportedFoo = Import(FromFoo, Lang_CXX);
  EXPECT_EQ(ImportedFoo, ToFoo);
}

struct DeclContextTest : ASTImporterOptionSpecificTestBase {};

TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
  Decl *TU = getTuDecl(
      R"(
      namespace NS {

      template <typename T>
      struct S {};
      template struct S<int>;

      inline namespace INS {
        template <typename T>
        struct S {};
        template struct S<int>;
      }

      }
      )", Lang_CXX11, "input0.cc");
  auto *NS = FirstDeclMatcher<NamespaceDecl>().match(
      TU, namespaceDecl());
  auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      TU, classTemplateSpecializationDecl());
  ASSERT_TRUE(NS->containsDecl(Spec));

  NS->removeDecl(Spec);
  EXPECT_FALSE(NS->containsDecl(Spec));
}

TEST_P(DeclContextTest,
       removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) {
  Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX);
  auto *A0 = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
  auto *A1 = LastDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));

  // Investigate the list.
  auto *DC = A0->getDeclContext();
  ASSERT_TRUE(DC->containsDecl(A0));
  ASSERT_TRUE(DC->containsDecl(A1));

  // Investigate the lookup table.
  auto *Map = DC->getLookupPtr();
  ASSERT_TRUE(Map);
  auto I = Map->find(A0->getDeclName());
  ASSERT_NE(I, Map->end());
  StoredDeclsList &L = I->second;
  // The lookup table contains the most recent decl of A.
  ASSERT_NE(L.getAsDecl(), A0);
  ASSERT_EQ(L.getAsDecl(), A1);

  ASSERT_TRUE(L.getAsDecl());
  // Simulate the private function DeclContext::reconcileExternalVisibleStorage.
  // The point here is to have a Vec with only one element, which is not the
  // one we are going to delete from the DC later.
  L.setHasExternalDecls();
  ASSERT_TRUE(L.getAsVector());
  ASSERT_EQ(1u, L.getAsVector()->size());

  // This asserts in the old implementation.
  DC->removeDecl(A0);
  EXPECT_FALSE(DC->containsDecl(A0));
}

struct ImportFunctionTemplateSpecializations
    : ASTImporterOptionSpecificTestBase {};

TEST_P(ImportFunctionTemplateSpecializations,
       TUshouldNotContainFunctionTemplateImplicitInstantiation) {

  Decl *FromTU = getTuDecl(
      R"(
      template<class T>
      int f() { return 0; }
      void foo() { f<int>(); }
      )",
      Lang_CXX, "input0.cc");

  // Check that the function template instantiation is NOT the child of the TU.
  auto Pattern = translationUnitDecl(
      unless(has(functionDecl(hasName("f"), isTemplateInstantiation()))));
  ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromTU, Pattern));

  auto *Foo = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("foo")));
  ASSERT_TRUE(Import(Foo, Lang_CXX));

  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern));
}

TEST_P(ImportFunctionTemplateSpecializations,
       TUshouldNotContainFunctionTemplateExplicitInstantiation) {

  Decl *FromTU = getTuDecl(
      R"(
      template<class T>
      int f() { return 0; }
      template int f<int>();
      )",
      Lang_CXX, "input0.cc");

  // Check that the function template instantiation is NOT the child of the TU.
  auto Instantiation = functionDecl(hasName("f"), isTemplateInstantiation());
  auto Pattern = translationUnitDecl(unless(has(Instantiation)));
  ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromTU, Pattern));

  ASSERT_TRUE(
      Import(FirstDeclMatcher<Decl>().match(FromTU, Instantiation), Lang_CXX));

  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern));
}

TEST_P(ImportFunctionTemplateSpecializations,
       TUshouldContainFunctionTemplateSpecialization) {

  Decl *FromTU = getTuDecl(
      R"(
      template<class T>
      int f() { return 0; }
      template <> int f<int>() { return 4; }
      )",
      Lang_CXX, "input0.cc");

  // Check that the function template specialization is the child of the TU.
  auto Specialization =
      functionDecl(hasName("f"), isExplicitTemplateSpecialization());
  auto Pattern = translationUnitDecl(has(Specialization));
  ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromTU, Pattern));

  ASSERT_TRUE(
      Import(FirstDeclMatcher<Decl>().match(FromTU, Specialization), Lang_CXX));

  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToTU, Pattern));
}

TEST_P(ImportFunctionTemplateSpecializations,
       FunctionTemplateSpecializationRedeclChain) {

  Decl *FromTU = getTuDecl(
      R"(
      template<class T>
      int f() { return 0; }
      template <> int f<int>() { return 4; }
      )",
      Lang_CXX, "input0.cc");

  auto Spec = functionDecl(hasName("f"), isExplicitTemplateSpecialization(),
                           hasParent(translationUnitDecl()));
  auto *FromSpecD = FirstDeclMatcher<Decl>().match(FromTU, Spec);
  {
    auto *TU = FromTU;
    auto *SpecD = FromSpecD;
    auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
        TU, functionTemplateDecl());
    auto *FirstSpecD = *(TemplateD->spec_begin());
    ASSERT_EQ(SpecD, FirstSpecD);
    ASSERT_TRUE(SpecD->getPreviousDecl());
    ASSERT_FALSE(cast<FunctionDecl>(SpecD->getPreviousDecl())
                     ->doesThisDeclarationHaveABody());
  }

  ASSERT_TRUE(Import(FromSpecD, Lang_CXX));

  {
    auto *TU = ToAST->getASTContext().getTranslationUnitDecl();
    auto *SpecD = FirstDeclMatcher<Decl>().match(TU, Spec);
    auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
        TU, functionTemplateDecl());
    auto *FirstSpecD = *(TemplateD->spec_begin());
    EXPECT_EQ(SpecD, FirstSpecD);
    ASSERT_TRUE(SpecD->getPreviousDecl());
    EXPECT_FALSE(cast<FunctionDecl>(SpecD->getPreviousDecl())
                     ->doesThisDeclarationHaveABody());
  }
}

TEST_P(ImportFunctionTemplateSpecializations,
       MatchNumberOfFunctionTemplateSpecializations) {

  Decl *FromTU = getTuDecl(
      R"(
      template <typename T> constexpr int f() { return 0; }
      template <> constexpr int f<int>() { return 4; }
      void foo() {
        static_assert(f<char>() == 0, "");
        static_assert(f<int>() == 4, "");
      }
      )",
      Lang_CXX11, "input0.cc");
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("foo")));

  Import(FromD, Lang_CXX11);
  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  EXPECT_EQ(
      DeclCounter<FunctionDecl>().match(FromTU, functionDecl(hasName("f"))),
      DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))));
}

TEST_P(ASTImporterOptionSpecificTestBase,
    ImportShouldNotReportFalseODRErrorWhenRecordIsBeingDefined) {
  {
    Decl *FromTU = getTuDecl(
        R"(
            template <typename T>
            struct B;
            )",
        Lang_CXX, "input0.cc");
    auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
        FromTU, classTemplateDecl(hasName("B")));

    Import(FromD, Lang_CXX);
  }

  {
    Decl *FromTU = getTuDecl(
        R"(
            template <typename T>
            struct B {
              void f();
              B* b;
            };
            )",
        Lang_CXX, "input1.cc");
    FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match(
        FromTU, functionDecl(hasName("f")));
    Import(FromD, Lang_CXX);
    auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match(
        FromTU, classTemplateDecl(hasName("B")));
    auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX));
    EXPECT_TRUE(ToCTD->isThisDeclarationADefinition());

    // We expect no (ODR) warning during the import.
    auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
    EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
  }
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportingTypedefShouldImportTheCompleteType) {
  // We already have an incomplete underlying type in the "To" context.
  auto Code =
      R"(
      template <typename T>
      struct S {
        void foo();
      };
      using U = S<int>;
      )";
  Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
  auto *ToD = FirstDeclMatcher<TypedefNameDecl>().match(ToTU,
      typedefNameDecl(hasName("U")));
  ASSERT_TRUE(ToD->getUnderlyingType()->isIncompleteType());

  // The "From" context has the same typedef, but the underlying type is
  // complete this time.
  Decl *FromTU = getTuDecl(std::string(Code) +
      R"(
      void foo(U* u) {
        u->foo();
      }
      )", Lang_CXX11);
  auto *FromD = FirstDeclMatcher<TypedefNameDecl>().match(FromTU,
      typedefNameDecl(hasName("U")));
  ASSERT_FALSE(FromD->getUnderlyingType()->isIncompleteType());

  // The imported type should be complete.
  auto *ImportedD = cast<TypedefNameDecl>(Import(FromD, Lang_CXX11));
  EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType());
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportTemplateParameterLists) {
  auto Code =
      R"(
      template<class T>
      int f() { return 0; }
      template <> int f<int>() { return 4; }
      )";

  Decl *FromTU = getTuDecl(Code, Lang_CXX);
  auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU,
      functionDecl(hasName("f"), isExplicitTemplateSpecialization()));
  ASSERT_EQ(FromD->getNumTemplateParameterLists(), 1u);

  auto *ToD = Import(FromD, Lang_CXX);
  // The template parameter list should exist.
  EXPECT_EQ(ToD->getNumTemplateParameterLists(), 1u);
}

struct ASTImporterLookupTableTest : ASTImporterOptionSpecificTestBase {};

TEST_P(ASTImporterLookupTableTest, OneDecl) {
  auto *ToTU = getToTuDecl("int a;", Lang_CXX);
  auto *D = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("a")));
  ASTImporterLookupTable LT(*ToTU);
  auto Res = LT.lookup(ToTU, D->getDeclName());
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), D);
}

static Decl *findInDeclListOfDC(DeclContext *DC, DeclarationName Name) {
  for (Decl *D : DC->decls()) {
    if (auto *ND = dyn_cast<NamedDecl>(D))
      if (ND->getDeclName() == Name)
        return ND;
  }
  return nullptr;
}

TEST_P(ASTImporterLookupTableTest,
    FriendWhichIsnotFoundByNormalLookupShouldBeFoundByImporterSpecificLookup) {
  auto *Code = R"(
  template <class T>
  struct X {
    friend void foo(){}
  };
      )";
  TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
  auto *X = FirstDeclMatcher<ClassTemplateDecl>().match(
      ToTU, classTemplateDecl(hasName("X")));
  auto *Foo = FirstDeclMatcher<FunctionDecl>().match(
      ToTU, functionDecl(hasName("foo")));
  DeclContext *FooDC = Foo->getDeclContext();
  DeclContext *FooLexicalDC = Foo->getLexicalDeclContext();
  ASSERT_EQ(cast<Decl>(FooLexicalDC), X->getTemplatedDecl());
  ASSERT_EQ(cast<Decl>(FooDC), ToTU);
  DeclarationName FooName = Foo->getDeclName();

  // Cannot find in the LookupTable of its DC (TUDecl)
  SmallVector<NamedDecl *, 2> FoundDecls;
  FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
  EXPECT_EQ(FoundDecls.size(), 0u);

  // Cannot find in the LookupTable of its LexicalDC (X)
  FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
  EXPECT_EQ(FoundDecls.size(), 0u);

  // Can't find in the list of Decls of the DC.
  EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr);

  // Can't find in the list of Decls of the LexicalDC
  EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), nullptr);

  // ASTImporter specific lookup finds it.
  ASTImporterLookupTable LT(*ToTU);
  auto Res = LT.lookup(FooDC, Foo->getDeclName());
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), Foo);
}

TEST_P(ASTImporterLookupTableTest,
       FwdDeclStructShouldBeFoundByImporterSpecificLookup) {
  TranslationUnitDecl *ToTU =
      getToTuDecl("struct A { struct Foo *p; };", Lang_C);
  auto *Foo =
      FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("Foo")));
  auto *A =
      FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
  DeclContext *FooDC = Foo->getDeclContext();
  DeclContext *FooLexicalDC = Foo->getLexicalDeclContext();
  ASSERT_EQ(cast<Decl>(FooLexicalDC), A);
  ASSERT_EQ(cast<Decl>(FooDC), ToTU);
  DeclarationName FooName = Foo->getDeclName();

  // Cannot find in the LookupTable of its DC (TUDecl).
  SmallVector<NamedDecl *, 2> FoundDecls;
  FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
  EXPECT_EQ(FoundDecls.size(), 0u);

  // Cannot find in the LookupTable of its LexicalDC (A).
  FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
  EXPECT_EQ(FoundDecls.size(), 0u);

  // Can't find in the list of Decls of the DC.
  EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr);

  // Can find in the list of Decls of the LexicalDC.
  EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), Foo);

  // ASTImporter specific lookup finds it.
  ASTImporterLookupTable LT(*ToTU);
  auto Res = LT.lookup(FooDC, Foo->getDeclName());
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), Foo);
}

TEST_P(ASTImporterLookupTableTest, LookupFindsNamesInDifferentDC) {
  TranslationUnitDecl *ToTU =
      getToTuDecl("int V; struct A { int V; }; struct B { int V; };", Lang_C);
  DeclarationName VName = FirstDeclMatcher<VarDecl>()
                              .match(ToTU, varDecl(hasName("V")))
                              ->getDeclName();
  auto *A =
      FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
  auto *B =
      FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("B")));

  ASTImporterLookupTable LT(*ToTU);

  auto Res = LT.lookup(cast<DeclContext>(A), VName);
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match(
                        ToTU, fieldDecl(hasName("V"),
                                        hasParent(recordDecl(hasName("A"))))));
  Res = LT.lookup(cast<DeclContext>(B), VName);
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match(
                        ToTU, fieldDecl(hasName("V"),
                                        hasParent(recordDecl(hasName("B"))))));
  Res = LT.lookup(ToTU, VName);
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), FirstDeclMatcher<VarDecl>().match(
                        ToTU, varDecl(hasName("V"),
                                        hasParent(translationUnitDecl()))));
}

TEST_P(ASTImporterLookupTableTest, LookupFindsOverloadedNames) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      void foo();
      void foo(int);
      void foo(int, int);
      )",
      Lang_CXX);

  ASTImporterLookupTable LT(*ToTU);
  auto *F0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
  auto *F2 = LastDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
  DeclarationName Name = F0->getDeclName();
  auto Res = LT.lookup(ToTU, Name);
  EXPECT_EQ(Res.size(), 3u);
  EXPECT_EQ(Res.count(F0), 1u);
  EXPECT_EQ(Res.count(F2), 1u);
}

TEST_P(ASTImporterLookupTableTest,
       DifferentOperatorsShouldHaveDifferentResultSet) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      struct X{};
      void operator+(X, X);
      void operator-(X, X);
      )",
      Lang_CXX);

  ASTImporterLookupTable LT(*ToTU);
  auto *FPlus = FirstDeclMatcher<FunctionDecl>().match(
      ToTU, functionDecl(hasOverloadedOperatorName("+")));
  auto *FMinus = FirstDeclMatcher<FunctionDecl>().match(
      ToTU, functionDecl(hasOverloadedOperatorName("-")));
  DeclarationName NamePlus = FPlus->getDeclName();
  auto ResPlus = LT.lookup(ToTU, NamePlus);
  EXPECT_EQ(ResPlus.size(), 1u);
  EXPECT_EQ(ResPlus.count(FPlus), 1u);
  EXPECT_EQ(ResPlus.count(FMinus), 0u);
  DeclarationName NameMinus = FMinus->getDeclName();
  auto ResMinus = LT.lookup(ToTU, NameMinus);
  EXPECT_EQ(ResMinus.size(), 1u);
  EXPECT_EQ(ResMinus.count(FMinus), 1u);
  EXPECT_EQ(ResMinus.count(FPlus), 0u);
  EXPECT_NE(*ResMinus.begin(), *ResPlus.begin());
}

TEST_P(ASTImporterLookupTableTest, LookupDeclNamesFromDifferentTUs) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      struct X {};
      void operator+(X, X);
      )",
      Lang_CXX);
  auto *ToPlus = FirstDeclMatcher<FunctionDecl>().match(
      ToTU, functionDecl(hasOverloadedOperatorName("+")));

  Decl *FromTU = getTuDecl(
      R"(
      struct X {};
      void operator+(X, X);
      )",
      Lang_CXX);
  auto *FromPlus = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasOverloadedOperatorName("+")));

  // FromPlus have a different TU, thus its DeclarationName is different too.
  ASSERT_NE(ToPlus->getDeclName(), FromPlus->getDeclName());

  ASTImporterLookupTable LT(*ToTU);
  auto Res = LT.lookup(ToTU, ToPlus->getDeclName());
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), ToPlus);

  // FromPlus have a different TU, thus its DeclarationName is different too.
  Res = LT.lookup(ToTU, FromPlus->getDeclName());
  ASSERT_EQ(Res.size(), 0u);
}

TEST_P(ASTImporterLookupTableTest,
       LookupFindsFwdFriendClassDeclWithElaboratedType) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      class Y { friend class F; };
      )",
      Lang_CXX);

  // In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent.
  // So we must dig up the underlying CXXRecordDecl.
  ASTImporterLookupTable LT(*ToTU);
  auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
  const RecordDecl *RD = getRecordDeclOfFriend(FriendD);
  auto *Y = FirstDeclMatcher<CXXRecordDecl>().match(
      ToTU, cxxRecordDecl(hasName("Y")));

  DeclarationName Name = RD->getDeclName();
  auto Res = LT.lookup(ToTU, Name);
  EXPECT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), RD);

  Res = LT.lookup(Y, Name);
  EXPECT_EQ(Res.size(), 0u);
}

TEST_P(ASTImporterLookupTableTest,
       LookupFindsFwdFriendClassDeclWithUnelaboratedType) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      class F;
      class Y { friend F; };
      )",
      Lang_CXX11);

  // In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent.
  // So we must dig up the underlying CXXRecordDecl.
  ASTImporterLookupTable LT(*ToTU);
  auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
  const RecordDecl *RD = getRecordDeclOfFriend(FriendD);
  auto *Y = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, cxxRecordDecl(hasName("Y")));

  DeclarationName Name = RD->getDeclName();
  auto Res = LT.lookup(ToTU, Name);
  EXPECT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), RD);

  Res = LT.lookup(Y, Name);
  EXPECT_EQ(Res.size(), 0u);
}

TEST_P(ASTImporterLookupTableTest,
       LookupFindsFriendClassDeclWithTypeAliasDoesNotAssert) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      class F;
      using alias_of_f = F;
      class Y { friend alias_of_f; };
      )",
      Lang_CXX11);

  // ASTImporterLookupTable constructor handles using declarations correctly,
  // no assert is expected.
  ASTImporterLookupTable LT(*ToTU);

  auto *Alias = FirstDeclMatcher<TypeAliasDecl>().match(
      ToTU, typeAliasDecl(hasName("alias_of_f")));
  DeclarationName Name = Alias->getDeclName();
  auto Res = LT.lookup(ToTU, Name);
  EXPECT_EQ(Res.count(Alias), 1u);
}

TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassTemplateDecl) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      class Y { template <class T> friend class F; };
      )",
      Lang_CXX);

  ASTImporterLookupTable LT(*ToTU);
  auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
      ToTU, classTemplateDecl(hasName("F")));
  DeclarationName Name = F->getDeclName();
  auto Res = LT.lookup(ToTU, Name);
  EXPECT_EQ(Res.size(), 2u);
  EXPECT_EQ(Res.count(F), 1u);
  EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
}

TEST_P(ASTImporterLookupTableTest, DependentFriendClass) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      template <typename T>
      class F;

      template <typename T>
      class Y {
        friend class F<T>;
      };
      )",
      Lang_CXX);

  ASTImporterLookupTable LT(*ToTU);
  auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
      ToTU, classTemplateDecl(hasName("F")));
  DeclarationName Name = F->getDeclName();
  auto Res = LT.lookup(ToTU, Name);
  EXPECT_EQ(Res.size(), 2u);
  EXPECT_EQ(Res.count(F), 1u);
  EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
}

TEST_P(ASTImporterLookupTableTest, FriendClassTemplateSpecialization) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      template <typename T>
      class F;

      class Y {
        friend class F<int>;
      };
      )",
      Lang_CXX);

  ASTImporterLookupTable LT(*ToTU);
  auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
      ToTU, classTemplateDecl(hasName("F")));
  DeclarationName Name = F->getDeclName();
  auto Res = LT.lookup(ToTU, Name);
  ASSERT_EQ(Res.size(), 3u);
  EXPECT_EQ(Res.count(F), 1u);
  EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
  EXPECT_EQ(Res.count(*F->spec_begin()), 1u);
}

TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionDecl) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      class Y { friend void F(); };
      )",
      Lang_CXX);

  ASTImporterLookupTable LT(*ToTU);
  auto *F =
      FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("F")));
  DeclarationName Name = F->getDeclName();
  auto Res = LT.lookup(ToTU, Name);
  EXPECT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), F);
}

TEST_P(ASTImporterLookupTableTest,
       LookupFindsDeclsInClassTemplateSpecialization) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      template <typename T>
      struct X {
        int F;
      };
      void foo() {
        X<char> xc;
      }
      )",
      Lang_CXX);

  ASTImporterLookupTable LT(*ToTU);

  auto *Template = FirstDeclMatcher<ClassTemplateDecl>().match(
      ToTU, classTemplateDecl(hasName("X")));
  auto *FieldInTemplate = FirstDeclMatcher<FieldDecl>().match(
      ToTU,
      fieldDecl(hasParent(cxxRecordDecl(hasParent(classTemplateDecl())))));

  auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      ToTU, classTemplateSpecializationDecl(hasName("X")));
  FieldDecl *FieldInSpec = *Spec->field_begin();
  ASSERT_TRUE(FieldInSpec);

  DeclarationName Name = FieldInSpec->getDeclName();
  auto TemplateDC = cast<DeclContext>(Template->getTemplatedDecl());

  SmallVector<NamedDecl *, 2> FoundDecls;
  TemplateDC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
  EXPECT_EQ(FoundDecls.size(), 1u);
  EXPECT_EQ(FoundDecls[0], FieldInTemplate);

  auto Res = LT.lookup(TemplateDC, Name);
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), FieldInTemplate);

  cast<DeclContext>(Spec)->getRedeclContext()->localUncachedLookup(Name,
                                                                   FoundDecls);
  EXPECT_EQ(FoundDecls.size(), 1u);
  EXPECT_EQ(FoundDecls[0], FieldInSpec);

  Res = LT.lookup(cast<DeclContext>(Spec), Name);
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), FieldInSpec);
}

TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionTemplateDecl) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      class Y { template <class T> friend void F(); };
      )",
      Lang_CXX);

  ASTImporterLookupTable LT(*ToTU);
  auto *F = FirstDeclMatcher<FunctionTemplateDecl>().match(
      ToTU, functionTemplateDecl(hasName("F")));
  DeclarationName Name = F->getDeclName();
  auto Res = LT.lookup(ToTU, Name);
  EXPECT_EQ(Res.size(), 2u);
  EXPECT_EQ(Res.count(F), 1u);
  EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
}

TEST_P(ASTImporterLookupTableTest, MultipleBefriendingClasses) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      struct X;
      struct A {
        friend struct X;
      };
      struct B {
        friend struct X;
      };
      )",
      Lang_CXX);

  ASTImporterLookupTable LT(*ToTU);
  auto *X = FirstDeclMatcher<CXXRecordDecl>().match(
      ToTU, cxxRecordDecl(hasName("X")));
  auto *FriendD0 = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
  auto *FriendD1 = LastDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
  const RecordDecl *RD0 = getRecordDeclOfFriend(FriendD0);
  const RecordDecl *RD1 = getRecordDeclOfFriend(FriendD1);
  ASSERT_EQ(RD0, RD1);
  ASSERT_EQ(RD1, X);

  DeclarationName Name = X->getDeclName();
  auto Res = LT.lookup(ToTU, Name);
  EXPECT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), X);
}

TEST_P(ASTImporterLookupTableTest, EnumConstantDecl) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      enum E {
        A,
        B
      };
      )",
      Lang_C);

  ASTImporterLookupTable LT(*ToTU);
  auto *E = FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl(hasName("E")));
  auto *A = FirstDeclMatcher<EnumConstantDecl>().match(
      ToTU, enumConstantDecl(hasName("A")));

  DeclarationName Name = A->getDeclName();
  // Redecl context is the TU.
  ASSERT_EQ(E->getRedeclContext(), ToTU);

  SmallVector<NamedDecl *, 2> FoundDecls;
  // Normal lookup finds in the DC.
  E->localUncachedLookup(Name, FoundDecls);
  EXPECT_EQ(FoundDecls.size(), 1u);

  // Normal lookup finds in the Redecl context.
  ToTU->localUncachedLookup(Name, FoundDecls);
  EXPECT_EQ(FoundDecls.size(), 1u);

  // Import specific lookup finds in the DC.
  auto Res = LT.lookup(E, Name);
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), A);

  // Import specific lookup finds in the Redecl context.
  Res = LT.lookup(ToTU, Name);
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), A);
}

TEST_P(ASTImporterLookupTableTest, LookupSearchesInTheWholeRedeclChain) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      namespace N {
        int A;
      }
      namespace N {
      }
      )",
      Lang_CXX);
  auto *N1 =
      LastDeclMatcher<NamespaceDecl>().match(ToTU, namespaceDecl(hasName("N")));
  auto *A = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("A")));
  DeclarationName Name = A->getDeclName();

  ASTImporterLookupTable LT(*ToTU);
  auto Res = LT.lookup(N1, Name);
  ASSERT_EQ(Res.size(), 1u);
  EXPECT_EQ(*Res.begin(), A);
}

TEST_P(ASTImporterOptionSpecificTestBase,
       RedeclChainShouldBeCorrectAmongstNamespaces) {
  Decl *FromTU = getTuDecl(
      R"(
      namespace NS {
        struct X;
        struct Y {
          static const int I = 3;
        };
      }
      namespace NS {
        struct X {  // <--- To be imported
          void method(int i = Y::I) {}
          int f;
        };
      }
      )",
      Lang_CXX);
  auto *FromFwd = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("X"), unless(isImplicit())));
  auto *FromDef = LastDeclMatcher<CXXRecordDecl>().match(
      FromTU,
      cxxRecordDecl(hasName("X"), isDefinition(), unless(isImplicit())));
  ASSERT_NE(FromFwd, FromDef);
  ASSERT_FALSE(FromFwd->isThisDeclarationADefinition());
  ASSERT_TRUE(FromDef->isThisDeclarationADefinition());
  ASSERT_EQ(FromFwd->getCanonicalDecl(), FromDef->getCanonicalDecl());

  auto *ToDef = cast_or_null<CXXRecordDecl>(Import(FromDef, Lang_CXX));
  auto *ToFwd = cast_or_null<CXXRecordDecl>(Import(FromFwd, Lang_CXX));
  EXPECT_NE(ToFwd, ToDef);
  EXPECT_FALSE(ToFwd->isThisDeclarationADefinition());
  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
  EXPECT_EQ(ToFwd->getCanonicalDecl(), ToDef->getCanonicalDecl());
  auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  // We expect no (ODR) warning during the import.
  EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
}

struct ImportFriendFunctionTemplates : ASTImporterOptionSpecificTestBase {};

TEST_P(ImportFriendFunctionTemplates, LookupShouldFindPreviousFriend) {
  Decl *ToTU = getToTuDecl(
      R"(
      class X {
        template <typename T> friend void foo();
      };
      )",
      Lang_CXX);
  auto *Friend = FirstDeclMatcher<FunctionTemplateDecl>().match(
      ToTU, functionTemplateDecl(hasName("foo")));

  Decl *FromTU = getTuDecl(
      R"(
      template <typename T> void foo();
      )",
      Lang_CXX);
  auto *FromFoo = FirstDeclMatcher<FunctionTemplateDecl>().match(
      FromTU, functionTemplateDecl(hasName("foo")));
  auto *Imported = Import(FromFoo, Lang_CXX);

  EXPECT_EQ(Imported->getPreviousDecl(), Friend);
}

struct ASTImporterWithFakeErrors : ASTImporter {
  using ASTImporter::ASTImporter;
  bool returnWithErrorInTest() override { return true; }
};

struct ErrorHandlingTest : ASTImporterOptionSpecificTestBase {
  ErrorHandlingTest() {
    Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
                 ASTContext &FromContext, FileManager &FromFileManager,
                 bool MinimalImport,
                 const std::shared_ptr<ASTImporterSharedState> &SharedState) {
      return new ASTImporterWithFakeErrors(ToContext, ToFileManager,
                                           FromContext, FromFileManager,
                                           MinimalImport, SharedState);
    };
  }
  // In this test we purposely report an error (UnsupportedConstruct) when
  // importing the below stmt.
  static constexpr auto* ErroneousStmt = R"( asm(""); )";
};

// Check a case when no new AST node is created in the AST before encountering
// the error.
TEST_P(ErrorHandlingTest, ErrorHappensBeforeCreatingANewNode) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      template <typename T>
      class X {};
      template <>
      class X<int> { int a; };
      )",
      Lang_CXX);
  TranslationUnitDecl *FromTU = getTuDecl(
      R"(
      template <typename T>
      class X {};
      template <>
      class X<int> { double b; };
      )",
      Lang_CXX);
  auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
      FromTU, classTemplateSpecializationDecl(hasName("X")));
  ClassTemplateSpecializationDecl *ImportedSpec = Import(FromSpec, Lang_CXX);
  EXPECT_FALSE(ImportedSpec);

  // The original Decl is kept, no new decl is created.
  EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(
                ToTU, classTemplateSpecializationDecl(hasName("X"))),
            1u);

  // But an error is set to the counterpart in the "from" context.
  ASTImporter *Importer = findFromTU(FromSpec)->Importer.get();
  Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromSpec);
  ASSERT_TRUE(OptErr);
  EXPECT_EQ(OptErr->Error, ImportError::NameConflict);
}

// Check a case when a new AST node is created but not linked to the AST before
// encountering the error.
TEST_P(ErrorHandlingTest,
       ErrorHappensAfterCreatingTheNodeButBeforeLinkingThatToTheAST) {
  TranslationUnitDecl *FromTU = getTuDecl(
      std::string("void foo() { ") + ErroneousStmt + " }",
      Lang_CXX);
  auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("foo")));

  FunctionDecl *ImportedFoo = Import(FromFoo, Lang_CXX);
  EXPECT_FALSE(ImportedFoo);

  TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  // Created, but not linked.
  EXPECT_EQ(
      DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("foo"))),
      0u);

  ASTImporter *Importer = findFromTU(FromFoo)->Importer.get();
  Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromFoo);
  ASSERT_TRUE(OptErr);
  EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
}

// Check a case when a new AST node is created and linked to the AST before
// encountering the error. The error is set for the counterpart of the nodes in
// the "from" context.
TEST_P(ErrorHandlingTest, ErrorHappensAfterNodeIsCreatedAndLinked) {
  TranslationUnitDecl *FromTU = getTuDecl(
      std::string(R"(
      void f();
      void f() { )") + ErroneousStmt + R"( }
      )",
    Lang_CXX);
  auto *FromProto = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));
  auto *FromDef =
      LastDeclMatcher<FunctionDecl>().match(FromTU, functionDecl(hasName("f")));
  FunctionDecl *ImportedProto = Import(FromProto, Lang_CXX);
  EXPECT_FALSE(ImportedProto); // Could not import.
  // However, we created two nodes in the AST. 1) the fwd decl 2) the
  // definition. The definition is not added to its DC, but the fwd decl is
  // there.
  TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))),
            1u);
  // Match the fwd decl.
  auto *ToProto =
      FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("f")));
  EXPECT_TRUE(ToProto);
  // An error is set to the counterpart in the "from" context both for the fwd
  // decl and the definition.
  ASTImporter *Importer = findFromTU(FromProto)->Importer.get();
  Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromProto);
  ASSERT_TRUE(OptErr);
  EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
  OptErr = Importer->getImportDeclErrorIfAny(FromDef);
  ASSERT_TRUE(OptErr);
  EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
}

// An error should be set for a class if we cannot import one member.
TEST_P(ErrorHandlingTest, ErrorIsPropagatedFromMemberToClass) {
  TranslationUnitDecl *FromTU = getTuDecl(
      std::string(R"(
      class X {
        void f() { )") + ErroneousStmt + R"( } // This member has the error
                                               // during import.
        void ok();        // The error should not prevent importing this.
      };                  // An error will be set for X too.
      )",
      Lang_CXX);
  auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("X")));
  CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX);

  // An error is set for X.
  EXPECT_FALSE(ImportedX);
  ASTImporter *Importer = findFromTU(FromX)->Importer.get();
  Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
  ASSERT_TRUE(OptErr);
  EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);

  // An error is set for f().
  auto *FromF = FirstDeclMatcher<CXXMethodDecl>().match(
      FromTU, cxxMethodDecl(hasName("f")));
  OptErr = Importer->getImportDeclErrorIfAny(FromF);
  ASSERT_TRUE(OptErr);
  EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
  // And any subsequent import should fail.
  CXXMethodDecl *ImportedF = Import(FromF, Lang_CXX);
  EXPECT_FALSE(ImportedF);

  // There is an error set for the other member too.
  auto *FromOK = FirstDeclMatcher<CXXMethodDecl>().match(
      FromTU, cxxMethodDecl(hasName("ok")));
  OptErr = Importer->getImportDeclErrorIfAny(FromOK);
  EXPECT_TRUE(OptErr);
  // Cannot import the other member.
  CXXMethodDecl *ImportedOK = Import(FromOK, Lang_CXX);
  EXPECT_FALSE(ImportedOK);
}

// Check that an error propagates to the dependent AST nodes.
// In the below code it means that an error in X should propagate to A.
// And even to F since the containing A is erroneous.
// And to all AST nodes which we visit during the import process which finally
// ends up in a failure (in the error() function).
TEST_P(ErrorHandlingTest, ErrorPropagatesThroughImportCycles) {
  Decl *FromTU = getTuDecl(
      std::string(R"(
      namespace NS {
        class A {
          template <int I> class F {};
          class X {
            template <int I> friend class F;
            void error() { )") + ErroneousStmt + R"( }
          };
        };

        class B {};
      } // NS
      )",
      Lang_CXX, "input0.cc");

  auto *FromFRD = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
  auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("A"), isDefinition()));
  auto *FromB = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("B"), isDefinition()));
  auto *FromNS = FirstDeclMatcher<NamespaceDecl>().match(
      FromTU, namespaceDecl(hasName("NS")));

  // Start by importing the templated CXXRecordDecl of F.
  // Import fails for that.
  EXPECT_FALSE(Import(FromFRD, Lang_CXX));
  // Import fails for A.
  EXPECT_FALSE(Import(FromA, Lang_CXX));
  // But we should be able to import the independent B.
  EXPECT_TRUE(Import(FromB, Lang_CXX));
  // And the namespace.
  EXPECT_TRUE(Import(FromNS, Lang_CXX));

  // An error is set to the templated CXXRecordDecl of F.
  ASTImporter *Importer = findFromTU(FromFRD)->Importer.get();
  Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromFRD);
  EXPECT_TRUE(OptErr);

  // An error is set to A.
  OptErr = Importer->getImportDeclErrorIfAny(FromA);
  EXPECT_TRUE(OptErr);

  // There is no error set to B.
  OptErr = Importer->getImportDeclErrorIfAny(FromB);
  EXPECT_FALSE(OptErr);

  // There is no error set to NS.
  OptErr = Importer->getImportDeclErrorIfAny(FromNS);
  EXPECT_FALSE(OptErr);

  // Check some of those decls whose ancestor is X, they all should have an
  // error set if we visited them during an import process which finally failed.
  // These decls are part of a cycle in an ImportPath.
  // There would not be any error set for these decls if we hadn't follow the
  // ImportPaths and the cycles.
  OptErr = Importer->getImportDeclErrorIfAny(
      FirstDeclMatcher<ClassTemplateDecl>().match(
          FromTU, classTemplateDecl(hasName("F"))));
  // An error is set to the 'F' ClassTemplateDecl.
  EXPECT_TRUE(OptErr);
  // An error is set to the FriendDecl.
  OptErr = Importer->getImportDeclErrorIfAny(
      FirstDeclMatcher<FriendDecl>().match(
          FromTU, friendDecl()));
  EXPECT_TRUE(OptErr);
  // An error is set to the implicit class of A.
  OptErr =
      Importer->getImportDeclErrorIfAny(FirstDeclMatcher<CXXRecordDecl>().match(
          FromTU, cxxRecordDecl(hasName("A"), isImplicit())));
  EXPECT_TRUE(OptErr);
  // An error is set to the implicit class of X.
  OptErr =
      Importer->getImportDeclErrorIfAny(FirstDeclMatcher<CXXRecordDecl>().match(
          FromTU, cxxRecordDecl(hasName("X"), isImplicit())));
  EXPECT_TRUE(OptErr);
}

TEST_P(ErrorHandlingTest, ErrorIsNotPropagatedFromMemberToNamespace) {
  TranslationUnitDecl *FromTU = getTuDecl(
      std::string(R"(
      namespace X {
        void f() { )") + ErroneousStmt + R"( } // This member has the error
                                               // during import.
        void ok();        // The error should not prevent importing this.
      };                  // An error will be set for X too.
      )",
      Lang_CXX);
  auto *FromX = FirstDeclMatcher<NamespaceDecl>().match(
      FromTU, namespaceDecl(hasName("X")));
  NamespaceDecl *ImportedX = Import(FromX, Lang_CXX);

  // There is no error set for X.
  EXPECT_TRUE(ImportedX);
  ASTImporter *Importer = findFromTU(FromX)->Importer.get();
  Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
  ASSERT_FALSE(OptErr);

  // An error is set for f().
  auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));
  OptErr = Importer->getImportDeclErrorIfAny(FromF);
  ASSERT_TRUE(OptErr);
  EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
  // And any subsequent import should fail.
  FunctionDecl *ImportedF = Import(FromF, Lang_CXX);
  EXPECT_FALSE(ImportedF);

  // There is no error set for ok().
  auto *FromOK = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("ok")));
  OptErr = Importer->getImportDeclErrorIfAny(FromOK);
  EXPECT_FALSE(OptErr);
  // And we should be able to import.
  FunctionDecl *ImportedOK = Import(FromOK, Lang_CXX);
  EXPECT_TRUE(ImportedOK);
}

// An error should be set for a class if it had a previous import with an error
// from another TU.
TEST_P(ErrorHandlingTest,
       ImportedDeclWithErrorShouldFailTheImportOfDeclWhichMapToIt) {
  // We already have a fwd decl.
  TranslationUnitDecl *ToTU = getToTuDecl(
      "class X;", Lang_CXX);
  // Then we import a definition.
  {
    TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
        class X {
          void f() { )") + ErroneousStmt + R"( }
          void ok();
        };
        )",
        Lang_CXX);
    auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
        FromTU, cxxRecordDecl(hasName("X")));
    CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX);

    // An error is set for X ...
    EXPECT_FALSE(ImportedX);
    ASTImporter *Importer = findFromTU(FromX)->Importer.get();
    Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
    ASSERT_TRUE(OptErr);
    EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
  }
  // ... but the node had been created.
  auto *ToXDef = FirstDeclMatcher<CXXRecordDecl>().match(
      ToTU, cxxRecordDecl(hasName("X"), isDefinition()));
  // An error is set for "ToXDef" in the shared state.
  Optional<ImportError> OptErr =
      SharedStatePtr->getImportDeclErrorIfAny(ToXDef);
  ASSERT_TRUE(OptErr);
  EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);

  auto *ToXFwd = FirstDeclMatcher<CXXRecordDecl>().match(
      ToTU, cxxRecordDecl(hasName("X"), unless(isDefinition())));
  // An error is NOT set for the fwd Decl of X in the shared state.
  OptErr = SharedStatePtr->getImportDeclErrorIfAny(ToXFwd);
  ASSERT_FALSE(OptErr);

  // Try to import  X again but from another TU.
  {
    TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
        class X {
          void f() { )") + ErroneousStmt + R"( }
          void ok();
        };
        )",
        Lang_CXX, "input1.cc");

    auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
        FromTU, cxxRecordDecl(hasName("X")));
    CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX);

    // If we did not save the errors for the "to" context then the below checks
    // would fail, because the lookup finds the fwd Decl of the existing
    // definition in the "to" context. We can reach the existing definition via
    // the found fwd Decl. That existing definition is structurally equivalent
    // (we check only the fields) with this one we want to import, so we return
    // with the existing definition, which is erroneous (one method is missing).

    // The import should fail.
    EXPECT_FALSE(ImportedX);
    ASTImporter *Importer = findFromTU(FromX)->Importer.get();
    Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
    // And an error is set for this new X in the "from" ctx.
    ASSERT_TRUE(OptErr);
    EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
  }
}

TEST_P(ErrorHandlingTest, ImportOfOverriddenMethods) {
  auto MatchFooA =
      functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("A"))));
  auto MatchFooB =
      functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("B"))));
  auto MatchFooC =
      functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("C"))));

  // Provoke import of a method that has overridden methods with import error.
  TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
        struct C;
        struct A {
          virtual void foo();
          void f1(C *);
        };
        void A::foo() {
          )") + ErroneousStmt + R"(
        }
        struct B : public A {
          void foo() override;
        };
        struct C : public B {
          void foo() override;
        };
        )",
                                          Lang_CXX11);
  auto *FromFooA = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooA);
  auto *FromFooB = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooB);
  auto *FromFooC = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooC);

  EXPECT_FALSE(Import(FromFooA, Lang_CXX11));
  ASTImporter *Importer = findFromTU(FromFooA)->Importer.get();
  auto CheckError = [&Importer](Decl *FromD) {
    Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromD);
    ASSERT_TRUE(OptErr);
    EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
  };
  CheckError(FromFooA);
  EXPECT_FALSE(Import(FromFooB, Lang_CXX11));
  CheckError(FromFooB);
  EXPECT_FALSE(Import(FromFooC, Lang_CXX11));
  CheckError(FromFooC);
}

TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionBody) {
  Decl *FromTU = getTuDecl(
      R"(
      void f() {
        auto L = [](){};
      }
      )",
      Lang_CXX11, "input0.cc");
  auto Pattern = lambdaExpr();
  CXXRecordDecl *FromL =
      FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();

  auto ToL = Import(FromL, Lang_CXX11);
  unsigned ToLSize = std::distance(ToL->decls().begin(), ToL->decls().end());
  unsigned FromLSize =
      std::distance(FromL->decls().begin(), FromL->decls().end());
  EXPECT_NE(ToLSize, 0u);
  EXPECT_EQ(ToLSize, FromLSize);
}

TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionParam) {
  Decl *FromTU = getTuDecl(
      R"(
      template <typename F>
      void f(F L = [](){}) {}
      )",
      Lang_CXX11, "input0.cc");
  auto Pattern = lambdaExpr();
  CXXRecordDecl *FromL =
      FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();

  auto ToL = Import(FromL, Lang_CXX11);
  unsigned ToLSize = std::distance(ToL->decls().begin(), ToL->decls().end());
  unsigned FromLSize =
      std::distance(FromL->decls().begin(), FromL->decls().end());
  EXPECT_NE(ToLSize, 0u);
  EXPECT_EQ(ToLSize, FromLSize);
}

TEST_P(ASTImporterOptionSpecificTestBase, LambdaInGlobalScope) {
  Decl *FromTU = getTuDecl(
      R"(
      auto l1 = [](unsigned lp) { return 1; };
      auto l2 = [](int lp) { return 2; };
      int f(int p) {
        return l1(p) + l2(p);
      }
      )",
      Lang_CXX11, "input0.cc");
  FunctionDecl *FromF = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));
  FunctionDecl *ToF = Import(FromF, Lang_CXX11);
  EXPECT_TRUE(ToF);
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportExistingFriendClassTemplateDef) {
  auto Code =
      R"(
        template <class T1, class T2>
        struct Base {
          template <class U1, class U2>
          friend struct Class;
        };
        template <class T1, class T2>
        struct Class { };
        )";

  TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
  TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX, "input.cc");

  auto *ToClassProto = FirstDeclMatcher<ClassTemplateDecl>().match(
      ToTU, classTemplateDecl(hasName("Class")));
  auto *ToClassDef = LastDeclMatcher<ClassTemplateDecl>().match(
      ToTU, classTemplateDecl(hasName("Class")));
  ASSERT_FALSE(ToClassProto->isThisDeclarationADefinition());
  ASSERT_TRUE(ToClassDef->isThisDeclarationADefinition());
  // Previous friend decl is not linked to it!
  ASSERT_FALSE(ToClassDef->getPreviousDecl());
  ASSERT_EQ(ToClassDef->getMostRecentDecl(), ToClassDef);
  ASSERT_EQ(ToClassProto->getMostRecentDecl(), ToClassProto);

  auto *FromClassProto = FirstDeclMatcher<ClassTemplateDecl>().match(
      FromTU, classTemplateDecl(hasName("Class")));
  auto *FromClassDef = LastDeclMatcher<ClassTemplateDecl>().match(
      FromTU, classTemplateDecl(hasName("Class")));
  ASSERT_FALSE(FromClassProto->isThisDeclarationADefinition());
  ASSERT_TRUE(FromClassDef->isThisDeclarationADefinition());
  ASSERT_FALSE(FromClassDef->getPreviousDecl());
  ASSERT_EQ(FromClassDef->getMostRecentDecl(), FromClassDef);
  ASSERT_EQ(FromClassProto->getMostRecentDecl(), FromClassProto);

  auto *ImportedDef = Import(FromClassDef, Lang_CXX);
  // At import we should find the definition for 'Class' even if the
  // prototype (inside 'friend') for it comes first in the AST and is not
  // linked to the definition.
  EXPECT_EQ(ImportedDef, ToClassDef);
}

struct LLDBLookupTest : ASTImporterOptionSpecificTestBase {
  LLDBLookupTest() {
    Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
                 ASTContext &FromContext, FileManager &FromFileManager,
                 bool MinimalImport,
                 const std::shared_ptr<ASTImporterSharedState> &SharedState) {
      return new ASTImporter(ToContext, ToFileManager, FromContext,
                             FromFileManager, MinimalImport,
                             // We use the regular lookup.
                             /*SharedState=*/nullptr);
    };
  }
};

TEST_P(LLDBLookupTest, ImporterShouldFindInTransparentContext) {
  TranslationUnitDecl *ToTU = getToTuDecl(
      R"(
      extern "C" {
        class X{};
      };
      )",
      Lang_CXX);
  auto *ToX = FirstDeclMatcher<CXXRecordDecl>().match(
      ToTU, cxxRecordDecl(hasName("X")));

  // Set up a stub external storage.
  ToTU->setHasExternalLexicalStorage(true);
  // Set up DeclContextBits.HasLazyExternalLexicalLookups to true.
  ToTU->setMustBuildLookupTable();
  struct TestExternalASTSource : ExternalASTSource {};
  ToTU->getASTContext().setExternalSource(new TestExternalASTSource());

  Decl *FromTU = getTuDecl(
      R"(
        class X;
      )",
      Lang_CXX);
  auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("X")));
  auto *ImportedX = Import(FromX, Lang_CXX);
  // The lookup must find the existing class definition in the LinkageSpecDecl.
  // Then the importer renders the existing and the new decl into one chain.
  EXPECT_EQ(ImportedX->getCanonicalDecl(), ToX->getCanonicalDecl());
}

struct SVEBuiltins : ASTImporterOptionSpecificTestBase {};

TEST_P(SVEBuiltins, ImportTypes) {
  static const char *const TypeNames[] = {
    "__SVInt8_t",
    "__SVInt16_t",
    "__SVInt32_t",
    "__SVInt64_t",
    "__SVUint8_t",
    "__SVUint16_t",
    "__SVUint32_t",
    "__SVUint64_t",
    "__SVFloat16_t",
    "__SVFloat32_t",
    "__SVFloat64_t",
    "__SVBool_t"
  };

  TranslationUnitDecl *ToTU = getToTuDecl("", Lang_CXX);
  TranslationUnitDecl *FromTU = getTuDecl("", Lang_CXX, "input.cc");
  for (auto *TypeName : TypeNames) {
    auto *ToTypedef = FirstDeclMatcher<TypedefDecl>().match(
      ToTU, typedefDecl(hasName(TypeName)));
    QualType ToType = ToTypedef->getUnderlyingType();

    auto *FromTypedef = FirstDeclMatcher<TypedefDecl>().match(
      FromTU, typedefDecl(hasName(TypeName)));
    QualType FromType = FromTypedef->getUnderlyingType();

    QualType ImportedType = ImportType(FromType, FromTypedef, Lang_CXX);
    EXPECT_EQ(ImportedType, ToType);
  }
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportOfDefaultImplicitFunctions) {
  // Test that import of implicit functions works and the functions
  // are merged into one chain.
  auto GetDeclToImport = [this](StringRef File) {
    Decl *FromTU = getTuDecl(
        R"(
        struct X { };
        // Force generating some implicit operator definitions for X.
        void f() { X x1, x2; x1 = x2; X *x3 = new X; delete x3; }
        )",
        Lang_CXX11, File);
    auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
        FromTU, cxxRecordDecl(hasName("X"), unless(isImplicit())));
    // Destructor is picked as one example of implicit function.
    return FromD->getDestructor();
  };

  auto *ToD1 = Import(GetDeclToImport("input1.cc"), Lang_CXX11);
  ASSERT_TRUE(ToD1);

  auto *ToD2 = Import(GetDeclToImport("input2.cc"), Lang_CXX11);
  ASSERT_TRUE(ToD2);

  EXPECT_EQ(ToD1->getCanonicalDecl(), ToD2->getCanonicalDecl());
}

TEST_P(ASTImporterOptionSpecificTestBase,
       ImportOfExplicitlyDefaultedOrDeleted) {
  Decl *FromTU = getTuDecl(
      R"(
        struct X { X() = default; X(const X&) = delete; };
      )",
      Lang_CXX11);
  auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
      FromTU, cxxRecordDecl(hasName("X")));
  auto *ImportedX = Import(FromX, Lang_CXX11);
  auto *Constr1 = FirstDeclMatcher<CXXConstructorDecl>().match(
      ImportedX, cxxConstructorDecl(hasName("X"), unless(isImplicit())));
  auto *Constr2 = LastDeclMatcher<CXXConstructorDecl>().match(
      ImportedX, cxxConstructorDecl(hasName("X"), unless(isImplicit())));

  ASSERT_TRUE(ImportedX);
  EXPECT_TRUE(Constr1->isDefaulted());
  EXPECT_TRUE(Constr1->isExplicitlyDefaulted());
  EXPECT_TRUE(Constr2->isDeletedAsWritten());
  EXPECT_EQ(ImportedX->isAggregate(), FromX->isAggregate());
}

INSTANTIATE_TEST_CASE_P(ParameterizedTests, SVEBuiltins,
                        ::testing::Values(ArgVector{"-target",
                                                    "aarch64-linux-gnu"}), );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest,
                        ::testing::Values(ArgVector()), );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, CanonicalRedeclChain,
                        ::testing::Values(ArgVector()), );

TEST_P(ASTImporterOptionSpecificTestBase, LambdasAreDifferentiated) {
  Decl *FromTU = getTuDecl(
      R"(
      void f() {
        auto L0 = [](){};
        auto L1 = [](){};
      }
      )",
      Lang_CXX11, "input0.cc");
  auto Pattern = lambdaExpr();
  CXXRecordDecl *FromL0 =
      FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();
  CXXRecordDecl *FromL1 =
      LastDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();
  ASSERT_NE(FromL0, FromL1);

  CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11);
  CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11);
  EXPECT_NE(ToL0, ToL1);
}

TEST_P(ASTImporterOptionSpecificTestBase,
       LambdasInFunctionParamsAreDifferentiated) {
  Decl *FromTU = getTuDecl(
      R"(
      template <typename F0, typename F1>
      void f(F0 L0 = [](){}, F1 L1 = [](){}) {}
      )",
      Lang_CXX11, "input0.cc");
  auto Pattern = cxxRecordDecl(isLambda());
  CXXRecordDecl *FromL0 =
      FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
  CXXRecordDecl *FromL1 =
      LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
  ASSERT_NE(FromL0, FromL1);

  CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11);
  CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11);
  ASSERT_NE(ToL0, ToL1);
}

TEST_P(ASTImporterOptionSpecificTestBase,
       LambdasInFunctionParamsAreDifferentiatedWhenMacroIsUsed) {
  Decl *FromTU = getTuDecl(
      R"(
      #define LAMBDA [](){}
      template <typename F0, typename F1>
      void f(F0 L0 = LAMBDA, F1 L1 = LAMBDA) {}
      )",
      Lang_CXX11, "input0.cc");
  auto Pattern = cxxRecordDecl(isLambda());
  CXXRecordDecl *FromL0 =
      FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
  CXXRecordDecl *FromL1 =
      LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
  ASSERT_NE(FromL0, FromL1);

  Import(FromL0, Lang_CXX11);
  Import(FromL1, Lang_CXX11);
  CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11);
  CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11);
  ASSERT_NE(ToL0, ToL1);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportAssignedLambda) {
  Decl *FromTU = getTuDecl(
      R"(
      void f() {
        auto x = []{} = {}; auto x2 = x;
      }
      )",
      Lang_CXX2a, "input0.cc");
  auto FromF = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));
  // We have only one lambda class.
  ASSERT_EQ(
      DeclCounter<CXXRecordDecl>().match(FromTU, cxxRecordDecl(isLambda())),
      1u);

  FunctionDecl *ToF = Import(FromF, Lang_CXX2a);
  EXPECT_TRUE(ToF);
  TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  // We have only one lambda class after the import.
  EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, cxxRecordDecl(isLambda())),
            1u);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImportDefaultConstructibleLambdas) {
  Decl *FromTU = getTuDecl(
      R"(
      void f() {
        auto x = []{} = {};
        auto xb = []{} = {};
      }
      )",
      Lang_CXX2a, "input0.cc");
  auto FromF = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));
  // We have two lambda classes.
  ASSERT_EQ(
      DeclCounter<CXXRecordDecl>().match(FromTU, cxxRecordDecl(isLambda())),
      2u);

  FunctionDecl *ToF = Import(FromF, Lang_CXX2a);
  EXPECT_TRUE(ToF);
  TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
  // We have two lambda classes after the import.
  EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, cxxRecordDecl(isLambda())),
            2u);
}

TEST_P(ASTImporterOptionSpecificTestBase, ImplicitlyDeclareSelf) {
  Decl *FromTU = getTuDecl(R"(
                           __attribute__((objc_root_class))
                           @interface Root
                           @end
                           @interface C : Root
                             -(void)method;
                           @end
                           @implementation C
                             -(void)method {}
                           @end
                           )",
                           Lang_OBJCXX, "input.mm");
  auto *FromMethod = LastDeclMatcher<ObjCMethodDecl>().match(
      FromTU, namedDecl(hasName("method")));
  ASSERT_TRUE(FromMethod);
  auto ToMethod = Import(FromMethod, Lang_OBJCXX);
  ASSERT_TRUE(ToMethod);

  // Both methods should have their implicit parameters.
  EXPECT_TRUE(FromMethod->getSelfDecl() != nullptr);
  EXPECT_TRUE(ToMethod->getSelfDecl() != nullptr);
}

struct ImportAutoFunctions : ASTImporterOptionSpecificTestBase {};

TEST_P(ImportAutoFunctions, ReturnWithTypedefDeclaredInside) {
  Decl *FromTU = getTuDecl(
      R"(
      auto X = [](long l) {
        using int_type = long;
        auto dur = 13;
        return static_cast<int_type>(dur);
      };
      )",
      Lang_CXX14, "input0.cc");
  CXXMethodDecl *From =
      FirstDeclMatcher<CXXMethodDecl>().match(FromTU, cxxMethodDecl());

  // Explicitly set the return type of the lambda's operator() to the TypeAlias.
  // Normally the return type would be the built-in 'long' type. However, there
  // are cases when Clang does not use the canonical type and the TypeAlias is
  // used. I could not create such an AST from regular source code, it requires
  // some special state in the preprocessor. I've found such an AST when Clang
  // parsed libcxx/src/filesystem/directory_iterator.cpp, but could not reduce
  // that with creduce, because after preprocessing, the AST no longer
  // contained the TypeAlias as a return type of the lambda.
  ASTContext &Ctx = From->getASTContext();
  TypeAliasDecl *FromTA =
      FirstDeclMatcher<TypeAliasDecl>().match(FromTU, typeAliasDecl());
  QualType TT = Ctx.getTypedefType(FromTA);
  const FunctionProtoType *FPT = cast<FunctionProtoType>(From->getType());
  QualType NewFunType =
      Ctx.getFunctionType(TT, FPT->getParamTypes(), FPT->getExtProtoInfo());
  From->setType(NewFunType);

  CXXMethodDecl *To = Import(From, Lang_CXX14);
  EXPECT_TRUE(To);
  EXPECT_TRUE(isa<TypedefType>(To->getReturnType()));
}

TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside) {
  Decl *FromTU = getTuDecl(
      R"(
      auto foo() {
        struct X {};
        return X();
      }
      )",
      Lang_CXX14, "input0.cc");
  FunctionDecl *From =
      FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());

  FunctionDecl *To = Import(From, Lang_CXX14);
  EXPECT_TRUE(To);
  EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}

TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside2) {
  Decl *FromTU = getTuDecl(
      R"(
      auto foo() {
        struct X {};
        return X();
      }
      )",
      Lang_CXX14, "input0.cc");
  FunctionDecl *From =
      FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());

  // This time import the type directly.
  QualType ToT = ImportType(From->getType(), From, Lang_CXX14);
  const FunctionProtoType *FPT = cast<FunctionProtoType>(ToT);
  EXPECT_TRUE(isa<AutoType>(FPT->getReturnType()));
}

TEST_P(ImportAutoFunctions, ReturnWithTypedefToStructDeclaredInside) {
  Decl *FromTU = getTuDecl(
      R"(
      auto foo() {
        struct X {};
        using Y = X;
        return Y();
      }
      )",
      Lang_CXX14, "input0.cc");
  FunctionDecl *From =
      FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());

  FunctionDecl *To = Import(From, Lang_CXX14);
  EXPECT_TRUE(To);
  EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}

TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredNestedInside) {
  Decl *FromTU = getTuDecl(
      R"(
      auto foo() {
        struct X { struct Y{}; };
        return X::Y();
      }
      )",
      Lang_CXX14, "input0.cc");
  FunctionDecl *From =
      FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());

  FunctionDecl *To = Import(From, Lang_CXX14);
  EXPECT_TRUE(To);
  EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}

TEST_P(ImportAutoFunctions, ReturnWithInternalLambdaType) {
  Decl *FromTU = getTuDecl(
      R"(
      auto f() {
        auto l = []() {
          struct X {};
          return X();
        };
        return l();
      }
      )",
      Lang_CXX17, "input0.cc");
  FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));

  FunctionDecl *To = Import(From, Lang_CXX17);
  EXPECT_TRUE(To);
  EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}

TEST_P(ImportAutoFunctions, ReturnWithTypeInIf) {
  Decl *FromTU = getTuDecl(
      R"(
      auto f() {
        if (struct X {} x; true)
          return X();
        else
          return X();
      }
      )",
      Lang_CXX17, "input0.cc");
  FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));

  FunctionDecl *To = Import(From, Lang_CXX17);
  EXPECT_TRUE(To);
  EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}

TEST_P(ImportAutoFunctions, ReturnWithTypeInFor) {
  Decl *FromTU = getTuDecl(
      R"(
      auto f() {
        for (struct X {} x;;)
          return X();
      }
      )",
      Lang_CXX17, "input0.cc");
  FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));

  FunctionDecl *To = Import(From, Lang_CXX17);
  EXPECT_TRUE(To);
  EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}

TEST_P(ImportAutoFunctions, ReturnWithTypeInSwitch) {
  Decl *FromTU = getTuDecl(
      R"(
      auto f() {
        switch (struct X {} x; 10) {
        case 10:
          return X();
        }
      }
      )",
      Lang_CXX17, "input0.cc");
  FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
      FromTU, functionDecl(hasName("f")));

  FunctionDecl *To = Import(From, Lang_CXX17);
  EXPECT_TRUE(To);
  EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportPath,
                        ::testing::Values(ArgVector()), );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportExpr,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportType,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportDecl,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterOptionSpecificTestBase,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ErrorHandlingTest,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, RedirectingImporterTest,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportAutoFunctions,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctionTemplates,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctionTemplates,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendClasses,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests,
                        ImportFunctionTemplateSpecializations,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportImplicitMethods,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportVariables,
                        DefaultTestValuesForRunOptions, );

INSTANTIATE_TEST_CASE_P(ParameterizedTests, LLDBLookupTest,
                        DefaultTestValuesForRunOptions, );

} // end namespace ast_matchers
} // end namespace clang