Skip to content

Commit c7797c4

Browse files
committedJul 11, 2018
[AST] Structural equivalence of methods
Summary: Added structural equivalence check for C++ methods. Improved structural equivalence tests. Added related ASTImporter tests. Reviewers: a.sidorin, szepet, xazax.hun, martong, a_sidorin Reviewed By: martong, a_sidorin Subscribers: a_sidorin, rnkovacs, cfe-commits Differential Revision: https://reviews.llvm.org/D48628 llvm-svn: 336776
1 parent e21cfa7 commit c7797c4

File tree

4 files changed

+619
-73
lines changed

4 files changed

+619
-73
lines changed
 

‎clang/lib/AST/ASTImporter.cpp

+17-7
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ namespace clang {
230230
bool IsStructuralMatch(EnumConstantDecl *FromEC, EnumConstantDecl *ToEC);
231231
bool IsStructuralMatch(FunctionTemplateDecl *From,
232232
FunctionTemplateDecl *To);
233+
bool IsStructuralMatch(FunctionDecl *From, FunctionDecl *To);
233234
bool IsStructuralMatch(ClassTemplateDecl *From, ClassTemplateDecl *To);
234235
bool IsStructuralMatch(VarTemplateDecl *From, VarTemplateDecl *To);
235236
Decl *VisitDecl(Decl *D);
@@ -1525,6 +1526,13 @@ bool ASTNodeImporter::IsStructuralMatch(FunctionTemplateDecl *From,
15251526
return Ctx.IsStructurallyEquivalent(From, To);
15261527
}
15271528

1529+
bool ASTNodeImporter::IsStructuralMatch(FunctionDecl *From, FunctionDecl *To) {
1530+
StructuralEquivalenceContext Ctx(
1531+
Importer.getFromContext(), Importer.getToContext(),
1532+
Importer.getNonEquivalentDecls(), false, false);
1533+
return Ctx.IsStructurallyEquivalent(From, To);
1534+
}
1535+
15281536
bool ASTNodeImporter::IsStructuralMatch(EnumConstantDecl *FromEC,
15291537
EnumConstantDecl *ToEC) {
15301538
const llvm::APSInt &FromVal = FromEC->getInitVal();
@@ -2433,13 +2441,15 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
24332441
if (auto *FoundFunction = dyn_cast<FunctionDecl>(FoundDecl)) {
24342442
if (FoundFunction->hasExternalFormalLinkage() &&
24352443
D->hasExternalFormalLinkage()) {
2436-
if (Importer.IsStructurallyEquivalent(D->getType(),
2437-
FoundFunction->getType())) {
2438-
if (D->doesThisDeclarationHaveABody() &&
2439-
FoundFunction->hasBody())
2440-
return Importer.Imported(D, FoundFunction);
2441-
FoundByLookup = FoundFunction;
2442-
break;
2444+
if (IsStructuralMatch(D, FoundFunction)) {
2445+
const FunctionDecl *Definition = nullptr;
2446+
if (D->doesThisDeclarationHaveABody() &&
2447+
FoundFunction->hasBody(Definition)) {
2448+
return Importer.Imported(
2449+
D, const_cast<FunctionDecl *>(Definition));
2450+
}
2451+
FoundByLookup = FoundFunction;
2452+
break;
24432453
}
24442454

24452455
// FIXME: Check for overloading more carefully, e.g., by boosting

‎clang/lib/AST/ASTStructuralEquivalence.cpp

+83-11
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
250250
if (T1.isNull() || T2.isNull())
251251
return T1.isNull() && T2.isNull();
252252

253+
QualType OrigT1 = T1;
254+
QualType OrigT2 = T2;
255+
253256
if (!Context.StrictTypeSpelling) {
254257
// We aren't being strict about token-to-token equivalence of types,
255258
// so map down to the canonical type.
@@ -422,6 +425,7 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
422425
case Type::FunctionProto: {
423426
const auto *Proto1 = cast<FunctionProtoType>(T1);
424427
const auto *Proto2 = cast<FunctionProtoType>(T2);
428+
425429
if (Proto1->getNumParams() != Proto2->getNumParams())
426430
return false;
427431
for (unsigned I = 0, N = Proto1->getNumParams(); I != N; ++I) {
@@ -431,23 +435,33 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
431435
}
432436
if (Proto1->isVariadic() != Proto2->isVariadic())
433437
return false;
434-
if (Proto1->getExceptionSpecType() != Proto2->getExceptionSpecType())
438+
439+
if (Proto1->getTypeQuals() != Proto2->getTypeQuals())
435440
return false;
436-
if (Proto1->getExceptionSpecType() == EST_Dynamic) {
437-
if (Proto1->getNumExceptions() != Proto2->getNumExceptions())
441+
442+
// Check exceptions, this information is lost in canonical type.
443+
const auto *OrigProto1 =
444+
cast<FunctionProtoType>(OrigT1.getDesugaredType(Context.FromCtx));
445+
const auto *OrigProto2 =
446+
cast<FunctionProtoType>(OrigT2.getDesugaredType(Context.ToCtx));
447+
auto Spec1 = OrigProto1->getExceptionSpecType();
448+
auto Spec2 = OrigProto2->getExceptionSpecType();
449+
450+
if (Spec1 != Spec2)
451+
return false;
452+
if (Spec1 == EST_Dynamic) {
453+
if (OrigProto1->getNumExceptions() != OrigProto2->getNumExceptions())
438454
return false;
439-
for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) {
440-
if (!IsStructurallyEquivalent(Context, Proto1->getExceptionType(I),
441-
Proto2->getExceptionType(I)))
455+
for (unsigned I = 0, N = OrigProto1->getNumExceptions(); I != N; ++I) {
456+
if (!IsStructurallyEquivalent(Context, OrigProto1->getExceptionType(I),
457+
OrigProto2->getExceptionType(I)))
442458
return false;
443459
}
444-
} else if (isComputedNoexcept(Proto1->getExceptionSpecType())) {
445-
if (!IsStructurallyEquivalent(Context, Proto1->getNoexceptExpr(),
446-
Proto2->getNoexceptExpr()))
460+
} else if (isComputedNoexcept(Spec1)) {
461+
if (!IsStructurallyEquivalent(Context, OrigProto1->getNoexceptExpr(),
462+
OrigProto2->getNoexceptExpr()))
447463
return false;
448464
}
449-
if (Proto1->getTypeQuals() != Proto2->getTypeQuals())
450-
return false;
451465

452466
// Fall through to check the bits common with FunctionNoProtoType.
453467
LLVM_FALLTHROUGH;
@@ -830,6 +844,56 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
830844
return true;
831845
}
832846

847+
/// Determine structural equivalence of two methodss.
848+
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
849+
CXXMethodDecl *Method1,
850+
CXXMethodDecl *Method2) {
851+
bool PropertiesEqual =
852+
Method1->getDeclKind() == Method2->getDeclKind() &&
853+
Method1->getRefQualifier() == Method2->getRefQualifier() &&
854+
Method1->getAccess() == Method2->getAccess() &&
855+
Method1->getOverloadedOperator() == Method2->getOverloadedOperator() &&
856+
Method1->isStatic() == Method2->isStatic() &&
857+
Method1->isConst() == Method2->isConst() &&
858+
Method1->isVolatile() == Method2->isVolatile() &&
859+
Method1->isVirtual() == Method2->isVirtual() &&
860+
Method1->isPure() == Method2->isPure() &&
861+
Method1->isDefaulted() == Method2->isDefaulted() &&
862+
Method1->isDeleted() == Method2->isDeleted();
863+
if (!PropertiesEqual)
864+
return false;
865+
// FIXME: Check for 'final'.
866+
867+
if (auto *Constructor1 = dyn_cast<CXXConstructorDecl>(Method1)) {
868+
auto *Constructor2 = cast<CXXConstructorDecl>(Method2);
869+
if (Constructor1->isExplicit() != Constructor2->isExplicit())
870+
return false;
871+
}
872+
873+
if (auto *Conversion1 = dyn_cast<CXXConversionDecl>(Method1)) {
874+
auto *Conversion2 = cast<CXXConversionDecl>(Method2);
875+
if (Conversion1->isExplicit() != Conversion2->isExplicit())
876+
return false;
877+
if (!IsStructurallyEquivalent(Context, Conversion1->getConversionType(),
878+
Conversion2->getConversionType()))
879+
return false;
880+
}
881+
882+
const IdentifierInfo *Name1 = Method1->getIdentifier();
883+
const IdentifierInfo *Name2 = Method2->getIdentifier();
884+
if (!::IsStructurallyEquivalent(Name1, Name2)) {
885+
return false;
886+
// TODO: Names do not match, add warning like at check for FieldDecl.
887+
}
888+
889+
// Check the prototypes.
890+
if (!::IsStructurallyEquivalent(Context,
891+
Method1->getType(), Method2->getType()))
892+
return false;
893+
894+
return true;
895+
}
896+
833897
/// Determine structural equivalence of two records.
834898
static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
835899
RecordDecl *D1, RecordDecl *D2) {
@@ -1445,6 +1509,14 @@ bool StructuralEquivalenceContext::Finish() {
14451509
// Kind mismatch.
14461510
Equivalent = false;
14471511
}
1512+
} else if (auto *MD1 = dyn_cast<CXXMethodDecl>(D1)) {
1513+
if (auto *MD2 = dyn_cast<CXXMethodDecl>(D2)) {
1514+
if (!::IsStructurallyEquivalent(*this, MD1, MD2))
1515+
Equivalent = false;
1516+
} else {
1517+
// Kind mismatch.
1518+
Equivalent = false;
1519+
}
14481520
} else if (FunctionDecl *FD1 = dyn_cast<FunctionDecl>(D1)) {
14491521
if (FunctionDecl *FD2 = dyn_cast<FunctionDecl>(D2)) {
14501522
if (!::IsStructurallyEquivalent(FD1->getIdentifier(),

‎clang/unittests/AST/ASTImporterTest.cpp

+126
Original file line numberDiff line numberDiff line change
@@ -2243,6 +2243,132 @@ TEST_P(ImportExpr, UnresolvedMemberExpr) {
22432243
compoundStmt(has(callExpr(has(unresolvedMemberExpr())))))))));
22442244
}
22452245

2246+
TEST_P(ASTImporterTestBase, ImportOfEquivalentRecord) {
2247+
Decl *ToR1;
2248+
{
2249+
Decl *FromTU = getTuDecl(
2250+
"struct A { };", Lang_CXX, "input0.cc");
2251+
auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
2252+
FromTU, cxxRecordDecl(hasName("A")));
2253+
2254+
ToR1 = Import(FromR, Lang_CXX);
2255+
}
2256+
2257+
Decl *ToR2;
2258+
{
2259+
Decl *FromTU = getTuDecl(
2260+
"struct A { };", Lang_CXX, "input1.cc");
2261+
auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
2262+
FromTU, cxxRecordDecl(hasName("A")));
2263+
2264+
ToR2 = Import(FromR, Lang_CXX);
2265+
}
2266+
2267+
EXPECT_EQ(ToR1, ToR2);
2268+
}
2269+
2270+
TEST_P(ASTImporterTestBase, ImportOfNonEquivalentRecord) {
2271+
Decl *ToR1;
2272+
{
2273+
Decl *FromTU = getTuDecl(
2274+
"struct A { int x; };", Lang_CXX, "input0.cc");
2275+
auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
2276+
FromTU, cxxRecordDecl(hasName("A")));
2277+
ToR1 = Import(FromR, Lang_CXX);
2278+
}
2279+
Decl *ToR2;
2280+
{
2281+
Decl *FromTU = getTuDecl(
2282+
"struct A { unsigned x; };", Lang_CXX, "input1.cc");
2283+
auto *FromR = FirstDeclMatcher<CXXRecordDecl>().match(
2284+
FromTU, cxxRecordDecl(hasName("A")));
2285+
ToR2 = Import(FromR, Lang_CXX);
2286+
}
2287+
EXPECT_NE(ToR1, ToR2);
2288+
}
2289+
2290+
TEST_P(ASTImporterTestBase, ImportOfEquivalentField) {
2291+
Decl *ToF1;
2292+
{
2293+
Decl *FromTU = getTuDecl(
2294+
"struct A { int x; };", Lang_CXX, "input0.cc");
2295+
auto *FromF = FirstDeclMatcher<FieldDecl>().match(
2296+
FromTU, fieldDecl(hasName("x")));
2297+
ToF1 = Import(FromF, Lang_CXX);
2298+
}
2299+
Decl *ToF2;
2300+
{
2301+
Decl *FromTU = getTuDecl(
2302+
"struct A { int x; };", Lang_CXX, "input1.cc");
2303+
auto *FromF = FirstDeclMatcher<FieldDecl>().match(
2304+
FromTU, fieldDecl(hasName("x")));
2305+
ToF2 = Import(FromF, Lang_CXX);
2306+
}
2307+
EXPECT_EQ(ToF1, ToF2);
2308+
}
2309+
2310+
TEST_P(ASTImporterTestBase, ImportOfNonEquivalentField) {
2311+
Decl *ToF1;
2312+
{
2313+
Decl *FromTU = getTuDecl(
2314+
"struct A { int x; };", Lang_CXX, "input0.cc");
2315+
auto *FromF = FirstDeclMatcher<FieldDecl>().match(
2316+
FromTU, fieldDecl(hasName("x")));
2317+
ToF1 = Import(FromF, Lang_CXX);
2318+
}
2319+
Decl *ToF2;
2320+
{
2321+
Decl *FromTU = getTuDecl(
2322+
"struct A { unsigned x; };", Lang_CXX, "input1.cc");
2323+
auto *FromF = FirstDeclMatcher<FieldDecl>().match(
2324+
FromTU, fieldDecl(hasName("x")));
2325+
ToF2 = Import(FromF, Lang_CXX);
2326+
}
2327+
EXPECT_NE(ToF1, ToF2);
2328+
}
2329+
2330+
TEST_P(ASTImporterTestBase, ImportOfEquivalentMethod) {
2331+
Decl *ToM1;
2332+
{
2333+
Decl *FromTU = getTuDecl(
2334+
"struct A { void x(); }; void A::x() { }", Lang_CXX, "input0.cc");
2335+
auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
2336+
FromTU, functionDecl(hasName("x"), isDefinition()));
2337+
ToM1 = Import(FromM, Lang_CXX);
2338+
}
2339+
Decl *ToM2;
2340+
{
2341+
Decl *FromTU = getTuDecl(
2342+
"struct A { void x(); }; void A::x() { }", Lang_CXX, "input1.cc");
2343+
auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
2344+
FromTU, functionDecl(hasName("x"), isDefinition()));
2345+
ToM2 = Import(FromM, Lang_CXX);
2346+
}
2347+
EXPECT_EQ(ToM1, ToM2);
2348+
}
2349+
2350+
TEST_P(ASTImporterTestBase, ImportOfNonEquivalentMethod) {
2351+
Decl *ToM1;
2352+
{
2353+
Decl *FromTU = getTuDecl(
2354+
"struct A { void x(); }; void A::x() { }",
2355+
Lang_CXX, "input0.cc");
2356+
auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
2357+
FromTU, functionDecl(hasName("x"), isDefinition()));
2358+
ToM1 = Import(FromM, Lang_CXX);
2359+
}
2360+
Decl *ToM2;
2361+
{
2362+
Decl *FromTU = getTuDecl(
2363+
"struct A { void x() const; }; void A::x() const { }",
2364+
Lang_CXX, "input1.cc");
2365+
auto *FromM = FirstDeclMatcher<FunctionDecl>().match(
2366+
FromTU, functionDecl(hasName("x"), isDefinition()));
2367+
ToM2 = Import(FromM, Lang_CXX);
2368+
}
2369+
EXPECT_NE(ToM1, ToM2);
2370+
}
2371+
22462372
struct DeclContextTest : ASTImporterTestBase {};
22472373

