blob: 82ef717f31558713d22bcca18c8b086a0e08e30a [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//
10// changeIvarsOfAssignProperties:
11//
12// If a property is synthesized with 'assign' attribute and the user didn't
13// set a lifetime attribute, change the property to 'weak' or add
14// __unsafe_unretained if the ARC runtime is not available.
15//
16// @interface Foo : NSObject {
17// NSObject *x;
18// }
19// @property (assign) id x;
20// @end
21// ---->
22// @interface Foo : NSObject {
23// NSObject *__weak x;
24// }
25// @property (weak) id x;
26// @end
27//
28//===----------------------------------------------------------------------===//
29
30#include "Transforms.h"
31#include "Internals.h"
32#include "clang/Sema/SemaDiagnostic.h"
33#include "clang/Basic/SourceManager.h"
34#include "clang/Lex/Lexer.h"
35
36using namespace clang;
37using namespace arcmt;
38using namespace trans;
39using llvm::StringRef;
40
41namespace {
42
43class AssignPropertiesTrans {
44 MigrationPass &Pass;
45 struct PropData {
46 ObjCPropertyDecl *PropD;
47 ObjCIvarDecl *IvarD;
48 bool ShouldChangeToWeak;
49 SourceLocation ArcPropAssignErrorLoc;
50 };
51
52 typedef llvm::SmallVector<PropData, 2> PropsTy;
53 typedef llvm::DenseMap<unsigned, PropsTy> PropsMapTy;
54 PropsMapTy PropsMap;
55
56public:
57 AssignPropertiesTrans(MigrationPass &pass) : Pass(pass) { }
58
59 void doTransform(ObjCImplementationDecl *D) {
60 SourceManager &SM = Pass.Ctx.getSourceManager();
61
62 ObjCInterfaceDecl *IFace = D->getClassInterface();
63 for (ObjCInterfaceDecl::prop_iterator
64 I = IFace->prop_begin(), E = IFace->prop_end(); I != E; ++I) {
65 ObjCPropertyDecl *propD = *I;
66 unsigned loc = SM.getInstantiationLoc(propD->getAtLoc()).getRawEncoding();
67 PropsTy &props = PropsMap[loc];
68 props.push_back(PropData());
69 props.back().PropD = propD;
70 props.back().IvarD = 0;
71 props.back().ShouldChangeToWeak = false;
72 }
73
74 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
75 prop_impl_iterator;
76 for (prop_impl_iterator
77 I = prop_impl_iterator(D->decls_begin()),
78 E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
79 VisitObjCPropertyImplDecl(*I);
80 }
81
82 for (PropsMapTy::iterator
83 I = PropsMap.begin(), E = PropsMap.end(); I != E; ++I) {
84 SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
85 PropsTy &props = I->second;
86 if (shouldApplyWeakToAllProp(props)) {
87 if (changeAssignToWeak(atLoc)) {
88 // Couldn't add the 'weak' property attribute,
89 // try adding __unsafe_unretained.
90 applyUnsafeUnretained(props);
91 } else {
92 for (PropsTy::iterator
93 PI = props.begin(), PE = props.end(); PI != PE; ++PI) {
94 applyWeak(*PI);
95 }
96 }
97 } else {
98 // We should not add 'weak' attribute since not all properties need it.
99 // So just add __unsafe_unretained to the ivars.
100 applyUnsafeUnretained(props);
101 }
102 }
103 }
104
105 bool shouldApplyWeakToAllProp(PropsTy &props) {
106 for (PropsTy::iterator
107 PI = props.begin(), PE = props.end(); PI != PE; ++PI) {
108 if (!PI->ShouldChangeToWeak)
109 return false;
110 }
111 return true;
112 }
113
114 void applyWeak(PropData &prop) {
Argyrios Kyrtzidis86625b52011-07-12 22:05:17 +0000115 assert(canApplyWeak(Pass.Ctx, prop.IvarD->getType()));
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000116
117 Transaction Trans(Pass.TA);
118 Pass.TA.insert(prop.IvarD->getLocation(), "__weak ");
Argyrios Kyrtzidisb8b03132011-06-24 00:08:59 +0000119 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000120 prop.ArcPropAssignErrorLoc);
121 }
122
123 void applyUnsafeUnretained(PropsTy &props) {
124 for (PropsTy::iterator
125 PI = props.begin(), PE = props.end(); PI != PE; ++PI) {
126 if (PI->ShouldChangeToWeak) {
127 Transaction Trans(Pass.TA);
128 Pass.TA.insert(PI->IvarD->getLocation(), "__unsafe_unretained ");
Argyrios Kyrtzidisb8b03132011-06-24 00:08:59 +0000129 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000130 PI->ArcPropAssignErrorLoc);
131 }
132 }
133 }
134
135 bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) {
136 SourceManager &SM = Pass.Ctx.getSourceManager();
137
138 if (D->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
139 return true;
140 ObjCPropertyDecl *propD = D->getPropertyDecl();
141 if (!propD || propD->isInvalidDecl())
142 return true;
143 ObjCIvarDecl *ivarD = D->getPropertyIvarDecl();
144 if (!ivarD || ivarD->isInvalidDecl())
145 return true;
146 if (!(propD->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_assign))
147 return true;
148 if (isa<AttributedType>(ivarD->getType().getTypePtr()))
149 return true;
150 if (ivarD->getType().getLocalQualifiers().getObjCLifetime()
151 != Qualifiers::OCL_Strong)
152 return true;
153 if (!Pass.TA.hasDiagnostic(
Argyrios Kyrtzidisb8b03132011-06-24 00:08:59 +0000154 diag::err_arc_assign_property_ownership, D->getLocation()))
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000155 return true;
156
157 // There is a "error: existing ivar for assign property must be
158 // __unsafe_unretained"; fix it.
159
Argyrios Kyrtzidis86625b52011-07-12 22:05:17 +0000160 if (!canApplyWeak(Pass.Ctx, ivarD->getType())) {
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000161 // We will just add __unsafe_unretained to the ivar.
162 Transaction Trans(Pass.TA);
163 Pass.TA.insert(ivarD->getLocation(), "__unsafe_unretained ");
164 Pass.TA.clearDiagnostic(
Argyrios Kyrtzidisb8b03132011-06-24 00:08:59 +0000165 diag::err_arc_assign_property_ownership, D->getLocation());
Argyrios Kyrtzidis7196d062011-06-21 20:20:39 +0000166 } else {
167 // Mark that we want the ivar to become weak.
168 unsigned loc = SM.getInstantiationLoc(propD->getAtLoc()).getRawEncoding();
169 PropsTy &props = PropsMap[loc];
170 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
171 if (I->PropD == propD) {
172 I->IvarD = ivarD;
173 I->ShouldChangeToWeak = true;
174 I->ArcPropAssignErrorLoc = D->getLocation();
175 }
176 }
177 }
178
179 return true;
180 }
181
182private:
183 bool changeAssignToWeak(SourceLocation atLoc) {
184 SourceManager &SM = Pass.Ctx.getSourceManager();
185
186 // Break down the source location.
187 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
188
189 // Try to load the file buffer.
190 bool invalidTemp = false;
191 llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
192 if (invalidTemp)
193 return true;
194
195 const char *tokenBegin = file.data() + locInfo.second;
196
197 // Lex from the start of the given location.
198 Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
199 Pass.Ctx.getLangOptions(),
200 file.begin(), tokenBegin, file.end());
201 Token tok;
202 lexer.LexFromRawLexer(tok);
203 if (tok.isNot(tok::at)) return true;
204 lexer.LexFromRawLexer(tok);
205 if (tok.isNot(tok::raw_identifier)) return true;
206 if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength())
207 != "property")
208 return true;
209 lexer.LexFromRawLexer(tok);
210 if (tok.isNot(tok::l_paren)) return true;
211
212 SourceLocation LParen = tok.getLocation();
213 SourceLocation assignLoc;
214 bool isEmpty = false;
215
216 lexer.LexFromRawLexer(tok);
217 if (tok.is(tok::r_paren)) {
218 isEmpty = true;
219 } else {
220 while (1) {
221 if (tok.isNot(tok::raw_identifier)) return true;
222 llvm::StringRef ident(tok.getRawIdentifierData(), tok.getLength());
223 if (ident == "assign")
224 assignLoc = tok.getLocation();
225
226 do {
227 lexer.LexFromRawLexer(tok);
228 } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
229 if (tok.is(tok::r_paren))
230 break;
231 lexer.LexFromRawLexer(tok);
232 }
233 }
234
235 Transaction Trans(Pass.TA);
236 if (assignLoc.isValid())
237 Pass.TA.replaceText(assignLoc, "assign", "weak");
238 else
239 Pass.TA.insertAfterToken(LParen, isEmpty ? "weak" : "weak, ");
240 return false;
241 }
242};
243
244class PropertiesChecker : public RecursiveASTVisitor<PropertiesChecker> {
245 MigrationPass &Pass;
246
247public:
248 PropertiesChecker(MigrationPass &pass) : Pass(pass) { }
249
250 bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
251 AssignPropertiesTrans(Pass).doTransform(D);
252 return true;
253 }
254};
255
256} // anonymous namespace
257
258void trans::changeIvarsOfAssignProperties(MigrationPass &pass) {
259 PropertiesChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
260}