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