Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 18fd0c6

Browse files
committed
[arcmt] More automatic transformations and safety improvements; rdar://9615812 :
- Replace calling -zone with 'nil'. -zone is obsolete in ARC. - Allow removing retain/release on a static global var. - Fix assertion hit when scanning for name references outside a NSAutoreleasePool scope. - Automatically add bridged casts for results of objc method calls and when calling CFRetain, for example: NSString *s; CFStringRef ref = [s string]; -> CFStringRef ref = (__bridge CFStringRef)([s string]); ref = s.string; -> ref = (__bridge CFStringRef)(s.string); ref = [NSString new]; -> ref = (__bridge_retained CFStringRef)([NSString new]); ref = [s newString]; -> ref = (__bridge_retained CFStringRef)([s newString]); ref = [[NSString alloc] init]; -> ref = (__bridge_retained CFStringRef)([[NSString alloc] init]); ref = [[s string] retain]; -> ref = (__bridge_retained CFStringRef)([s string]); ref = CFRetain(s); -> ref = (__bridge_retained CFTypeRef)(s); ref = [s retain]; -> ref = (__bridge_retained CFStringRef)(s); - Emit migrator error when trying to cast to CF type the result of autorelease/release: for CFStringRef f3() { return (CFStringRef)[[[NSString alloc] init] autorelease]; } emits: t.m:12:10: error: [rewriter] it is not safe to cast to 'CFStringRef' the result of 'autorelease' message; a __bridge cast may result in a pointer to a destroyed object and a __bridge_retained may leak the object return (CFStringRef)[[[NSString alloc] init] autorelease]; ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ t.m:12:3: note: [rewriter] remove the cast and change return type of function to 'NSString *' to have the object automatically autoreleased return (CFStringRef)[[[NSString alloc] init] autorelease]; ^ - Before changing attributes to weak/unsafe_unretained, check if the backing ivar is set to a +1 object, in which case use 'strong' instead. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@136208 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 3ef1ad2 commit 18fd0c6

21 files changed

+367
-28
lines changed

