clang  6.0.0svn
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->instance_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 
99  // Look through extensions.
100  for (auto *Ext : iface->visible_extensions())
101  collectProperties(Ext, AtProps);
102 
104  prop_impl_iterator;
105  for (prop_impl_iterator
106  I = prop_impl_iterator(D->decls_begin()),
107  E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
108  ObjCPropertyImplDecl *implD = *I;
110  continue;
111  ObjCPropertyDecl *propD = implD->getPropertyDecl();
112  if (!propD || propD->isInvalidDecl())
113  continue;
114  ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
115  if (!ivarD || ivarD->isInvalidDecl())
116  continue;
117  unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
118  AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
119  if (findAtLoc == AtProps.end())
120  continue;
121 
122  PropsTy &props = findAtLoc->second;
123  for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
124  if (I->PropD == propD) {
125  I->IvarD = ivarD;
126  I->ImplD = implD;
127  break;
128  }
129  }
130  }
131 
132  for (AtPropDeclsTy::iterator
133  I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
135  PropsTy &props = I->second;
136  if (!getPropertyType(props)->isObjCRetainableType())
137  continue;
138  if (hasIvarWithExplicitARCOwnership(props))
139  continue;
140 
141  Transaction Trans(Pass.TA);
142  rewriteProperty(props, atLoc);
143  }
144  }
145 
146 private:
147  void doPropAction(PropActionKind kind,
148  PropsTy &props, SourceLocation atLoc,
149  bool markAction = true) {
150  if (markAction)
151  for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
152  ActionOnProp[I->PropD->getIdentifier()] = kind;
153 
154  switch (kind) {
155  case PropAction_None:
156  return;
157  case PropAction_RetainReplacedWithStrong: {
158  StringRef toAttr = "strong";
159  MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
160  return;
161  }
162  case PropAction_AssignRemoved:
163  return removeAssignForDefaultStrong(props, atLoc);
164  case PropAction_AssignRewritten:
165  return rewriteAssign(props, atLoc);
166  case PropAction_MaybeAddWeakOrUnsafe:
167  return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
168  }
169  }
170 
171  void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
172  ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
173 
174  if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
178  return;
179 
180  if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
181  // strong is the default.
182  return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
183  }
184 
185  bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
186 
187  if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
188  if (HasIvarAssignedAPlusOneObject)
189  return doPropAction(PropAction_AssignRemoved, props, atLoc);
190  return doPropAction(PropAction_AssignRewritten, props, atLoc);
191  }
192 
193  if (HasIvarAssignedAPlusOneObject ||
194  (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
195  return; // 'strong' by default.
196 
197  return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
198  }
199 
200  void removeAssignForDefaultStrong(PropsTy &props,
201  SourceLocation atLoc) const {
202  removeAttribute("retain", atLoc);
203  if (!removeAttribute("assign", atLoc))
204  return;
205 
206  for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
207  if (I->ImplD)
208  Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
209  diag::err_arc_assign_property_ownership,
210  diag::err_arc_inconsistent_property_ownership,
211  I->IvarD->getLocation());
212  }
213  }
214 
215  void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
216  bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
217  /*AllowOnUnknownClass=*/Pass.isGCMigration());
218  const char *toWhich =
219  (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
220  (canUseWeak ? "weak" : "unsafe_unretained");
221 
222  bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
223  if (!rewroteAttr)
224  canUseWeak = false;
225 
226  for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
227  if (isUserDeclared(I->IvarD)) {
228  if (I->IvarD &&
229  I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
230  const char *toWhich =
231  (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
232  (canUseWeak ? "__weak " : "__unsafe_unretained ");
233  Pass.TA.insert(I->IvarD->getLocation(), toWhich);
234  }
235  }
236  if (I->ImplD)
237  Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
238  diag::err_arc_assign_property_ownership,
239  diag::err_arc_inconsistent_property_ownership,
240  I->IvarD->getLocation());
241  }
242  }
243 
244  void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
245  SourceLocation atLoc) const {
246  bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
247  /*AllowOnUnknownClass=*/Pass.isGCMigration());
248 
249  bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
250  atLoc);
251  if (!addedAttr)
252  canUseWeak = false;
253 
254  for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
255  if (isUserDeclared(I->IvarD)) {
256  if (I->IvarD &&
257  I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
258  Pass.TA.insert(I->IvarD->getLocation(),
259  canUseWeak ? "__weak " : "__unsafe_unretained ");
260  }
261  if (I->ImplD) {
262  Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
263  diag::err_arc_assign_property_ownership,
264  diag::err_arc_inconsistent_property_ownership,
265  I->IvarD->getLocation());
266  Pass.TA.clearDiagnostic(
267  diag::err_arc_objc_property_default_assign_on_object,
268  I->ImplD->getLocation());
269  }
270  }
271  }
272 
273  bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
274  return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
275  }
276 
277  bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
278  SourceLocation atLoc) const {
279  return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
280  }
281 
282  bool addAttribute(StringRef attr, SourceLocation atLoc) const {
283  return MigrateCtx.addPropertyAttribute(attr, atLoc);
284  }
285 
286  class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
287  ObjCIvarDecl *Ivar;
288  public:
289  PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
290 
291  bool VisitBinAssign(BinaryOperator *E) {
292  Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
293  if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
294  if (RE->getDecl() != Ivar)
295  return true;
296 
297  if (isPlusOneAssign(E))
298  return false;
299  }
300 
301  return true;
302  }
303  };
304 
305  bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
306  for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
307  PlusOneAssign oneAssign(I->IvarD);
308  bool notFound = oneAssign.TraverseDecl(CurImplD);
309  if (!notFound)
310  return true;
311  }
312 
313  return false;
314  }
315 
316  bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
317  if (Pass.isGCMigration())
318  return false;
319 
320  for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
321  if (isUserDeclared(I->IvarD)) {
322  if (isa<AttributedType>(I->IvarD->getType()))
323  return true;
324  if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
326  return true;
327  }
328  }
329 
330  return false;
331  }
332 
333  // \brief Returns true if all declarations in the @property have GC __weak.
334  bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
335  if (!Pass.isGCMigration())
336  return false;
337  if (props.empty())
338  return false;
339  return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
340  }
341 
342  bool isUserDeclared(ObjCIvarDecl *ivarD) const {
343  return ivarD && !ivarD->getSynthesize();
344  }
345 
346  QualType getPropertyType(PropsTy &props) const {
347  assert(!props.empty());
348  QualType ty = props[0].PropD->getType().getUnqualifiedType();
349 
350 #ifndef NDEBUG
351  for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
352  assert(ty == I->PropD->getType().getUnqualifiedType());
353 #endif
354 
355  return ty;
356  }
357 
359  getPropertyAttrs(PropsTy &props) const {
360  assert(!props.empty());
362  attrs = props[0].PropD->getPropertyAttributesAsWritten();
363 
364 #ifndef NDEBUG
365  for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
366  assert(attrs == I->PropD->getPropertyAttributesAsWritten());
367 #endif
368 
369  return attrs;
370  }
371 };
372 
373 } // anonymous namespace
374 
376  ObjCImplementationContext &ImplCtx) {
377  PropertiesRewriter(ImplCtx.getMigrationContext())
378  .doTransform(ImplCtx.getImplementationDecl());
379 }
unsigned getRawEncoding() const
When a SourceLocation itself cannot be used, this returns an (opaque) 32-bit integer encoding for it...
A (possibly-)qualified type.
Definition: Type.h:653
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:2846
Defines the SourceManager interface.
void traverseObjCImplementation(ObjCImplementationContext &ImplCtx) override
bool rewritePropertyAttribute(StringRef fromAttr, StringRef toAttr, SourceLocation atLoc)
Definition: Transforms.cpp:379
bool isInvalidDecl() const
Definition: DeclBase.h:546
instprop_range instance_properties() const
Definition: DeclObjC.h:1021
static SourceLocation getFromRawEncoding(unsigned Encoding)
Turn a raw encoding of a SourceLocation object into a real SourceLocation.
ObjCContainerDecl - Represents a container for method declarations.
Definition: DeclObjC.h:986
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:2985
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Represents an ObjC class declaration.
Definition: DeclObjC.h:1191
bool isPlusOneAssign(const BinaryOperator *E)
Definition: Transforms.cpp:67
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
Definition: DeclObjC.h:2778
visible_extensions_range visible_extensions() const
Definition: DeclObjC.h:1742
bool clearDiagnostic(ArrayRef< unsigned > IDs, SourceRange range)
Expr - This represents one expression.
Definition: Expr.h:106
ObjCImplementationDecl * getImplementationDecl()
Definition: Transforms.h:70
bool addPropertyAttribute(StringRef attr, SourceLocation atLoc)
Definition: Transforms.cpp:461
bool canApplyWeak(ASTContext &Ctx, QualType type, bool AllowOnUnknownClass=false)
Determine whether we can add weak to the given type.
Definition: Transforms.cpp:38
Assigning into this object requires the old value to be released and the new value to be retained...
Definition: Type.h:180
Encodes a location in the source.
bool getSynthesize() const
Definition: DeclObjC.h:2010
decl_iterator decls_begin() const
Definition: DeclBase.cpp:1320
bool removePropertyAttribute(StringRef fromAttr, SourceLocation atLoc)
Definition: Transforms.h:116
Represents one property declaration in an Objective-C interface.
Definition: DeclObjC.h:746
Expr * getLHS() const
Definition: Expr.h:3029
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2459
Dataflow Directional Tag Classes.
Kind getPropertyImplementation() const
Definition: DeclObjC.h:2842
SourceLocation getAtLoc() const
Definition: DeclObjC.h:838
specific_decl_iterator - Iterates over a subrange of declarations stored in a DeclContext, providing only those that are of type SpecificDecl (or a class derived from it).
Definition: DeclBase.h:1596
Expr * IgnoreParenImpCasts() LLVM_READONLY
IgnoreParenImpCasts - Ignore parentheses and implicit casts.
Definition: Expr.cpp:2552
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2571
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:513
Reading or writing from this object requires a barrier call.
Definition: Type.h:183
TransformActions & TA
Definition: Internals.h:151
ObjCIvarDecl - Represents an ObjC instance variable.
Definition: DeclObjC.h:1964
unsigned kind
All of the diagnostics that can be emitted by the frontend.
Definition: DiagnosticIDs.h:61
void insert(SourceLocation loc, StringRef text)
bool isGCMigration() const
Definition: Internals.h:166
ObjCPropertyDecl * getPropertyDecl() const
Definition: DeclObjC.h:2837
decl_iterator decls_end() const
Definition: DeclBase.h:1578
llvm::DenseSet< unsigned > AtPropsWeak
Set of raw &#39;@&#39; locations for &#39;assign&#39; properties group that contain GC __weak.
Definition: Transforms.h:102