llvm-project/clang/unittests/AST/ASTImporterTest.cpp
Matheus Izvekov 91cdd35008
[clang] Improve nested name specifier AST representation (#147835)
This is a major change on how we represent nested name qualifications in
the AST.

* The nested name specifier itself and how it's stored is changed. The
prefixes for types are handled within the type hierarchy, which makes
canonicalization for them super cheap, no memory allocation required.
Also translating a type into nested name specifier form becomes a no-op.
An identifier is stored as a DependentNameType. The nested name
specifier gains a lightweight handle class, to be used instead of
passing around pointers, which is similar to what is implemented for
TemplateName. There is still one free bit available, and this handle can
be used within a PointerUnion and PointerIntPair, which should keep
bit-packing aficionados happy.
* The ElaboratedType node is removed, all type nodes in which it could
previously apply to can now store the elaborated keyword and name
qualifier, tail allocating when present.
* TagTypes can now point to the exact declaration found when producing
these, as opposed to the previous situation of there only existing one
TagType per entity. This increases the amount of type sugar retained,
and can have several applications, for example in tracking module
ownership, and other tools which care about source file origins, such as
IWYU. These TagTypes are lazily allocated, in order to limit the
increase in AST size.

This patch offers a great performance benefit.

It greatly improves compilation time for
[stdexec](https://github.com/NVIDIA/stdexec). For one datapoint, for
`test_on2.cpp` in that project, which is the slowest compiling test,
this patch improves `-c` compilation time by about 7.2%, with the
`-fsyntax-only` improvement being at ~12%.

This has great results on compile-time-tracker as well:

![image](https://github.com/user-attachments/assets/700dce98-2cab-4aa8-97d1-b038c0bee831)

This patch also further enables other optimziations in the future, and
will reduce the performance impact of template specialization resugaring
when that lands.

It has some other miscelaneous drive-by fixes.

About the review: Yes the patch is huge, sorry about that. Part of the
reason is that I started by the nested name specifier part, before the
ElaboratedType part, but that had a huge performance downside, as
ElaboratedType is a big performance hog. I didn't have the steam to go
back and change the patch after the fact.

There is also a lot of internal API changes, and it made sense to remove
ElaboratedType in one go, versus removing it from one type at a time, as
that would present much more churn to the users. Also, the nested name
specifier having a different API avoids missing changes related to how
prefixes work now, which could make existing code compile but not work.

How to review: The important changes are all in
`clang/include/clang/AST` and `clang/lib/AST`, with also important
changes in `clang/lib/Sema/TreeTransform.h`.

The rest and bulk of the changes are mostly consequences of the changes
in API.

PS: TagType::getDecl is renamed to `getOriginalDecl` in this patch, just
for easier to rebasing. I plan to rename it back after this lands.

Fixes #136624
Fixes https://github.com/llvm/llvm-project/issues/43179
Fixes https://github.com/llvm/llvm-project/issues/68670
Fixes https://github.com/llvm/llvm-project/issues/92757
2025-08-09 05:06:53 -03:00

10727 lines
360 KiB
C++

//===- 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/AST/ExprConcepts.h"
#include "clang/AST/RecordLayout.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Testing/CommandLineArgs.h"
#include "llvm/Support/SmallVectorMemoryBuffer.h"
#include "clang/AST/DeclContextInternals.h"
#include "gtest/gtest.h"
#include "ASTImporterFixtures.h"
#include <optional>
namespace clang {
namespace ast_matchers {
using internal::Matcher;
static const RecordDecl *getRecordDeclOfFriend(FriendDecl *FD) {
QualType Ty = FD->getFriendType()->getType().getCanonicalType();
return cast<RecordType>(Ty)->getOriginalDecl();
}
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_FILE_NAME(); }", Lang_CXX03,
"", Lang_CXX03, Verifier,
functionDecl(hasDescendant(
sourceLocExpr(hasBuiltinStr("__builtin_FILE_NAME")))));
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(constantExpr(has(integerLiteral(equals(0))))),
has(constantExpr(has(integerLiteral(equals(1))))),
has(constantExpr(has(integerLiteral(equals(2))))),
has(constantExpr(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("point")), has(floatLiteral(equals(1.0))),
has(implicitValueInitExpr(hasType(asString("double")))))),
has(initListExpr(hasType(asString("point")),
has(floatLiteral(equals(2.0))),
has(floatLiteral(equals(1.0)))))))));
}
const internal::VariadicDynCastAllOfMatcher<Expr, CXXDefaultInitExpr>
cxxDefaultInitExpr;
TEST_P(ImportExpr, ImportCXXDefaultInitExpr) {
MatchVerifier<Decl> Verifier;
testImport("class declToImport { int DefInit = 5; }; declToImport X;",
Lang_CXX11, "", Lang_CXX11, Verifier,
cxxRecordDecl(hasDescendant(cxxConstructorDecl(
hasAnyConstructorInitializer(cxxCtorInitializer(
withInitializer(cxxDefaultInitExpr())))))));
testImport(
"struct X { int A = 5; }; X declToImport{};", Lang_CXX17, "", Lang_CXX17,
Verifier,
varDecl(hasInitializer(initListExpr(hasInit(0, cxxDefaultInitExpr())))));
}
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())))));
}
const internal::VariadicDynCastAllOfMatcher<Stmt, BuiltinBitCastExpr>
builtinBitCastExpr;
TEST_P(ImportExpr, ImportBuiltinBitCastExpr) {
MatchVerifier<Decl> Verifier;
testImport("void declToImport(int X) {"
" (void)__builtin_bit_cast(float, X); }",
Lang_CXX20, "", Lang_CXX20, Verifier,
functionDecl(hasDescendant(
cStyleCastExpr(hasSourceExpression(builtinBitCastExpr())))));
}
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(ImportType, ImportBitIntType) {
const AstTypeMatcher<BitIntType> bitIntType;
MatchVerifier<Decl> Verifier;
testImport("_BitInt(10) declToImport;", Lang_CXX11, "", Lang_CXX11, Verifier,
varDecl(hasType(bitIntType())));
}
TEST_P(ImportType, ImportDependentBitIntType) {
const AstTypeMatcher<DependentBitIntType> dependentBitIntType;
MatchVerifier<Decl> Verifier;
testImport("template<int Width> using declToImport = _BitInt(Width);",
Lang_CXX11, "", Lang_CXX11, Verifier,
typeAliasTemplateDecl(
has(typeAliasDecl(hasType(dependentBitIntType())))));
}
TEST_P(ImportType, ImportDependentAddressSpaceType) {
const AstTypeMatcher<DependentAddressSpaceType> dependentAddressSpaceType;
MatchVerifier<Decl> Verifier;
testImport(
R"(
template<typename T, int AddrSpace>
using declToImport = T __attribute__((address_space(AddrSpace)));
)",
Lang_CXX11, "", Lang_CXX11, Verifier,
typeAliasTemplateDecl(
has(typeAliasDecl(hasType(dependentAddressSpaceType())))));
}
TEST_P(ImportType, ImportVectorType) {
const AstTypeMatcher<VectorType> vectorType;
MatchVerifier<Decl> Verifier;
testImport("typedef int __attribute__((vector_size(12))) declToImport;",
Lang_CXX11, "", Lang_CXX11, Verifier,
typedefDecl(hasType(vectorType())));
}
TEST_P(ImportType, ImportDependentVectorType) {
const AstTypeMatcher<DependentVectorType> dependentVectorType;
MatchVerifier<Decl> Verifier;
testImport(
R"(
template<typename T, int Size>
using declToImport = T __attribute__((vector_size(Size)));
)",
Lang_CXX11, "", Lang_CXX11, Verifier,
typeAliasTemplateDecl(
has(typeAliasDecl(hasType(dependentVectorType())))));
}
struct ImportOpenCLPipe : ImportType {
std::vector<std::string> getExtraArgs() const override {
return {"-x", "cl", "-cl-no-stdinc", "-cl-std=CL2.0"};
}
};
TEST_P(ImportOpenCLPipe, ImportPipeType) {
const AstTypeMatcher<PipeType> pipeType;
MatchVerifier<Decl> Verifier;
testImport("typedef pipe int declToImport;", Lang_OpenCL, "", Lang_OpenCL,
Verifier, typedefDecl(hasType(pipeType())));
}
struct ImportMatrixType : ImportType {
std::vector<std::string> getExtraArgs() const override {
return {"-fenable-matrix"};
}
};
TEST_P(ImportMatrixType, ImportConstantMatrixType) {
const AstTypeMatcher<ConstantMatrixType> constantMatrixType;
MatchVerifier<Decl> Verifier;
testImport("typedef int __attribute__((matrix_type(5, 5))) declToImport;",
Lang_CXX11, "", Lang_CXX11, Verifier,
typedefDecl(hasType(constantMatrixType())));
}
TEST_P(ImportMatrixType, ImportDependentSizedMatrixType) {
const AstTypeMatcher<DependentSizedMatrixType> dependentSizedMatrixType;
MatchVerifier<Decl> Verifier;
testImport(
R"(
template<typename T, int Rows, int Cols>
using declToImport = T __attribute__((matrix_type(Rows, Cols)));
)",
Lang_CXX11, "", Lang_CXX11, Verifier,
typeAliasTemplateDecl(
has(typeAliasDecl(hasType(dependentSizedMatrixType())))));
}
TEST_P(ImportType, ImportUsingType) {
MatchVerifier<Decl> Verifier;
testImport("struct C {};"
"void declToImport() { using ::C; new C{}; }",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionDecl(hasDescendant(
cxxNewExpr(hasType(pointerType(pointee(usingType())))))));
}
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()))))))));
}
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))))))))));
}
TEST_P(ImportExpr, ImportCXXFoldExpr) {
auto Match1 = cxxFoldExpr(hasOperatorName("+"), isLeftFold(),
unless(hasFoldInit(expr())));
auto Match2 =
cxxFoldExpr(hasOperatorName("-"), isLeftFold(), hasFoldInit(expr()));
auto Match3 = cxxFoldExpr(hasOperatorName("*"), isRightFold(),
unless(hasFoldInit(expr())));
auto Match4 =
cxxFoldExpr(hasOperatorName("/"), isRightFold(), hasFoldInit(expr()));
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, ImportUsingTemplate) {
MatchVerifier<Decl> Verifier;
testImport("namespace ns { template <typename T> struct S {}; }"
"template <template <typename> class T> class X {};"
"void declToImport() {"
"using ns::S; X<S> xi; }",
Lang_CXX11, "", Lang_CXX11, Verifier,
functionDecl(
hasDescendant(varDecl(hasTypeLoc(templateSpecializationTypeLoc(
hasAnyTemplateArgumentLoc(templateArgumentLoc())))))));
}
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")))));
}
const internal::VariadicDynCastAllOfMatcher<Decl, UsingPackDecl> usingPackDecl;
TEST_P(ImportDecl, ImportUsingPackDecl) {
MatchVerifier<Decl> Verifier;
testImport(
"struct A { int operator()() { return 1; } };"
"struct B { int operator()() { return 2; } };"
"template<typename ...T> struct C : T... { using T::operator()...; };"
"C<A, B> declToImport;",
Lang_CXX20, "", Lang_CXX20, Verifier,
varDecl(hasType(templateSpecializationType(hasDeclaration(
classTemplateSpecializationDecl(hasDescendant(usingPackDecl())))))));
}
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(ImportExpr, DependentSizedExtVectorType) {
MatchVerifier<Decl> Verifier;
testImport("template<typename T, int Size>"
"class declToImport {"
" typedef T __attribute__((ext_vector_type(Size))) type;"
"};",
Lang_CXX03, "", Lang_CXX03, Verifier,
classTemplateDecl(has(cxxRecordDecl(
has(typedefDecl(hasType(dependentSizedExtVectorType())))))));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportUsingPackDecl) {
Decl *FromTU = getTuDecl(
"struct A { int operator()() { return 1; } };"
"struct B { int operator()() { return 2; } };"
"template<typename ...T> struct C : T... { using T::operator()...; };"
"C<A, B> Var;",
Lang_CXX20);
auto From = FirstDeclMatcher<UsingPackDecl>().match(FromTU, usingPackDecl());
ASSERT_TRUE(From);
auto To = cast<UsingPackDecl>(Import(From, Lang_CXX20));
ASSERT_TRUE(To);
ArrayRef<NamedDecl *> FromExpansions = From->expansions();
ArrayRef<NamedDecl *> ToExpansions = To->expansions();
ASSERT_EQ(FromExpansions.size(), ToExpansions.size());
for (unsigned int I = 0; I < FromExpansions.size(); ++I) {
auto ImportedExpansion = Import(FromExpansions[I], Lang_CXX20);
EXPECT_EQ(ImportedExpansion, ToExpansions[I]);
}
auto ImportedDC = cast<Decl>(Import(From->getDeclContext(), Lang_CXX20));
EXPECT_EQ(ImportedDC, cast<Decl>(To->getDeclContext()));
}
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().getArgument().getAsType();
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().getArgument().getAsExpr();
ASSERT_TRUE(isa<IntegerLiteral>(ToArg));
ASSERT_EQ(cast<IntegerLiteral>(ToArg)->getValue().getLimitedValue(), 1U);
}
TEST_P(ASTImporterOptionSpecificTestBase, TemplateArgumentsDefaulted) {
Decl *FromTU = getTuDecl(R"(
template<typename T> struct X {};
template<typename TP = double,
int NTTP = 50,
template<typename> typename TT = X> struct S {};
S<> s;
)",
Lang_CXX17);
auto *FromSpec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl(hasName("S")));
ASSERT_TRUE(FromSpec);
auto *ToSpec = Import(FromSpec, Lang_CXX03);
ASSERT_TRUE(ToSpec);
auto const &TList = ToSpec->getTemplateArgs();
for (auto const &Arg : TList.asArray()) {
ASSERT_TRUE(Arg.getIsDefaulted());
}
}
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,
ImportTemplateSpecializationStaticMember) {
auto FromCode =
R"(
template <typename H> class Test{
public:
static const unsigned int length;
};
template<> const unsigned int Test<int>::length;
template<> const unsigned int Test<int>::length = 0;
)";
auto ToCode =
R"(
template <typename H> class Test {
public:
static const unsigned int length;
};
template <> const unsigned int Test<int>::length;
void foo() { int i = 1 / Test<int>::length; }
)";
Decl *FromTU = getTuDecl(FromCode, Lang_CXX14);
auto FromDecl = FirstDeclMatcher<VarDecl>().match(
FromTU, varDecl(hasName("length"), isDefinition()));
Decl *ToTu = getToTuDecl(ToCode, Lang_CXX14);
auto ToX = Import(FromDecl, Lang_CXX03);
auto ToDecl = FirstDeclMatcher<VarDecl>().match(
ToTu, varDecl(hasName("length"), isDefinition()));
EXPECT_TRUE(ToX);
EXPECT_EQ(ToX, ToDecl);
}
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, ImportConvertVectorExpr) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
"typedef double v4double __attribute__((__vector_size__(32)));"
"typedef float v4float __attribute__((__vector_size__(16)));"
"v4float vf;"
"void declToImport() { (void)__builtin_convertvector(vf, v4double); }",
Lang_CXX03, "", Lang_CXX03);
auto ToResults =
match(convertVectorExpr().bind("convert"), To->getASTContext());
auto FromResults =
match(convertVectorExpr().bind("convert"), From->getASTContext());
const ConvertVectorExpr *FromConvertVectorExpr =
selectFirst<ConvertVectorExpr>("convert", FromResults);
ASSERT_TRUE(FromConvertVectorExpr);
const ConvertVectorExpr *ToConvertVectorExpr =
selectFirst<ConvertVectorExpr>("convert", ToResults);
ASSERT_TRUE(ToConvertVectorExpr);
}
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,
ImportVirtualOverriddenMethodOnALoop) {
// B::f() calls => f1() ==> C ==> C::f()
// \
// \---- A::f()
//
// C::f()'s ImportOverriddenMethods() asserts B::isVirtual(), so B::f()'s
// ImportOverriddenMethods() should be completed before B::f()'s body
const char *Code =
R"(
void f1();
class A {
virtual void f(){}
};
class B: public A {
void f() override {
f1();
}
};
class C: public B {
void f() override {}
};
void f1() { C c; }
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromF = FirstDeclMatcher<CXXMethodDecl>().match(
FromTU, cxxMethodDecl(hasName("B::f")));
auto *ToBF = Import(FromF, Lang_CXX11);
EXPECT_TRUE(ToBF->isVirtual());
auto *ToCF = FirstDeclMatcher<CXXMethodDecl>().match(
ToBF->getTranslationUnitDecl(), cxxMethodDecl(hasName("C::f")));
EXPECT_TRUE(ToCF->isVirtual());
}
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_TRUE(ToTU->getASTContext().hasSameType(
(*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_TRUE(ToTU->getASTContext().hasSameType(
(*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"})));
}
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())))))))));
}
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())))))))));
}
TEST_P(ImportExpr, ConceptNoRequirement) {
MatchVerifier<Decl> Verifier;
const char *Code = R"(
template<typename T>
struct is_int { static const bool value = false; };
template<> struct is_int<int> { static const bool value = true; };
template<typename T>
concept declToImport = is_int<T>::value;
)";
testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
conceptDecl(unless(has(requiresExpr()))));
}
TEST_P(ImportExpr, ConceptSimpleRequirement) {
MatchVerifier<Decl> Verifier;
const char *Code = R"(
template <class T1, class T2>
concept declToImport = requires(T1 i, T2 j) {
i + j;
};
)";
testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
conceptDecl(has(requiresExpr(has(requiresExprBodyDecl())))));
}
TEST_P(ImportExpr, ConceptCompoundNonTypeRequirement) {
MatchVerifier<Decl> Verifier;
const char *Code = R"(
template <class T1, class T2>
concept declToImport = requires(T1 i, T2 j) {
{i + j};
};
)";
testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
conceptDecl(has(requiresExpr(has(requiresExprBodyDecl())))));
}
TEST_P(ImportExpr, ConceptCompoundTypeRequirement) {
MatchVerifier<Decl> Verifier;
const char *Code = R"(
template<typename T>
struct is_int { static const bool value = false; };
template<> struct is_int<int> { static const bool value = true; };
template<typename T>
concept type_is_int = is_int<T>::value;
template<typename T>
concept declToImport = requires(T x) {
{x * 1} -> type_is_int;
};
)";
testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
conceptDecl(has(requiresExpr(has(requiresExprBodyDecl())))));
}
TEST_P(ImportExpr, ConceptTypeRequirement) {
MatchVerifier<Decl> Verifier;
const char *Code = R"(
template <class T>
concept declToImport = requires {
typename T::value;
};
)";
testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
conceptDecl(has(requiresExpr(has(requiresExprBodyDecl())))));
}
TEST_P(ImportExpr, ConceptNestedRequirement) {
MatchVerifier<Decl> Verifier;
const char *Code = R"(
template<typename T>
struct is_int { static const bool value = false; };
template<> struct is_int<int> { static const bool value = true; };
template<typename T>
concept declToImport = requires(T x) {
requires is_int<T>::value;
};
)";
testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
conceptDecl(has(requiresExpr(has(requiresExprBodyDecl())))));
}
TEST_P(ImportExpr, ConceptNestedNonInstantiationDependentRequirement) {
MatchVerifier<Decl> Verifier;
const char *Code = R"(
template<typename T>
concept declToImport = requires {
requires sizeof(long) == sizeof(int);
};
)";
testImport(Code, Lang_CXX20, "", Lang_CXX20, Verifier,
conceptDecl(has(requiresExpr(has(requiresExprBodyDecl())))));
}
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());
auto *ToField = Import(FromF, Lang_CXX03);
EXPECT_TRUE(ToField->isBitField());
EXPECT_EQ(3u, ToField->getBitWidthValue());
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 ASTImportError &Error) {
EXPECT_EQ(ASTImportError::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());
ASSERT_FALSE(FromVar->isExplicitObjectParameter());
const auto *ToVar = Import(FromVar, Lang_CXX11);
EXPECT_TRUE(ToVar);
EXPECT_TRUE(ToVar->hasUninstantiatedDefaultArg());
EXPECT_TRUE(ToVar->getUninstantiatedDefaultArg());
EXPECT_NE(FromVar->getUninstantiatedDefaultArg(),
ToVar->getUninstantiatedDefaultArg());
EXPECT_FALSE(ToVar->isExplicitObjectParameter());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportParmVarDecl_Explicit) {
const auto *Code = R"(
struct Wrapper {
void func(this Wrapper) {}
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX23);
auto *FromVar = FirstDeclMatcher<ParmVarDecl>().match(FromTU, parmVarDecl());
ASSERT_TRUE(FromVar);
ASSERT_TRUE(FromVar->isExplicitObjectParameter());
const auto *ToVar = Import(FromVar, Lang_CXX23);
EXPECT_TRUE(ToVar);
EXPECT_TRUE(ToVar->isExplicitObjectParameter());
EXPECT_NE(ToVar->getExplicitObjectParamThisLoc(),
FromVar->getExplicitObjectParamThisLoc());
}
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);
UnsignedOrNone 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 {
void testRecursiveFriendClassTemplate(Decl *FromTu) {
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTu, classTemplateDecl());
auto Pattern = classTemplateDecl(
has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
auto *FromFriend =
FirstDeclMatcher<FriendDecl>().match(FromD, friendDecl());
auto *FromRecordOfFriend =
cast<ClassTemplateDecl>(FromFriend->getFriendDecl())
->getTemplatedDecl();
EXPECT_NE(FromRecordOfFriend, FromD->getTemplatedDecl());
EXPECT_TRUE(FromRecordOfFriend->getPreviousDecl() == nullptr);
auto *FromDC = FromRecordOfFriend->getDeclContext();
auto *FromLexicalDC = FromRecordOfFriend->getLexicalDeclContext();
ASSERT_EQ(FromDC, cast<DeclContext>(FromTu));
ASSERT_EQ(FromLexicalDC, cast<DeclContext>(FromD->getTemplatedDecl()));
ASSERT_FALSE(FromDC->containsDecl(FromRecordOfFriend));
ASSERT_FALSE(FromLexicalDC->containsDecl(FromRecordOfFriend));
ASSERT_FALSE(cast<RecordDecl>(FromRecordOfFriend)
->getLookupParent()
->lookup(FromRecordOfFriend->getDeclName())
.empty());
auto *ToD = Import(FromD, Lang_CXX03);
EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
auto *ToFriend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
auto *ToRecordOfFriend =
cast<ClassTemplateDecl>(ToFriend->getFriendDecl())->getTemplatedDecl();
EXPECT_NE(ToRecordOfFriend, ToD->getTemplatedDecl());
EXPECT_TRUE(ToRecordOfFriend->getPreviousDecl() == nullptr);
auto *ToDC = ToRecordOfFriend->getDeclContext();
auto *ToLexicalDC = ToRecordOfFriend->getLexicalDeclContext();
ASSERT_EQ(ToDC, cast<DeclContext>(ToD->getTranslationUnitDecl()));
ASSERT_EQ(ToLexicalDC, cast<DeclContext>(ToD->getTemplatedDecl()));
ASSERT_FALSE(ToDC->containsDecl(ToRecordOfFriend));
ASSERT_FALSE(ToLexicalDC->containsDecl(ToRecordOfFriend));
ASSERT_FALSE(cast<RecordDecl>(ToRecordOfFriend)
->getLookupParent()
->lookup(ToRecordOfFriend->getDeclName())
.empty());
}
void testRepeatedFriendImport(const char *Code) {
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, 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");
testRecursiveFriendClassTemplate(FromTu);
}
TEST_P(ImportFriendClasses,
ImportOfRecursiveFriendClassTemplateWithNonTypeParm) {
Decl *FromTu = getTuDecl(
R"(
template<class A1, A1 A> class declToImport {
template<class B1, B1> friend class declToImport;
};
)",
Lang_CXX03, "input.cc");
testRecursiveFriendClassTemplate(FromTu);
}
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_TRUE(declaresSameEntity(Imported0, Imported1));
}
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, SkipComparingFriendTemplateDepth) {
Decl *ToTU = getToTuDecl(
R"(
template <class T, T U>
class A;
template <class T, T U>
class A {
public:
template <class P, P Q>
friend class A;
A(T x) :x(x) {}
private:
T x;
};
)",
Lang_CXX11);
auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
ToTU,
classTemplateDecl(has(cxxRecordDecl(hasDefinition(), hasName("A")))));
Decl *FromTU = getTuDecl(
R"(
template <class T, T U>
class A;
template <class T, T U>
class A {
public:
template <class P, P Q>
friend class A;
A(T x) : x(x) {}
private:
T x;
};
A<int,3> a1(0);
)",
Lang_CXX11, "input1.cc");
auto *FromA = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU,
classTemplateDecl(has(cxxRecordDecl(hasDefinition(), hasName("A")))));
auto *ToA = Import(FromA, Lang_CXX11);
EXPECT_TRUE(ToA);
const ASTContext &Ctx = ToTU->getASTContext();
EXPECT_TRUE(
Ctx.hasSameType(Ctx.getCanonicalTagType(Fwd->getTemplatedDecl()),
Ctx.getCanonicalTagType(ToA->getTemplatedDecl())));
}
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)->getOriginalDecl();
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,
ImportFriendTemplatesInDependentContext_DefToFriend) {
Decl *ToTU = getToTuDecl(
R"(
template<class T1>
struct X {
template<class T2>
friend struct Y;
};
)",
Lang_CXX03);
auto *ToYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("Y")));
Decl *FromTU = getTuDecl(
R"(
template<class T1>
struct Y {};
)",
Lang_CXX03, "input0.cc");
auto *FromYDef = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("Y")));
auto *ImportedYDef = Import(FromYDef, Lang_CXX03);
EXPECT_TRUE(ImportedYDef);
EXPECT_FALSE(ImportedYDef->getPreviousDecl());
EXPECT_NE(ImportedYDef, ToYFriend);
}
TEST_P(ImportFriendClasses,
ImportFriendTemplatesInDependentContext_DefToFriend_NE) {
getToTuDecl(
R"(
template<class T1>
struct X {
template<class T2>
friend struct Y;
};
)",
Lang_CXX03);
Decl *FromTU = getTuDecl(
R"(
template<class T1, class T2>
struct Y {};
)",
Lang_CXX03, "input0.cc");
auto *FromYDef = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("Y")));
auto *ImportedYDef = Import(FromYDef, Lang_CXX03);
EXPECT_FALSE(ImportedYDef);
}
TEST_P(ImportFriendClasses,
ImportFriendTemplatesInDependentContext_FriendToFriend) {
Decl *ToTU = getToTuDecl(
R"(
template<class T1>
struct X {
template<class T2>
friend struct Y;
};
)",
Lang_CXX03);
auto *ToYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("Y")));
Decl *FromTU = getTuDecl(
R"(
template<class T1>
struct X {
template<class T2>
friend struct Y;
};
)",
Lang_CXX03, "input0.cc");
auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("Y")));
auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03);
EXPECT_TRUE(ImportedYFriend);
EXPECT_FALSE(ImportedYFriend->getPreviousDecl());
EXPECT_NE(ImportedYFriend, ToYFriend);
}
TEST_P(ImportFriendClasses,
ImportFriendTemplatesInDependentContext_FriendToFriend_NE) {
getToTuDecl(
R"(
template<class T1>
struct X {
template<class T2>
friend struct Y;
};
)",
Lang_CXX03);
Decl *FromTU = getTuDecl(
R"(
template<class T1>
struct X {
template<class T2, class T3>
friend struct Y;
};
)",
Lang_CXX03, "input0.cc");
auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("Y")));
auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03);
EXPECT_FALSE(ImportedYFriend);
}
TEST_P(ImportFriendClasses,
ImportFriendTemplatesInDependentContext_FriendToDef) {
Decl *ToTU = getToTuDecl(
R"(
template<class T1>
struct Y {};
)",
Lang_CXX03);
auto *ToYDef = FirstDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("Y")));
Decl *FromTU = getTuDecl(
R"(
template<class T1>
struct X {
template<class T2>
friend struct Y;
};
)",
Lang_CXX03, "input0.cc");
auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("Y")));
auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03);
EXPECT_TRUE(ImportedYFriend);
EXPECT_FALSE(ImportedYFriend->getPreviousDecl());
EXPECT_NE(ImportedYFriend, ToYDef);
}
TEST_P(ImportFriendClasses,
ImportFriendTemplatesInDependentContext_FriendToDef_NE) {
getToTuDecl(
R"(
template<class T1>
struct Y {};
)",
Lang_CXX03);
Decl *FromTU = getTuDecl(
R"(
template<class T1>
struct X {
template<class T2, class T3>
friend struct Y;
};
)",
Lang_CXX03, "input0.cc");
auto *FromYFriend = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("Y")));
auto *ImportedYFriend = Import(FromYFriend, Lang_CXX03);
EXPECT_FALSE(ImportedYFriend);
}
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendType) {
const char *Code =
R"(
class Container {
friend class X;
friend class X;
};
)";
testRepeatedFriendImport(Code);
}
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendDecl) {
const char *Code =
R"(
class Container {
friend void f();
friend void f();
};
)";
testRepeatedFriendImport(Code);
}
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendFunctionTemplateDecl) {
const char *Code =
R"(
template <class T>
class Container {
template <class U> friend void m();
template <class U> friend void m();
};
)";
testRepeatedFriendImport(Code);
}
TEST_P(ImportFriendClasses, ImportOfRepeatedFriendClassTemplateDecl) {
const char *Code =
R"(
template <class T>
class Container {
template <class U> friend class X;
template <class U> friend class X;
};
)";
testRepeatedFriendImport(Code);
}
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));
// Make sure we do not leave a StoredDeclsList with no entries.
DC->removeDecl(A1);
ASSERT_EQ(Map->find(A1->getDeclName()), Map->end());
}
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'
`-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, RecordVarTemplateDecl) {
Decl *ToTU = getToTuDecl(
R"(
template <class T>
class A {
public:
template <class U>
static constexpr bool X = true;
};
)",
Lang_CXX14);
auto *ToTUX = FirstDeclMatcher<VarTemplateDecl>().match(
ToTU, varTemplateDecl(hasName("X")));
Decl *FromTU = getTuDecl(
R"(
template <class T>
class A {
public:
template <class U>
static constexpr bool X = true;
};
)",
Lang_CXX14, "input1.cc");
auto *FromX = FirstDeclMatcher<VarTemplateDecl>().match(
FromTU, varTemplateDecl(hasName("X")));
auto *ToX = Import(FromX, Lang_CXX11);
EXPECT_TRUE(ToX);
EXPECT_EQ(ToTUX, ToX);
}
TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateDeclConflict) {
getToTuDecl(
R"(
template <class U>
constexpr int X = 1;
)",
Lang_CXX14);
Decl *FromTU = getTuDecl(
R"(
template <class U>
constexpr int X = 2;
)",
Lang_CXX14, "input1.cc");
auto *FromX = FirstDeclMatcher<VarTemplateDecl>().match(
FromTU, varTemplateDecl(hasName("X")));
auto *ToX = Import(FromX, Lang_CXX11);
// FIXME: This import should fail.
EXPECT_TRUE(ToX);
}
TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateStaticDefinition) {
Decl *ToTU = getToTuDecl(
R"(
struct A {
template <class U>
static int X;
};
)",
Lang_CXX14);
auto *ToX = FirstDeclMatcher<VarTemplateDecl>().match(
ToTU, varTemplateDecl(hasName("X")));
ASSERT_FALSE(ToX->isThisDeclarationADefinition());
Decl *FromTU = getTuDecl(
R"(
struct A {
template <class U>
static int X;
};
template <class U>
int A::X = 2;
)",
Lang_CXX14, "input1.cc");
auto *FromXDef = LastDeclMatcher<VarTemplateDecl>().match(
FromTU, varTemplateDecl(hasName("X")));
ASSERT_TRUE(FromXDef->isThisDeclarationADefinition());
auto *ToXDef = Import(FromXDef, Lang_CXX14);
EXPECT_TRUE(ToXDef);
EXPECT_TRUE(ToXDef->isThisDeclarationADefinition());
EXPECT_EQ(ToXDef->getPreviousDecl(), ToX);
}
TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateSpecializationDeclValue) {
Decl *ToTU = getToTuDecl(
R"(
template <class U>
constexpr int X = U::Value;
struct A { static constexpr int Value = 1; };
constexpr int Y = X<A>;
)",
Lang_CXX14);
auto *ToTUX = FirstDeclMatcher<VarTemplateSpecializationDecl>().match(
ToTU, varTemplateSpecializationDecl(hasName("X")));
Decl *FromTU = getTuDecl(
R"(
template <class U>
constexpr int X = U::Value;
struct A { static constexpr int Value = 1; };
constexpr int Y = X<A>;
)",
Lang_CXX14, "input1.cc");
auto *FromX = FirstDeclMatcher<VarTemplateSpecializationDecl>().match(
FromTU, varTemplateSpecializationDecl(hasName("X")));
auto *ToX = Import(FromX, Lang_CXX14);
EXPECT_TRUE(ToX);
EXPECT_EQ(ToTUX, ToX);
}
TEST_P(ASTImporterOptionSpecificTestBase,
VarTemplateSpecializationDeclValueConflict) {
getToTuDecl(
R"(
template <class U>
constexpr int X = U::Value;
struct A { static constexpr int Value = 1; };
constexpr int Y = X<A>;
)",
Lang_CXX14);
Decl *FromTU = getTuDecl(
R"(
template <class U>
constexpr int X = U::Value;
struct A { static constexpr int Value = 2; };
constexpr int Y = X<A>;
)",
Lang_CXX14, "input1.cc");
auto *FromX = FirstDeclMatcher<VarTemplateSpecializationDecl>().match(
FromTU, varTemplateSpecializationDecl(hasName("X")));
auto *ToX = Import(FromX, Lang_CXX14);
EXPECT_FALSE(ToX);
}
TEST_P(ASTImporterOptionSpecificTestBase, VarTemplateDeclInlineWithCXX17) {
Decl *FromTU = getTuDecl(
R"(
struct S {
template <unsigned> static constexpr bool X = true;
};
)",
Lang_CXX17, "input1.cc");
Decl *FromTU2 = getTuDecl(
R"(
struct S {
template <unsigned> static constexpr bool X = true;
template <typename T> void get() { X<sizeof(T)>; }
};
template <typename U> U qvariant_cast(const S &v) { return v.get; }
)",
Lang_CXX17, "input2.cc");
auto *FromX = FirstDeclMatcher<VarTemplateDecl>().match(
FromTU, varTemplateDecl(hasName("X")));
auto *ToX = Import(FromX, Lang_CXX17);
ASSERT_TRUE(ToX);
auto *FromX2 = FirstDeclMatcher<VarTemplateDecl>().match(
FromTU2, varTemplateDecl(hasName("X")));
auto *ToX2 = Import(FromX2, Lang_CXX17);
EXPECT_TRUE(ToX2);
EXPECT_EQ(ToX, ToX2);
}
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)));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportSubstTemplateTypeParmType) {
constexpr auto Code = R"(
template <class A1, class... A2> struct A {
using B = A1(A2...);
};
template struct A<void, char, float, int, short>;
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11, "input.cpp");
auto *FromClass = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl());
auto testType = [&](ASTContext &Ctx, const char *Name,
UnsignedOrNone PackIndex) {
const auto *Subst = selectFirst<SubstTemplateTypeParmType>(
"sttp", match(substTemplateTypeParmType(
hasReplacementType(hasCanonicalType(asString(Name))))
.bind("sttp"),
Ctx));
const char *ExpectedTemplateParamName = PackIndex ? "A2" : "A1";
ASSERT_TRUE(Subst);
ASSERT_EQ(Subst->getReplacedParameter()->getIdentifier()->getName(),
ExpectedTemplateParamName);
ASSERT_EQ(Subst->getPackIndex(), PackIndex);
};
auto tests = [&](ASTContext &Ctx) {
testType(Ctx, "void", std::nullopt);
testType(Ctx, "char", 3u);
testType(Ctx, "float", 2u);
testType(Ctx, "int", 1u);
testType(Ctx, "short", 0u);
};
tests(FromTU->getASTContext());
ClassTemplateSpecializationDecl *ToClass = Import(FromClass, Lang_CXX11);
tests(ToClass->getASTContext());
}
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);
// Finds via linear search of its LexicalDC (A).
FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
EXPECT_EQ(FoundDecls.size(), 1u);
// 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,
LookupFindsFriendClassDeclWithUsingTypeDoesNotAssert) {
TranslationUnitDecl *ToTU = getToTuDecl(
R"(
namespace a {
namespace b { class InnerClass; }
using b::InnerClass;
}
class B {
friend a::InnerClass;
};
)",
Lang_CXX11);
// ASTImporterLookupTable constructor handles friend with using-type without
// asserts.
ASTImporterLookupTable LT(*ToTU);
auto *Using = FirstDeclMatcher<UsingDecl>().match(
ToTU, usingDecl(hasName("InnerClass")));
DeclarationName Name = Using->getDeclName();
auto Res = LT.lookup(ToTU, Name);
EXPECT_EQ(Res.size(), 0u);
auto *NsA = FirstDeclMatcher<NamespaceDecl>().match(
ToTU, namespaceDecl(hasName("a")));
auto *RecordB = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(hasName("B")));
auto Res1 = LT.lookup(NsA, Name);
EXPECT_EQ(Res1.count(Using), 1u);
auto Res2 = LT.lookup(RecordB, Name);
EXPECT_EQ(Res2.size(), 0u);
}
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, LookupSearchesInActualNamespaceOnly) {
TranslationUnitDecl *ToTU = getToTuDecl(
R"(
namespace N {
int A;
}
namespace N {
}
)",
Lang_CXX03);
auto *N1 = FirstDeclMatcher<NamespaceDecl>().match(
ToTU, namespaceDecl(hasName("N")));
auto *N2 =
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);
EXPECT_TRUE(LT.lookup(N2, Name).empty());
}
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);
}
TEST_P(ImportFriendFunctionTemplates, ImportFriendFunctionInsideClassTemplate) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
template <typename T> struct X {
template <typename U> friend void f();
};
)",
Lang_CXX03, "", Lang_CXX03, "X");
auto *FromFriend = FirstDeclMatcher<FriendDecl>().match(From, friendDecl());
auto *ToFriend = FirstDeclMatcher<FriendDecl>().match(To, friendDecl());
EXPECT_TRUE(FromFriend ==
LastDeclMatcher<FriendDecl>().match(From, friendDecl()));
EXPECT_TRUE(ToFriend ==
LastDeclMatcher<FriendDecl>().match(To, friendDecl()));
auto *FromDecl = FromFriend->getFriendDecl();
auto *FromDC = FromFriend->getDeclContext();
auto *FromLexicalDC = FromFriend->getLexicalDeclContext();
EXPECT_TRUE(FromDC->containsDecl(FromFriend));
EXPECT_FALSE(FromDC->containsDecl(FromDecl));
EXPECT_TRUE(FromLexicalDC->containsDecl(FromFriend));
EXPECT_FALSE(FromLexicalDC->containsDecl(FromDecl));
auto *ToDecl = ToFriend->getFriendDecl();
auto *ToDC = ToFriend->getDeclContext();
auto *ToLexicalDC = ToFriend->getLexicalDeclContext();
EXPECT_TRUE(ToDC->containsDecl(ToFriend));
EXPECT_FALSE(ToDC->containsDecl(ToDecl));
EXPECT_TRUE(ToLexicalDC->containsDecl(ToFriend));
EXPECT_FALSE(ToLexicalDC->containsDecl(ToDecl));
}
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();
std::optional<ASTImportError> OptErr =
Importer->getImportDeclErrorIfAny(FromSpec);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ASTImportError::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();
std::optional<ASTImportError> OptErr =
Importer->getImportDeclErrorIfAny(FromFoo);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ASTImportError::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();
std::optional<ASTImportError> OptErr =
Importer->getImportDeclErrorIfAny(FromProto);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ASTImportError::UnsupportedConstruct);
OptErr = Importer->getImportDeclErrorIfAny(FromDef);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ASTImportError::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();
std::optional<ASTImportError> OptErr =
Importer->getImportDeclErrorIfAny(FromX);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ASTImportError::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, ASTImportError::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();
std::optional<ASTImportError> 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();
std::optional<ASTImportError> 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, ASTImportError::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();
std::optional<ASTImportError> OptErr =
Importer->getImportDeclErrorIfAny(FromX);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ASTImportError::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.
std::optional<ASTImportError> OptErr =
SharedStatePtr->getImportDeclErrorIfAny(ToXDef);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ASTImportError::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();
std::optional<ASTImportError> OptErr =
Importer->getImportDeclErrorIfAny(FromX);
// And an error is set for this new X in the "from" ctx.
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ASTImportError::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) {
std::optional<ASTImportError> OptErr =
Importer->getImportDeclErrorIfAny(FromD);
ASSERT_TRUE(OptErr);
EXPECT_EQ(OptErr->Error, ASTImportError::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(ErrorHandlingTest, DoNotInheritErrorFromNonDependentChild) {
// Declarations should not inherit an import error from a child object
// if the declaration has no direct dependence to such a child.
// For example a namespace should not get import error if one of the
// declarations inside it fails to import.
// There was a special case in error handling (when "import path circles" are
// encountered) when this property was not held. This case is provoked by the
// following code.
constexpr auto ToTUCode = R"(
namespace ns {
struct Err {
char A;
};
}
)";
constexpr auto FromTUCode = R"(
namespace ns {
struct A {
using U = struct Err;
};
}
namespace ns {
struct Err {}; // ODR violation
void f(A) {}
}
)";
Decl *ToTU = getToTuDecl(ToTUCode, Lang_CXX11);
static_cast<void>(ToTU);
Decl *FromTU = getTuDecl(FromTUCode, Lang_CXX11);
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A"), hasDefinition()));
ASSERT_TRUE(FromA);
auto *ImportedA = Import(FromA, Lang_CXX11);
// 'A' can not be imported: ODR error at 'Err'
EXPECT_FALSE(ImportedA);
// When import of 'A' failed there was a "saved import path circle" that
// contained namespace 'ns' (A - U - Err - ns - f - A). This should not mean
// that every object in this path fails to import.
Decl *FromNS = FirstDeclMatcher<NamespaceDecl>().match(
FromTU, namespaceDecl(hasName("ns")));
EXPECT_TRUE(FromNS);
auto *ImportedNS = Import(FromNS, Lang_CXX11);
EXPECT_TRUE(ImportedNS);
}
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);
EXPECT_FALSE(FromL->isDependentLambda());
}
TEST_P(ASTImporterOptionSpecificTestBase,
ReturnTypeDeclaredInsideOfCXX11LambdaWithoutTrailingReturn) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
void foo() {
(void) []() {
struct X {};
return X();
};
}
)",
Lang_CXX11, "", Lang_CXX11, "foo"); // c++11 only
auto *ToLambda = FirstDeclMatcher<LambdaExpr>().match(To, lambdaExpr());
EXPECT_TRUE(ToLambda);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ReturnTypeDeclaredInsideOfCXX11LambdaWithTrailingReturn) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
void foo() {
(void) [] {
struct X {};
return X();
};
}
)",
Lang_CXX11, "", Lang_CXX11, "foo"); // c++11 only
auto *ToLambda = FirstDeclMatcher<LambdaExpr>().match(To, lambdaExpr());
EXPECT_TRUE(ToLambda);
}
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);
EXPECT_TRUE(FromL->isDependentLambda());
}
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(
llvm::makeIntrusiveRefCnt<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 {
void testImport(llvm::StringRef Code, clang::TestLanguage Lang = Lang_CXX14,
bool FindLast = false) {
Decl *FromTU = getTuDecl(Code, Lang, "input0.cc");
FunctionDecl *From = FindLast ? LastDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("foo")))
: FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("foo")));
FunctionDecl *To = Import(From, Lang);
EXPECT_TRUE(To);
// We check here only that the type is auto type.
// These tests are to verify that no crash happens.
// The crash possibility is the presence of a reference to a declaration
// in the function's body from the return type, if the function has auto
// return type.
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
}
};
TEST_P(ImportAutoFunctions, ReturnWithFunctionTemplate1) {
testImport(
R"(
template<class C>
C f1() { return C(); }
auto foo() {
struct B {};
return f1<B>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithFunctionTemplate2) {
testImport(
R"(
template<class T>
int f1(T t) { return 1; }
auto foo() {
struct B {};
return f1(B());
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithFunctionTemplate3) {
testImport(
R"(
template<class A> struct S1 {};
template<class A> struct S2 {};
template<class C>
S1<C> f1() { return S1<C>(); }
auto foo() {
struct B {};
return f1<S2<B *>>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithFunctionTemplate4) {
testImport(
R"(
template<class... A> struct S1 {};
template<class... A> struct S2 {};
template<class... C>
S1<C...> f1() { return S1<C...>(); }
auto foo() {
struct B {};
return f1<S2<int, B *>, bool>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithVarTemplate1) {
testImport(
R"(
template<class T> T X;
auto foo() {
struct A {};
return X<A>;
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithVarTemplate2) {
testImport(
R"(
template<class A> struct S1 {};
template<class T> S1<T> X;
auto foo() {
struct A {};
return X<S1<A>>;
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithVarTemplate3) {
testImport(
R"(
template<class... A> struct S1 {};
template<class... T> S1<T...> X;
auto foo() {
struct A {};
return X<bool, S1<A, int>>;
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithAutoUnresolvedArg) {
testImport(
R"(
template<int A>
auto foo() {
return 22;
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithTemplateTemplateArg) {
// FIXME: Is it possible to have the template arg inside the function?
testImport(
R"(
template<int> struct Tmpl {};
template<template<int> class> struct TmplTmpl {};
auto foo() {
return TmplTmpl<Tmpl>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithDeclarationTemplateArg) {
// FIXME: Is it possible to have the template arg inside the function?
testImport(
R"(
template<const int *> struct Tmpl {};
int A[10];
auto foo() {
return Tmpl<A>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithNullPtrTemplateArg) {
testImport(
R"(
template<int *> struct Tmpl {};
auto foo() {
constexpr int* A = nullptr;
return Tmpl<A>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithIntegralTemplateArg) {
testImport(
R"(
template<int> struct Tmpl {};
auto foo() {
using Int = int;
constexpr Int A = 7;
return Tmpl<A>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithDecltypeTypeDeclaredInside) {
testImport(
R"(
template<class> struct Tmpl {};
auto foo() {
struct X {};
X x;
return Tmpl<decltype(x)>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithUsingTypeDeclaredInside) {
testImport(
R"(
template<class> struct Tmpl {};
namespace A { struct X {}; }
auto foo() {
using A::X;
return Tmpl<X>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithArrayTypeDeclaredInside) {
testImport(
R"(
template<class> struct Tmpl {};
auto foo() {
struct X {};
return Tmpl<X[10]>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithArraySizeExprDeclaredInside) {
testImport(
R"(
template<class> struct Tmpl {};
auto foo() {
constexpr int S = 10;
return Tmpl<int[S]>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithPackArgDeclaredInside) {
testImport(
R"(
template<class ...> struct Tmpl {};
auto foo() {
using X = bool;
return Tmpl<int, X>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithIntegerArgDeclaredInside) {
testImport(
R"(
template<int> struct Tmpl {};
auto foo() {
constexpr int X = 1;
return Tmpl<X>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithPtrToStructDeclaredInside) {
testImport(
R"(
template<class> struct Tmpl {};
auto foo() {
struct X {};
return Tmpl<X *>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithRefToStructDeclaredInside) {
testImport(
R"(
template<class> struct Tmpl {};
struct X {};
auto foo() {
using Y = X;
return Tmpl<Y &>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithStructDeclaredInside1) {
testImport(
R"(
template<class> struct Tmpl {};
auto foo() {
struct X {};
return Tmpl<X>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithStructDeclaredInside2) {
testImport(
R"(
template<class> struct Tmpl {};
auto foo() {
struct X {};
return Tmpl<Tmpl<X>>();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithTemplateWithTypedefDeclaredInside) {
testImport(
R"(
template<class> struct Tmpl {};
auto foo() {
struct X {};
using x_type = X;
return Tmpl<x_type>();
}
)");
}
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(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, 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) {
testImport(
R"(
auto foo() {
struct X {};
return X();
}
)");
}
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, ReturnWithStructDeclaredInside3) {
Decl *FromTU = getTuDecl(
R"(
struct S {
constexpr auto foo();
};
constexpr auto S::foo() {
struct X {};
return X();
}
)",
Lang_CXX14, "input0.cc");
FunctionDecl *From = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("foo"), unless(hasBody(stmt()))));
ASSERT_FALSE(From->isThisDeclarationADefinition());
FunctionDecl *To = Import(From, Lang_CXX17);
EXPECT_TRUE(To);
EXPECT_TRUE(isa<AutoType>(To->getReturnType()));
EXPECT_FALSE(To->isThisDeclarationADefinition());
}
TEST_P(ImportAutoFunctions, ReturnWithTypedefToStructDeclaredInside) {
testImport(
R"(
auto foo() {
struct X {};
using Y = X;
return Y();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithStructDeclaredNestedInside) {
testImport(
R"(
auto foo() {
struct X { struct Y{}; };
return X::Y();
}
)");
}
TEST_P(ImportAutoFunctions, ReturnWithInternalLambdaType) {
testImport(
R"(
auto foo() {
auto l = []() {
struct X {};
return X();
};
return l();
}
)",
Lang_CXX17);
}
TEST_P(ImportAutoFunctions, ReturnWithTypeInIf) {
testImport(
R"(
auto foo() {
if (struct X {} x; true)
return X();
else
return X();
}
)",
Lang_CXX17);
}
TEST_P(ImportAutoFunctions, ReturnWithTypeInFor) {
testImport(
R"(
auto foo() {
for (struct X {} x;;)
return X();
}
)",
Lang_CXX17);
}
TEST_P(ImportAutoFunctions, ReturnWithTypeInSwitch) {
testImport(
R"(
auto foo() {
switch (struct X {} x; 10) {
case 10:
return X();
}
}
)",
Lang_CXX17);
}
TEST_P(ImportAutoFunctions, ReturnWithAutoTemplateType) {
testImport(
R"(
template<class T>
struct S {};
template<class T>
auto foo() {
return S<T>{};
}
auto a = foo<int>();
)",
Lang_CXX14, /*FindLast=*/true);
}
TEST_P(ImportAutoFunctions, ReturnWithSubstNonTypeTemplateParmExpr) {
const char *Code =
R"(
template<int>
struct array {};
template <int N>
auto foo() { return array<N>(); }
void bar() { foo<0>(); }
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX17);
auto *FromBar = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("bar")));
auto *ToBar = Import(FromBar, Lang_CXX17);
EXPECT_TRUE(ToBar);
}
TEST_P(ImportAutoFunctions, ReturnWithUnaryTransformType) {
const char *Code =
R"(
enum E { E1 };
template<typename T>
auto foo(T v) { return static_cast<__underlying_type(T)>(v); }
bool bar() { return foo(E1); }
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX17);
auto *FromBar = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("bar")));
auto *ToBar = Import(FromBar, Lang_CXX17);
EXPECT_TRUE(ToBar);
}
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();
clang::FileEntryRef FE =
FM.getVirtualFileRef(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, /*RequiresNullTerminator=*/false);
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,
TestLanguage Lang = Lang_CXX11) {
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, "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());
}
TEST_P(ImportAttributes, ImportAligned) {
AlignedAttr *FromAttr, *ToAttr;
importAttr<RecordDecl>(
R"(
struct __attribute__((packed)) A { int __attribute__((aligned(8))) X; };
struct alignas(alignof(A)) test {};
)",
FromAttr, ToAttr);
checkImported(FromAttr->getAlignmentExpr(), ToAttr->getAlignmentExpr());
auto *ToA = FirstDeclMatcher<CXXRecordDecl>().match(
ToAST->getASTContext().getTranslationUnitDecl(),
cxxRecordDecl(hasName("A"), unless(isImplicit())));
// Ensure that 'struct A' was imported (through reference from attribute of
// struct 'test').
EXPECT_TRUE(ToA);
}
TEST_P(ImportAttributes, ImportAlignValue) {
AlignValueAttr *FromAttr, *ToAttr;
importAttr<VarDecl>(
R"(
void *test __attribute__((align_value(64)));
)",
FromAttr, ToAttr);
checkImported(FromAttr->getAlignment(), ToAttr->getAlignment());
}
TEST_P(ImportAttributes, ImportFormat) {
FormatAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
R"(
int test(const char * fmt, ...)
__attribute__ ((__format__ (__scanf__, 1, 2)));
)",
FromAttr, ToAttr);
EXPECT_EQ(FromAttr->getType()->getName(), ToAttr->getType()->getName());
EXPECT_EQ(FromAttr->getFirstArg(), ToAttr->getFirstArg());
EXPECT_EQ(FromAttr->getFormatIdx(), ToAttr->getFormatIdx());
}
TEST_P(ImportAttributes, ImportEnableIf) {
EnableIfAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>(
"void test(int A) __attribute__((enable_if(A == 1, \"message\")));",
FromAttr, ToAttr);
checkImported(FromAttr->getCond(), ToAttr->getCond());
EXPECT_EQ(FromAttr->getMessage(), ToAttr->getMessage());
}
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, 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());
}
TEST_P(ImportAttributes, ImportReentrantCapability) {
ReentrantCapabilityAttr *FromAttr, *ToAttr;
importAttr<CXXRecordDecl>(
R"(
struct __attribute__((capability("x"), reentrant_capability)) test {};
)",
FromAttr, ToAttr);
}
TEST_P(ImportAttributes, ImportC99NoThrowAttr) {
NoThrowAttr *FromAttr, *ToAttr;
importAttr<FunctionDecl>("void test () __attribute__ ((__nothrow__));",
FromAttr, ToAttr, Lang_C99);
checkImported(FromAttr->getAttrName(), ToAttr->getAttrName());
}
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,
// Use minimal import for these tests.
FromFileManager, /*MinimalImport=*/true,
// 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;
auto source =
llvm::makeIntrusiveRefCnt<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, TagTypeKind::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_EQ(ToD->getDeductionCandidateKind(), DeductionCandidate::Copy);
EXPECT_EQ(ToD->getSourceDeductionGuide(), nullptr);
EXPECT_EQ(ToD->getSourceDeductionGuideKind(),
CXXDeductionGuideDecl::SourceDeductionGuideKind::None);
// 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());
EXPECT_EQ(ToD->getSourceDeductionGuide(), nullptr);
EXPECT_EQ(ToD->getSourceDeductionGuideKind(),
CXXDeductionGuideDecl::SourceDeductionGuideKind::None);
}
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, CTADAliasTemplate) {
Decl *TU = getTuDecl(
R"(
template <typename T> struct A {
A(T);
};
template<typename T>
using B = A<T>;
B b{(int)0};
)",
Lang_CXX20, "input.cc");
auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
TU, cxxDeductionGuideDecl(hasParameter(0, hasType(asString("int")))));
auto *ToD = Import(FromD, Lang_CXX20);
ASSERT_TRUE(ToD);
EXPECT_TRUE(ToD->getSourceDeductionGuideKind() ==
CXXDeductionGuideDecl::SourceDeductionGuideKind::Alias);
EXPECT_TRUE(ToD->getSourceDeductionGuide());
}
TEST_P(ImportFunctions, CTADAliasTemplateWithExplicitSourceDeductionGuide) {
Decl *TU = getTuDecl(
R"(
template <typename T> struct A {
A(T);
};
template<typename T>
using B = A<T>;
A(int) -> A<double>; // explicit
B b{(int)0};
)",
Lang_CXX20, "input.cc");
auto *FromD = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
TU, cxxDeductionGuideDecl(hasParameter(0, hasType(asString("int"))),
hasName("<deduction guide for B>"),
hasReturnTypeLoc(loc(asString("A<double>")))));
auto *ToD = Import(FromD, Lang_CXX20);
ASSERT_TRUE(ToD);
EXPECT_TRUE(ToD->getSourceDeductionGuideKind() ==
CXXDeductionGuideDecl::SourceDeductionGuideKind::Alias);
EXPECT_TRUE(ToD->getSourceDeductionGuide());
}
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()->getAs<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()->getAs<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);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportFunctionDeclBitShouldNotOverwriteCtorDeclBits) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"s(
struct A {
A() : m() {}
int m;
};
A foo() { A a; return a; }
A bar() { return {}; }
)s",
Lang_CXX17,
R"s(
struct A {
A() : m() {}
int m;
};
A baz() { return {}; }
)s",
Lang_CXX17, "A");
auto HasCtorInit =
hasAnyConstructorInitializer(cxxCtorInitializer(isMemberInitializer()));
auto ImpMoveCtor =
cxxConstructorDecl(isMoveConstructor(), isImplicit(), HasCtorInit);
auto *FromImpMoveCtor = FirstDeclMatcher<CXXConstructorDecl>().match(
From, ImpMoveCtor);
auto *ToImpMoveCtor = FirstDeclMatcher<CXXConstructorDecl>().match(
To, ImpMoveCtor);
EXPECT_TRUE(FromImpMoveCtor->getNumCtorInitializers() == 1);
EXPECT_FALSE(FromImpMoveCtor->FriendConstraintRefersToEnclosingTemplate());
EXPECT_TRUE(ToImpMoveCtor->getNumCtorInitializers() == 1);
EXPECT_FALSE(ToImpMoveCtor->FriendConstraintRefersToEnclosingTemplate());
EXPECT_TRUE(*ToImpMoveCtor->init_begin());
}
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);
}
AST_MATCHER_P(FunctionTemplateDecl, templateParameterCountIs, unsigned, Cnt) {
return Node.getTemplateParameters()->size() == Cnt;
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportDeductionGuide) {
TranslationUnitDecl *FromTU = getTuDecl(
R"(
template<class> class A { };
template<class T> class B {
template<class T1, typename = A<T>> B(T1);
};
template<class T>
B(T, T) -> B<int>;
)",
Lang_CXX17);
// Get the implicit deduction guide for (non-default) constructor of 'B'.
auto *FromDGCtor = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(templateParameterCountIs(3)));
// Implicit deduction guide for copy constructor of 'B'.
auto *FromDGCopyCtor = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(templateParameterCountIs(1), isImplicit()));
// User defined deduction guide.
auto *FromDGOther = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
FromTU, cxxDeductionGuideDecl(unless(isImplicit())));
TemplateParameterList *FromDGCtorTP = FromDGCtor->getTemplateParameters();
// Don't know why exactly but this is the DeclContext here.
EXPECT_EQ(FromDGCtorTP->getParam(0)->getDeclContext(),
FromDGCopyCtor->getTemplatedDecl());
EXPECT_EQ(FromDGCtorTP->getParam(1)->getDeclContext(),
FromDGCtor->getTemplatedDecl());
EXPECT_EQ(FromDGCtorTP->getParam(2)->getDeclContext(),
FromDGCtor->getTemplatedDecl());
EXPECT_EQ(
FromDGCopyCtor->getTemplateParameters()->getParam(0)->getDeclContext(),
FromDGCopyCtor->getTemplatedDecl());
EXPECT_EQ(FromDGOther->getDescribedTemplate()
->getTemplateParameters()
->getParam(0)
->getDeclContext(),
FromDGOther);
auto *ToDGCtor = Import(FromDGCtor, Lang_CXX17);
auto *ToDGCopyCtor = Import(FromDGCopyCtor, Lang_CXX17);
auto *ToDGOther = Import(FromDGOther, Lang_CXX17);
ASSERT_TRUE(ToDGCtor);
ASSERT_TRUE(ToDGCopyCtor);
ASSERT_TRUE(ToDGOther);
TemplateParameterList *ToDGCtorTP = ToDGCtor->getTemplateParameters();
EXPECT_EQ(ToDGCtorTP->getParam(0)->getDeclContext(),
ToDGCopyCtor->getTemplatedDecl());
EXPECT_EQ(ToDGCtorTP->getParam(1)->getDeclContext(),
ToDGCtor->getTemplatedDecl());
EXPECT_EQ(ToDGCtorTP->getParam(2)->getDeclContext(),
ToDGCtor->getTemplatedDecl());
EXPECT_EQ(
ToDGCopyCtor->getTemplateParameters()->getParam(0)->getDeclContext(),
ToDGCopyCtor->getTemplatedDecl());
EXPECT_EQ(ToDGOther->getDescribedTemplate()
->getTemplateParameters()
->getParam(0)
->getDeclContext(),
ToDGOther);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportDeductionGuideDifferentOrder) {
// This test demonstrates that the DeclContext of the imported object is
// dependent on the order of import. The test is an exact copy of the previous
// one except at the indicated locations.
TranslationUnitDecl *FromTU = getTuDecl(
R"(
template<class> class A { };
template<class T> class B {
template<class T1, typename = A<T>> B(T1);
};
template<class T>
B(T, T) -> B<int>;
)",
Lang_CXX17);
// Get the implicit deduction guide for (non-default) constructor of 'B'.
auto *FromDGCtor = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(templateParameterCountIs(3)));
// Implicit deduction guide for copy constructor of 'B'.
auto *FromDGCopyCtor = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(templateParameterCountIs(1), isImplicit()));
// User defined deduction guide.
auto *FromDGOther = FirstDeclMatcher<CXXDeductionGuideDecl>().match(
FromTU, cxxDeductionGuideDecl(unless(isImplicit())));
TemplateParameterList *FromDGCtorTP = FromDGCtor->getTemplateParameters();
// Don't know why exactly but this is the DeclContext here.
EXPECT_EQ(FromDGCtorTP->getParam(0)->getDeclContext(),
FromDGCopyCtor->getTemplatedDecl());
EXPECT_EQ(FromDGCtorTP->getParam(1)->getDeclContext(),
FromDGCtor->getTemplatedDecl());
EXPECT_EQ(FromDGCtorTP->getParam(2)->getDeclContext(),
FromDGCtor->getTemplatedDecl());
EXPECT_EQ(
FromDGCopyCtor->getTemplateParameters()->getParam(0)->getDeclContext(),
FromDGCopyCtor->getTemplatedDecl());
EXPECT_EQ(FromDGOther->getDescribedTemplate()
->getTemplateParameters()
->getParam(0)
->getDeclContext(),
FromDGOther);
// Here the import of 'ToDGCopyCtor' and 'ToDGCtor' is reversed relative to
// the previous test.
auto *ToDGCopyCtor = Import(FromDGCopyCtor, Lang_CXX17);
auto *ToDGCtor = Import(FromDGCtor, Lang_CXX17);
auto *ToDGOther = Import(FromDGOther, Lang_CXX17);
ASSERT_TRUE(ToDGCtor);
ASSERT_TRUE(ToDGCopyCtor);
ASSERT_TRUE(ToDGOther);
TemplateParameterList *ToDGCtorTP = ToDGCtor->getTemplateParameters();
// Next line: DeclContext is different relative to the previous test.
EXPECT_EQ(ToDGCtorTP->getParam(0)->getDeclContext(),
ToDGCtor->getTemplatedDecl());
EXPECT_EQ(ToDGCtorTP->getParam(1)->getDeclContext(),
ToDGCtor->getTemplatedDecl());
EXPECT_EQ(ToDGCtorTP->getParam(2)->getDeclContext(),
ToDGCtor->getTemplatedDecl());
// Next line: DeclContext is different relative to the previous test.
EXPECT_EQ(
ToDGCopyCtor->getTemplateParameters()->getParam(0)->getDeclContext(),
ToDGCtor->getTemplatedDecl());
EXPECT_EQ(ToDGOther->getDescribedTemplate()
->getTemplateParameters()
->getParam(0)
->getDeclContext(),
ToDGOther);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportFieldsFirstForCorrectRecordLayout) {
// UnaryOperator(&) triggers RecordLayout computation, which relies on
// correctly imported fields.
auto Code =
R"(
class A {
int m() {
return &((A *)0)->f1 - &((A *)0)->f2;
}
int f1;
int f2;
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromF = FirstDeclMatcher<CXXMethodDecl>().match(
FromTU, cxxMethodDecl(hasName("A::m")));
Import(FromF, Lang_CXX11);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportCirularRefFieldsWithoutCorruptedRecordLayoutCache) {
// Import sequence: A => A.b => B => B.f() => ... => UnaryOperator(&) => ...
//
// UnaryOperator(&) should not introduce invalid RecordLayout since 'A' is
// still not completely imported.
auto Code =
R"(
class B;
class A {
B* b;
int c;
};
class B {
A *f() { return &((B *)0)->a; }
A a;
};
)";
auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
getTuDecl(Code, Lang_CXX11), cxxRecordDecl(hasName("A")));
FromR = FromR->getDefinition();
auto &FromAST = FromR->getASTContext();
auto *ToR = Import(FromR, Lang_CXX11);
auto &ToAST = ToR->getASTContext();
uint64_t SecondFieldOffset = FromAST.getTypeSize(FromAST.VoidPtrTy);
EXPECT_TRUE(FromR->isCompleteDefinition());
const auto &FromLayout = FromAST.getASTRecordLayout(FromR);
EXPECT_TRUE(FromLayout.getFieldOffset(0) == 0);
EXPECT_TRUE(FromLayout.getFieldOffset(1) == SecondFieldOffset);
EXPECT_TRUE(ToR->isCompleteDefinition());
const auto &ToLayout = ToAST.getASTRecordLayout(ToR);
EXPECT_TRUE(ToLayout.getFieldOffset(0) == 0);
EXPECT_TRUE(ToLayout.getFieldOffset(1) == SecondFieldOffset);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportRecordWithLayoutRequestingExpr) {
TranslationUnitDecl *FromTU = getTuDecl(
R"(
struct A {
int idx;
static void foo(A x) {
(void)&"text"[x.idx];
}
};
)",
Lang_CXX11);
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
// Test that during import of 'foo' the record layout can be obtained without
// crash.
auto *ToA = Import(FromA, Lang_CXX11);
EXPECT_TRUE(ToA);
EXPECT_TRUE(ToA->isCompleteDefinition());
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportRecordWithLayoutRequestingExprDifferentRecord) {
TranslationUnitDecl *FromTU = getTuDecl(
R"(
struct B;
struct A {
int idx;
B *b;
};
struct B {
static void foo(A x) {
(void)&"text"[x.idx];
}
};
)",
Lang_CXX11);
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
// Test that during import of 'foo' the record layout (of 'A') can be obtained
// without crash. It is not possible to have all of the fields of 'A' imported
// at that time (without big code changes).
auto *ToA = Import(FromA, Lang_CXX11);
EXPECT_TRUE(ToA);
EXPECT_TRUE(ToA->isCompleteDefinition());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportInClassInitializerFromField) {
// Encounter import of a field when the field already exists but has the
// in-class initializer expression not yet set. Such case can occur in the AST
// of generated template specializations.
// The first code forces to create a template specialization of
// `A<int>` but without implicit constructors.
// The second ("From") code contains a variable of type `A<int>`, this
// results in a template specialization that has constructors and
// CXXDefaultInitExpr nodes.
Decl *ToTU = getToTuDecl(
R"(
void f();
template<typename> struct A { int X = 1; };
struct B { A<int> Y; };
)",
Lang_CXX11);
auto *ToX = FirstDeclMatcher<FieldDecl>().match(
ToTU,
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
ASSERT_TRUE(ToX->hasInClassInitializer());
ASSERT_FALSE(ToX->getInClassInitializer());
Decl *FromTU = getTuDecl(
R"(
void f();
template<typename> struct A { int X = 1; };
struct B { A<int> Y; };
//
A<int> Z;
)",
Lang_CXX11, "input1.cc");
auto *FromX = FirstDeclMatcher<FieldDecl>().match(
FromTU,
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
auto *ToXImported = Import(FromX, Lang_CXX11);
EXPECT_EQ(ToXImported, ToX);
EXPECT_TRUE(ToX->getInClassInitializer());
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportInClassInitializerFromCXXDefaultInitExpr) {
// Encounter AST import of a CXXDefaultInitExpr where the "to-field"
// of it exists but has the in-class initializer not set yet.
Decl *ToTU = getToTuDecl(
R"(
namespace N {
template<typename> int b;
struct X;
}
template<typename> struct A { N::X *X = nullptr; };
struct B { A<int> Y; };
)",
Lang_CXX14);
auto *ToX = FirstDeclMatcher<FieldDecl>().match(
ToTU,
fieldDecl(hasName("X"), hasParent(classTemplateSpecializationDecl())));
ASSERT_TRUE(ToX->hasInClassInitializer());
ASSERT_FALSE(ToX->getInClassInitializer());
Decl *FromTU = getTuDecl(
R"(
namespace N {
template<typename> int b;
struct X;
}
template<typename> struct A { N::X *X = nullptr; };
struct B { A<int> Y; };
//
void f() {
(void)A<int>{};
}
struct C {
C(): attr(new A<int>{}){}
A<int> *attr;
const int value = N::b<C>;
};
)",
Lang_CXX14, "input1.cc");
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f"), isDefinition()));
auto *ToF = Import(FromF, Lang_CXX11);
EXPECT_TRUE(ToF);
EXPECT_TRUE(ToX->getInClassInitializer());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportRecursiveFieldInitializer) {
const char *Code =
R"(
struct AP_TECS;
struct AP_Landing {
AP_TECS *TECS_controller;
};
struct AP_TECS {
AP_Landing landing;
};
class Plane {
AP_TECS TECS_controller{landing};
AP_Landing landing{&TECS_controller};
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("Plane")));
for (FieldDecl *F : FromR->fields())
EXPECT_TRUE(F->getInClassInitializer());
auto *ToR = Import(FromR, Lang_CXX11);
for (FieldDecl *F : ToR->fields())
EXPECT_TRUE(F->getInClassInitializer());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportFieldInitializerWithItself) {
const char *Code =
R"(
class A {
int a{a};
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
EXPECT_TRUE(FromA->field_begin()->getInClassInitializer());
auto *ToA = Import(FromA, Lang_CXX11);
EXPECT_TRUE(ToA->field_begin()->getInClassInitializer());
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportRecursiveFieldInitializer1) {
// FIXME: This is a example of recursive field initialization that is not
// supported.
// The following import chain occurs (not complete):
// import of A => A.a => in-class initializer of A.a => ref_B() => B => B.b
// => in-class initializer of B.b => ref_A() => CXXConstructExpr for A =>
// CXXDefaultInitExpr for A.a => in-class initializer of A.a
// in-class initializer of A.a is created in two different instances in this
// case (import of FieldDecl and CXXDefaultInitExpr). Probably not a big
// problem because it is an Expr (the second construction can be ignored
// instead of assert). But such recursive init code should not occur in
// practice.
const char *Code =
R"(
static int ref_A();
static int ref_B();
struct A {
int a = ref_B();
};
struct B {
int b = ref_A();
};
int ref_B() { B b; return b.b; }
int ref_A() { A a; return a.a; }
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
EXPECT_TRUE(FromA->field_begin()->getInClassInitializer());
// auto *ToA = Import(FromA, Lang_CXX11);
// EXPECT_TRUE(ToA->field_begin()->getInClassInitializer());
}
TEST_P(ASTImporterOptionSpecificTestBase, isNewDecl) {
Decl *FromTU = getTuDecl(
R"(
int bar() {
return 0;
}
void other() {
bar();
}
)",
Lang_CXX11);
Decl *ToTU = getToTuDecl(
R"(
int bar() {
return 0;
}
)",
Lang_CXX11);
auto *FromOther = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("other")));
ASSERT_TRUE(FromOther);
auto *ToOther = Import(FromOther, Lang_CXX11);
ASSERT_TRUE(ToOther);
auto *ToBar = FirstDeclMatcher<FunctionDecl>().match(
ToTU, functionDecl(hasName("bar")));
EXPECT_TRUE(SharedStatePtr->isNewDecl(ToOther));
EXPECT_FALSE(SharedStatePtr->isNewDecl(ToBar));
}
struct ImportInjectedClassNameType : public ASTImporterOptionSpecificTestBase {
protected:
void checkInjType(const ASTContext &Ctx, const CXXRecordDecl *D) {
ASSERT_TRUE(D->hasInjectedClassType());
const Type *Ty = Ctx.getTagType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, D,
/*OwnsTag=*/false)
.getTypePtr();
ASSERT_TRUE(Ty);
EXPECT_FALSE(Ty->isCanonicalUnqualified());
const auto *InjTy = Ty->castAs<InjectedClassNameType>();
EXPECT_TRUE(InjTy);
for (const Decl *ReD : D->redecls()) {
if (ReD == D)
continue;
const auto *ReRD = cast<CXXRecordDecl>(ReD);
ASSERT_TRUE(ReRD->hasInjectedClassType());
const Type *ReTy = Ctx.getTagType(ElaboratedTypeKeyword::None,
/*Qualifier=*/std::nullopt, ReRD,
/*OwnsTag=*/false)
.getTypePtr();
ASSERT_TRUE(ReTy);
EXPECT_FALSE(ReTy->isCanonicalUnqualified());
EXPECT_NE(ReTy, Ty);
EXPECT_TRUE(Ctx.hasSameType(ReTy, Ty));
const auto *ReInjTy = Ty->castAs<InjectedClassNameType>();
EXPECT_TRUE(ReInjTy);
}
}
void testImport(Decl *ToTU, Decl *FromTU, Decl *FromD) {
checkInjType(FromTU->getASTContext(), cast<CXXRecordDecl>(FromD));
Decl *ToD = Import(FromD, Lang_CXX11);
if (auto *ToRD = dyn_cast<CXXRecordDecl>(ToD))
checkInjType(ToTU->getASTContext(), ToRD);
}
const char *ToCodeA =
R"(
template <class T>
struct A;
)";
const char *ToCodeADef =
R"(
template <class T>
struct A {
typedef A T1;
};
)";
const char *ToCodeC =
R"(
template <class T>
struct C;
)";
const char *ToCodeCDef =
R"(
template <class T>
struct A {
typedef A T1;
};
template <class T1, class T2>
struct B {};
template<class T>
struct C {
typedef typename A<T>::T1 T1;
typedef B<T1, T> T2;
typedef B<T1, C> T3;
};
)";
const char *FromCode =
R"(
template <class T>
struct A;
template <class T>
struct A {
typedef A T1;
};
template <class T>
struct A;
template <class T1, class T2>
struct B {};
template <class T>
struct C;
template <class T>
struct C {
typedef typename A<T>::T1 T1;
typedef B<T1, T> T2;
typedef B<T1, C> T3;
};
template <class T>
struct C;
template <class T>
struct D {
void f(typename C<T>::T3 *);
};
)";
};
TEST_P(ImportInjectedClassNameType, ImportADef) {
Decl *ToTU = getToTuDecl(ToCodeA, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A"), isDefinition()));
testImport(ToTU, FromTU, FromA);
}
TEST_P(ImportInjectedClassNameType, ImportAFirst) {
Decl *ToTU = getToTuDecl(ToCodeA, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
testImport(ToTU, FromTU, FromA);
}
TEST_P(ImportInjectedClassNameType, ImportALast) {
Decl *ToTU = getToTuDecl(ToCodeA, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromA = LastDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
testImport(ToTU, FromTU, FromA);
}
TEST_P(ImportInjectedClassNameType, ImportADefToDef) {
Decl *ToTU = getToTuDecl(ToCodeADef, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A"), isDefinition()));
testImport(ToTU, FromTU, FromA);
}
TEST_P(ImportInjectedClassNameType, ImportAFirstToDef) {
Decl *ToTU = getToTuDecl(ToCodeADef, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromA = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
testImport(ToTU, FromTU, FromA);
}
TEST_P(ImportInjectedClassNameType, ImportALastToDef) {
Decl *ToTU = getToTuDecl(ToCodeADef, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromA = LastDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("A")));
testImport(ToTU, FromTU, FromA);
}
TEST_P(ImportInjectedClassNameType, ImportCDef) {
Decl *ToTU = getToTuDecl(ToCodeC, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromC = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("C"), isDefinition()));
testImport(ToTU, FromTU, FromC);
}
TEST_P(ImportInjectedClassNameType, ImportCLast) {
Decl *ToTU = getToTuDecl(ToCodeC, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromC = LastDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("C")));
testImport(ToTU, FromTU, FromC);
}
TEST_P(ImportInjectedClassNameType, ImportCDefToDef) {
Decl *ToTU = getToTuDecl(ToCodeCDef, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromC = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("C"), isDefinition()));
testImport(ToTU, FromTU, FromC);
}
TEST_P(ImportInjectedClassNameType, ImportCLastToDef) {
Decl *ToTU = getToTuDecl(ToCodeCDef, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromC = LastDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("C")));
testImport(ToTU, FromTU, FromC);
}
TEST_P(ImportInjectedClassNameType, ImportD) {
Decl *ToTU = getToTuDecl("", Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("D"), isDefinition()));
testImport(ToTU, FromTU, FromD);
}
TEST_P(ImportInjectedClassNameType, ImportDToDef) {
Decl *ToTU = getToTuDecl(ToCodeCDef, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("D"), isDefinition()));
testImport(ToTU, FromTU, FromD);
}
TEST_P(ImportInjectedClassNameType, ImportTypedefType) {
Decl *ToTU = getToTuDecl(
R"(
template <class T>
struct A {
typedef A A1;
void f(A1 *);
};
)",
Lang_CXX11);
Decl *FromTU = getTuDecl(
R"(
template <class T>
struct A {
typedef A A1;
void f(A1 *);
};
template<class T>
void A<T>::f(A::A1 *) {}
)",
Lang_CXX11);
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f"), isDefinition()));
auto *ToF = Import(FromF, Lang_CXX11);
EXPECT_TRUE(ToF);
ASTContext &ToCtx = ToF->getDeclContext()->getParentASTContext();
auto *ToA1 =
FirstDeclMatcher<TypedefDecl>().match(ToTU, typedefDecl(hasName("A1")));
QualType ToInjTypedef = ToA1->getUnderlyingType().getCanonicalType();
QualType ToInjParmVar =
ToF->parameters()[0]->getType().getDesugaredType(ToCtx);
ToInjParmVar =
ToInjParmVar->getAs<PointerType>()->getPointeeType().getCanonicalType();
EXPECT_TRUE(isa<InjectedClassNameType>(ToInjTypedef));
EXPECT_TRUE(isa<InjectedClassNameType>(ToInjParmVar));
EXPECT_TRUE(ToCtx.hasSameType(ToInjTypedef, ToInjParmVar));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportMacroQualifiedType) {
Decl *From, *To;
std::tie(From, To) = getImportedDecl(
R"(
#define CDECL __attribute__((cdecl))
typedef void (CDECL *X)();
)",
Lang_CXX03, "", Lang_CXX03, "X");
auto *FromTy =
FirstDeclMatcher<MacroQualifiedType>().match(From, macroQualifiedType());
auto *ToTy =
FirstDeclMatcher<MacroQualifiedType>().match(To, macroQualifiedType());
EXPECT_TRUE(isa<AttributedType>(FromTy->getUnderlyingType()));
EXPECT_TRUE(isa<AttributedType>(ToTy->getUnderlyingType()));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportCorrectTemplateName) {
constexpr auto TestCode = R"(
template <class T>
struct A;
template <class T>
struct A {};
template <template<class> class T = A>
struct B {};
using C = B<>;
)";
Decl *ToTU = getToTuDecl(TestCode, Lang_CXX11);
Decl *FromTU = getTuDecl(TestCode, Lang_CXX11);
auto *ToUsingFirst = FirstDeclMatcher<TypeAliasDecl>().match(
ToTU, typeAliasDecl(hasName("C")));
auto *FromUsing = FirstDeclMatcher<TypeAliasDecl>().match(
FromTU, typeAliasDecl(hasName("C")));
auto *ToUsing = Import(FromUsing, Lang_CXX11);
EXPECT_TRUE(ToUsing);
auto *ToB = FirstDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("B")));
auto *ToB1 = LastDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("B")));
// One template definition of 'B' should exist.
EXPECT_EQ(ToB, ToB1);
// These declarations are imported separately.
EXPECT_NE(ToUsingFirst, ToUsing);
auto SpB = ToB->spec_begin();
auto SpE = ToB->spec_end();
EXPECT_TRUE(SpB != SpE);
ClassTemplateSpecializationDecl *Spec1 = *SpB;
++SpB;
// The template 'B' should have one specialization (with default argument).
EXPECT_TRUE(SpB == SpE);
// Even if 'B' has one specialization with the default arguments, the AST
// contains after the import two specializations that are linked in the
// declaration chain. The 'spec_begin' iteration does not find these because
// the template arguments are the same. But the imported type alias has the
// link to the second specialization. The template name object in these
// specializations must point to the same (and one) instance of definition of
// 'B'.
auto *Spec2 = cast<ClassTemplateSpecializationDecl>(
ToUsing->getUnderlyingType()
->getAs<TemplateSpecializationType>()
->getAsRecordDecl());
EXPECT_NE(Spec1, Spec2);
EXPECT_TRUE(Spec1->getPreviousDecl() == Spec2 ||
Spec2->getPreviousDecl() == Spec1);
TemplateDecl *Templ1 =
Spec1->getTemplateArgs()[0].getAsTemplate().getAsTemplateDecl();
TemplateDecl *Templ2 =
Spec2->getTemplateArgs()[0].getAsTemplate().getAsTemplateDecl();
EXPECT_EQ(Templ1, Templ2);
}
TEST_P(ASTImporterOptionSpecificTestBase, VaListC) {
Decl *FromTU = getTuDecl(R"(typedef __builtin_va_list va_list;)", Lang_C99);
auto *FromVaList = FirstDeclMatcher<TypedefDecl>().match(
FromTU, typedefDecl(hasName("va_list")));
ASSERT_TRUE(FromVaList);
auto *ToVaList = Import(FromVaList, Lang_C99);
ASSERT_TRUE(ToVaList);
auto *ToBuiltinVaList = FirstDeclMatcher<TypedefDecl>().match(
ToAST->getASTContext().getTranslationUnitDecl(),
typedefDecl(hasName("__builtin_va_list")));
ASSERT_TRUE(ToAST->getASTContext().hasSameType(
ToVaList->getUnderlyingType(), ToBuiltinVaList->getUnderlyingType()));
}
TEST_P(ASTImporterOptionSpecificTestBase, VaListCpp) {
Decl *FromTU = getTuDecl(R"(typedef __builtin_va_list va_list;)", Lang_CXX03);
auto *FromVaList = FirstDeclMatcher<TypedefDecl>().match(
FromTU, typedefDecl(hasName("va_list")));
ASSERT_TRUE(FromVaList);
auto *ToVaList = Import(FromVaList, Lang_CXX03);
ASSERT_TRUE(ToVaList);
auto *ToBuiltinVaList = FirstDeclMatcher<TypedefDecl>().match(
ToAST->getASTContext().getTranslationUnitDecl(),
typedefDecl(hasName("__builtin_va_list")));
ASSERT_TRUE(ToAST->getASTContext().hasSameType(
ToVaList->getUnderlyingType(), ToBuiltinVaList->getUnderlyingType()));
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportExistingTypedefToRecord) {
const char *Code =
R"(
struct S { int i; };
typedef struct S T;
extern T x;
)";
Decl *ToTU = getToTuDecl(Code, Lang_C99);
Decl *FromTU = getTuDecl(Code, Lang_C99);
auto *FromX =
FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("x")));
auto *ToX = Import(FromX, Lang_C99);
EXPECT_TRUE(ToX);
auto *Typedef1 =
FirstDeclMatcher<TypedefDecl>().match(ToTU, typedefDecl(hasName("T")));
auto *Typedef2 =
LastDeclMatcher<TypedefDecl>().match(ToTU, typedefDecl(hasName("T")));
EXPECT_EQ(Typedef1, Typedef2);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportExistingTypedefToUnnamedRecord) {
const char *Code =
R"(
typedef const struct { int f; } T;
extern T x;
)";
Decl *ToTU = getToTuDecl(Code, Lang_C99);
Decl *FromTU = getTuDecl(Code, Lang_C99);
auto *FromX =
FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("x")));
auto *ToX = Import(FromX, Lang_C99);
EXPECT_TRUE(ToX);
auto *Typedef1 =
FirstDeclMatcher<TypedefDecl>().match(ToTU, typedefDecl(hasName("T")));
auto *Typedef2 =
LastDeclMatcher<TypedefDecl>().match(ToTU, typedefDecl(hasName("T")));
EXPECT_NE(Typedef1, Typedef2);
EXPECT_NE(Typedef1->getUnderlyingType().getTypePtr(),
Typedef2->getUnderlyingType().getTypePtr());
EXPECT_EQ(ToX->getType()->getAs<TypedefType>()->getDecl(), Typedef2);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportTwoTypedefsToUnnamedRecord) {
const char *Code =
R"(
typedef struct { int f; } T1;
typedef struct { int f; } T2;
extern T1 x1;
extern T2 x2;
)";
Decl *ToTU = getToTuDecl("", Lang_C99);
Decl *FromTU = getTuDecl(Code, Lang_C99);
auto *FromX1 =
FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("x1")));
auto *FromX2 =
FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("x2")));
auto *ToX1 = Import(FromX1, Lang_C99);
EXPECT_TRUE(ToX1);
auto *ToX2 = Import(FromX2, Lang_C99);
EXPECT_TRUE(ToX2);
auto *Typedef1 =
FirstDeclMatcher<TypedefDecl>().match(ToTU, typedefDecl(hasName("T1")));
auto *Typedef2 =
FirstDeclMatcher<TypedefDecl>().match(ToTU, typedefDecl(hasName("T2")));
EXPECT_NE(Typedef1->getUnderlyingType().getTypePtr(),
Typedef2->getUnderlyingType().getTypePtr());
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportExistingTypedefToUnnamedRecordPtr) {
const char *Code =
R"(
typedef const struct { int fff; } * const T;
extern T x;
)";
Decl *ToTU = getToTuDecl(Code, Lang_C99);
Decl *FromTU = getTuDecl(Code, Lang_C99);
auto *FromX =
FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("x")));
auto *ToX = Import(FromX, Lang_C99);
EXPECT_TRUE(ToX);
auto *Typedef1 =
FirstDeclMatcher<TypedefDecl>().match(ToTU, typedefDecl(hasName("T")));
auto *Typedef2 =
LastDeclMatcher<TypedefDecl>().match(ToTU, typedefDecl(hasName("T")));
// FIXME: These should be imported separately, like in the test above.
// Or: In the test above these should be merged too.
EXPECT_EQ(Typedef1, Typedef2);
auto *FromR = FirstDeclMatcher<RecordDecl>().match(
FromTU, recordDecl(hasDescendant(fieldDecl(hasName("fff")))));
auto *ToRExisting = FirstDeclMatcher<RecordDecl>().match(
ToTU, recordDecl(hasDescendant(fieldDecl(hasName("fff")))));
ASSERT_TRUE(FromR);
auto *ToRImported = Import(FromR, Lang_C99);
// FIXME: If typedefs are not imported separately, do not import ToRImported
// separately.
EXPECT_NE(ToRExisting, ToRImported);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportTypedefWithDifferentUnderlyingType) {
const char *Code =
R"(
using X1 = int;
using Y1 = int;
using RPB1 = X1*;
typedef RPB1 RPX1;
using RPB1 = Y1*; // redeclared
typedef RPB1 RPY1;
auto X = 0 ? (RPX1){} : (RPY1){};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromX =
FirstDeclMatcher<VarDecl>().match(FromTU, varDecl(hasName("X")));
auto *FromXType = FromX->getType()->getAs<TypedefType>();
EXPECT_FALSE(FromXType->typeMatchesDecl());
auto *ToX = Import(FromX, Lang_CXX11);
auto *ToXType = ToX->getType()->getAs<TypedefType>();
EXPECT_FALSE(ToXType->typeMatchesDecl());
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportTemplateArgumentWithPointerToDifferentInstantiation) {
const char *CodeTo =
R"(
template<class A>
A f1() {
return A();
}
template<class A, A (B)()>
class X {};
X<int, f1<int>> x;
)";
const char *CodeFrom =
R"(
template<class A>
A f1();
template<class A, A (B)()>
class X {};
X<int, f1<int>> x;
)";
Decl *ToTU = getToTuDecl(CodeTo, Lang_CXX11);
Decl *FromTU = getTuDecl(CodeFrom, Lang_CXX11);
auto *ToF1 = FirstDeclMatcher<FunctionDecl>().match(
ToTU, functionDecl(hasName("f1"), isInstantiated()));
auto *FromF1 = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f1"), isInstantiated()));
EXPECT_TRUE(ToF1->isThisDeclarationADefinition());
EXPECT_FALSE(FromF1->isThisDeclarationADefinition());
auto *ToX = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
ToTU, classTemplateSpecializationDecl(hasName("X")));
auto *FromX = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl(hasName("X")));
Decl *ToTArgF = ToX->getTemplateArgs().get(1).getAsDecl();
Decl *FromTArgF = FromX->getTemplateArgs().get(1).getAsDecl();
EXPECT_EQ(ToTArgF, ToF1);
EXPECT_EQ(FromTArgF, FromF1);
auto *ToXImported = Import(FromX, Lang_CXX11);
// The template argument 1 of 'X' in the "From" code points to a function
// that has no definition. The import must ensure that this template argument
// is imported in a way that it will point to the existing 'f1' function, not
// to the 'f1' that is imported. In this way when specialization of 'X' is
// imported it will have the same template arguments as the existing one.
EXPECT_EQ(ToXImported, ToX);
// FIXME: This matcher causes a crash "Tried to match orphan node".
// The code is removed until the problem is fixed.
// auto *ToF1Imported =
// LastDeclMatcher<FunctionDecl>().match(ToTU,
// functionDecl(hasName("f1"),isInstantiated()));
// EXPECT_NE(ToF1Imported, ToF1);
// EXPECT_EQ(ToF1Imported->getPreviousDecl(), ToF1);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ImportTypeAliasTemplateAfterSimilarCalledTemplateTypeParm) {
const char *Code =
R"(
struct S;
template <typename>
using Callable = S;
template <typename Callable>
int bindingFunctionVTable;
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX17);
auto *FromCallable = FirstDeclMatcher<TypeAliasTemplateDecl>().match(
FromTU, typeAliasTemplateDecl(hasName("Callable")));
auto *FromCallableParm = FirstDeclMatcher<TemplateTypeParmDecl>().match(
FromTU, templateTypeParmDecl(hasName("Callable")));
auto *ToFromCallableParm = Import(FromCallableParm, Lang_CXX17);
auto *ToCallable = Import(FromCallable, Lang_CXX17);
EXPECT_TRUE(ToFromCallableParm);
EXPECT_TRUE(ToCallable);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportConflictTypeAliasTemplate) {
const char *ToCode =
R"(
struct S;
template <typename, typename>
using Callable = S;
)";
const char *Code =
R"(
struct S;
template <typename>
using Callable = S;
)";
(void)getToTuDecl(ToCode, Lang_CXX17);
Decl *FromTU = getTuDecl(Code, Lang_CXX17);
auto *FromCallable = FirstDeclMatcher<TypeAliasTemplateDecl>().match(
FromTU, typeAliasTemplateDecl(hasName("Callable")));
auto *ImportedCallable = Import(FromCallable, Lang_CXX17);
EXPECT_FALSE(ImportedCallable);
}
AST_MATCHER(ClassTemplateSpecializationDecl, hasInstantiatedFromMember) {
if (auto Instantiate = Node.getInstantiatedFrom()) {
if (auto *FromPartialSpecialization =
cast<ClassTemplatePartialSpecializationDecl *>(Instantiate)) {
return nullptr != FromPartialSpecialization->getInstantiatedFromMember();
}
}
return false;
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportInstantiatedFromMember) {
const char *Code =
R"(
template <typename> struct B {
template <typename, bool = false> union D;
template <typename T> union D<T> {};
D<int> d;
};
B<int> b;
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
FromTU, classTemplateSpecializationDecl(hasName("D"),
hasInstantiatedFromMember()));
auto *FromPartialSpecialization =
cast<ClassTemplatePartialSpecializationDecl *>(
FromD->getInstantiatedFrom());
ASSERT_TRUE(FromPartialSpecialization->getInstantiatedFromMember());
auto *ImportedPartialSpecialization =
Import(FromPartialSpecialization, Lang_CXX11);
EXPECT_TRUE(ImportedPartialSpecialization->getInstantiatedFromMember());
}
AST_MATCHER_P(EnumDecl, hasEnumConstName, StringRef, ConstName) {
for (EnumConstantDecl *D : Node.enumerators())
if (D->getName() == ConstName)
return true;
return false;
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportAnonymousEnums) {
const char *Code =
R"(
struct A {
enum { E1, E2 } x;
enum { E3, E4 } y;
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromEnumE1 = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasEnumConstName("E1")));
auto *ImportedEnumE1 = Import(FromEnumE1, Lang_CXX11);
EXPECT_TRUE(ImportedEnumE1);
auto *FromEnumE3 = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasEnumConstName("E3")));
auto *ImportedEnumE3 = Import(FromEnumE3, Lang_CXX11);
EXPECT_TRUE(ImportedEnumE3);
EXPECT_NE(ImportedEnumE1, ImportedEnumE3);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportFreeStandingAnonymousEnums) {
const char *Code =
R"(
struct A {
enum { E1, E2 };
enum { E3, E4 };
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromEnumE1 = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasEnumConstName("E1")));
auto *ImportedEnumE1 = Import(FromEnumE1, Lang_CXX11);
EXPECT_TRUE(ImportedEnumE1);
auto *FromEnumE3 = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasEnumConstName("E3")));
auto *ImportedEnumE3 = Import(FromEnumE3, Lang_CXX11);
EXPECT_TRUE(ImportedEnumE3);
EXPECT_NE(ImportedEnumE1, ImportedEnumE3);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportExistingAnonymousEnums) {
const char *ToCode =
R"(
struct A {
enum { E1, E2 } x;
enum { E3, E4 } y;
};
)";
Decl *ToTU = getToTuDecl(ToCode, Lang_CXX11);
auto *ToEnumE1 = FirstDeclMatcher<EnumDecl>().match(
ToTU, enumDecl(hasEnumConstName("E1")));
auto *ToEnumE3 = FirstDeclMatcher<EnumDecl>().match(
ToTU, enumDecl(hasEnumConstName("E3")));
const char *Code =
R"(
struct A {
enum { E1, E2 } x;
enum { E3, E4 } y;
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromEnumE1 = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasEnumConstName("E1")));
auto *ImportedEnumE1 = Import(FromEnumE1, Lang_CXX11);
ASSERT_TRUE(ImportedEnumE1);
EXPECT_EQ(ImportedEnumE1, ToEnumE1);
auto *FromEnumE3 = FirstDeclMatcher<EnumDecl>().match(
FromTU, enumDecl(hasEnumConstName("E3")));
auto *ImportedEnumE3 = Import(FromEnumE3, Lang_CXX11);
ASSERT_TRUE(ImportedEnumE3);
EXPECT_EQ(ImportedEnumE3, ToEnumE3);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportExistingEmptyAnonymousEnums) {
const char *ToCode =
R"(
struct A {
enum {};
};
)";
Decl *ToTU = getToTuDecl(ToCode, Lang_CXX11);
auto *ToE1 = FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl());
const char *Code =
R"(
struct A {
enum {};
enum {};
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromE1 = FirstDeclMatcher<EnumDecl>().match(FromTU, enumDecl());
auto *ImportedE1 = Import(FromE1, Lang_CXX11);
ASSERT_TRUE(ImportedE1);
EXPECT_EQ(ImportedE1, ToE1);
auto *FromE2 = LastDeclMatcher<EnumDecl>().match(FromTU, enumDecl());
ASSERT_NE(FromE1, FromE2);
auto *ImportedE2 = Import(FromE2, Lang_CXX11);
ASSERT_TRUE(ImportedE2);
// FIXME: These should not be equal, or the import should fail.
EXPECT_EQ(ImportedE2, ToE1);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportMultipleAnonymousEnumDecls) {
Decl *ToTU = getToTuDecl("", Lang_CXX03);
Decl *FromTU = getTuDecl(
R"(
struct foo {
enum { A };
enum { B };
};
)",
Lang_CXX03);
auto EnumConstA = enumConstantDecl(hasName("A"));
auto EnumConstB = enumConstantDecl(hasName("B"));
auto *FromA = FirstDeclMatcher<EnumConstantDecl>().match(FromTU, EnumConstA);
auto *FromB = FirstDeclMatcher<EnumConstantDecl>().match(FromTU, EnumConstB);
auto *ToA = Import(FromA, Lang_CXX03);
auto *ToB = Import(FromB, Lang_CXX03);
ASSERT_TRUE(ToA);
ASSERT_TRUE(ToB);
auto *ToFooA = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, tagDecl(has(enumDecl(has(EnumConstA)))));
auto *ToFooB = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, tagDecl(has(enumDecl(has(EnumConstB)))));
ASSERT_EQ(ToFooA, ToFooB);
// different EnumDecl
auto *ToEnumDeclA =
FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl(has(EnumConstA)));
auto *ToEnumDeclB =
FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl(has(EnumConstB)));
ASSERT_NE(ToEnumDeclA, ToEnumDeclB);
}
struct ImportTemplateParmDeclDefaultValue
: public ASTImporterOptionSpecificTestBase {
protected:
void checkTemplateParams(RedeclarableTemplateDecl *D,
RedeclarableTemplateDecl *InheritedFromD) {
auto *NonTypeP =
cast<NonTypeTemplateParmDecl>(D->getTemplateParameters()->getParam(0));
auto *TypeP =
cast<TemplateTypeParmDecl>(D->getTemplateParameters()->getParam(1));
auto *TemplateP =
cast<TemplateTemplateParmDecl>(D->getTemplateParameters()->getParam(2));
if (InheritedFromD) {
EXPECT_TRUE(NonTypeP->getDefaultArgStorage().isInherited());
EXPECT_TRUE(TypeP->getDefaultArgStorage().isInherited());
EXPECT_TRUE(TemplateP->getDefaultArgStorage().isInherited());
EXPECT_EQ(NonTypeP->getDefaultArgStorage().getInheritedFrom(),
InheritedFromD->getTemplateParameters()->getParam(0));
EXPECT_EQ(TypeP->getDefaultArgStorage().getInheritedFrom(),
InheritedFromD->getTemplateParameters()->getParam(1));
EXPECT_EQ(TemplateP->getDefaultArgStorage().getInheritedFrom(),
InheritedFromD->getTemplateParameters()->getParam(2));
} else {
EXPECT_FALSE(NonTypeP->getDefaultArgStorage().isInherited());
EXPECT_FALSE(TypeP->getDefaultArgStorage().isInherited());
EXPECT_FALSE(TemplateP->getDefaultArgStorage().isInherited());
}
}
void testImport(RedeclarableTemplateDecl *FromD1,
RedeclarableTemplateDecl *FromD2,
RedeclarableTemplateDecl *FromD3,
RedeclarableTemplateDecl *ToExistingD1) {
auto *ToD1 = Import(FromD1, Lang_CXX14);
auto *ToD2 = Import(FromD2, Lang_CXX14);
auto *ToD3 = Import(FromD3, Lang_CXX14);
checkTemplateParams(ToD1, nullptr);
checkTemplateParams(ToD2, ToD1);
checkTemplateParams(ToD3, ToExistingD1 ? ToExistingD1 : ToD1);
}
// In these tests a circular dependency is created between the template
// parameter default value and the template declaration (with the same
// template parameter).
template <class TemplateParmDeclT>
void
testTemplateParmDeclCircularDependency(ClassTemplateDecl *FromD,
ClassTemplateDecl *FromDInherited) {
auto GetTemplateParm =
[](ClassTemplateDecl *D) -> const TemplateParmDeclT * {
return dyn_cast<TemplateParmDeclT>(
D->getTemplateParameters()->getParam(0));
};
ASSERT_FALSE(GetTemplateParm(FromD)->getDefaultArgStorage().isInherited());
ASSERT_TRUE(
GetTemplateParm(FromDInherited)->getDefaultArgStorage().isInherited());
auto *ToD = Import(FromD, Lang_CXX14);
EXPECT_TRUE(ToD);
auto *ToDInherited = Import(FromDInherited, Lang_CXX14);
EXPECT_TRUE(ToDInherited);
EXPECT_FALSE(GetTemplateParm(ToD)->getDefaultArgStorage().isInherited());
EXPECT_TRUE(
GetTemplateParm(ToDInherited)->getDefaultArgStorage().isInherited());
EXPECT_EQ(GetTemplateParm(ToDInherited)
->getDefaultArgStorage()
.getInheritedFrom(),
GetTemplateParm(ToD));
if (FromD->getPreviousDecl() == FromDInherited) {
EXPECT_EQ(ToD->getPreviousDecl(), ToDInherited);
} else {
EXPECT_EQ(FromD, FromDInherited->getPreviousDecl());
EXPECT_EQ(ToD, ToDInherited->getPreviousDecl());
}
}
const char *CodeFunction =
R"(
template <class> struct X;
template <int A = 2, typename B = int, template<class> class C = X>
void test();
template <int A, typename B, template<class> class C>
void test();
template <int A, typename B, template<class> class C>
void test() {}
)";
const char *CodeClass =
R"(
namespace N {
template <class> struct X;
template <int A = 2, typename B = int, template<class> class C = X>
struct test;
template <int A, typename B, template<class> class C>
struct test;
template <int A, typename B, template<class> class C>
struct test {};
}
)";
const char *CodeVar =
R"(
namespace N {
template <class> struct X;
template <int A = 2, typename B = int, template<class> class C = X>
extern int test;
template <int A, typename B, template<class> class C>
extern int test;
template <int A, typename B, template<class> class C>
int test = A;
}
)";
};
TEST_P(ImportTemplateParmDeclDefaultValue, InvisibleInheritedFrom) {
const char *ToCode =
R"(
template <int P = 1>
void f() {}
)";
TranslationUnitDecl *ToTU = getToTuDecl(ToCode, Lang_CXX14);
auto *ToFDef = FirstDeclMatcher<FunctionTemplateDecl>().match(
ToTU, functionTemplateDecl(hasName("f")));
const char *FromCode =
R"(
template <int P = 1>
void f() {}
template <int P>
void f();
)";
TranslationUnitDecl *FromTU = getTuDecl(FromCode, Lang_CXX14);
auto *FromFDef = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(hasName("f")));
auto *FromF = LastDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(hasName("f")));
auto *ToFDefImported = Import(FromFDef, Lang_CXX14);
EXPECT_EQ(ToFDefImported, ToFDef);
auto *ToF = Import(FromF, Lang_CXX14);
EXPECT_NE(ToF, ToFDef);
const auto *Parm = dyn_cast<NonTypeTemplateParmDecl>(
ToF->getTemplateParameters()->getParam(0));
EXPECT_TRUE(Parm->defaultArgumentWasInherited());
// FIXME: This behavior may be confusing:
// Default value is not inherited from the existing declaration, instead a new
// is created at import that is similar to the existing but not reachable from
// the AST.
EXPECT_NE(Parm->getDefaultArgStorage().getInheritedFrom(),
ToFDef->getTemplateParameters()->getParam(0));
}
TEST_P(ImportTemplateParmDeclDefaultValue, DefValImportError) {
const char *ToCode =
R"(
class X {
int A;
};
)";
getToTuDecl(ToCode, Lang_CXX14);
const char *FromCode =
R"(
class X;
template <typename P = X>
void f() {}
class X {
char A;
};
)";
TranslationUnitDecl *FromTU = getTuDecl(FromCode, Lang_CXX14);
auto *FromF = FirstDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(hasName("f")));
auto *ToFImported = Import(FromF, Lang_CXX14);
EXPECT_FALSE(ToFImported);
}
TEST_P(ImportTemplateParmDeclDefaultValue, ImportFunctionTemplate) {
TranslationUnitDecl *FromTU = getTuDecl(CodeFunction, Lang_CXX14);
auto *D3 = LastDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(hasName("test") /*, hasBody(stmt())*/));
auto *D2 = dyn_cast<FunctionTemplateDecl>(D3->getPreviousDecl());
auto *D1 = dyn_cast<FunctionTemplateDecl>(D2->getPreviousDecl());
testImport(D1, D2, D3, nullptr);
}
TEST_P(ImportTemplateParmDeclDefaultValue, ImportExistingFunctionTemplate) {
TranslationUnitDecl *ToTU = getToTuDecl(CodeFunction, Lang_CXX14);
auto *ToD1 = FirstDeclMatcher<FunctionTemplateDecl>().match(
ToTU, functionTemplateDecl(hasName("test")));
TranslationUnitDecl *FromTU = getTuDecl(CodeFunction, Lang_CXX14);
auto *D3 = LastDeclMatcher<FunctionTemplateDecl>().match(
FromTU, functionTemplateDecl(hasName("test")));
auto *D2 = dyn_cast<FunctionTemplateDecl>(D3->getPreviousDecl());
auto *D1 = dyn_cast<FunctionTemplateDecl>(D2->getPreviousDecl());
testImport(D1, D2, D3, ToD1);
}
TEST_P(ImportTemplateParmDeclDefaultValue, ImportClassTemplate) {
TranslationUnitDecl *FromTU = getTuDecl(CodeClass, Lang_CXX14);
auto *D3 = LastDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("test")));
auto *D2 = dyn_cast<ClassTemplateDecl>(D3->getPreviousDecl());
auto *D1 = dyn_cast<ClassTemplateDecl>(D2->getPreviousDecl());
testImport(D1, D2, D3, nullptr);
}
TEST_P(ImportTemplateParmDeclDefaultValue, ImportExistingClassTemplate) {
TranslationUnitDecl *ToTU = getToTuDecl(CodeClass, Lang_CXX14);
auto *ToD1 = FirstDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("test")));
TranslationUnitDecl *FromTU = getTuDecl(CodeClass, Lang_CXX14);
auto *D3 = LastDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("test")));
auto *D2 = dyn_cast<ClassTemplateDecl>(D3->getPreviousDecl());
auto *D1 = dyn_cast<ClassTemplateDecl>(D2->getPreviousDecl());
testImport(D1, D2, D3, ToD1);
}
TEST_P(ImportTemplateParmDeclDefaultValue, ImportVarTemplate) {
TranslationUnitDecl *FromTU = getTuDecl(CodeVar, Lang_CXX14);
auto *D3 = LastDeclMatcher<VarTemplateDecl>().match(
FromTU, varTemplateDecl(hasName("test")));
auto *D2 = dyn_cast<VarTemplateDecl>(D3->getPreviousDecl());
auto *D1 = dyn_cast<VarTemplateDecl>(D2->getPreviousDecl());
testImport(D1, D2, D3, nullptr);
}
TEST_P(ImportTemplateParmDeclDefaultValue, ImportExistingVarTemplate) {
TranslationUnitDecl *ToTU = getToTuDecl(CodeVar, Lang_CXX14);
auto *ToD1 = FirstDeclMatcher<VarTemplateDecl>().match(
ToTU, varTemplateDecl(hasName("test")));
TranslationUnitDecl *FromTU = getTuDecl(CodeVar, Lang_CXX14);
auto *D3 = LastDeclMatcher<VarTemplateDecl>().match(
FromTU, varTemplateDecl(hasName("test")));
auto *D2 = dyn_cast<VarTemplateDecl>(D3->getPreviousDecl());
auto *D1 = dyn_cast<VarTemplateDecl>(D2->getPreviousDecl());
testImport(D1, D2, D3, ToD1);
}
TEST_P(ImportTemplateParmDeclDefaultValue,
NonTypeTemplateParmDeclCircularDependency) {
const char *Code =
R"(
struct Z;
struct Y {
Z *z;
static const int x = 1;
};
template <int P1 = Y::x>
struct X;
template <int P2>
struct X {
static const int A = 1;
};
struct Z {
template<int P>
void f(int A = X<P>::A);
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX14);
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("X")));
auto *FromDInherited = LastDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("X")));
testTemplateParmDeclCircularDependency<NonTypeTemplateParmDecl>(
FromD, FromDInherited);
}
TEST_P(ImportTemplateParmDeclDefaultValue,
TemplateTypeParmDeclCircularDependency) {
const char *Code =
R"(
struct Z;
struct Y {
Z *z;
};
template <typename T1 = Y>
struct X;
template <typename T2>
struct X {
static const int A = 1;
};
struct Z {
template<typename T>
void f(int A = X<T>::A);
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX14);
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("X")));
auto *FromDInherited = LastDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("X")));
testTemplateParmDeclCircularDependency<TemplateTypeParmDecl>(FromD,
FromDInherited);
}
TEST_P(ImportTemplateParmDeclDefaultValue,
TemplateTemplateParmDeclCircularDependency) {
const char *Code =
R"(
struct Z;
template <int>
struct Y {
Z *z;
};
template <template <int> class T1 = Y>
struct X;
template <template <int> class T2>
struct X {
static const int A = 1;
};
struct Z {
template <template <int> class T>
void f(int A = X<T>::A);
};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX14);
auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("X")));
auto *FromDInherited = LastDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("X")));
testTemplateParmDeclCircularDependency<TemplateTemplateParmDecl>(
FromD, FromDInherited);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportIntoReopenedNamespaceNoMatch1) {
const char *ToCode =
R"(
namespace a {
}
namespace a {
struct X { int A; };
}
)";
getToTuDecl(ToCode, Lang_CXX11);
const char *Code =
R"(
namespace a {
struct X { char A; };
}
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X")));
auto *ImportedX = Import(FromX, Lang_CXX11);
EXPECT_FALSE(ImportedX);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportIntoReopenedNamespaceNoMatch2) {
const char *ToCode =
R"(
namespace a {
struct X { int A; };
}
namespace a {
}
)";
getToTuDecl(ToCode, Lang_CXX11);
const char *Code =
R"(
namespace a {
struct X { char A; };
}
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X")));
auto *ImportedX = Import(FromX, Lang_CXX11);
EXPECT_FALSE(ImportedX);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportIntoReopenedNamespaceMatch1) {
const char *ToCode =
R"(
namespace a {
}
namespace a {
struct X { int A; };
}
)";
Decl *ToTU = getToTuDecl(ToCode, Lang_CXX11);
const char *Code =
R"(
namespace a {
struct X { int A; };
}
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X")));
auto *ToX = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(hasName("X")));
auto *ImportedX = Import(FromX, Lang_CXX11);
EXPECT_EQ(ImportedX, ToX);
}
TEST_P(ASTImporterOptionSpecificTestBase, ImportIntoReopenedNamespaceMatch2) {
const char *ToCode =
R"(
namespace a {
struct X { int A; };
}
namespace a {
}
)";
Decl *ToTU = getToTuDecl(ToCode, Lang_CXX11);
const char *Code =
R"(
namespace a {
struct X { int A; };
}
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromX = FirstDeclMatcher<CXXRecordDecl>().match(
FromTU, cxxRecordDecl(hasName("X")));
auto *ToX = FirstDeclMatcher<CXXRecordDecl>().match(
ToTU, cxxRecordDecl(hasName("X")));
auto *ImportedX = Import(FromX, Lang_CXX11);
EXPECT_EQ(ImportedX, ToX);
}
TEST_P(ASTImporterLookupTableTest, PrimaryDCChangeAtImport) {
const char *ToCode =
R"(
template <class T>
struct X;
)";
Decl *ToTU = getToTuDecl(ToCode, Lang_CXX11);
auto *ToX = FirstDeclMatcher<ClassTemplateDecl>().match(
ToTU, classTemplateDecl(hasName("X")));
NamedDecl *ToParm = ToX->getTemplateParameters()->getParam(0);
DeclContext *OldPrimaryDC = ToX->getTemplatedDecl()->getPrimaryContext();
ASSERT_EQ(ToParm->getDeclContext(), ToX->getTemplatedDecl());
ASSERT_EQ(SharedStatePtr->getLookupTable()
->lookup(ToX->getTemplatedDecl(), ToParm->getDeclName())
.size(),
1u);
ASSERT_TRUE(SharedStatePtr->getLookupTable()->contains(
ToX->getTemplatedDecl(), ToParm));
const char *Code =
R"(
template <class T>
struct X;
template <class T>
struct X {};
)";
Decl *FromTU = getTuDecl(Code, Lang_CXX11);
auto *FromX = LastDeclMatcher<ClassTemplateDecl>().match(
FromTU, classTemplateDecl(hasName("X")));
auto *ImportedX = Import(FromX, Lang_CXX11);
EXPECT_TRUE(ImportedX);
EXPECT_EQ(ImportedX->getTemplateParameters()->getParam(0)->getDeclContext(),
ImportedX->getTemplatedDecl());
// ToX did not change at the import.
// Verify that primary context has changed after import of class definition.
DeclContext *NewPrimaryDC = ToX->getTemplatedDecl()->getPrimaryContext();
EXPECT_NE(OldPrimaryDC, NewPrimaryDC);
// The lookup table should not be different than it was before.
EXPECT_EQ(SharedStatePtr->getLookupTable()
->lookup(ToX->getTemplatedDecl(), ToParm->getDeclName())
.size(),
1u);
EXPECT_TRUE(SharedStatePtr->getLookupTable()->contains(
ToX->getTemplatedDecl(), ToParm));
}
TEST_P(ASTImporterOptionSpecificTestBase,
ExistingUndeclaredImportDeclaredFriend) {
Decl *ToTU = getToTuDecl(
R"(
template <class A, A>
struct foo;
template <class A>
struct X {
template <class A1, A1>
friend struct foo;
};
)",
Lang_CXX11);
Decl *FromTU = getTuDecl(
R"(
template <class A, A>
struct foo;
template <class A>
struct X {
template <class A1, A1>
friend struct foo;
};
X<int> x;
)",
Lang_CXX11);
auto *ToFr1 = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
auto *ToFrD1 = ToFr1->getFriendDecl();
auto *FromFr1 = FirstDeclMatcher<FriendDecl>().match(FromTU, friendDecl());
auto *FromFr2 = LastDeclMatcher<FriendDecl>().match(FromTU, friendDecl());
auto *FromFrD1 = FromFr1->getFriendDecl();
auto *FromFrD2 = FromFr2->getFriendDecl();
auto *Ctx1 = cast<Decl>(FromFrD1->getDeclContext());
auto *Ctx2 = cast<Decl>(FromFrD2->getDeclContext());
ASSERT_EQ(Ctx1, Ctx2);
ASSERT_EQ(ToFrD1->getTemplateDepth(), 1u);
ASSERT_EQ(FromFrD2->getTemplateDepth(), 0u);
ASSERT_EQ(ToFrD1->getFriendObjectKind(), Decl::FOK_Undeclared);
ASSERT_EQ(FromFrD2->getFriendObjectKind(), Decl::FOK_Declared);
auto *ToFr2Imp = Import(FromFr2, Lang_CXX11);
EXPECT_TRUE(ToFr2Imp);
}
TEST_P(ASTImporterOptionSpecificTestBase,
ExistingDeclaredImportUndeclaredFriend) {
Decl *ToTU = getToTuDecl(
R"(
template <class A, A>
struct foo;
template <class A>
struct X {
template <class A1, A1>
friend struct foo;
};
X<int> x;
)",
Lang_CXX11);
Decl *FromTU = getTuDecl(
R"(
template <class A, A>
struct foo;
template <class A>
struct X {
template <class A1, A1>
friend struct foo;
};
)",
Lang_CXX11);
auto *ToFr1 = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
auto *ToFr2 = LastDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
auto *ToFrD1 = ToFr1->getFriendDecl();
auto *ToFrD2 = ToFr2->getFriendDecl();
auto *FromFr1 = FirstDeclMatcher<FriendDecl>().match(FromTU, friendDecl());
auto *FromFrD1 = FromFr1->getFriendDecl();
auto *Ctx1 = cast<Decl>(ToFrD1->getDeclContext());
auto *Ctx2 = cast<Decl>(ToFrD2->getDeclContext());
ASSERT_EQ(Ctx1, Ctx2);
ASSERT_EQ(FromFrD1->getTemplateDepth(), 1u);
ASSERT_EQ(ToFrD2->getTemplateDepth(), 0u);
ASSERT_EQ(FromFrD1->getFriendObjectKind(), Decl::FOK_Undeclared);
ASSERT_EQ(ToFrD2->getFriendObjectKind(), Decl::FOK_Declared);
auto *ToFr1Imp = Import(FromFr1, Lang_CXX11);
EXPECT_TRUE(ToFr1Imp);
EXPECT_EQ(ToFr1Imp, ToFr1);
}
struct ImportAndMergeAnonymousNamespace
: public ASTImporterOptionSpecificTestBase {
protected:
void test(const char *ToCode, const char *FromCode) {
Decl *ToTU = getToTuDecl(ToCode, Lang_CXX11);
Decl *FromTU = getTuDecl(FromCode, Lang_CXX11);
auto *FromNS = FirstDeclMatcher<NamespaceDecl>().match(
FromTU, namespaceDecl(isAnonymous()));
auto *ToNS = FirstDeclMatcher<NamespaceDecl>().match(
ToTU, namespaceDecl(isAnonymous()));
auto *FromF = FirstDeclMatcher<FunctionDecl>().match(
FromTU, functionDecl(hasName("f")));
auto *ImportedF = Import(FromF, Lang_CXX11);
EXPECT_TRUE(ImportedF);
EXPECT_EQ(ImportedF->getDeclContext(), ToNS);
auto *ImportedNS = Import(FromNS, Lang_CXX11);
EXPECT_EQ(ImportedNS, ToNS);
}
};
TEST_P(ImportAndMergeAnonymousNamespace, NamespaceInTU) {
const char *ToCode =
R"(
namespace {
}
)";
const char *FromCode =
R"(
namespace {
void f();
}
)";
test(ToCode, FromCode);
}
TEST_P(ImportAndMergeAnonymousNamespace, NamespaceInLinkageSpec) {
const char *ToCode =
R"(
extern "C" {
namespace {
}
}
)";
const char *FromCode =
R"(
extern "C" {
namespace {
void f();
}
}
)";
test(ToCode, FromCode);
}
TEST_P(ImportAndMergeAnonymousNamespace, NamespaceInNamespace) {
const char *ToCode =
R"(
namespace X {
namespace {
}
}
)";
const char *FromCode =
R"(
namespace X {
namespace {
void f();
}
}
)";
test(ToCode, FromCode);
}
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);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportInjectedClassNameType,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportMatrixType,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportTemplateParmDeclDefaultValue,
DefaultTestValuesForRunOptions);
INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportAndMergeAnonymousNamespace,
DefaultTestValuesForRunOptions);
// FIXME: Make ImportOpenCLPipe test work.
// INSTANTIATE_TEST_SUITE_P(ParameterizedTests, ImportOpenCLPipe,
// DefaultTestValuesForRunOptions);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ImportOpenCLPipe);
} // end namespace ast_matchers
} // end namespace clang