1 //===--- TransProperties.cpp - Transformations to ARC mode ----------------===//
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
7 //===----------------------------------------------------------------------===//
11 // - Adds strong/weak/unsafe_unretained ownership specifier to properties that
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.
18 // @interface Foo : NSObject {
21 // @property (assign) id x;
24 // @interface Foo : NSObject {
25 // NSObject *__weak x;
27 // @property (weak) id x;
30 //===----------------------------------------------------------------------===//
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"
39 using namespace clang
;
40 using namespace arcmt
;
41 using namespace trans
;
45 class PropertiesRewriter
{
46 MigrationContext
&MigrateCtx
;
48 ObjCImplementationDecl
*CurImplD
;
52 PropAction_RetainReplacedWithStrong
,
53 PropAction_AssignRemoved
,
54 PropAction_AssignRewritten
,
55 PropAction_MaybeAddWeakOrUnsafe
59 ObjCPropertyDecl
*PropD
;
61 ObjCPropertyImplDecl
*ImplD
;
63 PropData(ObjCPropertyDecl
*propD
)
64 : PropD(propD
), IvarD(nullptr), ImplD(nullptr) {}
67 typedef SmallVector
<PropData
, 2> PropsTy
;
68 typedef std::map
<SourceLocation
, PropsTy
> AtPropDeclsTy
;
69 AtPropDeclsTy AtProps
;
70 llvm::DenseMap
<IdentifierInfo
*, PropActionKind
> ActionOnProp
;
73 explicit PropertiesRewriter(MigrationContext
&MigrateCtx
)
74 : MigrateCtx(MigrateCtx
), Pass(MigrateCtx
.Pass
) { }
76 static void collectProperties(ObjCContainerDecl
*D
, AtPropDeclsTy
&AtProps
,
77 AtPropDeclsTy
*PrevAtProps
= nullptr) {
78 for (auto *Prop
: D
->instance_properties()) {
79 SourceLocation Loc
= Prop
->getAtLoc();
83 if (PrevAtProps
->find(Loc
) != PrevAtProps
->end())
85 PropsTy
&props
= AtProps
[Loc
];
86 props
.push_back(Prop
);
90 void doTransform(ObjCImplementationDecl
*D
) {
92 ObjCInterfaceDecl
*iface
= D
->getClassInterface();
96 collectProperties(iface
, AtProps
);
98 // Look through extensions.
99 for (auto *Ext
: iface
->visible_extensions())
100 collectProperties(Ext
, AtProps
);
102 typedef DeclContext::specific_decl_iterator
<ObjCPropertyImplDecl
>
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
)
110 ObjCPropertyDecl
*propD
= implD
->getPropertyDecl();
111 if (!propD
|| propD
->isInvalidDecl())
113 ObjCIvarDecl
*ivarD
= implD
->getPropertyIvarDecl();
114 if (!ivarD
|| ivarD
->isInvalidDecl())
116 AtPropDeclsTy::iterator findAtLoc
= AtProps
.find(propD
->getAtLoc());
117 if (findAtLoc
== AtProps
.end())
120 PropsTy
&props
= findAtLoc
->second
;
121 for (PropsTy::iterator I
= props
.begin(), E
= props
.end(); I
!= E
; ++I
) {
122 if (I
->PropD
== propD
) {
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())
136 if (hasIvarWithExplicitARCOwnership(props
))
139 Transaction
Trans(Pass
.TA
);
140 rewriteProperty(props
, atLoc
);
145 void doPropAction(PropActionKind kind
,
146 PropsTy
&props
, SourceLocation atLoc
,
147 bool markAction
= true) {
149 for (PropsTy::iterator I
= props
.begin(), E
= props
.end(); I
!= E
; ++I
)
150 ActionOnProp
[I
->PropD
->getIdentifier()] = kind
;
153 case PropAction_None
:
155 case PropAction_RetainReplacedWithStrong
: {
156 StringRef toAttr
= "strong";
157 MigrateCtx
.rewritePropertyAttribute("retain", toAttr
, atLoc
);
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
);
169 void rewriteProperty(PropsTy
&props
, SourceLocation atLoc
) {
170 ObjCPropertyAttribute::Kind propAttrs
= getPropertyAttrs(props
);
173 (ObjCPropertyAttribute::kind_copy
|
174 ObjCPropertyAttribute::kind_unsafe_unretained
|
175 ObjCPropertyAttribute::kind_strong
| ObjCPropertyAttribute::kind_weak
))
178 if (propAttrs
& ObjCPropertyAttribute::kind_retain
) {
179 // strong is the default.
180 return doPropAction(PropAction_RetainReplacedWithStrong
, props
, atLoc
);
183 bool HasIvarAssignedAPlusOneObject
= hasIvarAssignedAPlusOneObject(props
);
185 if (propAttrs
& ObjCPropertyAttribute::kind_assign
) {
186 if (HasIvarAssignedAPlusOneObject
)
187 return doPropAction(PropAction_AssignRemoved
, props
, atLoc
);
188 return doPropAction(PropAction_AssignRewritten
, props
, atLoc
);
191 if (HasIvarAssignedAPlusOneObject
||
192 (Pass
.isGCMigration() && !hasGCWeak(props
, atLoc
)))
193 return; // 'strong' by default.
195 return doPropAction(PropAction_MaybeAddWeakOrUnsafe
, props
, atLoc
);
198 void removeAssignForDefaultStrong(PropsTy
&props
,
199 SourceLocation atLoc
) const {
200 removeAttribute("retain", atLoc
);
201 if (!removeAttribute("assign", atLoc
))
204 for (PropsTy::iterator I
= props
.begin(), E
= props
.end(); I
!= E
; ++I
) {
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());
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");
220 bool rewroteAttr
= rewriteAttribute("assign", toWhich
, atLoc
);
224 for (PropsTy::iterator I
= props
.begin(), E
= props
.end(); I
!= E
; ++I
) {
225 if (isUserDeclared(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
);
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());
242 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy
&props
,
243 SourceLocation atLoc
) const {
244 bool canUseWeak
= canApplyWeak(Pass
.Ctx
, getPropertyType(props
),
245 /*AllowOnUnknownClass=*/Pass
.isGCMigration());
247 bool addedAttr
= addAttribute(canUseWeak
? "weak" : "unsafe_unretained",
252 for (PropsTy::iterator I
= props
.begin(), E
= props
.end(); I
!= E
; ++I
) {
253 if (isUserDeclared(I
->IvarD
)) {
255 I
->IvarD
->getType().getObjCLifetime() != Qualifiers::OCL_Weak
)
256 Pass
.TA
.insert(I
->IvarD
->getLocation(),
257 canUseWeak
? "__weak " : "__unsafe_unretained ");
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());
271 bool removeAttribute(StringRef fromAttr
, SourceLocation atLoc
) const {
272 return MigrateCtx
.removePropertyAttribute(fromAttr
, atLoc
);
275 bool rewriteAttribute(StringRef fromAttr
, StringRef toAttr
,
276 SourceLocation atLoc
) const {
277 return MigrateCtx
.rewritePropertyAttribute(fromAttr
, toAttr
, atLoc
);
280 bool addAttribute(StringRef attr
, SourceLocation atLoc
) const {
281 return MigrateCtx
.addPropertyAttribute(attr
, atLoc
);
284 class PlusOneAssign
: public RecursiveASTVisitor
<PlusOneAssign
> {
287 PlusOneAssign(ObjCIvarDecl
*D
) : Ivar(D
) {}
289 bool VisitBinaryOperator(BinaryOperator
*E
) {
290 if (E
->getOpcode() != BO_Assign
)
293 Expr
*lhs
= E
->getLHS()->IgnoreParenImpCasts();
294 if (ObjCIvarRefExpr
*RE
= dyn_cast
<ObjCIvarRefExpr
>(lhs
)) {
295 if (RE
->getDecl() != Ivar
)
298 if (isPlusOneAssign(E
))
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
);
317 bool hasIvarWithExplicitARCOwnership(PropsTy
&props
) const {
318 if (Pass
.isGCMigration())
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()))
325 if (I
->IvarD
->getType().getLocalQualifiers().getObjCLifetime()
326 != Qualifiers::OCL_Strong
)
334 // Returns true if all declarations in the @property have GC __weak.
335 bool hasGCWeak(PropsTy
&props
, SourceLocation atLoc
) const {
336 if (!Pass
.isGCMigration())
340 return MigrateCtx
.AtPropsWeak
.count(atLoc
);
343 bool isUserDeclared(ObjCIvarDecl
*ivarD
) const {
344 return ivarD
&& !ivarD
->getSynthesize();
347 QualType
getPropertyType(PropsTy
&props
) const {
348 assert(!props
.empty());
349 QualType ty
= props
[0].PropD
->getType().getUnqualifiedType();
352 for (PropsTy::iterator I
= props
.begin(), E
= props
.end(); I
!= E
; ++I
)
353 assert(ty
== I
->PropD
->getType().getUnqualifiedType());
359 ObjCPropertyAttribute::Kind
getPropertyAttrs(PropsTy
&props
) const {
360 assert(!props
.empty());
361 ObjCPropertyAttribute::Kind attrs
=
362 props
[0].PropD
->getPropertyAttributesAsWritten();
365 for (PropsTy::iterator I
= props
.begin(), E
= props
.end(); I
!= E
; ++I
)
366 assert(attrs
== I
->PropD
->getPropertyAttributesAsWritten());
373 } // anonymous namespace
375 void PropertyRewriteTraverser::traverseObjCImplementation(
376 ObjCImplementationContext
&ImplCtx
) {
377 PropertiesRewriter(ImplCtx
.getMigrationContext())
378 .doTransform(ImplCtx
.getImplementationDecl());