22482374
TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {

‎clang/unittests/AST/StructuralEquivalenceTest.cpp

+393-55
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ struct StructuralEquivalenceTest : ::testing::Test {
1818
std::unique_ptr<ASTUnit> AST0, AST1;
1919
std::string Code0, Code1; // Buffers for SourceManager
2020

21-
// Get a pair of Decl pointers to the synthetised declarations from the given
22-
// code snipets. By default we search for the unique Decl with name 'foo' in
23-
// both snippets.
24-
std::tuple<NamedDecl *, NamedDecl *>
25-
makeNamedDecls(const std::string &SrcCode0, const std::string &SrcCode1,
26-
Language Lang, const char *const Identifier = "foo") {
27-
21+
// Get a pair of node pointers into the synthesized AST from the given code
22+
// snippets. To determine the returned node, a separate matcher is specified
23+
// for both snippets. The first matching node is returned.
24+
template <typename NodeType, typename MatcherType>
25+
std::tuple<NodeType *, NodeType *> makeDecls(
26+
const std::string &SrcCode0, const std::string &SrcCode1, Language Lang,
27+
const MatcherType &Matcher0, const MatcherType &Matcher1) {
2828
this->Code0 = SrcCode0;
2929
this->Code1 = SrcCode1;
3030
ArgVector Args = getBasicRunOptionsForLanguage(Lang);
@@ -34,28 +34,32 @@ struct StructuralEquivalenceTest : ::testing::Test {
3434
AST0 = tooling::buildASTFromCodeWithArgs(Code0, Args, InputFileName);
3535
AST1 = tooling::buildASTFromCodeWithArgs(Code1, Args, InputFileName);
3636

37-
ASTContext &Ctx0 = AST0->getASTContext(), &Ctx1 = AST1->getASTContext();
38-
39-
auto getDecl = [](ASTContext &Ctx, const std::string &Name) -> NamedDecl * {
40-
IdentifierInfo *SearchedII = &Ctx.Idents.get(Name);
41-
assert(SearchedII && "Declaration with the identifier "
42-
"should be specified in test!");
43-
DeclarationName SearchDeclName(SearchedII);
44-
SmallVector<NamedDecl *, 4> FoundDecls;
45-
Ctx.getTranslationUnitDecl()->localUncachedLookup(SearchDeclName,
46-
FoundDecls);
37+
NodeType *D0 = FirstDeclMatcher<NodeType>().match(
38+
AST0->getASTContext().getTranslationUnitDecl(), Matcher0);
39+
NodeType *D1 = FirstDeclMatcher<NodeType>().match(
40+
AST1->getASTContext().getTranslationUnitDecl(), Matcher1);
4741

48-
// We should find one Decl but one only.
49-
assert(FoundDecls.size() == 1);
42+
return std::make_tuple(D0, D1);
43+
}
5044

51-
return FoundDecls[0];
52-
};
45+
// Get a pair of node pointers into the synthesized AST from the given code
46+
// snippets. The same matcher is used for both snippets.
47+
template <typename NodeType, typename MatcherType>
48+
std::tuple<NodeType *, NodeType *> makeDecls(
49+
const std::string &SrcCode0, const std::string &SrcCode1, Language Lang,
50+
const MatcherType &AMatcher) {
51+
return makeDecls<NodeType, MatcherType>(
52+
SrcCode0, SrcCode1, Lang, AMatcher, AMatcher);
53+
}
5354

54-
NamedDecl *D0 = getDecl(Ctx0, Identifier);
55-
NamedDecl *D1 = getDecl(Ctx1, Identifier);
56-
assert(D0);
57-
assert(D1);
58-
return std::make_tuple(D0, D1);
55+
// Get a pair of Decl pointers to the synthesized declarations from the given
56+
// code snippets. We search for the first NamedDecl with given name in both
57+
// snippets.
58+
std::tuple<NamedDecl *, NamedDecl *> makeNamedDecls(
59+
const std::string &SrcCode0, const std::string &SrcCode1,
60+
Language Lang, const char *const Identifier = "foo") {
61+
auto Matcher = namedDecl(hasName(Identifier));
62+
return makeDecls<NamedDecl>(SrcCode0, SrcCode1, Lang, Matcher);
5963
}
6064

6165
bool testStructuralMatch(NamedDecl *D0, NamedDecl *D1) {
@@ -110,35 +114,29 @@ TEST_F(StructuralEquivalenceTest, CharVsSignedCharInStruct) {
110114
}
111115

112116
TEST_F(StructuralEquivalenceTest, IntVsSignedIntTemplateSpec) {
113-
auto Decls = makeNamedDecls(
114-
"template <class T> struct foo; template<> struct foo<int>{};",
115-
"template <class T> struct foo; template<> struct foo<signed int>{};",
116-
Lang_CXX);
117-
ClassTemplateSpecializationDecl *Spec0 =
118-
*cast<ClassTemplateDecl>(get<0>(Decls))->spec_begin();
119-
ClassTemplateSpecializationDecl *Spec1 =
120-
*cast<ClassTemplateDecl>(get<1>(Decls))->spec_begin();
121-
ASSERT_TRUE(Spec0 != nullptr);
122-
ASSERT_TRUE(Spec1 != nullptr);
117+
auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
118+
R"(template <class T> struct foo; template<> struct foo<int>{};)",
119+
R"(template <class T> struct foo; template<> struct foo<signed int>{};)",
120+
Lang_CXX,
121+
classTemplateSpecializationDecl());
122+
auto Spec0 = get<0>(Decls);
123+
auto Spec1 = get<1>(Decls);
123124
EXPECT_TRUE(testStructuralMatch(Spec0, Spec1));
124125
}
125126

126127
TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpec) {
127-
auto Decls = makeNamedDecls(
128-
"template <class T> struct foo; template<> struct foo<char>{};",
129-
"template <class T> struct foo; template<> struct foo<signed char>{};",
130-
Lang_CXX);
131-
ClassTemplateSpecializationDecl *Spec0 =
132-
*cast<ClassTemplateDecl>(get<0>(Decls))->spec_begin();
133-
ClassTemplateSpecializationDecl *Spec1 =
134-
*cast<ClassTemplateDecl>(get<1>(Decls))->spec_begin();
135-
ASSERT_TRUE(Spec0 != nullptr);
136-
ASSERT_TRUE(Spec1 != nullptr);
128+
auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
129+
R"(template <class T> struct foo; template<> struct foo<char>{};)",
130+
R"(template <class T> struct foo; template<> struct foo<signed char>{};)",
131+
Lang_CXX,
132+
classTemplateSpecializationDecl());
133+
auto Spec0 = get<0>(Decls);
134+
auto Spec1 = get<1>(Decls);
137135
EXPECT_FALSE(testStructuralMatch(Spec0, Spec1));
138136
}
139137

140138
TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpecWithInheritance) {
141-
auto Decls = makeNamedDecls(
139+
auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
142140
R"(
143141
struct true_type{};
144142
template <class T> struct foo;
@@ -149,14 +147,9 @@ TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpecWithInheritance) {
149147
template <class T> struct foo;
150148
template<> struct foo<signed char> : true_type {};
151149
)",
152-
Lang_CXX);
153-
ClassTemplateSpecializationDecl *Spec0 =
154-
*cast<ClassTemplateDecl>(get<0>(Decls))->spec_begin();
155-
ClassTemplateSpecializationDecl *Spec1 =
156-
*cast<ClassTemplateDecl>(get<1>(Decls))->spec_begin();
157-
ASSERT_TRUE(Spec0 != nullptr);
158-
ASSERT_TRUE(Spec1 != nullptr);
159-
EXPECT_FALSE(testStructuralMatch(Spec0, Spec1));
150+
Lang_CXX,
151+
classTemplateSpecializationDecl());
152+
EXPECT_FALSE(testStructuralMatch(Decls));
160153
}
161154

