Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/ARCMigrate/TransProperties.cpp
35236 views
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 = nullptr;
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<SourceLocation, 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
SourceLocation Loc = Prop->getAtLoc();
80
if (Loc.isInvalid())
81
continue;
82
if (PrevAtProps)
83
if (PrevAtProps->find(Loc) != PrevAtProps->end())
84
continue;
85
PropsTy &props = AtProps[Loc];
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
AtPropDeclsTy::iterator findAtLoc = AtProps.find(propD->getAtLoc());
117
if (findAtLoc == AtProps.end())
118
continue;
119
120
PropsTy &props = findAtLoc->second;
121
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
122
if (I->PropD == propD) {
123
I->IvarD = ivarD;
124
I->ImplD = implD;
125
break;
126
}
127
}
128
}
129
130
for (AtPropDeclsTy::iterator
131
I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
132
SourceLocation atLoc = I->first;
133
PropsTy &props = I->second;
134
if (!getPropertyType(props)->isObjCRetainableType())
135
continue;
136
if (hasIvarWithExplicitARCOwnership(props))
137
continue;
138
139
Transaction Trans(Pass.TA);
140
rewriteProperty(props, atLoc);
141
}
142
}
143
144
private:
145
void doPropAction(PropActionKind kind,
146
PropsTy &props, SourceLocation atLoc,
147
bool markAction = true) {
148
if (markAction)
149
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
150
ActionOnProp[I->PropD->getIdentifier()] = kind;
151
152
switch (kind) {
153
case PropAction_None:
154
return;
155
case PropAction_RetainReplacedWithStrong: {
156
StringRef toAttr = "strong";
157
MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
158
return;
159
}
160
case PropAction_AssignRemoved:
161
return removeAssignForDefaultStrong(props, atLoc);
162
case PropAction_AssignRewritten:
163
return rewriteAssign(props, atLoc);
164
case PropAction_MaybeAddWeakOrUnsafe:
165
return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
166
}
167
}
168
169
void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
170
ObjCPropertyAttribute::Kind propAttrs = getPropertyAttrs(props);
171
172
if (propAttrs &
173
(ObjCPropertyAttribute::kind_copy |
174
ObjCPropertyAttribute::kind_unsafe_unretained |
175
ObjCPropertyAttribute::kind_strong | ObjCPropertyAttribute::kind_weak))
176
return;
177
178
if (propAttrs & ObjCPropertyAttribute::kind_retain) {
179
// strong is the default.
180
return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
181
}
182
183
bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
184
185
if (propAttrs & ObjCPropertyAttribute::kind_assign) {
186
if (HasIvarAssignedAPlusOneObject)
187
return doPropAction(PropAction_AssignRemoved, props, atLoc);
188
return doPropAction(PropAction_AssignRewritten, props, atLoc);
189
}
190
191
if (HasIvarAssignedAPlusOneObject ||
192
(Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
193
return; // 'strong' by default.
194
195
return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
196
}
197
198
void removeAssignForDefaultStrong(PropsTy &props,
199
SourceLocation atLoc) const {
200
removeAttribute("retain", atLoc);
201
if (!removeAttribute("assign", atLoc))
202
return;
203
204
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
205
if (I->ImplD)
206
Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
207
diag::err_arc_assign_property_ownership,
208
diag::err_arc_inconsistent_property_ownership,
209
I->IvarD->getLocation());
210
}
211
}
212
213
void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
214
bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
215
/*AllowOnUnknownClass=*/Pass.isGCMigration());
216
const char *toWhich =
217
(Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
218
(canUseWeak ? "weak" : "unsafe_unretained");
219
220
bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
221
if (!rewroteAttr)
222
canUseWeak = false;
223
224
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
225
if (isUserDeclared(I->IvarD)) {
226
if (I->IvarD &&
227
I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
228
const char *toWhich =
229
(Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
230
(canUseWeak ? "__weak " : "__unsafe_unretained ");
231
Pass.TA.insert(I->IvarD->getLocation(), toWhich);
232
}
233
}
234
if (I->ImplD)
235
Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
236
diag::err_arc_assign_property_ownership,
237
diag::err_arc_inconsistent_property_ownership,
238
I->IvarD->getLocation());
239
}
240
}
241
242
void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
243
SourceLocation atLoc) const {
244
bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
245
/*AllowOnUnknownClass=*/Pass.isGCMigration());
246
247
bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
248
atLoc);
249
if (!addedAttr)
250
canUseWeak = false;
251
252
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
253
if (isUserDeclared(I->IvarD)) {
254
if (I->IvarD &&
255
I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
256
Pass.TA.insert(I->IvarD->getLocation(),
257
canUseWeak ? "__weak " : "__unsafe_unretained ");
258
}
259
if (I->ImplD) {
260
Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
261
diag::err_arc_assign_property_ownership,
262
diag::err_arc_inconsistent_property_ownership,
263
I->IvarD->getLocation());
264
Pass.TA.clearDiagnostic(
265
diag::err_arc_objc_property_default_assign_on_object,
266
I->ImplD->getLocation());
267
}
268
}
269
}
270
271
bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
272
return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
273
}
274
275
bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
276
SourceLocation atLoc) const {
277
return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
278
}
279
280
bool addAttribute(StringRef attr, SourceLocation atLoc) const {
281
return MigrateCtx.addPropertyAttribute(attr, atLoc);
282
}
283
284
class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
285
ObjCIvarDecl *Ivar;
286
public:
287
PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
288
289
bool VisitBinaryOperator(BinaryOperator *E) {
290
if (E->getOpcode() != BO_Assign)
291
return true;
292
293
Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
294
if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
295
if (RE->getDecl() != Ivar)
296
return true;
297
298
if (isPlusOneAssign(E))
299
return false;
300
}
301
302
return true;
303
}
304
};
305
306
bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
307
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
308
PlusOneAssign oneAssign(I->IvarD);
309
bool notFound = oneAssign.TraverseDecl(CurImplD);
310
if (!notFound)
311
return true;
312
}
313
314
return false;
315
}
316
317
bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
318
if (Pass.isGCMigration())
319
return false;
320
321
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
322
if (isUserDeclared(I->IvarD)) {
323
if (isa<AttributedType>(I->IvarD->getType()))
324
return true;
325
if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
326
!= Qualifiers::OCL_Strong)
327
return true;
328
}
329
}
330
331
return false;
332
}
333
334
// Returns true if all declarations in the @property have GC __weak.
335
bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
336
if (!Pass.isGCMigration())
337
return false;
338
if (props.empty())
339
return false;
340
return MigrateCtx.AtPropsWeak.count(atLoc);
341
}
342
343
bool isUserDeclared(ObjCIvarDecl *ivarD) const {
344
return ivarD && !ivarD->getSynthesize();
345
}
346
347
QualType getPropertyType(PropsTy &props) const {
348
assert(!props.empty());
349
QualType ty = props[0].PropD->getType().getUnqualifiedType();
350
351
#ifndef NDEBUG
352
for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
353
assert(ty == I->PropD->getType().getUnqualifiedType());
354
#endif
355
356
return ty;
357
}
358
359
ObjCPropertyAttribute::Kind getPropertyAttrs(PropsTy &props) const {
360
assert(!props.empty());
361
ObjCPropertyAttribute::Kind attrs =
362
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
375
void PropertyRewriteTraverser::traverseObjCImplementation(
376
ObjCImplementationContext &ImplCtx) {
377
PropertiesRewriter(ImplCtx.getMigrationContext())
378
.doTransform(ImplCtx.getImplementationDecl());
379
}
380
381