Clang Project

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