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