blob: 29b1500fcd76d2bda621ac493a6cbfc3bf1bf4cf [file] [log] [blame]
//===- unittest/AST/ASTImporterTest.cpp - AST node import test ------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Tests for the correct import of AST nodes from one AST context to another.
//
//===----------------------------------------------------------------------===//
#include "clang/ASTMatchers/ASTMatchers.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/SmallVectorMemoryBuffer.h"
#include "clang/AST/DeclContextInternals.h"
#include "gtest/gtest.h"
#include "ASTImporterFixtures.h"
namespace clang {
namespace ast_matchers {
using internal::Matcher;
using internal::BindableMatcher;
using llvm::StringMap;
static const RecordDecl *getRecordDeclOfFriend(FriendDecl *FD) {
QualType Ty = FD->getFriendType()->getType().getCanonicalType();
return cast<RecordType>(Ty)->getDecl();
}
struct ImportExpr : TestImportBase {};
struct ImportType : TestImportBase {};
struct ImportDecl : TestImportBase {};
struct ImportFixedPointExpr : ImportExpr {};
struct CanonicalRedeclChain : ASTImporterOptionSpecificTestBase {};
TEST_P(CanonicalRedeclChain, ShouldBeConsequentWithMatchers) {
Decl *FromTU = getTuDecl("void f();", Lang_CXX03);
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_CXX03);
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_CXX03);
auto Pattern = functionDecl(hasName("f"));
auto *D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
auto *D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
FunctionDecl *D1 = D2->getPreviousDecl();
auto RedeclsD0 = getCanonicalForwardRedeclChain(D0);
auto RedeclsD1 = getCanonicalForwardRedeclChain(D1);
auto RedeclsD2 = getCanonicalForwardRedeclChain(D2);
EXPECT_THAT(RedeclsD0, ::testing::ContainerEq(RedeclsD1));
EXPECT_THAT(RedeclsD1, ::testing::ContainerEq(RedeclsD2));
}
namespace {
struct RedirectingImporter : public ASTImporter {
using ASTImporter::ASTImporter;
protected:
llvm::Expected<Decl *> ImportImpl(Decl *FromD) override {
auto *ND = dyn_cast<NamedDecl>(FromD);
if (!ND || ND->getName() != "shouldNotBeImported")
return ASTImporter::ImportImpl(FromD);
for (Decl *D : getToContext().getTranslationUnitDecl()->decls()) {
if (auto *ND = dyn_cast<NamedDecl>(D))
if (ND->getName() == "realDecl") {
RegisterImportedDecl(FromD, ND);
return ND;
}
}
return ASTImporter::ImportImpl(FromD);
}
};
} // namespace
struct RedirectingImporterTest : ASTImporterOptionSpecificTestBase {
RedirectingImporterTest() {
Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
bool MinimalImport,
const std::shared_ptr<ASTImporterSharedState> &SharedState) {
return new RedirectingImporter(ToContext, ToFileManager, FromContext,
FromFileManager, MinimalImport,
SharedState);
};
}
};
// Test that an ASTImporter subclass can intercept an import call.
TEST_P(RedirectingImporterTest, InterceptImport) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl("class shouldNotBeImported {};", Lang_CXX03,
"class realDecl {};", Lang_CXX03, "shouldNotBeImported");
auto *Imported = cast<CXXRecordDecl>(To);
EXPECT_EQ(Imported->getQualifiedNameAsString(), "realDecl");
// Make sure our importer prevented the importing of the decl.
auto *ToTU = Imported->getTranslationUnitDecl();
auto Pattern = functionDecl(hasName("shouldNotBeImported"));
unsigned count =
DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_EQ(0U, count);
}
// Test that when we indirectly import a declaration the custom ASTImporter
// is still intercepting the import.
TEST_P(RedirectingImporterTest, InterceptIndirectImport) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl("class shouldNotBeImported {};"
"class F { shouldNotBeImported f; };",
Lang_CXX03, "class realDecl {};", Lang_CXX03, "F");
// Make sure our ASTImporter prevented the importing of the decl.
auto *ToTU = To->getTranslationUnitDecl();
auto Pattern = functionDecl(hasName("shouldNotBeImported"));
unsigned count =
DeclCounterWithPredicate<CXXRecordDecl>().match(ToTU, Pattern);
EXPECT_EQ(0U, count);
}
struct ImportPath : ASTImporterOptionSpecificTestBase {
Decl *FromTU;
FunctionDecl *D0, *D1, *D2;
ImportPath() {
FromTU = getTuDecl("void f(); void f(); void f();", Lang_CXX03);
auto Pattern = functionDecl(hasName("f"));
D0 = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
D2 = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
D1 = D2->getPreviousDecl();
}
};
TEST_P(ImportPath, Push) {
ASTImporter::ImportPathTy path;
path.push(D0);
EXPECT_FALSE(path.hasCycleAtBack());
}
TEST_P(ImportPath, SmallCycle) {
ASTImporter::ImportPathTy path;
path.push(D0);
path.push(D0);
EXPECT_TRUE(path.hasCycleAtBack());
path.pop();
EXPECT_FALSE(path.hasCycleAtBack());
path.push(D0);
EXPECT_TRUE(path.hasCycleAtBack());
}
TEST_P(ImportPath, GetSmallCycle) {
ASTImporter::ImportPathTy path;
path.push(D0);
path.push(D0);
EXPECT_TRUE(path.hasCycleAtBack());
std::array<Decl* ,2> Res;
int i = 0;
for (Decl *Di : path.getCycleAtBack()) {
Res[i++] = Di;
}
ASSERT_EQ(i, 2);
EXPECT_EQ(Res[0], D0);
EXPECT_EQ(Res[1], D0);
}
TEST_P(ImportPath, GetCycle) {
ASTImporter::ImportPathTy path;
path.push(D0);
path.push(D1);
path.push(D2);
path.push(D0);
EXPECT_TRUE(path.hasCycleAtBack());
std::array<Decl* ,4> Res;
int i = 0;
for (Decl *Di : path.getCycleAtBack()) {
Res[i++] = Di;
}
ASSERT_EQ(i, 4);
EXPECT_EQ(Res[0], D0);
EXPECT_EQ(Res[1], D2);
EXPECT_EQ(Res[2], D1);
EXPECT_EQ(Res[3], D0);
}
TEST_P(ImportPath, CycleAfterCycle) {
ASTImporter::ImportPathTy path;
path.push(D0);
path.push(D1);
path.push(D0);
path.push(D1);
path.push(D2);
path.push(D0);
EXPECT_TRUE(path.hasCycleAtBack());
std::array<Decl* ,4> Res;
int i = 0;
for (Decl *Di : path.getCycleAtBack()) {
Res[i++] = Di;
}
ASSERT_EQ(i, 4);
EXPECT_EQ(Res[0], D0);
EXPECT_EQ(Res[1], D2);
EXPECT_EQ(Res[2], D1);
EXPECT_EQ(Res[3], D0);
path.pop();
path.pop();
path.pop();
EXPECT_TRUE(path.hasCycleAtBack());
i = 0;
for (Decl *Di : path.getCycleAtBack()) {
Res[i++] = Di;
}
ASSERT_EQ(i, 3);
EXPECT_EQ(Res[0], D0);
EXPECT_EQ(Res[1], D1);
EXPECT_EQ(Res[2], D0);
path.pop();
EXPECT_FALSE(path.hasCycleAtBack());
}
const internal::VariadicDynCastAllOfMatcher<Stmt, SourceLocExpr> sourceLocExpr;
AST_MATCHER_P(SourceLocExpr, hasBuiltinStr, StringRef, Str) {
return Node.getBuiltinStr() == Str;
}
TEST_P(ImportExpr, ImportSourceLocExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { (void)__builtin_FILE(); }", Lang_CXX03, "",
Lang_CXX03, Verifier,
functionDecl(hasDescendant(
sourceLocExpr(hasBuiltinStr("__builtin_FILE")))));
testImport("void declToImport() { (void)__builtin_COLUMN(); }", Lang_CXX03,
"", Lang_CXX03, Verifier,
functionDecl(hasDescendant(
sourceLocExpr(hasBuiltinStr("__builtin_COLUMN")))));
}
TEST_P(ImportExpr, ImportStringLiteral) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { (void)\"foo\"; }", Lang_CXX03, "",
Lang_CXX03, Verifier,
functionDecl(hasDescendant(
stringLiteral(hasType(asString("const char[4]"))))));
testImport("void declToImport() { (void)L\"foo\"; }", Lang_CXX03, "",
Lang_CXX03, Verifier,
functionDecl(hasDescendant(
stringLiteral(hasType(asString("const wchar_t[4]"))))));
testImport("void declToImport() { (void) \"foo\" \"bar\"; }", Lang_CXX03, "",
Lang_CXX03, Verifier,
functionDecl(hasDescendant(
stringLiteral(hasType(asString("const char[7]"))))));
}
TEST_P(ImportExpr, ImportChooseExpr) {
MatchVerifier<Decl> Verifier;
// This case tests C code that is not condition-dependent and has a true
// condition.
testImport("void declToImport() { (void)__builtin_choose_expr(1, 2, 3); }",
Lang_C99, "", Lang_C99, Verifier,
functionDecl(hasDescendant(chooseExpr())));
}
const internal::VariadicDynCastAllOfMatcher<Stmt, ShuffleVectorExpr>
shuffleVectorExpr;
TEST_P(ImportExpr, ImportShuffleVectorExpr) {
MatchVerifier<Decl> Verifier;
constexpr auto Code = R"code(
typedef double vector4double __attribute__((__vector_size__(32)));
vector4double declToImport(vector4double a, vector4double b) {
return __builtin_shufflevector(a, b, 0, 1, 2, 3);
}
)code";
const auto Pattern = functionDecl(hasDescendant(shuffleVectorExpr(
allOf(has(declRefExpr(to(parmVarDecl(hasName("a"))))),
has(declRefExpr(to(parmVarDecl(hasName("b"))))),
has(integerLiteral(equals(0))), has(integerLiteral(equals(1))),
has(integerLiteral(equals(2))), has(integerLiteral(equals(3)))))));
testImport(Code, Lang_C99, "", Lang_C99, Verifier, Pattern);
}
TEST_P(ImportExpr, ImportGNUNullExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { (void)__null; }", Lang_CXX03, "",
Lang_CXX03, Verifier,
functionDecl(hasDescendant(gnuNullExpr(hasType(isInteger())))));
}
TEST_P(ImportExpr, ImportGenericSelectionExpr) {
MatchVerifier<Decl> Verifier;
testImport(
"void declToImport() { int x; (void)_Generic(x, int: 0, float: 1); }",
Lang_C99, "", Lang_C99, Verifier,
functionDecl(hasDescendant(genericSelectionExpr())));
}
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_C99, "", Lang_C99,
Verifier,
functionDecl(hasDescendant(
floatLiteral(equals(1.0), hasType(asString("double"))))));
testImport("void declToImport() { (void)1.0e-5f; }", Lang_C99, "", Lang_C99,
Verifier,
functionDecl(hasDescendant(
floatLiteral(equals(1.0e-5f), hasType(asString("float"))))));
}
TEST_P(ImportFixedPointExpr, ImportFixedPointerLiteralExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport() { (void)1.0k; }", Lang_C99, "", Lang_C99,
Verifier, functionDecl(hasDescendant(fixedPointLiteral())));
testImport("void declToImport() { (void)0.75r; }", Lang_C99, "", Lang_C99,
Verifier, functionDecl(hasDescendant(fixedPointLiteral())));
}
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_CXX03, "", Lang_CXX03, 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_CXX03, "",
Lang_CXX03, 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_C99, "", Lang_C99, 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_C99,
"", Lang_C99, 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_CXX03, "", Lang_CXX03, 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_C99, "", Lang_C99, 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_C99, "", Lang_C99, Verifier,
traverse(TK_AsIs,
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_CXX03, "",
Lang_CXX03, 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_CXX03, "", Lang_CXX03,
Verifier,
traverse(TK_AsIs,
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_C99, "", Lang_C99, 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_CXX03, "",
Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, Verifier,
functionDecl(hasDescendant(
cStyleCastExpr(hasSourceExpression(vaArgExpr())))));
}
TEST_P(ImportExpr, CXXTemporaryObjectExpr) {
MatchVerifier<Decl> Verifier;
testImport(
"struct C {};"
"void declToImport() { C c = C(); }",
Lang_CXX03, "", Lang_CXX03, Verifier,
traverse(TK_AsIs,
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_CXX03, "",
Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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,
traverse(TK_AsIs,
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,
traverse(TK_AsIs, 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_CXX03, "", Lang_CXX03, Verifier,
classTemplateDecl(has(cxxRecordDecl(has(
fieldDecl(hasType(dependentTemplateSpecializationType())))))));
}
TEST_P(ImportType, ImportDeducedTemplateSpecialization) {
MatchVerifier<Decl> Verifier;
testImport("template <typename T>"
"class C { public: C(T); };"
"C declToImport(123);",
Lang_CXX17, "", Lang_CXX17, Verifier,
varDecl(hasType(deducedTemplateSpecializationType())));
}
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))))))))));
}
const internal::VariadicDynCastAllOfMatcher<Stmt, CXXFoldExpr> cxxFoldExpr;
AST_MATCHER_P(CXXFoldExpr, hasOperator, BinaryOperatorKind, Op) {
return Node.getOperator() == Op;
}
AST_MATCHER(CXXFoldExpr, hasInit) { return Node.getInit(); }
AST_MATCHER(CXXFoldExpr, isRightFold) { return Node.isRightFold(); }
AST_MATCHER(CXXFoldExpr, isLeftFold) { return Node.isLeftFold(); }
TEST_P(ImportExpr, ImportCXXFoldExpr) {
auto Match1 =
cxxFoldExpr(hasOperator(BO_Add), isLeftFold(), unless(hasInit()));
auto Match2 = cxxFoldExpr(hasOperator(BO_Sub), isLeftFold(), hasInit());
auto Match3 =
cxxFoldExpr(hasOperator(BO_Mul), isRightFold(), unless(hasInit()));
auto Match4 = cxxFoldExpr(hasOperator(BO_Div), isRightFold(), hasInit());
MatchVerifier<Decl> Verifier;
testImport("template <typename... Ts>"
"void declToImport(Ts... args) {"
" const int i1 = (... + args);"
" const int i2 = (1 - ... - args);"
" const int i3 = (args * ...);"
" const int i4 = (args / ... / 1);"
"};"
"void g() { declToImport(1, 2, 3, 4, 5); }",
Lang_CXX17, "", Lang_CXX17, Verifier,
functionTemplateDecl(hasDescendant(Match1), hasDescendant(Match2),
hasDescendant(Match3),
hasDescendant(Match4)));
}
/// \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_C99, "", Lang_C99, 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,
traverse(
TK_AsIs,
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_C99, "", Lang_C99, Verifier,
functionDecl(hasBody(compoundStmt(
has(declStmt(hasSingleDecl(varDecl(hasName("d")))))))));
}
TEST_P(ImportDecl, ImportedVarDeclPreservesThreadLocalStorage) {
MatchVerifier<Decl> Verifier;
testImport("thread_local int declToImport;", Lang_CXX11, "", Lang_CXX11,
Verifier, varDecl(hasThreadStorageDuration()));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordTypeInFunc) {
Decl *FromTU = getTuDecl("int declToImport() { "
" struct data_t {int a;int b;};"
" struct data_t d;"
" return 0;"
"}",
Lang_C99, "input.c");
auto *FromVar =
FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("d")));
ASSERT_TRUE(FromVar);
auto ToType =
ImportType(FromVar->getType().getCanonicalType(), FromVar, Lang_C99);
EXPECT_FALSE(ToType.isNull());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordDeclInFuncParams) {
// This construct is not supported by ASTImporter.
Decl *FromTU = getTuDecl(
"int declToImport(struct data_t{int a;int b;} ***d){ return 0; }",
Lang_C99, "input.c");
auto *From = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("declToImport")));
ASSERT_TRUE(From);
auto *To = Import(From, Lang_C99);
EXPECT_EQ(To, nullptr);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportRecordDeclInFuncFromMacro) {
Decl *FromTU =
getTuDecl("#define NONAME_SIZEOF(type) sizeof(struct{type *dummy;}) \n"
"int declToImport(){ return NONAME_SIZEOF(int); }",
Lang_C99, "input.c");
auto *From = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("declToImport")));
ASSERT_TRUE(From);
auto *To = Import(From, Lang_C99);
ASSERT_TRUE(To);
EXPECT_TRUE(MatchVerifier<FunctionDecl>().match(
To, functionDecl(hasName("declToImport"),
hasDescendant(unaryExprOrTypeTraitExpr()))));
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportRecordDeclInFuncParamsFromMacro) {
// This construct is not supported by ASTImporter.
Decl *FromTU =
getTuDecl("#define PAIR_STRUCT(type) struct data_t{type a;type b;} \n"
"int declToImport(PAIR_STRUCT(int) ***d){ return 0; }",
Lang_C99, "input.c");
auto *From = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("declToImport")));
ASSERT_TRUE(From);
auto *To = Import(From, Lang_C99);
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_CXX03, "", Lang_CXX03, Verifier,
functionDecl(hasDescendant(callExpr(has(cxxPseudoDestructorExpr())))));
}
TEST_P(ImportDecl, ImportUsingDecl) {
MatchVerifier<Decl> Verifier;
testImport("namespace foo { int bar; }"
"void declToImport() { using foo::bar; }",
Lang_CXX03, "", Lang_CXX03, Verifier,
functionDecl(hasDescendant(usingDecl(hasName("bar")))));
}
TEST_P(ImportDecl, ImportUsingEnumDecl) {
MatchVerifier<Decl> Verifier;
testImport("namespace foo { enum bar { baz, toto, quux }; }"
"void declToImport() { using enum foo::bar; }",
Lang_CXX20, "", Lang_CXX20, Verifier,
functionDecl(hasDescendant(usingEnumDecl(hasName("bar")))));
}
/// \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;
// from using-decl
testImport("namespace foo { int bar; }"
"namespace declToImport { using foo::bar; }",
Lang_CXX03, "", Lang_CXX03, Verifier,
namespaceDecl(has(usingShadowDecl(hasName("bar")))));
// from using-enum-decl
testImport("namespace foo { enum bar {baz, toto, quux }; }"
"namespace declToImport { using enum foo::bar; }",
Lang_CXX20, "", Lang_CXX20, Verifier,
namespaceDecl(has(usingShadowDecl(hasName("baz")))));
}
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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, Verifier,
functionTemplateDecl(hasAncestor(translationUnitDecl(
unless(has(functionDecl(hasName("declToImport"))))))));
testImport("template <typename T> struct declToImport { T t; };"
"void instantiate() { declToImport<int>(); }",
Lang_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, Verifier,
recordDecl(has(classTemplateDecl()),
has(classTemplateSpecializationDecl())));
}
TEST_P(ImportExpr, CXXOperatorCallExpr) {
MatchVerifier<Decl> Verifier;
testImport(
"class declToImport {"
" void f() { *this = declToImport(); }"
"};",
Lang_CXX03, "", Lang_CXX03, 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_CXX03, "", Lang_CXX03, Verifier,
classTemplateDecl(has(cxxRecordDecl(
has(fieldDecl(hasType(dependentSizedArrayType())))))));
}
TEST_P(ASTImporterOptionSpecificTestBase, TemplateTypeParmDeclNoDefaultArg) {
Decl *FromTU = getTuDecl("template<typename T> struct X {};", Lang_CXX03);
auto From = FirstDeclMatcher<TemplateTypeParmDecl>().match(
FromTU, templateTypeParmDecl(hasName("T")));
TemplateTypeParmDecl *To = Import(From, Lang_CXX03);
ASSERT_FALSE(To->hasDefaultArgument());
}
TEST_P(ASTImporterOptionSpecificTestBase, TemplateTypeParmDeclDefaultArg) {
Decl *FromTU =
getTuDecl("template<typename T = int> struct X {};", Lang_CXX03);
auto From = FirstDeclMatcher<TemplateTypeParmDecl>().match(
FromTU, templateTypeParmDecl(hasName("T")));
TemplateTypeParmDecl *To = Import(From, Lang_CXX03);
ASSERT_TRUE(To->hasDefaultArgument());
QualType ToArg = To->getDefaultArgument();
ASSERT_EQ(ToArg, QualType(To->getASTContext().IntTy));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportBeginLocOfDeclRefExpr) {
Decl *FromTU =
getTuDecl("class A { public: static int X; }; void f() { (void)A::X; }",
Lang_CXX03);
auto From = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
ASSERT_TRUE(From);
ASSERT_TRUE(
cast<CStyleCastExpr>(cast<CompoundStmt>(From->getBody())->body_front())
->getSubExpr()
->getBeginLoc()
.isValid());
FunctionDecl *To = Import(From, Lang_CXX03);
ASSERT_TRUE(To);
ASSERT_TRUE(
cast<CStyleCastExpr>(cast<CompoundStmt>(To->getBody())->body_front())
->getSubExpr()
->getBeginLoc()
.isValid());
}
TEST_P(ASTImporterOptionSpecificTestBase,
TemplateTemplateParmDeclNoDefaultArg) {
Decl *FromTU = getTuDecl(R"(
template<template<typename> typename TT> struct Y {};
)",
Lang_CXX17);
auto From = FirstDeclMatcher<TemplateTemplateParmDecl>().match(
FromTU, templateTemplateParmDecl(hasName("TT")));
TemplateTemplateParmDecl *To = Import(From, Lang_CXX17);
ASSERT_FALSE(To->hasDefaultArgument());
}
TEST_P(ASTImporterOptionSpecificTestBase, TemplateTemplateParmDeclDefaultArg) {
Decl *FromTU = getTuDecl(R"(
template<typename T> struct X {};
template<template<typename> typename TT = X> struct Y {};
)",
Lang_CXX17);
auto From = FirstDeclMatcher<TemplateTemplateParmDecl>().match(
FromTU, templateTemplateParmDecl(hasName("TT")));
TemplateTemplateParmDecl *To = Import(From, Lang_CXX17);
ASSERT_TRUE(To->hasDefaultArgument());
const TemplateArgument &ToDefaultArg = To->getDefaultArgument().getArgument();
ASSERT_TRUE(To->isTemplateDecl());
TemplateDecl *ToTemplate = ToDefaultArg.getAsTemplate().getAsTemplateDecl();
// Find the default argument template 'X' in the AST and compare it against
// the default argument we got.
auto ToExpectedDecl = FirstDeclMatcher<ClassTemplateDecl>().match(
To->getTranslationUnitDecl(), classTemplateDecl(hasName("X")));
ASSERT_EQ(ToTemplate, ToExpectedDecl);
}
TEST_P(ASTImporterOptionSpecificTestBase, NonTypeTemplateParmDeclNoDefaultArg) {
Decl *FromTU = getTuDecl("template<int N> struct X {};", Lang_CXX03);
auto From = FirstDeclMatcher<NonTypeTemplateParmDecl>().match(
FromTU, nonTypeTemplateParmDecl(hasName("N")));
NonTypeTemplateParmDecl *To = Import(From, Lang_CXX03);
ASSERT_FALSE(To->hasDefaultArgument());
}
TEST_P(ASTImporterOptionSpecificTestBase, NonTypeTemplateParmDeclDefaultArg) {
Decl *FromTU = getTuDecl("template<int S = 1> struct X {};", Lang_CXX03);
auto From = FirstDeclMatcher<NonTypeTemplateParmDecl>().match(
FromTU, nonTypeTemplateParmDecl(hasName("S")));
NonTypeTemplateParmDecl *To = Import(From, Lang_CXX03);
ASSERT_TRUE(To->hasDefaultArgument());
Stmt *ToArg = To->getDefaultArgument();
ASSERT_TRUE(isa<ConstantExpr>(ToArg));
ToArg = *ToArg->child_begin();
ASSERT_TRUE(isa<IntegerLiteral>(ToArg));
ASSERT_EQ(cast<IntegerLiteral>(ToArg)->getValue().getLimitedValue(), 1U);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportOfTemplatedDeclOfClassTemplateDecl) {
Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX03);
auto From =
FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl());
ASSERT_TRUE(From);
auto To = cast<ClassTemplateDecl>(Import(From, Lang_CXX03));
ASSERT_TRUE(To);
Decl *ToTemplated = To->getTemplatedDecl();
Decl *ToTemplated1 = Import(From->getTemplatedDecl(), Lang_CXX03);
EXPECT_TRUE(ToTemplated1);
EXPECT_EQ(ToTemplated1, ToTemplated);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportOfTemplatedDeclOfFunctionTemplateDecl) {
Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX03);
auto From = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl());
ASSERT_TRUE(From);
auto To = cast<FunctionTemplateDecl>(Import(From, Lang_CXX03));
ASSERT_TRUE(To);
Decl *ToTemplated = To->getTemplatedDecl();
Decl *ToTemplated1 = Import(From->getTemplatedDecl(), Lang_CXX03);
EXPECT_TRUE(ToTemplated1);
EXPECT_EQ(ToTemplated1, ToTemplated);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportOfTemplatedDeclShouldImportTheClassTemplateDecl) {
Decl *FromTU = getTuDecl("template<class X> struct S{};", Lang_CXX03);
auto FromFT =
FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, classTemplateDecl());
ASSERT_TRUE(FromFT);
auto ToTemplated =
cast<CXXRecordDecl>(Import(FromFT->getTemplatedDecl(), Lang_CXX03));
EXPECT_TRUE(ToTemplated);
auto ToTU = ToTemplated->getTranslationUnitDecl();
auto ToFT =
FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, classTemplateDecl());
EXPECT_TRUE(ToFT);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportOfTemplatedDeclShouldImportTheFunctionTemplateDecl) {
Decl *FromTU = getTuDecl("template<class X> void f(){}", Lang_CXX03);
auto FromFT = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl());
ASSERT_TRUE(FromFT);
auto ToTemplated =
cast<FunctionDecl>(Import(FromFT->getTemplatedDecl(), Lang_CXX03));
EXPECT_TRUE(ToTemplated);
auto ToTU = ToTemplated->getTranslationUnitDecl();
auto ToFT = FirstDeclMatcher<FunctionTemplateDecl>().match(
ToTU, functionTemplateDecl());
EXPECT_TRUE(ToFT);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportCorrectTemplatedDecl) {
auto Code =
R"(
namespace x {
template<class X> struct S1{};
template<class X> struct S2{};
template<class X> struct S3{};
}
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX03);
auto FromNs =
FirstDeclMatcher<NamespaceDecl>().match(FromTU, namespaceDecl());
auto ToNs = cast<NamespaceDecl>(Import(FromNs, Lang_CXX03));
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_CXX03));
EXPECT_TRUE(ToTemplated1);
ASSERT_EQ(ToTemplated1, ToTemplated);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportChooseExpr) {
// This tests the import of isConditionTrue directly to make sure the importer
// gets it right.
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
"void declToImport() { (void)__builtin_choose_expr(1, 0, 1); }", Lang_C99,
"", Lang_C99);
auto ToResults = match(chooseExpr().bind("choose"), To->getASTContext());
auto FromResults = match(chooseExpr().bind("choose"), From->getASTContext());
const ChooseExpr *FromChooseExpr =
selectFirst<ChooseExpr>("choose", FromResults);
ASSERT_TRUE(FromChooseExpr);
const ChooseExpr *ToChooseExpr = selectFirst<ChooseExpr>("choose", ToResults);
ASSERT_TRUE(ToChooseExpr);
EXPECT_EQ(FromChooseExpr->isConditionTrue(), ToChooseExpr->isConditionTrue());
EXPECT_EQ(FromChooseExpr->isConditionDependent(),
ToChooseExpr->isConditionDependent());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportGenericSelectionExpr) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
int declToImport() {
int x;
return _Generic(x, int: 0, default: 1);
}
)",
Lang_C99, "", Lang_C99);
auto ToResults =
match(genericSelectionExpr().bind("expr"), To->getASTContext());
auto FromResults =
match(genericSelectionExpr().bind("expr"), From->getASTContext());
const GenericSelectionExpr *FromGenericSelectionExpr =
selectFirst<GenericSelectionExpr>("expr", FromResults);
ASSERT_TRUE(FromGenericSelectionExpr);
const GenericSelectionExpr *ToGenericSelectionExpr =
selectFirst<GenericSelectionExpr>("expr", ToResults);
ASSERT_TRUE(ToGenericSelectionExpr);
EXPECT_EQ(FromGenericSelectionExpr->isResultDependent(),
ToGenericSelectionExpr->isResultDependent());
EXPECT_EQ(FromGenericSelectionExpr->getResultIndex(),
ToGenericSelectionExpr->getResultIndex());
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportFunctionWithBackReferringParameter) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template <typename T> struct X {};
void declToImport(int y, X<int> &x) {}
template <> struct X<int> {
void g() {
X<int> x;
declToImport(0, x);
}
};
)",
Lang_CXX03, "", Lang_CXX03);
MatchVerifier<Decl> Verifier;
auto Matcher = functionDecl(hasName("declToImport"),
parameterCountIs(2),
hasParameter(0, hasName("y")),
hasParameter(1, hasName("x")),
hasParameter(1, hasType(asString("X<int> &"))));
ASSERT_TRUE(Verifier.match(From, Matcher));
EXPECT_TRUE(Verifier.match(To, Matcher));
}
TEST_P(ASTImporterOptionSpecificTestBase,
TUshouldNotContainTemplatedDeclOfFunctionTemplates) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl("template <typename T> void declToImport() { T a = 1; }"
"void instantiate() { declToImport<int>(); }",
Lang_CXX03, "", Lang_CXX03);
auto Check = [](Decl *D) -> bool {
auto TU = D->getTranslationUnitDecl();
for (auto Child : TU->decls()) {
if (auto *FD = dyn_cast<FunctionDecl>(Child)) {
if (FD->getNameAsString() == "declToImport") {
GTEST_NONFATAL_FAILURE_(
"TU should not contain any FunctionDecl with name declToImport");
return false;
}
}
}
return true;
};
ASSERT_TRUE(Check(From));
EXPECT_TRUE(Check(To));
}
TEST_P(ASTImporterOptionSpecificTestBase,
TUshouldNotContainTemplatedDeclOfClassTemplates) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl("template <typename T> struct declToImport { T t; };"
"void instantiate() { declToImport<int>(); }",
Lang_CXX03, "", Lang_CXX03);
auto Check = [](Decl *D) -> bool {
auto TU = D->getTranslationUnitDecl();
for (auto Child : TU->decls()) {
if (auto *RD = dyn_cast<CXXRecordDecl>(Child)) {
if (RD->getNameAsString() == "declToImport") {
GTEST_NONFATAL_FAILURE_(
"TU should not contain any CXXRecordDecl with name declToImport");
return false;
}
}
}
return true;
};
ASSERT_TRUE(Check(From));
EXPECT_TRUE(Check(To));
}
TEST_P(ASTImporterOptionSpecificTestBase,
TUshouldNotContainTemplatedDeclOfTypeAlias) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl(
"template <typename T> struct X {};"
"template <typename T> using declToImport = X<T>;"
"void instantiate() { declToImport<int> a; }",
Lang_CXX11, "", Lang_CXX11);
auto Check = [](Decl *D) -> bool {
auto TU = D->getTranslationUnitDecl();
for (auto Child : TU->decls()) {
if (auto *AD = dyn_cast<TypeAliasDecl>(Child)) {
if (AD->getNameAsString() == "declToImport") {
GTEST_NONFATAL_FAILURE_(
"TU should not contain any TypeAliasDecl with name declToImport");
return false;
}
}
}
return true;
};
ASSERT_TRUE(Check(From));
EXPECT_TRUE(Check(To));
}
TEST_P(ASTImporterOptionSpecificTestBase,
TUshouldNotContainClassTemplateSpecializationOfImplicitInstantiation) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template<class T>
class Base {};
class declToImport : public Base<declToImport> {};
)",
Lang_CXX03, "", Lang_CXX03);
// Check that the ClassTemplateSpecializationDecl is NOT the child of the TU.
auto Pattern =
translationUnitDecl(unless(has(classTemplateSpecializationDecl())));
ASSERT_TRUE(
MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
EXPECT_TRUE(
MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
// Check that the ClassTemplateSpecializationDecl is the child of the
// ClassTemplateDecl.
Pattern = translationUnitDecl(has(classTemplateDecl(
hasName("Base"), has(classTemplateSpecializationDecl()))));
ASSERT_TRUE(
MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
EXPECT_TRUE(
MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
}
AST_MATCHER_P(RecordDecl, hasFieldOrder, std::vector<StringRef>, Order) {
size_t Index = 0;
for (Decl *D : Node.decls()) {
if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) {
auto *ND = cast<NamedDecl>(D);
if (Index == Order.size())
return false;
if (ND->getName() != Order[Index])
return false;
++Index;
}
}
return Index == Order.size();
}
TEST_P(ASTImporterOptionSpecificTestBase,
TUshouldContainClassTemplateSpecializationOfExplicitInstantiation) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
namespace NS {
template<class T>
class X {};
template class X<int>;
}
)",
Lang_CXX03, "", Lang_CXX03, "NS");
// Check that the ClassTemplateSpecializationDecl is NOT the child of the
// ClassTemplateDecl.
auto Pattern = namespaceDecl(has(classTemplateDecl(
hasName("X"), unless(has(classTemplateSpecializationDecl())))));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(From, Pattern));
EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern));
// Check that the ClassTemplateSpecializationDecl is the child of the
// NamespaceDecl.
Pattern = namespaceDecl(has(classTemplateSpecializationDecl(hasName("X"))));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(From, Pattern));
EXPECT_TRUE(MatchVerifier<Decl>{}.match(To, Pattern));
}
TEST_P(ASTImporterOptionSpecificTestBase,
CXXRecordDeclFieldsShouldBeInCorrectOrder) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl(
"struct declToImport { int a; int b; };",
Lang_CXX11, "", Lang_CXX11);
MatchVerifier<Decl> Verifier;
ASSERT_TRUE(Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b"}))));
EXPECT_TRUE(Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b"}))));
}
TEST_P(ASTImporterOptionSpecificTestBase,
CXXRecordDeclFieldOrderShouldNotDependOnImportOrder) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
// The original recursive algorithm of ASTImporter first imports 'c' then
// 'b' and lastly 'a'. Therefore we must restore the order somehow.
R"s(
struct declToImport {
int a = c + b;
int b = 1;
int c = 2;
};
)s",
Lang_CXX11, "", Lang_CXX11);
MatchVerifier<Decl> Verifier;
ASSERT_TRUE(
Verifier.match(From, cxxRecordDecl(hasFieldOrder({"a", "b", "c"}))));
EXPECT_TRUE(
Verifier.match(To, cxxRecordDecl(hasFieldOrder({"a", "b", "c"}))));
}
TEST_P(ASTImporterOptionSpecificTestBase,
CXXRecordDeclFieldAndIndirectFieldOrder) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
// First field is "a", then the field for unnamed union, then "b" and "c"
// from it (indirect fields), then "d".
R"s(
struct declToImport {
int a = d;
union {
int b;
int c;
};
int d;
};
)s",
Lang_CXX11, "", Lang_CXX11);
MatchVerifier<Decl> Verifier;
ASSERT_TRUE(Verifier.match(
From, cxxRecordDecl(hasFieldOrder({"a", "", "b", "c", "d"}))));
EXPECT_TRUE(Verifier.match(
To, cxxRecordDecl(hasFieldOrder({"a", "", "b", "c", "d"}))));
}
TEST_P(ASTImporterOptionSpecificTestBase, ShouldImportImplicitCXXRecordDecl) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
struct declToImport {
};
)",
Lang_CXX03, "", Lang_CXX03);
MatchVerifier<Decl> Verifier;
// Match the implicit Decl.
auto Matcher = cxxRecordDecl(has(cxxRecordDecl()));
ASSERT_TRUE(Verifier.match(From, Matcher));
EXPECT_TRUE(Verifier.match(To, Matcher));
}
TEST_P(ASTImporterOptionSpecificTestBase,
ShouldImportImplicitCXXRecordDeclOfClassTemplate) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template <typename U>
struct declToImport {
};
)",
Lang_CXX03, "", Lang_CXX03);
MatchVerifier<Decl> Verifier;
// Match the implicit Decl.
auto Matcher = classTemplateDecl(has(cxxRecordDecl(has(cxxRecordDecl()))));
ASSERT_TRUE(Verifier.match(From, Matcher));
EXPECT_TRUE(Verifier.match(To, Matcher));
}
TEST_P(ASTImporterOptionSpecificTestBase,
ShouldImportImplicitCXXRecordDeclOfClassTemplateSpecializationDecl) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template<class T>
class Base {};
class declToImport : public Base<declToImport> {};
)",
Lang_CXX03, "", Lang_CXX03);
auto hasImplicitClass = has(cxxRecordDecl());
auto Pattern = translationUnitDecl(has(classTemplateDecl(
hasName("Base"),
has(classTemplateSpecializationDecl(hasImplicitClass)))));
ASSERT_TRUE(
MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
EXPECT_TRUE(
MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
}
TEST_P(ASTImporterOptionSpecificTestBase, IDNSOrdinary) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl("void declToImport() {}", Lang_CXX03, "", Lang_CXX03);
MatchVerifier<Decl> Verifier;
auto Matcher = functionDecl();
ASSERT_TRUE(Verifier.match(From, Matcher));
EXPECT_TRUE(Verifier.match(To, Matcher));
EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace());
}
TEST_P(ASTImporterOptionSpecificTestBase, IDNSOfNonmemberOperator) {
Decl *FromTU = getTuDecl(
R"(
struct X {};
void operator<<(int, X);
)",
Lang_CXX03);
Decl *From = LastDeclMatcher<Decl>{}.match(FromTU, functionDecl());
const Decl *To = Import(From, Lang_CXX03);
EXPECT_EQ(From->getIdentifierNamespace(), To->getIdentifierNamespace());
}
TEST_P(ASTImporterOptionSpecificTestBase,
ShouldImportMembersOfClassTemplateSpecializationDecl) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template<class T>
class Base { int a; };
class declToImport : Base<declToImport> {};
)",
Lang_CXX03, "", Lang_CXX03);
auto Pattern = translationUnitDecl(has(classTemplateDecl(
hasName("Base"),
has(classTemplateSpecializationDecl(has(fieldDecl(hasName("a"))))))));
ASSERT_TRUE(
MatchVerifier<Decl>{}.match(From->getTranslationUnitDecl(), Pattern));
EXPECT_TRUE(
MatchVerifier<Decl>{}.match(To->getTranslationUnitDecl(), Pattern));
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportDefinitionOfClassTemplateAfterFwdDecl) {
{
Decl *FromTU = getTuDecl(
R"(
template <typename T>
struct B;
)",
Lang_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("B")));
Import(FromD, Lang_CXX03);
}
{
Decl *FromTU = getTuDecl(
R"(
template <typename T>
struct B {
void f();
};
)",
Lang_CXX03, "input1.cc");
FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
Import(FromD, Lang_CXX03);
auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("B")));
auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX03));
EXPECT_TRUE(ToCTD->isThisDeclarationADefinition());
}
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportDefinitionOfClassTemplateIfThereIsAnExistingFwdDeclAndDefinition) {
Decl *ToTU = getToTuDecl(
R"(
template <typename T>
struct B {
void f();
};
template <typename T>
struct B;
)",
Lang_CXX03);
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_CXX03, "input1.cc");
ClassTemplateDecl *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("B")));
Import(FromD, Lang_CXX03);
// We should have only one definition.
EXPECT_EQ(1u, DeclCounterWithPredicate<ClassTemplateDecl>(
[](const ClassTemplateDecl *T) {
return T->isThisDeclarationADefinition();
})
.match(ToTU, classTemplateDecl()));
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportDefinitionOfClassIfThereIsAnExistingFwdDeclAndDefinition) {
Decl *ToTU = getToTuDecl(
R"(
struct B {
void f();
};
struct B;
)",
Lang_CXX03);
ASSERT_EQ(2u, DeclCounter<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(unless(isImplicit()))));
Decl *FromTU = getTuDecl(
R"(
struct B {
void f();
};
)",
Lang_CXX03, "input1.cc");
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("B")));
Import(FromD, Lang_CXX03);
EXPECT_EQ(2u, DeclCounter<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(unless(isImplicit()))));
}
static void CompareSourceLocs(FullSourceLoc Loc1, FullSourceLoc Loc2) {
EXPECT_EQ(Loc1.getExpansionLineNumber(), Loc2.getExpansionLineNumber());
EXPECT_EQ(Loc1.getExpansionColumnNumber(), Loc2.getExpansionColumnNumber());
EXPECT_EQ(Loc1.getSpellingLineNumber(), Loc2.getSpellingLineNumber());
EXPECT_EQ(Loc1.getSpellingColumnNumber(), Loc2.getSpellingColumnNumber());
}
static void CompareSourceRanges(SourceRange Range1, SourceRange Range2,
SourceManager &SM1, SourceManager &SM2) {
CompareSourceLocs(FullSourceLoc{ Range1.getBegin(), SM1 },
FullSourceLoc{ Range2.getBegin(), SM2 });
CompareSourceLocs(FullSourceLoc{ Range1.getEnd(), SM1 },
FullSourceLoc{ Range2.getEnd(), SM2 });
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportSourceLocs) {
Decl *FromTU = getTuDecl(
R"(
#define MFOO(arg) arg = arg + 1
void foo() {
int a = 5;
MFOO(a);
}
)",
Lang_CXX03);
auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
auto ToD = Import(FromD, Lang_CXX03);
auto ToLHS = LastDeclMatcher<DeclRefExpr>().match(ToD, declRefExpr());
auto FromLHS = LastDeclMatcher<DeclRefExpr>().match(FromTU, declRefExpr());
auto ToRHS = LastDeclMatcher<IntegerLiteral>().match(ToD, integerLiteral());
auto FromRHS =
LastDeclMatcher<IntegerLiteral>().match(FromTU, integerLiteral());
SourceManager &ToSM = ToAST->getASTContext().getSourceManager();
SourceManager &FromSM = FromD->getASTContext().getSourceManager();
CompareSourceRanges(ToD->getSourceRange(), FromD->getSourceRange(), ToSM,
FromSM);
CompareSourceRanges(ToLHS->getSourceRange(), FromLHS->getSourceRange(), ToSM,
FromSM);
CompareSourceRanges(ToRHS->getSourceRange(), FromRHS->getSourceRange(), ToSM,
FromSM);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportNestedMacro) {
Decl *FromTU = getTuDecl(
R"(
#define FUNC_INT void declToImport
#define FUNC FUNC_INT
FUNC(int a);
)",
Lang_CXX03);
auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
auto ToD = Import(FromD, Lang_CXX03);
SourceManager &ToSM = ToAST->getASTContext().getSourceManager();
SourceManager &FromSM = FromD->getASTContext().getSourceManager();
CompareSourceRanges(ToD->getSourceRange(), FromD->getSourceRange(), ToSM,
FromSM);
}
TEST_P(
ASTImporterOptionSpecificTestBase,
ImportDefinitionOfClassTemplateSpecIfThereIsAnExistingFwdDeclAndDefinition) {
Decl *ToTU = getToTuDecl(
R"(
template <typename T>
struct B;
template <>
struct B<int> {};
template <>
struct B<int>;
)",
Lang_CXX03);
// 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_CXX03, "input1.cc");
auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl(hasName("B")));
Import(FromD, Lang_CXX03);
// We should have only one definition.
EXPECT_EQ(1u, DeclCounterWithPredicate<ClassTemplateSpecializationDecl>(
[](const ClassTemplateSpecializationDecl *T) {
return T->isThisDeclarationADefinition();
})
.match(ToTU, classTemplateSpecializationDecl()));
}
TEST_P(ASTImporterOptionSpecificTestBase, ObjectsWithUnnamedStructType) {
Decl *FromTU = getTuDecl(
R"(
struct { int a; int b; } object0 = { 2, 3 };
struct { int x; int y; int z; } object1;
)",
Lang_CXX03, "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_CXX03);
auto *To1 = Import(From1, Lang_CXX03);
EXPECT_TRUE(To0);
EXPECT_TRUE(To1);
EXPECT_NE(To0, To1);
EXPECT_NE(To0->getCanonicalDecl(), To1->getCanonicalDecl());
}
TEST_P(ASTImporterOptionSpecificTestBase, AnonymousRecords) {
auto *Code =
R"(
struct X {
struct { int a; };
struct { int b; };
};
)";
Decl *FromTU0 = getTuDecl(Code, Lang_C99, "input0.c");
Decl *FromTU1 = getTuDecl(Code, Lang_C99, "input1.c");
auto *X0 =
FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
auto *X1 =
FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
Import(X0, Lang_C99);
Import(X1, Lang_C99);
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
// We expect no (ODR) warning during the import.
EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
EXPECT_EQ(1u,
DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
}
TEST_P(ASTImporterOptionSpecificTestBase, AnonymousRecordsReversed) {
Decl *FromTU0 = getTuDecl(
R"(
struct X {
struct { int a; };
struct { int b; };
};
)",
Lang_C99, "input0.c");
Decl *FromTU1 = getTuDecl(
R"(
struct X { // reversed order
struct { int b; };
struct { int a; };
};
)",
Lang_C99, "input1.c");
auto *X0 =
FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
auto *X1 =
FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
Import(X0, Lang_C99);
Import(X1, Lang_C99);
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
// We expect one (ODR) warning during the import.
EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
EXPECT_EQ(1u,
DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag) {
auto Pattern = varDecl(hasName("x"));
VarDecl *Imported1;
{
Decl *FromTU = getTuDecl("extern int x;", Lang_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
Imported1 = cast<VarDecl>(Import(FromD, Lang_CXX03));
}
VarDecl *Imported2;
{
Decl *FromTU = getTuDecl("int x;", Lang_CXX03, "input1.cc");
auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
Imported2 = cast<VarDecl>(Import(FromD, Lang_CXX03));
}
EXPECT_EQ(Imported1->getCanonicalDecl(), Imported2->getCanonicalDecl());
EXPECT_FALSE(Imported2->isUsed(false));
{
Decl *FromTU = getTuDecl("extern int x; int f() { return x; }", Lang_CXX03,
"input2.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
Import(FromD, Lang_CXX03);
}
EXPECT_TRUE(Imported2->isUsed(false));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag2) {
auto Pattern = varDecl(hasName("x"));
VarDecl *ExistingD;
{
Decl *ToTU = getToTuDecl("int x = 1;", Lang_CXX03);
ExistingD = FirstDeclMatcher<VarDecl>().match(ToTU, Pattern);
}
EXPECT_FALSE(ExistingD->isUsed(false));
{
Decl *FromTU =
getTuDecl("int x = 1; int f() { return x; }", Lang_CXX03, "input1.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
Import(FromD, Lang_CXX03);
}
EXPECT_TRUE(ExistingD->isUsed(false));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportDoesUpdateUsedFlag3) {
auto Pattern = varDecl(hasName("a"));
VarDecl *ExistingD;
{
Decl *ToTU = getToTuDecl(
R"(
struct A {
static const int a = 1;
};
)",
Lang_CXX03);
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_CXX03, "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_CXX03);
}
EXPECT_TRUE(ExistingD->isUsed(false));
}
TEST_P(ASTImporterOptionSpecificTestBase, ReimportWithUsedFlag) {
auto Pattern = varDecl(hasName("x"));
Decl *FromTU = getTuDecl("int x;", Lang_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
auto *Imported1 = cast<VarDecl>(Import(FromD, Lang_CXX03));
ASSERT_FALSE(Imported1->isUsed(false));
FromD->setIsUsed();
auto *Imported2 = cast<VarDecl>(Import(FromD, Lang_CXX03));
EXPECT_EQ(Imported1, Imported2);
EXPECT_TRUE(Imported2->isUsed(false));
}
struct ImportFunctions : ASTImporterOptionSpecificTestBase {};
TEST_P(ImportFunctions, ImportPrototypeOfRecursiveFunction) {
Decl *FromTU = getTuDecl("void f(); void f() { f(); }", Lang_CXX03);
auto Pattern = functionDecl(hasName("f"));
auto *From =
FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Proto
Decl *ImportedD = Import(From, Lang_CXX03);
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_CXX03);
auto Pattern = functionDecl(hasName("f"));
auto *From =
LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern); // Def
Decl *ImportedD = Import(From, Lang_CXX03);
Decl *ToTU = ImportedD->getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
auto *To1 = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedD == To1);
EXPECT_FALSE(To0->doesThisDeclarationHaveABody());
EXPECT_TRUE(To1->doesThisDeclarationHaveABody());
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ImportFunctions, OverriddenMethodsShouldBeImported) {
auto Code =
R"(
struct B { virtual void f(); };
void B::f() {}
struct D : B { void f(); };
)";
auto Pattern =
cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
Decl *FromTU = getTuDecl(Code, Lang_CXX03);
CXXMethodDecl *Proto =
FirstDeclMatcher<CXXMethodDecl>().match(FromTU, Pattern);
ASSERT_EQ(Proto->size_overridden_methods(), 1u);
CXXMethodDecl *To = cast<CXXMethodDecl>(Import(Proto, Lang_CXX03));
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_CXX03);
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_CXX03));
EXPECT_TRUE(To->isVirtual());
}
TEST_P(ImportFunctions,
ImportDefinitionIfThereIsAnExistingDefinitionAndFwdDecl) {
Decl *ToTU = getToTuDecl(
R"(
void f() {}
void f();
)",
Lang_CXX03);
ASSERT_EQ(1u,
DeclCounterWithPredicate<FunctionDecl>([](const FunctionDecl *FD) {
return FD->doesThisDeclarationHaveABody();
}).match(ToTU, functionDecl()));
Decl *FromTU = getTuDecl("void f() {}", Lang_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
Import(FromD, Lang_CXX03);
EXPECT_EQ(1u,
DeclCounterWithPredicate<FunctionDecl>([](const FunctionDecl *FD) {
return FD->doesThisDeclarationHaveABody();
}).match(ToTU, functionDecl()));
}
TEST_P(ImportFunctions, ImportOverriddenMethodTwice) {
auto Code =
R"(
struct B { virtual void f(); };
struct D:B { void f(); };
)";
auto BFP =
cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
auto DFP =
cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
Decl *FromTU0 = getTuDecl(Code, Lang_CXX03);
auto *DF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
Import(DF, Lang_CXX03);
Decl *FromTU1 = getTuDecl(Code, Lang_CXX03, "input1.cc");
auto *BF = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP);
Import(BF, Lang_CXX03);
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
}
TEST_P(ImportFunctions, ImportOverriddenMethodTwiceDefinitionFirst) {
auto CodeWithoutDef =
R"(
struct B { virtual void f(); };
struct D:B { void f(); };
)";
auto CodeWithDef =
R"(
struct B { virtual void f(){}; };
struct D:B { void f(){}; };
)";
auto BFP =
cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
auto DFP =
cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
auto BFDefP = cxxMethodDecl(
hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
auto DFDefP = cxxMethodDecl(
hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition());
auto FDefAllP = cxxMethodDecl(hasName("f"), isDefinition());
{
Decl *FromTU = getTuDecl(CodeWithDef, Lang_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, DFP);
Import(FromD, Lang_CXX03);
}
{
Decl *FromTU = getTuDecl(CodeWithoutDef, Lang_CXX03, "input1.cc");
auto *FromB = FirstDeclMatcher<CXXMethodDecl>().match(FromTU, BFP);
Import(FromB, Lang_CXX03);
}
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 1u);
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 1u);
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FDefAllP), 2u);
}
TEST_P(ImportFunctions, ImportOverriddenMethodTwiceOutOfClassDef) {
auto Code =
R"(
struct B { virtual void f(); };
struct D:B { void f(); };
void B::f(){};
)";
auto BFP =
cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
auto BFDefP = cxxMethodDecl(
hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
auto DFP = cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))),
unless(isDefinition()));
Decl *FromTU0 = getTuDecl(Code, Lang_CXX03);
auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
Import(D, Lang_CXX03);
Decl *FromTU1 = getTuDecl(Code, Lang_CXX03, "input1.cc");
auto *B = FirstDeclMatcher<CXXMethodDecl>().match(FromTU1, BFP);
Import(B, Lang_CXX03);
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u);
auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(hasName("B")));
auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP);
auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match(
ToTU, cxxMethodDecl(hasName("f"), isDefinition()));
// The definition should be out-of-class.
EXPECT_NE(ToBFInClass, ToBFOutOfClass);
EXPECT_NE(ToBFInClass->getLexicalDeclContext(),
ToBFOutOfClass->getLexicalDeclContext());
EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB);
EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU);
// Check that the redecl chain is intact.
EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass);
}
TEST_P(ImportFunctions,
ImportOverriddenMethodTwiceOutOfClassDefInSeparateCode) {
auto CodeTU0 =
R"(
struct B { virtual void f(); };
struct D:B { void f(); };
)";
auto CodeTU1 =
R"(
struct B { virtual void f(); };
struct D:B { void f(); };
void B::f(){}
void D::f(){}
void foo(B &b, D &d) { b.f(); d.f(); }
)";
auto BFP =
cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("B"))));
auto BFDefP = cxxMethodDecl(
hasName("f"), hasParent(cxxRecordDecl(hasName("B"))), isDefinition());
auto DFP =
cxxMethodDecl(hasName("f"), hasParent(cxxRecordDecl(hasName("D"))));
auto DFDefP = cxxMethodDecl(
hasName("f"), hasParent(cxxRecordDecl(hasName("D"))), isDefinition());
auto FooDef = functionDecl(hasName("foo"));
{
Decl *FromTU0 = getTuDecl(CodeTU0, Lang_CXX03, "input0.cc");
auto *D = FirstDeclMatcher<CXXMethodDecl>().match(FromTU0, DFP);
Import(D, Lang_CXX03);
}
{
Decl *FromTU1 = getTuDecl(CodeTU1, Lang_CXX03, "input1.cc");
auto *Foo = FirstDeclMatcher<FunctionDecl>().match(FromTU1, FooDef);
Import(Foo, Lang_CXX03);
}
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFP), 1u);
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFP), 1u);
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, BFDefP), 0u);
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, DFDefP), 0u);
auto *ToB = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(hasName("B")));
auto *ToD = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(hasName("D")));
auto *ToBFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, BFP);
auto *ToBFOutOfClass = FirstDeclMatcher<CXXMethodDecl>().match(
ToTU, cxxMethodDecl(hasName("f"), isDefinition()));
auto *ToDFInClass = FirstDeclMatcher<CXXMethodDecl>().match(ToTU, DFP);
auto *ToDFOutOfClass = LastDeclMatcher<CXXMethodDecl>().match(
ToTU, cxxMethodDecl(hasName("f"), isDefinition()));
// The definition should be out-of-class.
EXPECT_NE(ToBFInClass, ToBFOutOfClass);
EXPECT_NE(ToBFInClass->getLexicalDeclContext(),
ToBFOutOfClass->getLexicalDeclContext());
EXPECT_EQ(ToBFOutOfClass->getDeclContext(), ToB);
EXPECT_EQ(ToBFOutOfClass->getLexicalDeclContext(), ToTU);
EXPECT_NE(ToDFInClass, ToDFOutOfClass);
EXPECT_NE(ToDFInClass->getLexicalDeclContext(),
ToDFOutOfClass->getLexicalDeclContext());
EXPECT_EQ(ToDFOutOfClass->getDeclContext(), ToD);
EXPECT_EQ(ToDFOutOfClass->getLexicalDeclContext(), ToTU);
// Check that the redecl chain is intact.
EXPECT_EQ(ToBFOutOfClass->getPreviousDecl(), ToBFInClass);
EXPECT_EQ(ToDFOutOfClass->getPreviousDecl(), ToDFInClass);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportVariableChainInC) {
std::string Code = "static int v; static int v = 0;";
auto Pattern = varDecl(hasName("v"));
TranslationUnitDecl *FromTu = getTuDecl(Code, Lang_C99, "input0.c");
auto *From0 = FirstDeclMatcher<VarDecl>().match(FromTu, Pattern);
auto *From1 = LastDeclMatcher<VarDecl>().match(FromTu, Pattern);
auto *To0 = Import(From0, Lang_C99);
auto *To1 = Import(From1, Lang_C99);
EXPECT_TRUE(To0);
ASSERT_TRUE(To1);
EXPECT_NE(To0, To1);
EXPECT_EQ(To1->getPreviousDecl(), To0);
}
TEST_P(ImportFunctions, ImportFromDifferentScopedAnonNamespace) {
TranslationUnitDecl *FromTu =
getTuDecl("namespace NS0 { namespace { void f(); } }"
"namespace NS1 { namespace { void f(); } }",
Lang_CXX03, "input0.cc");
auto Pattern = functionDecl(hasName("f"));
auto *FromF0 = FirstDeclMatcher<FunctionDecl>().match(FromTu, Pattern);
auto *FromF1 = LastDeclMatcher<FunctionDecl>().match(FromTu, Pattern);
auto *ToF0 = Import(FromF0, Lang_CXX03);
auto *ToF1 = Import(FromF1, Lang_CXX03);
EXPECT_TRUE(ToF0);
ASSERT_TRUE(ToF1);
EXPECT_NE(ToF0, ToF1);
EXPECT_FALSE(ToF1->getPreviousDecl());
}
TEST_P(ImportFunctions, ImportFunctionFromUnnamedNamespace) {
{
Decl *FromTU = getTuDecl("namespace { void f() {} } void g0() { f(); }",
Lang_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("g0")));
Import(FromD, Lang_CXX03);
}
{
Decl *FromTU =
getTuDecl("namespace { void f() { int a; } } void g1() { f(); }",
Lang_CXX03, "input1.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("g1")));
Import(FromD, Lang_CXX03);
}
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))),
2u);
}
TEST_P(ImportFunctions, ImportImplicitFunctionsInLambda) {
Decl *FromTU = getTuDecl(
R"(
void foo() {
(void)[]() { ; };
}
)",
Lang_CXX11);
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("foo")));
auto *ToD = Import(FromD, Lang_CXX03);
EXPECT_TRUE(ToD);
CXXRecordDecl *LambdaRec =
cast<LambdaExpr>(cast<CStyleCastExpr>(
*cast<CompoundStmt>(ToD->getBody())->body_begin())
->getSubExpr())
->getLambdaClass();
EXPECT_TRUE(LambdaRec->getDestructor());
}
TEST_P(ImportFunctions,
CallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) {
Decl *FromTU = getTuDecl(
R"(
struct X {
template <typename T>
void foo(){}
};
void f() {
X x;
x.foo<int>();
}
)",
Lang_CXX03);
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
auto *ToD = Import(FromD, Lang_CXX03);
EXPECT_TRUE(ToD);
EXPECT_TRUE(MatchVerifier<FunctionDecl>().match(
ToD, functionDecl(hasName("f"), hasDescendant(declRefExpr()))));
}
TEST_P(ImportFunctions,
DependentCallExprOfMemberFunctionTemplateWithExplicitTemplateArgs) {
Decl *FromTU = getTuDecl(
R"(
struct X {
template <typename T>
void foo(){}
};
template <typename T>
void f() {
X x;
x.foo<T>();
}
void g() {
f<int>();
}
)",
Lang_CXX03);
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("g")));
auto *ToD = Import(FromD, Lang_CXX03);
EXPECT_TRUE(ToD);
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_TRUE(MatchVerifier<TranslationUnitDecl>().match(
ToTU, translationUnitDecl(hasDescendant(
functionDecl(hasName("f"), hasDescendant(declRefExpr()))))));
}
struct ImportFunctionTemplates : ASTImporterOptionSpecificTestBase {};
TEST_P(ImportFunctionTemplates, ImportFunctionTemplateInRecordDeclTwice) {
auto Code =
R"(
class X {
template <class T>
void f(T t);
};
)";
Decl *FromTU1 = getTuDecl(Code, Lang_CXX03, "input1.cc");
auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU1, functionTemplateDecl(hasName("f")));
auto *ToD1 = Import(FromD1, Lang_CXX03);
Decl *FromTU2 = getTuDecl(Code, Lang_CXX03, "input2.cc");
auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU2, functionTemplateDecl(hasName("f")));
auto *ToD2 = Import(FromD2, Lang_CXX03);
EXPECT_EQ(ToD1, ToD2);
}
TEST_P(ImportFunctionTemplates,
ImportFunctionTemplateWithDefInRecordDeclTwice) {
auto Code =
R"(
class X {
template <class T>
void f(T t);
};
template <class T>
void X::f(T t) {};
)";
Decl *FromTU1 = getTuDecl(Code, Lang_CXX03, "input1.cc");
auto *FromD1 = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU1, functionTemplateDecl(hasName("f")));
auto *ToD1 = Import(FromD1, Lang_CXX03);
Decl *FromTU2 = getTuDecl(Code, Lang_CXX03, "input2.cc");
auto *FromD2 = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU2, functionTemplateDecl(hasName("f")));
auto *ToD2 = Import(FromD2, Lang_CXX03);
EXPECT_EQ(ToD1, ToD2);
}
TEST_P(ImportFunctionTemplates,
ImportFunctionWhenThereIsAFunTemplateWithSameName) {
getToTuDecl(
R"(
template <typename T>
void foo(T) {}
void foo();
)",
Lang_CXX03);
Decl *FromTU = getTuDecl("void foo();", Lang_CXX03);
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("foo")));
auto *ImportedD = Import(FromD, Lang_CXX03);
EXPECT_TRUE(ImportedD);
}
TEST_P(ImportFunctionTemplates,
ImportConstructorWhenThereIsAFunTemplateWithSameName) {
auto Code =
R"(
struct Foo {
template <typename T>
Foo(T) {}
Foo();
};
)";
getToTuDecl(Code, Lang_CXX03);
Decl *FromTU = getTuDecl(Code, Lang_CXX03);
auto *FromD =
LastDeclMatcher<CXXConstructorDecl>().match(FromTU, cxxConstructorDecl());
auto *ImportedD = Import(FromD, Lang_CXX03);
EXPECT_TRUE(ImportedD);
}
TEST_P(ImportFunctionTemplates,
ImportOperatorWhenThereIsAFunTemplateWithSameName) {
getToTuDecl(
R"(
template <typename T>
void operator<(T,T) {}
struct X{};
void operator<(X, X);
)",
Lang_CXX03);
Decl *FromTU = getTuDecl(
R"(
struct X{};
void operator<(X, X);
)",
Lang_CXX03);
auto *FromD = LastDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasOverloadedOperatorName("<")));
auto *ImportedD = Import(FromD, Lang_CXX03);
EXPECT_TRUE(ImportedD);
}
struct ImportFriendFunctions : ImportFunctions {};
TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainProto) {
auto Pattern = functionDecl(hasName("f"));
Decl *FromTU = getTuDecl("struct X { friend void f(); };"
"void f();",
Lang_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03));
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_CXX03, "input0.cc");
auto FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03));
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_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03));
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_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03));
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
EXPECT_FALSE(ImportedD->doesThisDeclarationHaveABody());
auto *ToFD = LastDeclMatcher<FunctionDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ToFD->doesThisDeclarationHaveABody());
EXPECT_EQ(ToFD->getPreviousDecl(), ImportedD);
}
TEST_P(ImportFriendFunctions, ImportFriendFunctionRedeclChainDefWithClass) {
auto Pattern = functionDecl(hasName("f"));
Decl *FromTU = getTuDecl(
R"(
class X;
void f(X *x){}
class X{
friend void f(X *x);
};
)",
Lang_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03));
Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
ASSERT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 2u);
EXPECT_TRUE(ImportedD->doesThisDeclarationHaveABody());
auto *InClassFD = cast<FunctionDecl>(FirstDeclMatcher<FriendDecl>()
.match(ToTU, friendDecl())
->getFriendDecl());
EXPECT_FALSE(InClassFD->doesThisDeclarationHaveABody());
EXPECT_EQ(InClassFD->getPreviousDecl(), ImportedD);
// The parameters must refer the same type
EXPECT_EQ((*InClassFD->param_begin())->getOriginalType(),
(*ImportedD->param_begin())->getOriginalType());
}
TEST_P(ImportFriendFunctions,
ImportFriendFunctionRedeclChainDefWithClass_ImportTheProto) {
auto Pattern = functionDecl(hasName("f"));
Decl *FromTU = getTuDecl(
R"(
class X;
void f(X *x){}
class X{
friend void f(X *x);
};
)",
Lang_CXX03, "input0.cc");
auto *FromD = LastDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
auto *ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03));
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_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD = cast<FunctionDecl>(Import(FromD, Lang_CXX03));
}
FunctionDecl *ImportedD1;
{
Decl *FromTU = getTuDecl("void f();", Lang_CXX03, "input1.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU, Pattern);
ImportedD1 = cast<FunctionDecl>(Import(FromD, Lang_CXX03));
}
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_CXX03, "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_TRUE(LookupRes.empty());
LookupRes = FromTU->noload_lookup(FromName);
ASSERT_TRUE(LookupRes.isSingleResult());
}
auto *ToD = cast<FunctionDecl>(Import(FromD, Lang_CXX03));
auto ToName = ToD->getDeclName();
TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
auto *Class = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
auto LookupRes = Class->noload_lookup(ToName);
EXPECT_TRUE(LookupRes.empty());
LookupRes = ToTU->noload_lookup(ToName);
EXPECT_TRUE(LookupRes.isSingleResult());
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, FunctionPattern), 1u);
auto *To0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, FunctionPattern);
EXPECT_TRUE(To0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend));
EXPECT_FALSE(To0->isInIdentifierNamespace(Decl::IDNS_Ordinary));
}
TEST_P(ImportFriendFunctions, LookupWithProtoAfter) {
auto FunctionPattern = functionDecl(hasName("f"));
auto ClassPattern = cxxRecordDecl(hasName("X"));
TranslationUnitDecl *FromTU =
getTuDecl("struct X { friend void f(); };"
// This proto decl makes f available to normal
// lookup, otherwise it is hidden.
// Normal C++ lookup (implemented in
// `clang::Sema::CppLookupName()` and in `LookupDirect()`)
// returns the found `NamedDecl` only if the set IDNS is matched
"void f();",
Lang_CXX03, "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_TRUE(LookupRes.empty());
LookupRes = FromTU->noload_lookup(FromName);
ASSERT_TRUE(LookupRes.isSingleResult());
auto *ToFriend = cast<FunctionDecl>(Import(FromFriend, Lang_CXX03));
auto ToName = ToFriend->getDeclName();
TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
LookupRes = ToClass->noload_lookup(ToName);
EXPECT_TRUE(LookupRes.empty());
LookupRes = ToTU->noload_lookup(ToName);
// Test is disabled because this result is 2.
EXPECT_TRUE(LookupRes.isSingleResult());
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_CXX03, "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_TRUE(LookupRes.empty());
LookupRes = FromTU->noload_lookup(FromName);
ASSERT_TRUE(LookupRes.isSingleResult());
auto *ToNormal = cast<FunctionDecl>(Import(FromNormal, Lang_CXX03));
auto ToName = ToNormal->getDeclName();
TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, ClassPattern);
LookupRes = ToClass->noload_lookup(ToName);
EXPECT_TRUE(LookupRes.empty());
LookupRes = ToTU->noload_lookup(ToName);
EXPECT_TRUE(LookupRes.isSingleResult());
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_CXX03, "input0.cc");
auto *FromNormalF =
FirstDeclMatcher<FunctionDecl>().match(FromNormalTU, Pattern);
TranslationUnitDecl *FromFriendTU =
getTuDecl("class X { friend void f(); };", Lang_CXX03, "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_TRUE(LookupRes.isSingleResult());
LookupRes = FromFriendTU->noload_lookup(FromFriendName);
ASSERT_TRUE(LookupRes.isSingleResult());
auto *ToNormalF = cast<FunctionDecl>(Import(FromNormalF, Lang_CXX03));
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_TRUE(LookupRes.isSingleResult());
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, Pattern), 1u);
auto *ToFriendF = cast<FunctionDecl>(Import(FromFriendF, Lang_CXX03));
LookupRes = ToTU->noload_lookup(ToName);
EXPECT_TRUE(LookupRes.isSingleResult());
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_CXX03, "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_CXX03);
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_C99}},
{"foo.c",
{"typedef enum { THING_VALUE } thing_t;"
"void conflict(thing_t type);"
"void foo() { (void)THING_VALUE; }"
"void conflict(thing_t type) {}",
Lang_C99}},
{"moo.c",
{"typedef enum { THING_VALUE } thing_t;"
"void conflict(thing_t type);"
"void moo() { conflict(THING_VALUE); }",
Lang_C99}}};
auto VerificationMatcher =
enumDecl(has(enumConstantDecl(hasName("THING_VALUE"))),
hasTypedefForAnonDecl(hasName("thing_t")));
ImportAction ImportFoo{"foo.c", "main.c", functionDecl(hasName("foo"))},
ImportMoo{"moo.c", "main.c", functionDecl(hasName("moo"))};
testImportSequence(
Samples, {ImportFoo, ImportMoo}, // "foo", them "moo".
// Just check that there is only one enum decl in the result AST.
"main.c", enumDecl(), VerificationMatcher);
// For different import order, result should be the same.
testImportSequence(
Samples, {ImportMoo, ImportFoo}, // "moo", them "foo".
// Check that there is only one enum decl in the result AST.
"main.c", enumDecl(), VerificationMatcher);
}
TEST_P(ImportDecl, ImportFieldOrder) {
MatchVerifier<Decl> Verifier;
testImport("struct declToImport {"
" int b = a + 2;"
" int a = 5;"
"};",
Lang_CXX11, "", Lang_CXX11, Verifier,
recordDecl(hasFieldOrder({"b", "a"})));
}
const internal::VariadicDynCastAllOfMatcher<Expr, DependentScopeDeclRefExpr>
dependentScopeDeclRefExpr;
TEST_P(ImportExpr, DependentScopeDeclRefExpr) {
MatchVerifier<Decl> Verifier;
testImport("template <typename T> struct S { static T foo; };"
"template <typename T> void declToImport() {"
" (void) S<T>::foo;"
"}"
"void instantiate() { declToImport<int>(); }"
"template <typename T> T S<T>::foo;",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionTemplateDecl(has(functionDecl(has(compoundStmt(
has(cStyleCastExpr(has(dependentScopeDeclRefExpr())))))))));
testImport("template <typename T> struct S {"
"template<typename S> static void foo(){};"
"};"
"template <typename T> void declToImport() {"
" S<T>::template foo<T>();"
"}"
"void instantiate() { declToImport<int>(); }",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionTemplateDecl(has(functionDecl(has(compoundStmt(
has(callExpr(has(dependentScopeDeclRefExpr())))))))));
}
const internal::VariadicDynCastAllOfMatcher<Type, DependentNameType>
dependentNameType;
TEST_P(ImportExpr, DependentNameType) {
MatchVerifier<Decl> Verifier;
testImport("template <typename T> struct declToImport {"
" typedef typename T::type dependent_name;"
"};",
Lang_CXX11, "", Lang_CXX11, Verifier,
classTemplateDecl(has(
cxxRecordDecl(has(typedefDecl(has(dependentNameType())))))));
}
TEST_P(ImportExpr, UnresolvedMemberExpr) {
MatchVerifier<Decl> Verifier;
testImport("struct S { template <typename T> void mem(); };"
"template <typename U> void declToImport() {"
" S s;"
" s.mem<U>();"
"}"
"void instantiate() { declToImport<int>(); }",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionTemplateDecl(has(functionDecl(has(
compoundStmt(has(callExpr(has(unresolvedMemberExpr())))))))));
}
class ImportImplicitMethods : public ASTImporterOptionSpecificTestBase {
public:
static constexpr auto DefaultCode = R"(
struct A { int x; };
void f() {
A a;
A a1(a);
A a2(A{});
a = a1;
a = A{};
a.~A();
})";
template <typename MatcherType>
void testImportOf(
const MatcherType &MethodMatcher, const char *Code = DefaultCode) {
test(MethodMatcher, Code, /*ExpectedCount=*/1u);
}
template <typename MatcherType>
void testNoImportOf(
const MatcherType &MethodMatcher, const char *Code = DefaultCode) {
test(MethodMatcher, Code, /*ExpectedCount=*/0u);
}
private:
template <typename MatcherType>
void test(const MatcherType &MethodMatcher,
const char *Code, unsigned int ExpectedCount) {
auto ClassMatcher = cxxRecordDecl(unless(isImplicit()));
Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
auto *ToClass = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, ClassMatcher);
ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 1u);
{
CXXMethodDecl *Method =
FirstDeclMatcher<CXXMethodDecl>().match(ToClass, MethodMatcher);
ToClass->removeDecl(Method);
SharedStatePtr->getLookupTable()->remove(Method);
}
ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 0u);
Decl *ImportedClass = nullptr;
{
Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input1.cc");
auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, ClassMatcher);
ImportedClass = Import(FromClass, Lang_CXX11);
}
EXPECT_EQ(ToClass, ImportedClass);
EXPECT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher),
ExpectedCount);
}
};
TEST_P(ImportImplicitMethods, DefaultConstructor) {
testImportOf(cxxConstructorDecl(isDefaultConstructor()));
}
TEST_P(ImportImplicitMethods, CopyConstructor) {
testImportOf(cxxConstructorDecl(isCopyConstructor()));
}
TEST_P(ImportImplicitMethods, MoveConstructor) {
testImportOf(cxxConstructorDecl(isMoveConstructor()));
}
TEST_P(ImportImplicitMethods, Destructor) {
testImportOf(cxxDestructorDecl());
}
TEST_P(ImportImplicitMethods, CopyAssignment) {
testImportOf(cxxMethodDecl(isCopyAssignmentOperator()));
}
TEST_P(ImportImplicitMethods, MoveAssignment) {
testImportOf(cxxMethodDecl(isMoveAssignmentOperator()));
}
TEST_P(ImportImplicitMethods, DoNotImportUserProvided) {
auto Code = R"(
struct A { A() { int x; } };
)";
testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code);
}
TEST_P(ImportImplicitMethods, DoNotImportDefault) {
auto Code = R"(
struct A { A() = default; };
)";
testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code);
}
TEST_P(ImportImplicitMethods, DoNotImportDeleted) {
auto Code = R"(
struct A { A() = delete; };
)";
testNoImportOf(cxxConstructorDecl(isDefaultConstructor()), Code);
}
TEST_P(ImportImplicitMethods, DoNotImportOtherMethod) {
auto Code = R"(
struct A { void f() { } };
)";
testNoImportOf(cxxMethodDecl(hasName("f")), Code);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentRecord) {
Decl *ToR1;
{
Decl *FromTU = getTuDecl("struct A { };", Lang_CXX03, "input0.cc");
auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
ToR1 = Import(FromR, Lang_CXX03);
}
Decl *ToR2;
{
Decl *FromTU = getTuDecl("struct A { };", Lang_CXX03, "input1.cc");
auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
ToR2 = Import(FromR, Lang_CXX03);
}
EXPECT_EQ(ToR1, ToR2);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentRecord) {
Decl *ToR1;
{
Decl *FromTU = getTuDecl("struct A { int x; };", Lang_CXX03, "input0.cc");
auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
ToR1 = Import(FromR, Lang_CXX03);
}
Decl *ToR2;
{
Decl *FromTU =
getTuDecl("struct A { unsigned x; };", Lang_CXX03, "input1.cc");
auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
ToR2 = Import(FromR, Lang_CXX03);
}
EXPECT_NE(ToR1, ToR2);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentField) {
Decl *ToF1;
{
Decl *FromTU = getTuDecl("struct A { int x; };", Lang_CXX03, "input0.cc");
auto *FromF = FirstDeclMatcher<FieldDecl>().match(
FromTU, fieldDecl(hasName("x")));
ToF1 = Import(FromF, Lang_CXX03);
}
Decl *ToF2;
{
Decl *FromTU = getTuDecl("struct A { int x; };", Lang_CXX03, "input1.cc");
auto *FromF = FirstDeclMatcher<FieldDecl>().match(
FromTU, fieldDecl(hasName("x")));
ToF2 = Import(FromF, Lang_CXX03);
}
EXPECT_EQ(ToF1, ToF2);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportBitfields) {
Decl *FromTU = getTuDecl("struct A { unsigned x : 3; };", Lang_CXX03);
auto *FromF =
FirstDeclMatcher<FieldDecl>().match(FromTU, fieldDecl(hasName("x")));
ASSERT_TRUE(FromF->isBitField());
ASSERT_EQ(3u, FromF->getBitWidthValue(FromTU->getASTContext()));
auto *ToField = Import(FromF, Lang_CXX03);
auto *ToTU = ToField->getTranslationUnitDecl();
EXPECT_TRUE(ToField->isBitField());
EXPECT_EQ(3u, ToField->getBitWidthValue(ToTU->getASTContext()));
const auto *FromBT = FromF->getBitWidth()->getType()->getAs<BuiltinType>();
const auto *ToBT = ToField->getBitWidth()->getType()->getAs<BuiltinType>();
ASSERT_TRUE(FromBT);
ASSERT_EQ(BuiltinType::Int, FromBT->getKind());
EXPECT_TRUE(ToBT);
EXPECT_EQ(BuiltinType::Int, ToBT->getKind());
}
struct ImportBlock : ASTImporterOptionSpecificTestBase {};
TEST_P(ImportBlock, ImportBlocksAreUnsupported) {
const auto *Code = R"(
void test_block__capture_null() {
int *p = 0;
^(){
*p = 1;
}();
})";
Decl *FromTU = getTuDecl(Code, Lang_CXX03);
auto *FromBlock = FirstDeclMatcher<BlockDecl>().match(FromTU, blockDecl());
ASSERT_TRUE(FromBlock);
auto ToBlockOrError = importOrError(FromBlock, Lang_CXX03);
const auto ExpectUnsupportedConstructError = [](const ImportError &Error) {
EXPECT_EQ(ImportError::UnsupportedConstruct, Error.Error);
};
llvm::handleAllErrors(ToBlockOrError.takeError(),
ExpectUnsupportedConstructError);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportParmVarDecl) {
const auto *Code = R"(
template <typename T> struct Wrapper {
Wrapper(T Value = {}) {}
};
template class Wrapper<int>;
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromVar = FirstDeclMatcher<ParmVarDecl>().match(
FromTU, parmVarDecl(hasType(asString("int"))));
ASSERT_TRUE(FromVar);
ASSERT_TRUE(FromVar->hasUninstantiatedDefaultArg());
ASSERT_TRUE(FromVar->getUninstantiatedDefaultArg());
const auto *ToVar = Import(FromVar, Lang_CXX11);
EXPECT_TRUE(ToVar);
EXPECT_TRUE(ToVar->hasUninstantiatedDefaultArg());
EXPECT_TRUE(ToVar->getUninstantiatedDefaultArg());
EXPECT_NE(FromVar->getUninstantiatedDefaultArg(),
ToVar->getUninstantiatedDefaultArg());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentField) {
Decl *ToF1;
{
Decl *FromTU = getTuDecl("struct A { int x; };", Lang_CXX03, "input0.cc");
auto *FromF = FirstDeclMatcher<FieldDecl>().match(
FromTU, fieldDecl(hasName("x")));
ToF1 = Import(FromF, Lang_CXX03);
}
Decl *ToF2;
{
Decl *FromTU =
getTuDecl("struct A { unsigned x; };", Lang_CXX03, "input1.cc");
auto *FromF = FirstDeclMatcher<FieldDecl>().match(
FromTU, fieldDecl(hasName("x")));
ToF2 = Import(FromF, Lang_CXX03);
}
EXPECT_NE(ToF1, ToF2);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportOfEquivalentMethod) {
Decl *ToM1;
{
Decl *FromTU = getTuDecl("struct A { void x(); }; void A::x() { }",
Lang_CXX03, "input0.cc");
auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("x"), isDefinition()));
ToM1 = Import(FromM, Lang_CXX03);
}
Decl *ToM2;
{
Decl *FromTU = getTuDecl("struct A { void x(); }; void A::x() { }",
Lang_CXX03, "input1.cc");
auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("x"), isDefinition()));
ToM2 = Import(FromM, Lang_CXX03);
}
EXPECT_EQ(ToM1, ToM2);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportOfNonEquivalentMethod) {
Decl *ToM1;
{
Decl *FromTU = getTuDecl("struct A { void x(); }; void A::x() { }",
Lang_CXX03, "input0.cc");
auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("x"), isDefinition()));
ToM1 = Import(FromM, Lang_CXX03);
}
Decl *ToM2;
{
Decl *FromTU =
getTuDecl("struct A { void x() const; }; void A::x() const { }",
Lang_CXX03, "input1.cc");
auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("x"), isDefinition()));
ToM2 = Import(FromM, Lang_CXX03);
}
EXPECT_NE(ToM1, ToM2);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportUnnamedStructsWithRecursingField) {
Decl *FromTU = getTuDecl(
R"(
struct A {
struct {
struct A *next;
} entry0;
struct {
struct A *next;
} entry1;
};
)",
Lang_C99, "input0.cc");
auto *From =
FirstDeclMatcher<RecordDecl>().match(FromTU, recordDecl(hasName("A")));
Import(From, Lang_C99);
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
auto *Entry0 =
FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry0")));
auto *Entry1 =
FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry1")));
auto *R0 = getRecordDecl(Entry0);
auto *R1 = getRecordDecl(Entry1);
EXPECT_NE(R0, R1);
EXPECT_TRUE(MatchVerifier<RecordDecl>().match(
R0, recordDecl(has(fieldDecl(hasName("next"))))));
EXPECT_TRUE(MatchVerifier<RecordDecl>().match(
R1, recordDecl(has(fieldDecl(hasName("next"))))));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportUnnamedFieldsInCorrectOrder) {
Decl *FromTU = getTuDecl(
R"(
void f(int X, int Y, bool Z) {
(void)[X, Y, Z] { (void)Z; };
}
)",
Lang_CXX11, "input0.cc");
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
auto *ToF = cast_or_null<FunctionDecl>(Import(FromF, Lang_CXX11));
EXPECT_TRUE(ToF);
CXXRecordDecl *FromLambda =
cast<LambdaExpr>(cast<CStyleCastExpr>(cast<CompoundStmt>(
FromF->getBody())->body_front())->getSubExpr())->getLambdaClass();
auto *ToLambda = cast_or_null<CXXRecordDecl>(Import(FromLambda, Lang_CXX11));
EXPECT_TRUE(ToLambda);
// Check if the fields of the lambda class are imported in correct order.
unsigned FromIndex = 0u;
for (auto *FromField : FromLambda->fields()) {
ASSERT_FALSE(FromField->getDeclName());
auto *ToField = cast_or_null<FieldDecl>(Import(FromField, Lang_CXX11));
EXPECT_TRUE(ToField);
Optional<unsigned> ToIndex = ASTImporter::getFieldIndex(ToField);
EXPECT_TRUE(ToIndex);
EXPECT_EQ(*ToIndex, FromIndex);
++FromIndex;
}
EXPECT_EQ(FromIndex, 3u);
}
TEST_P(ASTImporterOptionSpecificTestBase,
MergeFieldDeclsOfClassTemplateSpecialization) {
std::string ClassTemplate =
R"(
template <typename T>
struct X {
int a{0}; // FieldDecl with InitListExpr
X(char) : a(3) {} // (1)
X(int) {} // (2)
};
)";
Decl *ToTU = getToTuDecl(ClassTemplate +
R"(
void foo() {
// ClassTemplateSpec with ctor (1): FieldDecl without InitlistExpr
X<char> xc('c');
}
)", Lang_CXX11);
auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl(hasName("X")));
// FieldDecl without InitlistExpr:
auto *ToField = *ToSpec->field_begin();
ASSERT_TRUE(ToField);
ASSERT_FALSE(ToField->getInClassInitializer());
Decl *FromTU = getTuDecl(ClassTemplate +
R"(
void bar() {
// ClassTemplateSpec with ctor (2): FieldDecl WITH InitlistExpr
X<char> xc(1);
}
)", Lang_CXX11);
auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl(hasName("X")));
// FieldDecl with InitlistExpr:
auto *FromField = *FromSpec->field_begin();
ASSERT_TRUE(FromField);
ASSERT_TRUE(FromField->getInClassInitializer());
auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
ASSERT_TRUE(ImportedSpec);
EXPECT_EQ(ImportedSpec, ToSpec);
// After the import, the FieldDecl has to be merged, thus it should have the
// InitListExpr.
EXPECT_TRUE(ToField->getInClassInitializer());
}
TEST_P(ASTImporterOptionSpecificTestBase,
MergeFunctionOfClassTemplateSpecialization) {
std::string ClassTemplate =
R"(
template <typename T>
struct X {
void f() {}
void g() {}
};
)";
Decl *ToTU = getToTuDecl(ClassTemplate +
R"(
void foo() {
X<char> x;
x.f();
}
)", Lang_CXX11);
Decl *FromTU = getTuDecl(ClassTemplate +
R"(
void bar() {
X<char> x;
x.g();
}
)", Lang_CXX11);
auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl(hasName("X")));
auto FunPattern = functionDecl(hasName("g"),
hasParent(classTemplateSpecializationDecl()));
auto *FromFun =
FirstDeclMatcher<FunctionDecl>().match(FromTU, FunPattern);
auto *ToFun =
FirstDeclMatcher<FunctionDecl>().match(ToTU, FunPattern);
ASSERT_TRUE(FromFun->hasBody());
ASSERT_FALSE(ToFun->hasBody());
auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
ASSERT_TRUE(ImportedSpec);
auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl(hasName("X")));
EXPECT_EQ(ImportedSpec, ToSpec);
EXPECT_TRUE(ToFun->hasBody());
}
TEST_P(ASTImporterOptionSpecificTestBase, MergeTemplateSpecWithForwardDecl) {
std::string ClassTemplate =
R"(
template<typename T>
struct X { int m; };
template<>
struct X<int> { int m; };
)";
// Append a forward decl for our template specialization.
getToTuDecl(ClassTemplate + "template<> struct X<int>;", Lang_CXX11);
Decl *FromTU = getTuDecl(ClassTemplate, Lang_CXX11);
auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl(hasName("X"), isDefinition()));
auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
// Check that our definition got merged with the existing definition.
EXPECT_TRUE(FromSpec->isThisDeclarationADefinition());
EXPECT_TRUE(ImportedSpec->isThisDeclarationADefinition());
}
TEST_P(ASTImporterOptionSpecificTestBase,
ODRViolationOfClassTemplateSpecializationsShouldBeReported) {
std::string ClassTemplate =
R"(
template <typename T>
struct X {};
)";
Decl *ToTU = getToTuDecl(ClassTemplate +
R"(
template <>
struct X<char> {
int a;
};
void foo() {
X<char> x;
}
)",
Lang_CXX11);
Decl *FromTU = getTuDecl(ClassTemplate +
R"(
template <>
struct X<char> {
int b;
};
void foo() {
X<char> x;
}
)",
Lang_CXX11);
auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl(hasName("X")));
auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
// We expect one (ODR) warning during the import.
EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
// The second specialization is different from the first, thus it violates
// ODR, consequently we expect to keep the first specialization only, which is
// already in the "To" context.
EXPECT_FALSE(ImportedSpec);
EXPECT_EQ(1u,
DeclCounter<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl(hasName("X"))));
}
TEST_P(ASTImporterOptionSpecificTestBase,
MergeCtorOfClassTemplateSpecialization) {
std::string ClassTemplate =
R"(
template <typename T>
struct X {
X(char) {}
X(int) {}
};
)";
Decl *ToTU = getToTuDecl(ClassTemplate +
R"(
void foo() {
X<char> x('c');
}
)", Lang_CXX11);
Decl *FromTU = getTuDecl(ClassTemplate +
R"(
void bar() {
X<char> x(1);
}
)", Lang_CXX11);
auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl(hasName("X")));
// Match the void(int) ctor.
auto CtorPattern =
cxxConstructorDecl(hasParameter(0, varDecl(hasType(asString("int")))),
hasParent(classTemplateSpecializationDecl()));
auto *FromCtor =
FirstDeclMatcher<CXXConstructorDecl>().match(FromTU, CtorPattern);
auto *ToCtor =
FirstDeclMatcher<CXXConstructorDecl>().match(ToTU, CtorPattern);
ASSERT_TRUE(FromCtor->hasBody());
ASSERT_FALSE(ToCtor->hasBody());
auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
ASSERT_TRUE(ImportedSpec);
auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl(hasName("X")));
EXPECT_EQ(ImportedSpec, ToSpec);
EXPECT_TRUE(ToCtor->hasBody());
}
TEST_P(ASTImporterOptionSpecificTestBase, ClassTemplateFriendDecl) {
const auto *Code =
R"(
template <class T> class X { friend T; };
struct Y {};
template class X<Y>;
)";
Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl());
auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl());
auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
EXPECT_EQ(ImportedSpec, ToSpec);
EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl()));
}
TEST_P(ASTImporterOptionSpecificTestBase,
ClassTemplatePartialSpecializationsShouldNotBeDuplicated) {
auto Code =
R"(
// primary template
template<class T1, class T2, int I>
class A {};
// partial specialization
template<class T, int I>
class A<T, T*, I> {};
)";
Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromSpec =
FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
FromTU, classTemplatePartialSpecializationDecl());
auto *ToSpec =
FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
ToTU, classTemplatePartialSpecializationDecl());
auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
EXPECT_EQ(ImportedSpec, ToSpec);
EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match(
ToTU, classTemplatePartialSpecializationDecl()));
}
TEST_P(ASTImporterOptionSpecificTestBase,
ClassTemplateSpecializationsShouldNotBeDuplicated) {
auto Code =
R"(
// primary template
template<class T1, class T2, int I>
class A {};
// full specialization
template<>
class A<int, int, 1> {};
)";
Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl());
auto *ToSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl());
auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
EXPECT_EQ(ImportedSpec, ToSpec);
EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl()));
}
TEST_P(ASTImporterOptionSpecificTestBase,
ClassTemplateFullAndPartialSpecsShouldNotBeMixed) {
std::string PrimaryTemplate =
R"(
template<class T1, class T2, int I>
class A {};
)";
auto PartialSpec =
R"(
template<class T, int I>
class A<T, T*, I> {};
)";
auto FullSpec =
R"(
template<>
class A<int, int, 1> {};
)";
Decl *ToTU = getToTuDecl(PrimaryTemplate + FullSpec, Lang_CXX11);
Decl *FromTU = getTuDecl(PrimaryTemplate + PartialSpec, Lang_CXX11);
auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl());
auto *ImportedSpec = Import(FromSpec, Lang_CXX11);
EXPECT_TRUE(ImportedSpec);
// Check the number of partial specializations.
EXPECT_EQ(1u, DeclCounter<ClassTemplatePartialSpecializationDecl>().match(
ToTU, classTemplatePartialSpecializationDecl()));
// Check the number of full specializations.
EXPECT_EQ(1u, DeclCounter<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl(
unless(classTemplatePartialSpecializationDecl()))));
}
TEST_P(ASTImporterOptionSpecificTestBase,
InitListExprValueKindShouldBeImported) {
Decl *TU = getTuDecl(
R"(
const int &init();
void foo() { const int &a{init()}; }
)", Lang_CXX11, "input0.cc");
auto *FromD = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("a")));
ASSERT_TRUE(FromD->getAnyInitializer());
auto *InitExpr = FromD->getAnyInitializer();
ASSERT_TRUE(InitExpr);
ASSERT_TRUE(InitExpr->isGLValue());
auto *ToD = Import(FromD, Lang_CXX11);
EXPECT_TRUE(ToD);
auto *ToInitExpr = cast<VarDecl>(ToD)->getAnyInitializer();
EXPECT_TRUE(ToInitExpr);
EXPECT_TRUE(ToInitExpr->isGLValue());
}
struct ImportVariables : ASTImporterOptionSpecificTestBase {};
TEST_P(ImportVariables, ImportOfOneDeclBringsInTheWholeChain) {
Decl *FromTU = getTuDecl(
R"(
struct A {
static const int a = 1 + 2;
};
const int A::a;
)",
Lang_CXX03, "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_CXX03);
Decl *FromTU = getTuDecl(std::string(StructA) + "const int A::a;", Lang_CXX03,
"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_CXX03);
Decl *FromTU = getTuDecl(std::string(StructA) + "const int A::a = 1 + 2;",
Lang_CXX03, "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());
}
TEST_P(ImportVariables, ImportBindingDecl) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
void declToImport() {
int a[2] = {1,2};
auto [x1,y1] = a;
auto& [x2,y2] = a;
struct S {
mutable int x1 : 2;
volatile double y1;
};
S b;
const auto [x3, y3] = b;
};
)",
Lang_CXX17, "", Lang_CXX17);
TranslationUnitDecl *FromTU = From->getTranslationUnitDecl();
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("declToImport")));
auto *ToF = Import(FromF, Lang_CXX17);
EXPECT_TRUE(ToF);
auto VerifyImport = [&](llvm::StringRef BindName) {
auto *FromB = FirstDeclMatcher<BindingDecl>().match(
FromF, bindingDecl(hasName(BindName)));
ASSERT_TRUE(FromB);
auto *ToB = Import(FromB, Lang_CXX17);
EXPECT_TRUE(ToB);
EXPECT_EQ(FromB->getBinding() != nullptr, ToB->getBinding() != nullptr);
EXPECT_EQ(FromB->getDecomposedDecl() != nullptr,
ToB->getDecomposedDecl() != nullptr);
EXPECT_EQ(FromB->getHoldingVar() != nullptr,
ToB->getHoldingVar() != nullptr);
};
VerifyImport("x1");
VerifyImport("y1");
VerifyImport("x2");
VerifyImport("y2");
VerifyImport("x3");
VerifyImport("y3");
}
TEST_P(ImportVariables, ImportDecompositionDeclArray) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
void declToImport() {
int a[2] = {1,2};
auto [x1,y1] = a;
};
)",
Lang_CXX17, "", Lang_CXX17);
TranslationUnitDecl *FromTU = From->getTranslationUnitDecl();
auto *FromDecomp =
FirstDeclMatcher<DecompositionDecl>().match(FromTU, decompositionDecl());
auto *ToDecomp = Import(FromDecomp, Lang_CXX17);
EXPECT_TRUE(ToDecomp);
ArrayRef<BindingDecl *> FromB = FromDecomp->bindings();
ArrayRef<BindingDecl *> ToB = ToDecomp->bindings();
EXPECT_EQ(FromB.size(), ToB.size());
for (unsigned int I = 0; I < FromB.size(); ++I) {
auto *ToBI = Import(FromB[I], Lang_CXX17);
EXPECT_EQ(ToBI, ToB[I]);
}
}
struct ImportClasses : ASTImporterOptionSpecificTestBase {};
TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContext) {
Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_C99);
Decl *FromTU1 = getTuDecl("struct X {};", Lang_C99, "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_C99);
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_CXX03);
Decl *FromTU1 = getTuDecl("struct X {};", Lang_CXX03, "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_CXX03);
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_C99, "input0.cc");
Decl *FromTU1 = getTuDecl("struct X {};", Lang_C99, "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_C99);
Decl *ImportedDef = Import(FromDef, Lang_C99);
Decl *ToTU = ImportedDef->getTranslationUnitDecl();
EXPECT_NE(ImportedDef, ImportedProto);
EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
EXPECT_TRUE(ImportedDef == ToDef);
EXPECT_TRUE(ImportedProto == ToProto);
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
}
struct ImportFriendClasses : ASTImporterOptionSpecificTestBase {};
TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) {
Decl *FromTU = getTuDecl(
R"(
class A {
template <int I> class F {};
class X {
template <int I> friend class F;
};
};
)",
Lang_CXX03, "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_CXX03));
auto *ToFriendClass =
cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX03));
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_CXX03, "input.cc");
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
FromTu, cxxRecordDecl(hasName("declToImport")));
auto *ToD = Import(FromD, Lang_CXX03);
auto Pattern = cxxRecordDecl(has(friendDecl()));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
}
TEST_P(ImportFriendClasses, UndeclaredFriendClassShouldNotBeVisible) {
Decl *FromTu =
getTuDecl("class X { friend class Y; };", Lang_CXX03, "from.cc");
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
FromTu, cxxRecordDecl(hasName("X")));
auto *FromFriend = FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
RecordDecl *FromRecordOfFriend =
const_cast<RecordDecl *>(getRecordDeclOfFriend(FromFriend));
ASSERT_EQ(FromRecordOfFriend->getDeclContext(), cast<DeclContext>(FromTu));
ASSERT_EQ(FromRecordOfFriend->getLexicalDeclContext(),
cast<DeclContext>(FromX));
ASSERT_FALSE(
FromRecordOfFriend->getDeclContext()->containsDecl(FromRecordOfFriend));
ASSERT_FALSE(FromRecordOfFriend->getLexicalDeclContext()->containsDecl(
FromRecordOfFriend));
ASSERT_FALSE(FromRecordOfFriend->getLookupParent()
->lookup(FromRecordOfFriend->getDeclName())
.empty());
auto *ToX = Import(FromX, Lang_CXX03);
ASSERT_TRUE(ToX);
Decl *ToTu = ToX->getTranslationUnitDecl();
auto *ToFriend = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
RecordDecl *ToRecordOfFriend =
const_cast<RecordDecl *>(getRecordDeclOfFriend(ToFriend));
ASSERT_EQ(ToRecordOfFriend->getDeclContext(), cast<DeclContext>(ToTu));
ASSERT_EQ(ToRecordOfFriend->getLexicalDeclContext(), cast<DeclContext>(ToX));
EXPECT_FALSE(
ToRecordOfFriend->getDeclContext()->containsDecl(ToRecordOfFriend));
EXPECT_FALSE(ToRecordOfFriend->getLexicalDeclContext()->containsDecl(
ToRecordOfFriend));
EXPECT_FALSE(ToRecordOfFriend->getLookupParent()
->lookup(ToRecordOfFriend->getDeclName())
.empty());
}
TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) {
Decl *FromTu = getTuDecl(
R"(
template<class A> class declToImport {
template<class A1> friend class declToImport;
};
)",
Lang_CXX03, "input.cc");
auto *FromD =
FirstDeclMatcher<ClassTemplateDecl>().match(FromTu, classTemplateDecl());
auto *ToD = Import(FromD, Lang_CXX03);
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_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, Pattern);
Imported1 =
cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX03));
}
ClassTemplateSpecializationDecl *Imported2;
{
Decl *FromTU = getTuDecl("template<class T> class X;"
"template<> class X<int>{};"
"struct Z { friend class X<int>; };",
Lang_CXX03, "input1.cc");
auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, Pattern);
Imported2 =
cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX03));
}
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_CXX03, "input0.cc");
auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU0, classTemplateDecl(hasName("F")));
auto *Imported0 = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX03));
Decl *FromTU1 = getTuDecl(
R"(
template <typename T>
class F {};
)",
Lang_CXX03, "input1.cc");
auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU1, classTemplateDecl(hasName("F")));
auto *Imported1 = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX03));
EXPECT_EQ(Imported0->getTemplatedDecl()->getTypeForDecl(),
Imported1->getTemplatedDecl()->getTypeForDecl());
}
TEST_P(ImportFriendClasses, DeclsFromFriendsShouldBeInRedeclChains) {
Decl *From, *To;
std::tie(From, To) =
getImportedDecl("class declToImport {};", Lang_CXX03,
"class Y { friend class declToImport; };", Lang_CXX03);
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_CXX03);
auto *ToDecl = FirstDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("F")));
Decl *FromTU = getTuDecl(
R"(
template <typename T>
class F {};
)",
Lang_CXX03, "input0.cc");
auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("F")));
auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX03));
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_CXX03, "input0.cc");
auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU0, classTemplateDecl(hasName("F")));
auto *ImportedFwd = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX03));
Decl *FromTU1 = getTuDecl(
R"(
template <typename T>
class F {};
)",
Lang_CXX03, "input1.cc");
auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU1, classTemplateDecl(hasName("F")));
auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX03));
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_CXX03, "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_CXX03);
Decl *FromTU1 = getTuDecl(
R"(
class F {};
)",
Lang_CXX03, "input1.cc");
auto *Definition = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU1, cxxRecordDecl(hasName("F")));
auto *ImportedDef = Import(Definition, Lang_CXX03);
EXPECT_TRUE(ImportedDef->getPreviousDecl());
EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
}
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendType) {
const char *Code =
R"(
class Container {
friend class X;
friend class X;
};
)";
Decl *ToTu = getToTuDecl(Code, Lang_CXX03);
Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc");
auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
auto *FromFriend1 =
FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03);
FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03);
EXPECT_NE(ToImportedFriend1, ToImportedFriend2);
EXPECT_EQ(ToFriend1, ToImportedFriend1);
EXPECT_EQ(ToFriend2, ToImportedFriend2);
}
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendDecl) {
const char *Code =
R"(
class Container {
friend void f();
friend void f();
};
)";
Decl *ToTu = getToTuDecl(Code, Lang_CXX03);
Decl *FromTu = getTuDecl(Code, Lang_CXX03, "from.cc");
auto *ToFriend1 = FirstDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
auto *ToFriend2 = LastDeclMatcher<FriendDecl>().match(ToTu, friendDecl());
auto *FromFriend1 =
FirstDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
auto *FromFriend2 = LastDeclMatcher<FriendDecl>().match(FromTu, friendDecl());
FriendDecl *ToImportedFriend1 = Import(FromFriend1, Lang_CXX03);
FriendDecl *ToImportedFriend2 = Import(FromFriend2, Lang_CXX03);
EXPECT_NE(ToImportedFriend1, ToImportedFriend2);
EXPECT_EQ(ToFriend1, ToImportedFriend1);
EXPECT_EQ(ToFriend2, ToImportedFriend2);
}
TEST_P(ASTImporterOptionSpecificTestBase, FriendFunInClassTemplate) {
auto *Code = R"(
template <class T>
struct X {
friend void foo(){}
};
)";
TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX03);
auto *ToFoo = FirstDeclMatcher<FunctionDecl>().match(
ToTU, functionDecl(hasName("foo")));
TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX03, "input.cc");
auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("foo")));
auto *ImportedFoo = Import(FromFoo, Lang_CXX03);
EXPECT_EQ(ImportedFoo, ToFoo);
}
struct DeclContextTest : ASTImporterOptionSpecificTestBase {};
TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
Decl *TU = getTuDecl(
R"(
namespace NS {
template <typename T>
struct S {};
template struct S<int>;
inline namespace INS {
template <typename T>
struct S {};
template struct S<int>;
}
}
)", Lang_CXX11, "input0.cc");
auto *NS = FirstDeclMatcher<NamespaceDecl>().match(
TU, namespaceDecl());
auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
TU, classTemplateSpecializationDecl());
ASSERT_TRUE(NS->containsDecl(Spec));
NS->removeDecl(Spec);
EXPECT_FALSE(NS->containsDecl(Spec));
}
TEST_P(DeclContextTest,
removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) {
Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX03);
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.
// We do not have a list with one element.
L.setHasExternalDecls();
ASSERT_FALSE(L.getAsList());
auto Results = L.getLookupResult();
ASSERT_EQ(1u, std::distance(Results.begin(), Results.end()));
// This asserts in the old implementation.
DC->removeDecl(A0);
EXPECT_FALSE(DC->containsDecl(A0));
}
struct ImportFunctionTemplateSpecializations
: ASTImporterOptionSpecificTestBase {};
TEST_P(ImportFunctionTemplateSpecializations,
TUshouldNotContainFunctionTemplateImplicitInstantiation) {
Decl *FromTU = getTuDecl(
R"(
template<class T>
int f() { return 0; }
void foo() { f<int>(); }
)",
Lang_CXX03, "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_CXX03));
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_CXX03, "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_CXX03));
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_CXX03, "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_CXX03));
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_CXX03, "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_CXX03));
{
auto *TU = ToAST->getASTContext().getTranslationUnitDecl();
auto *SpecD = FirstDeclMatcher<Decl>().match(TU, Spec);
auto *TemplateD = FirstDeclMatcher<FunctionTemplateDecl>().match(
TU, functionTemplateDecl());
auto *FirstSpecD = *(TemplateD->spec_begin());
EXPECT_EQ(SpecD, FirstSpecD);
ASSERT_TRUE(SpecD->getPreviousDecl());
EXPECT_FALSE(cast<FunctionDecl>(SpecD->getPreviousDecl())
->doesThisDeclarationHaveABody());
}
}
TEST_P(ImportFunctionTemplateSpecializations,
MatchNumberOfFunctionTemplateSpecializations) {
Decl *FromTU = getTuDecl(
R"(
template <typename T> constexpr int f() { return 0; }
template <> constexpr int f<int>() { return 4; }
void foo() {
static_assert(f<char>() == 0, "");
static_assert(f<int>() == 4, "");
}
)",
Lang_CXX11, "input0.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("foo")));
Import(FromD, Lang_CXX11);
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(
DeclCounter<FunctionDecl>().match(FromTU, functionDecl(hasName("f"))),
DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))));
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportShouldNotReportFalseODRErrorWhenRecordIsBeingDefined) {
{
Decl *FromTU = getTuDecl(
R"(
template <typename T>
struct B;
)",
Lang_CXX03, "input0.cc");
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("B")));
Import(FromD, Lang_CXX03);
}
{
Decl *FromTU = getTuDecl(
R"(
template <typename T>
struct B {
void f();
B* b;
};
)",
Lang_CXX03, "input1.cc");
FunctionDecl *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
Import(FromD, Lang_CXX03);
auto *FromCTD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("B")));
auto *ToCTD = cast<ClassTemplateDecl>(Import(FromCTD, Lang_CXX03));
EXPECT_TRUE(ToCTD->isThisDeclarationADefinition());
// We expect no (ODR) warning during the import.
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
}
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportingTypedefShouldImportTheCompleteType) {
// We already have an incomplete underlying type in the "To" context.
auto Code =
R"(
template <typename T>
struct S {
void foo();
};
using U = S<int>;
)";
Decl *ToTU = getToTuDecl(Code, Lang_CXX11);
auto *ToD = FirstDeclMatcher<TypedefNameDecl>().match(ToTU,
typedefNameDecl(hasName("U")));
ASSERT_TRUE(ToD->getUnderlyingType()->isIncompleteType());
// The "From" context has the same typedef, but the underlying type is
// complete this time.
Decl *FromTU = getTuDecl(std::string(Code) +
R"(
void foo(U* u) {
u->foo();
}
)", Lang_CXX11);
auto *FromD = FirstDeclMatcher<TypedefNameDecl>().match(FromTU,
typedefNameDecl(hasName("U")));
ASSERT_FALSE(FromD->getUnderlyingType()->isIncompleteType());
// The imported type should be complete.
auto *ImportedD = cast<TypedefNameDecl>(Import(FromD, Lang_CXX11));
EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportTemplateParameterLists) {
auto Code =
R"(
template<class T>
int f() { return 0; }
template <> int f<int>() { return 4; }
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX03);
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(FromTU,
functionDecl(hasName("f"), isExplicitTemplateSpecialization()));
ASSERT_EQ(FromD->getNumTemplateParameterLists(), 1u);
auto *ToD = Import(FromD, Lang_CXX03);
// The template parameter list should exist.
EXPECT_EQ(ToD->getNumTemplateParameterLists(), 1u);
}
const internal::VariadicDynCastAllOfMatcher<Decl, VarTemplateDecl>
varTemplateDecl;
const internal::VariadicDynCastAllOfMatcher<
Decl, VarTemplatePartialSpecializationDecl>
varTemplatePartialSpecializationDecl;
TEST_P(ASTImporterOptionSpecificTestBase,
FunctionTemplateParameterDeclContext) {
constexpr auto Code =
R"(
template<class T>
void f() {};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromD = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(hasName("f")));
ASSERT_EQ(FromD->getTemplateParameters()->getParam(0)->getDeclContext(),
FromD->getTemplatedDecl());
auto *ToD = Import(FromD, Lang_CXX11);
EXPECT_EQ(ToD->getTemplateParameters()->getParam(0)->getDeclContext(),
ToD->getTemplatedDecl());
EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
ToD->getTemplatedDecl(), ToD->getTemplateParameters()->getParam(0)));
}
TEST_P(ASTImporterOptionSpecificTestBase, ClassTemplateParameterDeclContext) {
constexpr auto Code =
R"(
template<class T1, class T2>
struct S {};
template<class T2>
struct S<int, T2> {};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("S")));
auto *FromDPart =
FirstDeclMatcher<ClassTemplatePartialSpecializationDecl>().match(
FromTU, classTemplatePartialSpecializationDecl(hasName("S")));
ASSERT_EQ(FromD->getTemplateParameters()->getParam(0)->getDeclContext(),
FromD->getTemplatedDecl());
ASSERT_EQ(FromDPart->getTemplateParameters()->getParam(0)->getDeclContext(),
FromDPart);
auto *ToD = Import(FromD, Lang_CXX11);
auto *ToDPart = Import(FromDPart, Lang_CXX11);
EXPECT_EQ(ToD->getTemplateParameters()->getParam(0)->getDeclContext(),
ToD->getTemplatedDecl());
EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
ToD->getTemplatedDecl(), ToD->getTemplateParameters()->getParam(0)));
EXPECT_EQ(ToDPart->getTemplateParameters()->getParam(0)->getDeclContext(),
ToDPart);
EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
ToDPart, ToDPart->getTemplateParameters()->getParam(0)));
}
TEST_P(ASTImporterOptionSpecificTestBase,
CXXDeductionGuideTemplateParameterDeclContext) {
Decl *FromTU = getTuDecl(
R"(
template <typename T> struct A {
A(T);
};
A a{(int)0};
)",
Lang_CXX17, "input.cc");
// clang-format off
/*
|-ClassTemplateDecl 0x1fe5000 <input.cc:2:7, line:4:7> line:2:36 A
| |-TemplateTypeParmDecl 0x1fe4eb0 <col:17, col:26> col:26 referenced typename depth 0 index 0 T
| |-CXXRecordDecl 0x1fe4f70 <col:29, line:4:7> line:2:36 struct A definition
|-FunctionTemplateDecl 0x1fe5860 <line:2:7, line:3:12> col:9 implicit <deduction guide for A>
| |-TemplateTypeParmDecl 0x1fe4eb0 <line:2:17, col:26> col:26 referenced typename depth 0 index 0 T
| |-CXXDeductionGuideDecl 0x1fe57a8 <line:3:9, col:12> col:9 implicit <deduction guide for A> 'auto (T) -> A<T>'
| | `-ParmVarDecl 0x1fe56b0 <col:11> col:12 'T'
| `-CXXDeductionGuideDecl 0x20515d8 <col:9, col:12> col:9 implicit used <deduction guide for A> 'auto (int) -> A<int>'
| |-TemplateArgument type 'int'
| | `-BuiltinType 0x20587e0 'int'
| `-ParmVarDecl 0x2051388 <col:11> col:12 'int':'int'
`-FunctionTemplateDecl 0x1fe5a78 <line:2:7, col:36> col:36 implicit <deduction guide for A>
|-TemplateTypeParmDecl 0x1fe4eb0 <col:17, col:26> col:26 referenced typename depth 0 index 0 T
`-CXXDeductionGuideDecl 0x1fe59c0 <col:36> col:36 implicit <deduction guide for A> 'auto (A<T>) -> A<T>'
`-ParmVarDecl 0x1fe5958 <col:36> col:36 'A<T>'
*/
// clang-format on
auto *FromD1 = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
FromTU, cxxDeductionGuideDecl());
auto *FromD2 = LastDeclMatcher<CXXDeductionGuideDecl>().match(
FromTU, cxxDeductionGuideDecl());
NamedDecl *P1 =
FromD1->getDescribedFunctionTemplate()->getTemplateParameters()->getParam(
0);
NamedDecl *P2 =
FromD2->getDescribedFunctionTemplate()->getTemplateParameters()->getParam(
0);
DeclContext *DC = P1->getDeclContext();
ASSERT_EQ(P1, P2);
ASSERT_TRUE(DC == FromD1 || DC == FromD2);
auto *ToD1 = Import(FromD1, Lang_CXX17);
auto *ToD2 = Import(FromD2, Lang_CXX17);
ASSERT_TRUE(ToD1 && ToD2);
P1 = ToD1->getDescribedFunctionTemplate()->getTemplateParameters()->getParam(
0);
P2 = ToD2->getDescribedFunctionTemplate()->getTemplateParameters()->getParam(
0);
DC = P1->getDeclContext();
EXPECT_EQ(P1, P2);
EXPECT_TRUE(DC == ToD1 || DC == ToD2);
ASTImporterLookupTable *Tbl = SharedStatePtr->getLookupTable();
if (Tbl->contains(ToD1, P1)) {
EXPECT_FALSE(Tbl->contains(ToD2, P1));
} else {
EXPECT_TRUE(Tbl->contains(ToD2, P1));
}
}
TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateParameterDeclContext) {
constexpr auto Code =
R"(
template<class T1, class T2>
int X1;
template<class T2>
int X1<int, T2>;
namespace Ns {
template<class T1, class T2>
int X2;
template<class T2>
int X2<int, T2>;
}
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX14);
auto *FromD1 = FirstDeclMatcher<VarTemplateDecl>().match(
FromTU, varTemplateDecl(hasName("X1")));
auto *FromD1Part =
FirstDeclMatcher<VarTemplatePartialSpecializationDecl>().match(
FromTU, varTemplatePartialSpecializationDecl(hasName("X1")));
auto *FromD2 = FirstDeclMatcher<VarTemplateDecl>().match(
FromTU, varTemplateDecl(hasName("X2")));
auto *FromD2Part =
FirstDeclMatcher<VarTemplatePartialSpecializationDecl>().match(
FromTU, varTemplatePartialSpecializationDecl(hasName("X2")));
ASSERT_EQ(FromD1->getTemplateParameters()->getParam(0)->getDeclContext(),
FromD1->getDeclContext());
ASSERT_EQ(FromD2->getTemplateParameters()->getParam(0)->getDeclContext(),
FromD2->getDeclContext());
ASSERT_EQ(FromD1Part->getTemplateParameters()->getParam(0)->getDeclContext(),
FromD1Part->getDeclContext());
// FIXME: VarTemplatePartialSpecializationDecl does not update ("adopt")
// template parameter decl context
// ASSERT_EQ(FromD2Part->getTemplateParameters()->getParam(0)->getDeclContext(),
// FromD2Part->getDeclContext());
auto *ToD1 = Import(FromD1, Lang_CXX14);
auto *ToD2 = Import(FromD2, Lang_CXX14);
auto *ToD1Part = Import(FromD1Part, Lang_CXX14);
auto *ToD2Part = Import(FromD2Part, Lang_CXX14);
EXPECT_EQ(ToD1->getTemplateParameters()->getParam(0)->getDeclContext(),
ToD1->getDeclContext());
EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
ToD1->getDeclContext(), ToD1->getTemplateParameters()->getParam(0)));
EXPECT_EQ(ToD2->getTemplateParameters()->getParam(0)->getDeclContext(),
ToD2->getDeclContext());
EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
ToD2->getDeclContext(), ToD2->getTemplateParameters()->getParam(0)));
EXPECT_EQ(ToD1Part->getTemplateParameters()->getParam(0)->getDeclContext(),
ToD1Part->getDeclContext());
EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
ToD1Part->getDeclContext(),
ToD1Part->getTemplateParameters()->getParam(0)));
// EXPECT_EQ(ToD2Part->getTemplateParameters()->getParam(0)->getDeclContext(),
// ToD2Part->getDeclContext());
// EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
// ToD2Part->getDeclContext(),
// ToD2Part->getTemplateParameters()->getParam(0)));
(void)ToD2Part;
}
TEST_P(ASTImporterOptionSpecificTestBase,
TypeAliasTemplateParameterDeclContext) {
constexpr auto Code =
R"(
template<class T1, class T2>
struct S {};
template<class T> using S1 = S<T, int>;
namespace Ns {
template<class T> using S2 = S<T, int>;
}
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromD1 = FirstDeclMatcher<TypeAliasTemplateDecl>().match(
FromTU, typeAliasTemplateDecl(hasName("S1")));
auto *FromD2 = FirstDeclMatcher<TypeAliasTemplateDecl>().match(
FromTU, typeAliasTemplateDecl(hasName("S2")));
ASSERT_EQ(FromD1->getTemplateParameters()->getParam(0)->getDeclContext(),
FromD1->getDeclContext());
ASSERT_EQ(FromD2->getTemplateParameters()->getParam(0)->getDeclContext(),
FromD2->getDeclContext());
auto *ToD1 = Import(FromD1, Lang_CXX11);
auto *ToD2 = Import(FromD2, Lang_CXX11);
EXPECT_EQ(ToD1->getTemplateParameters()->getParam(0)->getDeclContext(),
ToD1->getDeclContext());
EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
ToD1->getDeclContext(), ToD1->getTemplateParameters()->getParam(0)));
EXPECT_EQ(ToD2->getTemplateParameters()->getParam(0)->getDeclContext(),
ToD2->getDeclContext());
EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
ToD2->getDeclContext(), ToD2->getTemplateParameters()->getParam(0)));
}
const AstTypeMatcher<SubstTemplateTypeParmPackType>
substTemplateTypeParmPackType;
TEST_P(ASTImporterOptionSpecificTestBase, ImportSubstTemplateTypeParmPackType) {
constexpr auto Code = R"(
template<typename ...T> struct D {
template<typename... U> using B = int(int (*...p)(T, U));
template<typename U1, typename U2> D(B<U1, U2>*);
};
int f(int(int, int), int(int, int));
using asd = D<float, double, float>::B<int, long, int>;
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input.cpp");
auto *FromClass = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl());
{
ASTContext &FromCtx = FromTU->getASTContext();
const auto *FromSubstPack = selectFirst<SubstTemplateTypeParmPackType>(
"pack", match(substTemplateTypeParmPackType().bind("pack"), FromCtx));
ASSERT_TRUE(FromSubstPack);
ASSERT_EQ(FromSubstPack->getIdentifier()->getName(), "T");
ArrayRef<TemplateArgument> FromArgPack =
FromSubstPack->getArgumentPack().pack_elements();
ASSERT_EQ(FromArgPack.size(), 3u);
ASSERT_EQ(FromArgPack[0].getAsType(), FromCtx.FloatTy);
ASSERT_EQ(FromArgPack[1].getAsType(), FromCtx.DoubleTy);
ASSERT_EQ(FromArgPack[2].getAsType(), FromCtx.FloatTy);
}
{
// Let's do the import.
ClassTemplateSpecializationDecl *ToClass = Import(FromClass, Lang_CXX11);
ASTContext &ToCtx = ToClass->getASTContext();
const auto *ToSubstPack = selectFirst<SubstTemplateTypeParmPackType>(
"pack", match(substTemplateTypeParmPackType().bind("pack"), ToCtx));
// Check if it meets the requirements.
ASSERT_TRUE(ToSubstPack);
ASSERT_EQ(ToSubstPack->getIdentifier()->getName(), "T");
ArrayRef<TemplateArgument> ToArgPack =
ToSubstPack->getArgumentPack().pack_elements();
ASSERT_EQ(ToArgPack.size(), 3u);
ASSERT_EQ(ToArgPack[0].getAsType(), ToCtx.FloatTy);
ASSERT_EQ(ToArgPack[1].getAsType(), ToCtx.DoubleTy);
ASSERT_EQ(ToArgPack[2].getAsType(), ToCtx.FloatTy);
}
}
struct ASTImporterLookupTableTest : ASTImporterOptionSpecificTestBase {};
TEST_P(ASTImporterLookupTableTest, OneDecl) {
auto *ToTU = getToTuDecl("int a;", Lang_CXX03);
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_CXX03);
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_C99);
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_C99);
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_CXX03);
ASTImporterLookupTable LT(*ToTU);
auto *F0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
auto *F2 = LastDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
DeclarationName Name = F0->getDeclName();
auto Res = LT.lookup(ToTU, Name);
EXPECT_EQ(Res.size(), 3u);
EXPECT_EQ(Res.count(F0), 1u);
EXPECT_EQ(Res.count(F2), 1u);
}
TEST_P(ASTImporterLookupTableTest,
DifferentOperatorsShouldHaveDifferentResultSet) {
TranslationUnitDecl *ToTU = getToTuDecl(
R"(
struct X{};
void operator+(X, X);
void operator-(X, X);
)",
Lang_CXX03);
ASTImporterLookupTable LT(*ToTU);
auto *FPlus = FirstDeclMatcher<FunctionDecl>().match(
ToTU, functionDecl(hasOverloadedOperatorName("+")));
auto *FMinus = FirstDeclMatcher<FunctionDecl>().match(
ToTU, functionDecl(hasOverloadedOperatorName("-")));
DeclarationName NamePlus = FPlus->getDeclName();
auto ResPlus = LT.lookup(ToTU, NamePlus);
EXPECT_EQ(ResPlus.size(), 1u);
EXPECT_EQ(ResPlus.count(FPlus), 1u);
EXPECT_EQ(ResPlus.count(FMinus), 0u);
DeclarationName NameMinus = FMinus->getDeclName();
auto ResMinus = LT.lookup(ToTU, NameMinus);
EXPECT_EQ(ResMinus.size(), 1u);
EXPECT_EQ(ResMinus.count(FMinus), 1u);
EXPECT_EQ(ResMinus.count(FPlus), 0u);
EXPECT_NE(*ResMinus.begin(), *ResPlus.begin());
}
TEST_P(ASTImporterLookupTableTest, LookupDeclNamesFromDifferentTUs) {
TranslationUnitDecl *ToTU = getToTuDecl(
R"(
struct X {};
void operator+(X, X);
)",
Lang_CXX03);
auto *ToPlus = FirstDeclMatcher<FunctionDecl>().match(
ToTU, functionDecl(hasOverloadedOperatorName("+")));
Decl *FromTU = getTuDecl(
R"(
struct X {};
void operator+(X, X);
)",
Lang_CXX03);
auto *FromPlus = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasOverloadedOperatorName("+")));
// FromPlus have a different TU, thus its DeclarationName is different too.
ASSERT_NE(ToPlus->getDeclName(), FromPlus->getDeclName());
ASTImporterLookupTable LT(*ToTU);
auto Res = LT.lookup(ToTU, ToPlus->getDeclName());
ASSERT_EQ(Res.size(), 1u);
EXPECT_EQ(*Res.begin(), ToPlus);
// FromPlus have a different TU, thus its DeclarationName is different too.
Res = LT.lookup(ToTU, FromPlus->getDeclName());
ASSERT_EQ(Res.size(), 0u);
}
TEST_P(ASTImporterLookupTableTest,
LookupFindsFwdFriendClassDeclWithElaboratedType) {
TranslationUnitDecl *ToTU = getToTuDecl(
R"(
class Y { friend class F; };
)",
Lang_CXX03);
// In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent.
// So we must dig up the underlying CXXRecordDecl.
ASTImporterLookupTable LT(*ToTU);
auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
const RecordDecl *RD = getRecordDeclOfFriend(FriendD);
auto *Y = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(hasName("Y")));
DeclarationName Name = RD->getDeclName();
auto Res = LT.lookup(ToTU, Name);
EXPECT_EQ(Res.size(), 1u);
EXPECT_EQ(*Res.begin(), RD);
Res = LT.lookup(Y, Name);
EXPECT_EQ(Res.size(), 0u);
}
TEST_P(ASTImporterLookupTableTest,
LookupFindsFwdFriendClassDeclWithUnelaboratedType) {
TranslationUnitDecl *ToTU = getToTuDecl(
R"(
class F;
class Y { friend F; };
)",
Lang_CXX11);
// In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent.
// So we must dig up the underlying CXXRecordDecl.
ASTImporterLookupTable LT(*ToTU);
auto *FriendD = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
const RecordDecl *RD = getRecordDeclOfFriend(FriendD);
auto *Y = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, cxxRecordDecl(hasName("Y")));
DeclarationName Name = RD->getDeclName();
auto Res = LT.lookup(ToTU, Name);
EXPECT_EQ(Res.size(), 1u);
EXPECT_EQ(*Res.begin(), RD);
Res = LT.lookup(Y, Name);
EXPECT_EQ(Res.size(), 0u);
}
TEST_P(ASTImporterLookupTableTest,
LookupFindsFriendClassDeclWithTypeAliasDoesNotAssert) {
TranslationUnitDecl *ToTU = getToTuDecl(
R"(
class F;
using alias_of_f = F;
class Y { friend alias_of_f; };
)",
Lang_CXX11);
// ASTImporterLookupTable constructor handles using declarations correctly,
// no assert is expected.
ASTImporterLookupTable LT(*ToTU);
auto *Alias = FirstDeclMatcher<TypeAliasDecl>().match(
ToTU, typeAliasDecl(hasName("alias_of_f")));
DeclarationName Name = Alias->getDeclName();
auto Res = LT.lookup(ToTU, Name);
EXPECT_EQ(Res.count(Alias), 1u);
}
TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassTemplateDecl) {
TranslationUnitDecl *ToTU = getToTuDecl(
R"(
class Y { template <class T> friend class F; };
)",
Lang_CXX03);
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_CXX03);
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_CXX03);
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_CXX03);
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_CXX03);
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_CXX03);
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_CXX03);
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_C99);
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_CXX03);
auto *N1 =
LastDeclMatcher<NamespaceDecl>().match(ToTU, namespaceDecl(hasName("N")));
auto *A = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("A")));
DeclarationName Name = A->getDeclName();
ASTImporterLookupTable LT(*ToTU);
auto Res = LT.lookup(N1, Name);
ASSERT_EQ(Res.size(), 1u);
EXPECT_EQ(*Res.begin(), A);
}
TEST_P(ASTImporterOptionSpecificTestBase,
RedeclChainShouldBeCorrectAmongstNamespaces) {
Decl *FromTU = getTuDecl(
R"(
namespace NS {
struct X;
struct Y {
static const int I = 3;
};
}
namespace NS {
struct X { // <--- To be imported
void method(int i = Y::I) {}
int f;
};
}
)",
Lang_CXX03);
auto *FromFwd = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X"), unless(isImplicit())));
auto *FromDef = LastDeclMatcher<CXXRecordDecl>().match(
FromTU,
cxxRecordDecl(hasName("X"), isDefinition(), unless(isImplicit())));
ASSERT_NE(FromFwd, FromDef);
ASSERT_FALSE(FromFwd->isThisDeclarationADefinition());
ASSERT_TRUE(FromDef->isThisDeclarationADefinition());
ASSERT_EQ(FromFwd->getCanonicalDecl(), FromDef->getCanonicalDecl());
auto *ToDef = cast_or_null<CXXRecordDecl>(Import(FromDef, Lang_CXX03));
auto *ToFwd = cast_or_null<CXXRecordDecl>(Import(FromFwd, Lang_CXX03));
EXPECT_NE(ToFwd, ToDef);
EXPECT_FALSE(ToFwd->isThisDeclarationADefinition());
EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
EXPECT_EQ(ToFwd->getCanonicalDecl(), ToDef->getCanonicalDecl());
auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
// We expect no (ODR) warning during the import.
EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
}
struct ImportFriendFunctionTemplates : ASTImporterOptionSpecificTestBase {};
TEST_P(ImportFriendFunctionTemplates, LookupShouldFindPreviousFriend) {
Decl *ToTU = getToTuDecl(
R"(
class X {
template <typename T> friend void foo();
};
)",
Lang_CXX03);
auto *Friend = FirstDeclMatcher<FunctionTemplateDecl>().match(
ToTU, functionTemplateDecl(hasName("foo")));
Decl *FromTU = getTuDecl(
R"(
template <typename T> void foo();
)",
Lang_CXX03);
auto *FromFoo = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(hasName("foo")));
auto *Imported = Import(FromFoo, Lang_CXX03);
EXPECT_EQ(Imported->getPreviousDecl(), Friend);
}
struct ASTImporterWithFakeErrors : ASTImporter {
using ASTImporter::ASTImporter;
bool returnWithErrorInTest() override { return true; }
};
struct ErrorHandlingTest : ASTImporterOptionSpecificTestBase {
ErrorHandlingTest() {
Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
bool MinimalImport,
const std::shared_ptr<ASTImporterSharedState> &SharedState) {
return new ASTImporterWithFakeErrors(ToContext, ToFileManager,
FromContext, FromFileManager,
MinimalImport, SharedState);
};
}
// In this test we purposely report an error (UnsupportedConstruct) when
// importing the below stmt.
static constexpr auto* ErroneousStmt = R"( asm(""); )";
};
// Check a case when no new AST node is created in the AST before encountering
// the error.
TEST_P(ErrorHandlingTest, ErrorHappensBeforeCreatingANewNode) {
TranslationUnitDecl *ToTU = getToTuDecl(
R"(
template <typename T>
class X {};
template <>
class X<int> { int a; };
)",
Lang_CXX03);
TranslationUnitDecl *FromTU = getTuDecl(
R"(
template <typename T>
class X {};
template <>
class X<int> { double b; };
)",
Lang_CXX03);
auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl(hasName("X")));
ClassTemplateSpecializationDecl *ImportedSpec = Import(FromSpec, Lang_CXX03);
EXPECT_FALSE(ImportedSpec);
// The original Decl is kept, no new decl is created.
EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl(hasName("X"))),
1u);
// But an error is set to the counterpart in the "from" context.
ASTImporter *Importer = findFromTU(FromSpec)->Importer.get();
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromSpec);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::NameConflict);
}
// Check a case when a new AST node is created but not linked to the AST before
// encountering the error.
TEST_P(ErrorHandlingTest,
ErrorHappensAfterCreatingTheNodeButBeforeLinkingThatToTheAST) {
TranslationUnitDecl *FromTU = getTuDecl(
std::string("void foo() { ") + ErroneousStmt + " }", Lang_CXX03);
auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("foo")));
FunctionDecl *ImportedFoo = Import(FromFoo, Lang_CXX03);
EXPECT_FALSE(ImportedFoo);
TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
// Created, but not linked.
EXPECT_EQ(
DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("foo"))),
0u);
ASTImporter *Importer = findFromTU(FromFoo)->Importer.get();
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromFoo);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
}
// Check a case when a new AST node is created and linked to the AST before
// encountering the error. The error is set for the counterpart of the nodes in
// the "from" context.
TEST_P(ErrorHandlingTest, ErrorHappensAfterNodeIsCreatedAndLinked) {
TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
void f();
void f() { )") + ErroneousStmt + R"( }
)",
Lang_CXX03);
auto *FromProto = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
auto *FromDef =
LastDeclMatcher<FunctionDecl>().match(FromTU, functionDecl(hasName("f")));
FunctionDecl *ImportedProto = Import(FromProto, Lang_CXX03);
EXPECT_FALSE(ImportedProto); // Could not import.
// However, we created two nodes in the AST. 1) the fwd decl 2) the
// definition. The definition is not added to its DC, but the fwd decl is
// there.
TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
EXPECT_EQ(DeclCounter<FunctionDecl>().match(ToTU, functionDecl(hasName("f"))),
1u);
// Match the fwd decl.
auto *ToProto =
FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("f")));
EXPECT_TRUE(ToProto);
// An error is set to the counterpart in the "from" context both for the fwd
// decl and the definition.
ASTImporter *Importer = findFromTU(FromProto)->Importer.get();
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromProto);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
OptErr = Importer->getImportDeclErrorIfAny(FromDef);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
}
// An error should be set for a class if we cannot import one member.
TEST_P(ErrorHandlingTest, ErrorIsPropagatedFromMemberToClass) {
TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
class X {
void f() { )") + ErroneousStmt + R"( } // This member has the error
// during import.
void ok(); // The error should not prevent importing this.
}; // An error will be set for X too.
)",
Lang_CXX03);
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X")));
CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX03);
// An error is set for X.
EXPECT_FALSE(ImportedX);
ASTImporter *Importer = findFromTU(FromX)->Importer.get();
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
// An error is set for f().
auto *FromF = FirstDeclMatcher<CXXMethodDecl>().match(
FromTU, cxxMethodDecl(hasName("f")));
OptErr = Importer->getImportDeclErrorIfAny(FromF);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
// And any subsequent import should fail.
CXXMethodDecl *ImportedF = Import(FromF, Lang_CXX03);
EXPECT_FALSE(ImportedF);
// There is an error set for the other member too.
auto *FromOK = FirstDeclMatcher<CXXMethodDecl>().match(
FromTU, cxxMethodDecl(hasName("ok")));
OptErr = Importer->getImportDeclErrorIfAny(FromOK);
EXPECT_TRUE(OptErr);
// Cannot import the other member.
CXXMethodDecl *ImportedOK = Import(FromOK, Lang_CXX03);
EXPECT_FALSE(ImportedOK);
}
// Check that an error propagates to the dependent AST nodes.
// In the below code it means that an error in X should propagate to A.
// And even to F since the containing A is erroneous.
// And to all AST nodes which we visit during the import process which finally
// ends up in a failure (in the error() function).
TEST_P(ErrorHandlingTest, ErrorPropagatesThroughImportCycles) {
Decl *FromTU = getTuDecl(std::string(R"(
namespace NS {
class A {
template <int I> class F {};
class X {
template <int I> friend class F;
void error() { )") +
ErroneousStmt + R"( }
};
};
class B {};
} // NS
)",
Lang_CXX03, "input0.cc");
auto *FromFRD = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A"), isDefinition()));
auto *FromB = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("B"), isDefinition()));
auto *FromNS = FirstDeclMatcher<NamespaceDecl>().match(
FromTU, namespaceDecl(hasName("NS")));
// Start by importing the templated CXXRecordDecl of F.
// Import fails for that.
EXPECT_FALSE(Import(FromFRD, Lang_CXX03));
// Import fails for A.
EXPECT_FALSE(Import(FromA, Lang_CXX03));
// But we should be able to import the independent B.
EXPECT_TRUE(Import(FromB, Lang_CXX03));
// And the namespace.
EXPECT_TRUE(Import(FromNS, Lang_CXX03));
// An error is set to the templated CXXRecordDecl of F.
ASTImporter *Importer = findFromTU(FromFRD)->Importer.get();
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromFRD);
EXPECT_TRUE(OptErr);
// An error is set to A.
OptErr = Importer->getImportDeclErrorIfAny(FromA);
EXPECT_TRUE(OptErr);
// There is no error set to B.
OptErr = Importer->getImportDeclErrorIfAny(FromB);
EXPECT_FALSE(OptErr);
// There is no error set to NS.
OptErr = Importer->getImportDeclErrorIfAny(FromNS);
EXPECT_FALSE(OptErr);
// Check some of those decls whose ancestor is X, they all should have an
// error set if we visited them during an import process which finally failed.
// These decls are part of a cycle in an ImportPath.
// There would not be any error set for these decls if we hadn't follow the
// ImportPaths and the cycles.
OptErr = Importer->getImportDeclErrorIfAny(
FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("F"))));
// An error is set to the 'F' ClassTemplateDecl.
EXPECT_TRUE(OptErr);
// An error is set to the FriendDecl.
OptErr = Importer->getImportDeclErrorIfAny(
FirstDeclMatcher<FriendDecl>().match(
FromTU, friendDecl()));
EXPECT_TRUE(OptErr);
// An error is set to the implicit class of A.
OptErr =
Importer->getImportDeclErrorIfAny(FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A"), isImplicit())));
EXPECT_TRUE(OptErr);
// An error is set to the implicit class of X.
OptErr =
Importer->getImportDeclErrorIfAny(FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X"), isImplicit())));
EXPECT_TRUE(OptErr);
}
TEST_P(ErrorHandlingTest, ErrorIsNotPropagatedFromMemberToNamespace) {
TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
namespace X {
void f() { )") + ErroneousStmt + R"( } // This member has the error
// during import.
void ok(); // The error should not prevent importing this.
}; // An error will be set for X too.
)",
Lang_CXX03);
auto *FromX = FirstDeclMatcher<NamespaceDecl>().match(
FromTU, namespaceDecl(hasName("X")));
NamespaceDecl *ImportedX = Import(FromX, Lang_CXX03);
// There is no error set for X.
EXPECT_TRUE(ImportedX);
ASTImporter *Importer = findFromTU(FromX)->Importer.get();
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
ASSERT_FALSE(OptErr);
// An error is set for f().
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
OptErr = Importer->getImportDeclErrorIfAny(FromF);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
// And any subsequent import should fail.
FunctionDecl *ImportedF = Import(FromF, Lang_CXX03);
EXPECT_FALSE(ImportedF);
// There is no error set for ok().
auto *FromOK = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("ok")));
OptErr = Importer->getImportDeclErrorIfAny(FromOK);
EXPECT_FALSE(OptErr);
// And we should be able to import.
FunctionDecl *ImportedOK = Import(FromOK, Lang_CXX03);
EXPECT_TRUE(ImportedOK);
}
TEST_P(ErrorHandlingTest, ODRViolationWithinTypedefDecls) {
// Importing `z` should fail - instead of crashing - due to an ODR violation.
// The `bar::e` typedef sets it's DeclContext after the import is done.
// However, if the importation fails, it will be left as a nullptr.
// During the cleanup of the failed import, we should check whether the
// DeclContext is null or not - instead of dereferencing that unconditionally.
constexpr auto ToTUCode = R"(
namespace X {
struct bar {
int odr_violation;
};
})";
constexpr auto FromTUCode = R"(
namespace X {
enum b {};
struct bar {
typedef b e;
static e d;
};
}
int z = X::bar::d;
)";
Decl *ToTU = getToTuDecl(ToTUCode, Lang_CXX11);
static_cast<void>(ToTU);
Decl *FromTU = getTuDecl(FromTUCode, Lang_CXX11);
auto *FromZ =
FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("z")));
ASSERT_TRUE(FromZ);
ASSERT_TRUE(FromZ->hasInit());
auto *ImportedZ = Import(FromZ, Lang_CXX11);
EXPECT_FALSE(ImportedZ);
}
// An error should be set for a class if it had a previous import with an error
// from another TU.
TEST_P(ErrorHandlingTest,
ImportedDeclWithErrorShouldFailTheImportOfDeclWhichMapToIt) {
// We already have a fwd decl.
TranslationUnitDecl *ToTU = getToTuDecl("class X;", Lang_CXX03);
// Then we import a definition.
{
TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
class X {
void f() { )") + ErroneousStmt + R"( }
void ok();
};
)",
Lang_CXX03);
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X")));
CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX03);
// An error is set for X ...
EXPECT_FALSE(ImportedX);
ASTImporter *Importer = findFromTU(FromX)->Importer.get();
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
}
// ... but the node had been created.
auto *ToXDef = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(hasName("X"), isDefinition()));
// An error is set for "ToXDef" in the shared state.
Optional<ImportError> OptErr =
SharedStatePtr->getImportDeclErrorIfAny(ToXDef);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
auto *ToXFwd = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(hasName("X"), unless(isDefinition())));
// An error is NOT set for the fwd Decl of X in the shared state.
OptErr = SharedStatePtr->getImportDeclErrorIfAny(ToXFwd);
ASSERT_FALSE(OptErr);
// Try to import X again but from another TU.
{
TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
class X {
void f() { )") + ErroneousStmt + R"( }
void ok();
};
)",
Lang_CXX03, "input1.cc");
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X")));
CXXRecordDecl *ImportedX = Import(FromX, Lang_CXX03);
// If we did not save the errors for the "to" context then the below checks
// would fail, because the lookup finds the fwd Decl of the existing
// definition in the "to" context. We can reach the existing definition via
// the found fwd Decl. That existing definition is structurally equivalent
// (we check only the fields) with this one we want to import, so we return
// with the existing definition, which is erroneous (one method is missing).
// The import should fail.
EXPECT_FALSE(ImportedX);
ASTImporter *Importer = findFromTU(FromX)->Importer.get();
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromX);
// And an error is set for this new X in the "from" ctx.
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
}
}
TEST_P(ErrorHandlingTest, ImportOfOverriddenMethods) {
auto MatchFooA =
functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("A"))));
auto MatchFooB =
functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("B"))));
auto MatchFooC =
functionDecl(hasName("foo"), hasAncestor(cxxRecordDecl(hasName("C"))));
// Provoke import of a method that has overridden methods with import error.
TranslationUnitDecl *FromTU = getTuDecl(std::string(R"(
struct C;
struct A {
virtual void foo();
void f1(C *);
};
void A::foo() {
)") + ErroneousStmt + R"(
}
struct B : public A {
void foo() override;
};
struct C : public B {
void foo() override;
};
)",
Lang_CXX11);
auto *FromFooA = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooA);
auto *FromFooB = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooB);
auto *FromFooC = FirstDeclMatcher<FunctionDecl>().match(FromTU, MatchFooC);
EXPECT_FALSE(Import(FromFooA, Lang_CXX11));
ASTImporter *Importer = findFromTU(FromFooA)->Importer.get();
auto CheckError = [&Importer](Decl *FromD) {
Optional<ImportError> OptErr = Importer->getImportDeclErrorIfAny(FromD);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ImportError::UnsupportedConstruct);
};
CheckError(FromFooA);
EXPECT_FALSE(Import(FromFooB, Lang_CXX11));
CheckError(FromFooB);
EXPECT_FALSE(Import(FromFooC, Lang_CXX11));
CheckError(FromFooC);
}
TEST_P(ErrorHandlingTest, ODRViolationWithinParmVarDecls) {
// Importing of 'f' and parameter 'P' should cause an ODR error.
// The error happens after the ParmVarDecl for 'P' was already created.
// This is a special case because the ParmVarDecl has a temporary DeclContext.
// Expected is no crash at error handling of ASTImporter.
constexpr auto ToTUCode = R"(
struct X {
char A;
};
)";
constexpr auto FromTUCode = R"(
struct X {
enum Y { Z };
};
void f(int P = X::Z);
)";
Decl *ToTU = getToTuDecl(ToTUCode, Lang_CXX11);
static_cast<void>(ToTU);
Decl *FromTU = getTuDecl(FromTUCode, Lang_CXX11);
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
ASSERT_TRUE(FromF);
auto *ImportedF = Import(FromF, Lang_CXX11);
EXPECT_FALSE(ImportedF);
}
TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionBody) {
Decl *FromTU = getTuDecl(
R"(
void f() {
auto L = [](){};
}
)",
Lang_CXX11, "input0.cc");
auto Pattern = lambdaExpr();
CXXRecordDecl *FromL =
FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();
auto ToL = Import(FromL, Lang_CXX11);
unsigned ToLSize = std::distance(ToL->decls().begin(), ToL->decls().end());
unsigned FromLSize =
std::distance(FromL->decls().begin(), FromL->decls().end());
EXPECT_NE(ToLSize, 0u);
EXPECT_EQ(ToLSize, FromLSize);
}
TEST_P(ASTImporterOptionSpecificTestBase, LambdaInFunctionParam) {
Decl *FromTU = getTuDecl(
R"(
template <typename F>
void f(F L = [](){}) {}
)",
Lang_CXX11, "input0.cc");
auto Pattern = lambdaExpr();
CXXRecordDecl *FromL =
FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();
auto ToL = Import(FromL, Lang_CXX11);
unsigned ToLSize = std::distance(ToL->decls().begin(), ToL->decls().end());
unsigned FromLSize =
std::distance(FromL->decls().begin(), FromL->decls().end());
EXPECT_NE(ToLSize, 0u);
EXPECT_EQ(ToLSize, FromLSize);
}
TEST_P(ASTImporterOptionSpecificTestBase, LambdaInGlobalScope) {
Decl *FromTU = getTuDecl(
R"(
auto l1 = [](unsigned lp) { return 1; };
auto l2 = [](int lp) { return 2; };
int f(int p) {
return l1(p) + l2(p);
}
)",
Lang_CXX11, "input0.cc");
FunctionDecl *FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
FunctionDecl *ToF = Import(FromF, Lang_CXX11);
EXPECT_TRUE(ToF);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportExistingFriendClassTemplateDef) {
auto Code =
R"(
template <class T1, class T2>
struct Base {
template <class U1, class U2>
friend struct Class;
};
template <class T1, class T2>
struct Class { };
)";
TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX03);
TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX03, "input.cc");
auto *ToClassProto = FirstDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("Class")));
auto *ToClassDef = LastDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("Class")));
ASSERT_FALSE(ToClassProto->isThisDeclarationADefinition());
ASSERT_TRUE(ToClassDef->isThisDeclarationADefinition());
// Previous friend decl is not linked to it!
ASSERT_FALSE(ToClassDef->getPreviousDecl());
ASSERT_EQ(ToClassDef->getMostRecentDecl(), ToClassDef);
ASSERT_EQ(ToClassProto->getMostRecentDecl(), ToClassProto);
auto *FromClassProto = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("Class")));
auto *FromClassDef = LastDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("Class")));
ASSERT_FALSE(FromClassProto->isThisDeclarationADefinition());
ASSERT_TRUE(FromClassDef->isThisDeclarationADefinition());
ASSERT_FALSE(FromClassDef->getPreviousDecl());
ASSERT_EQ(FromClassDef->getMostRecentDecl(), FromClassDef);
ASSERT_EQ(FromClassProto->getMostRecentDecl(), FromClassProto);
auto *ImportedDef = Import(FromClassDef, Lang_CXX03);
// At import we should find the definition for 'Class' even if the
// prototype (inside 'friend') for it comes first in the AST and is not
// linked to the definition.
EXPECT_EQ(ImportedDef, ToClassDef);
}
struct LLDBLookupTest : ASTImporterOptionSpecificTestBase {
LLDBLookupTest() {
Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
bool MinimalImport,
const std::shared_ptr<ASTImporterSharedState> &SharedState) {
return new ASTImporter(ToContext, ToFileManager, FromContext,
FromFileManager, MinimalImport,
// We use the regular lookup.
/*SharedState=*/nullptr);
};
}
};
TEST_P(LLDBLookupTest, ImporterShouldFindInTransparentContext) {
TranslationUnitDecl *ToTU = getToTuDecl(
R"(
extern "C" {
class X{};
};
)",
Lang_CXX03);
auto *ToX = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(hasName("X")));
// Set up a stub external storage.
ToTU->setHasExternalLexicalStorage(true);
// Set up DeclContextBits.HasLazyExternalLexicalLookups to true.
ToTU->setMustBuildLookupTable();
struct TestExternalASTSource : ExternalASTSource {};
ToTU->getASTContext().setExternalSource(new TestExternalASTSource());
Decl *FromTU = getTuDecl(
R"(
class X;
)",
Lang_CXX03);
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X")));
auto *ImportedX = Import(FromX, Lang_CXX03);
// The lookup must find the existing class definition in the LinkageSpecDecl.
// Then the importer renders the existing and the new decl into one chain.
EXPECT_EQ(ImportedX->getCanonicalDecl(), ToX->getCanonicalDecl());
}
struct SVEBuiltins : ASTImporterOptionSpecificTestBase {};
TEST_P(SVEBuiltins, ImportTypes) {
static const char *const TypeNames[] = {
"__SVInt8_t",
"__SVInt16_t",
"__SVInt32_t",
"__SVInt64_t",
"__SVUint8_t",
"__SVUint16_t",
"__SVUint32_t",
"__SVUint64_t",
"__SVFloat16_t",
"__SVBFloat16_t",
"__SVFloat32_t",
"__SVFloat64_t",
"__SVBool_t"
};
TranslationUnitDecl *ToTU = getToTuDecl("", Lang_CXX03);
TranslationUnitDecl *FromTU = getTuDecl("", Lang_CXX03, "input.cc");
for (auto *TypeName : TypeNames) {
auto *ToTypedef = FirstDeclMatcher<TypedefDecl>().match(
ToTU, typedefDecl(hasName(TypeName)));
QualType ToType = ToTypedef->getUnderlyingType();
auto *FromTypedef = FirstDeclMatcher<TypedefDecl>().match(
FromTU, typedefDecl(hasName(TypeName)));
QualType FromType = FromTypedef->getUnderlyingType();
QualType ImportedType = ImportType(FromType, FromTypedef, Lang_CXX03);
EXPECT_EQ(ImportedType, ToType);
}
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportOfDefaultImplicitFunctions) {
// Test that import of implicit functions works and the functions
// are merged into one chain.
auto GetDeclToImport = [this](StringRef File) {
Decl *FromTU = getTuDecl(
R"(
struct X { };
// Force generating some implicit operator definitions for X.
void f() { X x1, x2; x1 = x2; X *x3 = new X; delete x3; }
)",
Lang_CXX11, File);
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X"), unless(isImplicit())));
// Destructor is picked as one example of implicit function.
return FromD->getDestructor();
};
auto *ToD1 = Import(GetDeclToImport("input1.cc"), Lang_CXX11);
ASSERT_TRUE(ToD1);
auto *ToD2 = Import(GetDeclToImport("input2.cc"), Lang_CXX11);
ASSERT_TRUE(ToD2);
EXPECT_EQ(ToD1->getCanonicalDecl(), ToD2->getCanonicalDecl());
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportOfExplicitlyDefaultedOrDeleted) {
Decl *FromTU = getTuDecl(
R"(
struct X { X() = default; X(const X&) = delete; };
)",
Lang_CXX11);
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X")));
auto *ImportedX = Import(FromX, Lang_CXX11);
auto *Constr1 = FirstDeclMatcher<CXXConstructorDecl>().match(
ImportedX, cxxConstructorDecl(hasName("X"), unless(isImplicit())));
auto *Constr2 = LastDeclMatcher<CXXConstructorDecl>().match(
ImportedX, cxxConstructorDecl(hasName("X"), unless(isImplicit())));
ASSERT_TRUE(ImportedX);
EXPECT_TRUE(Constr1->isDefaulted());
EXPECT_TRUE(Constr1->isExplicitlyDefaulted());
EXPECT_TRUE(Constr2->isDeletedAsWritten());
EXPECT_EQ(ImportedX->isAggregate(), FromX->isAggregate());
}
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, SVEBuiltins,
::testing::Values(std::vector<std::string>{
"-target", "aarch64-linux-gnu"}));
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, DeclContextTest,
::testing::Values(std::vector<std::string>()));
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, CanonicalRedeclChain,
::testing::Values(std::vector<std::string>()));
TEST_P(ASTImporterOptionSpecificTestBase, LambdasAreDifferentiated) {
Decl *FromTU = getTuDecl(
R"(
void f() {
auto L0 = [](){};
auto L1 = [](){};
}
)",
Lang_CXX11, "input0.cc");
auto Pattern = lambdaExpr();
CXXRecordDecl *FromL0 =
FirstDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();
CXXRecordDecl *FromL1 =
LastDeclMatcher<LambdaExpr>().match(FromTU, Pattern)->getLambdaClass();
ASSERT_NE(FromL0, FromL1);
CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11);
CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11);
EXPECT_NE(ToL0, ToL1);
}
TEST_P(ASTImporterOptionSpecificTestBase,
LambdasInFunctionParamsAreDifferentiated) {
Decl *FromTU = getTuDecl(
R"(
template <typename F0, typename F1>
void f(F0 L0 = [](){}, F1 L1 = [](){}) {}
)",
Lang_CXX11, "input0.cc");
auto Pattern = cxxRecordDecl(isLambda());
CXXRecordDecl *FromL0 =
FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
CXXRecordDecl *FromL1 =
LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
ASSERT_NE(FromL0, FromL1);
CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11);
CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11);
ASSERT_NE(ToL0, ToL1);
}
TEST_P(ASTImporterOptionSpecificTestBase,
LambdasInFunctionParamsAreDifferentiatedWhenMacroIsUsed) {
Decl *FromTU = getTuDecl(
R"(
#define LAMBDA [](){}
template <typename F0, typename F1>
void f(F0 L0 = LAMBDA, F1 L1 = LAMBDA) {}
)",
Lang_CXX11, "input0.cc");
auto Pattern = cxxRecordDecl(isLambda());
CXXRecordDecl *FromL0 =
FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
CXXRecordDecl *FromL1 =
LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
ASSERT_NE(FromL0, FromL1);
Import(FromL0, Lang_CXX11);
Import(FromL1, Lang_CXX11);
CXXRecordDecl *ToL0 = Import(FromL0, Lang_CXX11);
CXXRecordDecl *ToL1 = Import(FromL1, Lang_CXX11);
ASSERT_NE(ToL0, ToL1);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportAssignedLambda) {
Decl *FromTU = getTuDecl(
R"(
void f() {
auto x = []{} = {}; auto x2 = x;
}
)",
Lang_CXX20, "input0.cc");
auto FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
// We have only one lambda class.
ASSERT_EQ(
DeclCounter<CXXRecordDecl>().match(FromTU, cxxRecordDecl(isLambda())),
1u);
FunctionDecl *ToF = Import(FromF, Lang_CXX20);
EXPECT_TRUE(ToF);
TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
// We have only one lambda class after the import.
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, cxxRecordDecl(isLambda())),
1u);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportDefaultConstructibleLambdas) {
Decl *FromTU = getTuDecl(
R"(
void f() {
auto x = []{} = {};
auto xb = []{} = {};
}
)",
Lang_CXX20, "input0.cc");
auto FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
// We have two lambda classes.
ASSERT_EQ(
DeclCounter<CXXRecordDecl>().match(FromTU, cxxRecordDecl(isLambda())),
2u);
FunctionDecl *ToF = Import(FromF, Lang_CXX20);
EXPECT_TRUE(ToF);
TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
// We have two lambda classes after the import.
EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, cxxRecordDecl(isLambda())),
2u);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportFunctionDeclWithTypeSourceInfoWithSourceDecl) {
// This code results in a lambda with implicit constructor.
// The constructor's TypeSourceInfo points out the function prototype.
// This prototype has an EST_Unevaluated in its exception information and a
// SourceDecl that is the function declaration itself.
// The test verifies that AST import of such AST does not crash.
// (Here the function's TypeSourceInfo references the function itself.)
Decl *FromTU = getTuDecl(
R"(
template<typename T> void f(T) { auto X = [](){}; }
void g() { f(10); }
)",
Lang_CXX11, "input0.cc");
// Use LastDeclMatcher to find the LambdaExpr in the template specialization.
CXXRecordDecl *FromL = LastDeclMatcher<LambdaExpr>()
.match(FromTU, lambdaExpr())
->getLambdaClass();
CXXConstructorDecl *FromCtor = *FromL->ctor_begin();
ASSERT_TRUE(FromCtor->isCopyConstructor());
ASSERT_TRUE(FromCtor->getTypeSourceInfo());
const auto *FromFPT = FromCtor->getType()->getAs<FunctionProtoType>();
ASSERT_TRUE(FromFPT);
EXPECT_EQ(FromCtor->getTypeSourceInfo()->getType().getTypePtr(), FromFPT);
FunctionProtoType::ExtProtoInfo FromEPI = FromFPT->getExtProtoInfo();
// If type is EST_Unevaluated, SourceDecl should be set to the parent Decl.
EXPECT_EQ(FromEPI.ExceptionSpec.Type, EST_Unevaluated);
EXPECT_EQ(FromEPI.ExceptionSpec.SourceDecl, FromCtor);
auto ToL = Import(FromL, Lang_CXX11);
// Check if the import was correct.
CXXConstructorDecl *ToCtor = *ToL->ctor_begin();
EXPECT_TRUE(ToCtor->getTypeSourceInfo());
const auto *ToFPT = ToCtor->getType()->getAs<FunctionProtoType>();
ASSERT_TRUE(ToFPT);
EXPECT_EQ(ToCtor->getTypeSourceInfo()->getType().getTypePtr(), ToFPT);
FunctionProtoType::ExtProtoInfo ToEPI = ToFPT->getExtProtoInfo();
EXPECT_EQ(ToEPI.ExceptionSpec.Type, EST_Unevaluated);
EXPECT_EQ(ToEPI.ExceptionSpec.SourceDecl, ToCtor);
}
struct ImportAutoFunctions : ASTImporterOptionSpecificTestBase {};
TEST_P(ImportAutoFunctions, ReturnWithTypedefDeclaredInside) {
Decl *FromTU = getTuDecl(
R"(
auto X = [](long l) {
using int_type = long;
auto dur = 13;
return static_cast<int_type>(dur);
};
)",
Lang_CXX14, "input0.cc");
CXXMethodDecl *From =
FirstDeclMatcher<CXXMethodDecl>().match(FromTU, cxxMethodDecl());
// Explicitly set the return type of the lambda's operator() to the TypeAlias.
// Normally the return type would be the built-in 'long' type. However, there
// are cases when Clang does not use the canonical type and the TypeAlias is
// used. I could not create such an AST from regular source code, it requires
// some special state in the preprocessor. I've found such an AST when Clang
// parsed libcxx/src/filesystem/directory_iterator.cpp, but could not reduce
// that with creduce, because after preprocessing, the AST no longer
// contained the TypeAlias as a return type of the lambda.
ASTContext &Ctx = From->getASTContext();
TypeAliasDecl *FromTA =
FirstDeclMatcher<TypeAliasDecl>().match(FromTU, typeAliasDecl());
QualType TT = Ctx.getTypedefType(FromTA);
const FunctionProtoType *FPT = cast<FunctionProtoType>(From->getType());
QualType NewFunType =
Ctx.getFunctionType(TT, FPT->getParamTypes(), FPT->getExtProtoInfo());
From->setType(NewFunType);
CXXMethodDecl *To = Import(From, Lang_CXX14);
EXPECT_TRUE(To);
EXPECT_TRUE(isa<TypedefType>(To->getReturnType()));
}
TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside) {
Decl *FromTU = getTuDecl(
R"(
auto foo() {
struct X {};
return X();
}
)",
Lang_CXX14, "input0.cc");
FunctionDecl *From =
FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
FunctionDecl *To = Import(From, Lang_CXX14);
EXPECT_TRUE(To);
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}
TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredInside2) {
Decl *FromTU = getTuDecl(
R"(
auto foo() {
struct X {};
return X();
}
)",
Lang_CXX14, "input0.cc");
FunctionDecl *From =
FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
// This time import the type directly.
QualType ToT = ImportType(From->getType(), From, Lang_CXX14);
const FunctionProtoType *FPT = cast<FunctionProtoType>(ToT);
EXPECT_TRUE(isa<AutoType>(FPT->getReturnType()));
}
TEST_P(ImportAutoFunctions, ReturnWithTypedefToStructDeclaredInside) {
Decl *FromTU = getTuDecl(
R"(
auto foo() {
struct X {};
using Y = X;
return Y();
}
)",
Lang_CXX14, "input0.cc");
FunctionDecl *From =
FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
FunctionDecl *To = Import(From, Lang_CXX14);
EXPECT_TRUE(To);
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}
TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredNestedInside) {
Decl *FromTU = getTuDecl(
R"(
auto foo() {
struct X { struct Y{}; };
return X::Y();
}
)",
Lang_CXX14, "input0.cc");
FunctionDecl *From =
FirstDeclMatcher<FunctionDecl>().match(FromTU, functionDecl());
FunctionDecl *To = Import(From, Lang_CXX14);
EXPECT_TRUE(To);
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}
TEST_P(ImportAutoFunctions, ReturnWithInternalLambdaType) {
Decl *FromTU = getTuDecl(
R"(
auto f() {
auto l = []() {
struct X {};
return X();
};
return l();
}
)",
Lang_CXX17, "input0.cc");
FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
FunctionDecl *To = Import(From, Lang_CXX17);
EXPECT_TRUE(To);
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}
TEST_P(ImportAutoFunctions, ReturnWithTypeInIf) {
Decl *FromTU = getTuDecl(
R"(
auto f() {
if (struct X {} x; true)
return X();
else
return X();
}
)",
Lang_CXX17, "input0.cc");
FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
FunctionDecl *To = Import(From, Lang_CXX17);
EXPECT_TRUE(To);
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}
TEST_P(ImportAutoFunctions, ReturnWithTypeInFor) {
Decl *FromTU = getTuDecl(
R"(
auto f() {
for (struct X {} x;;)
return X();
}
)",
Lang_CXX17, "input0.cc");
FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
FunctionDecl *To = Import(From, Lang_CXX17);
EXPECT_TRUE(To);
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}
TEST_P(ImportAutoFunctions, ReturnWithTypeInSwitch) {
Decl *FromTU = getTuDecl(
R"(
auto f() {
switch (struct X {} x; 10) {
case 10:
return X();
}
}
)",
Lang_CXX17, "input0.cc");
FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
FunctionDecl *To = Import(From, Lang_CXX17);
EXPECT_TRUE(To);
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}
struct ImportSourceLocations : ASTImporterOptionSpecificTestBase {};
TEST_P(ImportSourceLocations, PreserveFileIDTreeStructure) {
// Tests that the FileID tree structure (with the links being the include
// chains) is preserved while importing other files (which need to be
// added to this structure with fake include locations.
SourceLocation Location1;
{
auto Pattern = varDecl(hasName("X"));
Decl *FromTU = getTuDecl("int X;", Lang_C99, "input0.c");
auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
Location1 = Import(FromD, Lang_C99)->getLocation();
}
SourceLocation Location2;
{
auto Pattern = varDecl(hasName("Y"));
Decl *FromTU = getTuDecl("int Y;", Lang_C99, "input1.c");
auto *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
Location2 = Import(FromD, Lang_C99)->getLocation();
}
SourceManager &ToSM = ToAST->getSourceManager();
FileID FileID1 = ToSM.getFileID(Location1);
FileID FileID2 = ToSM.getFileID(Location2);
// Check that the imported files look like as if they were included from the
// start of the main file.
SourceLocation FileStart = ToSM.getLocForStartOfFile(ToSM.getMainFileID());
EXPECT_NE(FileID1, ToSM.getMainFileID());
EXPECT_NE(FileID2, ToSM.getMainFileID());
EXPECT_EQ(ToSM.getIncludeLoc(FileID1), FileStart);
EXPECT_EQ(ToSM.getIncludeLoc(FileID2), FileStart);
// Let the SourceManager check the order of the locations. The order should
// be the order in which the declarations are imported.
EXPECT_TRUE(ToSM.isBeforeInTranslationUnit(Location1, Location2));
EXPECT_FALSE(ToSM.isBeforeInTranslationUnit(Location2, Location1));
}
TEST_P(ImportSourceLocations, NormalFileBuffer) {
// Test importing normal file buffers.
std::string Path = "input0.c";
std::string Source = "int X;";
TranslationUnitDecl *FromTU = getTuDecl(Source, Lang_C99, Path);
SourceLocation ImportedLoc;
{
// Import the VarDecl to trigger the importing of the FileID.
auto Pattern = varDecl(hasName("X"));
VarDecl *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
ImportedLoc = Import(FromD, Lang_C99)->getLocation();
}
// Make sure the imported buffer has the original contents.
SourceManager &ToSM = ToAST->getSourceManager();
FileID ImportedID = ToSM.getFileID(ImportedLoc);
EXPECT_EQ(Source,
ToSM.getBufferOrFake(ImportedID, SourceLocation()).getBuffer());
}
TEST_P(ImportSourceLocations, OverwrittenFileBuffer) {
// Test importing overwritten file buffers.
std::string Path = "input0.c";
TranslationUnitDecl *FromTU = getTuDecl("int X;", Lang_C99, Path);
// Overwrite the file buffer for our input file with new content.
const std::string Contents = "overwritten contents";
SourceLocation ImportedLoc;
{
SourceManager &FromSM = FromTU->getASTContext().getSourceManager();
clang::FileManager &FM = FromSM.getFileManager();
const clang::FileEntry &FE =
*FM.getVirtualFile(Path, static_cast<off_t>(Contents.size()), 0);
llvm::SmallVector<char, 64> Buffer;
Buffer.append(Contents.begin(), Contents.end());
auto FileContents =
std::make_unique<llvm::SmallVectorMemoryBuffer>(std::move(Buffer), Path);
FromSM.overrideFileContents(&FE, std::move(FileContents));
// Import the VarDecl to trigger the importing of the FileID.
auto Pattern = varDecl(hasName("X"));
VarDecl *FromD = FirstDeclMatcher<VarDecl>().match(FromTU, Pattern);
ImportedLoc = Import(FromD, Lang_C99)->getLocation();
}
// Make sure the imported buffer has the overwritten contents.
SourceManager &ToSM = ToAST->getSourceManager();
FileID ImportedID = ToSM.getFileID(ImportedLoc);
EXPECT_EQ(Contents,
ToSM.getBufferOrFake(ImportedID, SourceLocation()).getBuffer());
}
struct ImportAttributes : public ASTImporterOptionSpecificTestBase {
void checkAttrImportCommon(const Attr *From, const Attr *To,
const Decl *ToD) {
// Verify that dump does not crash because invalid data.
ToD->dump(llvm::nulls());
EXPECT_EQ(From->getParsedKind(), To->getParsedKind());
EXPECT_EQ(From->getSyntax(), To->getSyntax());
if (From->getAttrName()) {
EXPECT_TRUE(To->getAttrName());
EXPECT_STREQ(From->getAttrName()->getNameStart(),
To->getAttrName()->getNameStart());
} else {
EXPECT_FALSE(To->getAttrName());
}
if (From->getScopeName()) {
EXPECT_TRUE(To->getScopeName());
EXPECT_STREQ(From->getScopeName()->getNameStart(),
To->getScopeName()->getNameStart());
} else {
EXPECT_FALSE(To->getScopeName());
}
EXPECT_EQ(From->getSpellingListIndex(), To->getSpellingListIndex());
EXPECT_STREQ(From->getSpelling(), To->getSpelling());
EXPECT_EQ(From->isInherited(), To->isInherited());
EXPECT_EQ(From->isImplicit(), To->isImplicit());
EXPECT_EQ(From->isPackExpansion(), To->isPackExpansion());
EXPECT_EQ(From->isLateParsed(), To->isLateParsed());
}
template <class DT, class AT>
void importAttr(const char *Code, AT *&FromAttr, AT *&ToAttr) {
static_assert(std::is_base_of<Attr, AT>::value, "AT should be an Attr");
static_assert(std::is_base_of<Decl, DT>::value, "DT should be a Decl");
Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input.cc");
DT *FromD =
FirstDeclMatcher<DT>().match(FromTU, namedDecl(hasName("test")));
ASSERT_TRUE(FromD);
DT *ToD = Import(FromD, Lang_CXX11);
ASSERT_TRUE(ToD);
FromAttr = FromD->template getAttr<AT>();
ToAttr = ToD->template getAttr<AT>();
ASSERT_TRUE(FromAttr);
EXPECT_TRUE(ToAttr);
checkAttrImportCommon(FromAttr, ToAttr, ToD);
}
template <class T> void checkImported(const T *From, const T *To) {
EXPECT_TRUE(To);
EXPECT_NE(From, To);
}
template <class T>
void checkImportVariadicArg(const llvm::iterator_range<T **> &From,
const llvm::iterator_range<T **> &To) {
for (auto FromI = From.begin(), ToI = To.begin(); FromI != From.end();
++FromI, ++ToI) {
ASSERT_NE(ToI, To.end());
checkImported(*FromI, *ToI);
}
}
};
template <>
void ImportAttributes::checkImported<Decl>(const Decl *From, const Decl *To) {
EXPECT_TRUE(To);
EXPECT_NE(From, To);
EXPECT_EQ(To->getTranslationUnitDecl(),
ToAST->getASTContext().getTranslationUnitDecl());
}
// FIXME: Use ImportAttributes for this test.
TEST_P(ASTImporterOptionSpecificTestBase, ImportExprOfAlignmentAttr) {
// Test if import of these packed and aligned attributes does not trigger an
// error situation where source location from 'From' context is referenced in
// 'To' context through evaluation of the alignof attribute.
// This happens if the 'alignof(A)' expression is not imported correctly.
Decl *FromTU = getTuDecl(
R"(
struct __attribute__((packed)) A { int __attribute__((aligned(8))) X; };
struct alignas(alignof(A)) S {};
)",
Lang_CXX11, "input.cc");
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("S"), unless(isImplicit())));
ASSERT_TRUE(FromD);
auto *ToD = Import(FromD, Lang_CXX11);
ASSERT_TRUE(ToD);
auto *FromAttr = FromD->getAttr<AlignedAttr>();
auto *ToAttr = ToD->getAttr<AlignedAttr>();
EXPECT_EQ(FromAttr->isInherited(), ToAttr->isInherited());
EXPECT_EQ(FromAttr->isPackExpansion(), ToAttr->isPackExpansion());
EXPECT_EQ(FromAttr->isImplicit(), ToAttr->isImplicit());
EXPECT_EQ(FromAttr->getSyntax(), ToAttr->getSyntax());
EXPECT_EQ(FromAttr->getSemanticSpelling(), ToAttr->getSemanticSpelling());
EXPECT_TRUE(ToAttr->getAlignmentExpr());
auto *ToA = FirstDeclMatcher<CXXRecordDecl>().match(
ToD->getTranslationUnitDecl(),
cxxRecordDecl(hasName("A"), unless(isImplicit())));
// Ensure that 'struct A' was imported (through reference from attribute of
// 'S').
EXPECT_TRUE(ToA);
}
// FIXME: Use ImportAttributes for this test.
TEST_P(ASTImporterOptionSpecificTestBase, ImportFormatAttr) {
Decl *FromTU = getTuDecl(
R"(
int foo(const char * fmt, ...)
__attribute__ ((__format__ (__scanf__, 1, 2)));
)",
Lang_CXX03, "input.cc");
auto *FromD = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("foo")));
ASSERT_TRUE(FromD);
auto *ToD = Import(FromD, Lang_CXX03);
ASSERT_TRUE(ToD);
ToD->dump(); // Should not crash!
auto *FromAttr = FromD->getAttr<FormatAttr>();
auto *ToAttr = ToD->getAttr<FormatAttr>();
EXPECT_EQ(FromAttr->isInherited(), ToAttr->isInherited());
EXPECT_EQ(FromAttr->isPackExpansion(), ToAttr->isPackExpansion());
EXPECT_EQ(FromAttr->isImplicit(), ToAttr->isImplicit());
EXPECT_EQ(FromAttr->getSyntax(), ToAttr->getSyntax());
EXPECT_EQ(FromAttr->getAttributeSpellingListIndex(),
ToAttr->getAttributeSpellingListIndex());
EXPECT_EQ(FromAttr->getType()->getName(), ToAttr->getType()->getName());
}
TEST_P(ImportAttributes, ImportGuardedVar) {
GuardedVarAttr *FromAttr, *ToAttr;
importAttr<VarDecl>("int test __attribute__((guarded_var));", FromAttr,
ToAttr);
}
TEST_P(ImportAttributes, ImportPtGuardedVar) {
PtGuardedVarAttr *FromAttr, *ToAttr;
importAttr<VarDecl>("int *test __attribute__((pt_guarded_var));", FromAttr,
ToAttr);
}
TEST_P(ImportAttributes, ImportScopedLockable) {
ScopedLockableAttr *FromAttr, *ToAttr;
importAttr<CXXRecordDecl>("struct __attribute__((scoped_lockable)) test {};",
FromAttr, ToAttr);
}
TEST_P(ImportAttributes, ImportCapability) {
CapabilityAttr *FromAttr, *ToAttr;
importAttr<CXXRecordDecl>(
"struct __attribute__((capability(\"cap\"))) test {};", FromAttr, ToAttr);
EXPECT_EQ(FromAttr->getName(), ToAttr->getName());
}
TEST_P(ImportAttributes, ImportAssertCapability) {
AssertCapabilityAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A1, int A2) __attribute__((assert_capability(A1, A2)));",
FromAttr, ToAttr);
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportAcquireCapability) {
AcquireCapabilityAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A1, int A2) __attribute__((acquire_capability(A1, A2)));",
FromAttr, ToAttr);
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportTryAcquireCapability) {
TryAcquireCapabilityAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A1, int A2) __attribute__((try_acquire_capability(1, A1, "
"A2)));",
FromAttr, ToAttr);
checkImported(FromAttr->getSuccessValue(), ToAttr->getSuccessValue());
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportReleaseCapability) {
ReleaseCapabilityAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A1, int A2) __attribute__((release_capability(A1, A2)));",
FromAttr, ToAttr);
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportRequiresCapability) {
RequiresCapabilityAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A1, int A2) __attribute__((requires_capability(A1, A2)));",
FromAttr, ToAttr);
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportNoThreadSafetyAnalysis) {
NoThreadSafetyAnalysisAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test() __attribute__((no_thread_safety_analysis));", FromAttr,
ToAttr);
}
TEST_P(ImportAttributes, ImportGuardedBy) {
GuardedByAttr *FromAttr, *ToAttr;
importAttr<VarDecl>(
R"(
int G;
int test __attribute__((guarded_by(G)));
)",
FromAttr, ToAttr);
checkImported(FromAttr->getArg(), ToAttr->getArg());
}
TEST_P(ImportAttributes, ImportPtGuardedBy) {
PtGuardedByAttr *FromAttr, *ToAttr;
importAttr<VarDecl>(
R"(
int G;
int *test __attribute__((pt_guarded_by(G)));
)",
FromAttr, ToAttr);
checkImported(FromAttr->getArg(), ToAttr->getArg());
}
TEST_P(ImportAttributes, ImportAcquiredAfter) {
AcquiredAfterAttr *FromAttr, *ToAttr;
importAttr<VarDecl>(
R"(
struct __attribute__((lockable)) L {};
L A1;
L A2;
L test __attribute__((acquired_after(A1, A2)));
)",
FromAttr, ToAttr);
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportAcquiredBefore) {
AcquiredBeforeAttr *FromAttr, *ToAttr;
importAttr<VarDecl>(
R"(
struct __attribute__((lockable)) L {};
L A1;
L A2;
L test __attribute__((acquired_before(A1, A2)));
)",
FromAttr, ToAttr);
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportAssertExclusiveLock) {
AssertExclusiveLockAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>("void test(int A1, int A2) "
"__attribute__((assert_exclusive_lock(A1, A2)));",
FromAttr, ToAttr);
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportAssertSharedLock) {
AssertSharedLockAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A1, int A2) __attribute__((assert_shared_lock(A1, A2)));",
FromAttr, ToAttr);
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportExclusiveTrylockFunction) {
ExclusiveTrylockFunctionAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A1, int A2) __attribute__((exclusive_trylock_function(1, "
"A1, A2)));",
FromAttr, ToAttr);
checkImported(FromAttr->getSuccessValue(), ToAttr->getSuccessValue());
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportSharedTrylockFunction) {
SharedTrylockFunctionAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A1, int A2) __attribute__((shared_trylock_function(1, A1, "
"A2)));",
FromAttr, ToAttr);
checkImported(FromAttr->getSuccessValue(), ToAttr->getSuccessValue());
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
TEST_P(ImportAttributes, ImportLockReturned) {
LockReturnedAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A1) __attribute__((lock_returned(A1)));", FromAttr,
ToAttr);
checkImported(FromAttr->getArg(), ToAttr->getArg());
}
TEST_P(ImportAttributes, ImportLocksExcluded) {
LocksExcludedAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A1, int A2) __attribute__((locks_excluded(A1, A2)));",
FromAttr, ToAttr);
checkImportVariadicArg(FromAttr->args(), ToAttr->args());
}
template <typename T>
auto ExtendWithOptions(const T &Values, const std::vector<std::string> &Args) {
auto Copy = Values;
for (std::vector<std::string> &ArgV : Copy) {
for (const std::string &Arg : Args) {
ArgV.push_back(Arg);
}
}
return ::testing::ValuesIn(Copy);
}
struct ImportWithExternalSource : ASTImporterOptionSpecificTestBase {
ImportWithExternalSource() {
Creator = [](ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
bool MinimalImport,
const std::shared_ptr<ASTImporterSharedState> &SharedState) {
return new ASTImporter(ToContext, ToFileManager, FromContext,
FromFileManager, MinimalImport,
// We use the regular lookup.
/*SharedState=*/nullptr);
};
}
};
/// An ExternalASTSource that keeps track of the tags is completed.
struct SourceWithCompletedTagList : clang::ExternalASTSource {
std::vector<clang::TagDecl *> &CompletedTags;
SourceWithCompletedTagList(std::vector<clang::TagDecl *> &CompletedTags)
: CompletedTags(CompletedTags) {}
void CompleteType(TagDecl *Tag) override {
auto *Record = cast<CXXRecordDecl>(Tag);
Record->startDefinition();
Record->completeDefinition();
CompletedTags.push_back(Tag);
}
using clang::ExternalASTSource::CompleteType;
};
TEST_P(ImportWithExternalSource, CompleteRecordBeforeImporting) {
// Create an empty TU.
TranslationUnitDecl *FromTU = getTuDecl("", Lang_CXX03, "input.cpp");
// Create and add the test ExternalASTSource.
std::vector<clang::TagDecl *> CompletedTags;
IntrusiveRefCntPtr<ExternalASTSource> source =
new SourceWithCompletedTagList(CompletedTags);
clang::ASTContext &Context = FromTU->getASTContext();
Context.setExternalSource(std::move(source));
// Create a dummy class by hand with external lexical storage.
IdentifierInfo &Ident = Context.Idents.get("test_class");
auto *Record = CXXRecordDecl::Create(
Context, TTK_Class, FromTU, SourceLocation(), SourceLocation(), &Ident);
Record->setHasExternalLexicalStorage();
FromTU->addDecl(Record);
// Do a minimal import of the created class.
EXPECT_EQ(0U, CompletedTags.size());
Import(Record, Lang_CXX03);
EXPECT_EQ(0U, CompletedTags.size());
// Import the definition of the created class.
llvm::Error Err = findFromTU(Record)->Importer->ImportDefinition(Record);
EXPECT_FALSE((bool)Err);
consumeError(std::move(Err));
// Make sure the class was completed once.
EXPECT_EQ(1U, CompletedTags.size());
EXPECT_EQ(Record, CompletedTags.front());
}
TEST_P(ImportFunctions, CTADImplicit) {
Decl *FromTU = getTuDecl(
R"(
template <typename T> struct A {
A(T);
};
A a{(int)0};
)",
Lang_CXX17, "input.cc");
auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
FromTU,
cxxDeductionGuideDecl(hasParameter(0, hasType(asString("A<T>")))));
auto *ToD = Import(FromD, Lang_CXX17);
ASSERT_TRUE(ToD);
EXPECT_TRUE(ToD->isCopyDeductionCandidate());
// Check that the deduced class template is also imported.
EXPECT_TRUE(findFromTU(FromD)->Importer->GetAlreadyImportedOrNull(
FromD->getDeducedTemplate()));
}
TEST_P(ImportFunctions, CTADUserDefinedExplicit) {
Decl *FromTU = getTuDecl(
R"(
template <typename T> struct A {
A(T);
};
template <typename T> explicit A(T) -> A<float>;
A a{(int)0}; // calls A<float>::A(float)
)",
Lang_CXX17, "input.cc");
auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
FromTU, cxxDeductionGuideDecl(unless(isImplicit())));
// Not-implicit: i.e. not compiler-generated, user defined.
ASSERT_FALSE(FromD->isImplicit());
ASSERT_TRUE(FromD->isExplicit()); // Has the explicit keyword.
auto *ToD = Import(FromD, Lang_CXX17);
ASSERT_TRUE(ToD);
EXPECT_FALSE(FromD->isImplicit());
EXPECT_TRUE(ToD->isExplicit());
}
TEST_P(ImportFunctions, CTADWithLocalTypedef) {
Decl *TU = getTuDecl(
R"(
template <typename T> struct A {
typedef T U;
A(U);
};
A a{(int)0};
)",
Lang_CXX17, "input.cc");
auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
TU, cxxDeductionGuideDecl());
auto *ToD = Import(FromD, Lang_CXX17);
ASSERT_TRUE(ToD);
}
TEST_P(ImportFunctions, ParmVarDeclDeclContext) {
constexpr auto FromTUCode = R"(
void f(int P);
)";
Decl *FromTU = getTuDecl(FromTUCode, Lang_CXX11);
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
ASSERT_TRUE(FromF);
auto *ImportedF = Import(FromF, Lang_CXX11);
EXPECT_TRUE(ImportedF);
EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
ImportedF, ImportedF->getParamDecl(0)));
}
// FIXME Move these tests out of ASTImporterTest. For that we need to factor
// out the ASTImporter specific pars from ASTImporterOptionSpecificTestBase
// into a new test Fixture. Then we should lift up this Fixture to its own
// implementation file and only then could we reuse the Fixture in other AST
// unitttests.
struct CTAD : ASTImporterOptionSpecificTestBase {};
TEST_P(CTAD, DeductionGuideShouldReferToANonLocalTypedef) {
Decl *TU = getTuDecl(
R"(
typedef int U;
template <typename T> struct A {
A(U, T);
};
A a{(int)0, (int)0};
)",
Lang_CXX17, "input.cc");
auto *Guide = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
TU, cxxDeductionGuideDecl());
auto *Typedef = FirstDeclMatcher<TypedefNameDecl>().match(
TU, typedefNameDecl(hasName("U")));
ParmVarDecl *Param = Guide->getParamDecl(0);
// The type of the first param (which is a typedef) should match the typedef
// in the global scope.
EXPECT_EQ(Param->getType()->castAs<TypedefType>()->getDecl(), Typedef);
}
TEST_P(CTAD, DeductionGuideShouldReferToANonLocalTypedefInParamPtr) {
Decl *TU = getTuDecl(
R"(
typedef int U;
template <typename T> struct A {
A(U*, T);
};
A a{(int*)0, (int)0};
)",
Lang_CXX17, "input.cc");
auto *Guide = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
TU, cxxDeductionGuideDecl());
auto *Typedef = FirstDeclMatcher<TypedefNameDecl>().match(
TU, typedefNameDecl(hasName("U")));
ParmVarDecl *Param = Guide->getParamDecl(0);
EXPECT_EQ(Param->getType()
->getAs<PointerType>()
->getPointeeType()
->getAs<TypedefType>()
->getDecl(),
Typedef);
}
TEST_P(CTAD, DeductionGuideShouldCopyALocalTypedef) {
Decl *TU = getTuDecl(
R"(
template <typename T> struct A {
typedef T U;
A(U, T);
};
A a{(int)0, (int)0};
)",
Lang_CXX17, "input.cc");
auto *Guide = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
TU, cxxDeductionGuideDecl());
auto *Typedef = FirstDeclMatcher<TypedefNameDecl>().match(
TU, typedefNameDecl(hasName("U")));
ParmVarDecl *Param = Guide->getParamDecl(0);
EXPECT_NE(Param->getType()->castAs<TypedefType>()->getDecl(), Typedef);
}
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, CTAD,
DefaultTestValuesForRunOptions);
TEST_P(ASTImporterOptionSpecificTestBase, TypedefWithAttribute) {
Decl *TU = getTuDecl(
R"(
namespace N {
typedef int X __attribute__((annotate("A")));
}
)",
Lang_CXX17, "input.cc");
auto *FromD =
FirstDeclMatcher<TypedefDecl>().match(TU, typedefDecl(hasName("X")));
auto *ToD = Import(FromD, Lang_CXX17);
ASSERT_TRUE(ToD);
ASSERT_EQ(ToD->getAttrs().size(), 1U);
auto *ToAttr = dyn_cast<AnnotateAttr>(ToD->getAttrs()[0]);
ASSERT_TRUE(ToAttr);
EXPECT_EQ(ToAttr->getAnnotation(), "A");
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportOfTemplatedDeclWhenPreviousDeclHasNoDescribedTemplateSet) {
Decl *FromTU = getTuDecl(
R"(
namespace std {
template<typename T>
class basic_stringbuf;
}
namespace std {
class char_traits;
template<typename T = char_traits>
class basic_stringbuf;
}
namespace std {
template<typename T>
class basic_stringbuf {};
}
)",
Lang_CXX11);
auto *From1 = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU,
classTemplateDecl(hasName("basic_stringbuf"), unless(isImplicit())));
auto *To1 = cast_or_null<ClassTemplateDecl>(Import(From1, Lang_CXX11));
EXPECT_TRUE(To1);
auto *From2 = LastDeclMatcher<ClassTemplateDecl>().match(
FromTU,
classTemplateDecl(hasName("basic_stringbuf"), unless(isImplicit())));
auto *To2 = cast_or_null<ClassTemplateDecl>(Import(From2, Lang_CXX11));
EXPECT_TRUE(To2);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportOfCapturedVLAType) {
Decl *FromTU = getTuDecl(
R"(
void declToImport(int N) {
int VLA[N];
[&VLA] {}; // FieldDecl inside the lambda.
}
)",
Lang_CXX14);
auto *FromFD = FirstDeclMatcher<FieldDecl>().match(FromTU, fieldDecl());
ASSERT_TRUE(FromFD);
ASSERT_TRUE(FromFD->hasCapturedVLAType());
auto *ToFD = Import(FromFD, Lang_CXX14);
EXPECT_TRUE(ToFD);
EXPECT_TRUE(ToFD->hasCapturedVLAType());
EXPECT_NE(FromFD->getCapturedVLAType(), ToFD->getCapturedVLAType());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportEnumMemberSpecialization) {
Decl *FromTU = getTuDecl(
R"(
template <class T> struct A {
enum tagname { enumerator };
};
template struct A<int>;
)",
Lang_CXX03);
auto *FromD = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasName("tagname"),
hasParent(classTemplateSpecializationDecl())));
ASSERT_TRUE(FromD);
ASSERT_TRUE(FromD->getMemberSpecializationInfo());
auto *ToD = Import(FromD, Lang_CXX03);
EXPECT_TRUE(ToD);
EXPECT_TRUE(ToD->getMemberSpecializationInfo());
EXPECT_EQ(FromD->getTemplateSpecializationKind(),
ToD->getTemplateSpecializationKind());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportIsInheritingConstructorBit) {
Decl *FromTU = getTuDecl(
R"(
struct A {
A(int);
};
struct B : A {
using A::A; // Inherited ctor.
};
void f() {
(B(0));
}
)",
Lang_CXX11);
auto *FromD = FirstDeclMatcher<CXXConstructorDecl>().match(
FromTU, cxxConstructorDecl(isInheritingConstructor()));
ASSERT_TRUE(FromD);
ASSERT_TRUE(FromD->isInheritingConstructor());
auto *ToD = Import(FromD, Lang_CXX11);
ASSERT_TRUE(ToD);
EXPECT_TRUE(ToD->isInheritingConstructor());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportConstructorUsingShadow) {
TranslationUnitDecl *FromTU = getTuDecl(
R"(
struct A {
A(int, int);
};
struct B : A {
using A::A;
};
struct C : B {
using B::B;
};
)",
Lang_CXX11);
auto CheckAST = [](TranslationUnitDecl *TU, CXXRecordDecl *RecordC) {
auto *RecordA = FirstDeclMatcher<CXXRecordDecl>().match(
TU, cxxRecordDecl(hasName("A")));
auto *RecordB = FirstDeclMatcher<CXXRecordDecl>().match(
TU, cxxRecordDecl(hasName("B")));
auto *ConstrA = FirstDeclMatcher<CXXConstructorDecl>().match(
TU, cxxConstructorDecl(hasParent(equalsNode(RecordA)),
parameterCountIs(2)));
auto *ShadowBA = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordB)),
hasTargetDecl(equalsNode(ConstrA)))));
auto *ShadowCA = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordC)),
hasTargetDecl(equalsNode(ConstrA)))));
EXPECT_EQ(ShadowBA->getTargetDecl(), ConstrA);
EXPECT_EQ(ShadowBA->getNominatedBaseClass(), RecordA);
EXPECT_EQ(ShadowBA->getConstructedBaseClass(), RecordA);
EXPECT_EQ(ShadowBA->getNominatedBaseClassShadowDecl(), nullptr);
EXPECT_EQ(ShadowBA->getConstructedBaseClassShadowDecl(), nullptr);
EXPECT_FALSE(ShadowBA->constructsVirtualBase());
EXPECT_EQ(ShadowCA->getTargetDecl(), ConstrA);
EXPECT_EQ(ShadowCA->getNominatedBaseClass(), RecordB);
EXPECT_EQ(ShadowCA->getConstructedBaseClass(), RecordB);
EXPECT_EQ(ShadowCA->getNominatedBaseClassShadowDecl(), ShadowBA);
EXPECT_EQ(ShadowCA->getConstructedBaseClassShadowDecl(), ShadowBA);
EXPECT_FALSE(ShadowCA->constructsVirtualBase());
};
auto *FromC = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("C")));
auto *ToC = Import(FromC, Lang_CXX11);
TranslationUnitDecl *ToTU = ToC->getTranslationUnitDecl();
CheckAST(FromTU, FromC);
CheckAST(ToTU, ToC);
}
AST_MATCHER_P(UsingShadowDecl, hasIntroducerDecl, internal::Matcher<NamedDecl>,
InnerMatcher) {
return InnerMatcher.matches(*Node.getIntroducer(), Finder, Builder);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportConstructorUsingShadowVirtualBase) {
TranslationUnitDecl *FromTU = getTuDecl(
R"(
struct A { A(int, int); };
struct B : A { using A::A; };
struct V1 : virtual B { using B::B; };
struct V2 : virtual B { using B::B; };
struct D2 : V1, V2 {
using V1::V1;
using V2::V2;
};
)",
Lang_CXX11);
auto CheckAST = [](TranslationUnitDecl *TU, CXXRecordDecl *RecordD2) {
auto *RecordA = FirstDeclMatcher<CXXRecordDecl>().match(
TU, cxxRecordDecl(hasName("A")));
auto *RecordB = FirstDeclMatcher<CXXRecordDecl>().match(
TU, cxxRecordDecl(hasName("B")));
auto *RecordV1 = FirstDeclMatcher<CXXRecordDecl>().match(
TU, cxxRecordDecl(hasName("V1")));
auto *RecordV2 = FirstDeclMatcher<CXXRecordDecl>().match(
TU, cxxRecordDecl(hasName("V2")));
auto *ConstrA = FirstDeclMatcher<CXXConstructorDecl>().match(
TU, cxxConstructorDecl(hasParent(equalsNode(RecordA)),
parameterCountIs(2)));
auto *ConstrB = FirstDeclMatcher<CXXConstructorDecl>().match(
TU, cxxConstructorDecl(hasParent(equalsNode(RecordB)),
isCopyConstructor()));
auto *UsingD2V1 = FirstDeclMatcher<UsingDecl>().match(
TU, usingDecl(hasParent(equalsNode(RecordD2))));
auto *UsingD2V2 = LastDeclMatcher<UsingDecl>().match(
TU, usingDecl(hasParent(equalsNode(RecordD2))));
auto *ShadowBA = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordB)),
hasTargetDecl(equalsNode(ConstrA)))));
auto *ShadowV1A = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordV1)),
hasTargetDecl(equalsNode(ConstrA)))));
auto *ShadowV1B = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordV1)),
hasTargetDecl(equalsNode(ConstrB)))));
auto *ShadowV2A = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordV2)),
hasTargetDecl(equalsNode(ConstrA)))));
auto *ShadowV2B = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordV2)),
hasTargetDecl(equalsNode(ConstrB)))));
auto *ShadowD2V1A = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordD2)),
hasIntroducerDecl(equalsNode(UsingD2V1)),
hasTargetDecl(equalsNode(ConstrA)))));
auto *ShadowD2V1B = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordD2)),
hasIntroducerDecl(equalsNode(UsingD2V1)),
hasTargetDecl(equalsNode(ConstrB)))));
auto *ShadowD2V2A = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordD2)),
hasIntroducerDecl(equalsNode(UsingD2V2)),
hasTargetDecl(equalsNode(ConstrA)))));
auto *ShadowD2V2B = cast<ConstructorUsingShadowDecl>(
FirstDeclMatcher<UsingShadowDecl>().match(
TU, usingShadowDecl(hasParent(equalsNode(RecordD2)),
hasIntroducerDecl(equalsNode(UsingD2V2)),
hasTargetDecl(equalsNode(ConstrB)))));
EXPECT_EQ(ShadowD2V1A->getTargetDecl(), ConstrA);
EXPECT_EQ(ShadowD2V1A->getNominatedBaseClassShadowDecl(), ShadowV1A);
EXPECT_EQ(ShadowD2V1A->getNominatedBaseClass(), RecordV1);
EXPECT_EQ(ShadowD2V1A->getConstructedBaseClassShadowDecl(), ShadowBA);
EXPECT_EQ(ShadowD2V1A->getConstructedBaseClass(), RecordB);
EXPECT_TRUE(ShadowD2V1A->constructsVirtualBase());
EXPECT_EQ(ShadowD2V1B->getTargetDecl(), ConstrB);
EXPECT_EQ(ShadowD2V1B->getNominatedBaseClassShadowDecl(), ShadowV1B);
EXPECT_EQ(ShadowD2V1B->getNominatedBaseClass(), RecordV1);
EXPECT_EQ(ShadowD2V1B->getConstructedBaseClassShadowDecl(), nullptr);
EXPECT_EQ(ShadowD2V1B->getConstructedBaseClass(), RecordB);
EXPECT_TRUE(ShadowD2V1B->constructsVirtualBase());
EXPECT_EQ(ShadowD2V2A->getTargetDecl(), ConstrA);
EXPECT_EQ(ShadowD2V2A->getNominatedBaseClassShadowDecl(), ShadowV2A);
EXPECT_EQ(ShadowD2V2A->getNominatedBaseClass(), RecordV2);
EXPECT_EQ(ShadowD2V2A->getConstructedBaseClassShadowDecl(), ShadowBA);
EXPECT_EQ(ShadowD2V2A->getConstructedBaseClass(), RecordB);
EXPECT_TRUE(ShadowD2V2A->constructsVirtualBase());
EXPECT_EQ(ShadowD2V2B->getTargetDecl(), ConstrB);
EXPECT_EQ(ShadowD2V2B->getNominatedBaseClassShadowDecl(), ShadowV2B);
EXPECT_EQ(ShadowD2V2B->getNominatedBaseClass(), RecordV2);
EXPECT_EQ(ShadowD2V2B->getConstructedBaseClassShadowDecl(), nullptr);
EXPECT_EQ(ShadowD2V2B->getConstructedBaseClass(), RecordB);
EXPECT_TRUE(ShadowD2V2B->constructsVirtualBase());
EXPECT_TRUE(ShadowV1A->constructsVirtualBase());
EXPECT_TRUE(ShadowV1B->constructsVirtualBase());
EXPECT_TRUE(ShadowV2A->constructsVirtualBase());
EXPECT_TRUE(ShadowV2B->constructsVirtualBase());
EXPECT_FALSE(ShadowBA->constructsVirtualBase());
};
auto *FromD2 = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("D2")));
auto *ToD2 = Import(FromD2, Lang_CXX11);
TranslationUnitDecl *ToTU = ToD2->getTranslationUnitDecl();
CheckAST(FromTU, FromD2);
CheckAST(ToTU, ToD2);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportUsingShadowList) {
TranslationUnitDecl *FromTU = getTuDecl(
R"(
struct A {
void f();
void f(int);
};
struct B : A {
using A::f;
};
)",
Lang_CXX11);
auto *FromB = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("B")));
auto *ToB = Import(FromB, Lang_CXX11);
TranslationUnitDecl *ToTU = ToB->getTranslationUnitDecl();
auto *ToUsing = FirstDeclMatcher<UsingDecl>().match(
ToTU, usingDecl(hasParent(equalsNode(ToB))));
auto *ToUsingShadowF1 = FirstDeclMatcher<UsingShadowDecl>().match(
ToTU, usingShadowDecl(hasTargetDecl(
functionDecl(hasName("f"), parameterCountIs(0)))));
auto *ToUsingShadowF2 = FirstDeclMatcher<UsingShadowDecl>().match(
ToTU, usingShadowDecl(hasTargetDecl(
functionDecl(hasName("f"), parameterCountIs(1)))));
EXPECT_EQ(ToUsing->shadow_size(), 2u);
auto ShadowI = ToUsing->shadow_begin();
EXPECT_EQ(*ShadowI, ToUsingShadowF1);
++ShadowI;
EXPECT_EQ(*ShadowI, ToUsingShadowF2);
}
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ASTImporterLookupTableTest,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportPath,
::testing::Values(std::vector<std::string>()));
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportExpr,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportFixedPointExpr,
ExtendWithOptions(DefaultTestArrayForRunOptions,
std::vector<std::string>{
"-ffixed-point"}));
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportBlock,
ExtendWithOptions(DefaultTestArrayForRunOptions,
std::vector<std::string>{
"-fblocks"}));
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportType,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportDecl,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ASTImporterOptionSpecificTestBase,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ErrorHandlingTest,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, RedirectingImporterTest,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportFunctions,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportAutoFunctions,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportFunctionTemplates,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportFriendFunctionTemplates,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportClasses,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportFriendFunctions,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportFriendClasses,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests,
ImportFunctionTemplateSpecializations,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportImplicitMethods,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportVariables,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, LLDBLookupTest,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportSourceLocations,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportWithExternalSource,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportAttributes,
DefaultTestValuesForRunOptions);
} // end namespace ast_matchers
} // end namespace clang