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