TransProperties.cpp revision 01b2b9bb154d69cc695717876e903329f6f0973c
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  ObjCImplementationDecl *CurImplD;
49
50  struct PropData {
51    ObjCPropertyDecl *PropD;
52    ObjCIvarDecl *IvarD;
53    ObjCPropertyImplDecl *ImplD;
54
55    PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
56  };
57
58  typedef SmallVector<PropData, 2> PropsTy;
59  typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
60  AtPropDeclsTy AtProps;
61
62public:
63  PropertiesRewriter(MigrationPass &pass) : Pass(pass) { }
64
65  void doTransform(ObjCImplementationDecl *D) {
66    CurImplD = D;
67    ObjCInterfaceDecl *iface = D->getClassInterface();
68    if (!iface)
69      return;
70
71    for (ObjCInterfaceDecl::prop_iterator
72           propI = iface->prop_begin(),
73           propE = iface->prop_end(); propI != propE; ++propI) {
74      if (propI->getAtLoc().isInvalid())
75        continue;
76      PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
77      props.push_back(*propI);
78    }
79
80    typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
81        prop_impl_iterator;
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)
87        continue;
88      ObjCPropertyDecl *propD = implD->getPropertyDecl();
89      if (!propD || propD->isInvalidDecl())
90        continue;
91      ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
92      if (!ivarD || ivarD->isInvalidDecl())
93        continue;
94      unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
95      AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
96      if (findAtLoc == AtProps.end())
97        continue;
98
99      PropsTy &props = findAtLoc->second;
100      for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
101        if (I->PropD == propD) {
102          I->IvarD = ivarD;
103          I->ImplD = implD;
104          break;
105        }
106      }
107    }
108
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())
115        continue;
116      if (hasIvarWithExplicitOwnership(props))
117        continue;
118
119      Transaction Trans(Pass.TA);
120      rewriteProperty(props, atLoc);
121    }
122  }
123
124private:
125  void rewriteProperty(PropsTy &props, SourceLocation atLoc) const {
126    ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
127
128    if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
129                     ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
130                     ObjCPropertyDecl::OBJC_PR_strong |
131                     ObjCPropertyDecl::OBJC_PR_weak))
132      return;
133
134    if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
135      if (propAttrs & ObjCPropertyDecl::OBJC_PR_readonly)
136        rewriteAttribute("retain", "strong", atLoc);
137      else
138        removeAttribute("retain", atLoc); // strong is the default.
139      return;
140    }
141
142    if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
143      if (hasIvarAssignedAPlusOneObject(props)) {
144        rewriteAttribute("assign", "strong", atLoc);
145        return;
146      }
147      return rewriteAssign(props, atLoc);
148    }
149
150    if (hasIvarAssignedAPlusOneObject(props))
151      return maybeAddStrongAttr(props, atLoc);
152
153    return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
154  }
155
156  void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
157    bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
158
159    bool rewroteAttr = rewriteAttribute("assign",
160                                     canUseWeak ? "weak" : "unsafe_unretained",
161                                         atLoc);
162    if (!rewroteAttr)
163      canUseWeak = false;
164
165    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
166      if (isUserDeclared(I->IvarD))
167        Pass.TA.insert(I->IvarD->getLocation(),
168                       canUseWeak ? "__weak " : "__unsafe_unretained ");
169      if (I->ImplD)
170        Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
171                                I->ImplD->getLocation());
172    }
173  }
174
175  void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
176                                          SourceLocation atLoc) const {
177    ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
178
179    bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
180    if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
181        !hasAllIvarsBacked(props)) {
182      bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
183                                    atLoc);
184      if (!addedAttr)
185        canUseWeak = false;
186    }
187
188    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
189      if (isUserDeclared(I->IvarD))
190        Pass.TA.insert(I->IvarD->getLocation(),
191                       canUseWeak ? "__weak " : "__unsafe_unretained ");
192      if (I->ImplD) {
193        Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
194                                I->ImplD->getLocation());
195        Pass.TA.clearDiagnostic(
196                           diag::err_arc_objc_property_default_assign_on_object,
197                           I->ImplD->getLocation());
198      }
199    }
200  }
201
202  void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const {
203    ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
204
205    if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
206        !hasAllIvarsBacked(props)) {
207      addAttribute("strong", atLoc);
208    }
209
210    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
211      if (I->ImplD) {
212        Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
213                                I->ImplD->getLocation());
214        Pass.TA.clearDiagnostic(
215                           diag::err_arc_objc_property_default_assign_on_object,
216                           I->ImplD->getLocation());
217      }
218    }
219  }
220
221  bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
222    return rewriteAttribute(fromAttr, StringRef(), atLoc);
223  }
224
225  bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
226                        SourceLocation atLoc) const {
227    if (atLoc.isMacroID())
228      return false;
229
230    SourceManager &SM = Pass.Ctx.getSourceManager();
231
232    // Break down the source location.
233    std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
234
235    // Try to load the file buffer.
236    bool invalidTemp = false;
237    StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
238    if (invalidTemp)
239      return false;
240
241    const char *tokenBegin = file.data() + locInfo.second;
242
243    // Lex from the start of the given location.
244    Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
245                Pass.Ctx.getLangOptions(),
246                file.begin(), tokenBegin, file.end());
247    Token tok;
248    lexer.LexFromRawLexer(tok);
249    if (tok.isNot(tok::at)) return false;
250    lexer.LexFromRawLexer(tok);
251    if (tok.isNot(tok::raw_identifier)) return false;
252    if (StringRef(tok.getRawIdentifierData(), tok.getLength())
253          != "property")
254      return false;
255    lexer.LexFromRawLexer(tok);
256    if (tok.isNot(tok::l_paren)) return false;
257
258    Token BeforeTok = tok;
259    Token AfterTok;
260    AfterTok.startToken();
261    SourceLocation AttrLoc;
262
263    lexer.LexFromRawLexer(tok);
264    if (tok.is(tok::r_paren))
265      return false;
266
267    while (1) {
268      if (tok.isNot(tok::raw_identifier)) return false;
269      StringRef ident(tok.getRawIdentifierData(), tok.getLength());
270      if (ident == fromAttr) {
271        if (!toAttr.empty()) {
272          Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
273          return true;
274        }
275        // We want to remove the attribute.
276        AttrLoc = tok.getLocation();
277      }
278
279      do {
280        lexer.LexFromRawLexer(tok);
281        if (AttrLoc.isValid() && AfterTok.is(tok::unknown))
282          AfterTok = tok;
283      } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
284      if (tok.is(tok::r_paren))
285        break;
286      if (AttrLoc.isInvalid())
287        BeforeTok = tok;
288      lexer.LexFromRawLexer(tok);
289    }
290
291    if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) {
292      // We want to remove the attribute.
293      if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) {
294        Pass.TA.remove(SourceRange(BeforeTok.getLocation(),
295                                   AfterTok.getLocation()));
296      } else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) {
297        Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation()));
298      } else {
299        Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc));
300      }
301
302      return true;
303    }
304
305    return false;
306  }
307
308  bool addAttribute(StringRef attr, SourceLocation atLoc) const {
309    if (atLoc.isMacroID())
310      return false;
311
312    SourceManager &SM = Pass.Ctx.getSourceManager();
313
314    // Break down the source location.
315    std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
316
317    // Try to load the file buffer.
318    bool invalidTemp = false;
319    StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
320    if (invalidTemp)
321      return false;
322
323    const char *tokenBegin = file.data() + locInfo.second;
324
325    // Lex from the start of the given location.
326    Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
327                Pass.Ctx.getLangOptions(),
328                file.begin(), tokenBegin, file.end());
329    Token tok;
330    lexer.LexFromRawLexer(tok);
331    if (tok.isNot(tok::at)) return false;
332    lexer.LexFromRawLexer(tok);
333    if (tok.isNot(tok::raw_identifier)) return false;
334    if (StringRef(tok.getRawIdentifierData(), tok.getLength())
335          != "property")
336      return false;
337    lexer.LexFromRawLexer(tok);
338
339    if (tok.isNot(tok::l_paren)) {
340      Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
341      return true;
342    }
343
344    lexer.LexFromRawLexer(tok);
345    if (tok.is(tok::r_paren)) {
346      Pass.TA.insert(tok.getLocation(), attr);
347      return true;
348    }
349
350    if (tok.isNot(tok::raw_identifier)) return false;
351
352    Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
353    return true;
354  }
355
356  class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
357    ObjCIvarDecl *Ivar;
358  public:
359    PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
360
361    bool VisitBinAssign(BinaryOperator *E) {
362      Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
363      if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
364        if (RE->getDecl() != Ivar)
365          return true;
366
367      if (ObjCMessageExpr *
368            ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
369        if (ME->getMethodFamily() == OMF_retain)
370          return false;
371
372      ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
373      while (implCE && implCE->getCastKind() ==  CK_BitCast)
374        implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
375
376      if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
377        return false;
378      }
379
380      return true;
381    }
382  };
383
384  bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
385    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
386      PlusOneAssign oneAssign(I->IvarD);
387      bool notFound = oneAssign.TraverseDecl(CurImplD);
388      if (!notFound)
389        return true;
390    }
391
392    return false;
393  }
394
395  bool hasIvarWithExplicitOwnership(PropsTy &props) const {
396    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
397      if (isUserDeclared(I->IvarD)) {
398        if (isa<AttributedType>(I->IvarD->getType()))
399          return true;
400        if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
401              != Qualifiers::OCL_Strong)
402          return true;
403      }
404    }
405
406    return false;
407  }
408
409  bool hasAllIvarsBacked(PropsTy &props) const {
410    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
411      if (!isUserDeclared(I->IvarD))
412        return false;
413
414    return true;
415  }
416
417  bool isUserDeclared(ObjCIvarDecl *ivarD) const {
418    return ivarD && !ivarD->getSynthesize();
419  }
420
421  QualType getPropertyType(PropsTy &props) const {
422    assert(!props.empty());
423    QualType ty = props[0].PropD->getType();
424
425#ifndef NDEBUG
426    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
427      assert(ty == I->PropD->getType());
428#endif
429
430    return ty;
431  }
432
433  ObjCPropertyDecl::PropertyAttributeKind
434  getPropertyAttrs(PropsTy &props) const {
435    assert(!props.empty());
436    ObjCPropertyDecl::PropertyAttributeKind
437      attrs = props[0].PropD->getPropertyAttributesAsWritten();
438
439#ifndef NDEBUG
440    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
441      assert(attrs == I->PropD->getPropertyAttributesAsWritten());
442#endif
443
444    return attrs;
445  }
446};
447
448class ImplementationChecker :
449                             public RecursiveASTVisitor<ImplementationChecker> {
450  MigrationPass &Pass;
451
452public:
453  ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
454
455  bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
456    PropertiesRewriter(Pass).doTransform(D);
457    return true;
458  }
459};
460
461} // anonymous namespace
462
463void trans::rewriteProperties(MigrationPass &pass) {
464  ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
465}
466