1 //===--- Transforms.cpp - Transformations to ARC mode ---------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
21 using namespace clang
;
22 using namespace arcmt
;
23 using namespace trans
;
25 ASTTraverser::~ASTTraverser() { }
27 bool MigrationPass::CFBridgingFunctionsDefined() {
28 if (!EnableCFBridgeFns
)
29 EnableCFBridgeFns
= SemaRef
.isKnownName("CFBridgingRetain") &&
30 SemaRef
.isKnownName("CFBridgingRelease");
31 return *EnableCFBridgeFns
;
34 //===----------------------------------------------------------------------===//
36 //===----------------------------------------------------------------------===//
38 bool trans::canApplyWeak(ASTContext
&Ctx
, QualType type
,
39 bool AllowOnUnknownClass
) {
40 if (!Ctx
.getLangOpts().ObjCWeakRuntime
)
47 // iOS is always safe to use 'weak'.
48 if (Ctx
.getTargetInfo().getTriple().isiOS() ||
49 Ctx
.getTargetInfo().getTriple().isWatchOS())
50 AllowOnUnknownClass
= true;
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())
67 bool trans::isPlusOneAssign(const BinaryOperator
*E
) {
68 if (E
->getOpcode() != BO_Assign
)
71 return isPlusOne(E
->getRHS());
74 bool trans::isPlusOne(const Expr
*E
) {
77 if (const FullExpr
*FE
= dyn_cast
<FullExpr
>(E
))
80 if (const ObjCMessageExpr
*
81 ME
= dyn_cast
<ObjCMessageExpr
>(E
->IgnoreParenCasts()))
82 if (ME
->getMethodFamily() == OMF_retain
)
86 callE
= dyn_cast
<CallExpr
>(E
->IgnoreParenCasts())) {
87 if (const FunctionDecl
*FD
= callE
->getDirectCallee()) {
88 if (FD
->hasAttr
<CFReturnsRetainedAttr
>())
92 FD
->getIdentifier() &&
93 FD
->getParent()->isTranslationUnit() &&
94 FD
->isExternallyVisible() &&
95 ento::cocoa::isRefType(callE
->getType(), "CF",
96 FD
->getIdentifier()->getName())) {
97 StringRef fname
= FD
->getIdentifier()->getName();
98 if (fname
.endswith("Retain") || fname
.contains("Create") ||
99 fname
.contains("Copy"))
105 const ImplicitCastExpr
*implCE
= dyn_cast
<ImplicitCastExpr
>(E
);
106 while (implCE
&& implCE
->getCastKind() == CK_BitCast
)
107 implCE
= dyn_cast
<ImplicitCastExpr
>(implCE
->getSubExpr());
109 return implCE
&& implCE
->getCastKind() == CK_ARCConsumeObject
;
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(1);
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
,
131 SourceManager
&SM
= Ctx
.getSourceManager();
132 if (loc
.isMacroID()) {
133 if (!Lexer::isAtEndOfMacroExpansion(loc
, SM
, Ctx
.getLangOpts(), &loc
))
134 return SourceLocation();
136 loc
= Lexer::getLocForEndOfToken(loc
, /*Offset=*/0, SM
, Ctx
.getLangOpts());
138 // Break down the source location.
139 std::pair
<FileID
, unsigned> locInfo
= SM
.getDecomposedLoc(loc
);
141 // Try to load the file buffer.
142 bool invalidTemp
= false;
143 StringRef file
= SM
.getBufferData(locInfo
.first
, &invalidTemp
);
145 return SourceLocation();
147 const char *tokenBegin
= file
.data() + locInfo
.second
;
149 // Lex from the start of the given location.
150 Lexer
lexer(SM
.getLocForStartOfFile(locInfo
.first
),
152 file
.begin(), tokenBegin
, file
.end());
154 lexer
.LexFromRawLexer(tok
);
155 if (tok
.isNot(tok::semi
)) {
157 return SourceLocation();
158 // Declaration may be followed with other tokens; such as an __attribute,
159 // before ending with a semicolon.
160 return findSemiAfterLocation(tok
.getLocation(), Ctx
, /*IsDecl*/true);
163 return tok
.getLocation();
166 bool trans::hasSideEffects(Expr
*E
, ASTContext
&Ctx
) {
167 if (!E
|| !E
->HasSideEffects(Ctx
))
170 E
= E
->IgnoreParenCasts();
171 ObjCMessageExpr
*ME
= dyn_cast
<ObjCMessageExpr
>(E
);
174 switch (ME
->getMethodFamily()) {
175 case OMF_autorelease
:
179 switch (ME
->getReceiverKind()) {
180 case ObjCMessageExpr::SuperInstance
:
182 case ObjCMessageExpr::Instance
:
183 return hasSideEffects(ME
->getInstanceReceiver(), Ctx
);
195 bool trans::isGlobalVar(Expr
*E
) {
196 E
= E
->IgnoreParenCasts();
197 if (DeclRefExpr
*DRE
= dyn_cast
<DeclRefExpr
>(E
))
198 return DRE
->getDecl()->getDeclContext()->isFileContext() &&
199 DRE
->getDecl()->isExternallyVisible();
200 if (ConditionalOperator
*condOp
= dyn_cast
<ConditionalOperator
>(E
))
201 return isGlobalVar(condOp
->getTrueExpr()) &&
202 isGlobalVar(condOp
->getFalseExpr());
207 StringRef
trans::getNilString(MigrationPass
&Pass
) {
208 return Pass
.SemaRef
.PP
.isMacroDefined("nil") ? "nil" : "0";
213 class ReferenceClear
: public RecursiveASTVisitor
<ReferenceClear
> {
216 ReferenceClear(ExprSet
&refs
) : Refs(refs
) { }
217 bool VisitDeclRefExpr(DeclRefExpr
*E
) { Refs
.erase(E
); return true; }
220 class ReferenceCollector
: public RecursiveASTVisitor
<ReferenceCollector
> {
225 ReferenceCollector(ValueDecl
*D
, ExprSet
&refs
)
226 : Dcl(D
), Refs(refs
) { }
228 bool VisitDeclRefExpr(DeclRefExpr
*E
) {
229 if (E
->getDecl() == Dcl
)
235 class RemovablesCollector
: public RecursiveASTVisitor
<RemovablesCollector
> {
239 RemovablesCollector(ExprSet
&removables
)
240 : Removables(removables
) { }
242 bool shouldWalkTypesOfTypeLocs() const { return false; }
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
) {
255 bool VisitCompoundStmt(CompoundStmt
*S
) {
256 for (auto *I
: S
->body())
261 bool VisitIfStmt(IfStmt
*S
) {
267 bool VisitWhileStmt(WhileStmt
*S
) {
272 bool VisitDoStmt(DoStmt
*S
) {
277 bool VisitForStmt(ForStmt
*S
) {
288 while (auto *Label
= dyn_cast
<LabelStmt
>(S
))
289 S
= Label
->getSubStmt();
290 if (auto *E
= dyn_cast
<Expr
>(S
))
291 S
= E
->IgnoreImplicit();
292 if (auto *E
= dyn_cast
<Expr
>(S
))
293 Removables
.insert(E
);
297 } // end anonymous namespace
299 void trans::clearRefsIn(Stmt
*S
, ExprSet
&refs
) {
300 ReferenceClear(refs
).TraverseStmt(S
);
303 void trans::collectRefs(ValueDecl
*D
, Stmt
*S
, ExprSet
&refs
) {
304 ReferenceCollector(D
, refs
).TraverseStmt(S
);
307 void trans::collectRemovables(Stmt
*S
, ExprSet
&exprs
) {
308 RemovablesCollector(exprs
).TraverseStmt(S
);
311 //===----------------------------------------------------------------------===//
313 //===----------------------------------------------------------------------===//
317 class ASTTransform
: public RecursiveASTVisitor
<ASTTransform
> {
318 MigrationContext
&MigrateCtx
;
319 typedef RecursiveASTVisitor
<ASTTransform
> base
;
322 ASTTransform(MigrationContext
&MigrateCtx
) : MigrateCtx(MigrateCtx
) { }
324 bool shouldWalkTypesOfTypeLocs() const { return false; }
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
);
333 return base::TraverseObjCImplementationDecl(D
);
336 bool TraverseStmt(Stmt
*rootS
) {
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
);
352 MigrationContext::~MigrationContext() {
353 for (traverser_iterator
354 I
= traversers_begin(), E
= traversers_end(); I
!= E
; ++I
)
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();
365 if (T
->isArrayType())
366 T
= Pass
.Ctx
.getBaseElementType(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();
378 bool MigrationContext::rewritePropertyAttribute(StringRef fromAttr
,
380 SourceLocation atLoc
) {
381 if (atLoc
.isMacroID())
384 SourceManager
&SM
= Pass
.Ctx
.getSourceManager();
386 // Break down the source location.
387 std::pair
<FileID
, unsigned> locInfo
= SM
.getDecomposedLoc(atLoc
);
389 // Try to load the file buffer.
390 bool invalidTemp
= false;
391 StringRef file
= SM
.getBufferData(locInfo
.first
, &invalidTemp
);
395 const char *tokenBegin
= file
.data() + locInfo
.second
;
397 // Lex from the start of the given location.
398 Lexer
lexer(SM
.getLocForStartOfFile(locInfo
.first
),
399 Pass
.Ctx
.getLangOpts(),
400 file
.begin(), tokenBegin
, file
.end());
402 lexer
.LexFromRawLexer(tok
);
403 if (tok
.isNot(tok::at
)) return false;
404 lexer
.LexFromRawLexer(tok
);
405 if (tok
.isNot(tok::raw_identifier
)) return false;
406 if (tok
.getRawIdentifier() != "property")
408 lexer
.LexFromRawLexer(tok
);
409 if (tok
.isNot(tok::l_paren
)) return false;
411 Token BeforeTok
= tok
;
413 AfterTok
.startToken();
414 SourceLocation AttrLoc
;
416 lexer
.LexFromRawLexer(tok
);
417 if (tok
.is(tok::r_paren
))
421 if (tok
.isNot(tok::raw_identifier
)) return false;
422 if (tok
.getRawIdentifier() == fromAttr
) {
423 if (!toAttr
.empty()) {
424 Pass
.TA
.replaceText(tok
.getLocation(), fromAttr
, toAttr
);
427 // We want to remove the attribute.
428 AttrLoc
= tok
.getLocation();
432 lexer
.LexFromRawLexer(tok
);
433 if (AttrLoc
.isValid() && AfterTok
.is(tok::unknown
))
435 } while (tok
.isNot(tok::comma
) && tok
.isNot(tok::r_paren
));
436 if (tok
.is(tok::r_paren
))
438 if (AttrLoc
.isInvalid())
440 lexer
.LexFromRawLexer(tok
);
443 if (toAttr
.empty() && AttrLoc
.isValid() && AfterTok
.isNot(tok::unknown
)) {
444 // We want to remove the attribute.
445 if (BeforeTok
.is(tok::l_paren
) && AfterTok
.is(tok::r_paren
)) {
446 Pass
.TA
.remove(SourceRange(BeforeTok
.getLocation(),
447 AfterTok
.getLocation()));
448 } else if (BeforeTok
.is(tok::l_paren
) && AfterTok
.is(tok::comma
)) {
449 Pass
.TA
.remove(SourceRange(AttrLoc
, AfterTok
.getLocation()));
451 Pass
.TA
.remove(SourceRange(BeforeTok
.getLocation(), AttrLoc
));
460 bool MigrationContext::addPropertyAttribute(StringRef attr
,
461 SourceLocation atLoc
) {
462 if (atLoc
.isMacroID())
465 SourceManager
&SM
= Pass
.Ctx
.getSourceManager();
467 // Break down the source location.
468 std::pair
<FileID
, unsigned> locInfo
= SM
.getDecomposedLoc(atLoc
);
470 // Try to load the file buffer.
471 bool invalidTemp
= false;
472 StringRef file
= SM
.getBufferData(locInfo
.first
, &invalidTemp
);
476 const char *tokenBegin
= file
.data() + locInfo
.second
;
478 // Lex from the start of the given location.
479 Lexer
lexer(SM
.getLocForStartOfFile(locInfo
.first
),
480 Pass
.Ctx
.getLangOpts(),
481 file
.begin(), tokenBegin
, file
.end());
483 lexer
.LexFromRawLexer(tok
);
484 if (tok
.isNot(tok::at
)) return false;
485 lexer
.LexFromRawLexer(tok
);
486 if (tok
.isNot(tok::raw_identifier
)) return false;
487 if (tok
.getRawIdentifier() != "property")
489 lexer
.LexFromRawLexer(tok
);
491 if (tok
.isNot(tok::l_paren
)) {
492 Pass
.TA
.insert(tok
.getLocation(), std::string("(") + attr
.str() + ") ");
496 lexer
.LexFromRawLexer(tok
);
497 if (tok
.is(tok::r_paren
)) {
498 Pass
.TA
.insert(tok
.getLocation(), attr
);
502 if (tok
.isNot(tok::raw_identifier
)) return false;
504 Pass
.TA
.insert(tok
.getLocation(), std::string(attr
) + ", ");
508 void MigrationContext::traverse(TranslationUnitDecl
*TU
) {
509 for (traverser_iterator
510 I
= traversers_begin(), E
= traversers_end(); I
!= E
; ++I
)
511 (*I
)->traverseTU(*this);
513 ASTTransform(*this).TraverseDecl(TU
);
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(&pass
.Ctx
.Idents
.get("finalize"));
523 typedef DeclContext::specific_decl_iterator
<ObjCImplementationDecl
>
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()) {
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();
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
);
552 //===----------------------------------------------------------------------===//
553 // getAllTransformations.
554 //===----------------------------------------------------------------------===//
556 static void traverseAST(MigrationPass
&pass
) {
557 MigrationContext
MigrateCtx(pass
);
559 if (pass
.isGCMigration()) {
560 MigrateCtx
.addTraverser(new GCCollectableCallsTraverser
);
561 MigrateCtx
.addTraverser(new GCAttrsTraverser());
563 MigrateCtx
.addTraverser(new PropertyRewriteTraverser());
564 MigrateCtx
.addTraverser(new BlockObjCVariableTraverser());
565 MigrateCtx
.addTraverser(new ProtectedScopeTraverser());
567 MigrateCtx
.traverse(pass
.Ctx
.getTranslationUnitDecl());
570 static void independentTransforms(MigrationPass
&pass
) {
571 rewriteAutoreleasePool(pass
);
572 removeRetainReleaseDeallocFinalize(pass
);
573 rewriteUnusedInitDelegate(pass
);
574 removeZeroOutPropsInDeallocFinalize(pass
);
575 makeAssignARCSafe(pass
);
576 rewriteUnbridgedCasts(pass
);
581 std::vector
<TransformFn
> arcmt::getAllTransformations(
582 LangOptions::GCMode OrigGCMode
,
583 bool NoFinalizeRemoval
) {
584 std::vector
<TransformFn
> transforms
;
586 if (OrigGCMode
== LangOptions::GCOnly
&& NoFinalizeRemoval
)
587 transforms
.push_back(GCRewriteFinalize
);
588 transforms
.push_back(independentTransforms
);
589 // This depends on previous transformations removing various expressions.
590 transforms
.push_back(removeEmptyStatementsAndDeallocFinalize
);