1 //===--- 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 //
10 // rewriteProperties:
11 //
12 // - 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.
18 //
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"
38 #include <map>
39
40 using namespace clang;
41 using namespace arcmt;
42 using namespace trans;
43 using llvm::StringRef;
44
45 namespace {
46
47 class PropertiesRewriter {
48 MigrationPass &Pass;
49
50 struct PropData {
51 ObjCPropertyDecl *PropD;
52 ObjCIvarDecl *IvarD;
53 ObjCPropertyImplDecl *ImplD;
54
PropData__anon16a169420111::PropertiesRewriter::PropData55 PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
56 };
57
58 typedef llvm::SmallVector<PropData, 2> PropsTy;
59 typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
60 AtPropDeclsTy AtProps;
61
62 public:
PropertiesRewriter(MigrationPass & pass)63 PropertiesRewriter(MigrationPass &pass) : Pass(pass) { }
64
doTransform(ObjCImplementationDecl * D)65 void doTransform(ObjCImplementationDecl *D) {
66 ObjCInterfaceDecl *iface = D->getClassInterface();
67 if (!iface)
68 return;
69
70 for (ObjCInterfaceDecl::prop_iterator
71 propI = iface->prop_begin(),
72 propE = iface->prop_end(); propI != propE; ++propI) {
73 if (propI->getAtLoc().isInvalid())
74 continue;
75 PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
76 props.push_back(*propI);
77 }
78
79 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
80 prop_impl_iterator;
81 for (prop_impl_iterator
82 I = prop_impl_iterator(D->decls_begin()),
83 E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
84 ObjCPropertyImplDecl *implD = *I;
85 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
86 continue;
87 ObjCPropertyDecl *propD = implD->getPropertyDecl();
88 if (!propD || propD->isInvalidDecl())
89 continue;
90 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
91 if (!ivarD || ivarD->isInvalidDecl())
92 continue;
93 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
94 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
95 if (findAtLoc == AtProps.end())
96 continue;
97
98 PropsTy &props = findAtLoc->second;
99 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
100 if (I->PropD == propD) {
101 I->IvarD = ivarD;
102 I->ImplD = implD;
103 break;
104 }
105 }
106 }
107
108 for (AtPropDeclsTy::iterator
109 I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
110 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
111 PropsTy &props = I->second;
112 QualType ty = getPropertyType(props);
113 if (!ty->isObjCRetainableType())
114 continue;
115 if (hasIvarWithExplicitOwnership(props))
116 continue;
117
118 Transaction Trans(Pass.TA);
119 rewriteProperty(props, atLoc);
120 }
121 }
122
123 private:
rewriteProperty(PropsTy & props,SourceLocation atLoc) const124 void rewriteProperty(PropsTy &props, SourceLocation atLoc) const {
125 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
126
127 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
128 ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
129 ObjCPropertyDecl::OBJC_PR_strong |
130 ObjCPropertyDecl::OBJC_PR_weak))
131 return;
132
133 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
134 rewriteAttribute("retain", "strong", atLoc);
135 return;
136 }
137
138 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign)
139 return rewriteAssign(props, atLoc);
140
141 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
142 }
143
rewriteAssign(PropsTy & props,SourceLocation atLoc) const144 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
145 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
146
147 bool rewroteAttr = rewriteAttribute("assign",
148 canUseWeak ? "weak" : "unsafe_unretained",
149 atLoc);
150 if (!rewroteAttr)
151 canUseWeak = false;
152
153 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
154 if (isUserDeclared(I->IvarD))
155 Pass.TA.insert(I->IvarD->getLocation(),
156 canUseWeak ? "__weak " : "__unsafe_unretained ");
157 if (I->ImplD)
158 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
159 I->ImplD->getLocation());
160 }
161 }
162
maybeAddWeakOrUnsafeUnretainedAttr(PropsTy & props,SourceLocation atLoc) const163 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
164 SourceLocation atLoc) const {
165 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
166 if ((propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) &&
167 hasNoBackingIvars(props))
168 return;
169
170 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
171 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
172 atLoc);
173 if (!addedAttr)
174 canUseWeak = false;
175
176 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
177 if (isUserDeclared(I->IvarD))
178 Pass.TA.insert(I->IvarD->getLocation(),
179 canUseWeak ? "__weak " : "__unsafe_unretained ");
180 if (I->ImplD) {
181 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
182 I->ImplD->getLocation());
183 Pass.TA.clearDiagnostic(
184 diag::err_arc_objc_property_default_assign_on_object,
185 I->ImplD->getLocation());
186 }
187 }
188 }
189
rewriteAttribute(llvm::StringRef fromAttr,llvm::StringRef toAttr,SourceLocation atLoc) const190 bool rewriteAttribute(llvm::StringRef fromAttr, llvm::StringRef toAttr,
191 SourceLocation atLoc) const {
192 if (atLoc.isMacroID())
193 return false;
194
195 SourceManager &SM = Pass.Ctx.getSourceManager();
196
197 // Break down the source location.
198 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
199
200 // Try to load the file buffer.
201 bool invalidTemp = false;
202 llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
203 if (invalidTemp)
204 return false;
205
206 const char *tokenBegin = file.data() + locInfo.second;
207
208 // Lex from the start of the given location.
209 Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
210 Pass.Ctx.getLangOptions(),
211 file.begin(), tokenBegin, file.end());
212 Token tok;
213 lexer.LexFromRawLexer(tok);
214 if (tok.isNot(tok::at)) return false;
215 lexer.LexFromRawLexer(tok);
216 if (tok.isNot(tok::raw_identifier)) return false;
217 if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength())
218 != "property")
219 return false;
220 lexer.LexFromRawLexer(tok);
221 if (tok.isNot(tok::l_paren)) return false;
222
223 lexer.LexFromRawLexer(tok);
224 if (tok.is(tok::r_paren))
225 return false;
226
227 while (1) {
228 if (tok.isNot(tok::raw_identifier)) return false;
229 llvm::StringRef ident(tok.getRawIdentifierData(), tok.getLength());
230 if (ident == fromAttr) {
231 Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
232 return true;
233 }
234
235 do {
236 lexer.LexFromRawLexer(tok);
237 } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
238 if (tok.is(tok::r_paren))
239 break;
240 lexer.LexFromRawLexer(tok);
241 }
242
243 return false;
244 }
245
addAttribute(llvm::StringRef attr,SourceLocation atLoc) const246 bool addAttribute(llvm::StringRef attr, SourceLocation atLoc) const {
247 if (atLoc.isMacroID())
248 return false;
249
250 SourceManager &SM = Pass.Ctx.getSourceManager();
251
252 // Break down the source location.
253 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
254
255 // Try to load the file buffer.
256 bool invalidTemp = false;
257 llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
258 if (invalidTemp)
259 return false;
260
261 const char *tokenBegin = file.data() + locInfo.second;
262
263 // Lex from the start of the given location.
264 Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
265 Pass.Ctx.getLangOptions(),
266 file.begin(), tokenBegin, file.end());
267 Token tok;
268 lexer.LexFromRawLexer(tok);
269 if (tok.isNot(tok::at)) return false;
270 lexer.LexFromRawLexer(tok);
271 if (tok.isNot(tok::raw_identifier)) return false;
272 if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength())
273 != "property")
274 return false;
275 lexer.LexFromRawLexer(tok);
276
277 if (tok.isNot(tok::l_paren)) {
278 Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
279 return true;
280 }
281
282 lexer.LexFromRawLexer(tok);
283 if (tok.is(tok::r_paren)) {
284 Pass.TA.insert(tok.getLocation(), attr);
285 return true;
286 }
287
288 if (tok.isNot(tok::raw_identifier)) return false;
289
290 Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
291 return true;
292 }
293
hasIvarWithExplicitOwnership(PropsTy & props) const294 bool hasIvarWithExplicitOwnership(PropsTy &props) const {
295 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
296 if (isUserDeclared(I->IvarD)) {
297 if (isa<AttributedType>(I->IvarD->getType()))
298 return true;
299 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
300 != Qualifiers::OCL_Strong)
301 return true;
302 }
303 }
304
305 return false;
306 }
307
hasNoBackingIvars(PropsTy & props) const308 bool hasNoBackingIvars(PropsTy &props) const {
309 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
310 if (I->IvarD)
311 return false;
312
313 return true;
314 }
315
isUserDeclared(ObjCIvarDecl * ivarD) const316 bool isUserDeclared(ObjCIvarDecl *ivarD) const {
317 return ivarD && !ivarD->getSynthesize();
318 }
319
getPropertyType(PropsTy & props) const320 QualType getPropertyType(PropsTy &props) const {
321 assert(!props.empty());
322 QualType ty = props[0].PropD->getType();
323
324 #ifndef NDEBUG
325 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
326 assert(ty == I->PropD->getType());
327 #endif
328
329 return ty;
330 }
331
332 ObjCPropertyDecl::PropertyAttributeKind
getPropertyAttrs(PropsTy & props) const333 getPropertyAttrs(PropsTy &props) const {
334 assert(!props.empty());
335 ObjCPropertyDecl::PropertyAttributeKind
336 attrs = props[0].PropD->getPropertyAttributesAsWritten();
337
338 #ifndef NDEBUG
339 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
340 assert(attrs == I->PropD->getPropertyAttributesAsWritten());
341 #endif
342
343 return attrs;
344 }
345 };
346
347 class ImplementationChecker :
348 public RecursiveASTVisitor<ImplementationChecker> {
349 MigrationPass &Pass;
350
351 public:
ImplementationChecker(MigrationPass & pass)352 ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
353
TraverseObjCImplementationDecl(ObjCImplementationDecl * D)354 bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
355 PropertiesRewriter(Pass).doTransform(D);
356 return true;
357 }
358 };
359
360 } // anonymous namespace
361
rewriteProperties(MigrationPass & pass)362 void trans::rewriteProperties(MigrationPass &pass) {
363 ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
364 }
365