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;
46 class PropertiesRewriter {
48 ObjCImplementationDecl *CurImplD;
51 ObjCPropertyDecl *PropD;
53 ObjCPropertyImplDecl *ImplD;
55 PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
58 typedef 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) {
67 ObjCInterfaceDecl *iface = D->getClassInterface();
71 for (ObjCInterfaceDecl::prop_iterator
72 propI = iface->prop_begin(),
73 propE = iface->prop_end(); propI != propE; ++propI) {
74 if (propI->getAtLoc().isInvalid())
76 PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
77 props.push_back(*propI);
80 typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
82 for (prop_impl_iterator
83 I = prop_impl_iterator(D->decls_begin()),
84 E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
85 ObjCPropertyImplDecl *implD = *I;
86 if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
88 ObjCPropertyDecl *propD = implD->getPropertyDecl();
89 if (!propD || propD->isInvalidDecl())
91 ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
92 if (!ivarD || ivarD->isInvalidDecl())
94 unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
95 AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
96 if (findAtLoc == AtProps.end())
99 PropsTy &props = findAtLoc->second;
100 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
101 if (I->PropD == propD) {
109 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())
116 if (hasIvarWithExplicitOwnership(props))
119 Transaction Trans(Pass.TA);
120 rewriteProperty(props, atLoc);
125 void rewriteProperty(PropsTy &props, SourceLocation atLoc) const {
126 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
128 if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
129 ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
130 ObjCPropertyDecl::OBJC_PR_strong |
131 ObjCPropertyDecl::OBJC_PR_weak))
134 if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
135 rewriteAttribute("retain", "strong", atLoc);
139 if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
140 if (hasIvarAssignedAPlusOneObject(props)) {
141 rewriteAttribute("assign", "strong", atLoc);
144 return rewriteAssign(props, atLoc);
147 if (hasIvarAssignedAPlusOneObject(props))
148 return maybeAddStrongAttr(props, atLoc);
150 return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
153 void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
154 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
156 bool rewroteAttr = rewriteAttribute("assign",
157 canUseWeak ? "weak" : "unsafe_unretained",
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 ");
167 Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
168 I->ImplD->getLocation());
172 void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
173 SourceLocation atLoc) const {
174 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
176 bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
177 if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
178 !hasAllIvarsBacked(props)) {
179 bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
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 ");
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());
199 void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const {
200 ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
202 if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
203 !hasAllIvarsBacked(props)) {
204 addAttribute("strong", atLoc);
207 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
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());
218 bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
219 SourceLocation atLoc) const {
220 if (atLoc.isMacroID())
223 SourceManager &SM = Pass.Ctx.getSourceManager();
225 // Break down the source location.
226 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
228 // Try to load the file buffer.
229 bool invalidTemp = false;
230 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
234 const char *tokenBegin = file.data() + locInfo.second;
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());
241 lexer.LexFromRawLexer(tok);
242 if (tok.isNot(tok::at)) return false;
243 lexer.LexFromRawLexer(tok);
244 if (tok.isNot(tok::raw_identifier)) return false;
245 if (StringRef(tok.getRawIdentifierData(), tok.getLength())
248 lexer.LexFromRawLexer(tok);
249 if (tok.isNot(tok::l_paren)) return false;
251 lexer.LexFromRawLexer(tok);
252 if (tok.is(tok::r_paren))
256 if (tok.isNot(tok::raw_identifier)) return false;
257 StringRef ident(tok.getRawIdentifierData(), tok.getLength());
258 if (ident == fromAttr) {
259 Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
264 lexer.LexFromRawLexer(tok);
265 } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
266 if (tok.is(tok::r_paren))
268 lexer.LexFromRawLexer(tok);
274 bool addAttribute(StringRef attr, SourceLocation atLoc) const {
275 if (atLoc.isMacroID())
278 SourceManager &SM = Pass.Ctx.getSourceManager();
280 // Break down the source location.
281 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
283 // Try to load the file buffer.
284 bool invalidTemp = false;
285 StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
289 const char *tokenBegin = file.data() + locInfo.second;
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());
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;
300 if (StringRef(tok.getRawIdentifierData(), tok.getLength())
303 lexer.LexFromRawLexer(tok);
305 if (tok.isNot(tok::l_paren)) {
306 Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
310 lexer.LexFromRawLexer(tok);
311 if (tok.is(tok::r_paren)) {
312 Pass.TA.insert(tok.getLocation(), attr);
316 if (tok.isNot(tok::raw_identifier)) return false;
318 Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
322 class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
325 PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
327 bool VisitBinAssign(BinaryOperator *E) {
328 Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
329 if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
330 if (RE->getDecl() != Ivar)
333 if (ObjCMessageExpr *
334 ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
335 if (ME->getMethodFamily() == OMF_retain)
338 ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
339 while (implCE && implCE->getCastKind() == CK_BitCast)
340 implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
342 if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
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);
361 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()))
366 if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
367 != Qualifiers::OCL_Strong)
375 bool hasAllIvarsBacked(PropsTy &props) const {
376 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
377 if (!isUserDeclared(I->IvarD))
383 bool isUserDeclared(ObjCIvarDecl *ivarD) const {
384 return ivarD && !ivarD->getSynthesize();
387 QualType getPropertyType(PropsTy &props) const {
388 assert(!props.empty());
389 QualType ty = props[0].PropD->getType();
392 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
393 assert(ty == I->PropD->getType());
399 ObjCPropertyDecl::PropertyAttributeKind
400 getPropertyAttrs(PropsTy &props) const {
401 assert(!props.empty());
402 ObjCPropertyDecl::PropertyAttributeKind
403 attrs = props[0].PropD->getPropertyAttributesAsWritten();
406 for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
407 assert(attrs == I->PropD->getPropertyAttributesAsWritten());
414 class ImplementationChecker :
415 public RecursiveASTVisitor<ImplementationChecker> {
419 ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
421 bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
422 PropertiesRewriter(Pass).doTransform(D);
427 } // anonymous namespace
429 void trans::rewriteProperties(MigrationPass &pass) {
430 ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());