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"
20 #include "clang/Sema/SemaObjC.h"
22 using namespace clang
;
23 using namespace arcmt
;
24 using namespace trans
;
26 ASTTraverser::~ASTTraverser() { }
28 bool MigrationPass::CFBridgingFunctionsDefined() {
29 if (!EnableCFBridgeFns
)
30 EnableCFBridgeFns
= SemaRef
.ObjC().isKnownName("CFBridgingRetain") &&
31 SemaRef
.ObjC().isKnownName("CFBridgingRelease");
32 return *EnableCFBridgeFns
;
35 //===----------------------------------------------------------------------===//
37 //===----------------------------------------------------------------------===//
39 bool trans::canApplyWeak(ASTContext
&Ctx
, QualType type
,
40 bool AllowOnUnknownClass
) {
41 if (!Ctx
.getLangOpts().ObjCWeakRuntime
)
48 // iOS is always safe to use 'weak'.
49 if (Ctx
.getTargetInfo().getTriple().isiOS() ||
50 Ctx
.getTargetInfo().getTriple().isWatchOS())
51 AllowOnUnknownClass
= true;
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())
68 bool trans::isPlusOneAssign(const BinaryOperator
*E
) {
69 if (E
->getOpcode() != BO_Assign
)
72 return isPlusOne(E
->getRHS());
75 bool trans::isPlusOne(const Expr
*E
) {
78 if (const FullExpr
*FE
= dyn_cast
<FullExpr
>(E
))
81 if (const ObjCMessageExpr
*
82 ME
= dyn_cast
<ObjCMessageExpr
>(E
->IgnoreParenCasts()))
83 if (ME
->getMethodFamily() == OMF_retain
)
87 callE
= dyn_cast
<CallExpr
>(E
->IgnoreParenCasts())) {
88 if (const FunctionDecl
*FD
= callE
->getDirectCallee()) {
89 if (FD
->hasAttr
<CFReturnsRetainedAttr
>())
93 FD
->getIdentifier() &&
94 FD
->getParent()->isTranslationUnit() &&
95 FD
->isExternallyVisible() &&
96 ento::cocoa::isRefType(callE
->getType(), "CF",
97 FD
->getIdentifier()->getName())) {
98 StringRef fname
= FD
->getIdentifier()->getName();
99 if (fname
.ends_with("Retain") || fname
.contains("Create") ||
100 fname
.contains("Copy"))
106 const ImplicitCastExpr
*implCE
= dyn_cast
<ImplicitCastExpr
>(E
);
107 while (implCE
&& implCE
->getCastKind() == CK_BitCast
)
108 implCE
= dyn_cast
<ImplicitCastExpr
>(implCE
->getSubExpr());
110 return implCE
&& implCE
->getCastKind() == CK_ARCConsumeObject
;
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(1);
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
,
132 SourceManager
&SM
= Ctx
.getSourceManager();
133 if (loc
.isMacroID()) {
134 if (!Lexer::isAtEndOfMacroExpansion(loc
, SM
, Ctx
.getLangOpts(), &loc
))
135 return SourceLocation();
137 loc
= Lexer::getLocForEndOfToken(loc
, /*Offset=*/0, SM
, Ctx
.getLangOpts());
139 // Break down the source location.
140 std::pair
<FileID
, unsigned> locInfo
= SM
.getDecomposedLoc(loc
);
142 // Try to load the file buffer.
143 bool invalidTemp
= false;
144 StringRef file
= SM
.getBufferData(locInfo
.first
, &invalidTemp
);
146 return SourceLocation();
148 const char *tokenBegin
= file
.data() + locInfo
.second
;
150 // Lex from the start of the given location.
151 Lexer
lexer(SM
.getLocForStartOfFile(locInfo
.first
),
153 file
.begin(), tokenBegin
, file
.end());
155 lexer
.LexFromRawLexer(tok
);
156 if (tok
.isNot(tok::semi
)) {
158 return SourceLocation();
159 // Declaration may be followed with other tokens; such as an __attribute,
160 // before ending with a semicolon.
161 return findSemiAfterLocation(tok
.getLocation(), Ctx
, /*IsDecl*/true);
164 return tok
.getLocation();
167 bool trans::hasSideEffects(Expr
*E
, ASTContext
&Ctx
) {
168 if (!E
|| !E
->HasSideEffects(Ctx
))
171 E
= E
->IgnoreParenCasts();
172 ObjCMessageExpr
*ME
= dyn_cast
<ObjCMessageExpr
>(E
);
175 switch (ME
->getMethodFamily()) {
176 case OMF_autorelease
:
180 switch (ME
->getReceiverKind()) {
181 case ObjCMessageExpr::SuperInstance
:
183 case ObjCMessageExpr::Instance
:
184 return hasSideEffects(ME
->getInstanceReceiver(), Ctx
);
196 bool trans::isGlobalVar(Expr
*E
) {
197 E
= E
->IgnoreParenCasts();
198 if (DeclRefExpr
*DRE
= dyn_cast
<DeclRefExpr
>(E
))
199 return DRE
->getDecl()->getDeclContext()->isFileContext() &&
200 DRE
->getDecl()->isExternallyVisible();
201 if (ConditionalOperator
*condOp
= dyn_cast
<ConditionalOperator
>(E
))
202 return isGlobalVar(condOp
->getTrueExpr()) &&
203 isGlobalVar(condOp
->getFalseExpr());
208 StringRef
trans::getNilString(MigrationPass
&Pass
) {
209 return Pass
.SemaRef
.PP
.isMacroDefined("nil") ? "nil" : "0";
214 class ReferenceClear
: public RecursiveASTVisitor
<ReferenceClear
> {
217 ReferenceClear(ExprSet
&refs
) : Refs(refs
) { }
218 bool VisitDeclRefExpr(DeclRefExpr
*E
) { Refs
.erase(E
); return true; }
221 class ReferenceCollector
: public RecursiveASTVisitor
<ReferenceCollector
> {
226 ReferenceCollector(ValueDecl
*D
, ExprSet
&refs
)
227 : Dcl(D
), Refs(refs
) { }
229 bool VisitDeclRefExpr(DeclRefExpr
*E
) {
230 if (E
->getDecl() == Dcl
)
236 class RemovablesCollector
: public RecursiveASTVisitor
<RemovablesCollector
> {
240 RemovablesCollector(ExprSet
&removables
)
241 : Removables(removables
) { }
243 bool shouldWalkTypesOfTypeLocs() const { return false; }
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
) {
256 bool VisitCompoundStmt(CompoundStmt
*S
) {
257 for (auto *I
: S
->body())
262 bool VisitIfStmt(IfStmt
*S
) {
268 bool VisitWhileStmt(WhileStmt
*S
) {
273 bool VisitDoStmt(DoStmt
*S
) {
278 bool VisitForStmt(ForStmt
*S
) {
289 while (auto *Label
= dyn_cast
<LabelStmt
>(S
))
290 S
= Label
->getSubStmt();
291 if (auto *E
= dyn_cast
<Expr
>(S
))
292 S
= E
->IgnoreImplicit();
293 if (auto *E
= dyn_cast
<Expr
>(S
))
294 Removables
.insert(E
);
298 } // end anonymous namespace
300 void trans::clearRefsIn(Stmt
*S
, ExprSet
&refs
) {
301 ReferenceClear(refs
).TraverseStmt(S
);
304 void trans::collectRefs(ValueDecl
*D
, Stmt
*S
, ExprSet
&refs
) {
305 ReferenceCollector(D
, refs
).TraverseStmt(S
);
308 void trans::collectRemovables(Stmt
*S
, ExprSet
&exprs
) {
309 RemovablesCollector(exprs
).TraverseStmt(S
);
312 //===----------------------------------------------------------------------===//
314 //===----------------------------------------------------------------------===//
318 class ASTTransform
: public RecursiveASTVisitor
<ASTTransform
> {
319 MigrationContext
&MigrateCtx
;
320 typedef RecursiveASTVisitor
<ASTTransform
> base
;
323 ASTTransform(MigrationContext
&MigrateCtx
) : MigrateCtx(MigrateCtx
) { }
325 bool shouldWalkTypesOfTypeLocs() const { return false; }
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
);
334 return base::TraverseObjCImplementationDecl(D
);
337 bool TraverseStmt(Stmt
*rootS
) {
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
);
353 MigrationContext::~MigrationContext() {
354 for (traverser_iterator
355 I
= traversers_begin(), E
= traversers_end(); I
!= E
; ++I
)
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();
366 if (T
->isArrayType())
367 T
= Pass
.Ctx
.getBaseElementType(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();
379 bool MigrationContext::rewritePropertyAttribute(StringRef fromAttr
,
381 SourceLocation atLoc
) {
382 if (atLoc
.isMacroID())
385 SourceManager
&SM
= Pass
.Ctx
.getSourceManager();
387 // Break down the source location.
388 std::pair
<FileID
, unsigned> locInfo
= SM
.getDecomposedLoc(atLoc
);
390 // Try to load the file buffer.
391 bool invalidTemp
= false;
392 StringRef file
= SM
.getBufferData(locInfo
.first
, &invalidTemp
);
396 const char *tokenBegin
= file
.data() + locInfo
.second
;
398 // Lex from the start of the given location.
399 Lexer
lexer(SM
.getLocForStartOfFile(locInfo
.first
),
400 Pass
.Ctx
.getLangOpts(),
401 file
.begin(), tokenBegin
, file
.end());
403 lexer
.LexFromRawLexer(tok
);
404 if (tok
.isNot(tok::at
)) return false;
405 lexer
.LexFromRawLexer(tok
);
406 if (tok
.isNot(tok::raw_identifier
)) return false;
407 if (tok
.getRawIdentifier() != "property")
409 lexer
.LexFromRawLexer(tok
);
410 if (tok
.isNot(tok::l_paren
)) return false;
412 Token BeforeTok
= tok
;
414 AfterTok
.startToken();
415 SourceLocation AttrLoc
;
417 lexer
.LexFromRawLexer(tok
);
418 if (tok
.is(tok::r_paren
))
422 if (tok
.isNot(tok::raw_identifier
)) return false;
423 if (tok
.getRawIdentifier() == fromAttr
) {
424 if (!toAttr
.empty()) {
425 Pass
.TA
.replaceText(tok
.getLocation(), fromAttr
, toAttr
);
428 // We want to remove the attribute.
429 AttrLoc
= tok
.getLocation();
433 lexer
.LexFromRawLexer(tok
);
434 if (AttrLoc
.isValid() && AfterTok
.is(tok::unknown
))
436 } while (tok
.isNot(tok::comma
) && tok
.isNot(tok::r_paren
));
437 if (tok
.is(tok::r_paren
))
439 if (AttrLoc
.isInvalid())
441 lexer
.LexFromRawLexer(tok
);
444 if (toAttr
.empty() && AttrLoc
.isValid() && AfterTok
.isNot(tok::unknown
)) {
445 // We want to remove the attribute.
446 if (BeforeTok
.is(tok::l_paren
) && AfterTok
.is(tok::r_paren
)) {
447 Pass
.TA
.remove(SourceRange(BeforeTok
.getLocation(),
448 AfterTok
.getLocation()));
449 } else if (BeforeTok
.is(tok::l_paren
) && AfterTok
.is(tok::comma
)) {
450 Pass
.TA
.remove(SourceRange(AttrLoc
, AfterTok
.getLocation()));
452 Pass
.TA
.remove(SourceRange(BeforeTok
.getLocation(), AttrLoc
));
461 bool MigrationContext::addPropertyAttribute(StringRef attr
,
462 SourceLocation atLoc
) {
463 if (atLoc
.isMacroID())
466 SourceManager
&SM
= Pass
.Ctx
.getSourceManager();
468 // Break down the source location.
469 std::pair
<FileID
, unsigned> locInfo
= SM
.getDecomposedLoc(atLoc
);
471 // Try to load the file buffer.
472 bool invalidTemp
= false;
473 StringRef file
= SM
.getBufferData(locInfo
.first
, &invalidTemp
);
477 const char *tokenBegin
= file
.data() + locInfo
.second
;
479 // Lex from the start of the given location.
480 Lexer
lexer(SM
.getLocForStartOfFile(locInfo
.first
),
481 Pass
.Ctx
.getLangOpts(),
482 file
.begin(), tokenBegin
, file
.end());
484 lexer
.LexFromRawLexer(tok
);
485 if (tok
.isNot(tok::at
)) return false;
486 lexer
.LexFromRawLexer(tok
);
487 if (tok
.isNot(tok::raw_identifier
)) return false;
488 if (tok
.getRawIdentifier() != "property")
490 lexer
.LexFromRawLexer(tok
);
492 if (tok
.isNot(tok::l_paren
)) {
493 Pass
.TA
.insert(tok
.getLocation(), std::string("(") + attr
.str() + ") ");
497 lexer
.LexFromRawLexer(tok
);
498 if (tok
.is(tok::r_paren
)) {
499 Pass
.TA
.insert(tok
.getLocation(), attr
);
503 if (tok
.isNot(tok::raw_identifier
)) return false;
505 Pass
.TA
.insert(tok
.getLocation(), std::string(attr
) + ", ");
509 void MigrationContext::traverse(TranslationUnitDecl
*TU
) {
510 for (traverser_iterator
511 I
= traversers_begin(), E
= traversers_end(); I
!= E
; ++I
)
512 (*I
)->traverseTU(*this);
514 ASTTransform(*this).TraverseDecl(TU
);
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(&pass
.Ctx
.Idents
.get("finalize"));
524 typedef DeclContext::specific_decl_iterator
<ObjCImplementationDecl
>
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()) {
532 if (MD
->isInstanceMethod() && MD
->getSelector() == FinalizeSel
) {
533 const ObjCMethodDecl
*FinalizeM
= MD
;
534 Transaction
Trans(TA
);
535 TA
.insert(FinalizeM
->getSourceRange().getBegin(),
536 "#if !__has_feature(objc_arc)\n");
537 CharSourceRange::getTokenRange(FinalizeM
->getSourceRange());
538 const SourceManager
&SM
= pass
.Ctx
.getSourceManager();
539 const LangOptions
&LangOpts
= pass
.Ctx
.getLangOpts();
541 std::string str
= "\n#endif\n";
542 str
+= Lexer::getSourceText(
543 CharSourceRange::getTokenRange(FinalizeM
->getSourceRange()),
544 SM
, LangOpts
, &Invalid
);
545 TA
.insertAfterToken(FinalizeM
->getSourceRange().getEnd(), str
);
553 //===----------------------------------------------------------------------===//
554 // getAllTransformations.
555 //===----------------------------------------------------------------------===//
557 static void traverseAST(MigrationPass
&pass
) {
558 MigrationContext
MigrateCtx(pass
);
560 if (pass
.isGCMigration()) {
561 MigrateCtx
.addTraverser(new GCCollectableCallsTraverser
);
562 MigrateCtx
.addTraverser(new GCAttrsTraverser());
564 MigrateCtx
.addTraverser(new PropertyRewriteTraverser());
565 MigrateCtx
.addTraverser(new BlockObjCVariableTraverser());
566 MigrateCtx
.addTraverser(new ProtectedScopeTraverser());
568 MigrateCtx
.traverse(pass
.Ctx
.getTranslationUnitDecl());
571 static void independentTransforms(MigrationPass
&pass
) {
572 rewriteAutoreleasePool(pass
);
573 removeRetainReleaseDeallocFinalize(pass
);
574 rewriteUnusedInitDelegate(pass
);
575 removeZeroOutPropsInDeallocFinalize(pass
);
576 makeAssignARCSafe(pass
);
577 rewriteUnbridgedCasts(pass
);
582 std::vector
<TransformFn
> arcmt::getAllTransformations(
583 LangOptions::GCMode OrigGCMode
,
584 bool NoFinalizeRemoval
) {
585 std::vector
<TransformFn
> transforms
;
587 if (OrigGCMode
== LangOptions::GCOnly
&& NoFinalizeRemoval
)
588 transforms
.push_back(GCRewriteFinalize
);
589 transforms
.push_back(independentTransforms
);
590 // This depends on previous transformations removing various expressions.
591 transforms
.push_back(removeEmptyStatementsAndDeallocFinalize
);