1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
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 | |
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 | |
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; |
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 | .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 | .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 | |
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 | PropD->getType().getUnqualifiedType()", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/TransProperties.cpp", 351, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">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 | PropD->getPropertyAttributesAsWritten()", "/home/seafit/code_projects/clang_source/clang/lib/ARCMigrate/TransProperties.cpp", 365, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(attrs == I->PropD->getPropertyAttributesAsWritten()); |
366 | #endif |
367 | |
368 | return attrs; |
369 | } |
370 | }; |
371 | |
372 | } |
373 | |
374 | void PropertyRewriteTraverser::traverseObjCImplementation( |
375 | ObjCImplementationContext &ImplCtx) { |
376 | PropertiesRewriter(ImplCtx.getMigrationContext()) |
377 | .doTransform(ImplCtx.getImplementationDecl()); |
378 | } |
379 | |