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