blob: ca845b6b330d236dc7f5e27f21b67ceaabe623d5 [file] [log] [blame]
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +00001//===--- TransProperties.cpp - Tranformations to ARC mode -----------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000010// rewriteProperties:
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000011//
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000012// - Adds strong/weak/unsafe_unretained ownership specifier to properties that
13// are missing one.
14// - Migrates properties from (retain) to (strong) and (assign) to
15// (unsafe_unretained/weak).
16// - If a property is synthesized, adds the ownership specifier in the ivar
17// backing the property.
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000018//
19// @interface Foo : NSObject {
20// NSObject *x;
21// }
22// @property (assign) id x;
23// @end
24// ---->
25// @interface Foo : NSObject {
26// NSObject *__weak x;
27// }
28// @property (weak) id x;
29// @end
30//
31//===----------------------------------------------------------------------===//
32
33#include "Transforms.h"
34#include "Internals.h"
35#include "clang/Sema/SemaDiagnostic.h"
36#include "clang/Basic/SourceManager.h"
37#include "clang/Lex/Lexer.h"
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000038#include <map>
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000039
40using namespace clang;
41using namespace arcmt;
42using namespace trans;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000043
44namespace {
45
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000046class PropertiesRewriter {
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000047 MigrationPass &Pass;
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +000048 ObjCImplementationDecl *CurImplD;
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000049
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000050 struct PropData {
51 ObjCPropertyDecl *PropD;
52 ObjCIvarDecl *IvarD;
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000053 ObjCPropertyImplDecl *ImplD;
54
55 PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000056 };
57
Chris Lattner5f9e2722011-07-23 10:55:15 +000058 typedef SmallVector<PropData, 2> PropsTy;
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000059 typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
60 AtPropDeclsTy AtProps;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000061
62public:
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000063 PropertiesRewriter(MigrationPass &pass) : Pass(pass) { }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000064
65 void doTransform(ObjCImplementationDecl *D) {
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +000066 CurImplD = D;
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000067 ObjCInterfaceDecl *iface = D->getClassInterface();
68 if (!iface)
69 return;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000070
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000071 for (ObjCInterfaceDecl::prop_iterator
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000072 propI = iface->prop_begin(),
73 propE = iface->prop_end(); propI != propE; ++propI) {
74 if (propI->getAtLoc().isInvalid())
75 continue;
76 PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
77 props.push_back(*propI);
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000078 }
79
80 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
81 prop_impl_iterator;
82 for (prop_impl_iterator
83 I = prop_impl_iterator(D->decls_begin()),
84 E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000085 ObjCPropertyImplDecl *implD = *I;
86 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
87 continue;
88 ObjCPropertyDecl *propD = implD->getPropertyDecl();
89 if (!propD || propD->isInvalidDecl())
90 continue;
91 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
92 if (!ivarD || ivarD->isInvalidDecl())
93 continue;
94 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
95 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
96 if (findAtLoc == AtProps.end())
97 continue;
98
99 PropsTy &props = findAtLoc->second;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000100 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
101 if (I->PropD == propD) {
102 I->IvarD = ivarD;
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000103 I->ImplD = implD;
104 break;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000105 }
106 }
107 }
108
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000109 for (AtPropDeclsTy::iterator
110 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
111 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
112 PropsTy &props = I->second;
113 QualType ty = getPropertyType(props);
114 if (!ty->isObjCRetainableType())
115 continue;
116 if (hasIvarWithExplicitOwnership(props))
117 continue;
118
119 Transaction Trans(Pass.TA);
120 rewriteProperty(props, atLoc);
121 }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000122 }
123
124private:
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000125 void rewriteProperty(PropsTy &props, SourceLocation atLoc) const {
126 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
127
128 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
129 ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
130 ObjCPropertyDecl::OBJC_PR_strong |
131 ObjCPropertyDecl::OBJC_PR_weak))
132 return;
133
134 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
135 rewriteAttribute("retain", "strong", atLoc);
136 return;
137 }
138
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +0000139 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
140 if (hasIvarAssignedAPlusOneObject(props)) {
141 rewriteAttribute("assign", "strong", atLoc);
142 return;
143 }
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000144 return rewriteAssign(props, atLoc);
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +0000145 }
146
147 if (hasIvarAssignedAPlusOneObject(props))
148 return maybeAddStrongAttr(props, atLoc);
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000149
150 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
151 }
152
153 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
154 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
155
156 bool rewroteAttr = rewriteAttribute("assign",
157 canUseWeak ? "weak" : "unsafe_unretained",
158 atLoc);
159 if (!rewroteAttr)
160 canUseWeak = false;
161
162 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
163 if (isUserDeclared(I->IvarD))
164 Pass.TA.insert(I->IvarD->getLocation(),
165 canUseWeak ? "__weak " : "__unsafe_unretained ");
166 if (I->ImplD)
167 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
168 I->ImplD->getLocation());
169 }
170 }
171
172 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
173 SourceLocation atLoc) const {
174 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000175
176 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +0000177 if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
178 !hasAllIvarsBacked(props)) {
179 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
180 atLoc);
181 if (!addedAttr)
182 canUseWeak = false;
183 }
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000184
185 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
186 if (isUserDeclared(I->IvarD))
187 Pass.TA.insert(I->IvarD->getLocation(),
188 canUseWeak ? "__weak " : "__unsafe_unretained ");
189 if (I->ImplD) {
190 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
191 I->ImplD->getLocation());
192 Pass.TA.clearDiagnostic(
193 diag::err_arc_objc_property_default_assign_on_object,
194 I->ImplD->getLocation());
195 }
196 }
197 }
198
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +0000199 void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const {
200 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
201
202 if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
203 !hasAllIvarsBacked(props)) {
204 addAttribute("strong", atLoc);
205 }
206
207 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
208 if (I->ImplD) {
209 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
210 I->ImplD->getLocation());
211 Pass.TA.clearDiagnostic(
212 diag::err_arc_objc_property_default_assign_on_object,
213 I->ImplD->getLocation());
214 }
215 }
216 }
217
Chris Lattner5f9e2722011-07-23 10:55:15 +0000218 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000219 SourceLocation atLoc) const {
220 if (atLoc.isMacroID())
221 return false;
222
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000223 SourceManager &SM = Pass.Ctx.getSourceManager();
224
225 // Break down the source location.
226 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
227
228 // Try to load the file buffer.
229 bool invalidTemp = false;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000230 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000231 if (invalidTemp)
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000232 return false;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000233
234 const char *tokenBegin = file.data() + locInfo.second;
235
236 // Lex from the start of the given location.
237 Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
238 Pass.Ctx.getLangOptions(),
239 file.begin(), tokenBegin, file.end());
240 Token tok;
241 lexer.LexFromRawLexer(tok);
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000242 if (tok.isNot(tok::at)) return false;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000243 lexer.LexFromRawLexer(tok);
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000244 if (tok.isNot(tok::raw_identifier)) return false;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000245 if (StringRef(tok.getRawIdentifierData(), tok.getLength())
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000246 != "property")
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000247 return false;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000248 lexer.LexFromRawLexer(tok);
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000249 if (tok.isNot(tok::l_paren)) return false;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000250
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000251 lexer.LexFromRawLexer(tok);
252 if (tok.is(tok::r_paren))
253 return false;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000254
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000255 while (1) {
256 if (tok.isNot(tok::raw_identifier)) return false;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000257 StringRef ident(tok.getRawIdentifierData(), tok.getLength());
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000258 if (ident == fromAttr) {
259 Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
260 return true;
261 }
262
263 do {
264 lexer.LexFromRawLexer(tok);
265 } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
266 if (tok.is(tok::r_paren))
267 break;
268 lexer.LexFromRawLexer(tok);
269 }
270
271 return false;
272 }
273
Chris Lattner5f9e2722011-07-23 10:55:15 +0000274 bool addAttribute(StringRef attr, SourceLocation atLoc) const {
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000275 if (atLoc.isMacroID())
276 return false;
277
278 SourceManager &SM = Pass.Ctx.getSourceManager();
279
280 // Break down the source location.
281 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
282
283 // Try to load the file buffer.
284 bool invalidTemp = false;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000285 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000286 if (invalidTemp)
287 return false;
288
289 const char *tokenBegin = file.data() + locInfo.second;
290
291 // Lex from the start of the given location.
292 Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
293 Pass.Ctx.getLangOptions(),
294 file.begin(), tokenBegin, file.end());
295 Token tok;
296 lexer.LexFromRawLexer(tok);
297 if (tok.isNot(tok::at)) return false;
298 lexer.LexFromRawLexer(tok);
299 if (tok.isNot(tok::raw_identifier)) return false;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000300 if (StringRef(tok.getRawIdentifierData(), tok.getLength())
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000301 != "property")
302 return false;
303 lexer.LexFromRawLexer(tok);
304
305 if (tok.isNot(tok::l_paren)) {
306 Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
307 return true;
308 }
309
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000310 lexer.LexFromRawLexer(tok);
311 if (tok.is(tok::r_paren)) {
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000312 Pass.TA.insert(tok.getLocation(), attr);
313 return true;
314 }
315
316 if (tok.isNot(tok::raw_identifier)) return false;
317
318 Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
319 return true;
320 }
321
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +0000322 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
323 ObjCIvarDecl *Ivar;
324 public:
325 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
326
327 bool VisitBinAssign(BinaryOperator *E) {
328 Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
329 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
330 if (RE->getDecl() != Ivar)
331 return true;
332
Argyrios Kyrtzidis94a90162011-08-10 21:46:48 +0000333 if (ObjCMessageExpr *
334 ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
335 if (ME->getMethodFamily() == OMF_retain)
336 return false;
337
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +0000338 ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
339 while (implCE && implCE->getCastKind() == CK_BitCast)
340 implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
341
John McCall33e56f32011-09-10 06:18:15 +0000342 if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +0000343 return false;
344 }
345
346 return true;
347 }
348 };
349
350 bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
351 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
352 PlusOneAssign oneAssign(I->IvarD);
353 bool notFound = oneAssign.TraverseDecl(CurImplD);
354 if (!notFound)
355 return true;
356 }
357
358 return false;
359 }
360
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000361 bool hasIvarWithExplicitOwnership(PropsTy &props) const {
362 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
363 if (isUserDeclared(I->IvarD)) {
364 if (isa<AttributedType>(I->IvarD->getType()))
365 return true;
366 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
367 != Qualifiers::OCL_Strong)
368 return true;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000369 }
370 }
371
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000372 return false;
373 }
374
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +0000375 bool hasAllIvarsBacked(PropsTy &props) const {
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000376 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
Argyrios Kyrtzidis18fd0c62011-07-27 05:28:18 +0000377 if (!isUserDeclared(I->IvarD))
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000378 return false;
379
380 return true;
381 }
382
383 bool isUserDeclared(ObjCIvarDecl *ivarD) const {
384 return ivarD && !ivarD->getSynthesize();
385 }
386
387 QualType getPropertyType(PropsTy &props) const {
388 assert(!props.empty());
389 QualType ty = props[0].PropD->getType();
390
391#ifndef NDEBUG
392 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
393 assert(ty == I->PropD->getType());
394#endif
395
396 return ty;
397 }
398
399 ObjCPropertyDecl::PropertyAttributeKind
400 getPropertyAttrs(PropsTy &props) const {
401 assert(!props.empty());
402 ObjCPropertyDecl::PropertyAttributeKind
403 attrs = props[0].PropD->getPropertyAttributesAsWritten();
404
405#ifndef NDEBUG
406 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
407 assert(attrs == I->PropD->getPropertyAttributesAsWritten());
408#endif
409
410 return attrs;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000411 }
412};
413
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000414class ImplementationChecker :
415 public RecursiveASTVisitor<ImplementationChecker> {
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000416 MigrationPass &Pass;
417
418public:
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000419 ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000420
421 bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000422 PropertiesRewriter(Pass).doTransform(D);
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000423 return true;
424 }
425};
426
427} // anonymous namespace
428
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000429void trans::rewriteProperties(MigrationPass &pass) {
430 ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000431}