Path: blob/main/contrib/llvm-project/clang/lib/ARCMigrate/Transforms.cpp
35236 views
//===--- Transforms.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//===----------------------------------------------------------------------===//78#include "Transforms.h"9#include "Internals.h"10#include "clang/ARCMigrate/ARCMT.h"11#include "clang/AST/ASTContext.h"12#include "clang/AST/RecursiveASTVisitor.h"13#include "clang/Analysis/DomainSpecific/CocoaConventions.h"14#include "clang/Basic/SourceManager.h"15#include "clang/Basic/TargetInfo.h"16#include "clang/Lex/Lexer.h"17#include "clang/Lex/Preprocessor.h"18#include "clang/Sema/Sema.h"19#include "clang/Sema/SemaObjC.h"2021using namespace clang;22using namespace arcmt;23using namespace trans;2425ASTTraverser::~ASTTraverser() { }2627bool MigrationPass::CFBridgingFunctionsDefined() {28if (!EnableCFBridgeFns)29EnableCFBridgeFns = SemaRef.ObjC().isKnownName("CFBridgingRetain") &&30SemaRef.ObjC().isKnownName("CFBridgingRelease");31return *EnableCFBridgeFns;32}3334//===----------------------------------------------------------------------===//35// Helpers.36//===----------------------------------------------------------------------===//3738bool trans::canApplyWeak(ASTContext &Ctx, QualType type,39bool AllowOnUnknownClass) {40if (!Ctx.getLangOpts().ObjCWeakRuntime)41return false;4243QualType T = type;44if (T.isNull())45return false;4647// iOS is always safe to use 'weak'.48if (Ctx.getTargetInfo().getTriple().isiOS() ||49Ctx.getTargetInfo().getTriple().isWatchOS())50AllowOnUnknownClass = true;5152while (const PointerType *ptr = T->getAs<PointerType>())53T = ptr->getPointeeType();54if (const ObjCObjectPointerType *ObjT = T->getAs<ObjCObjectPointerType>()) {55ObjCInterfaceDecl *Class = ObjT->getInterfaceDecl();56if (!AllowOnUnknownClass && (!Class || Class->getName() == "NSObject"))57return false; // id/NSObject is not safe for weak.58if (!AllowOnUnknownClass && !Class->hasDefinition())59return false; // forward classes are not verifiable, therefore not safe.60if (Class && Class->isArcWeakrefUnavailable())61return false;62}6364return true;65}6667bool trans::isPlusOneAssign(const BinaryOperator *E) {68if (E->getOpcode() != BO_Assign)69return false;7071return isPlusOne(E->getRHS());72}7374bool trans::isPlusOne(const Expr *E) {75if (!E)76return false;77if (const FullExpr *FE = dyn_cast<FullExpr>(E))78E = FE->getSubExpr();7980if (const ObjCMessageExpr *81ME = dyn_cast<ObjCMessageExpr>(E->IgnoreParenCasts()))82if (ME->getMethodFamily() == OMF_retain)83return true;8485if (const CallExpr *86callE = dyn_cast<CallExpr>(E->IgnoreParenCasts())) {87if (const FunctionDecl *FD = callE->getDirectCallee()) {88if (FD->hasAttr<CFReturnsRetainedAttr>())89return true;9091if (FD->isGlobal() &&92FD->getIdentifier() &&93FD->getParent()->isTranslationUnit() &&94FD->isExternallyVisible() &&95ento::cocoa::isRefType(callE->getType(), "CF",96FD->getIdentifier()->getName())) {97StringRef fname = FD->getIdentifier()->getName();98if (fname.ends_with("Retain") || fname.contains("Create") ||99fname.contains("Copy"))100return true;101}102}103}104105const ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E);106while (implCE && implCE->getCastKind() == CK_BitCast)107implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());108109return implCE && implCE->getCastKind() == CK_ARCConsumeObject;110}111112/// 'Loc' is the end of a statement range. This returns the location113/// immediately after the semicolon following the statement.114/// If no semicolon is found or the location is inside a macro, the returned115/// source location will be invalid.116SourceLocation trans::findLocationAfterSemi(SourceLocation loc,117ASTContext &Ctx, bool IsDecl) {118SourceLocation SemiLoc = findSemiAfterLocation(loc, Ctx, IsDecl);119if (SemiLoc.isInvalid())120return SourceLocation();121return SemiLoc.getLocWithOffset(1);122}123124/// \arg Loc is the end of a statement range. This returns the location125/// of the semicolon following the statement.126/// If no semicolon is found or the location is inside a macro, the returned127/// source location will be invalid.128SourceLocation trans::findSemiAfterLocation(SourceLocation loc,129ASTContext &Ctx,130bool IsDecl) {131SourceManager &SM = Ctx.getSourceManager();132if (loc.isMacroID()) {133if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOpts(), &loc))134return SourceLocation();135}136loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOpts());137138// Break down the source location.139std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);140141// Try to load the file buffer.142bool invalidTemp = false;143StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);144if (invalidTemp)145return SourceLocation();146147const char *tokenBegin = file.data() + locInfo.second;148149// Lex from the start of the given location.150Lexer lexer(SM.getLocForStartOfFile(locInfo.first),151Ctx.getLangOpts(),152file.begin(), tokenBegin, file.end());153Token tok;154lexer.LexFromRawLexer(tok);155if (tok.isNot(tok::semi)) {156if (!IsDecl)157return SourceLocation();158// Declaration may be followed with other tokens; such as an __attribute,159// before ending with a semicolon.160return findSemiAfterLocation(tok.getLocation(), Ctx, /*IsDecl*/true);161}162163return tok.getLocation();164}165166bool trans::hasSideEffects(Expr *E, ASTContext &Ctx) {167if (!E || !E->HasSideEffects(Ctx))168return false;169170E = E->IgnoreParenCasts();171ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);172if (!ME)173return true;174switch (ME->getMethodFamily()) {175case OMF_autorelease:176case OMF_dealloc:177case OMF_release:178case OMF_retain:179switch (ME->getReceiverKind()) {180case ObjCMessageExpr::SuperInstance:181return false;182case ObjCMessageExpr::Instance:183return hasSideEffects(ME->getInstanceReceiver(), Ctx);184default:185break;186}187break;188default:189break;190}191192return true;193}194195bool trans::isGlobalVar(Expr *E) {196E = E->IgnoreParenCasts();197if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))198return DRE->getDecl()->getDeclContext()->isFileContext() &&199DRE->getDecl()->isExternallyVisible();200if (ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(E))201return isGlobalVar(condOp->getTrueExpr()) &&202isGlobalVar(condOp->getFalseExpr());203204return false;205}206207StringRef trans::getNilString(MigrationPass &Pass) {208return Pass.SemaRef.PP.isMacroDefined("nil") ? "nil" : "0";209}210211namespace {212213class ReferenceClear : public RecursiveASTVisitor<ReferenceClear> {214ExprSet &Refs;215public:216ReferenceClear(ExprSet &refs) : Refs(refs) { }217bool VisitDeclRefExpr(DeclRefExpr *E) { Refs.erase(E); return true; }218};219220class ReferenceCollector : public RecursiveASTVisitor<ReferenceCollector> {221ValueDecl *Dcl;222ExprSet &Refs;223224public:225ReferenceCollector(ValueDecl *D, ExprSet &refs)226: Dcl(D), Refs(refs) { }227228bool VisitDeclRefExpr(DeclRefExpr *E) {229if (E->getDecl() == Dcl)230Refs.insert(E);231return true;232}233};234235class RemovablesCollector : public RecursiveASTVisitor<RemovablesCollector> {236ExprSet &Removables;237238public:239RemovablesCollector(ExprSet &removables)240: Removables(removables) { }241242bool shouldWalkTypesOfTypeLocs() const { return false; }243244bool TraverseStmtExpr(StmtExpr *E) {245CompoundStmt *S = E->getSubStmt();246for (CompoundStmt::body_iterator247I = S->body_begin(), E = S->body_end(); I != E; ++I) {248if (I != E - 1)249mark(*I);250TraverseStmt(*I);251}252return true;253}254255bool VisitCompoundStmt(CompoundStmt *S) {256for (auto *I : S->body())257mark(I);258return true;259}260261bool VisitIfStmt(IfStmt *S) {262mark(S->getThen());263mark(S->getElse());264return true;265}266267bool VisitWhileStmt(WhileStmt *S) {268mark(S->getBody());269return true;270}271272bool VisitDoStmt(DoStmt *S) {273mark(S->getBody());274return true;275}276277bool VisitForStmt(ForStmt *S) {278mark(S->getInit());279mark(S->getInc());280mark(S->getBody());281return true;282}283284private:285void mark(Stmt *S) {286if (!S) return;287288while (auto *Label = dyn_cast<LabelStmt>(S))289S = Label->getSubStmt();290if (auto *E = dyn_cast<Expr>(S))291S = E->IgnoreImplicit();292if (auto *E = dyn_cast<Expr>(S))293Removables.insert(E);294}295};296297} // end anonymous namespace298299void trans::clearRefsIn(Stmt *S, ExprSet &refs) {300ReferenceClear(refs).TraverseStmt(S);301}302303void trans::collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs) {304ReferenceCollector(D, refs).TraverseStmt(S);305}306307void trans::collectRemovables(Stmt *S, ExprSet &exprs) {308RemovablesCollector(exprs).TraverseStmt(S);309}310311//===----------------------------------------------------------------------===//312// MigrationContext313//===----------------------------------------------------------------------===//314315namespace {316317class ASTTransform : public RecursiveASTVisitor<ASTTransform> {318MigrationContext &MigrateCtx;319typedef RecursiveASTVisitor<ASTTransform> base;320321public:322ASTTransform(MigrationContext &MigrateCtx) : MigrateCtx(MigrateCtx) { }323324bool shouldWalkTypesOfTypeLocs() const { return false; }325326bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {327ObjCImplementationContext ImplCtx(MigrateCtx, D);328for (MigrationContext::traverser_iterator329I = MigrateCtx.traversers_begin(),330E = MigrateCtx.traversers_end(); I != E; ++I)331(*I)->traverseObjCImplementation(ImplCtx);332333return base::TraverseObjCImplementationDecl(D);334}335336bool TraverseStmt(Stmt *rootS) {337if (!rootS)338return true;339340BodyContext BodyCtx(MigrateCtx, rootS);341for (MigrationContext::traverser_iterator342I = MigrateCtx.traversers_begin(),343E = MigrateCtx.traversers_end(); I != E; ++I)344(*I)->traverseBody(BodyCtx);345346return true;347}348};349350}351352MigrationContext::~MigrationContext() {353for (traverser_iterator354I = traversers_begin(), E = traversers_end(); I != E; ++I)355delete *I;356}357358bool MigrationContext::isGCOwnedNonObjC(QualType T) {359while (!T.isNull()) {360if (const AttributedType *AttrT = T->getAs<AttributedType>()) {361if (AttrT->getAttrKind() == attr::ObjCOwnership)362return !AttrT->getModifiedType()->isObjCRetainableType();363}364365if (T->isArrayType())366T = Pass.Ctx.getBaseElementType(T);367else if (const PointerType *PT = T->getAs<PointerType>())368T = PT->getPointeeType();369else if (const ReferenceType *RT = T->getAs<ReferenceType>())370T = RT->getPointeeType();371else372break;373}374375return false;376}377378bool MigrationContext::rewritePropertyAttribute(StringRef fromAttr,379StringRef toAttr,380SourceLocation atLoc) {381if (atLoc.isMacroID())382return false;383384SourceManager &SM = Pass.Ctx.getSourceManager();385386// Break down the source location.387std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);388389// Try to load the file buffer.390bool invalidTemp = false;391StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);392if (invalidTemp)393return false;394395const char *tokenBegin = file.data() + locInfo.second;396397// Lex from the start of the given location.398Lexer lexer(SM.getLocForStartOfFile(locInfo.first),399Pass.Ctx.getLangOpts(),400file.begin(), tokenBegin, file.end());401Token tok;402lexer.LexFromRawLexer(tok);403if (tok.isNot(tok::at)) return false;404lexer.LexFromRawLexer(tok);405if (tok.isNot(tok::raw_identifier)) return false;406if (tok.getRawIdentifier() != "property")407return false;408lexer.LexFromRawLexer(tok);409if (tok.isNot(tok::l_paren)) return false;410411Token BeforeTok = tok;412Token AfterTok;413AfterTok.startToken();414SourceLocation AttrLoc;415416lexer.LexFromRawLexer(tok);417if (tok.is(tok::r_paren))418return false;419420while (true) {421if (tok.isNot(tok::raw_identifier)) return false;422if (tok.getRawIdentifier() == fromAttr) {423if (!toAttr.empty()) {424Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);425return true;426}427// We want to remove the attribute.428AttrLoc = tok.getLocation();429}430431do {432lexer.LexFromRawLexer(tok);433if (AttrLoc.isValid() && AfterTok.is(tok::unknown))434AfterTok = tok;435} while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));436if (tok.is(tok::r_paren))437break;438if (AttrLoc.isInvalid())439BeforeTok = tok;440lexer.LexFromRawLexer(tok);441}442443if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) {444// We want to remove the attribute.445if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) {446Pass.TA.remove(SourceRange(BeforeTok.getLocation(),447AfterTok.getLocation()));448} else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) {449Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation()));450} else {451Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc));452}453454return true;455}456457return false;458}459460bool MigrationContext::addPropertyAttribute(StringRef attr,461SourceLocation atLoc) {462if (atLoc.isMacroID())463return false;464465SourceManager &SM = Pass.Ctx.getSourceManager();466467// Break down the source location.468std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);469470// Try to load the file buffer.471bool invalidTemp = false;472StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);473if (invalidTemp)474return false;475476const char *tokenBegin = file.data() + locInfo.second;477478// Lex from the start of the given location.479Lexer lexer(SM.getLocForStartOfFile(locInfo.first),480Pass.Ctx.getLangOpts(),481file.begin(), tokenBegin, file.end());482Token tok;483lexer.LexFromRawLexer(tok);484if (tok.isNot(tok::at)) return false;485lexer.LexFromRawLexer(tok);486if (tok.isNot(tok::raw_identifier)) return false;487if (tok.getRawIdentifier() != "property")488return false;489lexer.LexFromRawLexer(tok);490491if (tok.isNot(tok::l_paren)) {492Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");493return true;494}495496lexer.LexFromRawLexer(tok);497if (tok.is(tok::r_paren)) {498Pass.TA.insert(tok.getLocation(), attr);499return true;500}501502if (tok.isNot(tok::raw_identifier)) return false;503504Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");505return true;506}507508void MigrationContext::traverse(TranslationUnitDecl *TU) {509for (traverser_iterator510I = traversers_begin(), E = traversers_end(); I != E; ++I)511(*I)->traverseTU(*this);512513ASTTransform(*this).TraverseDecl(TU);514}515516static void GCRewriteFinalize(MigrationPass &pass) {517ASTContext &Ctx = pass.Ctx;518TransformActions &TA = pass.TA;519DeclContext *DC = Ctx.getTranslationUnitDecl();520Selector FinalizeSel =521Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize"));522523typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl>524impl_iterator;525for (impl_iterator I = impl_iterator(DC->decls_begin()),526E = impl_iterator(DC->decls_end()); I != E; ++I) {527for (const auto *MD : I->instance_methods()) {528if (!MD->hasBody())529continue;530531if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {532const ObjCMethodDecl *FinalizeM = MD;533Transaction Trans(TA);534TA.insert(FinalizeM->getSourceRange().getBegin(),535"#if !__has_feature(objc_arc)\n");536CharSourceRange::getTokenRange(FinalizeM->getSourceRange());537const SourceManager &SM = pass.Ctx.getSourceManager();538const LangOptions &LangOpts = pass.Ctx.getLangOpts();539bool Invalid;540std::string str = "\n#endif\n";541str += Lexer::getSourceText(542CharSourceRange::getTokenRange(FinalizeM->getSourceRange()),543SM, LangOpts, &Invalid);544TA.insertAfterToken(FinalizeM->getSourceRange().getEnd(), str);545546break;547}548}549}550}551552//===----------------------------------------------------------------------===//553// getAllTransformations.554//===----------------------------------------------------------------------===//555556static void traverseAST(MigrationPass &pass) {557MigrationContext MigrateCtx(pass);558559if (pass.isGCMigration()) {560MigrateCtx.addTraverser(new GCCollectableCallsTraverser);561MigrateCtx.addTraverser(new GCAttrsTraverser());562}563MigrateCtx.addTraverser(new PropertyRewriteTraverser());564MigrateCtx.addTraverser(new BlockObjCVariableTraverser());565MigrateCtx.addTraverser(new ProtectedScopeTraverser());566567MigrateCtx.traverse(pass.Ctx.getTranslationUnitDecl());568}569570static void independentTransforms(MigrationPass &pass) {571rewriteAutoreleasePool(pass);572removeRetainReleaseDeallocFinalize(pass);573rewriteUnusedInitDelegate(pass);574removeZeroOutPropsInDeallocFinalize(pass);575makeAssignARCSafe(pass);576rewriteUnbridgedCasts(pass);577checkAPIUses(pass);578traverseAST(pass);579}580581std::vector<TransformFn> arcmt::getAllTransformations(582LangOptions::GCMode OrigGCMode,583bool NoFinalizeRemoval) {584std::vector<TransformFn> transforms;585586if (OrigGCMode == LangOptions::GCOnly && NoFinalizeRemoval)587transforms.push_back(GCRewriteFinalize);588transforms.push_back(independentTransforms);589// This depends on previous transformations removing various expressions.590transforms.push_back(removeEmptyStatementsAndDeallocFinalize);591592return transforms;593}594595596