llvm-project/clang/unittests/AST/StructuralEquivalenceTest.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

2770 lines
92 KiB
C++

#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTStructuralEquivalence.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Testing/CommandLineArgs.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/TargetParser/Host.h"
#include "DeclMatcher.h"
#include "gtest/gtest.h"
namespace clang {
namespace ast_matchers {
using std::get;
struct StructuralEquivalenceTest : ::testing::Test {
std::unique_ptr<ASTUnit> AST0, AST1;
std::string Code0, Code1; // Buffers for SourceManager
// Parses the source code in the specified language and sets the ASTs of
// the current test instance to the parse result.
void makeASTUnits(const std::string &SrcCode0, const std::string &SrcCode1,
TestLanguage Lang) {
this->Code0 = SrcCode0;
this->Code1 = SrcCode1;
std::vector<std::string> Args = getCommandLineArgsForTesting(Lang);
const char *const InputFileName = "input.cc";
AST0 = tooling::buildASTFromCodeWithArgs(Code0, Args, InputFileName);
AST1 = tooling::buildASTFromCodeWithArgs(Code1, Args, InputFileName);
}
// Get a pair of node pointers into the synthesized AST from the given code
// snippets. To determine the returned node, a separate matcher is specified
// for both snippets. The first matching node is returned.
template <typename NodeType, typename MatcherType>
std::tuple<NodeType *, NodeType *>
makeDecls(const std::string &SrcCode0, const std::string &SrcCode1,
TestLanguage Lang, const MatcherType &Matcher0,
const MatcherType &Matcher1) {
makeASTUnits(SrcCode0, SrcCode1, Lang);
NodeType *D0 = FirstDeclMatcher<NodeType>().match(
AST0->getASTContext().getTranslationUnitDecl(), Matcher0);
NodeType *D1 = FirstDeclMatcher<NodeType>().match(
AST1->getASTContext().getTranslationUnitDecl(), Matcher1);
return std::make_tuple(D0, D1);
}
std::tuple<TranslationUnitDecl *, TranslationUnitDecl *>
makeTuDecls(const std::string &SrcCode0, const std::string &SrcCode1,
TestLanguage Lang) {
makeASTUnits(SrcCode0, SrcCode1, Lang);
return std::make_tuple(AST0->getASTContext().getTranslationUnitDecl(),
AST1->getASTContext().getTranslationUnitDecl());
}
// Get a pair of node pointers into the synthesized AST from the given code
// snippets. The same matcher is used for both snippets.
template <typename NodeType, typename MatcherType>
std::tuple<NodeType *, NodeType *>
makeDecls(const std::string &SrcCode0, const std::string &SrcCode1,
TestLanguage Lang, const MatcherType &AMatcher) {
return makeDecls<NodeType, MatcherType>(
SrcCode0, SrcCode1, Lang, AMatcher, AMatcher);
}
// Get a pair of Decl pointers to the synthesized declarations from the given
// code snippets. We search for the first NamedDecl with given name in both
// snippets.
std::tuple<NamedDecl *, NamedDecl *>
makeNamedDecls(const std::string &SrcCode0, const std::string &SrcCode1,
TestLanguage Lang, const char *const Identifier = "foo") {
auto Matcher = namedDecl(hasName(Identifier));
return makeDecls<NamedDecl>(SrcCode0, SrcCode1, Lang, Matcher);
}
// Wraps a Stmt and the ASTContext that contains it.
struct StmtWithASTContext {
Stmt *S;
ASTContext *Context;
explicit StmtWithASTContext(Stmt &S, ASTContext &Context)
: S(&S), Context(&Context) {}
explicit StmtWithASTContext(FunctionDecl *FD)
: S(FD->getBody()), Context(&FD->getASTContext()) {}
};
// Get a pair of node pointers into the synthesized AST from the given code
// snippets. To determine the returned node, a separate matcher is specified
// for both snippets. The first matching node is returned.
template <typename MatcherType>
std::tuple<StmtWithASTContext, StmtWithASTContext>
makeStmts(const std::string &SrcCode0, const std::string &SrcCode1,
TestLanguage Lang, const MatcherType &Matcher0,
const MatcherType &Matcher1) {
makeASTUnits(SrcCode0, SrcCode1, Lang);
Stmt *S0 = FirstDeclMatcher<Stmt>().match(
AST0->getASTContext().getTranslationUnitDecl(), Matcher0);
Stmt *S1 = FirstDeclMatcher<Stmt>().match(
AST1->getASTContext().getTranslationUnitDecl(), Matcher1);
return std::make_tuple(StmtWithASTContext(*S0, AST0->getASTContext()),
StmtWithASTContext(*S1, AST1->getASTContext()));
}
// Get a pair of node pointers into the synthesized AST from the given code
// snippets. The same matcher is used for both snippets.
template <typename MatcherType>
std::tuple<StmtWithASTContext, StmtWithASTContext>
makeStmts(const std::string &SrcCode0, const std::string &SrcCode1,
TestLanguage Lang, const MatcherType &AMatcher) {
return makeStmts(SrcCode0, SrcCode1, Lang, AMatcher, AMatcher);
}
// Convenience function for makeStmts that wraps the code inside a function
// body.
template <typename MatcherType>
std::tuple<StmtWithASTContext, StmtWithASTContext>
makeWrappedStmts(const std::string &SrcCode0, const std::string &SrcCode1,
TestLanguage Lang, const MatcherType &AMatcher) {
auto Wrap = [](const std::string &Src) {
return "void wrapped() {" + Src + ";}";
};
return makeStmts(Wrap(SrcCode0), Wrap(SrcCode1), Lang, AMatcher);
}
bool testStructuralMatch(Decl *D0, Decl *D1,
bool IgnoreTemplateParmDepth = false) {
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls01;
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls10;
StructuralEquivalenceContext Ctx01(
D0->getLangOpts(), D0->getASTContext(), D1->getASTContext(),
NonEquivalentDecls01, StructuralEquivalenceKind::Default,
/*StrictTypeSpelling=*/false,
/*Complain=*/false, /*ErrorOnTagTypeMismatch=*/false,
IgnoreTemplateParmDepth);
StructuralEquivalenceContext Ctx10(
D1->getLangOpts(), D1->getASTContext(), D0->getASTContext(),
NonEquivalentDecls10, StructuralEquivalenceKind::Default,
/*StrictTypeSpelling=*/false,
/*Complain=*/false, /*ErrorOnTagTypeMismatch=*/false,
IgnoreTemplateParmDepth);
bool Eq01 = Ctx01.IsEquivalent(D0, D1);
bool Eq10 = Ctx10.IsEquivalent(D1, D0);
EXPECT_EQ(Eq01, Eq10);
return Eq01;
}
bool testStructuralMatch(StmtWithASTContext S0, StmtWithASTContext S1) {
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls01;
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls10;
StructuralEquivalenceContext Ctx01(S0.Context->getLangOpts(), *S0.Context,
*S1.Context, NonEquivalentDecls01,
StructuralEquivalenceKind::Default,
/*StrictTypeSpelling=*/false,
/*Complain=*/false);
StructuralEquivalenceContext Ctx10(S1.Context->getLangOpts(), *S1.Context,
*S0.Context, NonEquivalentDecls10,
StructuralEquivalenceKind::Default,
/*StrictTypeSpelling=*/false,
/*Complain=*/false);
bool Eq01 = Ctx01.IsEquivalent(S0.S, S1.S);
bool Eq10 = Ctx10.IsEquivalent(S1.S, S0.S);
EXPECT_EQ(Eq01, Eq10);
return Eq01;
}
bool
testStructuralMatch(std::tuple<StmtWithASTContext, StmtWithASTContext> t) {
return testStructuralMatch(get<0>(t), get<1>(t));
}
bool testStructuralMatch(std::tuple<Decl *, Decl *> t,
bool IgnoreTemplateParmDepth = false) {
return testStructuralMatch(get<0>(t), get<1>(t), IgnoreTemplateParmDepth);
}
};
TEST_F(StructuralEquivalenceTest, Int) {
auto Decls = makeNamedDecls("int foo;", "int foo;", Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceTest, IntVsSignedInt) {
auto Decls = makeNamedDecls("int foo;", "signed int foo;", Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceTest, Char) {
auto Decls = makeNamedDecls("char foo;", "char foo;", Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(Decls));
}
// This test is disabled for now.
// FIXME Whether this is equivalent is dependent on the target.
TEST_F(StructuralEquivalenceTest, DISABLED_CharVsSignedChar) {
auto Decls = makeNamedDecls("char foo;", "signed char foo;", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceTest, ForwardRecordDecl) {
auto Decls = makeNamedDecls("struct foo;", "struct foo;", Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceTest, IntVsSignedIntInStruct) {
auto Decls = makeNamedDecls("struct foo { int x; };",
"struct foo { signed int x; };", Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceTest, CharVsSignedCharInStruct) {
auto Decls = makeNamedDecls("struct foo { char x; };",
"struct foo { signed char x; };", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceTest, IntVsSignedIntTemplateSpec) {
auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
R"(template <class T> struct foo; template<> struct foo<int>{};)",
R"(template <class T> struct foo; template<> struct foo<signed int>{};)",
Lang_CXX03, classTemplateSpecializationDecl());
auto Spec0 = get<0>(Decls);
auto Spec1 = get<1>(Decls);
EXPECT_TRUE(testStructuralMatch(Spec0, Spec1));
}
TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpec) {
auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
R"(template <class T> struct foo; template<> struct foo<char>{};)",
R"(template <class T> struct foo; template<> struct foo<signed char>{};)",
Lang_CXX03, classTemplateSpecializationDecl());
auto Spec0 = get<0>(Decls);
auto Spec1 = get<1>(Decls);
EXPECT_FALSE(testStructuralMatch(Spec0, Spec1));
}
TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpecWithInheritance) {
auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
R"(
struct true_type{};
template <class T> struct foo;
template<> struct foo<char> : true_type {};
)",
R"(
struct true_type{};
template <class T> struct foo;
template<> struct foo<signed char> : true_type {};
)",
Lang_CXX03, classTemplateSpecializationDecl());
EXPECT_FALSE(testStructuralMatch(Decls));
}
// This test is disabled for now.
// FIXME Enable it, once the check is implemented.
TEST_F(StructuralEquivalenceTest, DISABLED_WrongOrderInNamespace) {
auto Code =
R"(
namespace NS {
template <class T> class Base {
int a;
};
class Derived : Base<Derived> {
};
}
void foo(NS::Derived &);
)";
auto Decls = makeNamedDecls(Code, Code, Lang_CXX03);
NamespaceDecl *NS =
LastDeclMatcher<NamespaceDecl>().match(get<1>(Decls), namespaceDecl());
ClassTemplateDecl *TD = LastDeclMatcher<ClassTemplateDecl>().match(
get<1>(Decls), classTemplateDecl(hasName("Base")));
// Reorder the decls, move the TD to the last place in the DC.
NS->removeDecl(TD);
NS->addDeclInternal(TD);
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceTest, WrongOrderOfFieldsInClass) {
auto Code = "class X { int a; int b; };";
auto Decls = makeNamedDecls(Code, Code, Lang_CXX03, "X");
CXXRecordDecl *RD = FirstDeclMatcher<CXXRecordDecl>().match(
get<1>(Decls), cxxRecordDecl(hasName("X")));
FieldDecl *FD =
FirstDeclMatcher<FieldDecl>().match(get<1>(Decls), fieldDecl(hasName("a")));
// Reorder the FieldDecls
RD->removeDecl(FD);
RD->addDeclInternal(FD);
EXPECT_FALSE(testStructuralMatch(Decls));
}
struct StructuralEquivalenceFunctionTest : StructuralEquivalenceTest {
};
TEST_F(StructuralEquivalenceFunctionTest, TemplateVsNonTemplate) {
auto t = makeNamedDecls("void foo();", "template<class T> void foo();",
Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, DifferentOperators) {
auto t = makeDecls<FunctionDecl>(
"struct X{}; bool operator<(X, X);", "struct X{}; bool operator==(X, X);",
Lang_CXX03, functionDecl(hasOverloadedOperatorName("<")),
functionDecl(hasOverloadedOperatorName("==")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, SameOperators) {
auto t = makeDecls<FunctionDecl>(
"struct X{}; bool operator<(X, X);", "struct X{}; bool operator<(X, X);",
Lang_CXX03, functionDecl(hasOverloadedOperatorName("<")),
functionDecl(hasOverloadedOperatorName("<")));
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, CtorVsDtor) {
auto t = makeDecls<FunctionDecl>("struct X{ X(); };", "struct X{ ~X(); };",
Lang_CXX03, cxxConstructorDecl(),
cxxDestructorDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ParamConstWithRef) {
auto t =
makeNamedDecls("void foo(int&);", "void foo(const int&);", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ParamConstSimple) {
auto t = makeNamedDecls("void foo(int);", "void foo(const int);", Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(t));
// consider this OK
}
TEST_F(StructuralEquivalenceFunctionTest, Throw) {
auto t = makeNamedDecls("void foo();", "void foo() throw();", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, Noexcept) {
auto t = makeNamedDecls("void foo();",
"void foo() noexcept;", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexcept) {
auto t = makeNamedDecls("void foo() throw();",
"void foo() noexcept;", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexceptFalse) {
auto t = makeNamedDecls("void foo() throw();",
"void foo() noexcept(false);", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexceptTrue) {
auto t = makeNamedDecls("void foo() throw();",
"void foo() noexcept(true);", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, NoexceptNonMatch) {
auto t = makeNamedDecls("void foo() noexcept(false);",
"void foo() noexcept(true);", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, NoexceptMatch) {
auto t = makeNamedDecls("void foo() noexcept(false);",
"void foo() noexcept(false);", Lang_CXX11);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, NoexceptVsNoexceptFalse) {
auto t = makeNamedDecls("void foo() noexcept;",
"void foo() noexcept(false);", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, NoexceptVsNoexceptTrue) {
auto t = makeNamedDecls("void foo() noexcept;",
"void foo() noexcept(true);", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ReturnType) {
auto t = makeNamedDecls("char foo();", "int foo();", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ReturnConst) {
auto t = makeNamedDecls("char foo();", "const char foo();", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ReturnRef) {
auto t = makeNamedDecls("char &foo();",
"char &&foo();", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ParamCount) {
auto t = makeNamedDecls("void foo(int);", "void foo(int, int);", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ParamType) {
auto t = makeNamedDecls("void foo(int);", "void foo(char);", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ParamName) {
auto t = makeNamedDecls("void foo(int a);", "void foo(int b);", Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, Variadic) {
auto t =
makeNamedDecls("void foo(int x...);", "void foo(int x);", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, ParamPtr) {
auto t = makeNamedDecls("void foo(int *);", "void foo(int);", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, NameInParen) {
auto t = makeNamedDecls("void ((foo))();", "void foo();", Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, NameInParenWithExceptionSpec) {
auto t = makeNamedDecls(
"void (foo)() throw(int);",
"void (foo)() noexcept;",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, NameInParenWithConst) {
auto t = makeNamedDecls(
"struct A { void (foo)() const; };",
"struct A { void (foo)(); };",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, FunctionsWithDifferentNoreturnAttr) {
auto t = makeNamedDecls("__attribute__((noreturn)) void foo();",
" void foo();", Lang_C99);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest,
FunctionsWithDifferentCallingConventions) {
// These attributes may not be available on certain platforms.
if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getArch() !=
llvm::Triple::x86_64)
GTEST_SKIP();
auto t = makeNamedDecls("__attribute__((preserve_all)) void foo();",
"__attribute__((ms_abi)) void foo();", Lang_C99);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceFunctionTest, FunctionsWithDifferentSavedRegsAttr) {
if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getArch() !=
llvm::Triple::x86_64)
GTEST_SKIP();
auto t = makeNamedDecls(
"__attribute__((no_caller_saved_registers)) void foo();",
" void foo();", Lang_C99);
EXPECT_FALSE(testStructuralMatch(t));
}
struct StructuralEquivalenceCXXMethodTest : StructuralEquivalenceTest {
};
TEST_F(StructuralEquivalenceCXXMethodTest, Virtual) {
auto t = makeDecls<CXXMethodDecl>("struct X { void foo(); };",
"struct X { virtual void foo(); };",
Lang_CXX03, cxxMethodDecl(hasName("foo")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, Pure) {
auto t = makeNamedDecls("struct X { virtual void foo(); };",
"struct X { virtual void foo() = 0; };", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, DISABLED_Final) {
// The final-ness is not checked yet.
auto t =
makeNamedDecls("struct X { virtual void foo(); };",
"struct X { virtual void foo() final; };", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, Const) {
auto t = makeNamedDecls("struct X { void foo(); };",
"struct X { void foo() const; };", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, Static) {
auto t = makeNamedDecls("struct X { void foo(); };",
"struct X { static void foo(); };", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, Ref1) {
auto t = makeNamedDecls("struct X { void foo(); };",
"struct X { void foo() &&; };", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, Ref2) {
auto t = makeNamedDecls("struct X { void foo() &; };",
"struct X { void foo() &&; };", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, AccessSpecifier) {
auto t = makeDecls<CXXMethodDecl>("struct X { public: void foo(); };",
"struct X { private: void foo(); };",
Lang_CXX03, cxxMethodDecl(hasName("foo")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, Delete) {
auto t = makeNamedDecls("struct X { void foo(); };",
"struct X { void foo() = delete; };", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, Constructor) {
auto t = makeDecls<FunctionDecl>("void foo();", "struct foo { foo(); };",
Lang_CXX03, functionDecl(hasName("foo")),
cxxConstructorDecl(hasName("foo")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorParam) {
auto t = makeDecls<CXXConstructorDecl>("struct X { X(); };",
"struct X { X(int); };", Lang_CXX03,
cxxConstructorDecl(hasName("X")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorExplicit) {
auto t = makeDecls<CXXConstructorDecl>("struct X { X(int); };",
"struct X { explicit X(int); };",
Lang_CXX11,
cxxConstructorDecl(hasName("X")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorDefault) {
auto t = makeDecls<CXXConstructorDecl>("struct X { X(); };",
"struct X { X() = default; };",
Lang_CXX11,
cxxConstructorDecl(hasName("X")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, Conversion) {
auto t = makeDecls<CXXConversionDecl>("struct X { operator bool(); };",
"struct X { operator char(); };",
Lang_CXX11,
cxxConversionDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, Operator) {
auto t =
makeDecls<FunctionDecl>("struct X { int operator +(int); };",
"struct X { int operator -(int); };", Lang_CXX03,
functionDecl(hasOverloadedOperatorName("+")),
functionDecl(hasOverloadedOperatorName("-")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, OutOfClass1) {
auto t = makeDecls<FunctionDecl>(
"struct X { virtual void f(); }; void X::f() { }",
"struct X { virtual void f() { }; };", Lang_CXX03,
functionDecl(allOf(hasName("f"), isDefinition())));
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCXXMethodTest, OutOfClass2) {
auto t = makeDecls<FunctionDecl>(
"struct X { virtual void f(); }; void X::f() { }",
"struct X { void f(); }; void X::f() { }", Lang_CXX03,
functionDecl(allOf(hasName("f"), isDefinition())));
EXPECT_FALSE(testStructuralMatch(t));
}
struct StructuralEquivalenceRecordTest : StructuralEquivalenceTest {
// FIXME Use a common getRecordDecl with ASTImporterTest.cpp!
RecordDecl *getRecordDecl(FieldDecl *FD) {
return FD->getType()->getAsRecordDecl();
};
};
TEST_F(StructuralEquivalenceRecordTest, Name) {
auto t = makeDecls<CXXRecordDecl>("struct A{ };", "struct B{ };", Lang_CXX03,
cxxRecordDecl(hasName("A")),
cxxRecordDecl(hasName("B")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, Fields) {
auto t = makeNamedDecls("struct foo{ int x; };", "struct foo{ char x; };",
Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, DISABLED_Methods) {
// Currently, methods of a class are not checked at class equivalence.
auto t = makeNamedDecls("struct foo{ int x(); };", "struct foo{ char x(); };",
Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, Bases) {
auto t = makeNamedDecls("struct A{ }; struct foo: A { };",
"struct B{ }; struct foo: B { };", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, InheritanceVirtual) {
auto t =
makeNamedDecls("struct A{ }; struct foo: A { };",
"struct A{ }; struct foo: virtual A { };", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, DISABLED_InheritanceType) {
// Access specifier in inheritance is not checked yet.
auto t =
makeNamedDecls("struct A{ }; struct foo: public A { };",
"struct A{ }; struct foo: private A { };", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, Match) {
auto Code = R"(
struct A{ };
struct B{ };
struct foo: A, virtual B {
void x();
int a;
};
)";
auto t = makeNamedDecls(Code, Code, Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, UnnamedRecordsShouldBeInequivalent) {
auto t = makeTuDecls(
R"(
struct A {
struct {
struct A *next;
} entry0;
struct {
struct A *next;
} entry1;
};
)",
"", Lang_C99);
auto *TU = get<0>(t);
auto *Entry0 =
FirstDeclMatcher<FieldDecl>().match(TU, fieldDecl(hasName("entry0")));
auto *Entry1 =
FirstDeclMatcher<FieldDecl>().match(TU, fieldDecl(hasName("entry1")));
auto *R0 = getRecordDecl(Entry0);
auto *R1 = getRecordDecl(Entry1);
ASSERT_NE(R0, R1);
EXPECT_TRUE(testStructuralMatch(R0, R0));
EXPECT_TRUE(testStructuralMatch(R1, R1));
EXPECT_FALSE(testStructuralMatch(R0, R1));
}
TEST_F(StructuralEquivalenceRecordTest, AnonymousRecordsShouldBeInequivalent) {
auto t = makeTuDecls(
R"(
struct X {
struct {
int a;
};
struct {
int b;
};
};
)",
"", Lang_C99);
auto *TU = get<0>(t);
auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
TU, indirectFieldDecl(hasName("a")));
auto *FA = cast<FieldDecl>(A->chain().front());
RecordDecl *RA =
cast<RecordType>(FA->getType().getTypePtr())->getOriginalDecl();
auto *B = FirstDeclMatcher<IndirectFieldDecl>().match(
TU, indirectFieldDecl(hasName("b")));
auto *FB = cast<FieldDecl>(B->chain().front());
RecordDecl *RB =
cast<RecordType>(FB->getType().getTypePtr())->getOriginalDecl();
ASSERT_NE(RA, RB);
EXPECT_TRUE(testStructuralMatch(RA, RA));
EXPECT_TRUE(testStructuralMatch(RB, RB));
EXPECT_FALSE(testStructuralMatch(RA, RB));
}
TEST_F(StructuralEquivalenceRecordTest,
RecordsAreInequivalentIfOrderOfAnonRecordsIsDifferent) {
auto t = makeTuDecls(
R"(
struct X {
struct { int a; };
struct { int b; };
};
)",
R"(
struct X { // The order is reversed.
struct { int b; };
struct { int a; };
};
)",
Lang_C99);
auto *TU = get<0>(t);
auto *A = FirstDeclMatcher<IndirectFieldDecl>().match(
TU, indirectFieldDecl(hasName("a")));
auto *FA = cast<FieldDecl>(A->chain().front());
RecordDecl *RA =
cast<RecordType>(FA->getType().getTypePtr())->getOriginalDecl();
auto *TU1 = get<1>(t);
auto *A1 = FirstDeclMatcher<IndirectFieldDecl>().match(
TU1, indirectFieldDecl(hasName("a")));
auto *FA1 = cast<FieldDecl>(A1->chain().front());
RecordDecl *RA1 =
cast<RecordType>(FA1->getType().getTypePtr())->getOriginalDecl();
RecordDecl *X =
FirstDeclMatcher<RecordDecl>().match(TU, recordDecl(hasName("X")));
RecordDecl *X1 =
FirstDeclMatcher<RecordDecl>().match(TU1, recordDecl(hasName("X")));
ASSERT_NE(X, X1);
EXPECT_FALSE(testStructuralMatch(X, X1));
ASSERT_NE(RA, RA1);
EXPECT_TRUE(testStructuralMatch(RA, RA));
EXPECT_TRUE(testStructuralMatch(RA1, RA1));
EXPECT_FALSE(testStructuralMatch(RA1, RA));
}
TEST_F(StructuralEquivalenceRecordTest,
UnnamedRecordsShouldBeInequivalentEvenIfTheSecondIsBeingDefined) {
auto Code =
R"(
struct A {
struct {
struct A *next;
} entry0;
struct {
struct A *next;
} entry1;
};
)";
auto t = makeTuDecls(Code, Code, Lang_C99);
auto *FromTU = get<0>(t);
auto *Entry1 =
FirstDeclMatcher<FieldDecl>().match(FromTU, fieldDecl(hasName("entry1")));
auto *ToTU = get<1>(t);
auto *Entry0 =
FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry0")));
auto *A =
FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
A->startDefinition(); // Set isBeingDefined, getDefinition() will return a
// nullptr. This may be the case during ASTImport.
auto *R0 = getRecordDecl(Entry0);
auto *R1 = getRecordDecl(Entry1);
ASSERT_NE(R0, R1);
EXPECT_TRUE(testStructuralMatch(R0, R0));
EXPECT_TRUE(testStructuralMatch(R1, R1));
EXPECT_FALSE(testStructuralMatch(R0, R1));
}
TEST_F(StructuralEquivalenceRecordTest, TemplateVsNonTemplate) {
auto t = makeDecls<CXXRecordDecl>("struct A { };",
"template<class T> struct A { };",
Lang_CXX03, cxxRecordDecl(hasName("A")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest,
FwdDeclRecordShouldBeEqualWithFwdDeclRecord) {
auto t = makeNamedDecls("class foo;", "class foo;", Lang_CXX11);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest,
FwdDeclRecordShouldBeEqualWithRecordWhichHasDefinition) {
auto t =
makeNamedDecls("class foo;", "class foo { int A; };", Lang_CXX11);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest,
RecordShouldBeEqualWithRecordWhichHasDefinition) {
auto t = makeNamedDecls("class foo { int A; };", "class foo { int A; };",
Lang_CXX11);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, RecordsWithDifferentBody) {
auto t = makeNamedDecls("class foo { int B; };", "class foo { int A; };",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, SameFriendMultipleTimes) {
auto t = makeNamedDecls("struct foo { friend class X; };",
"struct foo { friend class X; friend class X; };",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, SameFriendsDifferentOrder) {
auto t = makeNamedDecls("struct foo { friend class X; friend class Y; };",
"struct foo { friend class Y; friend class X; };",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceRecordTest, SameFriendsSameOrder) {
auto t = makeNamedDecls("struct foo { friend class X; friend class Y; };",
"struct foo { friend class X; friend class Y; };",
Lang_CXX11);
EXPECT_TRUE(testStructuralMatch(t));
}
struct StructuralEquivalenceLambdaTest : StructuralEquivalenceTest {};
TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithDifferentMethods) {
// Get the LambdaExprs, unfortunately we can't match directly the underlying
// implicit CXXRecordDecl of the Lambda classes.
auto t = makeDecls<LambdaExpr>(
"void f() { auto L0 = [](int){}; }",
"void f() { auto L1 = [](){}; }",
Lang_CXX11,
lambdaExpr(),
lambdaExpr());
CXXRecordDecl *L0 = get<0>(t)->getLambdaClass();
CXXRecordDecl *L1 = get<1>(t)->getLambdaClass();
EXPECT_FALSE(testStructuralMatch(L0, L1));
}
TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithEqMethods) {
auto t = makeDecls<LambdaExpr>(
"void f() { auto L0 = [](int){}; }",
"void f() { auto L1 = [](int){}; }",
Lang_CXX11,
lambdaExpr(),
lambdaExpr());
CXXRecordDecl *L0 = get<0>(t)->getLambdaClass();
CXXRecordDecl *L1 = get<1>(t)->getLambdaClass();
EXPECT_TRUE(testStructuralMatch(L0, L1));
}
TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithDifferentFields) {
auto t = makeDecls<LambdaExpr>(
"void f() { char* X; auto L0 = [X](){}; }",
"void f() { float X; auto L1 = [X](){}; }",
Lang_CXX11,
lambdaExpr(),
lambdaExpr());
CXXRecordDecl *L0 = get<0>(t)->getLambdaClass();
CXXRecordDecl *L1 = get<1>(t)->getLambdaClass();
EXPECT_FALSE(testStructuralMatch(L0, L1));
}
TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithEqFields) {
auto t = makeDecls<LambdaExpr>(
"void f() { float X; auto L0 = [X](){}; }",
"void f() { float X; auto L1 = [X](){}; }",
Lang_CXX11,
lambdaExpr(),
lambdaExpr());
CXXRecordDecl *L0 = get<0>(t)->getLambdaClass();
CXXRecordDecl *L1 = get<1>(t)->getLambdaClass();
EXPECT_TRUE(testStructuralMatch(L0, L1));
}
TEST_F(StructuralEquivalenceTest, CompareSameDeclWithMultiple) {
auto t = makeNamedDecls("struct A{ }; struct B{ }; void foo(A a, A b);",
"struct A{ }; struct B{ }; void foo(A a, B b);",
Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTest, ExplicitBoolDifferent) {
auto Decls = makeNamedDecls("struct foo {explicit(false) foo(int);};",
"struct foo {explicit(true) foo(int);};", Lang_CXX20);
CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match(
get<0>(Decls), cxxConstructorDecl(hasName("foo")));
CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match(
get<1>(Decls), cxxConstructorDecl(hasName("foo")));
EXPECT_FALSE(testStructuralMatch(First, Second));
}
TEST_F(StructuralEquivalenceTest, ExplicitBoolSame) {
auto Decls = makeNamedDecls("struct foo {explicit(true) foo(int);};",
"struct foo {explicit(true) foo(int);};", Lang_CXX20);
CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match(
get<0>(Decls), cxxConstructorDecl(hasName("foo")));
CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match(
get<1>(Decls), cxxConstructorDecl(hasName("foo")));
EXPECT_TRUE(testStructuralMatch(First, Second));
}
struct StructuralEquivalenceRecordContextTest : StructuralEquivalenceTest {};
TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNoVsNamed) {
auto Decls =
makeNamedDecls("class X;", "namespace N { class X; }", Lang_CXX03, "X");
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNamedVsNamed) {
auto Decls = makeNamedDecls("namespace A { class X; }",
"namespace B { class X; }", Lang_CXX03, "X");
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, NamespaceAnonVsNamed) {
auto Decls = makeNamedDecls("namespace { class X; }",
"namespace N { class X; }", Lang_CXX03, "X");
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNoVsAnon) {
auto Decls =
makeNamedDecls("class X;", "namespace { class X; }", Lang_CXX03, "X");
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, NamespaceAnonVsAnon) {
auto Decls = makeNamedDecls("namespace { class X; }",
"namespace { class X; }", Lang_CXX03, "X");
EXPECT_TRUE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, NamespaceAnonVsAnonAnon) {
auto Decls =
makeNamedDecls("namespace { class X; }",
"namespace { namespace { class X; } }", Lang_CXX03, "X");
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest,
NamespaceNamedNamedVsNamedNamed) {
auto Decls = makeNamedDecls("namespace A { namespace N { class X; } }",
"namespace B { namespace N { class X; } }",
Lang_CXX03, "X");
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, NamespaceNamedVsInline) {
auto Decls = makeNamedDecls("namespace A { namespace A { class X; } }",
"namespace A { inline namespace A { class X; } }",
Lang_CXX17, "X");
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, NamespaceInlineVsInline) {
auto Decls = makeNamedDecls("namespace A { inline namespace A { class X; } }",
"namespace A { inline namespace B { class X; } }",
Lang_CXX17, "X");
EXPECT_TRUE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, NamespaceInlineTopLevel) {
auto Decls =
makeNamedDecls("inline namespace A { class X; }",
"inline namespace B { class X; }", Lang_CXX17, "X");
EXPECT_TRUE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, TransparentContext) {
auto Decls =
makeNamedDecls("extern \"C\" { class X; }", "class X;", Lang_CXX03, "X");
EXPECT_TRUE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, TransparentContextNE) {
auto Decls = makeNamedDecls("extern \"C\" { class X; }",
"namespace { class X; }", Lang_CXX03, "X");
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, TransparentContextInNamespace) {
auto Decls = makeNamedDecls("extern \"C\" { namespace N { class X; } }",
"namespace N { extern \"C\" { class X; } }",
Lang_CXX03, "X");
EXPECT_TRUE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest,
ClassTemplateSpecializationContext) {
std::string Code =
R"(
template <typename T> struct O {
struct M {};
};
)";
auto t = makeDecls<VarDecl>(Code + R"(
typedef O<int>::M MT1;
MT1 A;
)",
Code + R"(
namespace {
struct I {};
} // namespace
typedef O<I>::M MT2;
MT2 A;
)",
Lang_CXX11, varDecl(hasName("A")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTest, NamespaceOfRecordMember) {
auto Decls = makeNamedDecls(
R"(
class X;
class Y { X* x; };
)",
R"(
namespace N { class X; }
class Y { N::X* x; };
)",
Lang_CXX03, "Y");
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceTest, StructDefinitionInPrototype) {
auto Decls =
makeNamedDecls("struct Param { int a; }; void foo(struct Param *p);",
"void foo(struct Param { int a; } *p);", Lang_C89);
EXPECT_TRUE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceTest, StructDefinitionInPrototypeDifferentName) {
auto Decls =
makeNamedDecls("struct Param1 { int a; }; void foo(struct Param1 *p);",
"void foo(struct Param2 { int a; } *p);", Lang_C89);
EXPECT_FALSE(testStructuralMatch(Decls));
}
TEST_F(StructuralEquivalenceRecordContextTest, RecordInsideFunction) {
auto Decls = makeNamedDecls("struct Param { int a; };",
"void f() { struct Param { int a; }; }", Lang_C89,
"Param");
EXPECT_TRUE(testStructuralMatch(Decls));
}
struct StructuralEquivalenceEnumTest : StructuralEquivalenceTest {};
TEST_F(StructuralEquivalenceEnumTest, FwdDeclEnumShouldBeEqualWithFwdDeclEnum) {
auto t = makeNamedDecls("enum class foo;", "enum class foo;", Lang_CXX11);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceEnumTest,
FwdDeclEnumShouldBeEqualWithEnumWhichHasDefinition) {
auto t =
makeNamedDecls("enum class foo;", "enum class foo { A };", Lang_CXX11);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceEnumTest,
EnumShouldBeEqualWithEnumWhichHasDefinition) {
auto t = makeNamedDecls("enum class foo { A };", "enum class foo { A };",
Lang_CXX11);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceEnumTest, EnumsWithDifferentBody) {
auto t = makeNamedDecls("enum class foo { B };", "enum class foo { A };",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceEnumTest, AnonymousEnumsWithSameConsts) {
// field x is required to trigger comparison of the anonymous enum
auto t = makeNamedDecls("struct foo { enum { A } x; };",
"struct foo { enum { A } x;};", Lang_CXX11);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceEnumTest, AnonymousEnumsWithDiffConsts) {
// field x is required to trigger comparison of the anonymous enum
auto t = makeNamedDecls("struct foo { enum { A } x; };",
"struct foo { enum { B } x;};", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
struct StructuralEquivalenceEnumConstantTest : StructuralEquivalenceTest {};
TEST_F(StructuralEquivalenceEnumConstantTest, EnumConstantsWithSameValues) {
auto t = makeNamedDecls("enum foo { foo = 1 };", "enum foo { foo = 1 };",
Lang_C89);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceEnumConstantTest,
EnumConstantsWithDifferentValues) {
auto t =
makeNamedDecls("enum e { foo = 1 };", "enum e { foo = 2 };", Lang_C89);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceEnumConstantTest,
EnumConstantsWithDifferentExprsButSameValues) {
auto t = makeNamedDecls("enum e { foo = 1 + 1 };", "enum e { foo = 2 };",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceEnumConstantTest,
EnumConstantsWithDifferentSignedness) {
auto t = makeNamedDecls("enum e : unsigned { foo = 1 };",
"enum e : int { foo = 1 };", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceEnumConstantTest, EnumConstantsWithDifferentWidth) {
auto t = makeNamedDecls("enum e : short { foo = 1 };",
"enum e : int { foo = 1 };", Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceEnumConstantTest, EnumConstantsWithDifferentName) {
auto t =
makeDecls<EnumConstantDecl>("enum e { foo = 1 };", "enum e { bar = 1 };",
Lang_CXX11, enumConstantDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
struct StructuralEquivalenceObjCCategoryTest : StructuralEquivalenceTest {};
TEST_F(StructuralEquivalenceObjCCategoryTest, MatchinCategoryNames) {
auto t = makeDecls<ObjCCategoryDecl>("@interface A @end @interface A(X) @end",
"@interface A @end @interface A(X) @end",
Lang_OBJC, objcCategoryDecl());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, CategoriesForDifferentClasses) {
auto t = makeDecls<ObjCCategoryDecl>("@interface A @end @interface A(X) @end",
"@interface B @end @interface B(X) @end",
Lang_OBJC, objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, CategoriesWithDifferentNames) {
auto t = makeDecls<ObjCCategoryDecl>("@interface A @end @interface A(X) @end",
"@interface A @end @interface A(Y) @end",
Lang_OBJC, objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, CategoriesWithoutInterfaces) {
auto t = makeDecls<ObjCCategoryDecl>(" @interface A(X) @end",
"@interface A @end @interface A(X) @end",
Lang_OBJC, objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
auto t2 = makeDecls<ObjCCategoryDecl>("@interface A(X) @end",
"@interface A(X) @end",
Lang_OBJC, objcCategoryDecl());
EXPECT_TRUE(testStructuralMatch(t2));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, CategoryAndExtension) {
auto t = makeDecls<ObjCCategoryDecl>("@interface A @end @interface A(X) @end",
"@interface A @end @interface A() @end",
Lang_OBJC, objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, MatchingProtocols) {
auto t = makeDecls<ObjCCategoryDecl>(
"@protocol P @end @interface A @end @interface A(X)<P> @end",
"@protocol P @end @interface A @end @interface A(X)<P> @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentProtocols) {
auto t = makeDecls<ObjCCategoryDecl>(
"@protocol P @end @interface A @end @interface A(X)<P> @end",
"@protocol Q @end @interface A @end @interface A(X)<Q> @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentProtocolsOrder) {
auto t = makeDecls<ObjCCategoryDecl>(
"@protocol P @end @protocol Q @end @interface A @end @interface A(X)<P, "
"Q> @end",
"@protocol P @end @protocol Q @end @interface A @end @interface A(X)<Q, "
"P> @end",
Lang_OBJC, objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, MatchingIvars) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A() { int x; } @end",
"@interface A @end @interface A() { int x; } @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarName) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A() { int x; } @end",
"@interface A @end @interface A() { int y; } @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarType) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A() { int x; } @end",
"@interface A @end @interface A() { float x; } @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarBitfieldWidth) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A() { int x: 1; } @end",
"@interface A @end @interface A() { int x: 2; } @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarVisibility) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A() { @public int x; } @end",
"@interface A @end @interface A() { @protected int x; } @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarNumber) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A() { int x; } @end",
"@interface A @end @interface A() {} @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentIvarOrder) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A() { int x; int y; } @end",
"@interface A @end @interface A() { int y; int x; } @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, MatchingMethods) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A(X) -(void)test; @end",
"@interface A @end @interface A(X) -(void)test; @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodName) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A(X) -(void)test; @end",
"@interface A @end @interface A(X) -(void)wasd; @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
auto t2 = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A(X) -(void)test:(int)x more:(int)y; @end",
"@interface A @end @interface A(X) -(void)test:(int)x :(int)y; @end",
Lang_OBJC, objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t2));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodClassInstance) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A(X) -(void)test; @end",
"@interface A @end @interface A(X) +(void)test; @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodReturnType) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A(X) -(void)test; @end",
"@interface A @end @interface A(X) -(int)test; @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodParameterType) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A(X) -(void)test:(int)x; @end",
"@interface A @end @interface A(X) -(void)test:(float)x; @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodParameterName) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A(X) -(void)test:(int)x; @end",
"@interface A @end @interface A(X) -(void)test:(int)y; @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodNumber) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A(X) -(void)test; @end",
"@interface A @end @interface A(X) @end", Lang_OBJC, objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceObjCCategoryTest, DifferentMethodOrder) {
auto t = makeDecls<ObjCCategoryDecl>(
"@interface A @end @interface A(X) -(void)u; -(void)v; @end",
"@interface A @end @interface A(X) -(void)v; -(void)u; @end", Lang_OBJC,
objcCategoryDecl());
EXPECT_FALSE(testStructuralMatch(t));
}
struct StructuralEquivalenceTemplateTest : StructuralEquivalenceTest {};
TEST_F(StructuralEquivalenceTemplateTest, ExactlySameTemplates) {
auto t = makeNamedDecls("template <class T> struct foo;",
"template <class T> struct foo;", Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTemplateTest, DifferentTemplateArgName) {
auto t = makeNamedDecls("template <class T> struct foo;",
"template <class U> struct foo;", Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTemplateTest, DifferentTemplateArgKind) {
auto t = makeNamedDecls("template <class T> struct foo;",
"template <int T> struct foo;", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTemplateTest, BitFieldDecl) {
const char *Code = "class foo { int a : 2; };";
auto t = makeNamedDecls(Code, Code, Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTemplateTest, BitFieldDeclDifferentWidth) {
auto t = makeNamedDecls("class foo { int a : 2; };",
"class foo { int a : 4; };", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTemplateTest, DependentBitFieldDecl) {
const char *Code = "template <class T> class foo { int a : sizeof(T); };";
auto t = makeNamedDecls(Code, Code, Lang_CXX03);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTemplateTest, DependentBitFieldDeclDifferentVal) {
auto t = makeNamedDecls(
"template <class A, class B> class foo { int a : sizeof(A); };",
"template <class A, class B> class foo { int a : sizeof(B); };",
Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTemplateTest, DependentBitFieldDeclDifferentVal2) {
auto t = makeNamedDecls(
"template <class A> class foo { int a : sizeof(A); };",
"template <class A> class foo { int a : sizeof(A) + 1; };", Lang_CXX03);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTemplateTest, ExplicitBoolSame) {
auto Decls = makeNamedDecls(
"template <bool b> struct foo {explicit(b) foo(int);};",
"template <bool b> struct foo {explicit(b) foo(int);};", Lang_CXX20);
CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match(
get<0>(Decls), cxxConstructorDecl(hasName("foo<b>")));
CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match(
get<1>(Decls), cxxConstructorDecl(hasName("foo<b>")));
EXPECT_TRUE(testStructuralMatch(First, Second));
}
TEST_F(StructuralEquivalenceTemplateTest, ExplicitBoolDifference) {
auto Decls = makeNamedDecls(
"template <bool b> struct foo {explicit(b) foo(int);};",
"template <bool b> struct foo {explicit(!b) foo(int);};", Lang_CXX20);
CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match(
get<0>(Decls), cxxConstructorDecl(hasName("foo<b>")));
CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match(
get<1>(Decls), cxxConstructorDecl(hasName("foo<b>")));
EXPECT_FALSE(testStructuralMatch(First, Second));
}
TEST_F(StructuralEquivalenceTemplateTest,
TemplateVsSubstTemplateTemplateParmInArgEq) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <typename P1> class Arg { };
template <template <typename PP1> class P1> class Primary { };
void f() {
// Make specialization with simple template.
Primary <Arg> A;
}
)",
R"(
template <typename P1> class Arg { };
template <template <typename PP1> class P1> class Primary { };
template <template <typename PP1> class P1> class Templ {
void f() {
// Make specialization with substituted template template param.
Primary <P1> A;
};
};
// Instantiate with substitution Arg into P1.
template class Templ <Arg>;
)",
Lang_CXX03, classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTemplateTest,
TemplateVsSubstTemplateTemplateParmInArgNotEq) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <typename P1> class Arg { };
template <template <typename PP1> class P1> class Primary { };
void f() {
// Make specialization with simple template.
Primary <Arg> A;
}
)",
R"(
// Arg is different from the other, this should cause non-equivalence.
template <typename P1> class Arg { int X; };
template <template <typename PP1> class P1> class Primary { };
template <template <typename PP1> class P1> class Templ {
void f() {
// Make specialization with substituted template template param.
Primary <P1> A;
};
};
// Instantiate with substitution Arg into P1.
template class Templ <Arg>;
)",
Lang_CXX03, classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_FALSE(testStructuralMatch(t));
}
struct StructuralEquivalenceDependentTemplateArgsTest
: StructuralEquivalenceTemplateTest {};
TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
SameStructsInDependentArgs) {
std::string Code =
R"(
template <typename>
struct S1;
template <typename>
struct enable_if;
struct S
{
template <typename T, typename enable_if<S1<T>>::type>
void f();
};
)";
auto t = makeDecls<FunctionTemplateDecl>(Code, Code, Lang_CXX11,
functionTemplateDecl(hasName("f")));
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
DifferentStructsInDependentArgs) {
std::string Code =
R"(
template <typename>
struct S1;
template <typename>
struct S2;
template <typename>
struct enable_if;
)";
auto t = makeDecls<FunctionTemplateDecl>(Code + R"(
struct S
{
template <typename T, typename enable_if<S1<T>>::type>
void f();
};
)",
Code + R"(
struct S
{
template <typename T, typename enable_if<S2<T>>::type>
void f();
};
)",
Lang_CXX11,
functionTemplateDecl(hasName("f")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
SameStructsInDependentScopeDeclRefExpr) {
std::string Code =
R"(
template <typename>
struct S1;
template <bool>
struct enable_if;
struct S
{
template <typename T, typename enable_if<S1<T>::value>::type>
void f(); // DependentScopeDeclRefExpr:^^^^^^^^^^^^
};
)";
auto t = makeDecls<FunctionTemplateDecl>(Code, Code, Lang_CXX11,
functionTemplateDecl(hasName("f")));
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
DifferentStructsInDependentScopeDeclRefExpr) {
std::string Code =
R"(
template <typename>
struct S1;
template <typename>
struct S2;
template <bool>
struct enable_if;
)";
auto t = makeDecls<FunctionTemplateDecl>(Code + R"(
struct S
{
template <typename T, typename enable_if<S1<T>::value>::type>
void f(); // DependentScopeDeclRefExpr:^^^^^^^^^^^^
};
)",
Code + R"(
struct S
{
template <typename T, typename enable_if<S2<T>::value>::type>
void f();
};
)",
Lang_CXX03,
functionTemplateDecl(hasName("f")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceDependentTemplateArgsTest,
DifferentValueInDependentScopeDeclRefExpr) {
std::string Code =
R"(
template <typename>
struct S1;
template <bool>
struct enable_if;
)";
auto t = makeDecls<FunctionTemplateDecl>(Code + R"(
struct S
{
template <typename T, typename enable_if<S1<T>::value1>::type>
void f(); // DependentScopeDeclRefExpr:^^^^^^^^^^^^
};
)",
Code + R"(
struct S
{
template <typename T, typename enable_if<S1<T>::value2>::type>
void f();
};
)",
Lang_CXX03,
functionTemplateDecl(hasName("f")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(
StructuralEquivalenceTemplateTest,
ClassTemplSpecWithQualifiedAndNonQualifiedTypeArgsShouldBeEqual) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <class T> struct Primary {};
namespace N {
struct Arg;
}
// Explicit instantiation with qualified name.
template struct Primary<N::Arg>;
)",
R"(
template <class T> struct Primary {};
namespace N {
struct Arg;
}
using namespace N;
// Explicit instantiation with UNqualified name.
template struct Primary<Arg>;
)",
Lang_CXX03, classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(
StructuralEquivalenceTemplateTest,
ClassTemplSpecWithInequivalentQualifiedAndNonQualifiedTypeArgs) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <class T> struct Primary {};
namespace N {
struct Arg { int a; };
}
// Explicit instantiation with qualified name.
template struct Primary<N::Arg>;
)",
R"(
template <class T> struct Primary {};
namespace N {
// This struct is not equivalent with the other in the prev TU.
struct Arg { double b; }; // -- Field mismatch.
}
using namespace N;
// Explicit instantiation with UNqualified name.
template struct Primary<Arg>;
)",
Lang_CXX03, classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(
StructuralEquivalenceTemplateTest,
ClassTemplSpecWithQualifiedAndNonQualifiedTemplArgsShouldBeEqual) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <template <class> class T> struct Primary {};
namespace N {
template <class T> struct Arg;
}
// Explicit instantiation with qualified name.
template struct Primary<N::Arg>;
)",
R"(
template <template <class> class T> struct Primary {};
namespace N {
template <class T> struct Arg;
}
using namespace N;
// Explicit instantiation with UNqualified name.
template struct Primary<Arg>;
)",
Lang_CXX03, classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(
StructuralEquivalenceTemplateTest,
ClassTemplSpecWithInequivalentQualifiedAndNonQualifiedTemplArgs) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <template <class> class T> struct Primary {};
namespace N {
template <class T> struct Arg { int a; };
}
// Explicit instantiation with qualified name.
template struct Primary<N::Arg>;
)",
R"(
template <template <class> class T> struct Primary {};
namespace N {
// This template is not equivalent with the other in the prev TU.
template <class T> struct Arg { double b; }; // -- Field mismatch.
}
using namespace N;
// Explicit instantiation with UNqualified name.
template struct Primary<Arg>;
)",
Lang_CXX03, classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceTemplateTest,
IgnoreTemplateParmDepthAtTemplateTypeParmDecl) {
auto Decls = makeDecls<ClassTemplateDecl>(
R"(
template<class> struct A;
)",
R"(
template<class> struct S {
template<class> friend struct A;
};
)",
Lang_CXX03, classTemplateDecl(hasName("A")),
classTemplateDecl(hasName("A")));
EXPECT_TRUE(testStructuralMatch(Decls));
EXPECT_TRUE(testStructuralMatch(Decls, true));
}
TEST_F(StructuralEquivalenceTemplateTest,
IgnoreTemplateParmDepthAtNonTypeTemplateParmDecl) {
auto Decls = makeDecls<ClassTemplateDecl>(
R"(
template<class T, T U> struct A;
)",
R"(
template<class T> struct S {
template<class P, P Q> friend struct A;
};
)",
Lang_CXX03, classTemplateDecl(hasName("A")),
classTemplateDecl(hasName("A")));
EXPECT_FALSE(testStructuralMatch(Decls));
EXPECT_TRUE(testStructuralMatch(Decls, /*IgnoreTemplateParmDepth=*/true));
}
TEST_F(
StructuralEquivalenceTemplateTest,
ClassTemplSpecWithInequivalentShadowedTemplArg) {
auto t = makeDecls<ClassTemplateSpecializationDecl>(
R"(
template <template <class> class T> struct Primary {};
template <class T> struct Arg { int a; };
// Explicit instantiation with ::Arg
template struct Primary<Arg>;
)",
R"(
template <template <class> class T> struct Primary {};
template <class T> struct Arg { int a; };
namespace N {
// This template is not equivalent with the other in the global scope.
template <class T> struct Arg { double b; }; // -- Field mismatch.
// Explicit instantiation with N::Arg which shadows ::Arg
template struct Primary<Arg>;
}
)",
Lang_CXX03, classTemplateSpecializationDecl(hasName("Primary")));
EXPECT_FALSE(testStructuralMatch(t));
}
struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest {
StructuralEquivalenceContext::NonEquivalentDeclSet NonEquivalentDecls;
template <typename NodeType, typename MatcherType>
std::pair<NodeType *, NodeType *>
findDeclPair(std::tuple<TranslationUnitDecl *, TranslationUnitDecl *> TU,
MatcherType M) {
NodeType *D0 = FirstDeclMatcher<NodeType>().match(get<0>(TU), M);
NodeType *D1 = FirstDeclMatcher<NodeType>().match(get<1>(TU), M);
return {D0, D1};
}
template <typename NodeType>
bool isInNonEqCache(std::pair<NodeType *, NodeType *> D,
bool IgnoreTemplateParmDepth = false) {
return NonEquivalentDecls.count(
std::make_tuple(D.first, D.second, IgnoreTemplateParmDepth)) > 0;
}
};
TEST_F(StructuralEquivalenceCacheTest, SimpleNonEq) {
auto TU = makeTuDecls(
R"(
class A {};
class B {};
void x(A, A);
)",
R"(
class A {};
class B {};
void x(A, B);
)",
Lang_CXX03);
StructuralEquivalenceContext Ctx(
get<0>(TU)->getASTContext().getLangOpts(), get<0>(TU)->getASTContext(),
get<1>(TU)->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
/*Complain=*/false);
auto X = findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x")));
EXPECT_FALSE(Ctx.IsEquivalent(X.first, X.second));
EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>(
TU, cxxRecordDecl(hasName("A"), unless(isImplicit())))));
EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>(
TU, cxxRecordDecl(hasName("B"), unless(isImplicit())))));
}
TEST_F(StructuralEquivalenceCacheTest, ReturnStmtNonEq) {
auto TU = makeTuDecls(
R"(
bool x() { return true; }
)",
R"(
bool x() { return false; }
)",
Lang_CXX03);
StructuralEquivalenceContext Ctx(
get<0>(TU)->getASTContext().getLangOpts(), get<0>(TU)->getASTContext(),
get<1>(TU)->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
/*Complain=*/false);
auto X = findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x")));
EXPECT_FALSE(Ctx.IsEquivalent(X.first->getBody(), X.second->getBody()));
}
TEST_F(StructuralEquivalenceCacheTest, VarDeclNoEq) {
auto TU = makeTuDecls(
R"(
int p;
)",
R"(
int q;
)",
Lang_CXX03);
StructuralEquivalenceContext Ctx(
get<0>(TU)->getASTContext().getLangOpts(), get<0>(TU)->getASTContext(),
get<1>(TU)->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
/*Complain=*/false);
auto Var = findDeclPair<VarDecl>(TU, varDecl());
EXPECT_FALSE(Ctx.IsEquivalent(Var.first, Var.second));
}
TEST_F(StructuralEquivalenceCacheTest, VarDeclWithDifferentStorageClassNoEq) {
auto TU = makeTuDecls(
R"(
int p;
)",
R"(
static int p;
)",
Lang_CXX03);
StructuralEquivalenceContext Ctx(
get<0>(TU)->getASTContext().getLangOpts(), get<0>(TU)->getASTContext(),
get<1>(TU)->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
/*Complain=*/false);
auto Var = findDeclPair<VarDecl>(TU, varDecl());
EXPECT_FALSE(Ctx.IsEquivalent(Var.first, Var.second));
}
TEST_F(StructuralEquivalenceCacheTest,
NonTypeTemplateParmWithDifferentPositionNoEq) {
auto TU = makeTuDecls(
R"(
template<int T>
struct A {
template<int U>
void foo() {}
};
)",
R"(
template<int U>
struct A {
template<int V, int T>
void foo() {}
};
)",
Lang_CXX03);
StructuralEquivalenceContext Ctx(
get<0>(TU)->getASTContext().getLangOpts(), get<0>(TU)->getASTContext(),
get<1>(TU)->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
/*Complain=*/false);
auto NTTP = findDeclPair<NonTypeTemplateParmDecl>(
TU, nonTypeTemplateParmDecl(hasName("T")));
EXPECT_FALSE(Ctx.IsEquivalent(NTTP.first, NTTP.second));
}
TEST_F(StructuralEquivalenceCacheTest, VarDeclWithInitNoEq) {
auto TU = makeTuDecls(
R"(
int p = 1;
)",
R"(
int p = 2;
)",
Lang_CXX03);
StructuralEquivalenceContext Ctx(
get<0>(TU)->getASTContext().getLangOpts(), get<0>(TU)->getASTContext(),
get<1>(TU)->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
/*Complain=*/false);
auto Var = findDeclPair<VarDecl>(TU, varDecl());
EXPECT_FALSE(Ctx.IsEquivalent(Var.first, Var.second));
}
TEST_F(StructuralEquivalenceCacheTest, SpecialNonEq) {
auto TU = makeTuDecls(
R"(
class A {};
class B { int i; };
void x(A *);
void y(A *);
class C {
friend void x(A *);
friend void y(A *);
};
)",
R"(
class A {};
class B { int i; };
void x(A *);
void y(B *);
class C {
friend void x(A *);
friend void y(B *);
};
)",
Lang_CXX03);
StructuralEquivalenceContext Ctx(
get<0>(TU)->getASTContext().getLangOpts(), get<0>(TU)->getASTContext(),
get<1>(TU)->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
/*Complain=*/false);
auto C = findDeclPair<CXXRecordDecl>(
TU, cxxRecordDecl(hasName("C"), unless(isImplicit())));
EXPECT_FALSE(Ctx.IsEquivalent(C.first, C.second));
EXPECT_FALSE(isInNonEqCache(C));
EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>(
TU, cxxRecordDecl(hasName("A"), unless(isImplicit())))));
EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>(
TU, cxxRecordDecl(hasName("B"), unless(isImplicit())))));
EXPECT_FALSE(isInNonEqCache(
findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x")))));
EXPECT_FALSE(isInNonEqCache(
findDeclPair<FunctionDecl>(TU, functionDecl(hasName("y")))));
}
TEST_F(StructuralEquivalenceCacheTest, Cycle) {
auto TU = makeTuDecls(
R"(
class C;
class A { C *c; };
void x(A *);
class C {
friend void x(A *);
};
)",
R"(
class C;
class A { C *c; };
void x(A *);
class C {
friend void x(A *);
};
)",
Lang_CXX03);
StructuralEquivalenceContext Ctx(
get<0>(TU)->getASTContext().getLangOpts(), get<0>(TU)->getASTContext(),
get<1>(TU)->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
/*Complain=*/false);
auto C = findDeclPair<CXXRecordDecl>(
TU, cxxRecordDecl(hasName("C"), unless(isImplicit())));
EXPECT_TRUE(Ctx.IsEquivalent(C.first, C.second));
EXPECT_FALSE(isInNonEqCache(C));
EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>(
TU, cxxRecordDecl(hasName("A"), unless(isImplicit())))));
EXPECT_FALSE(isInNonEqCache(
findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x")))));
}
TEST_F(StructuralEquivalenceCacheTest, TemplateParmDepth) {
// In 'friend struct Y' ClassTemplateDecl has the TU as parent context.
// This declaration has template depth 1 (it is already inside a template).
// It has not a previous declaration and is an "undeclared" friend.
//
// Second TU has a specialization of 'struct X'.
// In this case 'friend struct Y' has the ClassTemplateSpecializationDecl as
// parent. It has template depth 0 (it is in the specialization). It has the
// first 'struct Y' declaration as previous declaration and canonical
// declaration.
//
// When these two 'friend struct Y' are compared, only the template depth is
// different.
// FIXME: Structural equivalence checks the depth only in types, not in
// TemplateParmDecl. For this reason the second 'A1' argument is needed (as a
// type) in the template to make the check fail.
auto TU = makeTuDecls(
R"(
template <class A1, A1>
struct Y;
template <class A>
struct X {
template <class A1, A1>
friend struct Y;
};
)",
R"(
template <class A1, A1>
struct Y;
template <class A>
struct X {
template <class A1, A1>
friend struct Y;
};
X<int> x;
)",
Lang_CXX03);
auto *D0 = LastDeclMatcher<ClassTemplateDecl>().match(
get<0>(TU), classTemplateDecl(hasName("Y"), unless(isImplicit())));
auto *D1 = LastDeclMatcher<ClassTemplateDecl>().match(
get<1>(TU), classTemplateDecl(hasName("Y"), unless(isImplicit())));
ASSERT_EQ(D0->getTemplateDepth(), 1u);
ASSERT_EQ(D1->getTemplateDepth(), 0u);
StructuralEquivalenceContext Ctx_NoIgnoreTemplateParmDepth(
get<0>(TU)->getASTContext().getLangOpts(), get<0>(TU)->getASTContext(),
get<1>(TU)->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
/*Complain=*/false, /*ErrorOnTagTypeMismatch=*/false,
/*IgnoreTemplateParmDepth=*/false);
EXPECT_FALSE(Ctx_NoIgnoreTemplateParmDepth.IsEquivalent(D0, D1));
Decl *NonEqDecl0 =
D0->getCanonicalDecl()->getTemplateParameters()->getParam(1);
Decl *NonEqDecl1 =
D1->getCanonicalDecl()->getTemplateParameters()->getParam(1);
EXPECT_TRUE(isInNonEqCache(std::make_pair(NonEqDecl0, NonEqDecl1), false));
EXPECT_FALSE(isInNonEqCache(std::make_pair(NonEqDecl0, NonEqDecl1), true));
StructuralEquivalenceContext Ctx_IgnoreTemplateParmDepth(
get<0>(TU)->getASTContext().getLangOpts(), get<0>(TU)->getASTContext(),
get<1>(TU)->getASTContext(), NonEquivalentDecls,
StructuralEquivalenceKind::Default, /*StrictTypeSpelling=*/false,
/*Complain=*/false, /*ErrorOnTagTypeMismatch=*/false,
/*IgnoreTemplateParmDepth=*/true);
EXPECT_TRUE(Ctx_IgnoreTemplateParmDepth.IsEquivalent(D0, D1));
EXPECT_FALSE(isInNonEqCache(std::make_pair(NonEqDecl0, NonEqDecl1), true));
}
struct StructuralEquivalenceStmtTest : StructuralEquivalenceTest {};
/// Fallback matcher to be used only when there is no specific matcher for a
/// Expr subclass. Remove this once all Expr subclasses have their own matcher.
static auto &fallbackExprMatcher = expr;
TEST_F(StructuralEquivalenceStmtTest, AddrLabelExpr) {
auto t = makeWrappedStmts("lbl: &&lbl;", "lbl: &&lbl;", Lang_CXX03,
addrLabelExpr());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, AddrLabelExprDifferentLabel) {
auto t = makeWrappedStmts("lbl1: lbl2: &&lbl1;", "lbl1: lbl2: &&lbl2;",
Lang_CXX03, addrLabelExpr());
// FIXME: Should be false. LabelDecl are incorrectly matched.
EXPECT_TRUE(testStructuralMatch(t));
}
static const std::string MemoryOrderSrc = R"(
enum memory_order {
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst
};
)";
TEST_F(StructuralEquivalenceStmtTest, AtomicExpr) {
std::string Prefix = "char a, b; " + MemoryOrderSrc;
auto t = makeStmts(
Prefix +
"void wrapped() { __atomic_load(&a, &b, memory_order_seq_cst); }",
Prefix +
"void wrapped() { __atomic_load(&a, &b, memory_order_seq_cst); }",
Lang_CXX03, atomicExpr());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, AtomicExprDifferentOp) {
std::string Prefix = "char a, b; " + MemoryOrderSrc;
auto t = makeStmts(
Prefix +
"void wrapped() { __atomic_load(&a, &b, memory_order_seq_cst); }",
Prefix +
"void wrapped() { __atomic_store(&a, &b, memory_order_seq_cst); }",
Lang_CXX03, atomicExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, BinaryOperator) {
auto t = makeWrappedStmts("1 + 1", "1 + 1", Lang_CXX03, binaryOperator());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, BinaryOperatorDifferentOps) {
auto t = makeWrappedStmts("1 + 1", "1 - 1", Lang_CXX03, binaryOperator());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, CallExpr) {
std::string Src = "int call(); int wrapped() { call(); }";
auto t = makeStmts(Src, Src, Lang_CXX03, callExpr());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, CallExprDifferentCallee) {
std::string FunctionSrc = "int func1(); int func2();\n";
auto t = makeStmts(FunctionSrc + "void wrapper() { func1(); }",
FunctionSrc + "void wrapper() { func2(); }", Lang_CXX03,
callExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, CharacterLiteral) {
auto t = makeWrappedStmts("'a'", "'a'", Lang_CXX03, characterLiteral());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, CharacterLiteralDifferentValues) {
auto t = makeWrappedStmts("'a'", "'b'", Lang_CXX03, characterLiteral());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, ExpressionTraitExpr) {
auto t = makeWrappedStmts("__is_lvalue_expr(1)", "__is_lvalue_expr(1)",
Lang_CXX03, fallbackExprMatcher());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, ExpressionTraitExprDifferentKind) {
auto t = makeWrappedStmts("__is_lvalue_expr(1)", "__is_rvalue_expr(1)",
Lang_CXX03, fallbackExprMatcher());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, FloatingLiteral) {
auto t = makeWrappedStmts("1.0", "1.0", Lang_CXX03, fallbackExprMatcher());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, FloatingLiteralDifferentSpelling) {
auto t = makeWrappedStmts("0x10.1p0", "16.0625", Lang_CXX17,
fallbackExprMatcher());
// Same value but with different spelling is equivalent.
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, FloatingLiteralDifferentType) {
auto t = makeWrappedStmts("1.0", "1.0f", Lang_CXX03, fallbackExprMatcher());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, FloatingLiteralDifferentValue) {
auto t = makeWrappedStmts("1.01", "1.0", Lang_CXX03, fallbackExprMatcher());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, GenericSelectionExprSame) {
auto t = makeWrappedStmts("_Generic(0u, unsigned int: 0, float: 1)",
"_Generic(0u, unsigned int: 0, float: 1)", Lang_C99,
genericSelectionExpr());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, GenericSelectionExprSignsDiffer) {
auto t = makeWrappedStmts("_Generic(0u, unsigned int: 0, float: 1)",
"_Generic(0, int: 0, float: 1)", Lang_C99,
genericSelectionExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, GenericSelectionExprOrderDiffers) {
auto t = makeWrappedStmts("_Generic(0u, unsigned int: 0, float: 1)",
"_Generic(0u, float: 1, unsigned int: 0)", Lang_C99,
genericSelectionExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, GenericSelectionExprDependentResultSame) {
auto t = makeStmts(
R"(
template <typename T>
void f() {
T x;
(void)_Generic(x, int: 0, float: 1);
}
void g() { f<int>(); }
)",
R"(
template <typename T>
void f() {
T x;
(void)_Generic(x, int: 0, float: 1);
}
void g() { f<int>(); }
)",
Lang_CXX03, genericSelectionExpr());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest,
GenericSelectionExprDependentResultOrderDiffers) {
auto t = makeStmts(
R"(
template <typename T>
void f() {
T x;
(void)_Generic(x, float: 1, int: 0);
}
void g() { f<int>(); }
)",
R"(
template <typename T>
void f() {
T x;
(void)_Generic(x, int: 0, float: 1);
}
void g() { f<int>(); }
)",
Lang_CXX03, genericSelectionExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, IntegerLiteral) {
auto t = makeWrappedStmts("1", "1", Lang_CXX03, integerLiteral());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, IntegerLiteralDifferentSpelling) {
auto t = makeWrappedStmts("1", "0x1", Lang_CXX03, integerLiteral());
// Same value but with different spelling is equivalent.
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, IntegerLiteralDifferentValue) {
auto t = makeWrappedStmts("1", "2", Lang_CXX03, integerLiteral());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, IntegerLiteralDifferentTypes) {
auto t = makeWrappedStmts("1", "1L", Lang_CXX03, integerLiteral());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, MemberExpr) {
std::string ClassSrc = "struct C { int a; int b; };";
auto t = makeStmts(ClassSrc + "int wrapper() { C c; return c.a; }",
ClassSrc + "int wrapper() { C c; return c.a; }",
Lang_CXX03, memberExpr());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, MemberExprDifferentMember) {
std::string ClassSrc = "struct C { int a; int b; };";
auto t = makeStmts(ClassSrc + "int wrapper() { C c; return c.a; }",
ClassSrc + "int wrapper() { C c; return c.b; }",
Lang_CXX03, memberExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, ObjCStringLiteral) {
auto t =
makeWrappedStmts("@\"a\"", "@\"a\"", Lang_OBJCXX, fallbackExprMatcher());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, ObjCStringLiteralDifferentContent) {
auto t =
makeWrappedStmts("@\"a\"", "@\"b\"", Lang_OBJCXX, fallbackExprMatcher());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, StringLiteral) {
auto t = makeWrappedStmts("\"a\"", "\"a\"", Lang_CXX03, stringLiteral());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, StringLiteralDifferentContent) {
auto t = makeWrappedStmts("\"a\"", "\"b\"", Lang_CXX03, stringLiteral());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, StringLiteralDifferentLength) {
auto t = makeWrappedStmts("\"a\"", "\"aa\"", Lang_CXX03, stringLiteral());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, TypeTraitExpr) {
auto t = makeWrappedStmts("__is_pod(int)", "__is_pod(int)", Lang_CXX03,
fallbackExprMatcher());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, TypeTraitExprDifferentType) {
auto t = makeWrappedStmts("__is_pod(int)", "__is_pod(long)", Lang_CXX03,
fallbackExprMatcher());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, TypeTraitExprDifferentTrait) {
auto t = makeWrappedStmts(
"__is_pod(int)", "__is_trivially_constructible(int)", Lang_CXX03, expr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, TypeTraitExprDifferentTraits) {
auto t = makeWrappedStmts("__is_constructible(int)",
"__is_constructible(int, int)", Lang_CXX03, expr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, UnaryExprOrTypeTraitExpr) {
auto t = makeWrappedStmts("sizeof(int)", "sizeof(int)", Lang_CXX03,
unaryExprOrTypeTraitExpr());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, UnaryExprOrTypeTraitExprDifferentKind) {
auto t = makeWrappedStmts("sizeof(int)", "alignof(long)", Lang_CXX11,
unaryExprOrTypeTraitExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, UnaryExprOrTypeTraitExprDifferentType) {
auto t = makeWrappedStmts("sizeof(int)", "sizeof(long)", Lang_CXX03,
unaryExprOrTypeTraitExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, UnaryOperator) {
auto t = makeWrappedStmts("+1", "+1", Lang_CXX03, unaryOperator());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, UnaryOperatorDifferentOps) {
auto t = makeWrappedStmts("+1", "-1", Lang_CXX03, unaryOperator());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest,
CXXOperatorCallExprVsUnaryBinaryOperator) {
auto t = makeNamedDecls(
R"(
template <typename T, T x>
class A;
template <typename T, T x, T y>
void foo(
A<T, x + y>,
A<T, x - y>,
A<T, -x>,
A<T, x * y>,
A<T, *x>,
A<T, x / y>,
A<T, x % y>,
A<T, x ^ y>,
A<T, x & y>,
A<T, &x>,
A<T, x | y>,
A<T, ~x>,
A<T, !x>,
A<T, x < y>,
A<T, (x > y)>,
A<T, x << y>,
A<T, (x >> y)>,
A<T, x == y>,
A<T, x != y>,
A<T, x <= y>,
A<T, x >= y>,
A<T, x <=> y>,
A<T, x && y>,
A<T, x || y>,
A<T, ++x>,
A<T, --x>,
A<T, (x , y)>,
A<T, x ->* y>,
A<T, x -> y>
);
)",
R"(
struct Bar {
Bar& operator=(Bar&);
Bar& operator->();
};
Bar& operator+(Bar&, Bar&);
Bar& operator+(Bar&);
Bar& operator-(Bar&, Bar&);
Bar& operator-(Bar&);
Bar& operator*(Bar&, Bar&);
Bar& operator*(Bar&);
Bar& operator/(Bar&, Bar&);
Bar& operator%(Bar&, Bar&);
Bar& operator^(Bar&, Bar&);
Bar& operator&(Bar&, Bar&);
Bar& operator&(Bar&);
Bar& operator|(Bar&, Bar&);
Bar& operator~(Bar&);
Bar& operator!(Bar&);
Bar& operator<(Bar&, Bar&);
Bar& operator>(Bar&, Bar&);
Bar& operator+=(Bar&, Bar&);
Bar& operator-=(Bar&, Bar&);
Bar& operator*=(Bar&, Bar&);
Bar& operator/=(Bar&, Bar&);
Bar& operator%=(Bar&, Bar&);
Bar& operator^=(Bar&, Bar&);
Bar& operator&=(Bar&, Bar&);
Bar& operator|=(Bar&, Bar&);
Bar& operator<<(Bar&, Bar&);
Bar& operator>>(Bar&, Bar&);
Bar& operator<<=(Bar&, Bar&);
Bar& operator>>=(Bar&, Bar&);
Bar& operator==(Bar&, Bar&);
Bar& operator!=(Bar&, Bar&);
Bar& operator<=(Bar&, Bar&);
Bar& operator>=(Bar&, Bar&);
Bar& operator<=>(Bar&, Bar&);
Bar& operator&&(Bar&, Bar&);
Bar& operator||(Bar&, Bar&);
Bar& operator++(Bar&);
Bar& operator--(Bar&);
Bar& operator,(Bar&, Bar&);
Bar& operator->*(Bar&, Bar&);
template <typename T, T x>
class A;
template <typename T, T x, T y>
void foo(
A<T, x + y>,
A<T, x - y>,
A<T, -x>,
A<T, x * y>,
A<T, *x>,
A<T, x / y>,
A<T, x % y>,
A<T, x ^ y>,
A<T, x & y>,
A<T, &x>,
A<T, x | y>,
A<T, ~x>,
A<T, !x>,
A<T, x < y>,
A<T, (x > y)>,
A<T, x << y>,
A<T, (x >> y)>,
A<T, x == y>,
A<T, x != y>,
A<T, x <= y>,
A<T, x >= y>,
A<T, x <=> y>,
A<T, x && y>,
A<T, x || y>,
A<T, ++x>,
A<T, --x>,
A<T, (x , y)>,
A<T, x ->* y>,
A<T, x -> y>
);
)",
Lang_CXX20);
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest,
CXXOperatorCallExprVsUnaryBinaryOperatorNe) {
auto t = makeNamedDecls(
R"(
template <typename T, T x>
class A;
template <typename T, T x, T y>
void foo(
A<T, x + y>
);
)",
R"(
struct Bar;
Bar& operator-(Bar&, Bar&);
template <typename T, T x>
class A;
template <typename T, T x, T y>
void foo(
A<T, x - y>
);
)",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, NonTypeTemplateParm) {
auto t = makeNamedDecls(
R"(
template <typename T, T x>
class A;
template <typename T, T x, T y>
void foo(A<T, x>);
)",
R"(
template <typename T, T x>
class A;
template <typename T, T x, T y>
void foo(A<T, y>);
)",
Lang_CXX11);
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, UnresolvedLookupDifferentName) {
auto t = makeStmts(
R"(
void f1(int);
template <typename T>
void f(T t) {
f1(t);
}
void g() { f<int>(1); }
)",
R"(
void f2(int);
template <typename T>
void f(T t) {
f2(t);
}
void g() { f<int>(1); }
)",
Lang_CXX03, unresolvedLookupExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, UnresolvedLookupDifferentQualifier) {
auto t = makeStmts(
R"(
struct X {
static void g(int);
static void g(char);
};
template <typename T>
void f(T t) {
X::g(t);
}
void g() { f<int>(1); }
)",
R"(
struct Y {
static void g(int);
static void g(char);
};
template <typename T>
void f(T t) {
Y::g(t);
}
void g() { f<int>(1); }
)",
Lang_CXX03, unresolvedLookupExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest,
UnresolvedLookupDifferentTemplateArgument) {
auto t = makeStmts(
R"(
struct A {};
template<typename T1, typename T2>
void g() {}
template <typename T>
void f() {
g<A, T>();
}
void h() { f<int>(); }
)",
R"(
struct B {};
template<typename T1, typename T2>
void g() {}
template <typename T>
void f() {
g<B, T>();
}
void h() { f<int>(); }
)",
Lang_CXX03, unresolvedLookupExpr());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceStmtTest, UnresolvedLookup) {
auto t = makeStmts(
R"(
struct A {};
struct B {
template<typename T1, typename T2>
static void g(int) {};
template<typename T1, typename T2>
static void g(char) {};
};
template <typename T1, typename T2>
void f(T2 x) {
B::g<A, T1>(x);
}
void g() { f<char, int>(1); }
)",
R"(
struct A {};
struct B {
template<typename T1, typename T2>
static void g(int) {};
template<typename T1, typename T2>
static void g(char) {};
};
template <typename T1, typename T2>
void f(T2 x) {
B::g<A, T1>(x);
}
void g() { f<char, int>(1); }
)",
Lang_CXX03, unresolvedLookupExpr());
EXPECT_TRUE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCacheTest, GotoStmtNoEq) {
auto S = makeStmts(
R"(
void foo() {
goto L1;
L1: foo();
}
)",
R"(
void foo() {
goto L2;
L2: foo();
}
)",
Lang_CXX03, gotoStmt());
EXPECT_FALSE(testStructuralMatch(S));
}
TEST_F(StructuralEquivalenceStmtTest, DeclRefExpr) {
std::string Prefix = "enum Test { AAA, BBB };";
auto t = makeStmts(
Prefix + "void foo(int i) {if (i > 0) {i = AAA;} else {i = BBB;}}",
Prefix + "void foo(int i) {if (i > 0) {i = BBB;} else {i = AAA;}}",
Lang_CXX03, ifStmt());
EXPECT_FALSE(testStructuralMatch(t));
}
TEST_F(StructuralEquivalenceCacheTest, CXXDependentScopeMemberExprNoEq) {
auto S = makeStmts(
R"(
template <class T>
void foo() {
(void)T().x;
}
struct A { int x; };
void bar() {
foo<A>();
}
)",
R"(
template <class T>
void foo() {
(void)T().y;
}
struct A { int y; };
void bar() {
foo<A>();
}
)",
Lang_CXX11, cxxDependentScopeMemberExpr());
EXPECT_FALSE(testStructuralMatch(S));
}
} // end namespace ast_matchers
} // end namespace clang