TransProperties.cpp revision 224145
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;
43using llvm::StringRef;
44
45namespace {
46
47class PropertiesRewriter {
48  MigrationPass &Pass;
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 llvm::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    ObjCInterfaceDecl *iface = D->getClassInterface();
67    if (!iface)
68      return;
69
70    for (ObjCInterfaceDecl::prop_iterator
71           propI = iface->prop_begin(),
72           propE = iface->prop_end(); propI != propE; ++propI) {
73      if (propI->getAtLoc().isInvalid())
74        continue;
75      PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
76      props.push_back(*propI);
77    }
78
79    typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
80        prop_impl_iterator;
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)
86        continue;
87      ObjCPropertyDecl *propD = implD->getPropertyDecl();
88      if (!propD || propD->isInvalidDecl())
89        continue;
90      ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
91      if (!ivarD || ivarD->isInvalidDecl())
92        continue;
93      unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
94      AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
95      if (findAtLoc == AtProps.end())
96        continue;
97
98      PropsTy &props = findAtLoc->second;
99      for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
100        if (I->PropD == propD) {
101          I->IvarD = ivarD;
102          I->ImplD = implD;
103          break;
104        }
105      }
106    }
107
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())
114        continue;
115      if (hasIvarWithExplicitOwnership(props))
116        continue;
117
118      Transaction Trans(Pass.TA);
119      rewriteProperty(props, atLoc);
120    }
121  }
122
123private:
124  void rewriteProperty(PropsTy &props, SourceLocation atLoc) const {
125    ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
126
127    if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
128                     ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
129                     ObjCPropertyDecl::OBJC_PR_strong |
130                     ObjCPropertyDecl::OBJC_PR_weak))
131      return;
132
133    if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
134      rewriteAttribute("retain", "strong", atLoc);
135      return;
136    }
137
138    if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign)
139      return rewriteAssign(props, atLoc);
140
141    return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
142  }
143
144  void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
145    bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
146
147    bool rewroteAttr = rewriteAttribute("assign",
148                                     canUseWeak ? "weak" : "unsafe_unretained",
149                                         atLoc);
150    if (!rewroteAttr)
151      canUseWeak = false;
152
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 ");
157      if (I->ImplD)
158        Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
159                                I->ImplD->getLocation());
160    }
161  }
162
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))
168      return;
169
170    bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
171    bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
172                                  atLoc);
173    if (!addedAttr)
174      canUseWeak = false;
175
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 ");
180      if (I->ImplD) {
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());
186      }
187    }
188  }
189
190  bool rewriteAttribute(llvm::StringRef fromAttr, llvm::StringRef toAttr,
191                        SourceLocation atLoc) const {
192    if (atLoc.isMacroID())
193      return false;
194
195    SourceManager &SM = Pass.Ctx.getSourceManager();
196
197    // Break down the source location.
198    std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
199
200    // Try to load the file buffer.
201    bool invalidTemp = false;
202    llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
203    if (invalidTemp)
204      return false;
205
206    const char *tokenBegin = file.data() + locInfo.second;
207
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());
212    Token tok;
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())
218          != "property")
219      return false;
220    lexer.LexFromRawLexer(tok);
221    if (tok.isNot(tok::l_paren)) return false;
222
223    lexer.LexFromRawLexer(tok);
224    if (tok.is(tok::r_paren))
225      return false;
226
227    while (1) {
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);
232        return true;
233      }
234
235      do {
236        lexer.LexFromRawLexer(tok);
237      } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
238      if (tok.is(tok::r_paren))
239        break;
240      lexer.LexFromRawLexer(tok);
241    }
242
243    return false;
244  }
245
246  bool addAttribute(llvm::StringRef attr, SourceLocation atLoc) const {
247    if (atLoc.isMacroID())
248      return false;
249
250    SourceManager &SM = Pass.Ctx.getSourceManager();
251
252    // Break down the source location.
253    std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
254
255    // Try to load the file buffer.
256    bool invalidTemp = false;
257    llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
258    if (invalidTemp)
259      return false;
260
261    const char *tokenBegin = file.data() + locInfo.second;
262
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());
267    Token tok;
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())
273          != "property")
274      return false;
275    lexer.LexFromRawLexer(tok);
276
277    if (tok.isNot(tok::l_paren)) {
278      Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
279      return true;
280    }
281
282    lexer.LexFromRawLexer(tok);
283    if (tok.is(tok::r_paren)) {
284      Pass.TA.insert(tok.getLocation(), attr);
285      return true;
286    }
287
288    if (tok.isNot(tok::raw_identifier)) return false;
289
290    Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
291    return true;
292  }
293
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()))
298          return true;
299        if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
300              != Qualifiers::OCL_Strong)
301          return true;
302      }
303    }
304
305    return false;
306  }
307
308  bool hasNoBackingIvars(PropsTy &props) const {
309    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
310      if (I->IvarD)
311        return false;
312
313    return true;
314  }
315
316  bool isUserDeclared(ObjCIvarDecl *ivarD) const {
317    return ivarD && !ivarD->getSynthesize();
318  }
319
320  QualType getPropertyType(PropsTy &props) const {
321    assert(!props.empty());
322    QualType ty = props[0].PropD->getType();
323
324#ifndef NDEBUG
325    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
326      assert(ty == I->PropD->getType());
327#endif
328
329    return ty;
330  }
331
332  ObjCPropertyDecl::PropertyAttributeKind
333  getPropertyAttrs(PropsTy &props) const {
334    assert(!props.empty());
335    ObjCPropertyDecl::PropertyAttributeKind
336      attrs = props[0].PropD->getPropertyAttributesAsWritten();
337
338#ifndef NDEBUG
339    for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
340      assert(attrs == I->PropD->getPropertyAttributesAsWritten());
341#endif
342
343    return attrs;
344  }
345};
346
347class ImplementationChecker :
348                             public RecursiveASTVisitor<ImplementationChecker> {
349  MigrationPass &Pass;
350
351public:
352  ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
353
354  bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
355    PropertiesRewriter(Pass).doTransform(D);
356    return true;
357  }
358};
359
360} // anonymous namespace
361
362void trans::rewriteProperties(MigrationPass &pass) {
363  ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
364}
365