]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.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
44 namespace {
45
46 class 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
62 public:
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
124 private:
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       rewriteAttribute("retain", "strong", atLoc);
136       return;
137     }
138
139     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
140       if (hasIvarAssignedAPlusOneObject(props)) {
141         rewriteAttribute("assign", "strong", atLoc);
142         return;
143       }
144       return rewriteAssign(props, atLoc);
145     }
146
147     if (hasIvarAssignedAPlusOneObject(props))
148       return maybeAddStrongAttr(props, atLoc);
149
150     return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
151   }
152
153   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
154     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
155
156     bool rewroteAttr = rewriteAttribute("assign",
157                                      canUseWeak ? "weak" : "unsafe_unretained",
158                                          atLoc);
159     if (!rewroteAttr)
160       canUseWeak = false;
161
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 ");
166       if (I->ImplD)
167         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
168                                 I->ImplD->getLocation());
169     }
170   }
171
172   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
173                                           SourceLocation atLoc) const {
174     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
175
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",
180                                     atLoc);
181       if (!addedAttr)
182         canUseWeak = false;
183     }
184
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 ");
189       if (I->ImplD) {
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());
195       }
196     }
197   }
198
199   void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const {
200     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
201
202     if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
203         !hasAllIvarsBacked(props)) {
204       addAttribute("strong", atLoc);
205     }
206
207     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
208       if (I->ImplD) {
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());
214       }
215     }
216   }
217
218   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
219                         SourceLocation atLoc) const {
220     if (atLoc.isMacroID())
221       return false;
222
223     SourceManager &SM = Pass.Ctx.getSourceManager();
224
225     // Break down the source location.
226     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
227
228     // Try to load the file buffer.
229     bool invalidTemp = false;
230     StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
231     if (invalidTemp)
232       return false;
233
234     const char *tokenBegin = file.data() + locInfo.second;
235
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());
240     Token tok;
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())
246           != "property")
247       return false;
248     lexer.LexFromRawLexer(tok);
249     if (tok.isNot(tok::l_paren)) return false;
250     
251     lexer.LexFromRawLexer(tok);
252     if (tok.is(tok::r_paren))
253       return false;
254
255     while (1) {
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);
260         return true;
261       }
262
263       do {
264         lexer.LexFromRawLexer(tok);
265       } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
266       if (tok.is(tok::r_paren))
267         break;
268       lexer.LexFromRawLexer(tok);
269     }
270
271     return false;
272   }
273
274   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
275     if (atLoc.isMacroID())
276       return false;
277
278     SourceManager &SM = Pass.Ctx.getSourceManager();
279
280     // Break down the source location.
281     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
282
283     // Try to load the file buffer.
284     bool invalidTemp = false;
285     StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
286     if (invalidTemp)
287       return false;
288
289     const char *tokenBegin = file.data() + locInfo.second;
290
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());
295     Token tok;
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())
301           != "property")
302       return false;
303     lexer.LexFromRawLexer(tok);
304
305     if (tok.isNot(tok::l_paren)) {
306       Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
307       return true;
308     }
309     
310     lexer.LexFromRawLexer(tok);
311     if (tok.is(tok::r_paren)) {
312       Pass.TA.insert(tok.getLocation(), attr);
313       return true;
314     }
315
316     if (tok.isNot(tok::raw_identifier)) return false;
317
318     Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
319     return true;
320   }
321
322   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
323     ObjCIvarDecl *Ivar;
324   public:
325     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
326
327     bool VisitBinAssign(BinaryOperator *E) {
328       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
329       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
330         if (RE->getDecl() != Ivar)
331           return true;
332
333       if (ObjCMessageExpr *
334             ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
335         if (ME->getMethodFamily() == OMF_retain)
336           return false;
337
338       ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
339       while (implCE && implCE->getCastKind() ==  CK_BitCast)
340         implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
341
342       if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
343         return false;
344       }
345
346       return true;
347     }
348   };
349
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);
354       if (!notFound)
355         return true;
356     }
357
358     return false;
359   }
360
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()))
365           return true;
366         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
367               != Qualifiers::OCL_Strong)
368           return true;
369       }
370     }
371
372     return false;    
373   }
374
375   bool hasAllIvarsBacked(PropsTy &props) const {
376     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
377       if (!isUserDeclared(I->IvarD))
378         return false;
379
380     return true;
381   }
382
383   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
384     return ivarD && !ivarD->getSynthesize();
385   }
386
387   QualType getPropertyType(PropsTy &props) const {
388     assert(!props.empty());
389     QualType ty = props[0].PropD->getType();
390
391 #ifndef NDEBUG
392     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
393       assert(ty == I->PropD->getType());
394 #endif
395
396     return ty;
397   }
398
399   ObjCPropertyDecl::PropertyAttributeKind
400   getPropertyAttrs(PropsTy &props) const {
401     assert(!props.empty());
402     ObjCPropertyDecl::PropertyAttributeKind
403       attrs = props[0].PropD->getPropertyAttributesAsWritten();
404
405 #ifndef NDEBUG
406     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
407       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
408 #endif
409
410     return attrs;
411   }
412 };
413
414 class ImplementationChecker :
415                              public RecursiveASTVisitor<ImplementationChecker> {
416   MigrationPass &Pass;
417
418 public:
419   ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
420
421   bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
422     PropertiesRewriter(Pass).doTransform(D);
423     return true;
424   }
425 };
426
427 } // anonymous namespace
428
429 void trans::rewriteProperties(MigrationPass &pass) {
430   ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
431 }