blob: 6513d98c9297892d5351e85c604a592716cec1ca [file] [log] [blame]
Argyrios Kyrtzidise5b475c2011-06-21 20:20:39 +00001//===--- TransUnbridgedCasts.cpp - Tranformations to ARC mode -------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// rewriteUnbridgedCasts:
11//
12// A cast of non-objc pointer to an objc one is checked. If the non-objc pointer
13// is from a file-level variable, __bridge cast is used to convert it.
14// For the result of a function call that we know is +1/+0,
15// __bridge/__bridge_transfer is used.
16//
17// NSString *str = (NSString *)kUTTypePlainText;
18// str = b ? kUTTypeRTF : kUTTypePlainText;
19// NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault,
20// _uuid);
21// ---->
22// NSString *str = (__bridge NSString *)kUTTypePlainText;
23// str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText);
24// NSString *_uuidString = (__bridge_transfer NSString *)
25// CFUUIDCreateString(kCFAllocatorDefault, _uuid);
26//
27// For a C pointer to ObjC, for casting 'self', __bridge is used.
28//
29// CFStringRef str = (CFStringRef)self;
30// ---->
31// CFStringRef str = (__bridge CFStringRef)self;
32//
33//===----------------------------------------------------------------------===//
34
35#include "Transforms.h"
36#include "Internals.h"
37#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
38#include "clang/Sema/SemaDiagnostic.h"
39#include "clang/Basic/SourceManager.h"
40
41using namespace clang;
42using namespace arcmt;
43using namespace trans;
44using llvm::StringRef;
45
46namespace {
47
48class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{
49 MigrationPass &Pass;
50 IdentifierInfo *SelfII;
51public:
52 UnbridgedCastRewriter(MigrationPass &pass) : Pass(pass) {
53 SelfII = &Pass.Ctx.Idents.get("self");
54 }
55
56 bool VisitCastExpr(CastExpr *E) {
57 if (E->getCastKind() != CK_AnyPointerToObjCPointerCast
58 && E->getCastKind() != CK_BitCast)
59 return true;
60
61 QualType castType = E->getType();
62 Expr *castExpr = E->getSubExpr();
63 QualType castExprType = castExpr->getType();
64
65 if (castType->isObjCObjectPointerType() &&
66 castExprType->isObjCObjectPointerType())
67 return true;
68 if (!castType->isObjCObjectPointerType() &&
69 !castExprType->isObjCObjectPointerType())
70 return true;
71
72 bool exprRetainable = castExprType->isObjCIndirectLifetimeType();
73 bool castRetainable = castType->isObjCIndirectLifetimeType();
74 if (exprRetainable == castRetainable) return true;
75
76 if (castExpr->isNullPointerConstant(Pass.Ctx,
77 Expr::NPC_ValueDependentIsNull))
78 return true;
79
80 SourceLocation loc = castExpr->getExprLoc();
81 if (loc.isValid() && Pass.Ctx.getSourceManager().isInSystemHeader(loc))
82 return true;
83
84 if (castType->isObjCObjectPointerType())
85 transformNonObjCToObjCCast(E);
86 else
87 transformObjCToNonObjCCast(E);
88
89 return true;
90 }
91
92private:
93 void transformNonObjCToObjCCast(CastExpr *E) {
94 if (!E) return;
95
96 // Global vars are assumed that are cast as unretained.
97 if (isGlobalVar(E))
98 if (E->getSubExpr()->getType()->isPointerType()) {
99 castToObjCObject(E, /*retained=*/false);
100 return;
101 }
102
103 // If the cast is directly over the result of a Core Foundation function
104 // try to figure out whether it should be cast as retained or unretained.
105 Expr *inner = E->IgnoreParenCasts();
106 if (CallExpr *callE = dyn_cast<CallExpr>(inner)) {
107 if (FunctionDecl *FD = callE->getDirectCallee()) {
108 if (FD->getAttr<CFReturnsRetainedAttr>()) {
109 castToObjCObject(E, /*retained=*/true);
110 return;
111 }
112 if (FD->getAttr<CFReturnsNotRetainedAttr>()) {
113 castToObjCObject(E, /*retained=*/false);
114 return;
115 }
116 if (FD->isGlobal() &&
117 FD->getIdentifier() &&
118 ento::cocoa::isRefType(E->getSubExpr()->getType(), "CF",
119 FD->getIdentifier()->getName())) {
120 StringRef fname = FD->getIdentifier()->getName();
121 if (fname.endswith("Retain") ||
122 fname.find("Create") != StringRef::npos ||
123 fname.find("Copy") != StringRef::npos) {
124 castToObjCObject(E, /*retained=*/true);
125 return;
126 }
127
128 if (fname.find("Get") != StringRef::npos) {
129 castToObjCObject(E, /*retained=*/false);
130 return;
131 }
132 }
133 }
134 }
135 }
136
137 void castToObjCObject(CastExpr *E, bool retained) {
138 rewriteToBridgedCast(E, retained ? OBC_BridgeTransfer : OBC_Bridge);
139 }
140
141 void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind) {
142 TransformActions &TA = Pass.TA;
143
144 // We will remove the compiler diagnostic.
145 if (!TA.hasDiagnostic(diag::err_arc_mismatched_cast,
146 diag::err_arc_cast_requires_bridge,
147 E->getLocStart()))
148 return;
149
150 StringRef bridge;
151 switch(Kind) {
152 case OBC_Bridge:
153 bridge = "__bridge "; break;
154 case OBC_BridgeTransfer:
155 bridge = "__bridge_transfer "; break;
156 case OBC_BridgeRetained:
157 bridge = "__bridge_retained "; break;
158 }
159
160 Transaction Trans(TA);
161 TA.clearDiagnostic(diag::err_arc_mismatched_cast,
162 diag::err_arc_cast_requires_bridge,
163 E->getLocStart());
164 if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) {
165 TA.insertAfterToken(CCE->getLParenLoc(), bridge);
166 } else {
167 SourceLocation insertLoc = E->getSubExpr()->getLocStart();
168 llvm::SmallString<128> newCast;
169 newCast += '(';
170 newCast += bridge;
171 newCast += E->getType().getAsString(Pass.Ctx.PrintingPolicy);
172 newCast += ')';
173
174 if (isa<ParenExpr>(E->getSubExpr())) {
175 TA.insert(insertLoc, newCast.str());
176 } else {
177 newCast += '(';
178 TA.insert(insertLoc, newCast.str());
179 TA.insertAfterToken(E->getLocEnd(), ")");
180 }
181 }
182 }
183
184 void transformObjCToNonObjCCast(CastExpr *E) {
185 if (isSelf(E->getSubExpr()))
186 return rewriteToBridgedCast(E, OBC_Bridge);
187 }
188
189 bool isSelf(Expr *E) {
190 E = E->IgnoreParenLValueCasts();
191 if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
192 if (DRE->getDecl()->getIdentifier() == SelfII)
193 return true;
194 return false;
195 }
196
197 static bool isGlobalVar(Expr *E) {
198 E = E->IgnoreParenCasts();
199 if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
200 return DRE->getDecl()->getDeclContext()->isFileContext();
201 if (ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(E))
202 return isGlobalVar(condOp->getTrueExpr()) &&
203 isGlobalVar(condOp->getFalseExpr());
204
205 return false;
206 }
207};
208
209} // end anonymous namespace
210
211void trans::rewriteUnbridgedCasts(MigrationPass &pass) {
212 UnbridgedCastRewriter trans(pass);
213 trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
214}