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