]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp
MFC r244628:
[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
44 namespace {
45
46 class PropertiesRewriter {
47   MigrationContext &MigrateCtx;
48   MigrationPass &Pass;
49   ObjCImplementationDecl *CurImplD;
50   
51   enum PropActionKind {
52     PropAction_None,
53     PropAction_RetainReplacedWithStrong,
54     PropAction_AssignRemoved,
55     PropAction_AssignRewritten,
56     PropAction_MaybeAddWeakOrUnsafe
57   };
58
59   struct PropData {
60     ObjCPropertyDecl *PropD;
61     ObjCIvarDecl *IvarD;
62     ObjCPropertyImplDecl *ImplD;
63
64     PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
65   };
66
67   typedef SmallVector<PropData, 2> PropsTy;
68   typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
69   AtPropDeclsTy AtProps;
70   llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
71
72 public:
73   explicit PropertiesRewriter(MigrationContext &MigrateCtx)
74     : MigrateCtx(MigrateCtx), Pass(MigrateCtx.Pass) { }
75
76   static void collectProperties(ObjCContainerDecl *D, AtPropDeclsTy &AtProps,
77                                 AtPropDeclsTy *PrevAtProps = 0) {
78     for (ObjCInterfaceDecl::prop_iterator
79            propI = D->prop_begin(),
80            propE = D->prop_end(); propI != propE; ++propI) {
81       if (propI->getAtLoc().isInvalid())
82         continue;
83       unsigned RawLoc = propI->getAtLoc().getRawEncoding();
84       if (PrevAtProps)
85         if (PrevAtProps->find(RawLoc) != PrevAtProps->end())
86           continue;
87       PropsTy &props = AtProps[RawLoc];
88       props.push_back(*propI);
89     }
90   }
91
92   void doTransform(ObjCImplementationDecl *D) {
93     CurImplD = D;
94     ObjCInterfaceDecl *iface = D->getClassInterface();
95     if (!iface)
96       return;
97
98     collectProperties(iface, AtProps);
99
100     typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
101         prop_impl_iterator;
102     for (prop_impl_iterator
103            I = prop_impl_iterator(D->decls_begin()),
104            E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
105       ObjCPropertyImplDecl *implD = *I;
106       if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
107         continue;
108       ObjCPropertyDecl *propD = implD->getPropertyDecl();
109       if (!propD || propD->isInvalidDecl())
110         continue;
111       ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
112       if (!ivarD || ivarD->isInvalidDecl())
113         continue;
114       unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
115       AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
116       if (findAtLoc == AtProps.end())
117         continue;
118       
119       PropsTy &props = findAtLoc->second;
120       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
121         if (I->PropD == propD) {
122           I->IvarD = ivarD;
123           I->ImplD = implD;
124           break;
125         }
126       }
127     }
128
129     for (AtPropDeclsTy::iterator
130            I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
131       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
132       PropsTy &props = I->second;
133       if (!getPropertyType(props)->isObjCRetainableType())
134         continue;
135       if (hasIvarWithExplicitARCOwnership(props))
136         continue;
137       
138       Transaction Trans(Pass.TA);
139       rewriteProperty(props, atLoc);
140     }
141
142     AtPropDeclsTy AtExtProps;
143     // Look through extensions.
144     for (ObjCCategoryDecl *Cat = iface->getCategoryList();
145            Cat; Cat = Cat->getNextClassCategory())
146       if (Cat->IsClassExtension())
147         collectProperties(Cat, AtExtProps, &AtProps);
148
149     for (AtPropDeclsTy::iterator
150            I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) {
151       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
152       PropsTy &props = I->second;
153       Transaction Trans(Pass.TA);
154       doActionForExtensionProp(props, atLoc);
155     }
156   }
157
158 private:
159   void doPropAction(PropActionKind kind,
160                     PropsTy &props, SourceLocation atLoc,
161                     bool markAction = true) {
162     if (markAction)
163       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
164         ActionOnProp[I->PropD->getIdentifier()] = kind;
165
166     switch (kind) {
167     case PropAction_None:
168       return;
169     case PropAction_RetainReplacedWithStrong: {
170       StringRef toAttr = "strong";
171       MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
172       return;
173     }
174     case PropAction_AssignRemoved:
175       return removeAssignForDefaultStrong(props, atLoc);
176     case PropAction_AssignRewritten:
177       return rewriteAssign(props, atLoc);
178     case PropAction_MaybeAddWeakOrUnsafe:
179       return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
180     }
181   }
182
183   void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) {
184     llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I;
185     I = ActionOnProp.find(props[0].PropD->getIdentifier());
186     if (I == ActionOnProp.end())
187       return;
188
189     doPropAction(I->second, props, atLoc, false);
190   }
191
192   void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
193     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
194     
195     if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
196                      ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
197                      ObjCPropertyDecl::OBJC_PR_strong |
198                      ObjCPropertyDecl::OBJC_PR_weak))
199       return;
200
201     if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
202       // strong is the default.
203       return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
204     }
205
206     bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
207
208     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
209       if (HasIvarAssignedAPlusOneObject)
210         return doPropAction(PropAction_AssignRemoved, props, atLoc);
211       return doPropAction(PropAction_AssignRewritten, props, atLoc);
212     }
213
214     if (HasIvarAssignedAPlusOneObject ||
215         (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
216       return; // 'strong' by default.
217
218     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
219   }
220
221   void removeAssignForDefaultStrong(PropsTy &props,
222                                     SourceLocation atLoc) const {
223     removeAttribute("retain", atLoc);
224     if (!removeAttribute("assign", atLoc))
225       return;
226
227     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
228       if (I->ImplD)
229         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
230                                 I->ImplD->getLocation());
231     }
232   }
233
234   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
235     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
236                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
237     const char *toWhich = 
238       (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
239       (canUseWeak ? "weak" : "unsafe_unretained");
240
241     bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
242     if (!rewroteAttr)
243       canUseWeak = false;
244
245     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
246       if (isUserDeclared(I->IvarD)) {
247         if (I->IvarD &&
248             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
249           const char *toWhich = 
250             (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
251               (canUseWeak ? "__weak " : "__unsafe_unretained ");
252           Pass.TA.insert(I->IvarD->getLocation(), toWhich);
253         }
254       }
255       if (I->ImplD)
256         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
257                                 I->ImplD->getLocation());
258     }
259   }
260
261   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
262                                           SourceLocation atLoc) const {
263     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
264                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
265
266     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
267                                   atLoc);
268     if (!addedAttr)
269       canUseWeak = false;
270
271     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
272       if (isUserDeclared(I->IvarD)) {
273         if (I->IvarD &&
274             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
275           Pass.TA.insert(I->IvarD->getLocation(),
276                          canUseWeak ? "__weak " : "__unsafe_unretained ");
277       }
278       if (I->ImplD) {
279         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
280                                 I->ImplD->getLocation());
281         Pass.TA.clearDiagnostic(
282                            diag::err_arc_objc_property_default_assign_on_object,
283                            I->ImplD->getLocation());
284       }
285     }
286   }
287
288   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
289     return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
290   }
291
292   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
293                         SourceLocation atLoc) const {
294     return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
295   }
296
297   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
298     return MigrateCtx.addPropertyAttribute(attr, atLoc);
299   }
300
301   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
302     ObjCIvarDecl *Ivar;
303   public:
304     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
305
306     bool VisitBinAssign(BinaryOperator *E) {
307       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
308       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
309         if (RE->getDecl() != Ivar)
310           return true;
311
312         if (isPlusOneAssign(E))
313           return false;
314       }
315
316       return true;
317     }
318   };
319
320   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
321     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
322       PlusOneAssign oneAssign(I->IvarD);
323       bool notFound = oneAssign.TraverseDecl(CurImplD);
324       if (!notFound)
325         return true;
326     }
327
328     return false;
329   }
330
331   bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
332     if (Pass.isGCMigration())
333       return false;
334
335     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
336       if (isUserDeclared(I->IvarD)) {
337         if (isa<AttributedType>(I->IvarD->getType()))
338           return true;
339         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
340               != Qualifiers::OCL_Strong)
341           return true;
342       }
343     }
344
345     return false;    
346   }
347
348   bool hasAllIvarsBacked(PropsTy &props) const {
349     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
350       if (!isUserDeclared(I->IvarD))
351         return false;
352
353     return true;
354   }
355
356   // \brief Returns true if all declarations in the @property have GC __weak.
357   bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
358     if (!Pass.isGCMigration())
359       return false;
360     if (props.empty())
361       return false;
362     return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
363   }
364
365   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
366     return ivarD && !ivarD->getSynthesize();
367   }
368
369   QualType getPropertyType(PropsTy &props) const {
370     assert(!props.empty());
371     QualType ty = props[0].PropD->getType().getUnqualifiedType();
372
373 #ifndef NDEBUG
374     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
375       assert(ty == I->PropD->getType().getUnqualifiedType());
376 #endif
377
378     return ty;
379   }
380
381   ObjCPropertyDecl::PropertyAttributeKind
382   getPropertyAttrs(PropsTy &props) const {
383     assert(!props.empty());
384     ObjCPropertyDecl::PropertyAttributeKind
385       attrs = props[0].PropD->getPropertyAttributesAsWritten();
386
387 #ifndef NDEBUG
388     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
389       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
390 #endif
391
392     return attrs;
393   }
394 };
395
396 } // anonymous namespace
397
398 void PropertyRewriteTraverser::traverseObjCImplementation(
399                                            ObjCImplementationContext &ImplCtx) {
400   PropertiesRewriter(ImplCtx.getMigrationContext())
401                                   .doTransform(ImplCtx.getImplementationDecl());
402 }