]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp
Copy head to stable/9 as part of 9.0-RELEASE release cycle.
[FreeBSD/stable/9.git] / contrib / llvm / tools / clang / lib / ARCMigrate / TransProperties.cpp
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
40 using namespace clang;
41 using namespace arcmt;
42 using namespace trans;
43 using llvm::StringRef;
44
45 namespace {
46
47 class 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
62 public:
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
123 private:
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
347 class ImplementationChecker :
348                              public RecursiveASTVisitor<ImplementationChecker> {
349   MigrationPass &Pass;
350
351 public:
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
362 void trans::rewriteProperties(MigrationPass &pass) {
363   ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
364 }