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