clang 20.0.0git
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
39using namespace clang;
40using namespace arcmt;
41using namespace trans;
42
43namespace {
44
45class 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;
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
72public:
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
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 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
144private:
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 &
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()
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());
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
376 ObjCImplementationContext &ImplCtx) {
377 PropertiesRewriter(ImplCtx.getMigrationContext())
378 .doTransform(ImplCtx.getImplementationDecl());
379}
const Decl * D
Expr * E
SourceLocation Loc
Definition: SemaObjC.cpp:758
Defines the SourceManager interface.
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3860
specific_decl_iterator - Iterates over a subrange of declarations stored in a DeclContext,...
Definition: DeclBase.h:2370
bool isInvalidDecl() const
Definition: DeclBase.h:595
This represents one expression.
Definition: Expr.h:110
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition: Expr.cpp:3070
ObjCContainerDecl - Represents a container for method declarations.
Definition: DeclObjC.h:947
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2596
Represents an ObjC class declaration.
Definition: DeclObjC.h:1153
visible_extensions_range visible_extensions() const
Definition: DeclObjC.h:1722
ObjCIvarDecl - Represents an ObjC instance variable.
Definition: DeclObjC.h:1951
bool getSynthesize() const
Definition: DeclObjC.h:2006
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:549
Represents one property declaration in an Objective-C interface.
Definition: DeclObjC.h:730
SourceLocation getAtLoc() const
Definition: DeclObjC.h:795
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
Definition: DeclObjC.h:2804
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:2878
Kind getPropertyImplementation() const
Definition: DeclObjC.h:2874
ObjCPropertyDecl * getPropertyDecl() const
Definition: DeclObjC.h:2869
A (possibly-)qualified type.
Definition: Type.h:941
@ OCL_Strong
Assigning into this object requires the old value to be released and the new value to be retained.
Definition: Type.h:348
@ OCL_Weak
Reading or writing from this object requires a barrier call.
Definition: Type.h:351
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Encodes a location in the source.
bool isGCMigration() const
Definition: Internals.h:165
TransformActions & TA
Definition: Internals.h:152
void insert(SourceLocation loc, StringRef text)
bool clearDiagnostic(ArrayRef< unsigned > IDs, SourceRange range)
bool addPropertyAttribute(StringRef attr, SourceLocation atLoc)
Definition: Transforms.cpp:461
bool removePropertyAttribute(StringRef fromAttr, SourceLocation atLoc)
Definition: Transforms.h:115
llvm::DenseSet< SourceLocation > AtPropsWeak
Set of raw '@' locations for 'assign' properties group that contain GC __weak.
Definition: Transforms.h:101
bool rewritePropertyAttribute(StringRef fromAttr, StringRef toAttr, SourceLocation atLoc)
Definition: Transforms.cpp:379
ObjCImplementationDecl * getImplementationDecl()
Definition: Transforms.h:69
void traverseObjCImplementation(ObjCImplementationContext &ImplCtx) override
bool canApplyWeak(ASTContext &Ctx, QualType type, bool AllowOnUnknownClass=false)
Determine whether we can add weak to the given type.
Definition: Transforms.cpp:39
bool isPlusOneAssign(const BinaryOperator *E)
Definition: Transforms.cpp:68
unsigned kind
All of the diagnostics that can be emitted by the frontend.
Definition: DiagnosticIDs.h:68
The JSON file list parser is used to communicate input to InstallAPI.