162155
// This test is disabled for now.
@@ -203,5 +196,350 @@ TEST_F(StructuralEquivalenceTest, WrongOrderOfFieldsInClass) {
203196
EXPECT_FALSE(testStructuralMatch(Decls));
204197
}
205198

199+
struct StructuralEquivalenceFunctionTest : StructuralEquivalenceTest {
200+
};
201+
202+
TEST_F(StructuralEquivalenceFunctionTest, ParamConstWithRef) {
203+
auto t = makeNamedDecls("void foo(int&);",
204+
"void foo(const int&);", Lang_CXX);
205+
EXPECT_FALSE(testStructuralMatch(t));
206+
}
207+
208+
TEST_F(StructuralEquivalenceFunctionTest, ParamConstSimple) {
209+
auto t = makeNamedDecls("void foo(int);",
210+
"void foo(const int);", Lang_CXX);
211+
EXPECT_TRUE(testStructuralMatch(t));
212+
// consider this OK
213+
}
214+
215+
TEST_F(StructuralEquivalenceFunctionTest, Throw) {
216+
auto t = makeNamedDecls("void foo();",
217+
"void foo() throw();", Lang_CXX);
218+
EXPECT_FALSE(testStructuralMatch(t));
219+
}
220+
221+
TEST_F(StructuralEquivalenceFunctionTest, Noexcept) {
222+
auto t = makeNamedDecls("void foo();",
223+
"void foo() noexcept;", Lang_CXX11);
224+
EXPECT_FALSE(testStructuralMatch(t));
225+
}
226+
227+
TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexcept) {
228+
auto t = makeNamedDecls("void foo() throw();",
229+
"void foo() noexcept;", Lang_CXX11);
230+
EXPECT_FALSE(testStructuralMatch(t));
231+
}
232+
233+
TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexceptFalse) {
234+
auto t = makeNamedDecls("void foo() throw();",
235+
"void foo() noexcept(false);", Lang_CXX11);
236+
EXPECT_FALSE(testStructuralMatch(t));
237+
}
238+
239+
TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexceptTrue) {
240+
auto t = makeNamedDecls("void foo() throw();",
241+
"void foo() noexcept(true);", Lang_CXX11);
242+
EXPECT_FALSE(testStructuralMatch(t));
243+
}
244+
245+
TEST_F(StructuralEquivalenceFunctionTest, DISABLED_NoexceptNonMatch) {
246+
// The expression is not checked yet.
247+
auto t = makeNamedDecls("void foo() noexcept(false);",
248+
"void foo() noexcept(true);", Lang_CXX11);
249+
EXPECT_FALSE(testStructuralMatch(t));
250+
}
251+
252+
TEST_F(StructuralEquivalenceFunctionTest, NoexceptMatch) {
253+
auto t = makeNamedDecls("void foo() noexcept(false);",
254+
"void foo() noexcept(false);", Lang_CXX11);
255+
EXPECT_TRUE(testStructuralMatch(t));
256+
}
257+
258+
TEST_F(StructuralEquivalenceFunctionTest, NoexceptVsNoexceptFalse) {
259+
auto t = makeNamedDecls("void foo() noexcept;",
260+
"void foo() noexcept(false);", Lang_CXX11);
261+
EXPECT_FALSE(testStructuralMatch(t));
262+
}
263+
264+
TEST_F(StructuralEquivalenceFunctionTest, NoexceptVsNoexceptTrue) {
265+
auto t = makeNamedDecls("void foo() noexcept;",
266+
"void foo() noexcept(true);", Lang_CXX11);
267+
EXPECT_FALSE(testStructuralMatch(t));
268+
}
269+
270+
TEST_F(StructuralEquivalenceFunctionTest, ReturnType) {
271+
auto t = makeNamedDecls("char foo();",
272+
"int foo();", Lang_CXX);
273+
EXPECT_FALSE(testStructuralMatch(t));
274+
}
275+
276+
TEST_F(StructuralEquivalenceFunctionTest, ReturnConst) {
277+
auto t = makeNamedDecls("char foo();",
278+
"const char foo();", Lang_CXX);
279+
EXPECT_FALSE(testStructuralMatch(t));
280+
}
281+
282+
TEST_F(StructuralEquivalenceFunctionTest, ReturnRef) {
283+
auto t = makeNamedDecls("char &foo();",
284+
"char &&foo();", Lang_CXX11);
285+
EXPECT_FALSE(testStructuralMatch(t));
286+
}
287+
288+
TEST_F(StructuralEquivalenceFunctionTest, ParamCount) {
289+
auto t = makeNamedDecls("void foo(int);",
290+
"void foo(int, int);", Lang_CXX);
291+
EXPECT_FALSE(testStructuralMatch(t));
292+
}
293+
294+
TEST_F(StructuralEquivalenceFunctionTest, ParamType) {
295+
auto t = makeNamedDecls("void foo(int);",
296+
"void foo(char);", Lang_CXX);
297+
EXPECT_FALSE(testStructuralMatch(t));
298+
}
299+
300+
TEST_F(StructuralEquivalenceFunctionTest, ParamName) {
301+
auto t = makeNamedDecls("void foo(int a);",
302+
"void foo(int b);", Lang_CXX);
303+
EXPECT_TRUE(testStructuralMatch(t));
304+
}
305+
306+
TEST_F(StructuralEquivalenceFunctionTest, Variadic) {
307+
auto t = makeNamedDecls("void foo(int x...);",
308+
"void foo(int x);", Lang_CXX);
309+
EXPECT_FALSE(testStructuralMatch(t));
310+
}
311+
312+
TEST_F(StructuralEquivalenceFunctionTest, ParamPtr) {
313+
auto t = makeNamedDecls("void foo(int *);",
314+
"void foo(int);", Lang_CXX);
315+
EXPECT_FALSE(testStructuralMatch(t));
316+
}
317+
318+
TEST_F(StructuralEquivalenceFunctionTest, NameInParen) {
319+
auto t = makeNamedDecls(
320+
"void ((foo))();",
321+
"void foo();",
322+
Lang_CXX);
323+
EXPECT_TRUE(testStructuralMatch(t));
324+
}
325+
326+
TEST_F(StructuralEquivalenceFunctionTest, NameInParenWithExceptionSpec) {
327+
auto t = makeNamedDecls(
328+
"void (foo)() throw(int);",
329+
"void (foo)() noexcept;",
330+
Lang_CXX11);
331+
EXPECT_FALSE(testStructuralMatch(t));
332+
}
333+
334+
TEST_F(StructuralEquivalenceFunctionTest, NameInParenWithConst) {
335+
auto t = makeNamedDecls(
336+
"struct A { void (foo)() const; };",
337+
"struct A { void (foo)(); };",
338+
Lang_CXX11);
339+
EXPECT_FALSE(testStructuralMatch(t));
340+
}
341+
342+
struct StructuralEquivalenceCXXMethodTest : StructuralEquivalenceTest {
343+
};
344+
345+
TEST_F(StructuralEquivalenceCXXMethodTest, Virtual) {
346+
auto t = makeDecls<CXXMethodDecl>(
347+
"struct X { void foo(); };",
348+
"struct X { virtual void foo(); };", Lang_CXX,
349+
cxxMethodDecl(hasName("foo")));
350+
EXPECT_FALSE(testStructuralMatch(t));
351+
}
352+
353+
TEST_F(StructuralEquivalenceCXXMethodTest, Pure) {
354+
auto t = makeNamedDecls("struct X { virtual void foo(); };",
355+
"struct X { virtual void foo() = 0; };", Lang_CXX);
356+
EXPECT_FALSE(testStructuralMatch(t));
357+
}
358+
359+
TEST_F(StructuralEquivalenceCXXMethodTest, DISABLED_Final) {
360+
// The final-ness is not checked yet.
361+
auto t = makeNamedDecls("struct X { virtual void foo(); };",
362+
"struct X { virtual void foo() final; };", Lang_CXX);
363+
EXPECT_FALSE(testStructuralMatch(t));
364+
}
365+
366+
TEST_F(StructuralEquivalenceCXXMethodTest, Const) {
367+
auto t = makeNamedDecls("struct X { void foo(); };",
368+
"struct X { void foo() const; };", Lang_CXX);
369+
EXPECT_FALSE(testStructuralMatch(t));
370+
}
371+
372+
TEST_F(StructuralEquivalenceCXXMethodTest, Static) {
373+
auto t = makeNamedDecls("struct X { void foo(); };",
374+
"struct X { static void foo(); };", Lang_CXX);
375+
EXPECT_FALSE(testStructuralMatch(t));
376+
}
377+
378+
TEST_F(StructuralEquivalenceCXXMethodTest, Ref1) {
379+
auto t = makeNamedDecls("struct X { void foo(); };",
380+
"struct X { void foo() &&; };", Lang_CXX11);
381+
EXPECT_FALSE(testStructuralMatch(t));
382+
}
383+
384+
TEST_F(StructuralEquivalenceCXXMethodTest, Ref2) {
385+
auto t = makeNamedDecls("struct X { void foo() &; };",
386+
"struct X { void foo() &&; };", Lang_CXX11);
387+
EXPECT_FALSE(testStructuralMatch(t));
388+
}
389+
390+
TEST_F(StructuralEquivalenceCXXMethodTest, AccessSpecifier) {
391+
auto t = makeDecls<CXXMethodDecl>(
392+
"struct X { public: void foo(); };",
393+
"struct X { private: void foo(); };", Lang_CXX,
394+
cxxMethodDecl(hasName("foo")));
395+
EXPECT_FALSE(testStructuralMatch(t));
396+
}
397+
398+
TEST_F(StructuralEquivalenceCXXMethodTest, Delete) {
399+
auto t = makeNamedDecls("struct X { void foo(); };",
400+
"struct X { void foo() = delete; };", Lang_CXX11);
401+
EXPECT_FALSE(testStructuralMatch(t));
402+
}
403+
404+
TEST_F(StructuralEquivalenceCXXMethodTest, Constructor) {
405+
auto t = makeDecls<FunctionDecl>(
406+
"void foo();", "struct foo { foo(); };", Lang_CXX,
407+
functionDecl(), cxxConstructorDecl());
408+
EXPECT_FALSE(testStructuralMatch(t));
409+
}
410+
411+
TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorParam) {
412+
auto t = makeDecls<CXXConstructorDecl>("struct X { X(); };",
413+
"struct X { X(int); };", Lang_CXX,
414+
cxxConstructorDecl());
415+
EXPECT_FALSE(testStructuralMatch(t));
416+
}
417+
418+
TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorExplicit) {
419+
auto t = makeDecls<CXXConstructorDecl>("struct X { X(int); };",
420+
"struct X { explicit X(int); };",
421+
Lang_CXX11,
422+
cxxConstructorDecl());
423+
EXPECT_FALSE(testStructuralMatch(t));
424+
}
425+
426+
TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorDefault) {
427+
auto t = makeDecls<CXXConstructorDecl>("struct X { X(); };",
428+
"struct X { X() = default; };",
429+
Lang_CXX11,
430+
cxxConstructorDecl());
431+
EXPECT_FALSE(testStructuralMatch(t));
432+
}
433+
434+
TEST_F(StructuralEquivalenceCXXMethodTest, Conversion) {
435+
auto t = makeDecls<CXXConversionDecl>("struct X { operator bool(); };",
436+
"struct X { operator char(); };",
437+
Lang_CXX11,
438+
cxxConversionDecl());
439+
EXPECT_FALSE(testStructuralMatch(t));
440+
}
441+
442+
TEST_F(StructuralEquivalenceCXXMethodTest, Operator) {
443+
auto t = makeDecls<FunctionDecl>(
444+
"struct X { int operator +(int); };",
445+
"struct X { int operator -(int); };", Lang_CXX,
446+
functionDecl(hasOverloadedOperatorName("+")),
447+
functionDecl(hasOverloadedOperatorName("-")));
448+
EXPECT_FALSE(testStructuralMatch(t));
449+
}
450+
451+
TEST_F(StructuralEquivalenceCXXMethodTest, OutOfClass1) {
452+
auto t = makeDecls<FunctionDecl>(
453+
"struct X { virtual void f(); }; void X::f() { }",
454+
"struct X { virtual void f() { }; };",
455+
Lang_CXX,
456+
functionDecl(allOf(hasName("f"), isDefinition())));
457+
EXPECT_TRUE(testStructuralMatch(t));
458+
}
459+
460+
TEST_F(StructuralEquivalenceCXXMethodTest, OutOfClass2) {
461+
auto t = makeDecls<FunctionDecl>(
462+
"struct X { virtual void f(); }; void X::f() { }",
463+
"struct X { void f(); }; void X::f() { }",
464+
Lang_CXX,
465+
functionDecl(allOf(hasName("f"), isDefinition())));
466+
EXPECT_FALSE(testStructuralMatch(t));
467+
}
468+
469+
struct StructuralEquivalenceRecordTest : StructuralEquivalenceTest {
470+
};
471+
472+
TEST_F(StructuralEquivalenceRecordTest, Name) {
473+
auto t = makeDecls<CXXRecordDecl>(
474+
"struct A{ };",
475+
"struct B{ };",
476+
Lang_CXX,
477+
cxxRecordDecl());
478+
EXPECT_FALSE(testStructuralMatch(t));
479+
}
480+
481+
TEST_F(StructuralEquivalenceRecordTest, Fields) {
482+
auto t = makeNamedDecls(
483+
"struct foo{ int x; };",
484+
"struct foo{ char x; };",
485+
Lang_CXX);
486+
EXPECT_FALSE(testStructuralMatch(t));
487+
}
488+
489+
TEST_F(StructuralEquivalenceRecordTest, DISABLED_Methods) {
490+
// Currently, methods of a class are not checked at class equivalence.
491+
auto t = makeNamedDecls(
492+
"struct foo{ int x(); };",
493+
"struct foo{ char x(); };",
494+
Lang_CXX);
495+
EXPECT_FALSE(testStructuralMatch(t));
496+
}
497+
498+
TEST_F(StructuralEquivalenceRecordTest, Bases) {
499+
auto t = makeNamedDecls(
500+
"struct A{ }; struct foo: A { };",
501+
"struct B{ }; struct foo: B { };",
502+
Lang_CXX);
503+
EXPECT_FALSE(testStructuralMatch(t));
504+
}
505+
506+
TEST_F(StructuralEquivalenceRecordTest, InheritanceVirtual) {
507+
auto t = makeNamedDecls(
508+
"struct A{ }; struct foo: A { };",
509+
"struct A{ }; struct foo: virtual A { };",
510+
Lang_CXX);
511+
EXPECT_FALSE(testStructuralMatch(t));
512+
}
513+
514+
TEST_F(StructuralEquivalenceRecordTest, DISABLED_InheritanceType) {
515+
// Access specifier in inheritance is not checked yet.
516+
auto t = makeNamedDecls(
517+
"struct A{ }; struct foo: public A { };",
518+
"struct A{ }; struct foo: private A { };",
519+
Lang_CXX);
520+
EXPECT_FALSE(testStructuralMatch(t));
521+
}
522+
523+
TEST_F(StructuralEquivalenceRecordTest, Match) {
524+
auto Code = R"(
525+
struct A{ };
526+
struct B{ };
527+
struct foo: A, virtual B {
528+
void x();
529+
int a;
530+
};
531+
)";
532+
auto t = makeNamedDecls(Code, Code, Lang_CXX);
533+
EXPECT_TRUE(testStructuralMatch(t));
534+
}
535+
536+
TEST_F(StructuralEquivalenceTest, CompareSameDeclWithMultiple) {
537+
auto t = makeNamedDecls(
538+
"struct A{ }; struct B{ }; void foo(A a, A b);",
539+
"struct A{ }; struct B{ }; void foo(A a, B b);",
540+
Lang_CXX);
541+
EXPECT_FALSE(testStructuralMatch(t));
542+
}
543+
206544
} // end namespace ast_matchers
207545
} // end namespace clang

0 commit comments

Comments
 (0)
Please sign in to comment.