1 //===--- TransProperties.cpp - Transformations 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/Basic/SourceManager.h"
36 #include "clang/Lex/Lexer.h"
37 #include "clang/Sema/SemaDiagnostic.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                                 AtPropDeclsTy *PrevAtProps = 0) {
78     for (auto *Prop : D->properties()) {
79       if (Prop->getAtLoc().isInvalid())
80         continue;
81       unsigned RawLoc = Prop->getAtLoc().getRawEncoding();
82       if (PrevAtProps)
83         if (PrevAtProps->find(RawLoc) != PrevAtProps->end())
84           continue;
85       PropsTy &props = AtProps[RawLoc];
86       props.push_back(Prop);
87     }
88   }
89 
90   void doTransform(ObjCImplementationDecl *D) {
91     CurImplD = D;
92     ObjCInterfaceDecl *iface = D->getClassInterface();
93     if (!iface)
94       return;
95 
96     collectProperties(iface, AtProps);
97 
98     typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
99         prop_impl_iterator;
100     for (prop_impl_iterator
101            I = prop_impl_iterator(D->decls_begin()),
102            E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
103       ObjCPropertyImplDecl *implD = *I;
104       if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
105         continue;
106       ObjCPropertyDecl *propD = implD->getPropertyDecl();
107       if (!propD || propD->isInvalidDecl())
108         continue;
109       ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
110       if (!ivarD || ivarD->isInvalidDecl())
111         continue;
112       unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
113       AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
114       if (findAtLoc == AtProps.end())
115         continue;
116 
117       PropsTy &props = findAtLoc->second;
118       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
119         if (I->PropD == propD) {
120           I->IvarD = ivarD;
121           I->ImplD = implD;
122           break;
123         }
124       }
125     }
126 
127     for (AtPropDeclsTy::iterator
128            I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
129       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
130       PropsTy &props = I->second;
131       if (!getPropertyType(props)->isObjCRetainableType())
132         continue;
133       if (hasIvarWithExplicitARCOwnership(props))
134         continue;
135 
136       Transaction Trans(Pass.TA);
137       rewriteProperty(props, atLoc);
138     }
139 
140     AtPropDeclsTy AtExtProps;
141     // Look through extensions.
142     for (auto *Ext : iface->visible_extensions())
143       collectProperties(Ext, AtExtProps, &AtProps);
144 
145     for (AtPropDeclsTy::iterator
146            I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) {
147       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
148       PropsTy &props = I->second;
149       Transaction Trans(Pass.TA);
150       doActionForExtensionProp(props, atLoc);
151     }
152   }
153 
154 private:
155   void doPropAction(PropActionKind kind,
156                     PropsTy &props, SourceLocation atLoc,
157                     bool markAction = true) {
158     if (markAction)
159       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
160         ActionOnProp[I->PropD->getIdentifier()] = kind;
161 
162     switch (kind) {
163     case PropAction_None:
164       return;
165     case PropAction_RetainReplacedWithStrong: {
166       StringRef toAttr = "strong";
167       MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
168       return;
169     }
170     case PropAction_AssignRemoved:
171       return removeAssignForDefaultStrong(props, atLoc);
172     case PropAction_AssignRewritten:
173       return rewriteAssign(props, atLoc);
174     case PropAction_MaybeAddWeakOrUnsafe:
175       return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
176     }
177   }
178 
179   void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) {
180     llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I;
181     I = ActionOnProp.find(props[0].PropD->getIdentifier());
182     if (I == ActionOnProp.end())
183       return;
184 
185     doPropAction(I->second, props, atLoc, false);
186   }
187 
188   void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
189     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
190 
191     if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
192                      ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
193                      ObjCPropertyDecl::OBJC_PR_strong |
194                      ObjCPropertyDecl::OBJC_PR_weak))
195       return;
196 
197     if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
198       // strong is the default.
199       return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
200     }
201 
202     bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
203 
204     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
205       if (HasIvarAssignedAPlusOneObject)
206         return doPropAction(PropAction_AssignRemoved, props, atLoc);
207       return doPropAction(PropAction_AssignRewritten, props, atLoc);
208     }
209 
210     if (HasIvarAssignedAPlusOneObject ||
211         (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
212       return; // 'strong' by default.
213 
214     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
215   }
216 
217   void removeAssignForDefaultStrong(PropsTy &props,
218                                     SourceLocation atLoc) const {
219     removeAttribute("retain", atLoc);
220     if (!removeAttribute("assign", atLoc))
221       return;
222 
223     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
224       if (I->ImplD)
225         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
226                                 diag::err_arc_assign_property_ownership,
227                                 diag::err_arc_inconsistent_property_ownership,
228                                 I->IvarD->getLocation());
229     }
230   }
231 
232   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
233     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
234                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
235     const char *toWhich =
236       (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
237       (canUseWeak ? "weak" : "unsafe_unretained");
238 
239     bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
240     if (!rewroteAttr)
241       canUseWeak = false;
242 
243     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
244       if (isUserDeclared(I->IvarD)) {
245         if (I->IvarD &&
246             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
247           const char *toWhich =
248             (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
249               (canUseWeak ? "__weak " : "__unsafe_unretained ");
250           Pass.TA.insert(I->IvarD->getLocation(), toWhich);
251         }
252       }
253       if (I->ImplD)
254         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
255                                 diag::err_arc_assign_property_ownership,
256                                 diag::err_arc_inconsistent_property_ownership,
257                                 I->IvarD->getLocation());
258     }
259   }
260 
261   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
262                                           SourceLocation atLoc) const {
263     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
264                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
265 
266     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
267                                   atLoc);
268     if (!addedAttr)
269       canUseWeak = false;
270 
271     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
272       if (isUserDeclared(I->IvarD)) {
273         if (I->IvarD &&
274             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
275           Pass.TA.insert(I->IvarD->getLocation(),
276                          canUseWeak ? "__weak " : "__unsafe_unretained ");
277       }
278       if (I->ImplD) {
279         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
280                                 diag::err_arc_assign_property_ownership,
281                                 diag::err_arc_inconsistent_property_ownership,
282                                 I->IvarD->getLocation());
283         Pass.TA.clearDiagnostic(
284                            diag::err_arc_objc_property_default_assign_on_object,
285                            I->ImplD->getLocation());
286       }
287     }
288   }
289 
290   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
291     return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
292   }
293 
294   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
295                         SourceLocation atLoc) const {
296     return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
297   }
298 
299   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
300     return MigrateCtx.addPropertyAttribute(attr, atLoc);
301   }
302 
303   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
304     ObjCIvarDecl *Ivar;
305   public:
306     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
307 
308     bool VisitBinAssign(BinaryOperator *E) {
309       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
310       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
311         if (RE->getDecl() != Ivar)
312           return true;
313 
314         if (isPlusOneAssign(E))
315           return false;
316       }
317 
318       return true;
319     }
320   };
321 
322   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
323     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
324       PlusOneAssign oneAssign(I->IvarD);
325       bool notFound = oneAssign.TraverseDecl(CurImplD);
326       if (!notFound)
327         return true;
328     }
329 
330     return false;
331   }
332 
333   bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
334     if (Pass.isGCMigration())
335       return false;
336 
337     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
338       if (isUserDeclared(I->IvarD)) {
339         if (isa<AttributedType>(I->IvarD->getType()))
340           return true;
341         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
342               != Qualifiers::OCL_Strong)
343           return true;
344       }
345     }
346 
347     return false;
348   }
349 
350   // \brief Returns true if all declarations in the @property have GC __weak.
351   bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
352     if (!Pass.isGCMigration())
353       return false;
354     if (props.empty())
355       return false;
356     return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
357   }
358 
359   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
360     return ivarD && !ivarD->getSynthesize();
361   }
362 
363   QualType getPropertyType(PropsTy &props) const {
364     assert(!props.empty());
365     QualType ty = props[0].PropD->getType().getUnqualifiedType();
366 
367 #ifndef NDEBUG
368     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
369       assert(ty == I->PropD->getType().getUnqualifiedType());
370 #endif
371 
372     return ty;
373   }
374 
375   ObjCPropertyDecl::PropertyAttributeKind
376   getPropertyAttrs(PropsTy &props) const {
377     assert(!props.empty());
378     ObjCPropertyDecl::PropertyAttributeKind
379       attrs = props[0].PropD->getPropertyAttributesAsWritten();
380 
381 #ifndef NDEBUG
382     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
383       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
384 #endif
385 
386     return attrs;
387   }
388 };
389 
390 } // anonymous namespace
391 
392 void PropertyRewriteTraverser::traverseObjCImplementation(
393                                            ObjCImplementationContext &ImplCtx) {
394   PropertiesRewriter(ImplCtx.getMigrationContext())
395                                   .doTransform(ImplCtx.getImplementationDecl());
396 }
397