]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/clang/lib/ARCMigrate/TransProperties.cpp
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm-project / clang / 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
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;
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(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 |
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_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()
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 &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
357   ObjCPropertyDecl::PropertyAttributeKind
358   getPropertyAttrs(PropsTy &props) const {
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       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
366 #endif
367
368     return attrs;
369   }
370 };
371
372 } // anonymous namespace
373
374 void PropertyRewriteTraverser::traverseObjCImplementation(
375                                            ObjCImplementationContext &ImplCtx) {
376   PropertiesRewriter(ImplCtx.getMigrationContext())
377                                   .doTransform(ImplCtx.getImplementationDecl());
378 }