From f19ada140e21a47e9ee3b1ce5e36a2b219cb6f40 Mon Sep 17 00:00:00 2001 From: Alex Lorenz <arphaman@gmail.com> Date: Thu, 26 Oct 2017 00:56:54 +0000 Subject: [PATCH] Allow StmtPrinter to supress implicit 'this' and 'self' base expressions This will be useful for certain refactoring actions. rdar://34202062 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@316631 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/PrettyPrinter.h | 7 ++- lib/AST/StmtPrinter.cpp | 42 ++++++++++++++---- unittests/AST/StmtPrinterTest.cpp | 74 ++++++++++++++++++++++++++++--- 3 files changed, 105 insertions(+), 18 deletions(-) diff --git a/include/clang/AST/PrettyPrinter.h b/include/clang/AST/PrettyPrinter.h index 54fe3983371..953ecada6c0 100644 --- a/include/clang/AST/PrettyPrinter.h +++ b/include/clang/AST/PrettyPrinter.h @@ -51,7 +51,7 @@ struct PrintingPolicy { TerseOutput(false), PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false), - ConstantsAsWritten(false) { } + ConstantsAsWritten(false), SuppressImplicitBase(false) { } /// \brief Adjust this printing policy for cases where it's known that /// we're printing C++ code (for instance, if AST dumping reaches a @@ -218,7 +218,10 @@ struct PrintingPolicy { /// 0x10 /// 2.5e3 /// \endcode - bool ConstantsAsWritten; + bool ConstantsAsWritten : 1; + + /// \brief When true, don't print the implicit 'self' or 'this' expressions. + bool SuppressImplicitBase : 1; }; } // end namespace clang diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 9cbd1ef7602..371d3e181d2 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1346,10 +1346,25 @@ void StmtPrinter::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *Node) { OS, Node->template_arguments(), Policy); } +static bool isImplicitSelf(const Expr *E) { + if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) { + if (const ImplicitParamDecl *PD = + dyn_cast<ImplicitParamDecl>(DRE->getDecl())) { + if (PD->getParameterKind() == ImplicitParamDecl::ObjCSelf && + DRE->getLocStart().isInvalid()) + return true; + } + } + return false; +} + void StmtPrinter::VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node) { if (Node->getBase()) { - PrintExpr(Node->getBase()); - OS << (Node->isArrow() ? "->" : "."); + if (!Policy.SuppressImplicitBase || + !isImplicitSelf(Node->getBase()->IgnoreImpCasts())) { + PrintExpr(Node->getBase()); + OS << (Node->isArrow() ? "->" : "."); + } } OS << *Node->getDecl(); } @@ -1670,16 +1685,25 @@ void StmtPrinter::VisitCallExpr(CallExpr *Call) { PrintCallArgs(Call); OS << ")"; } + +static bool isImplicitThis(const Expr *E) { + if (const auto *TE = dyn_cast<CXXThisExpr>(E)) + return TE->isImplicit(); + return false; +} + void StmtPrinter::VisitMemberExpr(MemberExpr *Node) { - // FIXME: Suppress printing implicit bases (like "this") - PrintExpr(Node->getBase()); + if (!Policy.SuppressImplicitBase || !isImplicitThis(Node->getBase())) { + PrintExpr(Node->getBase()); - MemberExpr *ParentMember = dyn_cast<MemberExpr>(Node->getBase()); - FieldDecl *ParentDecl = ParentMember - ? dyn_cast<FieldDecl>(ParentMember->getMemberDecl()) : nullptr; + MemberExpr *ParentMember = dyn_cast<MemberExpr>(Node->getBase()); + FieldDecl *ParentDecl = + ParentMember ? dyn_cast<FieldDecl>(ParentMember->getMemberDecl()) + : nullptr; - if (!ParentDecl || !ParentDecl->isAnonymousStructOrUnion()) - OS << (Node->isArrow() ? "->" : "."); + if (!ParentDecl || !ParentDecl->isAnonymousStructOrUnion()) + OS << (Node->isArrow() ? "->" : "."); + } if (FieldDecl *FD = dyn_cast<FieldDecl>(Node->getMemberDecl())) if (FD->isAnonymousStructOrUnion()) diff --git a/unittests/AST/StmtPrinterTest.cpp b/unittests/AST/StmtPrinterTest.cpp index 12b203236c9..a0644401a76 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")); +} -- GitLab