diff options
Diffstat (limited to 'unittests/AST')
-rw-r--r-- | unittests/AST/ASTImporterTest.cpp | 220 | ||||
-rw-r--r-- | unittests/AST/CMakeLists.txt | 2 | ||||
-rw-r--r-- | unittests/AST/DataCollectionTest.cpp | 173 | ||||
-rw-r--r-- | unittests/AST/DeclPrinterTest.cpp | 111 | ||||
-rw-r--r-- | unittests/AST/StmtPrinterTest.cpp | 74 |
5 files changed, 525 insertions, 55 deletions
diff --git a/unittests/AST/ASTImporterTest.cpp b/unittests/AST/ASTImporterTest.cpp index 57b41f12b0bac..099d5412a7df8 100644 --- a/unittests/AST/ASTImporterTest.cpp +++ b/unittests/AST/ASTImporterTest.cpp @@ -97,6 +97,10 @@ testImport(const std::string &FromCode, Language FromLang, llvm::raw_svector_ostream ToNothing(ImportChecker); ToCtx.getTranslationUnitDecl()->print(ToNothing); + // This traverses the AST to catch certain bugs like poorly or not + // implemented subtrees. + Imported->dump(ToNothing); + return Verifier.match(Imported, AMatcher); } @@ -252,35 +256,28 @@ AST_MATCHER_P(TemplateDecl, hasTemplateDecl, TEST(ImportExpr, ImportParenListExpr) { MatchVerifier<Decl> Verifier; + EXPECT_TRUE(testImport( + "template<typename T> class dummy { void f() { dummy X(*this); } };" + "typedef dummy<int> declToImport;" + "template class dummy<int>;", + Lang_CXX, "", Lang_CXX, Verifier, + typedefDecl(hasType(templateSpecializationType( + hasDeclaration(classTemplateSpecializationDecl(hasSpecializedTemplate( + classTemplateDecl(hasTemplateDecl(cxxRecordDecl(hasMethod(allOf( + hasName("f"), + hasBody(compoundStmt(has(declStmt(hasSingleDecl( + varDecl(hasInitializer(parenListExpr(has(unaryOperator( + hasOperatorName("*"), + hasUnaryOperand(cxxThisExpr())))))))))))))))))))))))); +} + +TEST(ImportExpr, ImportSwitch) { + MatchVerifier<Decl> Verifier; EXPECT_TRUE( - testImport( - "template<typename T> class dummy { void f() { dummy X(*this); } };" - "typedef dummy<int> declToImport;" - "template class dummy<int>;", - Lang_CXX, "", Lang_CXX, Verifier, - typedefDecl( - hasType( - templateSpecializationType( - hasDeclaration( - classTemplateDecl( - hasTemplateDecl( - cxxRecordDecl( - hasMethod( - allOf( - hasName("f"), - hasBody( - compoundStmt( - has( - declStmt( - hasSingleDecl( - varDecl( - hasInitializer( - parenListExpr( - has( - unaryOperator( - hasOperatorName("*"), - hasUnaryOperand(cxxThisExpr()) - ))))))))))))))))))))); + testImport("void declToImport() { int b; switch (b) { case 1: break; } }", + Lang_CXX, "", Lang_CXX, Verifier, + functionDecl(hasBody(compoundStmt( + has(switchStmt(has(compoundStmt(has(caseStmt())))))))))); } TEST(ImportExpr, ImportStmtExpr) { @@ -489,5 +486,172 @@ TEST(ImportType, ImportAtomicType) { } +TEST(ImportType, ImportTypeAliasTemplate) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE(testImport("template <int K>" + "struct dummy { static const int i = K; };" + "template <int K> using dummy2 = dummy<K>;" + "int declToImport() { return dummy2<3>::i; }", + Lang_CXX11, "", Lang_CXX11, Verifier, + functionDecl( + hasBody( + compoundStmt( + has( + returnStmt( + has( + implicitCastExpr( + has( + declRefExpr())))))))))); +} + +TEST(ImportDecl, ImportFunctionTemplateDecl) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE(testImport("template <typename T> void declToImport() { };", + Lang_CXX, "", Lang_CXX, Verifier, + functionTemplateDecl())); +} + +const internal::VariadicDynCastAllOfMatcher<Expr, CXXDependentScopeMemberExpr> + cxxDependentScopeMemberExpr; + +TEST(ImportExpr, ImportCXXDependentScopeMemberExpr) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE(testImport("template <typename T> class C { T t; };" + "template <typename T> void declToImport() {" + " C<T> d;" + " d.t;" + "}", + Lang_CXX, "", Lang_CXX, Verifier, + functionTemplateDecl(has(functionDecl(has(compoundStmt( + has(cxxDependentScopeMemberExpr())))))))); + EXPECT_TRUE(testImport("template <typename T> class C { T t; };" + "template <typename T> void declToImport() {" + " C<T> d;" + " (&d)->t;" + "}", + Lang_CXX, "", Lang_CXX, Verifier, + functionTemplateDecl(has(functionDecl(has(compoundStmt( + has(cxxDependentScopeMemberExpr())))))))); +} + +TEST(ImportType, ImportPackExpansion) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE(testImport("template <typename... Args>" + "struct dummy {" + " dummy(Args... args) {}" + " static const int i = 4;" + "};" + "int declToImport() { return dummy<int>::i; }", + Lang_CXX11, "", Lang_CXX11, Verifier, + functionDecl( + hasBody( + compoundStmt( + has( + returnStmt( + has( + implicitCastExpr( + has( + declRefExpr())))))))))); +} + +/// \brief Matches __builtin_types_compatible_p: +/// GNU extension to check equivalent types +/// Given +/// \code +/// __builtin_types_compatible_p(int, int) +/// \endcode +// will generate TypeTraitExpr <...> 'int' +const internal::VariadicDynCastAllOfMatcher<Stmt, TypeTraitExpr> typeTraitExpr; + +TEST(ImportExpr, ImportTypeTraitExpr) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE(testImport("void declToImport() { " + " __builtin_types_compatible_p(int, int);" + "}", + Lang_C, "", Lang_C, Verifier, + functionDecl( + hasBody( + compoundStmt( + has( + typeTraitExpr(hasType(asString("int"))))))))); +} + +TEST(ImportExpr, ImportTypeTraitExprValDep) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE(testImport("template<typename T> struct declToImport {" + " void m() { __is_pod(T); }" + "};" + "void f() { declToImport<int>().m(); }", + Lang_CXX11, "", Lang_CXX11, Verifier, + classTemplateDecl( + has( + cxxRecordDecl( + has( + functionDecl( + hasBody( + compoundStmt( + has( + typeTraitExpr( + hasType(booleanType()) + ))))))))))); +} + +const internal::VariadicDynCastAllOfMatcher<Expr, CXXPseudoDestructorExpr> + cxxPseudoDestructorExpr; + +TEST(ImportExpr, ImportCXXPseudoDestructorExpr) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE( + testImport("typedef int T;" + "void declToImport(int *p) {" + " T t;" + " p->T::~T();" + "}", + Lang_CXX, "", Lang_CXX, Verifier, + functionDecl(has(compoundStmt(has( + callExpr(has(cxxPseudoDestructorExpr())))))))); +} + +TEST(ImportDecl, ImportUsingDecl) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE( + testImport( + "namespace foo { int bar; }" + "int declToImport(){ using foo::bar; }", + Lang_CXX, "", Lang_CXX, Verifier, + functionDecl( + has( + compoundStmt( + has( + declStmt( + has( + usingDecl())))))))); +} + +/// \brief Matches shadow declarations introduced into a scope by a +/// (resolved) using declaration. +/// +/// Given +/// \code +/// namespace n { int f; } +/// namespace declToImport { using n::f; } +/// \endcode +/// usingShadowDecl() +/// matches \code f \endcode +const internal::VariadicDynCastAllOfMatcher<Decl, + UsingShadowDecl> usingShadowDecl; + +TEST(ImportDecl, ImportUsingShadowDecl) { + MatchVerifier<Decl> Verifier; + EXPECT_TRUE( + testImport( + "namespace foo { int bar; }" + "namespace declToImport { using foo::bar; }", + Lang_CXX, "", Lang_CXX, Verifier, + namespaceDecl( + has( + usingShadowDecl())))); +} + } // end namespace ast_matchers } // end namespace clang diff --git a/unittests/AST/CMakeLists.txt b/unittests/AST/CMakeLists.txt index a7008f3e7e092..9839cdb1f2ec2 100644 --- a/unittests/AST/CMakeLists.txt +++ b/unittests/AST/CMakeLists.txt @@ -9,6 +9,7 @@ add_clang_unittest(ASTTests ASTVectorTest.cpp CommentLexer.cpp CommentParser.cpp + DataCollectionTest.cpp DeclPrinterTest.cpp DeclTest.cpp EvaluateAsRValueTest.cpp @@ -20,6 +21,7 @@ add_clang_unittest(ASTTests ) target_link_libraries(ASTTests + PRIVATE clangAST clangASTMatchers clangBasic diff --git a/unittests/AST/DataCollectionTest.cpp b/unittests/AST/DataCollectionTest.cpp new file mode 100644 index 0000000000000..e8ebd16217f43 --- /dev/null +++ b/unittests/AST/DataCollectionTest.cpp @@ -0,0 +1,173 @@ +//===- unittests/AST/DataCollectionTest.cpp -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains tests for the DataCollection module. +// +// They work by hashing the collected data of two nodes and asserting that the +// hash values are equal iff the nodes are considered equal. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DataCollection.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +using namespace clang; +using namespace tooling; +using namespace ast_matchers; + +namespace { +class StmtDataCollector : public ConstStmtVisitor<StmtDataCollector> { + ASTContext &Context; + llvm::MD5 &DataConsumer; + + template <class T> void addData(const T &Data) { + data_collection::addDataToConsumer(DataConsumer, Data); + } + +public: + StmtDataCollector(const Stmt *S, ASTContext &Context, llvm::MD5 &DataConsumer) + : Context(Context), DataConsumer(DataConsumer) { + this->Visit(S); + } + +#define DEF_ADD_DATA(CLASS, CODE) \ + template <class Dummy = void> Dummy Visit##CLASS(const CLASS *S) { \ + CODE; \ + ConstStmtVisitor<StmtDataCollector>::Visit##CLASS(S); \ + } + +#include "clang/AST/StmtDataCollectors.inc" +}; +} // end anonymous namespace + +namespace { +struct StmtHashMatch : public MatchFinder::MatchCallback { + unsigned NumFound; + llvm::MD5::MD5Result &Hash; + StmtHashMatch(llvm::MD5::MD5Result &Hash) : NumFound(0), Hash(Hash) {} + + void run(const MatchFinder::MatchResult &Result) override { + const Stmt *S = Result.Nodes.getNodeAs<Stmt>("id"); + if (!S) + return; + ++NumFound; + if (NumFound > 1) + return; + llvm::MD5 MD5; + StmtDataCollector(S, *Result.Context, MD5); + MD5.final(Hash); + } +}; +} // end anonymous namespace + +static testing::AssertionResult hashStmt(llvm::MD5::MD5Result &Hash, + const StatementMatcher &StmtMatch, + StringRef Code) { + StmtHashMatch Hasher(Hash); + MatchFinder Finder; + Finder.addMatcher(StmtMatch, &Hasher); + std::unique_ptr<FrontendActionFactory> Factory( + newFrontendActionFactory(&Finder)); + if (!runToolOnCode(Factory->create(), Code)) + return testing::AssertionFailure() + << "Parsing error in \"" << Code.str() << "\""; + if (Hasher.NumFound == 0) + return testing::AssertionFailure() << "Matcher didn't find any statements"; + if (Hasher.NumFound > 1) + return testing::AssertionFailure() + << "Matcher should match only one statement " + "(found " + << Hasher.NumFound << ")"; + return testing::AssertionSuccess(); +} + +static testing::AssertionResult +isStmtHashEqual(const StatementMatcher &StmtMatch, StringRef Code1, + StringRef Code2) { + llvm::MD5::MD5Result Hash1, Hash2; + testing::AssertionResult Result = hashStmt(Hash1, StmtMatch, Code1); + if (!Result) + return Result; + if (!(Result = hashStmt(Hash2, StmtMatch, Code2))) + return Result; + + return testing::AssertionResult(Hash1 == Hash2); +} + +TEST(StmtDataCollector, TestDeclRefExpr) { + ASSERT_TRUE(isStmtHashEqual(declRefExpr().bind("id"), "int x, r = x;", + "int x, r = x;")); + ASSERT_FALSE(isStmtHashEqual(declRefExpr().bind("id"), "int x, r = x;", + "int y, r = y;")); + ASSERT_FALSE(isStmtHashEqual(declRefExpr().bind("id"), "int x, r = x;", + "namespace n { int x, r = x; };")); +} + +TEST(StmtDataCollector, TestMemberExpr) { + ASSERT_TRUE(isStmtHashEqual(memberExpr().bind("id"), + "struct { int x; } X; int r = X.x;", + "struct { int x; } X; int r = (&X)->x;")); + ASSERT_TRUE(isStmtHashEqual(memberExpr().bind("id"), + "struct { int x; } X; int r = X.x;", + "struct { int x; } Y; int r = Y.x;")); + ASSERT_TRUE(isStmtHashEqual(memberExpr().bind("id"), + "struct { int x; } X; int r = X.x;", + "struct C { int x; } X; int r = X.C::x;")); + ASSERT_FALSE(isStmtHashEqual(memberExpr().bind("id"), + "struct { int x; } X; int r = X.x;", + "struct { int y; } X; int r = X.y;")); +} + +TEST(StmtDataCollector, TestIntegerLiteral) { + ASSERT_TRUE( + isStmtHashEqual(integerLiteral().bind("id"), "int x = 0;", "int x = 0;")); + ASSERT_TRUE( + isStmtHashEqual(integerLiteral().bind("id"), "int x = 0;", "int x =00;")); + ASSERT_FALSE( + isStmtHashEqual(integerLiteral().bind("id"), "int x = 0;", "int x = 1;")); +} + +TEST(StmtDataCollector, TestFloatingLiteral) { + ASSERT_TRUE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .0;", + "double x = .0;")); + ASSERT_TRUE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .10;", + "double x = .1;")); + ASSERT_TRUE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .1;", + "double x = 1e-1;")); + ASSERT_FALSE(isStmtHashEqual(floatLiteral().bind("id"), "double x = .0;", + "double x = .1;")); +} + +TEST(StmtDataCollector, TestStringLiteral) { + ASSERT_TRUE(isStmtHashEqual(stringLiteral().bind("id"), R"(char x[] = "0";)", + R"(char x[] = "0";)")); + ASSERT_FALSE(isStmtHashEqual(stringLiteral().bind("id"), R"(char x[] = "0";)", + R"(char x[] = "1";)")); +} + +TEST(StmtDataCollector, TestCXXBoolLiteral) { + ASSERT_TRUE(isStmtHashEqual(cxxBoolLiteral().bind("id"), "bool x = false;", + "bool x = false;")); + ASSERT_FALSE(isStmtHashEqual(cxxBoolLiteral().bind("id"), "bool x = false;", + "bool x = true;")); +} + +TEST(StmtDataCollector, TestCharacterLiteral) { + ASSERT_TRUE(isStmtHashEqual(characterLiteral().bind("id"), "char x = '0';", + "char x = '0';")); + ASSERT_TRUE(isStmtHashEqual(characterLiteral().bind("id"), + R"(char x = '\0';)", + R"(char x = '\x00';)")); + ASSERT_FALSE(isStmtHashEqual(characterLiteral().bind("id"), "char x = '0';", + "char x = '1';")); +} diff --git a/unittests/AST/DeclPrinterTest.cpp b/unittests/AST/DeclPrinterTest.cpp index ae6d0f0dd2e23..4cf8bce20ea29 100644 --- a/unittests/AST/DeclPrinterTest.cpp +++ b/unittests/AST/DeclPrinterTest.cpp @@ -31,18 +31,25 @@ using namespace tooling; namespace { -void PrintDecl(raw_ostream &Out, const ASTContext *Context, const Decl *D) { +using PrintingPolicyModifier = void (*)(PrintingPolicy &policy); + +void PrintDecl(raw_ostream &Out, const ASTContext *Context, const Decl *D, + PrintingPolicyModifier PolicyModifier) { PrintingPolicy Policy = Context->getPrintingPolicy(); Policy.TerseOutput = true; + if (PolicyModifier) + PolicyModifier(Policy); D->print(Out, Policy, /*Indentation*/ 0, /*PrintInstantiation*/ false); } class PrintMatch : public MatchFinder::MatchCallback { SmallString<1024> Printed; unsigned NumFoundDecls; + PrintingPolicyModifier PolicyModifier; public: - PrintMatch() : NumFoundDecls(0) {} + PrintMatch(PrintingPolicyModifier PolicyModifier) + : NumFoundDecls(0), PolicyModifier(PolicyModifier) {} void run(const MatchFinder::MatchResult &Result) override { const Decl *D = Result.Nodes.getNodeAs<Decl>("id"); @@ -53,7 +60,7 @@ public: return; llvm::raw_svector_ostream Out(Printed); - PrintDecl(Out, Result.Context, D); + PrintDecl(Out, Result.Context, D, PolicyModifier); } StringRef getPrinted() const { @@ -65,13 +72,12 @@ public: } }; -::testing::AssertionResult PrintedDeclMatches( - StringRef Code, - const std::vector<std::string> &Args, - const DeclarationMatcher &NodeMatch, - StringRef ExpectedPrinted, - StringRef FileName) { - PrintMatch Printer; +::testing::AssertionResult +PrintedDeclMatches(StringRef Code, const std::vector<std::string> &Args, + const DeclarationMatcher &NodeMatch, + StringRef ExpectedPrinted, StringRef FileName, + PrintingPolicyModifier PolicyModifier = nullptr) { + PrintMatch Printer(PolicyModifier); MatchFinder Finder; Finder.addMatcher(NodeMatch, &Printer); std::unique_ptr<FrontendActionFactory> Factory( @@ -98,27 +104,30 @@ public: return ::testing::AssertionSuccess(); } -::testing::AssertionResult PrintedDeclCXX98Matches(StringRef Code, - StringRef DeclName, - StringRef ExpectedPrinted) { +::testing::AssertionResult +PrintedDeclCXX98Matches(StringRef Code, StringRef DeclName, + StringRef ExpectedPrinted, + PrintingPolicyModifier PolicyModifier = nullptr) { std::vector<std::string> Args(1, "-std=c++98"); return PrintedDeclMatches(Code, Args, namedDecl(hasName(DeclName)).bind("id"), ExpectedPrinted, - "input.cc"); + "input.cc", + PolicyModifier); } -::testing::AssertionResult PrintedDeclCXX98Matches( - StringRef Code, - const DeclarationMatcher &NodeMatch, - StringRef ExpectedPrinted) { +::testing::AssertionResult +PrintedDeclCXX98Matches(StringRef Code, const DeclarationMatcher &NodeMatch, + StringRef ExpectedPrinted, + PrintingPolicyModifier PolicyModifier = nullptr) { std::vector<std::string> Args(1, "-std=c++98"); return PrintedDeclMatches(Code, Args, NodeMatch, ExpectedPrinted, - "input.cc"); + "input.cc", + PolicyModifier); } ::testing::AssertionResult PrintedDeclCXX11Matches(StringRef Code, @@ -343,6 +352,47 @@ TEST(DeclPrinter, TestFunctionDecl1) { "void A()")); } +TEST(DeclPrinter, TestFreeFunctionDecl_FullyQualifiedName) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "void A();", + "A", + "void A()", + [](PrintingPolicy &Policy){ Policy.FullyQualifiedName = true; })); +} + +TEST(DeclPrinter, TestFreeFunctionDeclInNamespace_FullyQualifiedName) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "namespace X { void A(); };", + "A", + "void X::A()", + [](PrintingPolicy &Policy){ Policy.FullyQualifiedName = true; })); +} + +TEST(DeclPrinter, TestMemberFunction_FullyQualifiedName) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct X { void A(); };", + "A", + "void X::A()", + [](PrintingPolicy &Policy){ Policy.FullyQualifiedName = true; })); +} + +TEST(DeclPrinter, TestMemberFunctionInNamespace_FullyQualifiedName) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "namespace Z { struct X { void A(); }; }", + "A", + "void Z::X::A()", + [](PrintingPolicy &Policy){ Policy.FullyQualifiedName = true; })); +} + +TEST(DeclPrinter, TestMemberFunctionOutside_FullyQualifiedName) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct X { void A(); };" + "void X::A() {}", + functionDecl(hasName("A"), isDefinition()).bind("id"), + "void X::A()", + [](PrintingPolicy &Policy){ Policy.FullyQualifiedName = true; })); +} + TEST(DeclPrinter, TestFunctionDecl2) { ASSERT_TRUE(PrintedDeclCXX98Matches( "void A() {}", @@ -478,6 +528,27 @@ TEST(DeclPrinter, TestCXXConstructorDecl4) { "A(const A &a, int = 0)")); } +TEST(DeclPrinter, TestCXXConstructorDeclWithMemberInitializer) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " int m;" + " A() : m(2) {}" + "};", + cxxConstructorDecl(ofClass(hasName("A"))).bind("id"), + "A()")); +} + +TEST(DeclPrinter, TestCXXConstructorDeclWithMemberInitializer_NoTerseOutput) { + ASSERT_TRUE(PrintedDeclCXX98Matches( + "struct A {" + " int m;" + " A() : m(2) {}" + "};", + cxxConstructorDecl(ofClass(hasName("A"))).bind("id"), + "A() : m(2) {\n}\n", + [](PrintingPolicy &Policy){ Policy.TerseOutput = false; })); +} + TEST(DeclPrinter, TestCXXConstructorDecl5) { ASSERT_TRUE(PrintedDeclCXX11Matches( "struct A {" @@ -540,7 +611,7 @@ TEST(DeclPrinter, TestCXXConstructorDecl11) { " A(T&&... ts) : T(ts)... {}" "};", cxxConstructorDecl(ofClass(hasName("A"))).bind("id"), - "A<T...>(T &&...ts) : T(ts)... {}")); + "A<T...>(T &&...ts)")); } TEST(DeclPrinter, TestCXXDestructorDecl1) { diff --git a/unittests/AST/StmtPrinterTest.cpp b/unittests/AST/StmtPrinterTest.cpp index 12b203236c996..a0644401a76ab 100644 --- a/unittests/AST/StmtPrinterTest.cpp +++ b/unittests/AST/StmtPrinterTest.cpp @@ -31,18 +31,26 @@ using namespace tooling; namespace { -void PrintStmt(raw_ostream &Out, const ASTContext *Context, const Stmt *S) { +using PolicyAdjusterType = + Optional<llvm::function_ref<void(PrintingPolicy &Policy)>>; + +void PrintStmt(raw_ostream &Out, const ASTContext *Context, const Stmt *S, + PolicyAdjusterType PolicyAdjuster) { assert(S != nullptr && "Expected non-null Stmt"); PrintingPolicy Policy = Context->getPrintingPolicy(); + if (PolicyAdjuster) + (*PolicyAdjuster)(Policy); S->printPretty(Out, /*Helper*/ nullptr, Policy); } class PrintMatch : public MatchFinder::MatchCallback { SmallString<1024> Printed; unsigned NumFoundStmts; + PolicyAdjusterType PolicyAdjuster; public: - PrintMatch() : NumFoundStmts(0) {} + PrintMatch(PolicyAdjusterType PolicyAdjuster) + : NumFoundStmts(0), PolicyAdjuster(PolicyAdjuster) {} void run(const MatchFinder::MatchResult &Result) override { const Stmt *S = Result.Nodes.getNodeAs<Stmt>("id"); @@ -53,7 +61,7 @@ public: return; llvm::raw_svector_ostream Out(Printed); - PrintStmt(Out, Result.Context, S); + PrintStmt(Out, Result.Context, S, PolicyAdjuster); } StringRef getPrinted() const { @@ -68,9 +76,10 @@ public: template <typename T> ::testing::AssertionResult PrintedStmtMatches(StringRef Code, const std::vector<std::string> &Args, - const T &NodeMatch, StringRef ExpectedPrinted) { + const T &NodeMatch, StringRef ExpectedPrinted, + PolicyAdjusterType PolicyAdjuster = None) { - PrintMatch Printer; + PrintMatch Printer(PolicyAdjuster); MatchFinder Finder; Finder.addMatcher(NodeMatch, &Printer); std::unique_ptr<FrontendActionFactory> Factory( @@ -122,11 +131,13 @@ PrintedStmtCXX98Matches(StringRef Code, const StatementMatcher &NodeMatch, ::testing::AssertionResult PrintedStmtCXX11Matches(StringRef Code, const StatementMatcher &NodeMatch, - StringRef ExpectedPrinted) { + StringRef ExpectedPrinted, + PolicyAdjusterType PolicyAdjuster = None) { std::vector<std::string> Args; Args.push_back("-std=c++11"); Args.push_back("-Wno-unused-value"); - return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted); + return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted, + PolicyAdjuster); } ::testing::AssertionResult PrintedStmtMSMatches( @@ -146,6 +157,17 @@ PrintedStmtCXX11Matches(StringRef Code, const StatementMatcher &NodeMatch, ExpectedPrinted); } +::testing::AssertionResult +PrintedStmtObjCMatches(StringRef Code, const StatementMatcher &NodeMatch, + StringRef ExpectedPrinted, + PolicyAdjusterType PolicyAdjuster = None) { + std::vector<std::string> Args; + Args.push_back("-ObjC"); + Args.push_back("-fobjc-runtime=macosx-10.12.0"); + return PrintedStmtMatches(Code, Args, NodeMatch, ExpectedPrinted, + PolicyAdjuster); +} + } // unnamed namespace TEST(StmtPrinter, TestIntegerLiteral) { @@ -214,3 +236,41 @@ TEST(StmtPrinter, TestCXXConversionDeclExplicit) { "(a & b)")); // WRONG; Should be: (a & b).operator void *() } + +TEST(StmtPrinter, TestNoImplicitBases) { + const char *CPPSource = R"( +class A { + int field; + int member() { return field; } +}; +)"; + // No implicit 'this'. + ASSERT_TRUE(PrintedStmtCXX11Matches( + CPPSource, memberExpr(anything()).bind("id"), "field", + PolicyAdjusterType( + [](PrintingPolicy &PP) { PP.SuppressImplicitBase = true; }))); + // Print implicit 'this'. + ASSERT_TRUE(PrintedStmtCXX11Matches( + CPPSource, memberExpr(anything()).bind("id"), "this->field")); + + const char *ObjCSource = R"( +@interface I { + int ivar; +} +@end +@implementation I +- (int) method { + return ivar; +} +@end + )"; + // No implicit 'self'. + ASSERT_TRUE(PrintedStmtObjCMatches(ObjCSource, returnStmt().bind("id"), + "return ivar;\n", + PolicyAdjusterType([](PrintingPolicy &PP) { + PP.SuppressImplicitBase = true; + }))); + // Print implicit 'self'. + ASSERT_TRUE(PrintedStmtObjCMatches(ObjCSource, returnStmt().bind("id"), + "return self->ivar;\n")); +} |