llvm-project/clang/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp
Simon Tatham 21401a7262 [clang] Introduce SourceLocation::[U]IntTy typedefs.
This is part of a patch series working towards the ability to make
SourceLocation into a 64-bit type to handle larger translation units.

NFC: this patch introduces typedefs for the integer type used by
SourceLocation and makes all the boring changes to use the typedefs
everywhere, but for the moment, they are unconditionally defined to
uint32_t.

Patch originally by Mikhail Maltsev.

Reviewed By: tmatheson

Differential Revision: https://reviews.llvm.org/D105492
2021-07-21 10:45:46 +01:00

250 lines
7.0 KiB
C++

//===-- TransEmptyStatementsAndDealloc.cpp - Transformations to ARC mode --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// removeEmptyStatementsAndDealloc:
//
// Removes empty statements that are leftovers from previous transformations.
// e.g for
//
// [x retain];
//
// removeRetainReleaseDealloc will leave an empty ";" that removeEmptyStatements
// will remove.
//
//===----------------------------------------------------------------------===//
#include "Transforms.h"
#include "Internals.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/SourceManager.h"
using namespace clang;
using namespace arcmt;
using namespace trans;
static bool isEmptyARCMTMacroStatement(NullStmt *S,
std::vector<SourceLocation> &MacroLocs,
ASTContext &Ctx) {
if (!S->hasLeadingEmptyMacro())
return false;
SourceLocation SemiLoc = S->getSemiLoc();
if (SemiLoc.isInvalid() || SemiLoc.isMacroID())
return false;
if (MacroLocs.empty())
return false;
SourceManager &SM = Ctx.getSourceManager();
std::vector<SourceLocation>::iterator I = llvm::upper_bound(
MacroLocs, SemiLoc, BeforeThanCompare<SourceLocation>(SM));
--I;
SourceLocation
AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size());
assert(AfterMacroLoc.isFileID());
if (AfterMacroLoc == SemiLoc)
return true;
SourceLocation::IntTy RelOffs = 0;
if (!SM.isInSameSLocAddrSpace(AfterMacroLoc, SemiLoc, &RelOffs))
return false;
if (RelOffs < 0)
return false;
// We make the reasonable assumption that a semicolon after 100 characters
// means that it is not the next token after our macro. If this assumption
// fails it is not critical, we will just fail to clear out, e.g., an empty
// 'if'.
if (RelOffs - getARCMTMacroName().size() > 100)
return false;
SourceLocation AfterMacroSemiLoc = findSemiAfterLocation(AfterMacroLoc, Ctx);
return AfterMacroSemiLoc == SemiLoc;
}
namespace {
/// Returns true if the statement became empty due to previous
/// transformations.
class EmptyChecker : public StmtVisitor<EmptyChecker, bool> {
ASTContext &Ctx;
std::vector<SourceLocation> &MacroLocs;
public:
EmptyChecker(ASTContext &ctx, std::vector<SourceLocation> &macroLocs)
: Ctx(ctx), MacroLocs(macroLocs) { }
bool VisitNullStmt(NullStmt *S) {
return isEmptyARCMTMacroStatement(S, MacroLocs, Ctx);
}
bool VisitCompoundStmt(CompoundStmt *S) {
if (S->body_empty())
return false; // was already empty, not because of transformations.
for (auto *I : S->body())
if (!Visit(I))
return false;
return true;
}
bool VisitIfStmt(IfStmt *S) {
if (S->getConditionVariable())
return false;
Expr *condE = S->getCond();
if (!condE)
return false;
if (hasSideEffects(condE, Ctx))
return false;
if (!S->getThen() || !Visit(S->getThen()))
return false;
return !S->getElse() || Visit(S->getElse());
}
bool VisitWhileStmt(WhileStmt *S) {
if (S->getConditionVariable())
return false;
Expr *condE = S->getCond();
if (!condE)
return false;
if (hasSideEffects(condE, Ctx))
return false;
if (!S->getBody())
return false;
return Visit(S->getBody());
}
bool VisitDoStmt(DoStmt *S) {
Expr *condE = S->getCond();
if (!condE)
return false;
if (hasSideEffects(condE, Ctx))
return false;
if (!S->getBody())
return false;
return Visit(S->getBody());
}
bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
Expr *Exp = S->getCollection();
if (!Exp)
return false;
if (hasSideEffects(Exp, Ctx))
return false;
if (!S->getBody())
return false;
return Visit(S->getBody());
}
bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) {
if (!S->getSubStmt())
return false;
return Visit(S->getSubStmt());
}
};
class EmptyStatementsRemover :
public RecursiveASTVisitor<EmptyStatementsRemover> {
MigrationPass &Pass;
public:
EmptyStatementsRemover(MigrationPass &pass) : Pass(pass) { }
bool TraverseStmtExpr(StmtExpr *E) {
CompoundStmt *S = E->getSubStmt();
for (CompoundStmt::body_iterator
I = S->body_begin(), E = S->body_end(); I != E; ++I) {
if (I != E - 1)
check(*I);
TraverseStmt(*I);
}
return true;
}
bool VisitCompoundStmt(CompoundStmt *S) {
for (auto *I : S->body())
check(I);
return true;
}
ASTContext &getContext() { return Pass.Ctx; }
private:
void check(Stmt *S) {
if (!S) return;
if (EmptyChecker(Pass.Ctx, Pass.ARCMTMacroLocs).Visit(S)) {
Transaction Trans(Pass.TA);
Pass.TA.removeStmt(S);
}
}
};
} // anonymous namespace
static bool isBodyEmpty(CompoundStmt *body, ASTContext &Ctx,
std::vector<SourceLocation> &MacroLocs) {
for (auto *I : body->body())
if (!EmptyChecker(Ctx, MacroLocs).Visit(I))
return false;
return true;
}
static void cleanupDeallocOrFinalize(MigrationPass &pass) {
ASTContext &Ctx = pass.Ctx;
TransformActions &TA = pass.TA;
DeclContext *DC = Ctx.getTranslationUnitDecl();
Selector FinalizeSel =
Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize"));
typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl>
impl_iterator;
for (impl_iterator I = impl_iterator(DC->decls_begin()),
E = impl_iterator(DC->decls_end()); I != E; ++I) {
ObjCMethodDecl *DeallocM = nullptr;
ObjCMethodDecl *FinalizeM = nullptr;
for (auto *MD : I->instance_methods()) {
if (!MD->hasBody())
continue;
if (MD->getMethodFamily() == OMF_dealloc) {
DeallocM = MD;
} else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {
FinalizeM = MD;
}
}
if (DeallocM) {
if (isBodyEmpty(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) {
Transaction Trans(TA);
TA.remove(DeallocM->getSourceRange());
}
if (FinalizeM) {
Transaction Trans(TA);
TA.remove(FinalizeM->getSourceRange());
}
} else if (FinalizeM) {
if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) {
Transaction Trans(TA);
TA.remove(FinalizeM->getSourceRange());
} else {
Transaction Trans(TA);
TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc");
}
}
}
}
void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) {
EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
cleanupDeallocOrFinalize(pass);
for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) {
Transaction Trans(pass.TA);
pass.TA.remove(pass.ARCMTMacroLocs[i]);
}
}