1e5dd7070Spatrick //===--- Transforms.cpp - Transformations to ARC mode ---------------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick
9e5dd7070Spatrick #include "Transforms.h"
10e5dd7070Spatrick #include "Internals.h"
11ec727ea7Spatrick #include "clang/ARCMigrate/ARCMT.h"
12e5dd7070Spatrick #include "clang/AST/ASTContext.h"
13e5dd7070Spatrick #include "clang/AST/RecursiveASTVisitor.h"
14e5dd7070Spatrick #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
15e5dd7070Spatrick #include "clang/Basic/SourceManager.h"
16e5dd7070Spatrick #include "clang/Basic/TargetInfo.h"
17e5dd7070Spatrick #include "clang/Lex/Lexer.h"
18e5dd7070Spatrick #include "clang/Lex/Preprocessor.h"
19e5dd7070Spatrick #include "clang/Sema/Sema.h"
20e5dd7070Spatrick
21e5dd7070Spatrick using namespace clang;
22e5dd7070Spatrick using namespace arcmt;
23e5dd7070Spatrick using namespace trans;
24e5dd7070Spatrick
~ASTTraverser()25e5dd7070Spatrick ASTTraverser::~ASTTraverser() { }
26e5dd7070Spatrick
CFBridgingFunctionsDefined()27e5dd7070Spatrick bool MigrationPass::CFBridgingFunctionsDefined() {
28*12c85518Srobert if (!EnableCFBridgeFns)
29e5dd7070Spatrick EnableCFBridgeFns = SemaRef.isKnownName("CFBridgingRetain") &&
30e5dd7070Spatrick SemaRef.isKnownName("CFBridgingRelease");
31e5dd7070Spatrick return *EnableCFBridgeFns;
32e5dd7070Spatrick }
33e5dd7070Spatrick
34e5dd7070Spatrick //===----------------------------------------------------------------------===//
35e5dd7070Spatrick // Helpers.
36e5dd7070Spatrick //===----------------------------------------------------------------------===//
37e5dd7070Spatrick
canApplyWeak(ASTContext & Ctx,QualType type,bool AllowOnUnknownClass)38e5dd7070Spatrick bool trans::canApplyWeak(ASTContext &Ctx, QualType type,
39e5dd7070Spatrick bool AllowOnUnknownClass) {
40e5dd7070Spatrick if (!Ctx.getLangOpts().ObjCWeakRuntime)
41e5dd7070Spatrick return false;
42e5dd7070Spatrick
43e5dd7070Spatrick QualType T = type;
44e5dd7070Spatrick if (T.isNull())
45e5dd7070Spatrick return false;
46e5dd7070Spatrick
47e5dd7070Spatrick // iOS is always safe to use 'weak'.
48e5dd7070Spatrick if (Ctx.getTargetInfo().getTriple().isiOS() ||
49e5dd7070Spatrick Ctx.getTargetInfo().getTriple().isWatchOS())
50e5dd7070Spatrick AllowOnUnknownClass = true;
51e5dd7070Spatrick
52e5dd7070Spatrick while (const PointerType *ptr = T->getAs<PointerType>())
53e5dd7070Spatrick T = ptr->getPointeeType();
54e5dd7070Spatrick if (const ObjCObjectPointerType *ObjT = T->getAs<ObjCObjectPointerType>()) {
55e5dd7070Spatrick ObjCInterfaceDecl *Class = ObjT->getInterfaceDecl();
56e5dd7070Spatrick if (!AllowOnUnknownClass && (!Class || Class->getName() == "NSObject"))
57e5dd7070Spatrick return false; // id/NSObject is not safe for weak.
58e5dd7070Spatrick if (!AllowOnUnknownClass && !Class->hasDefinition())
59e5dd7070Spatrick return false; // forward classes are not verifiable, therefore not safe.
60e5dd7070Spatrick if (Class && Class->isArcWeakrefUnavailable())
61e5dd7070Spatrick return false;
62e5dd7070Spatrick }
63e5dd7070Spatrick
64e5dd7070Spatrick return true;
65e5dd7070Spatrick }
66e5dd7070Spatrick
isPlusOneAssign(const BinaryOperator * E)67e5dd7070Spatrick bool trans::isPlusOneAssign(const BinaryOperator *E) {
68e5dd7070Spatrick if (E->getOpcode() != BO_Assign)
69e5dd7070Spatrick return false;
70e5dd7070Spatrick
71e5dd7070Spatrick return isPlusOne(E->getRHS());
72e5dd7070Spatrick }
73e5dd7070Spatrick
isPlusOne(const Expr * E)74e5dd7070Spatrick bool trans::isPlusOne(const Expr *E) {
75e5dd7070Spatrick if (!E)
76e5dd7070Spatrick return false;
77e5dd7070Spatrick if (const FullExpr *FE = dyn_cast<FullExpr>(E))
78e5dd7070Spatrick E = FE->getSubExpr();
79e5dd7070Spatrick
80e5dd7070Spatrick if (const ObjCMessageExpr *
81e5dd7070Spatrick ME = dyn_cast<ObjCMessageExpr>(E->IgnoreParenCasts()))
82e5dd7070Spatrick if (ME->getMethodFamily() == OMF_retain)
83e5dd7070Spatrick return true;
84e5dd7070Spatrick
85e5dd7070Spatrick if (const CallExpr *
86e5dd7070Spatrick callE = dyn_cast<CallExpr>(E->IgnoreParenCasts())) {
87e5dd7070Spatrick if (const FunctionDecl *FD = callE->getDirectCallee()) {
88e5dd7070Spatrick if (FD->hasAttr<CFReturnsRetainedAttr>())
89e5dd7070Spatrick return true;
90e5dd7070Spatrick
91e5dd7070Spatrick if (FD->isGlobal() &&
92e5dd7070Spatrick FD->getIdentifier() &&
93e5dd7070Spatrick FD->getParent()->isTranslationUnit() &&
94e5dd7070Spatrick FD->isExternallyVisible() &&
95e5dd7070Spatrick ento::cocoa::isRefType(callE->getType(), "CF",
96e5dd7070Spatrick FD->getIdentifier()->getName())) {
97e5dd7070Spatrick StringRef fname = FD->getIdentifier()->getName();
98*12c85518Srobert if (fname.endswith("Retain") || fname.contains("Create") ||
99*12c85518Srobert fname.contains("Copy"))
100e5dd7070Spatrick return true;
101e5dd7070Spatrick }
102e5dd7070Spatrick }
103e5dd7070Spatrick }
104e5dd7070Spatrick
105e5dd7070Spatrick const ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E);
106e5dd7070Spatrick while (implCE && implCE->getCastKind() == CK_BitCast)
107e5dd7070Spatrick implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
108e5dd7070Spatrick
109e5dd7070Spatrick return implCE && implCE->getCastKind() == CK_ARCConsumeObject;
110e5dd7070Spatrick }
111e5dd7070Spatrick
112e5dd7070Spatrick /// 'Loc' is the end of a statement range. This returns the location
113e5dd7070Spatrick /// immediately after the semicolon following the statement.
114e5dd7070Spatrick /// If no semicolon is found or the location is inside a macro, the returned
115e5dd7070Spatrick /// source location will be invalid.
findLocationAfterSemi(SourceLocation loc,ASTContext & Ctx,bool IsDecl)116e5dd7070Spatrick SourceLocation trans::findLocationAfterSemi(SourceLocation loc,
117e5dd7070Spatrick ASTContext &Ctx, bool IsDecl) {
118e5dd7070Spatrick SourceLocation SemiLoc = findSemiAfterLocation(loc, Ctx, IsDecl);
119e5dd7070Spatrick if (SemiLoc.isInvalid())
120e5dd7070Spatrick return SourceLocation();
121e5dd7070Spatrick return SemiLoc.getLocWithOffset(1);
122e5dd7070Spatrick }
123e5dd7070Spatrick
124e5dd7070Spatrick /// \arg Loc is the end of a statement range. This returns the location
125e5dd7070Spatrick /// of the semicolon following the statement.
126e5dd7070Spatrick /// If no semicolon is found or the location is inside a macro, the returned
127e5dd7070Spatrick /// source location will be invalid.
findSemiAfterLocation(SourceLocation loc,ASTContext & Ctx,bool IsDecl)128e5dd7070Spatrick SourceLocation trans::findSemiAfterLocation(SourceLocation loc,
129e5dd7070Spatrick ASTContext &Ctx,
130e5dd7070Spatrick bool IsDecl) {
131e5dd7070Spatrick SourceManager &SM = Ctx.getSourceManager();
132e5dd7070Spatrick if (loc.isMacroID()) {
133e5dd7070Spatrick if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOpts(), &loc))
134e5dd7070Spatrick return SourceLocation();
135e5dd7070Spatrick }
136e5dd7070Spatrick loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOpts());
137e5dd7070Spatrick
138e5dd7070Spatrick // Break down the source location.
139e5dd7070Spatrick std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
140e5dd7070Spatrick
141e5dd7070Spatrick // Try to load the file buffer.
142e5dd7070Spatrick bool invalidTemp = false;
143e5dd7070Spatrick StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
144e5dd7070Spatrick if (invalidTemp)
145e5dd7070Spatrick return SourceLocation();
146e5dd7070Spatrick
147e5dd7070Spatrick const char *tokenBegin = file.data() + locInfo.second;
148e5dd7070Spatrick
149e5dd7070Spatrick // Lex from the start of the given location.
150e5dd7070Spatrick Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
151e5dd7070Spatrick Ctx.getLangOpts(),
152e5dd7070Spatrick file.begin(), tokenBegin, file.end());
153e5dd7070Spatrick Token tok;
154e5dd7070Spatrick lexer.LexFromRawLexer(tok);
155e5dd7070Spatrick if (tok.isNot(tok::semi)) {
156e5dd7070Spatrick if (!IsDecl)
157e5dd7070Spatrick return SourceLocation();
158e5dd7070Spatrick // Declaration may be followed with other tokens; such as an __attribute,
159e5dd7070Spatrick // before ending with a semicolon.
160e5dd7070Spatrick return findSemiAfterLocation(tok.getLocation(), Ctx, /*IsDecl*/true);
161e5dd7070Spatrick }
162e5dd7070Spatrick
163e5dd7070Spatrick return tok.getLocation();
164e5dd7070Spatrick }
165e5dd7070Spatrick
hasSideEffects(Expr * E,ASTContext & Ctx)166e5dd7070Spatrick bool trans::hasSideEffects(Expr *E, ASTContext &Ctx) {
167e5dd7070Spatrick if (!E || !E->HasSideEffects(Ctx))
168e5dd7070Spatrick return false;
169e5dd7070Spatrick
170e5dd7070Spatrick E = E->IgnoreParenCasts();
171e5dd7070Spatrick ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
172e5dd7070Spatrick if (!ME)
173e5dd7070Spatrick return true;
174e5dd7070Spatrick switch (ME->getMethodFamily()) {
175e5dd7070Spatrick case OMF_autorelease:
176e5dd7070Spatrick case OMF_dealloc:
177e5dd7070Spatrick case OMF_release:
178e5dd7070Spatrick case OMF_retain:
179e5dd7070Spatrick switch (ME->getReceiverKind()) {
180e5dd7070Spatrick case ObjCMessageExpr::SuperInstance:
181e5dd7070Spatrick return false;
182e5dd7070Spatrick case ObjCMessageExpr::Instance:
183e5dd7070Spatrick return hasSideEffects(ME->getInstanceReceiver(), Ctx);
184e5dd7070Spatrick default:
185e5dd7070Spatrick break;
186e5dd7070Spatrick }
187e5dd7070Spatrick break;
188e5dd7070Spatrick default:
189e5dd7070Spatrick break;
190e5dd7070Spatrick }
191e5dd7070Spatrick
192e5dd7070Spatrick return true;
193e5dd7070Spatrick }
194e5dd7070Spatrick
isGlobalVar(Expr * E)195e5dd7070Spatrick bool trans::isGlobalVar(Expr *E) {
196e5dd7070Spatrick E = E->IgnoreParenCasts();
197e5dd7070Spatrick if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
198e5dd7070Spatrick return DRE->getDecl()->getDeclContext()->isFileContext() &&
199e5dd7070Spatrick DRE->getDecl()->isExternallyVisible();
200e5dd7070Spatrick if (ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(E))
201e5dd7070Spatrick return isGlobalVar(condOp->getTrueExpr()) &&
202e5dd7070Spatrick isGlobalVar(condOp->getFalseExpr());
203e5dd7070Spatrick
204e5dd7070Spatrick return false;
205e5dd7070Spatrick }
206e5dd7070Spatrick
getNilString(MigrationPass & Pass)207e5dd7070Spatrick StringRef trans::getNilString(MigrationPass &Pass) {
208e5dd7070Spatrick return Pass.SemaRef.PP.isMacroDefined("nil") ? "nil" : "0";
209e5dd7070Spatrick }
210e5dd7070Spatrick
211e5dd7070Spatrick namespace {
212e5dd7070Spatrick
213e5dd7070Spatrick class ReferenceClear : public RecursiveASTVisitor<ReferenceClear> {
214e5dd7070Spatrick ExprSet &Refs;
215e5dd7070Spatrick public:
ReferenceClear(ExprSet & refs)216e5dd7070Spatrick ReferenceClear(ExprSet &refs) : Refs(refs) { }
VisitDeclRefExpr(DeclRefExpr * E)217e5dd7070Spatrick bool VisitDeclRefExpr(DeclRefExpr *E) { Refs.erase(E); return true; }
218e5dd7070Spatrick };
219e5dd7070Spatrick
220e5dd7070Spatrick class ReferenceCollector : public RecursiveASTVisitor<ReferenceCollector> {
221e5dd7070Spatrick ValueDecl *Dcl;
222e5dd7070Spatrick ExprSet &Refs;
223e5dd7070Spatrick
224e5dd7070Spatrick public:
ReferenceCollector(ValueDecl * D,ExprSet & refs)225e5dd7070Spatrick ReferenceCollector(ValueDecl *D, ExprSet &refs)
226e5dd7070Spatrick : Dcl(D), Refs(refs) { }
227e5dd7070Spatrick
VisitDeclRefExpr(DeclRefExpr * E)228e5dd7070Spatrick bool VisitDeclRefExpr(DeclRefExpr *E) {
229e5dd7070Spatrick if (E->getDecl() == Dcl)
230e5dd7070Spatrick Refs.insert(E);
231e5dd7070Spatrick return true;
232e5dd7070Spatrick }
233e5dd7070Spatrick };
234e5dd7070Spatrick
235e5dd7070Spatrick class RemovablesCollector : public RecursiveASTVisitor<RemovablesCollector> {
236e5dd7070Spatrick ExprSet &Removables;
237e5dd7070Spatrick
238e5dd7070Spatrick public:
RemovablesCollector(ExprSet & removables)239e5dd7070Spatrick RemovablesCollector(ExprSet &removables)
240e5dd7070Spatrick : Removables(removables) { }
241e5dd7070Spatrick
shouldWalkTypesOfTypeLocs() const242e5dd7070Spatrick bool shouldWalkTypesOfTypeLocs() const { return false; }
243e5dd7070Spatrick
TraverseStmtExpr(StmtExpr * E)244e5dd7070Spatrick bool TraverseStmtExpr(StmtExpr *E) {
245e5dd7070Spatrick CompoundStmt *S = E->getSubStmt();
246e5dd7070Spatrick for (CompoundStmt::body_iterator
247e5dd7070Spatrick I = S->body_begin(), E = S->body_end(); I != E; ++I) {
248e5dd7070Spatrick if (I != E - 1)
249e5dd7070Spatrick mark(*I);
250e5dd7070Spatrick TraverseStmt(*I);
251e5dd7070Spatrick }
252e5dd7070Spatrick return true;
253e5dd7070Spatrick }
254e5dd7070Spatrick
VisitCompoundStmt(CompoundStmt * S)255e5dd7070Spatrick bool VisitCompoundStmt(CompoundStmt *S) {
256e5dd7070Spatrick for (auto *I : S->body())
257e5dd7070Spatrick mark(I);
258e5dd7070Spatrick return true;
259e5dd7070Spatrick }
260e5dd7070Spatrick
VisitIfStmt(IfStmt * S)261e5dd7070Spatrick bool VisitIfStmt(IfStmt *S) {
262e5dd7070Spatrick mark(S->getThen());
263e5dd7070Spatrick mark(S->getElse());
264e5dd7070Spatrick return true;
265e5dd7070Spatrick }
266e5dd7070Spatrick
VisitWhileStmt(WhileStmt * S)267e5dd7070Spatrick bool VisitWhileStmt(WhileStmt *S) {
268e5dd7070Spatrick mark(S->getBody());
269e5dd7070Spatrick return true;
270e5dd7070Spatrick }
271e5dd7070Spatrick
VisitDoStmt(DoStmt * S)272e5dd7070Spatrick bool VisitDoStmt(DoStmt *S) {
273e5dd7070Spatrick mark(S->getBody());
274e5dd7070Spatrick return true;
275e5dd7070Spatrick }
276e5dd7070Spatrick
VisitForStmt(ForStmt * S)277e5dd7070Spatrick bool VisitForStmt(ForStmt *S) {
278e5dd7070Spatrick mark(S->getInit());
279e5dd7070Spatrick mark(S->getInc());
280e5dd7070Spatrick mark(S->getBody());
281e5dd7070Spatrick return true;
282e5dd7070Spatrick }
283e5dd7070Spatrick
284e5dd7070Spatrick private:
mark(Stmt * S)285e5dd7070Spatrick void mark(Stmt *S) {
286e5dd7070Spatrick if (!S) return;
287e5dd7070Spatrick
288e5dd7070Spatrick while (auto *Label = dyn_cast<LabelStmt>(S))
289e5dd7070Spatrick S = Label->getSubStmt();
290e5dd7070Spatrick if (auto *E = dyn_cast<Expr>(S))
291e5dd7070Spatrick S = E->IgnoreImplicit();
292e5dd7070Spatrick if (auto *E = dyn_cast<Expr>(S))
293e5dd7070Spatrick Removables.insert(E);
294e5dd7070Spatrick }
295e5dd7070Spatrick };
296e5dd7070Spatrick
297e5dd7070Spatrick } // end anonymous namespace
298e5dd7070Spatrick
clearRefsIn(Stmt * S,ExprSet & refs)299e5dd7070Spatrick void trans::clearRefsIn(Stmt *S, ExprSet &refs) {
300e5dd7070Spatrick ReferenceClear(refs).TraverseStmt(S);
301e5dd7070Spatrick }
302e5dd7070Spatrick
collectRefs(ValueDecl * D,Stmt * S,ExprSet & refs)303e5dd7070Spatrick void trans::collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs) {
304e5dd7070Spatrick ReferenceCollector(D, refs).TraverseStmt(S);
305e5dd7070Spatrick }
306e5dd7070Spatrick
collectRemovables(Stmt * S,ExprSet & exprs)307e5dd7070Spatrick void trans::collectRemovables(Stmt *S, ExprSet &exprs) {
308e5dd7070Spatrick RemovablesCollector(exprs).TraverseStmt(S);
309e5dd7070Spatrick }
310e5dd7070Spatrick
311e5dd7070Spatrick //===----------------------------------------------------------------------===//
312e5dd7070Spatrick // MigrationContext
313e5dd7070Spatrick //===----------------------------------------------------------------------===//
314e5dd7070Spatrick
315e5dd7070Spatrick namespace {
316e5dd7070Spatrick
317e5dd7070Spatrick class ASTTransform : public RecursiveASTVisitor<ASTTransform> {
318e5dd7070Spatrick MigrationContext &MigrateCtx;
319e5dd7070Spatrick typedef RecursiveASTVisitor<ASTTransform> base;
320e5dd7070Spatrick
321e5dd7070Spatrick public:
ASTTransform(MigrationContext & MigrateCtx)322e5dd7070Spatrick ASTTransform(MigrationContext &MigrateCtx) : MigrateCtx(MigrateCtx) { }
323e5dd7070Spatrick
shouldWalkTypesOfTypeLocs() const324e5dd7070Spatrick bool shouldWalkTypesOfTypeLocs() const { return false; }
325e5dd7070Spatrick
TraverseObjCImplementationDecl(ObjCImplementationDecl * D)326e5dd7070Spatrick bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
327e5dd7070Spatrick ObjCImplementationContext ImplCtx(MigrateCtx, D);
328e5dd7070Spatrick for (MigrationContext::traverser_iterator
329e5dd7070Spatrick I = MigrateCtx.traversers_begin(),
330e5dd7070Spatrick E = MigrateCtx.traversers_end(); I != E; ++I)
331e5dd7070Spatrick (*I)->traverseObjCImplementation(ImplCtx);
332e5dd7070Spatrick
333e5dd7070Spatrick return base::TraverseObjCImplementationDecl(D);
334e5dd7070Spatrick }
335e5dd7070Spatrick
TraverseStmt(Stmt * rootS)336e5dd7070Spatrick bool TraverseStmt(Stmt *rootS) {
337e5dd7070Spatrick if (!rootS)
338e5dd7070Spatrick return true;
339e5dd7070Spatrick
340e5dd7070Spatrick BodyContext BodyCtx(MigrateCtx, rootS);
341e5dd7070Spatrick for (MigrationContext::traverser_iterator
342e5dd7070Spatrick I = MigrateCtx.traversers_begin(),
343e5dd7070Spatrick E = MigrateCtx.traversers_end(); I != E; ++I)
344e5dd7070Spatrick (*I)->traverseBody(BodyCtx);
345e5dd7070Spatrick
346e5dd7070Spatrick return true;
347e5dd7070Spatrick }
348e5dd7070Spatrick };
349e5dd7070Spatrick
350e5dd7070Spatrick }
351e5dd7070Spatrick
~MigrationContext()352e5dd7070Spatrick MigrationContext::~MigrationContext() {
353e5dd7070Spatrick for (traverser_iterator
354e5dd7070Spatrick I = traversers_begin(), E = traversers_end(); I != E; ++I)
355e5dd7070Spatrick delete *I;
356e5dd7070Spatrick }
357e5dd7070Spatrick
isGCOwnedNonObjC(QualType T)358e5dd7070Spatrick bool MigrationContext::isGCOwnedNonObjC(QualType T) {
359e5dd7070Spatrick while (!T.isNull()) {
360e5dd7070Spatrick if (const AttributedType *AttrT = T->getAs<AttributedType>()) {
361e5dd7070Spatrick if (AttrT->getAttrKind() == attr::ObjCOwnership)
362e5dd7070Spatrick return !AttrT->getModifiedType()->isObjCRetainableType();
363e5dd7070Spatrick }
364e5dd7070Spatrick
365e5dd7070Spatrick if (T->isArrayType())
366e5dd7070Spatrick T = Pass.Ctx.getBaseElementType(T);
367e5dd7070Spatrick else if (const PointerType *PT = T->getAs<PointerType>())
368e5dd7070Spatrick T = PT->getPointeeType();
369e5dd7070Spatrick else if (const ReferenceType *RT = T->getAs<ReferenceType>())
370e5dd7070Spatrick T = RT->getPointeeType();
371e5dd7070Spatrick else
372e5dd7070Spatrick break;
373e5dd7070Spatrick }
374e5dd7070Spatrick
375e5dd7070Spatrick return false;
376e5dd7070Spatrick }
377e5dd7070Spatrick
rewritePropertyAttribute(StringRef fromAttr,StringRef toAttr,SourceLocation atLoc)378e5dd7070Spatrick bool MigrationContext::rewritePropertyAttribute(StringRef fromAttr,
379e5dd7070Spatrick StringRef toAttr,
380e5dd7070Spatrick SourceLocation atLoc) {
381e5dd7070Spatrick if (atLoc.isMacroID())
382e5dd7070Spatrick return false;
383e5dd7070Spatrick
384e5dd7070Spatrick SourceManager &SM = Pass.Ctx.getSourceManager();
385e5dd7070Spatrick
386e5dd7070Spatrick // Break down the source location.
387e5dd7070Spatrick std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
388e5dd7070Spatrick
389e5dd7070Spatrick // Try to load the file buffer.
390e5dd7070Spatrick bool invalidTemp = false;
391e5dd7070Spatrick StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
392e5dd7070Spatrick if (invalidTemp)
393e5dd7070Spatrick return false;
394e5dd7070Spatrick
395e5dd7070Spatrick const char *tokenBegin = file.data() + locInfo.second;
396e5dd7070Spatrick
397e5dd7070Spatrick // Lex from the start of the given location.
398e5dd7070Spatrick Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
399e5dd7070Spatrick Pass.Ctx.getLangOpts(),
400e5dd7070Spatrick file.begin(), tokenBegin, file.end());
401e5dd7070Spatrick Token tok;
402e5dd7070Spatrick lexer.LexFromRawLexer(tok);
403e5dd7070Spatrick if (tok.isNot(tok::at)) return false;
404e5dd7070Spatrick lexer.LexFromRawLexer(tok);
405e5dd7070Spatrick if (tok.isNot(tok::raw_identifier)) return false;
406e5dd7070Spatrick if (tok.getRawIdentifier() != "property")
407e5dd7070Spatrick return false;
408e5dd7070Spatrick lexer.LexFromRawLexer(tok);
409e5dd7070Spatrick if (tok.isNot(tok::l_paren)) return false;
410e5dd7070Spatrick
411e5dd7070Spatrick Token BeforeTok = tok;
412e5dd7070Spatrick Token AfterTok;
413e5dd7070Spatrick AfterTok.startToken();
414e5dd7070Spatrick SourceLocation AttrLoc;
415e5dd7070Spatrick
416e5dd7070Spatrick lexer.LexFromRawLexer(tok);
417e5dd7070Spatrick if (tok.is(tok::r_paren))
418e5dd7070Spatrick return false;
419e5dd7070Spatrick
420*12c85518Srobert while (true) {
421e5dd7070Spatrick if (tok.isNot(tok::raw_identifier)) return false;
422e5dd7070Spatrick if (tok.getRawIdentifier() == fromAttr) {
423e5dd7070Spatrick if (!toAttr.empty()) {
424e5dd7070Spatrick Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
425e5dd7070Spatrick return true;
426e5dd7070Spatrick }
427e5dd7070Spatrick // We want to remove the attribute.
428e5dd7070Spatrick AttrLoc = tok.getLocation();
429e5dd7070Spatrick }
430e5dd7070Spatrick
431e5dd7070Spatrick do {
432e5dd7070Spatrick lexer.LexFromRawLexer(tok);
433e5dd7070Spatrick if (AttrLoc.isValid() && AfterTok.is(tok::unknown))
434e5dd7070Spatrick AfterTok = tok;
435e5dd7070Spatrick } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
436e5dd7070Spatrick if (tok.is(tok::r_paren))
437e5dd7070Spatrick break;
438e5dd7070Spatrick if (AttrLoc.isInvalid())
439e5dd7070Spatrick BeforeTok = tok;
440e5dd7070Spatrick lexer.LexFromRawLexer(tok);
441e5dd7070Spatrick }
442e5dd7070Spatrick
443e5dd7070Spatrick if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) {
444e5dd7070Spatrick // We want to remove the attribute.
445e5dd7070Spatrick if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) {
446e5dd7070Spatrick Pass.TA.remove(SourceRange(BeforeTok.getLocation(),
447e5dd7070Spatrick AfterTok.getLocation()));
448e5dd7070Spatrick } else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) {
449e5dd7070Spatrick Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation()));
450e5dd7070Spatrick } else {
451e5dd7070Spatrick Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc));
452e5dd7070Spatrick }
453e5dd7070Spatrick
454e5dd7070Spatrick return true;
455e5dd7070Spatrick }
456e5dd7070Spatrick
457e5dd7070Spatrick return false;
458e5dd7070Spatrick }
459e5dd7070Spatrick
addPropertyAttribute(StringRef attr,SourceLocation atLoc)460e5dd7070Spatrick bool MigrationContext::addPropertyAttribute(StringRef attr,
461e5dd7070Spatrick SourceLocation atLoc) {
462e5dd7070Spatrick if (atLoc.isMacroID())
463e5dd7070Spatrick return false;
464e5dd7070Spatrick
465e5dd7070Spatrick SourceManager &SM = Pass.Ctx.getSourceManager();
466e5dd7070Spatrick
467e5dd7070Spatrick // Break down the source location.
468e5dd7070Spatrick std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
469e5dd7070Spatrick
470e5dd7070Spatrick // Try to load the file buffer.
471e5dd7070Spatrick bool invalidTemp = false;
472e5dd7070Spatrick StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
473e5dd7070Spatrick if (invalidTemp)
474e5dd7070Spatrick return false;
475e5dd7070Spatrick
476e5dd7070Spatrick const char *tokenBegin = file.data() + locInfo.second;
477e5dd7070Spatrick
478e5dd7070Spatrick // Lex from the start of the given location.
479e5dd7070Spatrick Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
480e5dd7070Spatrick Pass.Ctx.getLangOpts(),
481e5dd7070Spatrick file.begin(), tokenBegin, file.end());
482e5dd7070Spatrick Token tok;
483e5dd7070Spatrick lexer.LexFromRawLexer(tok);
484e5dd7070Spatrick if (tok.isNot(tok::at)) return false;
485e5dd7070Spatrick lexer.LexFromRawLexer(tok);
486e5dd7070Spatrick if (tok.isNot(tok::raw_identifier)) return false;
487e5dd7070Spatrick if (tok.getRawIdentifier() != "property")
488e5dd7070Spatrick return false;
489e5dd7070Spatrick lexer.LexFromRawLexer(tok);
490e5dd7070Spatrick
491e5dd7070Spatrick if (tok.isNot(tok::l_paren)) {
492e5dd7070Spatrick Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
493e5dd7070Spatrick return true;
494e5dd7070Spatrick }
495e5dd7070Spatrick
496e5dd7070Spatrick lexer.LexFromRawLexer(tok);
497e5dd7070Spatrick if (tok.is(tok::r_paren)) {
498e5dd7070Spatrick Pass.TA.insert(tok.getLocation(), attr);
499e5dd7070Spatrick return true;
500e5dd7070Spatrick }
501e5dd7070Spatrick
502e5dd7070Spatrick if (tok.isNot(tok::raw_identifier)) return false;
503e5dd7070Spatrick
504e5dd7070Spatrick Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
505e5dd7070Spatrick return true;
506e5dd7070Spatrick }
507e5dd7070Spatrick
traverse(TranslationUnitDecl * TU)508e5dd7070Spatrick void MigrationContext::traverse(TranslationUnitDecl *TU) {
509e5dd7070Spatrick for (traverser_iterator
510e5dd7070Spatrick I = traversers_begin(), E = traversers_end(); I != E; ++I)
511e5dd7070Spatrick (*I)->traverseTU(*this);
512e5dd7070Spatrick
513e5dd7070Spatrick ASTTransform(*this).TraverseDecl(TU);
514e5dd7070Spatrick }
515e5dd7070Spatrick
GCRewriteFinalize(MigrationPass & pass)516e5dd7070Spatrick static void GCRewriteFinalize(MigrationPass &pass) {
517e5dd7070Spatrick ASTContext &Ctx = pass.Ctx;
518e5dd7070Spatrick TransformActions &TA = pass.TA;
519e5dd7070Spatrick DeclContext *DC = Ctx.getTranslationUnitDecl();
520e5dd7070Spatrick Selector FinalizeSel =
521e5dd7070Spatrick Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize"));
522e5dd7070Spatrick
523e5dd7070Spatrick typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl>
524e5dd7070Spatrick impl_iterator;
525e5dd7070Spatrick for (impl_iterator I = impl_iterator(DC->decls_begin()),
526e5dd7070Spatrick E = impl_iterator(DC->decls_end()); I != E; ++I) {
527e5dd7070Spatrick for (const auto *MD : I->instance_methods()) {
528e5dd7070Spatrick if (!MD->hasBody())
529e5dd7070Spatrick continue;
530e5dd7070Spatrick
531e5dd7070Spatrick if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {
532e5dd7070Spatrick const ObjCMethodDecl *FinalizeM = MD;
533e5dd7070Spatrick Transaction Trans(TA);
534e5dd7070Spatrick TA.insert(FinalizeM->getSourceRange().getBegin(),
535e5dd7070Spatrick "#if !__has_feature(objc_arc)\n");
536e5dd7070Spatrick CharSourceRange::getTokenRange(FinalizeM->getSourceRange());
537e5dd7070Spatrick const SourceManager &SM = pass.Ctx.getSourceManager();
538e5dd7070Spatrick const LangOptions &LangOpts = pass.Ctx.getLangOpts();
539e5dd7070Spatrick bool Invalid;
540e5dd7070Spatrick std::string str = "\n#endif\n";
541e5dd7070Spatrick str += Lexer::getSourceText(
542e5dd7070Spatrick CharSourceRange::getTokenRange(FinalizeM->getSourceRange()),
543e5dd7070Spatrick SM, LangOpts, &Invalid);
544e5dd7070Spatrick TA.insertAfterToken(FinalizeM->getSourceRange().getEnd(), str);
545e5dd7070Spatrick
546e5dd7070Spatrick break;
547e5dd7070Spatrick }
548e5dd7070Spatrick }
549e5dd7070Spatrick }
550e5dd7070Spatrick }
551e5dd7070Spatrick
552e5dd7070Spatrick //===----------------------------------------------------------------------===//
553e5dd7070Spatrick // getAllTransformations.
554e5dd7070Spatrick //===----------------------------------------------------------------------===//
555e5dd7070Spatrick
traverseAST(MigrationPass & pass)556e5dd7070Spatrick static void traverseAST(MigrationPass &pass) {
557e5dd7070Spatrick MigrationContext MigrateCtx(pass);
558e5dd7070Spatrick
559e5dd7070Spatrick if (pass.isGCMigration()) {
560e5dd7070Spatrick MigrateCtx.addTraverser(new GCCollectableCallsTraverser);
561e5dd7070Spatrick MigrateCtx.addTraverser(new GCAttrsTraverser());
562e5dd7070Spatrick }
563e5dd7070Spatrick MigrateCtx.addTraverser(new PropertyRewriteTraverser());
564e5dd7070Spatrick MigrateCtx.addTraverser(new BlockObjCVariableTraverser());
565e5dd7070Spatrick MigrateCtx.addTraverser(new ProtectedScopeTraverser());
566e5dd7070Spatrick
567e5dd7070Spatrick MigrateCtx.traverse(pass.Ctx.getTranslationUnitDecl());
568e5dd7070Spatrick }
569e5dd7070Spatrick
independentTransforms(MigrationPass & pass)570e5dd7070Spatrick static void independentTransforms(MigrationPass &pass) {
571e5dd7070Spatrick rewriteAutoreleasePool(pass);
572e5dd7070Spatrick removeRetainReleaseDeallocFinalize(pass);
573e5dd7070Spatrick rewriteUnusedInitDelegate(pass);
574e5dd7070Spatrick removeZeroOutPropsInDeallocFinalize(pass);
575e5dd7070Spatrick makeAssignARCSafe(pass);
576e5dd7070Spatrick rewriteUnbridgedCasts(pass);
577e5dd7070Spatrick checkAPIUses(pass);
578e5dd7070Spatrick traverseAST(pass);
579e5dd7070Spatrick }
580e5dd7070Spatrick
getAllTransformations(LangOptions::GCMode OrigGCMode,bool NoFinalizeRemoval)581e5dd7070Spatrick std::vector<TransformFn> arcmt::getAllTransformations(
582e5dd7070Spatrick LangOptions::GCMode OrigGCMode,
583e5dd7070Spatrick bool NoFinalizeRemoval) {
584e5dd7070Spatrick std::vector<TransformFn> transforms;
585e5dd7070Spatrick
586e5dd7070Spatrick if (OrigGCMode == LangOptions::GCOnly && NoFinalizeRemoval)
587e5dd7070Spatrick transforms.push_back(GCRewriteFinalize);
588e5dd7070Spatrick transforms.push_back(independentTransforms);
589e5dd7070Spatrick // This depends on previous transformations removing various expressions.
590e5dd7070Spatrick transforms.push_back(removeEmptyStatementsAndDeallocFinalize);
591e5dd7070Spatrick
592e5dd7070Spatrick return transforms;
593e5dd7070Spatrick }
594