2018-05-24 16:41:07 +08:00
|
|
|
#include "clang/AST/ASTContext.h"
|
|
|
|
#include "clang/ASTMatchers/ASTMatchers.h"
|
|
|
|
#include "clang/AST/ASTStructuralEquivalence.h"
|
|
|
|
#include "clang/Frontend/ASTUnit.h"
|
|
|
|
#include "clang/Tooling/Tooling.h"
|
|
|
|
|
|
|
|
#include "Language.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
|
|
|
|
|
2018-07-11 17:37:24 +08:00
|
|
|
// 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, Language Lang,
|
|
|
|
const MatcherType &Matcher0, const MatcherType &Matcher1) {
|
2018-05-24 16:41:07 +08:00
|
|
|
this->Code0 = SrcCode0;
|
|
|
|
this->Code1 = SrcCode1;
|
|
|
|
ArgVector Args = getBasicRunOptionsForLanguage(Lang);
|
|
|
|
|
|
|
|
const char *const InputFileName = "input.cc";
|
|
|
|
|
|
|
|
AST0 = tooling::buildASTFromCodeWithArgs(Code0, Args, InputFileName);
|
|
|
|
AST1 = tooling::buildASTFromCodeWithArgs(Code1, Args, InputFileName);
|
|
|
|
|
2018-07-11 17:37:24 +08:00
|
|
|
NodeType *D0 = FirstDeclMatcher<NodeType>().match(
|
|
|
|
AST0->getASTContext().getTranslationUnitDecl(), Matcher0);
|
|
|
|
NodeType *D1 = FirstDeclMatcher<NodeType>().match(
|
|
|
|
AST1->getASTContext().getTranslationUnitDecl(), Matcher1);
|
2018-05-24 16:41:07 +08:00
|
|
|
|
2018-07-11 17:37:24 +08:00
|
|
|
return std::make_tuple(D0, D1);
|
|
|
|
}
|
2018-05-24 16:41:07 +08:00
|
|
|
|
2018-07-17 20:06:36 +08:00
|
|
|
std::tuple<TranslationUnitDecl *, TranslationUnitDecl *> makeTuDecls(
|
|
|
|
const std::string &SrcCode0, const std::string &SrcCode1, Language Lang) {
|
|
|
|
this->Code0 = SrcCode0;
|
|
|
|
this->Code1 = SrcCode1;
|
|
|
|
ArgVector Args = getBasicRunOptionsForLanguage(Lang);
|
|
|
|
|
|
|
|
const char *const InputFileName = "input.cc";
|
|
|
|
|
|
|
|
AST0 = tooling::buildASTFromCodeWithArgs(Code0, Args, InputFileName);
|
|
|
|
AST1 = tooling::buildASTFromCodeWithArgs(Code1, Args, InputFileName);
|
|
|
|
|
|
|
|
return std::make_tuple(AST0->getASTContext().getTranslationUnitDecl(),
|
|
|
|
AST1->getASTContext().getTranslationUnitDecl());
|
|
|
|
}
|
|
|
|
|
2018-07-11 17:37:24 +08:00
|
|
|
// 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, Language Lang,
|
|
|
|
const MatcherType &AMatcher) {
|
|
|
|
return makeDecls<NodeType, MatcherType>(
|
|
|
|
SrcCode0, SrcCode1, Lang, AMatcher, AMatcher);
|
|
|
|
}
|
2018-05-24 16:41:07 +08:00
|
|
|
|
2018-07-11 17:37:24 +08:00
|
|
|
// 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,
|
|
|
|
Language Lang, const char *const Identifier = "foo") {
|
|
|
|
auto Matcher = namedDecl(hasName(Identifier));
|
|
|
|
return makeDecls<NamedDecl>(SrcCode0, SrcCode1, Lang, Matcher);
|
2018-05-24 16:41:07 +08:00
|
|
|
}
|
|
|
|
|
2018-07-17 20:06:36 +08:00
|
|
|
bool testStructuralMatch(Decl *D0, Decl *D1) {
|
2018-05-24 16:41:07 +08:00
|
|
|
llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls;
|
[ASTImporter] Refactor Decl creation
Summary:
Generalize the creation of Decl nodes during Import. With this patch we do the
same things after and before a new AST node is created (::Create) The import
logic should be really simple, we create the node, then we mark that as
imported, then we recursively import the parts for that node and then set them
on that node. However, the AST is actually a graph, so we have to handle
circles. If we mark something as imported (`MapImported()`) then we return with
the corresponding `To` decl whenever we want to import that node again, this way
circles are handled. In order to make this algorithm work we must ensure
things, which are handled in the generic CreateDecl<> template:
* There are no `Import()` calls in between any node creation (::Create)
and the `MapImported()` call.
* Before actually creating an AST node (::Create), we must check if
the Node had been imported already, if yes then return with that one.
One very important case for this is connected to templates: we may
start an import both from the templated decl of a template and from
the template itself.
Now, the virtual `Imported` function is called in `ASTImporter::Impor(Decl *)`,
but only once, when the `Decl` is imported. One point of this refactor is to
separate responsibilities. The original `Imported()` had 3 responsibilities:
- notify subclasses when an import happened
- register the decl into `ImportedDecls`
- initialise the Decl (set attributes, etc)
Now all of these are in separate functions:
- `Imported`
- `MapImported`
- `InitializeImportedDecl`
I tried to check all the clients, I executed tests for `ExternalASTMerger.cpp`
and some unittests for lldb.
Reviewers: a.sidorin, balazske, xazax.hun, r.stahl
Subscribers: rnkovacs, dkrupp, cfe-commits
Differential Revision: https://reviews.llvm.org/D47632
llvm-svn: 336896
2018-07-12 17:42:05 +08:00
|
|
|
StructuralEquivalenceContext Ctx(
|
|
|
|
D0->getASTContext(), D1->getASTContext(), NonEquivalentDecls,
|
|
|
|
StructuralEquivalenceKind::Default, false, false);
|
2018-07-17 20:39:27 +08:00
|
|
|
return Ctx.IsEquivalent(D0, D1);
|
2018-05-24 16:41:07 +08:00
|
|
|
}
|
|
|
|
|
2018-07-17 20:06:36 +08:00
|
|
|
bool testStructuralMatch(std::tuple<Decl *, Decl *> t) {
|
2018-05-24 16:41:07 +08:00
|
|
|
return testStructuralMatch(get<0>(t), get<1>(t));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceTest, Int) {
|
|
|
|
auto Decls = makeNamedDecls("int foo;", "int foo;", Lang_CXX);
|
|
|
|
EXPECT_TRUE(testStructuralMatch(Decls));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceTest, IntVsSignedInt) {
|
|
|
|
auto Decls = makeNamedDecls("int foo;", "signed int foo;", Lang_CXX);
|
|
|
|
EXPECT_TRUE(testStructuralMatch(Decls));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceTest, Char) {
|
|
|
|
auto Decls = makeNamedDecls("char foo;", "char foo;", Lang_CXX);
|
|
|
|
EXPECT_TRUE(testStructuralMatch(Decls));
|
|
|
|
}
|
|
|
|
|
|
|
|
// This test is disabled for now.
|
|
|
|
// FIXME Whether this is equivalent is dependendant on the target.
|
|
|
|
TEST_F(StructuralEquivalenceTest, DISABLED_CharVsSignedChar) {
|
|
|
|
auto Decls = makeNamedDecls("char foo;", "signed char foo;", Lang_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(Decls));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceTest, ForwardRecordDecl) {
|
|
|
|
auto Decls = makeNamedDecls("struct foo;", "struct foo;", Lang_CXX);
|
|
|
|
EXPECT_TRUE(testStructuralMatch(Decls));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceTest, IntVsSignedIntInStruct) {
|
|
|
|
auto Decls = makeNamedDecls("struct foo { int x; };",
|
|
|
|
"struct foo { signed int x; };", Lang_CXX);
|
|
|
|
EXPECT_TRUE(testStructuralMatch(Decls));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceTest, CharVsSignedCharInStruct) {
|
|
|
|
auto Decls = makeNamedDecls("struct foo { char x; };",
|
|
|
|
"struct foo { signed char x; };", Lang_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(Decls));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceTest, IntVsSignedIntTemplateSpec) {
|
2018-07-11 17:37:24 +08:00
|
|
|
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_CXX,
|
|
|
|
classTemplateSpecializationDecl());
|
|
|
|
auto Spec0 = get<0>(Decls);
|
|
|
|
auto Spec1 = get<1>(Decls);
|
2018-05-24 16:41:07 +08:00
|
|
|
EXPECT_TRUE(testStructuralMatch(Spec0, Spec1));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpec) {
|
2018-07-11 17:37:24 +08:00
|
|
|
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_CXX,
|
|
|
|
classTemplateSpecializationDecl());
|
|
|
|
auto Spec0 = get<0>(Decls);
|
|
|
|
auto Spec1 = get<1>(Decls);
|
2018-05-24 16:41:07 +08:00
|
|
|
EXPECT_FALSE(testStructuralMatch(Spec0, Spec1));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpecWithInheritance) {
|
2018-07-11 17:37:24 +08:00
|
|
|
auto Decls = makeDecls<ClassTemplateSpecializationDecl>(
|
2018-05-24 16:41:07 +08:00
|
|
|
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 {};
|
|
|
|
)",
|
2018-07-11 17:37:24 +08:00
|
|
|
Lang_CXX,
|
|
|
|
classTemplateSpecializationDecl());
|
|
|
|
EXPECT_FALSE(testStructuralMatch(Decls));
|
2018-05-24 16:41:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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_CXX);
|
|
|
|
|
|
|
|
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_CXX, "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));
|
|
|
|
}
|
|
|
|
|
2018-07-11 17:37:24 +08:00
|
|
|
struct StructuralEquivalenceFunctionTest : StructuralEquivalenceTest {
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceFunctionTest, ParamConstWithRef) {
|
|
|
|
auto t = makeNamedDecls("void foo(int&);",
|
|
|
|
"void foo(const int&);", Lang_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceFunctionTest, ParamConstSimple) {
|
|
|
|
auto t = makeNamedDecls("void foo(int);",
|
|
|
|
"void foo(const int);", Lang_CXX);
|
|
|
|
EXPECT_TRUE(testStructuralMatch(t));
|
|
|
|
// consider this OK
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceFunctionTest, Throw) {
|
|
|
|
auto t = makeNamedDecls("void foo();",
|
|
|
|
"void foo() throw();", Lang_CXX);
|
|
|
|
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, DISABLED_NoexceptNonMatch) {
|
|
|
|
// The expression is not checked yet.
|
|
|
|
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_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceFunctionTest, ReturnConst) {
|
|
|
|
auto t = makeNamedDecls("char foo();",
|
|
|
|
"const char foo();", Lang_CXX);
|
|
|
|
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_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceFunctionTest, ParamType) {
|
|
|
|
auto t = makeNamedDecls("void foo(int);",
|
|
|
|
"void foo(char);", Lang_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceFunctionTest, ParamName) {
|
|
|
|
auto t = makeNamedDecls("void foo(int a);",
|
|
|
|
"void foo(int b);", Lang_CXX);
|
|
|
|
EXPECT_TRUE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceFunctionTest, Variadic) {
|
|
|
|
auto t = makeNamedDecls("void foo(int x...);",
|
|
|
|
"void foo(int x);", Lang_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceFunctionTest, ParamPtr) {
|
|
|
|
auto t = makeNamedDecls("void foo(int *);",
|
|
|
|
"void foo(int);", Lang_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceFunctionTest, NameInParen) {
|
|
|
|
auto t = makeNamedDecls(
|
|
|
|
"void ((foo))();",
|
|
|
|
"void foo();",
|
|
|
|
Lang_CXX);
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
struct StructuralEquivalenceCXXMethodTest : StructuralEquivalenceTest {
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceCXXMethodTest, Virtual) {
|
|
|
|
auto t = makeDecls<CXXMethodDecl>(
|
|
|
|
"struct X { void foo(); };",
|
|
|
|
"struct X { virtual void foo(); };", Lang_CXX,
|
|
|
|
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_CXX);
|
|
|
|
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_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceCXXMethodTest, Const) {
|
|
|
|
auto t = makeNamedDecls("struct X { void foo(); };",
|
|
|
|
"struct X { void foo() const; };", Lang_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceCXXMethodTest, Static) {
|
|
|
|
auto t = makeNamedDecls("struct X { void foo(); };",
|
|
|
|
"struct X { static void foo(); };", Lang_CXX);
|
|
|
|
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_CXX,
|
|
|
|
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_CXX,
|
2018-07-11 23:26:26 +08:00
|
|
|
functionDecl(hasName("foo")), cxxConstructorDecl(hasName("foo")));
|
2018-07-11 17:37:24 +08:00
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorParam) {
|
|
|
|
auto t = makeDecls<CXXConstructorDecl>("struct X { X(); };",
|
|
|
|
"struct X { X(int); };", Lang_CXX,
|
2018-07-11 23:26:26 +08:00
|
|
|
cxxConstructorDecl(hasName("X")));
|
2018-07-11 17:37:24 +08:00
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorExplicit) {
|
|
|
|
auto t = makeDecls<CXXConstructorDecl>("struct X { X(int); };",
|
|
|
|
"struct X { explicit X(int); };",
|
|
|
|
Lang_CXX11,
|
2018-07-11 23:26:26 +08:00
|
|
|
cxxConstructorDecl(hasName("X")));
|
2018-07-11 17:37:24 +08:00
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorDefault) {
|
|
|
|
auto t = makeDecls<CXXConstructorDecl>("struct X { X(); };",
|
|
|
|
"struct X { X() = default; };",
|
|
|
|
Lang_CXX11,
|
2018-07-11 23:26:26 +08:00
|
|
|
cxxConstructorDecl(hasName("X")));
|
2018-07-11 17:37:24 +08:00
|
|
|
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_CXX,
|
|
|
|
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_CXX,
|
|
|
|
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_CXX,
|
|
|
|
functionDecl(allOf(hasName("f"), isDefinition())));
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
struct StructuralEquivalenceRecordTest : StructuralEquivalenceTest {
|
2018-07-17 20:06:36 +08:00
|
|
|
// FIXME Use a common getRecordDecl with ASTImporterTest.cpp!
|
|
|
|
RecordDecl *getRecordDecl(FieldDecl *FD) {
|
|
|
|
auto *ET = cast<ElaboratedType>(FD->getType().getTypePtr());
|
|
|
|
return cast<RecordType>(ET->getNamedType().getTypePtr())->getDecl();
|
|
|
|
};
|
2018-07-11 17:37:24 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceRecordTest, Name) {
|
|
|
|
auto t = makeDecls<CXXRecordDecl>(
|
|
|
|
"struct A{ };",
|
|
|
|
"struct B{ };",
|
|
|
|
Lang_CXX,
|
2018-07-11 23:26:26 +08:00
|
|
|
cxxRecordDecl(hasName("A")),
|
|
|
|
cxxRecordDecl(hasName("B")));
|
2018-07-11 17:37:24 +08:00
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceRecordTest, Fields) {
|
|
|
|
auto t = makeNamedDecls(
|
|
|
|
"struct foo{ int x; };",
|
|
|
|
"struct foo{ char x; };",
|
|
|
|
Lang_CXX);
|
|
|
|
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_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceRecordTest, Bases) {
|
|
|
|
auto t = makeNamedDecls(
|
|
|
|
"struct A{ }; struct foo: A { };",
|
|
|
|
"struct B{ }; struct foo: B { };",
|
|
|
|
Lang_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_F(StructuralEquivalenceRecordTest, InheritanceVirtual) {
|
|
|
|
auto t = makeNamedDecls(
|
|
|
|
"struct A{ }; struct foo: A { };",
|
|
|
|
"struct A{ }; struct foo: virtual A { };",
|
|
|
|
Lang_CXX);
|
|
|
|
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_CXX);
|
|
|
|
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_CXX);
|
|
|
|
EXPECT_TRUE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
2018-07-17 20:06:36 +08:00
|
|
|
TEST_F(StructuralEquivalenceRecordTest, UnnamedRecordsShouldBeInequivalent) {
|
|
|
|
auto t = makeTuDecls(
|
|
|
|
R"(
|
|
|
|
struct A {
|
|
|
|
struct {
|
|
|
|
struct A *next;
|
|
|
|
} entry0;
|
|
|
|
struct {
|
|
|
|
struct A *next;
|
|
|
|
} entry1;
|
|
|
|
};
|
|
|
|
)",
|
|
|
|
"", Lang_C);
|
|
|
|
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,
|
|
|
|
UnnamedRecordsShouldBeInequivalentEvenIfTheSecondIsBeingDefined) {
|
|
|
|
auto Code =
|
|
|
|
R"(
|
|
|
|
struct A {
|
|
|
|
struct {
|
|
|
|
struct A *next;
|
|
|
|
} entry0;
|
|
|
|
struct {
|
|
|
|
struct A *next;
|
|
|
|
} entry1;
|
|
|
|
};
|
|
|
|
)";
|
|
|
|
auto t = makeTuDecls(Code, Code, Lang_C);
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-07-11 17:37:24 +08:00
|
|
|
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_CXX);
|
|
|
|
EXPECT_FALSE(testStructuralMatch(t));
|
|
|
|
}
|
|
|
|
|
2018-05-24 16:41:07 +08:00
|
|
|
} // end namespace ast_matchers
|
|
|
|
} // end namespace clang
|