Allow StmtPrinter to supress implicit 'this' and 'self' base expressions

This will be useful for certain refactoring actions.

rdar://34202062

llvm-svn: 316631
This commit is contained in:
Alex Lorenz 2017-10-26 00:56:54 +00:00
parent 5adb96cc92
commit f757961c0f
3 changed files with 105 additions and 18 deletions

View File

@ -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

View File

@ -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())

View File

@ -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"));
}