1 //===--- TransProperties.cpp - Transformations 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/Basic/SourceManager.h"
36 #include "clang/Lex/Lexer.h"
37 #include "clang/Sema/SemaDiagnostic.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)
65 : PropD(propD), IvarD(nullptr), ImplD(nullptr) {}
68 typedef SmallVector<PropData, 2> PropsTy;
69 typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
70 AtPropDeclsTy AtProps;
71 llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
74 explicit PropertiesRewriter(MigrationContext &MigrateCtx)
75 : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
77 static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
78 AtPropDeclsTy *PrevAtProps = nullptr) {
79 for (auto *Prop : D->instance_properties()) {
80 if (Prop->getAtLoc().isInvalid())
82 unsigned RawLoc = Prop->getAtLoc().getRawEncoding();
84 if (PrevAtProps->find(RawLoc) != PrevAtProps->end())
86 PropsTy &props = AtProps[RawLoc];
87 props.push_back(Prop);
91 void doTransform(ObjCImplementationDecl *D) {
93 ObjCInterfaceDecl *iface = D->getClassInterface();
97 collectProperties(iface, AtProps);
99 // Look through extensions.
100 for (auto *Ext : iface->visible_extensions())
101 collectProperties(Ext, AtProps);
103 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
105 for (prop_impl_iterator
106 I = prop_impl_iterator(D->decls_begin()),
107 E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
108 ObjCPropertyImplDecl *implD = *I;
109 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
111 ObjCPropertyDecl *propD = implD->getPropertyDecl();
112 if (!propD || propD->isInvalidDecl())
114 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
115 if (!ivarD || ivarD->isInvalidDecl())
117 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
118 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
119 if (findAtLoc == AtProps.end())
122 PropsTy &props = findAtLoc->second;
123 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
124 if (I->PropD == propD) {
132 for (AtPropDeclsTy::iterator
133 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
134 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
135 PropsTy &props = I->second;
136 if (!getPropertyType(props)->isObjCRetainableType())
138 if (hasIvarWithExplicitARCOwnership(props))
141 Transaction Trans(Pass.TA);
142 rewriteProperty(props, atLoc);
147 void doPropAction(PropActionKind kind,
148 PropsTy &props, SourceLocation atLoc,
149 bool markAction = true) {
151 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
152 ActionOnProp[I->PropD->getIdentifier()] = kind;
155 case PropAction_None:
157 case PropAction_RetainReplacedWithStrong: {
158 StringRef toAttr = "strong";
159 MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
162 case PropAction_AssignRemoved:
163 return removeAssignForDefaultStrong(props, atLoc);
164 case PropAction_AssignRewritten:
165 return rewriteAssign(props, atLoc);
166 case PropAction_MaybeAddWeakOrUnsafe:
167 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
171 void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
172 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
174 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
175 ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
176 ObjCPropertyDecl::OBJC_PR_strong |
177 ObjCPropertyDecl::OBJC_PR_weak))
180 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
181 // strong is the default.
182 return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
185 bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
187 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
188 if (HasIvarAssignedAPlusOneObject)
189 return doPropAction(PropAction_AssignRemoved, props, atLoc);
190 return doPropAction(PropAction_AssignRewritten, props, atLoc);
193 if (HasIvarAssignedAPlusOneObject ||
194 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
195 return; // 'strong' by default.
197 return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
200 void removeAssignForDefaultStrong(PropsTy &props,
201 SourceLocation atLoc) const {
202 removeAttribute("retain", atLoc);
203 if (!removeAttribute("assign", atLoc))
206 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
208 Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
209 diag::err_arc_assign_property_ownership,
210 diag::err_arc_inconsistent_property_ownership,
211 I->IvarD->getLocation());
215 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
216 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
217 /*AllowOnUnknownClass=*/Pass.isGCMigration());
218 const char *toWhich =
219 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
220 (canUseWeak ? "weak" : "unsafe_unretained");
222 bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
226 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
227 if (isUserDeclared(I->IvarD)) {
229 I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
230 const char *toWhich =
231 (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
232 (canUseWeak ? "__weak " : "__unsafe_unretained ");
233 Pass.TA.insert(I->IvarD->getLocation(), toWhich);
237 Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
238 diag::err_arc_assign_property_ownership,
239 diag::err_arc_inconsistent_property_ownership,
240 I->IvarD->getLocation());
244 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
245 SourceLocation atLoc) const {
246 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
247 /*AllowOnUnknownClass=*/Pass.isGCMigration());
249 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
254 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
255 if (isUserDeclared(I->IvarD)) {
257 I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
258 Pass.TA.insert(I->IvarD->getLocation(),
259 canUseWeak ? "__weak " : "__unsafe_unretained ");
262 Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
263 diag::err_arc_assign_property_ownership,
264 diag::err_arc_inconsistent_property_ownership,
265 I->IvarD->getLocation());
266 Pass.TA.clearDiagnostic(
267 diag::err_arc_objc_property_default_assign_on_object,
268 I->ImplD->getLocation());
273 bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
274 return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
277 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
278 SourceLocation atLoc) const {
279 return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
282 bool addAttribute(StringRef attr, SourceLocation atLoc) const {
283 return MigrateCtx.addPropertyAttribute(attr, atLoc);
286 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
289 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
291 bool VisitBinAssign(BinaryOperator *E) {
292 Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
293 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
294 if (RE->getDecl() != Ivar)
297 if (isPlusOneAssign(E))
305 bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
306 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
307 PlusOneAssign oneAssign(I->IvarD);
308 bool notFound = oneAssign.TraverseDecl(CurImplD);
316 bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
317 if (Pass.isGCMigration())
320 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
321 if (isUserDeclared(I->IvarD)) {
322 if (isa<AttributedType>(I->IvarD->getType()))
324 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
325 != Qualifiers::OCL_Strong)
333 // Returns true if all declarations in the @property have GC __weak.
334 bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
335 if (!Pass.isGCMigration())
339 return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
342 bool isUserDeclared(ObjCIvarDecl *ivarD) const {
343 return ivarD && !ivarD->getSynthesize();
346 QualType getPropertyType(PropsTy &props) const {
347 assert(!props.empty());
348 QualType ty = props[0].PropD->getType().getUnqualifiedType();
351 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
352 assert(ty == I->PropD->getType().getUnqualifiedType());
358 ObjCPropertyDecl::PropertyAttributeKind
359 getPropertyAttrs(PropsTy &props) const {
360 assert(!props.empty());
361 ObjCPropertyDecl::PropertyAttributeKind
362 attrs = props[0].PropD->getPropertyAttributesAsWritten();
365 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
366 assert(attrs == I->PropD->getPropertyAttributesAsWritten());
373 } // anonymous namespace
375 void PropertyRewriteTraverser::traverseObjCImplementation(
376 ObjCImplementationContext &ImplCtx) {
377 PropertiesRewriter(ImplCtx.getMigrationContext())
378 .doTransform(ImplCtx.getImplementationDecl());