clang  10.0.0svn
TransProperties.cpp
Go to the documentation of this file.
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"
35 #include "clang/Lex/Lexer.h"
37 #include <map>
38 
39 using namespace clang;
40 using namespace arcmt;
41 using namespace trans;
42 
43 namespace {
44 
45 class 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<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 = 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;
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 
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;
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) {
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(props, atLoc);
142  }
143  }
144 
145 private:
146  void doPropAction(PropActionKind kind,
147  PropsTy &props, SourceLocation 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(props, atLoc);
163  case PropAction_AssignRewritten:
164  return rewriteAssign(props, atLoc);
165  case PropAction_MaybeAddWeakOrUnsafe:
166  return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
167  }
168  }
169 
170  void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
171  ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
172 
173  if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
177  return;
178 
179  if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
180  // strong is the default.
181  return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
182  }
183 
184  bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
185 
186  if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
187  if (HasIvarAssignedAPlusOneObject)
188  return doPropAction(PropAction_AssignRemoved, props, atLoc);
189  return doPropAction(PropAction_AssignRewritten, props, atLoc);
190  }
191 
192  if (HasIvarAssignedAPlusOneObject ||
193  (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
194  return; // 'strong' by default.
195 
196  return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
197  }
198 
199  void removeAssignForDefaultStrong(PropsTy &props,
200  SourceLocation atLoc) const {
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 &props, SourceLocation atLoc) const {
215  bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
216  /*AllowOnUnknownClass=*/Pass.isGCMigration());
217  const char *toWhich =
218  (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
219  (canUseWeak ? "weak" : "unsafe_unretained");
220 
221  bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
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 atLoc) const {
245  bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(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 fromAttr, SourceLocation atLoc) const {
273  return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
274  }
275 
276  bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
277  SourceLocation atLoc) const {
278  return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
279  }
280 
281  bool addAttribute(StringRef attr, SourceLocation atLoc) const {
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 &props) const {
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 &props) const {
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()
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 &props, SourceLocation atLoc) const {
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 *ivarD) const {
342  return ivarD && !ivarD->getSynthesize();
343  }
344 
345  QualType getPropertyType(PropsTy &props) const {
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  assert(ty == I->PropD->getType().getUnqualifiedType());
352 #endif
353 
354  return ty;
355  }
356 
358  getPropertyAttrs(PropsTy &props) const {
359  assert(!props.empty());
361  attrs = props[0].PropD->getPropertyAttributesAsWritten();
362 
363 #ifndef NDEBUG
364  for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
365  assert(attrs == I->PropD->getPropertyAttributesAsWritten());
366 #endif
367 
368  return attrs;
369  }
370 };
371 
372 } // anonymous namespace
373 
375  ObjCImplementationContext &ImplCtx) {
376  PropertiesRewriter(ImplCtx.getMigrationContext())
377  .doTransform(ImplCtx.getImplementationDecl());
378 }
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:643
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:2826
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:553
instprop_range instance_properties() const
Definition: DeclObjC.h:1002
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:968
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3409
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:1171
bool isPlusOneAssign(const BinaryOperator *E)
Definition: Transforms.cpp:66
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
Definition: DeclObjC.h:2758
visible_extensions_range visible_extensions() const
Definition: DeclObjC.h:1722
bool clearDiagnostic(ArrayRef< unsigned > IDs, SourceRange range)
This represents one expression.
Definition: Expr.h:108
ObjCImplementationDecl * getImplementationDecl()
Definition: Transforms.h:69
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:37
Assigning into this object requires the old value to be released and the new value to be retained...
Definition: Type.h:165
Encodes a location in the source.
bool getSynthesize() const
Definition: DeclObjC.h:1990
decl_iterator decls_begin() const
Definition: DeclBase.cpp:1385
bool removePropertyAttribute(StringRef fromAttr, SourceLocation atLoc)
Definition: Transforms.h:115
Represents one property declaration in an Objective-C interface.
Definition: DeclObjC.h:728
Expr * getLHS() const
Definition: Expr.h:3449
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2439
Dataflow Directional Tag Classes.
Kind getPropertyImplementation() const
Definition: DeclObjC.h:2822
SourceLocation getAtLoc() const
Definition: DeclObjC.h:820
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:2048
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition: Expr.cpp:2966
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2551
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:546
Reading or writing from this object requires a barrier call.
Definition: Type.h:168
TransformActions & TA
Definition: Internals.h:150
ObjCIvarDecl - Represents an ObjC instance variable.
Definition: DeclObjC.h:1944
unsigned kind
All of the diagnostics that can be emitted by the frontend.
Definition: DiagnosticIDs.h:60
void insert(SourceLocation loc, StringRef text)
bool isGCMigration() const
Definition: Internals.h:165
ObjCPropertyDecl * getPropertyDecl() const
Definition: DeclObjC.h:2817
decl_iterator decls_end() const
Definition: DeclBase.h:2030
llvm::DenseSet< unsigned > AtPropsWeak
Set of raw &#39;@&#39; locations for &#39;assign&#39; properties group that contain GC __weak.
Definition: Transforms.h:101