blob: 365168e933b3c4b1677e8ce81f4abd42a84f8911 [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 Kyrtzidis14c4b442011-07-13 19:22:00 +000048
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000049 struct PropData {
50 ObjCPropertyDecl *PropD;
51 ObjCIvarDecl *IvarD;
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000052 ObjCPropertyImplDecl *ImplD;
53
54 PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000055 };
56
Chris Lattner5f9e2722011-07-23 10:55:15 +000057 typedef SmallVector<PropData, 2> PropsTy;
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000058 typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
59 AtPropDeclsTy AtProps;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000060
61public:
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000062 PropertiesRewriter(MigrationPass &pass) : Pass(pass) { }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000063
64 void doTransform(ObjCImplementationDecl *D) {
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000065 ObjCInterfaceDecl *iface = D->getClassInterface();
66 if (!iface)
67 return;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000068
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000069 for (ObjCInterfaceDecl::prop_iterator
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000070 propI = iface->prop_begin(),
71 propE = iface->prop_end(); propI != propE; ++propI) {
72 if (propI->getAtLoc().isInvalid())
73 continue;
74 PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
75 props.push_back(*propI);
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000076 }
77
78 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
79 prop_impl_iterator;
80 for (prop_impl_iterator
81 I = prop_impl_iterator(D->decls_begin()),
82 E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +000083 ObjCPropertyImplDecl *implD = *I;
84 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
85 continue;
86 ObjCPropertyDecl *propD = implD->getPropertyDecl();
87 if (!propD || propD->isInvalidDecl())
88 continue;
89 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
90 if (!ivarD || ivarD->isInvalidDecl())
91 continue;
92 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
93 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
94 if (findAtLoc == AtProps.end())
95 continue;
96
97 PropsTy &props = findAtLoc->second;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +000098 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
99 if (I->PropD == propD) {
100 I->IvarD = ivarD;
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000101 I->ImplD = implD;
102 break;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000103 }
104 }
105 }
106
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000107 for (AtPropDeclsTy::iterator
108 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
109 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
110 PropsTy &props = I->second;
111 QualType ty = getPropertyType(props);
112 if (!ty->isObjCRetainableType())
113 continue;
114 if (hasIvarWithExplicitOwnership(props))
115 continue;
116
117 Transaction Trans(Pass.TA);
118 rewriteProperty(props, atLoc);
119 }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000120 }
121
122private:
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000123 void rewriteProperty(PropsTy &props, SourceLocation atLoc) const {
124 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
125
126 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
127 ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
128 ObjCPropertyDecl::OBJC_PR_strong |
129 ObjCPropertyDecl::OBJC_PR_weak))
130 return;
131
132 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
133 rewriteAttribute("retain", "strong", atLoc);
134 return;
135 }
136
137 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign)
138 return rewriteAssign(props, atLoc);
139
140 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
141 }
142
143 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
144 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
145
146 bool rewroteAttr = rewriteAttribute("assign",
147 canUseWeak ? "weak" : "unsafe_unretained",
148 atLoc);
149 if (!rewroteAttr)
150 canUseWeak = false;
151
152 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
153 if (isUserDeclared(I->IvarD))
154 Pass.TA.insert(I->IvarD->getLocation(),
155 canUseWeak ? "__weak " : "__unsafe_unretained ");
156 if (I->ImplD)
157 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
158 I->ImplD->getLocation());
159 }
160 }
161
162 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
163 SourceLocation atLoc) const {
164 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
165 if ((propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) &&
166 hasNoBackingIvars(props))
167 return;
168
169 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
170 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
171 atLoc);
172 if (!addedAttr)
173 canUseWeak = false;
174
175 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
176 if (isUserDeclared(I->IvarD))
177 Pass.TA.insert(I->IvarD->getLocation(),
178 canUseWeak ? "__weak " : "__unsafe_unretained ");
179 if (I->ImplD) {
180 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
181 I->ImplD->getLocation());
182 Pass.TA.clearDiagnostic(
183 diag::err_arc_objc_property_default_assign_on_object,
184 I->ImplD->getLocation());
185 }
186 }
187 }
188
Chris Lattner5f9e2722011-07-23 10:55:15 +0000189 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000190 SourceLocation atLoc) const {
191 if (atLoc.isMacroID())
192 return false;
193
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000194 SourceManager &SM = Pass.Ctx.getSourceManager();
195
196 // Break down the source location.
197 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
198
199 // Try to load the file buffer.
200 bool invalidTemp = false;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000201 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000202 if (invalidTemp)
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000203 return false;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000204
205 const char *tokenBegin = file.data() + locInfo.second;
206
207 // Lex from the start of the given location.
208 Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
209 Pass.Ctx.getLangOptions(),
210 file.begin(), tokenBegin, file.end());
211 Token tok;
212 lexer.LexFromRawLexer(tok);
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000213 if (tok.isNot(tok::at)) return false;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000214 lexer.LexFromRawLexer(tok);
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000215 if (tok.isNot(tok::raw_identifier)) return false;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000216 if (StringRef(tok.getRawIdentifierData(), tok.getLength())
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000217 != "property")
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000218 return false;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000219 lexer.LexFromRawLexer(tok);
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000220 if (tok.isNot(tok::l_paren)) return false;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000221
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000222 lexer.LexFromRawLexer(tok);
223 if (tok.is(tok::r_paren))
224 return false;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000225
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000226 while (1) {
227 if (tok.isNot(tok::raw_identifier)) return false;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000228 StringRef ident(tok.getRawIdentifierData(), tok.getLength());
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000229 if (ident == fromAttr) {
230 Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
231 return true;
232 }
233
234 do {
235 lexer.LexFromRawLexer(tok);
236 } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
237 if (tok.is(tok::r_paren))
238 break;
239 lexer.LexFromRawLexer(tok);
240 }
241
242 return false;
243 }
244
Chris Lattner5f9e2722011-07-23 10:55:15 +0000245 bool addAttribute(StringRef attr, SourceLocation atLoc) const {
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000246 if (atLoc.isMacroID())
247 return false;
248
249 SourceManager &SM = Pass.Ctx.getSourceManager();
250
251 // Break down the source location.
252 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
253
254 // Try to load the file buffer.
255 bool invalidTemp = false;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000256 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000257 if (invalidTemp)
258 return false;
259
260 const char *tokenBegin = file.data() + locInfo.second;
261
262 // Lex from the start of the given location.
263 Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
264 Pass.Ctx.getLangOptions(),
265 file.begin(), tokenBegin, file.end());
266 Token tok;
267 lexer.LexFromRawLexer(tok);
268 if (tok.isNot(tok::at)) return false;
269 lexer.LexFromRawLexer(tok);
270 if (tok.isNot(tok::raw_identifier)) return false;
Chris Lattner5f9e2722011-07-23 10:55:15 +0000271 if (StringRef(tok.getRawIdentifierData(), tok.getLength())
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000272 != "property")
273 return false;
274 lexer.LexFromRawLexer(tok);
275
276 if (tok.isNot(tok::l_paren)) {
277 Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
278 return true;
279 }
280
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000281 lexer.LexFromRawLexer(tok);
282 if (tok.is(tok::r_paren)) {
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000283 Pass.TA.insert(tok.getLocation(), attr);
284 return true;
285 }
286
287 if (tok.isNot(tok::raw_identifier)) return false;
288
289 Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
290 return true;
291 }
292
293 bool hasIvarWithExplicitOwnership(PropsTy &props) const {
294 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
295 if (isUserDeclared(I->IvarD)) {
296 if (isa<AttributedType>(I->IvarD->getType()))
297 return true;
298 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
299 != Qualifiers::OCL_Strong)
300 return true;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000301 }
302 }
303
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000304 return false;
305 }
306
307 bool hasNoBackingIvars(PropsTy &props) const {
308 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
Argyrios Kyrtzidis96886092011-07-13 19:47:57 +0000309 if (I->IvarD)
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000310 return false;
311
312 return true;
313 }
314
315 bool isUserDeclared(ObjCIvarDecl *ivarD) const {
316 return ivarD && !ivarD->getSynthesize();
317 }
318
319 QualType getPropertyType(PropsTy &props) const {
320 assert(!props.empty());
321 QualType ty = props[0].PropD->getType();
322
323#ifndef NDEBUG
324 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
325 assert(ty == I->PropD->getType());
326#endif
327
328 return ty;
329 }
330
331 ObjCPropertyDecl::PropertyAttributeKind
332 getPropertyAttrs(PropsTy &props) const {
333 assert(!props.empty());
334 ObjCPropertyDecl::PropertyAttributeKind
335 attrs = props[0].PropD->getPropertyAttributesAsWritten();
336
337#ifndef NDEBUG
338 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
339 assert(attrs == I->PropD->getPropertyAttributesAsWritten());
340#endif
341
342 return attrs;
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000343 }
344};
345
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000346class ImplementationChecker :
347 public RecursiveASTVisitor<ImplementationChecker> {
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000348 MigrationPass &Pass;
349
350public:
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000351 ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000352
353 bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000354 PropertiesRewriter(Pass).doTransform(D);
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000355 return true;
356 }
357};
358
359} // anonymous namespace
360
Argyrios Kyrtzidis14c4b442011-07-13 19:22:00 +0000361void trans::rewriteProperties(MigrationPass &pass) {
362 ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000363}