1*67e74705SXin Li //===--- TransProperties.cpp - Transformations to ARC mode ----------------===//
2*67e74705SXin Li //
3*67e74705SXin Li // The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li // rewriteProperties:
11*67e74705SXin Li //
12*67e74705SXin Li // - Adds strong/weak/unsafe_unretained ownership specifier to properties that
13*67e74705SXin Li // are missing one.
14*67e74705SXin Li // - Migrates properties from (retain) to (strong) and (assign) to
15*67e74705SXin Li // (unsafe_unretained/weak).
16*67e74705SXin Li // - If a property is synthesized, adds the ownership specifier in the ivar
17*67e74705SXin Li // backing the property.
18*67e74705SXin Li //
19*67e74705SXin Li // @interface Foo : NSObject {
20*67e74705SXin Li // NSObject *x;
21*67e74705SXin Li // }
22*67e74705SXin Li // @property (assign) id x;
23*67e74705SXin Li // @end
24*67e74705SXin Li // ---->
25*67e74705SXin Li // @interface Foo : NSObject {
26*67e74705SXin Li // NSObject *__weak x;
27*67e74705SXin Li // }
28*67e74705SXin Li // @property (weak) id x;
29*67e74705SXin Li // @end
30*67e74705SXin Li //
31*67e74705SXin Li //===----------------------------------------------------------------------===//
32*67e74705SXin Li
33*67e74705SXin Li #include "Transforms.h"
34*67e74705SXin Li #include "Internals.h"
35*67e74705SXin Li #include "clang/Basic/SourceManager.h"
36*67e74705SXin Li #include "clang/Lex/Lexer.h"
37*67e74705SXin Li #include "clang/Sema/SemaDiagnostic.h"
38*67e74705SXin Li #include <map>
39*67e74705SXin Li
40*67e74705SXin Li using namespace clang;
41*67e74705SXin Li using namespace arcmt;
42*67e74705SXin Li using namespace trans;
43*67e74705SXin Li
44*67e74705SXin Li namespace {
45*67e74705SXin Li
46*67e74705SXin Li class PropertiesRewriter {
47*67e74705SXin Li MigrationContext &MigrateCtx;
48*67e74705SXin Li MigrationPass &Pass;
49*67e74705SXin Li ObjCImplementationDecl *CurImplD;
50*67e74705SXin Li
51*67e74705SXin Li enum PropActionKind {
52*67e74705SXin Li PropAction_None,
53*67e74705SXin Li PropAction_RetainReplacedWithStrong,
54*67e74705SXin Li PropAction_AssignRemoved,
55*67e74705SXin Li PropAction_AssignRewritten,
56*67e74705SXin Li PropAction_MaybeAddWeakOrUnsafe
57*67e74705SXin Li };
58*67e74705SXin Li
59*67e74705SXin Li struct PropData {
60*67e74705SXin Li ObjCPropertyDecl *PropD;
61*67e74705SXin Li ObjCIvarDecl *IvarD;
62*67e74705SXin Li ObjCPropertyImplDecl *ImplD;
63*67e74705SXin Li
PropData__anona36ea4150111::PropertiesRewriter::PropData64*67e74705SXin Li PropData(ObjCPropertyDecl *propD)
65*67e74705SXin Li : PropD(propD), IvarD(nullptr), ImplD(nullptr) {}
66*67e74705SXin Li };
67*67e74705SXin Li
68*67e74705SXin Li typedef SmallVector<PropData, 2> PropsTy;
69*67e74705SXin Li typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
70*67e74705SXin Li AtPropDeclsTy AtProps;
71*67e74705SXin Li llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
72*67e74705SXin Li
73*67e74705SXin Li public:
PropertiesRewriter(MigrationContext & MigrateCtx)74*67e74705SXin Li explicit PropertiesRewriter(MigrationContext &MigrateCtx)
75*67e74705SXin Li : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
76*67e74705SXin Li
collectProperties(ObjCContainerDecl * D,AtPropDeclsTy & AtProps,AtPropDeclsTy * PrevAtProps=nullptr)77*67e74705SXin Li static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
78*67e74705SXin Li AtPropDeclsTy *PrevAtProps = nullptr) {
79*67e74705SXin Li for (auto *Prop : D->instance_properties()) {
80*67e74705SXin Li if (Prop->getAtLoc().isInvalid())
81*67e74705SXin Li continue;
82*67e74705SXin Li unsigned RawLoc = Prop->getAtLoc().getRawEncoding();
83*67e74705SXin Li if (PrevAtProps)
84*67e74705SXin Li if (PrevAtProps->find(RawLoc) != PrevAtProps->end())
85*67e74705SXin Li continue;
86*67e74705SXin Li PropsTy &props = AtProps[RawLoc];
87*67e74705SXin Li props.push_back(Prop);
88*67e74705SXin Li }
89*67e74705SXin Li }
90*67e74705SXin Li
doTransform(ObjCImplementationDecl * D)91*67e74705SXin Li void doTransform(ObjCImplementationDecl *D) {
92*67e74705SXin Li CurImplD = D;
93*67e74705SXin Li ObjCInterfaceDecl *iface = D->getClassInterface();
94*67e74705SXin Li if (!iface)
95*67e74705SXin Li return;
96*67e74705SXin Li
97*67e74705SXin Li collectProperties(iface, AtProps);
98*67e74705SXin Li
99*67e74705SXin Li // Look through extensions.
100*67e74705SXin Li for (auto *Ext : iface->visible_extensions())
101*67e74705SXin Li collectProperties(Ext, AtProps);
102*67e74705SXin Li
103*67e74705SXin Li typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
104*67e74705SXin Li prop_impl_iterator;
105*67e74705SXin Li for (prop_impl_iterator
106*67e74705SXin Li I = prop_impl_iterator(D->decls_begin()),
107*67e74705SXin Li E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
108*67e74705SXin Li ObjCPropertyImplDecl *implD = *I;
109*67e74705SXin Li if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
110*67e74705SXin Li continue;
111*67e74705SXin Li ObjCPropertyDecl *propD = implD->getPropertyDecl();
112*67e74705SXin Li if (!propD || propD->isInvalidDecl())
113*67e74705SXin Li continue;
114*67e74705SXin Li ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
115*67e74705SXin Li if (!ivarD || ivarD->isInvalidDecl())
116*67e74705SXin Li continue;
117*67e74705SXin Li unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
118*67e74705SXin Li AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
119*67e74705SXin Li if (findAtLoc == AtProps.end())
120*67e74705SXin Li continue;
121*67e74705SXin Li
122*67e74705SXin Li PropsTy &props = findAtLoc->second;
123*67e74705SXin Li for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
124*67e74705SXin Li if (I->PropD == propD) {
125*67e74705SXin Li I->IvarD = ivarD;
126*67e74705SXin Li I->ImplD = implD;
127*67e74705SXin Li break;
128*67e74705SXin Li }
129*67e74705SXin Li }
130*67e74705SXin Li }
131*67e74705SXin Li
132*67e74705SXin Li for (AtPropDeclsTy::iterator
133*67e74705SXin Li I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
134*67e74705SXin Li SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
135*67e74705SXin Li PropsTy &props = I->second;
136*67e74705SXin Li if (!getPropertyType(props)->isObjCRetainableType())
137*67e74705SXin Li continue;
138*67e74705SXin Li if (hasIvarWithExplicitARCOwnership(props))
139*67e74705SXin Li continue;
140*67e74705SXin Li
141*67e74705SXin Li Transaction Trans(Pass.TA);
142*67e74705SXin Li rewriteProperty(props, atLoc);
143*67e74705SXin Li }
144*67e74705SXin Li }
145*67e74705SXin Li
146*67e74705SXin Li private:
doPropAction(PropActionKind kind,PropsTy & props,SourceLocation atLoc,bool markAction=true)147*67e74705SXin Li void doPropAction(PropActionKind kind,
148*67e74705SXin Li PropsTy &props, SourceLocation atLoc,
149*67e74705SXin Li bool markAction = true) {
150*67e74705SXin Li if (markAction)
151*67e74705SXin Li for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
152*67e74705SXin Li ActionOnProp[I->PropD->getIdentifier()] = kind;
153*67e74705SXin Li
154*67e74705SXin Li switch (kind) {
155*67e74705SXin Li case PropAction_None:
156*67e74705SXin Li return;
157*67e74705SXin Li case PropAction_RetainReplacedWithStrong: {
158*67e74705SXin Li StringRef toAttr = "strong";
159*67e74705SXin Li MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
160*67e74705SXin Li return;
161*67e74705SXin Li }
162*67e74705SXin Li case PropAction_AssignRemoved:
163*67e74705SXin Li return removeAssignForDefaultStrong(props, atLoc);
164*67e74705SXin Li case PropAction_AssignRewritten:
165*67e74705SXin Li return rewriteAssign(props, atLoc);
166*67e74705SXin Li case PropAction_MaybeAddWeakOrUnsafe:
167*67e74705SXin Li return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
168*67e74705SXin Li }
169*67e74705SXin Li }
170*67e74705SXin Li
rewriteProperty(PropsTy & props,SourceLocation atLoc)171*67e74705SXin Li void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
172*67e74705SXin Li ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
173*67e74705SXin Li
174*67e74705SXin Li if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
175*67e74705SXin Li ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
176*67e74705SXin Li ObjCPropertyDecl::OBJC_PR_strong |
177*67e74705SXin Li ObjCPropertyDecl::OBJC_PR_weak))
178*67e74705SXin Li return;
179*67e74705SXin Li
180*67e74705SXin Li if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
181*67e74705SXin Li // strong is the default.
182*67e74705SXin Li return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
183*67e74705SXin Li }
184*67e74705SXin Li
185*67e74705SXin Li bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
186*67e74705SXin Li
187*67e74705SXin Li if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
188*67e74705SXin Li if (HasIvarAssignedAPlusOneObject)
189*67e74705SXin Li return doPropAction(PropAction_AssignRemoved, props, atLoc);
190*67e74705SXin Li return doPropAction(PropAction_AssignRewritten, props, atLoc);
191*67e74705SXin Li }
192*67e74705SXin Li
193*67e74705SXin Li if (HasIvarAssignedAPlusOneObject ||
194*67e74705SXin Li (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
195*67e74705SXin Li return; // 'strong' by default.
196*67e74705SXin Li
197*67e74705SXin Li return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
198*67e74705SXin Li }
199*67e74705SXin Li
removeAssignForDefaultStrong(PropsTy & props,SourceLocation atLoc) const200*67e74705SXin Li void removeAssignForDefaultStrong(PropsTy &props,
201*67e74705SXin Li SourceLocation atLoc) const {
202*67e74705SXin Li removeAttribute("retain", atLoc);
203*67e74705SXin Li if (!removeAttribute("assign", atLoc))
204*67e74705SXin Li return;
205*67e74705SXin Li
206*67e74705SXin Li for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
207*67e74705SXin Li if (I->ImplD)
208*67e74705SXin Li Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
209*67e74705SXin Li diag::err_arc_assign_property_ownership,
210*67e74705SXin Li diag::err_arc_inconsistent_property_ownership,
211*67e74705SXin Li I->IvarD->getLocation());
212*67e74705SXin Li }
213*67e74705SXin Li }
214*67e74705SXin Li
rewriteAssign(PropsTy & props,SourceLocation atLoc) const215*67e74705SXin Li void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
216*67e74705SXin Li bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
217*67e74705SXin Li /*AllowOnUnknownClass=*/Pass.isGCMigration());
218*67e74705SXin Li const char *toWhich =
219*67e74705SXin Li (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
220*67e74705SXin Li (canUseWeak ? "weak" : "unsafe_unretained");
221*67e74705SXin Li
222*67e74705SXin Li bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
223*67e74705SXin Li if (!rewroteAttr)
224*67e74705SXin Li canUseWeak = false;
225*67e74705SXin Li
226*67e74705SXin Li for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
227*67e74705SXin Li if (isUserDeclared(I->IvarD)) {
228*67e74705SXin Li if (I->IvarD &&
229*67e74705SXin Li I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
230*67e74705SXin Li const char *toWhich =
231*67e74705SXin Li (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
232*67e74705SXin Li (canUseWeak ? "__weak " : "__unsafe_unretained ");
233*67e74705SXin Li Pass.TA.insert(I->IvarD->getLocation(), toWhich);
234*67e74705SXin Li }
235*67e74705SXin Li }
236*67e74705SXin Li if (I->ImplD)
237*67e74705SXin Li Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
238*67e74705SXin Li diag::err_arc_assign_property_ownership,
239*67e74705SXin Li diag::err_arc_inconsistent_property_ownership,
240*67e74705SXin Li I->IvarD->getLocation());
241*67e74705SXin Li }
242*67e74705SXin Li }
243*67e74705SXin Li
maybeAddWeakOrUnsafeUnretainedAttr(PropsTy & props,SourceLocation atLoc) const244*67e74705SXin Li void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
245*67e74705SXin Li SourceLocation atLoc) const {
246*67e74705SXin Li bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
247*67e74705SXin Li /*AllowOnUnknownClass=*/Pass.isGCMigration());
248*67e74705SXin Li
249*67e74705SXin Li bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
250*67e74705SXin Li atLoc);
251*67e74705SXin Li if (!addedAttr)
252*67e74705SXin Li canUseWeak = false;
253*67e74705SXin Li
254*67e74705SXin Li for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
255*67e74705SXin Li if (isUserDeclared(I->IvarD)) {
256*67e74705SXin Li if (I->IvarD &&
257*67e74705SXin Li I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
258*67e74705SXin Li Pass.TA.insert(I->IvarD->getLocation(),
259*67e74705SXin Li canUseWeak ? "__weak " : "__unsafe_unretained ");
260*67e74705SXin Li }
261*67e74705SXin Li if (I->ImplD) {
262*67e74705SXin Li Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
263*67e74705SXin Li diag::err_arc_assign_property_ownership,
264*67e74705SXin Li diag::err_arc_inconsistent_property_ownership,
265*67e74705SXin Li I->IvarD->getLocation());
266*67e74705SXin Li Pass.TA.clearDiagnostic(
267*67e74705SXin Li diag::err_arc_objc_property_default_assign_on_object,
268*67e74705SXin Li I->ImplD->getLocation());
269*67e74705SXin Li }
270*67e74705SXin Li }
271*67e74705SXin Li }
272*67e74705SXin Li
removeAttribute(StringRef fromAttr,SourceLocation atLoc) const273*67e74705SXin Li bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
274*67e74705SXin Li return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
275*67e74705SXin Li }
276*67e74705SXin Li
rewriteAttribute(StringRef fromAttr,StringRef toAttr,SourceLocation atLoc) const277*67e74705SXin Li bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
278*67e74705SXin Li SourceLocation atLoc) const {
279*67e74705SXin Li return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
280*67e74705SXin Li }
281*67e74705SXin Li
addAttribute(StringRef attr,SourceLocation atLoc) const282*67e74705SXin Li bool addAttribute(StringRef attr, SourceLocation atLoc) const {
283*67e74705SXin Li return MigrateCtx.addPropertyAttribute(attr, atLoc);
284*67e74705SXin Li }
285*67e74705SXin Li
286*67e74705SXin Li class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
287*67e74705SXin Li ObjCIvarDecl *Ivar;
288*67e74705SXin Li public:
PlusOneAssign(ObjCIvarDecl * D)289*67e74705SXin Li PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
290*67e74705SXin Li
VisitBinAssign(BinaryOperator * E)291*67e74705SXin Li bool VisitBinAssign(BinaryOperator *E) {
292*67e74705SXin Li Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
293*67e74705SXin Li if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
294*67e74705SXin Li if (RE->getDecl() != Ivar)
295*67e74705SXin Li return true;
296*67e74705SXin Li
297*67e74705SXin Li if (isPlusOneAssign(E))
298*67e74705SXin Li return false;
299*67e74705SXin Li }
300*67e74705SXin Li
301*67e74705SXin Li return true;
302*67e74705SXin Li }
303*67e74705SXin Li };
304*67e74705SXin Li
hasIvarAssignedAPlusOneObject(PropsTy & props) const305*67e74705SXin Li bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
306*67e74705SXin Li for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
307*67e74705SXin Li PlusOneAssign oneAssign(I->IvarD);
308*67e74705SXin Li bool notFound = oneAssign.TraverseDecl(CurImplD);
309*67e74705SXin Li if (!notFound)
310*67e74705SXin Li return true;
311*67e74705SXin Li }
312*67e74705SXin Li
313*67e74705SXin Li return false;
314*67e74705SXin Li }
315*67e74705SXin Li
hasIvarWithExplicitARCOwnership(PropsTy & props) const316*67e74705SXin Li bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
317*67e74705SXin Li if (Pass.isGCMigration())
318*67e74705SXin Li return false;
319*67e74705SXin Li
320*67e74705SXin Li for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
321*67e74705SXin Li if (isUserDeclared(I->IvarD)) {
322*67e74705SXin Li if (isa<AttributedType>(I->IvarD->getType()))
323*67e74705SXin Li return true;
324*67e74705SXin Li if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
325*67e74705SXin Li != Qualifiers::OCL_Strong)
326*67e74705SXin Li return true;
327*67e74705SXin Li }
328*67e74705SXin Li }
329*67e74705SXin Li
330*67e74705SXin Li return false;
331*67e74705SXin Li }
332*67e74705SXin Li
333*67e74705SXin Li // \brief Returns true if all declarations in the @property have GC __weak.
hasGCWeak(PropsTy & props,SourceLocation atLoc) const334*67e74705SXin Li bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
335*67e74705SXin Li if (!Pass.isGCMigration())
336*67e74705SXin Li return false;
337*67e74705SXin Li if (props.empty())
338*67e74705SXin Li return false;
339*67e74705SXin Li return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
340*67e74705SXin Li }
341*67e74705SXin Li
isUserDeclared(ObjCIvarDecl * ivarD) const342*67e74705SXin Li bool isUserDeclared(ObjCIvarDecl *ivarD) const {
343*67e74705SXin Li return ivarD && !ivarD->getSynthesize();
344*67e74705SXin Li }
345*67e74705SXin Li
getPropertyType(PropsTy & props) const346*67e74705SXin Li QualType getPropertyType(PropsTy &props) const {
347*67e74705SXin Li assert(!props.empty());
348*67e74705SXin Li QualType ty = props[0].PropD->getType().getUnqualifiedType();
349*67e74705SXin Li
350*67e74705SXin Li #ifndef NDEBUG
351*67e74705SXin Li for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
352*67e74705SXin Li assert(ty == I->PropD->getType().getUnqualifiedType());
353*67e74705SXin Li #endif
354*67e74705SXin Li
355*67e74705SXin Li return ty;
356*67e74705SXin Li }
357*67e74705SXin Li
358*67e74705SXin Li ObjCPropertyDecl::PropertyAttributeKind
getPropertyAttrs(PropsTy & props) const359*67e74705SXin Li getPropertyAttrs(PropsTy &props) const {
360*67e74705SXin Li assert(!props.empty());
361*67e74705SXin Li ObjCPropertyDecl::PropertyAttributeKind
362*67e74705SXin Li attrs = props[0].PropD->getPropertyAttributesAsWritten();
363*67e74705SXin Li
364*67e74705SXin Li #ifndef NDEBUG
365*67e74705SXin Li for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
366*67e74705SXin Li assert(attrs == I->PropD->getPropertyAttributesAsWritten());
367*67e74705SXin Li #endif
368*67e74705SXin Li
369*67e74705SXin Li return attrs;
370*67e74705SXin Li }
371*67e74705SXin Li };
372*67e74705SXin Li
373*67e74705SXin Li } // anonymous namespace
374*67e74705SXin Li
traverseObjCImplementation(ObjCImplementationContext & ImplCtx)375*67e74705SXin Li void PropertyRewriteTraverser::traverseObjCImplementation(
376*67e74705SXin Li ObjCImplementationContext &ImplCtx) {
377*67e74705SXin Li PropertiesRewriter(ImplCtx.getMigrationContext())
378*67e74705SXin Li .doTransform(ImplCtx.getImplementationDecl());
379*67e74705SXin Li }
380