1 //===--- TransProperties.cpp - Tranformations to ARC mode -----------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
12 // - Adds strong/weak/unsafe_unretained ownership specifier to properties that
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.
19 // @interface Foo : NSObject {
22 // @property (assign) id x;
25 // @interface Foo : NSObject {
26 // NSObject *__weak x;
28 // @property (weak) id x;
31 //===----------------------------------------------------------------------===//
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"
40 using namespace clang;
41 using namespace arcmt;
42 using namespace trans;
43 using llvm::StringRef;
47 class PropertiesRewriter {
51 ObjCPropertyDecl *PropD;
53 ObjCPropertyImplDecl *ImplD;
55 PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
58 typedef llvm::SmallVector<PropData, 2> PropsTy;
59 typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
60 AtPropDeclsTy AtProps;
63 PropertiesRewriter(MigrationPass &pass) : Pass(pass) { }
65 void doTransform(ObjCImplementationDecl *D) {
66 ObjCInterfaceDecl *iface = D->getClassInterface();
70 for (ObjCInterfaceDecl::prop_iterator
71 propI = iface->prop_begin(),
72 propE = iface->prop_end(); propI != propE; ++propI) {
73 if (propI->getAtLoc().isInvalid())
75 PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
76 props.push_back(*propI);
79 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
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)
87 ObjCPropertyDecl *propD = implD->getPropertyDecl();
88 if (!propD || propD->isInvalidDecl())
90 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
91 if (!ivarD || ivarD->isInvalidDecl())
93 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
94 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
95 if (findAtLoc == AtProps.end())
98 PropsTy &props = findAtLoc->second;
99 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
100 if (I->PropD == propD) {
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())
115 if (hasIvarWithExplicitOwnership(props))
118 Transaction Trans(Pass.TA);
119 rewriteProperty(props, atLoc);
124 void rewriteProperty(PropsTy &props, SourceLocation atLoc) const {
125 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
127 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
128 ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
129 ObjCPropertyDecl::OBJC_PR_strong |
130 ObjCPropertyDecl::OBJC_PR_weak))
133 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
134 rewriteAttribute("retain", "strong", atLoc);
138 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign)
139 return rewriteAssign(props, atLoc);
141 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
144 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
145 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
147 bool rewroteAttr = rewriteAttribute("assign",
148 canUseWeak ? "weak" : "unsafe_unretained",
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 ");
158 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
159 I->ImplD->getLocation());
163 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
164 SourceLocation atLoc) const {
165 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
166 if ((propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) &&
167 hasNoBackingIvars(props))
170 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
171 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
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 ");
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());
190 bool rewriteAttribute(llvm::StringRef fromAttr, llvm::StringRef toAttr,
191 SourceLocation atLoc) const {
192 if (atLoc.isMacroID())
195 SourceManager &SM = Pass.Ctx.getSourceManager();
197 // Break down the source location.
198 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
200 // Try to load the file buffer.
201 bool invalidTemp = false;
202 llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
206 const char *tokenBegin = file.data() + locInfo.second;
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());
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())
220 lexer.LexFromRawLexer(tok);
221 if (tok.isNot(tok::l_paren)) return false;
223 lexer.LexFromRawLexer(tok);
224 if (tok.is(tok::r_paren))
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);
236 lexer.LexFromRawLexer(tok);
237 } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
238 if (tok.is(tok::r_paren))
240 lexer.LexFromRawLexer(tok);
246 bool addAttribute(llvm::StringRef attr, SourceLocation atLoc) const {
247 if (atLoc.isMacroID())
250 SourceManager &SM = Pass.Ctx.getSourceManager();
252 // Break down the source location.
253 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
255 // Try to load the file buffer.
256 bool invalidTemp = false;
257 llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
261 const char *tokenBegin = file.data() + locInfo.second;
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());
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())
275 lexer.LexFromRawLexer(tok);
277 if (tok.isNot(tok::l_paren)) {
278 Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
282 lexer.LexFromRawLexer(tok);
283 if (tok.is(tok::r_paren)) {
284 Pass.TA.insert(tok.getLocation(), attr);
288 if (tok.isNot(tok::raw_identifier)) return false;
290 Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
294 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()))
299 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
300 != Qualifiers::OCL_Strong)
308 bool hasNoBackingIvars(PropsTy &props) const {
309 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
316 bool isUserDeclared(ObjCIvarDecl *ivarD) const {
317 return ivarD && !ivarD->getSynthesize();
320 QualType getPropertyType(PropsTy &props) const {
321 assert(!props.empty());
322 QualType ty = props[0].PropD->getType();
325 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
326 assert(ty == I->PropD->getType());
332 ObjCPropertyDecl::PropertyAttributeKind
333 getPropertyAttrs(PropsTy &props) const {
334 assert(!props.empty());
335 ObjCPropertyDecl::PropertyAttributeKind
336 attrs = props[0].PropD->getPropertyAttributesAsWritten();
339 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
340 assert(attrs == I->PropD->getPropertyAttributesAsWritten());
347 class ImplementationChecker :
348 public RecursiveASTVisitor<ImplementationChecker> {
352 ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
354 bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
355 PropertiesRewriter(Pass).doTransform(D);
360 } // anonymous namespace
362 void trans::rewriteProperties(MigrationPass &pass) {
363 ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());