Path: blob/main/contrib/llvm-project/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
35236 views
//===--- TransRetainReleaseDealloc.cpp - Transformations to ARC mode ------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// removeRetainReleaseDealloc:9//10// Removes retain/release/autorelease/dealloc messages.11//12// return [[foo retain] autorelease];13// ---->14// return foo;15//16//===----------------------------------------------------------------------===//1718#include "Transforms.h"19#include "Internals.h"20#include "clang/AST/ASTContext.h"21#include "clang/AST/ParentMap.h"22#include "clang/Basic/SourceManager.h"23#include "clang/Lex/Lexer.h"24#include "clang/Sema/SemaDiagnostic.h"25#include "llvm/ADT/StringSwitch.h"2627using namespace clang;28using namespace arcmt;29using namespace trans;3031namespace {3233class RetainReleaseDeallocRemover :34public RecursiveASTVisitor<RetainReleaseDeallocRemover> {35Stmt *Body;36MigrationPass &Pass;3738ExprSet Removables;39std::unique_ptr<ParentMap> StmtMap;4041Selector DelegateSel, FinalizeSel;4243public:44RetainReleaseDeallocRemover(MigrationPass &pass)45: Body(nullptr), Pass(pass) {46DelegateSel =47Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));48FinalizeSel =49Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize"));50}5152void transformBody(Stmt *body, Decl *ParentD) {53Body = body;54collectRemovables(body, Removables);55StmtMap.reset(new ParentMap(body));56TraverseStmt(body);57}5859bool VisitObjCMessageExpr(ObjCMessageExpr *E) {60switch (E->getMethodFamily()) {61default:62if (E->isInstanceMessage() && E->getSelector() == FinalizeSel)63break;64return true;65case OMF_autorelease:66if (isRemovable(E)) {67if (!isCommonUnusedAutorelease(E)) {68// An unused autorelease is badness. If we remove it the receiver69// will likely die immediately while previously it was kept alive70// by the autorelease pool. This is bad practice in general, leave it71// and emit an error to force the user to restructure their code.72Pass.TA.reportError(73"it is not safe to remove an unused 'autorelease' "74"message; its receiver may be destroyed immediately",75E->getBeginLoc(), E->getSourceRange());76return true;77}78}79// Pass through.80[[fallthrough]];81case OMF_retain:82case OMF_release:83if (E->getReceiverKind() == ObjCMessageExpr::Instance)84if (Expr *rec = E->getInstanceReceiver()) {85rec = rec->IgnoreParenImpCasts();86if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone &&87(E->getMethodFamily() != OMF_retain || isRemovable(E))) {88std::string err = "it is not safe to remove '";89err += E->getSelector().getAsString() + "' message on "90"an __unsafe_unretained type";91Pass.TA.reportError(err, rec->getBeginLoc());92return true;93}9495if (isGlobalVar(rec) &&96(E->getMethodFamily() != OMF_retain || isRemovable(E))) {97std::string err = "it is not safe to remove '";98err += E->getSelector().getAsString() + "' message on "99"a global variable";100Pass.TA.reportError(err, rec->getBeginLoc());101return true;102}103104if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) {105Pass.TA.reportError(106"it is not safe to remove 'retain' "107"message on the result of a 'delegate' message; "108"the object that was passed to 'setDelegate:' may not be "109"properly retained",110rec->getBeginLoc());111return true;112}113}114break;115case OMF_dealloc:116break;117}118119switch (E->getReceiverKind()) {120default:121return true;122case ObjCMessageExpr::SuperInstance: {123Transaction Trans(Pass.TA);124clearDiagnostics(E->getSelectorLoc(0));125if (tryRemoving(E))126return true;127Pass.TA.replace(E->getSourceRange(), "self");128return true;129}130case ObjCMessageExpr::Instance:131break;132}133134Expr *rec = E->getInstanceReceiver();135if (!rec) return true;136137Transaction Trans(Pass.TA);138clearDiagnostics(E->getSelectorLoc(0));139140ObjCMessageExpr *Msg = E;141Expr *RecContainer = Msg;142SourceRange RecRange = rec->getSourceRange();143checkForGCDOrXPC(Msg, RecContainer, rec, RecRange);144145if (Msg->getMethodFamily() == OMF_release &&146isRemovable(RecContainer) && isInAtFinally(RecContainer)) {147// Change the -release to "receiver = nil" in a finally to avoid a leak148// when an exception is thrown.149Pass.TA.replace(RecContainer->getSourceRange(), RecRange);150std::string str = " = ";151str += getNilString(Pass);152Pass.TA.insertAfterToken(RecRange.getEnd(), str);153return true;154}155156if (hasSideEffects(rec, Pass.Ctx) || !tryRemoving(RecContainer))157Pass.TA.replace(RecContainer->getSourceRange(), RecRange);158159return true;160}161162private:163/// Checks for idioms where an unused -autorelease is common.164///165/// Returns true for this idiom which is common in property166/// setters:167///168/// [backingValue autorelease];169/// backingValue = [newValue retain]; // in general a +1 assign170///171/// For these as well:172///173/// [[var retain] autorelease];174/// return var;175///176bool isCommonUnusedAutorelease(ObjCMessageExpr *E) {177return isPlusOneAssignBeforeOrAfterAutorelease(E) ||178isReturnedAfterAutorelease(E);179}180181bool isReturnedAfterAutorelease(ObjCMessageExpr *E) {182Expr *Rec = E->getInstanceReceiver();183if (!Rec)184return false;185186Decl *RefD = getReferencedDecl(Rec);187if (!RefD)188return false;189190Stmt *nextStmt = getNextStmt(E);191if (!nextStmt)192return false;193194// Check for "return <variable>;".195196if (ReturnStmt *RetS = dyn_cast<ReturnStmt>(nextStmt))197return RefD == getReferencedDecl(RetS->getRetValue());198199return false;200}201202bool isPlusOneAssignBeforeOrAfterAutorelease(ObjCMessageExpr *E) {203Expr *Rec = E->getInstanceReceiver();204if (!Rec)205return false;206207Decl *RefD = getReferencedDecl(Rec);208if (!RefD)209return false;210211Stmt *prevStmt, *nextStmt;212std::tie(prevStmt, nextStmt) = getPreviousAndNextStmt(E);213214return isPlusOneAssignToVar(prevStmt, RefD) ||215isPlusOneAssignToVar(nextStmt, RefD);216}217218bool isPlusOneAssignToVar(Stmt *S, Decl *RefD) {219if (!S)220return false;221222// Check for "RefD = [+1 retained object];".223224if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(S)) {225return (RefD == getReferencedDecl(Bop->getLHS())) && isPlusOneAssign(Bop);226}227228if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) {229if (DS->isSingleDecl() && DS->getSingleDecl() == RefD) {230if (VarDecl *VD = dyn_cast<VarDecl>(RefD))231return isPlusOne(VD->getInit());232}233return false;234}235236return false;237}238239Stmt *getNextStmt(Expr *E) {240return getPreviousAndNextStmt(E).second;241}242243std::pair<Stmt *, Stmt *> getPreviousAndNextStmt(Expr *E) {244Stmt *prevStmt = nullptr, *nextStmt = nullptr;245if (!E)246return std::make_pair(prevStmt, nextStmt);247248Stmt *OuterS = E, *InnerS;249do {250InnerS = OuterS;251OuterS = StmtMap->getParent(InnerS);252}253while (OuterS && (isa<ParenExpr>(OuterS) ||254isa<CastExpr>(OuterS) ||255isa<FullExpr>(OuterS)));256257if (!OuterS)258return std::make_pair(prevStmt, nextStmt);259260Stmt::child_iterator currChildS = OuterS->child_begin();261Stmt::child_iterator childE = OuterS->child_end();262Stmt::child_iterator prevChildS = childE;263for (; currChildS != childE; ++currChildS) {264if (*currChildS == InnerS)265break;266prevChildS = currChildS;267}268269if (prevChildS != childE) {270prevStmt = *prevChildS;271if (auto *E = dyn_cast_or_null<Expr>(prevStmt))272prevStmt = E->IgnoreImplicit();273}274275if (currChildS == childE)276return std::make_pair(prevStmt, nextStmt);277++currChildS;278if (currChildS == childE)279return std::make_pair(prevStmt, nextStmt);280281nextStmt = *currChildS;282if (auto *E = dyn_cast_or_null<Expr>(nextStmt))283nextStmt = E->IgnoreImplicit();284285return std::make_pair(prevStmt, nextStmt);286}287288Decl *getReferencedDecl(Expr *E) {289if (!E)290return nullptr;291292E = E->IgnoreParenCasts();293if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) {294switch (ME->getMethodFamily()) {295case OMF_copy:296case OMF_autorelease:297case OMF_release:298case OMF_retain:299return getReferencedDecl(ME->getInstanceReceiver());300default:301return nullptr;302}303}304if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))305return DRE->getDecl();306if (MemberExpr *ME = dyn_cast<MemberExpr>(E))307return ME->getMemberDecl();308if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E))309return IRE->getDecl();310311return nullptr;312}313314/// Check if the retain/release is due to a GCD/XPC macro that are315/// defined as:316///317/// #define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })318/// #define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })319/// #define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })320/// #define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })321///322/// and return the top container which is the StmtExpr and the macro argument323/// expression.324void checkForGCDOrXPC(ObjCMessageExpr *Msg, Expr *&RecContainer,325Expr *&Rec, SourceRange &RecRange) {326SourceLocation Loc = Msg->getExprLoc();327if (!Loc.isMacroID())328return;329SourceManager &SM = Pass.Ctx.getSourceManager();330StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM,331Pass.Ctx.getLangOpts());332bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName)333.Case("dispatch_retain", true)334.Case("dispatch_release", true)335.Case("xpc_retain", true)336.Case("xpc_release", true)337.Default(false);338if (!isGCDOrXPC)339return;340341StmtExpr *StmtE = nullptr;342Stmt *S = Msg;343while (S) {344if (StmtExpr *SE = dyn_cast<StmtExpr>(S)) {345StmtE = SE;346break;347}348S = StmtMap->getParent(S);349}350351if (!StmtE)352return;353354Stmt::child_range StmtExprChild = StmtE->children();355if (StmtExprChild.begin() == StmtExprChild.end())356return;357auto *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild.begin());358if (!CompS)359return;360361Stmt::child_range CompStmtChild = CompS->children();362if (CompStmtChild.begin() == CompStmtChild.end())363return;364auto *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild.begin());365if (!DeclS)366return;367if (!DeclS->isSingleDecl())368return;369VarDecl *VD = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl());370if (!VD)371return;372Expr *Init = VD->getInit();373if (!Init)374return;375376RecContainer = StmtE;377Rec = Init->IgnoreParenImpCasts();378if (FullExpr *FE = dyn_cast<FullExpr>(Rec))379Rec = FE->getSubExpr()->IgnoreParenImpCasts();380RecRange = Rec->getSourceRange();381if (SM.isMacroArgExpansion(RecRange.getBegin()))382RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin()));383if (SM.isMacroArgExpansion(RecRange.getEnd()))384RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd()));385}386387void clearDiagnostics(SourceLocation loc) const {388Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message,389diag::err_unavailable,390diag::err_unavailable_message,391loc);392}393394bool isDelegateMessage(Expr *E) const {395if (!E) return false;396397E = E->IgnoreParenCasts();398399// Also look through property-getter sugar.400if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E))401E = pseudoOp->getResultExpr()->IgnoreImplicit();402403if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))404return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);405406return false;407}408409bool isInAtFinally(Expr *E) const {410assert(E);411Stmt *S = E;412while (S) {413if (isa<ObjCAtFinallyStmt>(S))414return true;415S = StmtMap->getParent(S);416}417418return false;419}420421bool isRemovable(Expr *E) const {422return Removables.count(E);423}424425bool tryRemoving(Expr *E) const {426if (isRemovable(E)) {427Pass.TA.removeStmt(E);428return true;429}430431Stmt *parent = StmtMap->getParent(E);432433if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent))434return tryRemoving(castE);435436if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent))437return tryRemoving(parenE);438439if (BinaryOperator *440bopE = dyn_cast_or_null<BinaryOperator>(parent)) {441if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E &&442isRemovable(bopE)) {443Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange());444return true;445}446}447448return false;449}450451};452453} // anonymous namespace454455void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) {456BodyTransform<RetainReleaseDeallocRemover> trans(pass);457trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());458}459460461