include/clang/AST/ParentMap.h

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class ParentMap {
3232
Stmt *getParent(Stmt*) const;
3333
Stmt *getParentIgnoreParens(Stmt *) const;
3434
Stmt *getParentIgnoreParenCasts(Stmt *) const;
35+
Stmt *getParentIgnoreParenImpCasts(Stmt *) const;
3536
Stmt *getOuterParenParent(Stmt *) const;
3637

3738
const Stmt *getParent(const Stmt* S) const {

lib/ARCMigrate/TransAPIUses.cpp

+23-1
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,19 @@
99
//
1010
// checkAPIUses:
1111
//
12-
// Emits error with some API uses that are not safe in ARC mode:
12+
// Emits error/fix with some API uses that are obsolete or not safe in ARC mode:
1313
//
1414
// - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe
1515
// with __unsafe_unretained objects.
1616
// - When a NSData's 'bytes' family of methods are used on a local var,
1717
// add __attribute__((objc_precise_lifetime)) to make it safer.
18+
// - Calling -zone gets replaced with 'nil'.
1819
//
1920
//===----------------------------------------------------------------------===//
2021

2122
#include "Transforms.h"
2223
#include "Internals.h"
24+
#include "clang/Sema/SemaDiagnostic.h"
2325

2426
using namespace clang;
2527
using namespace arcmt;
@@ -35,6 +37,8 @@ class APIChecker : public RecursiveASTVisitor<APIChecker> {
3537

3638
Selector bytesSel, getBytesSel, getBytesLengthSel, getBytesRangeSel;
3739

40+
Selector zoneSel;
41+
3842
llvm::DenseSet<VarDecl *> ChangedNSDataVars;
3943
public:
4044
APIChecker(MigrationPass &pass) : Pass(pass) {
@@ -57,9 +61,12 @@ class APIChecker : public RecursiveASTVisitor<APIChecker> {
5761
getBytesLengthSel = sels.getSelector(2, selIds);
5862
selIds[1] = &ids.get("range");
5963
getBytesRangeSel = sels.getSelector(2, selIds);
64+
65+
zoneSel = sels.getNullarySelector(&ids.get("zone"));
6066
}
6167

6268
bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
69+
// NSInvocation.
6370
if (E->isInstanceMessage() &&
6471
E->getReceiverInterface() &&
6572
E->getReceiverInterface()->getName() == "NSInvocation") {
@@ -91,6 +98,7 @@ class APIChecker : public RecursiveASTVisitor<APIChecker> {
9198
return true;
9299
}
93100

101+
// NSData.
94102
if (E->isInstanceMessage() &&
95103
E->getReceiverInterface() &&
96104
E->getReceiverInterface()->getName() == "NSData" &&
@@ -111,6 +119,20 @@ class APIChecker : public RecursiveASTVisitor<APIChecker> {
111119
}
112120
}
113121

122+
// -zone.
123+
if (E->isInstanceMessage() &&
124+
E->getInstanceReceiver() &&
125+
E->getSelector() == zoneSel &&
126+
Pass.TA.hasDiagnostic(diag::err_unavailable,
127+
diag::err_unavailable_message,
128+
E->getInstanceReceiver()->getExprLoc())) {
129+
// Calling -zone is meaningless in ARC, change it to nil.
130+
Transaction Trans(Pass.TA);
131+
Pass.TA.clearDiagnostic(diag::err_unavailable,
132+
diag::err_unavailable_message,
133+
E->getInstanceReceiver()->getExprLoc());
134+
Pass.TA.replace(E->getSourceRange(), getNilString(Pass.Ctx));
135+
}
114136
return true;
115137
}
116138
};

lib/ARCMigrate/TransAutoreleasePool.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ class AutoreleasePoolRewriter
286286
}
287287

288288
bool isInScope(SourceLocation loc) {
289+
if (loc.isInvalid())
290+
return false;
291+
289292
SourceManager &SM = Ctx.getSourceManager();
290293
if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin()))
291294
return false;

lib/ARCMigrate/TransProperties.cpp

+73-10
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ namespace {
4545

4646
class PropertiesRewriter {
4747
MigrationPass &Pass;
48+
ObjCImplementationDecl *CurImplD;
4849

4950
struct PropData {
5051
ObjCPropertyDecl *PropD;
@@ -62,6 +63,7 @@ class PropertiesRewriter {
6263
PropertiesRewriter(MigrationPass &pass) : Pass(pass) { }
6364

6465
void doTransform(ObjCImplementationDecl *D) {
66+
CurImplD = D;
6567
ObjCInterfaceDecl *iface = D->getClassInterface();
6668
if (!iface)
6769
return;
@@ -134,8 +136,16 @@ class PropertiesRewriter {
134136
return;
135137
}
136138

137-
if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign)
139+
if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
140+
if (hasIvarAssignedAPlusOneObject(props)) {
141+
rewriteAttribute("assign", "strong", atLoc);
142+
return;
143+
}
138144
return rewriteAssign(props, atLoc);
145+
}
146+
147+
if (hasIvarAssignedAPlusOneObject(props))
148+
return maybeAddStrongAttr(props, atLoc);
139149

140150
return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
141151
}
@@ -162,15 +172,15 @@ class PropertiesRewriter {
162172
void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
163173
SourceLocation atLoc) const {
164174
ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
165-
if ((propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) &&
166-
hasNoBackingIvars(props))
167-
return;
168175

169176
bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
170-
bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
171-
atLoc);
172-
if (!addedAttr)
173-
canUseWeak = false;
177+
if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
178+
!hasAllIvarsBacked(props)) {
179+
bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
180+
atLoc);
181+
if (!addedAttr)
182+
canUseWeak = false;
183+
}
174184

175185
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
176186
if (isUserDeclared(I->IvarD))
@@ -186,6 +196,25 @@ class PropertiesRewriter {
186196
}
187197
}
188198

