TransProperties.cpp revision 5f9e272e632e951b1efe824cd16acb4d96077930
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
40using namespace clang;
41using namespace arcmt;
42using namespace trans;
43
44namespace {
45
46class PropertiesRewriter {
47  MigrationPass &Pass;
48
49  struct PropData {
50    ObjCPropertyDecl *PropD;
51    ObjCIvarDecl *IvarD;
52    ObjCPropertyImplDecl *ImplD;
53
54    PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
55  };
56
57  typedef SmallVector<PropData, 2> PropsTy;
58  typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
59  AtPropDeclsTy AtProps;
60
61public:
62  PropertiesRewriter(MigrationPass &pass) : Pass(pass) { }
63
64  void doTransform(ObjCImplementationDecl *D) {
65    ObjCInterfaceDecl *iface = D->getClassInterface();
66    if (!iface)
67      return;
68
69    for (ObjCInterfaceDecl::prop_iterator
70           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);
76    }
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) {
83      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;
98      for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
99        if (I->PropD == propD) {
100          I->IvarD = ivarD;
101          I->ImplD = implD;
102          break;
103        }
104      }
105    }
106
107    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    }
120  }
121
122private:
123  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
189  bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
190                        SourceLocation atLoc) const {
191    if (atLoc.isMacroID())
192      return false;
193
194    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;
201    StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
202    if (invalidTemp)
203      return false;
204
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);
213    if (tok.isNot(tok::at)) return false;
214    lexer.LexFromRawLexer(tok);
215    if (tok.isNot(tok::raw_identifier)) return false;
216    if (StringRef(tok.getRawIdentifierData(), tok.getLength())
217          != "property")
218      return false;
219    lexer.LexFromRawLexer(tok);
220    if (tok.isNot(tok::l_paren)) return false;
221
222    lexer.LexFromRawLexer(tok);
223    if (tok.is(tok::r_paren))
224      return false;
225
226    while (1) {
227      if (tok.isNot(tok::raw_identifier)) return false;
228      StringRef ident(tok.getRawIdentifierData(), tok.getLength());
229      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
245  bool addAttribute(StringRef attr, SourceLocation atLoc) const {
246    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;
256    StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
257    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;
271    if (StringRef(tok.getRawIdentifierData(), tok.getLength())
272          != "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
281    lexer.LexFromRawLexer(tok);
282    if (tok.is(tok::r_paren)) {
283      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;
301      }
302    }
303
304    return false;
305  }
306
307  bool hasNoBackingIvars(PropsTy &props) const {
308    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
309      if (I->IvarD)
310        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;
343  }
344};
345
346class ImplementationChecker :
347                             public RecursiveASTVisitor<ImplementationChecker> {
348  MigrationPass &Pass;
349
350public:
351  ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
352
353  bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
354    PropertiesRewriter(Pass).doTransform(D);
355    return true;
356  }
357};
358
359} // anonymous namespace
360
361void trans::rewriteProperties(MigrationPass &pass) {
362  ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
363}
364