freebsd-nq/unittests/AST/ASTImporterTest.cpp

4917 lines
171 KiB
C++

//===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Tests for the correct import of AST nodes from one AST context to another.
//
//===----------------------------------------------------------------------===//
#include "clang/AST/ASTImporter.h"
#include "MatchVerifier.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclContextInternals.h"
#include "clang/AST/ASTImporter.h"
#include "clang/AST/ASTImporterLookupTable.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
#include "DeclMatcher.h"
#include "Language.h"
#include "gmock/gmock.h"
#include "llvm/ADT/StringMap.h"
namespace clang {
namespace ast_matchers {
using internal::Matcher;
using internal::BindableMatcher;
using llvm::StringMap;
// Creates a virtual file and assigns that to the context of given AST. If the
// file already exists then the file will not be created again as a duplicate.
static void
createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
std::unique_ptr<llvm::MemoryBuffer> &&Buffer) {
assert(ToAST);
ASTContext &ToCtx = ToAST->getASTContext();
auto *OFS = static_cast<llvm::vfs::OverlayFileSystem *>(
ToCtx.getSourceManager().getFileManager().getVirtualFileSystem().get());
auto *MFS = static_cast<llvm::vfs::InMemoryFileSystem *>(
OFS->overlays_begin()->get());
MFS->addFile(FileName, 0, std::move(Buffer));
}
static void createVirtualFileIfNeeded(ASTUnit *ToAST, StringRef FileName,
StringRef Code) {
return createVirtualFileIfNeeded(ToAST, FileName,
llvm::MemoryBuffer::getMemBuffer(Code));
}
const StringRef DeclToImportID = "declToImport";
const StringRef DeclToVerifyID = "declToVerify";
// Common base for the different families of ASTImporter tests that are
// parameterized on the compiler options which may result a different AST. E.g.
// -fms-compatibility or -fdelayed-template-parsing.
struct ParameterizedTestsFixture : ::testing::TestWithParam<ArgVector> {
// Returns the argument vector used for a specific language option, this set
// can be tweaked by the test parameters.
ArgVector getArgVectorForLanguage(Language Lang) const {
ArgVector Args = getBasicRunOptionsForLanguage(Lang);
ArgVector ExtraArgs = GetParam();
for (const auto &Arg : ExtraArgs) {
Args.push_back(Arg);
}
return Args;
}
};
// Base class for those tests which use the family of `testImport` functions.
class TestImportBase : public ParameterizedTestsFixture {
template <typename NodeType>
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);
// 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)
return testing::AssertionFailure() << "Import failed, nullptr returned!";
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);
}
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(Imported);
}
// 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();
}
// This class provides generic methods to write tests which can check internal
// attributes of AST nodes like getPreviousDecl(), isVirtual(), etc. Also,
// this fixture makes it possible to import from several "From" contexts.
class ASTImporterTestBase : public ParameterizedTestsFixture {
const char *const InputFileName = "input.cc";
const char *const OutputFileName = "output.cc";
// Buffer for the To context, must live in the test scope.
std::string ToCode;
// Represents a "From" translation unit and holds an importer object which we
// use to import from this translation unit.
struct TU {
// Buffer for the context, must live in the test scope.
std::string Code;
std::string FileName;
std::unique_ptr<ASTUnit> Unit;
TranslationUnitDecl *TUDecl = nullptr;
std::unique_ptr<ASTImporter> Importer;
TU(StringRef Code, StringRef FileName, ArgVector Args)
: Code(Code), FileName(FileName),
Unit(tooling::buildASTFromCodeWithArgs(this->Code, Args,
this->FileName)),
TUDecl(Unit->getASTContext().getTranslationUnitDecl()) {
Unit->enableSourceFileDiagnostics();
}
void lazyInitImporter(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST) {
assert(ToAST);
if (!Importer) {
Importer.reset(
new ASTImporter(ToAST->getASTContext(), ToAST->getFileManager(),
Unit->getASTContext(), Unit->getFileManager(),
false, &LookupTable));
}
assert(&ToAST->getASTContext() == &Importer->getToContext());
createVirtualFileIfNeeded(ToAST, FileName, Code);
}
Decl *import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST,
Decl *FromDecl) {
lazyInitImporter(LookupTable, ToAST);
return Importer->Import(FromDecl);
}
QualType import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST,
QualType FromType) {
lazyInitImporter(LookupTable, ToAST);
return Importer->Import(FromType);
}
};
// We may have several From contexts and related translation units. In each
// AST, the buffers for the source are handled via references and are set
// during the creation of the AST. These references must point to a valid
// buffer until the AST is alive. Thus, we must use a list in order to avoid
// moving of the stored objects because that would mean breaking the
// references in the AST. By using a vector a move could happen when the
// vector is expanding, with the list we won't have these issues.
std::list<TU> FromTUs;
// Initialize the lookup table if not initialized already.
void lazyInitLookupTable(TranslationUnitDecl *ToTU) {
assert(ToTU);
if (!LookupTablePtr)
LookupTablePtr = llvm::make_unique<ASTImporterLookupTable>(*ToTU);
}
void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName) {
if (ToAST)
return;
ArgVector ToArgs = getArgVectorForLanguage(ToLang);
// Source code must be a valid live buffer through the tests lifetime.
ToCode = ToSrcCode;
// Build the AST from an empty file.
ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName);
ToAST->enableSourceFileDiagnostics();
lazyInitLookupTable(ToAST->getASTContext().getTranslationUnitDecl());
}
TU *findFromTU(Decl *From) {
// Create a virtual file in the To Ctx which corresponds to the file from
// which we want to import the `From` Decl. Without this source locations
// will be invalid in the ToCtx.
auto It = std::find_if(FromTUs.begin(), FromTUs.end(), [From](const TU &E) {
return E.TUDecl == From->getTranslationUnitDecl();
});
assert(It != FromTUs.end());
return &*It;
}
protected:
std::unique_ptr<ASTImporterLookupTable> LookupTablePtr;
public:
// We may have several From context but only one To context.
std::unique_ptr<ASTUnit> ToAST;
// Creates an AST both for the From and To source code and imports the Decl
// of the identifier into the To context.
// Must not be called more than once within the same test.
std::tuple<Decl *, Decl *>
getImportedDecl(StringRef FromSrcCode, Language FromLang, StringRef ToSrcCode,
Language ToLang, StringRef Identifier = DeclToImportID) {
ArgVector FromArgs = getArgVectorForLanguage(FromLang),
ToArgs = getArgVectorForLanguage(ToLang);
FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs);
TU &FromTU = FromTUs.back();
assert(!ToAST);
lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
ASTContext &FromCtx = FromTU.Unit->getASTContext();
IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier);
assert(ImportedII && "Declaration with the given identifier "
"should be specified in test!");
DeclarationName ImportDeclName(ImportedII);
SmallVector<NamedDecl *, 1> FoundDecls;
FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName,
FoundDecls);
assert(FoundDecls.size() == 1);
Decl *Imported =
FromTU.import(*LookupTablePtr, ToAST.get(), FoundDecls.front());
assert(Imported);
return std::make_tuple(*FoundDecls.begin(), Imported);
}
// Creates a TU decl for the given source code which can be used as a From
// context. May be called several times in a given test (with different file
// name).
TranslationUnitDecl *getTuDecl(StringRef SrcCode, Language Lang,
StringRef FileName = "input.cc") {
assert(
std::find_if(FromTUs.begin(), FromTUs.end(), [FileName](const TU &E) {
return E.FileName == FileName;
}) == FromTUs.end());
ArgVector Args = getArgVectorForLanguage(Lang);
FromTUs.emplace_back(SrcCode, FileName, Args);
TU &Tu = FromTUs.back();
return Tu.TUDecl;
}
// Creates the To context with the given source code and returns the TU decl.
TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang) {
ArgVector ToArgs = getArgVectorForLanguage(ToLang);
assert(!ToAST);
lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
return ToAST->getASTContext().getTranslationUnitDecl();
}
// Import the given Decl into the ToCtx.
// May be called several times in a given test.
// The different instances of the param From may have different ASTContext.
Decl *Import(Decl *From, Language ToLang) {
lazyInitToAST(ToLang, "", OutputFileName);
TU *FromTU = findFromTU(From);
assert(LookupTablePtr);
return FromTU->import(*LookupTablePtr, ToAST.get(), From);
}
QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang) {
lazyInitToAST(ToLang, "", OutputFileName);
TU *FromTU = findFromTU(TUDecl);
assert(LookupTablePtr);
return FromTU->import(*LookupTablePtr, ToAST.get(), FromType);
}
~ASTImporterTestBase() {
if (!::testing::Test::HasFailure()) return;
for (auto &Tu : FromTUs) {
assert(Tu.Unit);
llvm::errs() << "FromAST:\n";
Tu.Unit->getASTContext().getTranslationUnitDecl()->dump();
llvm::errs() << "\n";
}
if (ToAST) {
llvm::errs() << "ToAST:\n";
ToAST->getASTContext().getTranslationUnitDecl()->dump();
}
}
};
struct ImportExpr : TestImportBase {};
struct ImportType : TestImportBase {};
struct ImportDecl : TestImportBase {};
struct CanonicalRedeclChain : ASTImporterTestBase {};
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));
}
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, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase,
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(ASTImporterTestBase,
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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase,
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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(
ASTImporterTestBase,
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 (FieldDecl *Field : Node.fields()) {
if (Index == Order.size())
return false;
if (Field->getName() != Order[Index])
return false;
++Index;
}
return Index == Order.size();
}
TEST_P(ASTImporterTestBase,
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(ASTImporterTestBase, 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(ASTImporterTestBase,
DISABLED_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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(
ASTImporterTestBase,
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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase,
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(ASTImporterTestBase, 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(ASTImporterTestBase,
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(ASTImporterTestBase,
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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(
ASTImporterTestBase,
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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(2u,
DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
}
TEST_P(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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 : ASTImporterTestBase {};
TEST_P(ImportFunctions,
DefinitionShouldBeImportedAsDefintionWhenThereIsAPrototype) {
Decl *FromTU = getTuDecl("void f(); void f() {}", Lang_CXX);
auto Pattern = functionDecl(hasName("f"));
FunctionDecl *FromD = // Definition
LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
}
TEST_P(ImportFunctions, DefinitionShouldBeImportedAsADefinition) {
Decl *FromTU = getTuDecl("void f() {}", Lang_CXX);
auto Pattern = functionDecl(hasName("f"));
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
EXPECT_TRUE(cast<FunctionDecl>(ImportedD)->doesThisDeclarationHaveABody());
}
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, ImportPrototypes) {
auto Pattern = functionDecl(hasName("f"));
Decl *ImportedD;
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = Import(FromD, Lang_CXX);
}
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, 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_FALSE(To1->doesThisDeclarationHaveABody());
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ImportFunctions, ImportDefinitions) {
auto Pattern = functionDecl(hasName("f"));
Decl *ImportedD;
{
Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = Import(FromD, Lang_CXX);
}
{
Decl *FromTU = getTuDecl("void f(){};", Lang_CXX, "input1.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedD == To0);
EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
}
TEST_P(ImportFunctions, ImportDefinitionThenPrototype) {
auto Pattern = functionDecl(hasName("f"));
Decl *ImportedD;
{
Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = Import(FromD, Lang_CXX);
}
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input1.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
Decl *ToTU = ToAST->getASTContext().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_TRUE(To0->doesThisDeclarationHaveABody());
EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ImportFunctions, ImportPrototypeThenDefinition) {
auto Pattern = functionDecl(hasName("f"));
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
{
Decl *FromTU = getTuDecl("void f(){}", Lang_CXX, "input1.cc");
FunctionDecl *FromD =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody());
FunctionDecl *DefinitionD =
LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
EXPECT_EQ(DefinitionD->getPreviousDecl(), ProtoD);
}
TEST_P(ImportFunctions, ImportPrototypeThenProtoAndDefinition) {
auto Pattern = functionDecl(hasName("f"));
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
{
Decl *FromTU = getTuDecl("void f(); void f(){}", Lang_CXX, "input1.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 3u);
FunctionDecl *ProtoD = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_FALSE(ProtoD->doesThisDeclarationHaveABody());
FunctionDecl *DefinitionD =
LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(DefinitionD->doesThisDeclarationHaveABody());
EXPECT_TRUE(DefinitionD->getPreviousDecl());
EXPECT_FALSE(DefinitionD->getPreviousDecl()->doesThisDeclarationHaveABody());
EXPECT_EQ(DefinitionD->getPreviousDecl()->getPreviousDecl(), ProtoD);
}
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()));
}
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);
}
// Disabled temporarily, because the new structural equivalence check
// (https://reviews.llvm.org/D48628) breaks it.
// PreviousDecl is not set because there is no structural match.
// FIXME Enable!
TEST_P(ImportFriendFunctions,
DISABLED_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());
}
// Disabled temporarily, because the new structural equivalence check
// (https://reviews.llvm.org/D48628) breaks it.
// PreviousDecl is not set because there is no structural match.
// FIXME Enable!
TEST_P(ImportFriendFunctions,
DISABLED_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, DISABLED_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);
}
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 ASTImporterTestBase {
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);
LookupTablePtr->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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase,
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_TRUE(ImportedSpec);
auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl(hasName("X")));
EXPECT_EQ(ImportedSpec, ToSpec);
EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl()));
}
TEST_P(ASTImporterTestBase, 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(ASTImporterTestBase,
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(ASTImporterTestBase, 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(ASTImporterTestBase, 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(ASTImporterTestBase, 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 : ASTImporterTestBase {};
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 : ASTImporterTestBase {};
TEST_P(ImportClasses,
PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
Decl *FromTU = getTuDecl("class X;", Lang_CXX);
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
auto ToD = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedD == ToD);
EXPECT_FALSE(ToD->isThisDeclarationADefinition());
}
TEST_P(ImportClasses, ImportPrototypeAfterImportedPrototype) {
Decl *FromTU = getTuDecl("class X; class X;", Lang_CXX);
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
auto From1 = LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
EXPECT_TRUE(Imported1 == To1);
EXPECT_FALSE(To0->isThisDeclarationADefinition());
EXPECT_FALSE(To1->isThisDeclarationADefinition());
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ImportClasses, DefinitionShouldBeImportedAsADefinition) {
Decl *FromTU = getTuDecl("class X {};", Lang_CXX);
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
EXPECT_TRUE(cast<CXXRecordDecl>(ImportedD)->isThisDeclarationADefinition());
}
TEST_P(ImportClasses, ImportPrototypeFromDifferentTUAfterImportedPrototype) {
Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
EXPECT_TRUE(Imported1 == To1);
EXPECT_FALSE(To0->isThisDeclarationADefinition());
EXPECT_FALSE(To1->isThisDeclarationADefinition());
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ImportClasses, ImportDefinitions) {
Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(Imported0, Imported1);
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
EXPECT_TRUE(To0->isThisDeclarationADefinition());
}
TEST_P(ImportClasses, ImportDefinitionThenPrototype) {
Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
Decl *ImportedDef = Import(FromDef, Lang_CXX);
Decl *ImportedProto = Import(FromProto, Lang_CXX);
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
EXPECT_NE(ImportedDef, ImportedProto);
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
auto ToDef = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
auto ToProto = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedDef == ToDef);
EXPECT_TRUE(ImportedProto == ToProto);
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
}
TEST_P(ImportClasses, ImportPrototypeThenDefinition) {
Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
Decl *ImportedProto = Import(FromProto, Lang_CXX);
Decl *ImportedDef = Import(FromDef, Lang_CXX);
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
EXPECT_NE(ImportedDef, ImportedProto);
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
auto ToProto = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
auto ToDef = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedDef == ToDef);
EXPECT_TRUE(ImportedProto == ToProto);
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
}
TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInToContext) {
Decl *ToTU = getToTuDecl("struct X;", 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, 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 ImportClassTemplates : ASTImporterTestBase {};
TEST_P(ImportClassTemplates,
PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
Decl *FromTU = getTuDecl("template <class T> class X;", Lang_CXX);
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedD == ToD);
ASSERT_TRUE(ToD->getTemplatedDecl());
EXPECT_FALSE(ToD->isThisDeclarationADefinition());
}
TEST_P(ImportClassTemplates, ImportPrototypeAfterImportedPrototype) {
Decl *FromTU = getTuDecl(
"template <class T> class X; template <class T> class X;", Lang_CXX);
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
auto From1 = LastDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
EXPECT_TRUE(Imported1 == To1);
ASSERT_TRUE(To0->getTemplatedDecl());
ASSERT_TRUE(To1->getTemplatedDecl());
EXPECT_FALSE(To0->isThisDeclarationADefinition());
EXPECT_FALSE(To1->isThisDeclarationADefinition());
EXPECT_EQ(To1->getPreviousDecl(), To0);
EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
To0->getTemplatedDecl());
}
TEST_P(ImportClassTemplates, DefinitionShouldBeImportedAsADefinition) {
Decl *FromTU = getTuDecl("template <class T> class X {};", Lang_CXX);
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
Decl *ImportedD = Import(FromD, Lang_CXX);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
ASSERT_TRUE(ToD->getTemplatedDecl());
EXPECT_TRUE(ToD->isThisDeclarationADefinition());
}
TEST_P(ImportClassTemplates,
ImportPrototypeFromDifferentTUAfterImportedPrototype) {
Decl *FromTU0 =
getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
Decl *FromTU1 =
getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
EXPECT_TRUE(Imported1 == To1);
ASSERT_TRUE(To0->getTemplatedDecl());
ASSERT_TRUE(To1->getTemplatedDecl());
EXPECT_FALSE(To0->isThisDeclarationADefinition());
EXPECT_FALSE(To1->isThisDeclarationADefinition());
EXPECT_EQ(To1->getPreviousDecl(), To0);
EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
To0->getTemplatedDecl());
}
TEST_P(ImportClassTemplates, ImportDefinitions) {
Decl *FromTU0 =
getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
Decl *FromTU1 =
getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
Decl *Imported0 = Import(From0, Lang_CXX);
Decl *Imported1 = Import(From1, Lang_CXX);
Decl *ToTU = Imported0->getTranslationUnitDecl();
EXPECT_EQ(Imported0, Imported1);
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(Imported0 == To0);
ASSERT_TRUE(To0->getTemplatedDecl());
EXPECT_TRUE(To0->isThisDeclarationADefinition());
}
TEST_P(ImportClassTemplates, ImportDefinitionThenPrototype) {
Decl *FromTU0 =
getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
Decl *FromTU1 =
getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
auto FromProto =
FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
Decl *ImportedDef = Import(FromDef, Lang_CXX);
Decl *ImportedProto = Import(FromProto, Lang_CXX);
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
EXPECT_NE(ImportedDef, ImportedProto);
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
auto ToDef = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
auto ToProto = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedDef == ToDef);
EXPECT_TRUE(ImportedProto == ToProto);
ASSERT_TRUE(ToDef->getTemplatedDecl());
ASSERT_TRUE(ToProto->getTemplatedDecl());
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
EXPECT_EQ(ToProto->getTemplatedDecl()->getPreviousDecl(),
ToDef->getTemplatedDecl());
}
TEST_P(ImportClassTemplates, ImportPrototypeThenDefinition) {
Decl *FromTU0 =
getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
Decl *FromTU1 =
getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
auto FromProto =
FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
Decl *ImportedProto = Import(FromProto, Lang_CXX);
Decl *ImportedDef = Import(FromDef, Lang_CXX);
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
EXPECT_NE(ImportedDef, ImportedProto);
EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
auto ToProto = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
auto ToDef = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedDef == ToDef);
EXPECT_TRUE(ImportedProto == ToProto);
ASSERT_TRUE(ToProto->getTemplatedDecl());
ASSERT_TRUE(ToDef->getTemplatedDecl());
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
EXPECT_EQ(ToDef->getTemplatedDecl()->getPreviousDecl(),
ToProto->getTemplatedDecl());
}
struct ImportFriendClasses : ASTImporterTestBase {};
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, 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(ASTImporterTestBase, 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 : ASTImporterTestBase {};
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 : ASTImporterTestBase {};
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(ImportFunctionTemplateSpecializations,
ImportPrototypes) {
auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
auto Code =
R"(
// Proto of the primary template.
template <class T>
void f();
// Proto of the specialization.
template <>
void f<int>();
)";
Decl *ImportedD;
{
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = Import(FromD, Lang_CXX);
}
{
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input1.cc");
auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, 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_TRUE(ImportedD != To1);
EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
// Check that they are part of the same redecl chain.
EXPECT_EQ(To1->getCanonicalDecl(), To0->getCanonicalDecl());
}
TEST_P(ImportFunctionTemplateSpecializations, ImportDefinitions) {
auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
auto Code =
R"(
// Proto of the primary template.
template <class T>
void f();
// Specialization and definition.
template <>
void f<int>() {}
)";
Decl *ImportedD;
{
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = Import(FromD, Lang_CXX);
}
{
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input1.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
Import(FromD, Lang_CXX);
}
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedD == To0);
EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
ToTU, functionTemplateDecl());
auto *FirstSpecD = *(TemplateD->spec_begin());
EXPECT_EQ(FirstSpecD->getCanonicalDecl(), To0->getCanonicalDecl());
}
TEST_P(ImportFunctionTemplateSpecializations, PrototypeThenPrototype) {
auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
auto Code =
R"(
// Proto of the primary template.
template <class T>
void f();
// Specialization proto.
template <>
void f<int>();
// Specialization proto.
template <>
void f<int>();
)";
Decl *ImportedD;
{
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = Import(FromD, 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_TRUE(ImportedD != To1);
EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ImportFunctionTemplateSpecializations, PrototypeThenDefinition) {
auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
auto Code =
R"(
// Proto of the primary template.
template <class T>
void f();
// Specialization proto.
template <>
void f<int>();
// Specialization definition.
template <>
void f<int>() {}
)";
Decl *ImportedD;
{
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = Import(FromD, 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_TRUE(ImportedD != To1);
EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
EXPECT_TRUE(To1->doesThisDeclarationHaveABody());
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ImportFunctionTemplateSpecializations, DefinitionThenPrototype) {
auto Pattern = functionDecl(hasName("f"), isExplicitTemplateSpecialization());
auto Code =
R"(
// Proto of the primary template.
template <class T>
void f();
// Specialization definition.
template <>
void f<int>() {}
// Specialization proto.
template <>
void f<int>();
)";
Decl *ImportedD;
{
Decl *FromTU = getTuDecl(Code, Lang_CXX, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = Import(FromD, 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_TRUE(ImportedD != To1);
EXPECT_TRUE(To0->doesThisDeclarationHaveABody());
EXPECT_FALSE(To1->doesThisDeclarationHaveABody());
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ASTImporterTestBase,
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(ASTImporterTestBase, 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());
}
struct ASTImporterLookupTableTest : ASTImporterTestBase {};
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);
}
static const RecordDecl * getRecordDeclOfFriend(FriendDecl *FD) {
QualType Ty = FD->getFriendType()->getType();
QualType NamedTy = cast<ElaboratedType>(Ty)->getNamedType();
return cast<RecordType>(NamedTy)->getDecl();
}
TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassDecl) {
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, 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);
}
INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest,
::testing::Values(ArgVector()), );
INSTANTIATE_TEST_CASE_P(
ParameterizedTests, CanonicalRedeclChain,
::testing::Values(ArgVector()),);
auto DefaultTestValuesForRunOptions = ::testing::Values(
ArgVector(),
ArgVector{"-fdelayed-template-parsing"},
ArgVector{"-fms-compatibility"},
ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"});
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
DefaultTestValuesForRunOptions, );
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, ASTImporterTestBase,
DefaultTestValuesForRunOptions, );
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
DefaultTestValuesForRunOptions, );
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
DefaultTestValuesForRunOptions, );
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
DefaultTestValuesForRunOptions, );
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates,
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, );
} // end namespace ast_matchers
} // end namespace clang