199+
void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const {
200+
ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
201+
202+
if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
203+
!hasAllIvarsBacked(props)) {
204+
addAttribute("strong", atLoc);
205+
}
206+
207+
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
208+
if (I->ImplD) {
209+
Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
210+
I->ImplD->getLocation());
211+
Pass.TA.clearDiagnostic(
212+
diag::err_arc_objc_property_default_assign_on_object,
213+
I->ImplD->getLocation());
214+
}
215+
}
216+
}
217+
189218
bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
190219
SourceLocation atLoc) const {
191220
if (atLoc.isMacroID())
@@ -290,6 +319,40 @@ class PropertiesRewriter {
290319
return true;
291320
}
292321

322+
class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
323+
ObjCIvarDecl *Ivar;
324+
public:
325+
PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
326+
327+
bool VisitBinAssign(BinaryOperator *E) {
328+
Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
329+
if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
330+
if (RE->getDecl() != Ivar)
331+
return true;
332+
333+
ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
334+
while (implCE && implCE->getCastKind() == CK_BitCast)
335+
implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
336+
337+
if (implCE && implCE->getCastKind() == CK_ObjCConsumeObject)
338+
return false;
339+
}
340+
341+
return true;
342+
}
343+
};
344+
345+
bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
346+
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
347+
PlusOneAssign oneAssign(I->IvarD);
348+
bool notFound = oneAssign.TraverseDecl(CurImplD);
349+
if (!notFound)
350+
return true;
351+
}
352+
353+
return false;
354+
}
355+
293356
bool hasIvarWithExplicitOwnership(PropsTy &props) const {
294357
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
295358
if (isUserDeclared(I->IvarD)) {
@@ -304,9 +367,9 @@ class PropertiesRewriter {
304367
return false;
305368
}
306369

307-
bool hasNoBackingIvars(PropsTy &props) const {
370+
bool hasAllIvarsBacked(PropsTy &props) const {
308371
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
309-
if (I->IvarD)
372+
if (!isUserDeclared(I->IvarD))
310373
return false;
311374

312375
return true;

lib/ARCMigrate/TransRetainReleaseDealloc.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,9 @@ class RetainReleaseDeallocRemover :
129129
// Change the -release to "receiver = nil" in a finally to avoid a leak
130130
// when an exception is thrown.
131131
Pass.TA.replace(E->getSourceRange(), rec->getSourceRange());
132-
if (Pass.Ctx.Idents.get("nil").hasMacroDefinition())
133-
Pass.TA.insertAfterToken(rec->getLocEnd(), " = nil");
134-
else
135-
Pass.TA.insertAfterToken(rec->getLocEnd(), " = 0");
132+
std::string str = " = ";
133+
str += getNilString(Pass.Ctx);
134+
Pass.TA.insertAfterToken(rec->getLocEnd(), str);
136135
return true;
137136
}
138137

lib/ARCMigrate/TransUnbridgedCasts.cpp

+88-6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "Internals.h"
3737
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
3838
#include "clang/Sema/SemaDiagnostic.h"
39+
#include "clang/AST/ParentMap.h"
3940
#include "clang/Basic/SourceManager.h"
4041

4142
using namespace clang;
@@ -47,11 +48,18 @@ namespace {
4748
class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{
4849
MigrationPass &Pass;
4950
IdentifierInfo *SelfII;
51+
llvm::OwningPtr<ParentMap> StmtMap;
52+
5053
public:
5154
UnbridgedCastRewriter(MigrationPass &pass) : Pass(pass) {
5255
SelfII = &Pass.Ctx.Idents.get("self");
5356
}
5457

58+
void transformBody(Stmt *body) {
59+
StmtMap.reset(new ParentMap(body));
60+
TraverseStmt(body);
61+
}
62+
5563
bool VisitCastExpr(CastExpr *E) {
5664
if (E->getCastKind() != CK_AnyPointerToObjCPointerCast
5765
&& E->getCastKind() != CK_BitCast)
@@ -138,13 +146,21 @@ class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{
138146
}
139147

140148
void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind) {
149+
Transaction Trans(Pass.TA);
150+
rewriteToBridgedCast(E, Kind, Trans);
151+
}
152+
153+
void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind,
154+
Transaction &Trans) {
141155
TransformActions &TA = Pass.TA;
142156

143157
// We will remove the compiler diagnostic.
144158
if (!TA.hasDiagnostic(diag::err_arc_mismatched_cast,
145159
diag::err_arc_cast_requires_bridge,
146-
E->getLocStart()))
160+
E->getLocStart())) {
161+
Trans.abort();
147162
return;
163+
}
148164

149165
StringRef bridge;
150166
switch(Kind) {
@@ -156,7 +172,6 @@ class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{
156172
bridge = "__bridge_retained "; break;
157173
}
158174

159-
Transaction Trans(TA);
160175
TA.clearDiagnostic(diag::err_arc_mismatched_cast,
161176
diag::err_arc_cast_requires_bridge,
162177
E->getLocStart());
@@ -180,23 +195,90 @@ class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{
180195
}
181196
}
182197

198+
void rewriteCastForCFRetain(CastExpr *castE, CallExpr *callE) {
199+
Transaction Trans(Pass.TA);
200+
Pass.TA.replace(callE->getSourceRange(), callE->getArg(0)->getSourceRange());
201+
rewriteToBridgedCast(castE, OBC_BridgeRetained, Trans);
202+
}
203+
183204
void transformObjCToNonObjCCast(CastExpr *E) {
184205
if (isSelf(E->getSubExpr()))
185206
return rewriteToBridgedCast(E, OBC_Bridge);
207+
208+
CallExpr *callE;
209+
if (isPassedToCFRetain(E, callE))
210+
return rewriteCastForCFRetain(E, callE);
211+
212+
ObjCMethodFamily family = getFamilyOfMessage(E->getSubExpr());
213+
if (family == OMF_retain)
214+
return rewriteToBridgedCast(E, OBC_BridgeRetained);
215+
216+
if (family == OMF_autorelease || family == OMF_release) {
217+
std::string err = "it is not safe to cast to '";
218+
err += E->getType().getAsString(Pass.Ctx.PrintingPolicy);
219+
err += "' the result of '";
220+
err += family == OMF_autorelease ? "autorelease" : "release";
221+
err += "' message; a __bridge cast may result in a pointer to a "
222+
"destroyed object and a __bridge_retained may leak the object";
223+
Pass.TA.reportError(err, E->getLocStart(),
224+
E->getSubExpr()->getSourceRange());
225+
Stmt *parent = E;
226+
do {
227+
parent = StmtMap->getParentIgnoreParenImpCasts(parent);
228+
} while (parent && isa<ExprWithCleanups>(parent));
229+
230+
if (ReturnStmt *retS = dyn_cast_or_null<ReturnStmt>(parent)) {
231+
std::string note = "remove the cast and change return type of function "
232+
"to '";
233+
note += E->getSubExpr()->getType().getAsString(Pass.Ctx.PrintingPolicy);
234+
note += "' to have the object automatically autoreleased";
235+
Pass.TA.reportNote(note, retS->getLocStart());
236+
}
237+
}
238+
239+
if (ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getSubExpr())){
240+
if (implCE->getCastKind() == CK_ObjCConsumeObject)
241+
return rewriteToBridgedCast(E, OBC_BridgeRetained);
242+
if (implCE->getCastKind() == CK_ObjCReclaimReturnedObject)
243+
return rewriteToBridgedCast(E, OBC_Bridge);
244+
}
245+
}
246+
247+
static ObjCMethodFamily getFamilyOfMessage(Expr *E) {
248+
E = E->IgnoreParenCasts();
249+
if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
250+
return ME->getMethodFamily();
251+
252+
return OMF_None;
253+
}
254+
255+
bool isPassedToCFRetain(Expr *E, CallExpr *&callE) const {
256+
if ((callE = dyn_cast_or_null<CallExpr>(
257+
StmtMap->getParentIgnoreParenImpCasts(E))))
258+
if (FunctionDecl *
259+
FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl()))
260+
if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 &&
261+
FD->getParent()->isTranslationUnit() &&
262+
FD->getLinkage() == ExternalLinkage)
263+
return true;
264+
265+
return false;
186266
}
187267

188-
bool isSelf(Expr *E) {
268+
bool isSelf(Expr *E) const {
189269
E = E->IgnoreParenLValueCasts();
190270
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
191-
if (DRE->getDecl()->getIdentifier() == SelfII)
192-
return true;
271+
if (ImplicitParamDecl *IPD = dyn_cast<ImplicitParamDecl>(DRE->getDecl()))
272+
if (IPD->getIdentifier() == SelfII)
273+
return true;
274+
193275
return false;
194276
}
195277
};
196278

197279
} // end anonymous namespace
198280

199281
void trans::rewriteUnbridgedCasts(MigrationPass &pass) {
200-
UnbridgedCastRewriter trans(pass);
282+
BodyTransform<UnbridgedCastRewriter> trans(pass);
201283
trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
202284
}

0 commit comments

Comments
 (0)