clang  3.7.0
TransProperties.cpp
Go to the documentation of this file.
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"
36 #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)
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 
73 public:
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;
94  if (!iface)
95  return;
96 
97  collectProperties(iface, AtProps);
98 
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;
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) {
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) {
149  PropsTy &props = I->second;
150  Transaction Trans(Pass.TA);
151  doActionForExtensionProp(props, atLoc);
152  }
153  }
154 
155 private:
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 |
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()
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 
377  getPropertyAttrs(PropsTy &props) const {
378  assert(!props.empty());
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 
394  ObjCImplementationContext &ImplCtx) {
395  PropertiesRewriter(ImplCtx.getMigrationContext())
396  .doTransform(ImplCtx.getImplementationDecl());
397 }
Defines the SourceManager interface.
unsigned getRawEncoding() const
When a SourceLocation itself cannot be used, this returns an (opaque) 32-bit integer encoding for it...
void traverseObjCImplementation(ObjCImplementationContext &ImplCtx) override
decl_iterator decls_end() const
Definition: DeclBase.h:1415
Kind getPropertyImplementation() const
Definition: DeclObjC.h:2683
static SourceLocation getFromRawEncoding(unsigned Encoding)
Turn a raw encoding of a SourceLocation object into a real SourceLocation.
Expr * getLHS() const
Definition: Expr.h:2964
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:2918
A class that does preorder depth-first traversal on the entire Clang AST and visits each node...
Represents an ObjC class declaration.
Definition: DeclObjC.h:851
bool isPlusOneAssign(const BinaryOperator *E)
Definition: Transforms.cpp:71
decl_iterator decls_begin() const
Definition: DeclBase.cpp:1141
SourceLocation getAtLoc() const
Definition: DeclObjC.h:2497
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:2687
ObjCImplementationDecl * getImplementationDecl()
Definition: Transforms.h:70
bool canApplyWeak(ASTContext &Ctx, QualType type, bool AllowOnUnknownClass=false)
Determine whether we can add weak to the given type.
Definition: Transforms.cpp:43
Encodes a location in the source. The SourceManager can decode this to get at the full include stack...
bool getSynthesize() const
Definition: DeclObjC.h:1658
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2093
Represents one property declaration in an Objective-C interface.
Definition: DeclObjC.h:2424
prop_range properties() const
Definition: DeclObjC.h:714
bool isInvalidDecl() const
Definition: DeclBase.h:498
Expr * IgnoreParenImpCasts() LLVM_READONLY
Definition: Expr.cpp:2526
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:474
ObjCPropertyDecl * getPropertyDecl() const
Definition: DeclObjC.h:2678
Reading or writing from this object requires a barrier call.
Definition: Type.h:144
unsigned kind
All of the diagnostics that can be emitted by the frontend.
Definition: DiagnosticIDs.h:43
visible_extensions_range visible_extensions() const
Definition: DeclObjC.h:1394