1 //===--- TransProperties.cpp - Tranformations to ARC mode -----------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
12 // - Adds strong/weak/unsafe_unretained ownership specifier to properties that
14 // - Migrates properties from (retain) to (strong) and (assign) to
15 // (unsafe_unretained/weak).
16 // - If a property is synthesized, adds the ownership specifier in the ivar
17 // backing the property.
19 // @interface Foo : NSObject {
22 // @property (assign) id x;
25 // @interface Foo : NSObject {
26 // NSObject *__weak x;
28 // @property (weak) id x;
31 //===----------------------------------------------------------------------===//
33 #include "Transforms.h"
34 #include "Internals.h"
35 #include "clang/Sema/SemaDiagnostic.h"
36 #include "clang/Basic/SourceManager.h"
37 #include "clang/Lex/Lexer.h"
40 using namespace clang;
41 using namespace arcmt;
42 using namespace trans;
46 class PropertiesRewriter {
47 MigrationContext &MigrateCtx;
49 ObjCImplementationDecl *CurImplD;
53 PropAction_RetainReplacedWithStrong,
54 PropAction_AssignRemoved,
55 PropAction_AssignRewritten,
56 PropAction_MaybeAddWeakOrUnsafe
60 ObjCPropertyDecl *PropD;
62 ObjCPropertyImplDecl *ImplD;
64 PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
67 typedef SmallVector<PropData, 2> PropsTy;
68 typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
69 AtPropDeclsTy AtProps;
70 llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
73 explicit PropertiesRewriter(MigrationContext &MigrateCtx)
74 : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
76 static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
77 AtPropDeclsTy *PrevAtProps = 0) {
78 for (ObjCInterfaceDecl::prop_iterator
79 propI = D->prop_begin(),
80 propE = D->prop_end(); propI != propE; ++propI) {
81 if (propI->getAtLoc().isInvalid())
83 unsigned RawLoc = propI->getAtLoc().getRawEncoding();
85 if (PrevAtProps->find(RawLoc) != PrevAtProps->end())
87 PropsTy &props = AtProps[RawLoc];
88 props.push_back(*propI);
92 void doTransform(ObjCImplementationDecl *D) {
94 ObjCInterfaceDecl *iface = D->getClassInterface();
98 collectProperties(iface, AtProps);
100 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
102 for (prop_impl_iterator
103 I = prop_impl_iterator(D->decls_begin()),
104 E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
105 ObjCPropertyImplDecl *implD = *I;
106 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
108 ObjCPropertyDecl *propD = implD->getPropertyDecl();
109 if (!propD || propD->isInvalidDecl())
111 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
112 if (!ivarD || ivarD->isInvalidDecl())
114 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
115 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
116 if (findAtLoc == AtProps.end())
119 PropsTy &props = findAtLoc->second;
120 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
121 if (I->PropD == propD) {
129 for (AtPropDeclsTy::iterator
130 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
131 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
132 PropsTy &props = I->second;
133 if (!getPropertyType(props)->isObjCRetainableType())
135 if (hasIvarWithExplicitARCOwnership(props))
138 Transaction Trans(Pass.TA);
139 rewriteProperty(props, atLoc);
142 AtPropDeclsTy AtExtProps;
143 // Look through extensions.
144 for (ObjCCategoryDecl *Cat = iface->getCategoryList();
145 Cat; Cat = Cat->getNextClassCategory())
146 if (Cat->IsClassExtension())
147 collectProperties(Cat, AtExtProps, &AtProps);
149 for (AtPropDeclsTy::iterator
150 I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) {
151 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
152 PropsTy &props = I->second;
153 Transaction Trans(Pass.TA);
154 doActionForExtensionProp(props, atLoc);
159 void doPropAction(PropActionKind kind,
160 PropsTy &props, SourceLocation atLoc,
161 bool markAction = true) {
163 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
164 ActionOnProp[I->PropD->getIdentifier()] = kind;
167 case PropAction_None:
169 case PropAction_RetainReplacedWithStrong: {
170 StringRef toAttr = "strong";
171 MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
174 case PropAction_AssignRemoved:
175 return removeAssignForDefaultStrong(props, atLoc);
176 case PropAction_AssignRewritten:
177 return rewriteAssign(props, atLoc);
178 case PropAction_MaybeAddWeakOrUnsafe:
179 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
183 void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) {
184 llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I;
185 I = ActionOnProp.find(props[0].PropD->getIdentifier());
186 if (I == ActionOnProp.end())
189 doPropAction(I->second, props, atLoc, false);
192 void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
193 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
195 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
196 ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
197 ObjCPropertyDecl::OBJC_PR_strong |
198 ObjCPropertyDecl::OBJC_PR_weak))
201 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
202 // strong is the default.
203 return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
206 bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
208 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
209 if (HasIvarAssignedAPlusOneObject)
210 return doPropAction(PropAction_AssignRemoved, props, atLoc);
211 return doPropAction(PropAction_AssignRewritten, props, atLoc);
214 if (HasIvarAssignedAPlusOneObject ||
215 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
216 return; // 'strong' by default.
218 return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
221 void removeAssignForDefaultStrong(PropsTy &props,
222 SourceLocation atLoc) const {
223 removeAttribute("retain", atLoc);
224 if (!removeAttribute("assign", atLoc))
227 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
229 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
230 I->ImplD->getLocation());
234 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
235 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
236 /*AllowOnUnknownClass=*/Pass.isGCMigration());
237 const char *toWhich =
238 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
239 (canUseWeak ? "weak" : "unsafe_unretained");
241 bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
245 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
246 if (isUserDeclared(I->IvarD)) {
248 I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
249 const char *toWhich =
250 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
251 (canUseWeak ? "__weak " : "__unsafe_unretained ");
252 Pass.TA.insert(I->IvarD->getLocation(), toWhich);
256 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
257 I->ImplD->getLocation());
261 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
262 SourceLocation atLoc) const {
263 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
264 /*AllowOnUnknownClass=*/Pass.isGCMigration());
266 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
271 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
272 if (isUserDeclared(I->IvarD)) {
274 I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
275 Pass.TA.insert(I->IvarD->getLocation(),
276 canUseWeak ? "__weak " : "__unsafe_unretained ");
279 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
280 I->ImplD->getLocation());
281 Pass.TA.clearDiagnostic(
282 diag::err_arc_objc_property_default_assign_on_object,
283 I->ImplD->getLocation());
288 bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
289 return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
292 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
293 SourceLocation atLoc) const {
294 return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
297 bool addAttribute(StringRef attr, SourceLocation atLoc) const {
298 return MigrateCtx.addPropertyAttribute(attr, atLoc);
301 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
304 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
306 bool VisitBinAssign(BinaryOperator *E) {
307 Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
308 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
309 if (RE->getDecl() != Ivar)
312 if (ObjCMessageExpr *
313 ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
314 if (ME->getMethodFamily() == OMF_retain)
317 ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
318 while (implCE && implCE->getCastKind() == CK_BitCast)
319 implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
321 if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
329 bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
330 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
331 PlusOneAssign oneAssign(I->IvarD);
332 bool notFound = oneAssign.TraverseDecl(CurImplD);
340 bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
341 if (Pass.isGCMigration())
344 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
345 if (isUserDeclared(I->IvarD)) {
346 if (isa<AttributedType>(I->IvarD->getType()))
348 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
349 != Qualifiers::OCL_Strong)
357 bool hasAllIvarsBacked(PropsTy &props) const {
358 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
359 if (!isUserDeclared(I->IvarD))
365 // \brief Returns true if all declarations in the @property have GC __weak.
366 bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
367 if (!Pass.isGCMigration())
371 return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
374 bool isUserDeclared(ObjCIvarDecl *ivarD) const {
375 return ivarD && !ivarD->getSynthesize();
378 QualType getPropertyType(PropsTy &props) const {
379 assert(!props.empty());
380 QualType ty = props[0].PropD->getType().getUnqualifiedType();
383 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
384 assert(ty == I->PropD->getType().getUnqualifiedType());
390 ObjCPropertyDecl::PropertyAttributeKind
391 getPropertyAttrs(PropsTy &props) const {
392 assert(!props.empty());
393 ObjCPropertyDecl::PropertyAttributeKind
394 attrs = props[0].PropD->getPropertyAttributesAsWritten();
397 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
398 assert(attrs == I->PropD->getPropertyAttributesAsWritten());
405 } // anonymous namespace
407 void PropertyRewriteTraverser::traverseObjCImplementation(
408 ObjCImplementationContext &ImplCtx) {
409 PropertiesRewriter(ImplCtx.getMigrationContext())
410 .doTransform(ImplCtx.getImplementationDecl());