]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/llvm/tools/clang/lib/ARCMigrate/TransProperties.cpp
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / llvm / tools / clang / lib / ARCMigrate / TransProperties.cpp
1 //===--- TransProperties.cpp - Transformations 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/Basic/SourceManager.h"
36 #include "clang/Lex/Lexer.h"
37 #include "clang/Sema/SemaDiagnostic.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 (ObjCInterfaceDecl::visible_extensions_iterator
145            ext = iface->visible_extensions_begin(),
146            extEnd = iface->visible_extensions_end();
147          ext != extEnd; ++ext) {
148       collectProperties(*ext, AtExtProps, &AtProps);
149     }
150
151     for (AtPropDeclsTy::iterator
152            I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) {
153       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
154       PropsTy &props = I->second;
155       Transaction Trans(Pass.TA);
156       doActionForExtensionProp(props, atLoc);
157     }
158   }
159
160 private:
161   void doPropAction(PropActionKind kind,
162                     PropsTy &props, SourceLocation atLoc,
163                     bool markAction = true) {
164     if (markAction)
165       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
166         ActionOnProp[I->PropD->getIdentifier()] = kind;
167
168     switch (kind) {
169     case PropAction_None:
170       return;
171     case PropAction_RetainReplacedWithStrong: {
172       StringRef toAttr = "strong";
173       MigrateCtx.rewritePropertyAttribute("retain", toAttr, atLoc);
174       return;
175     }
176     case PropAction_AssignRemoved:
177       return removeAssignForDefaultStrong(props, atLoc);
178     case PropAction_AssignRewritten:
179       return rewriteAssign(props, atLoc);
180     case PropAction_MaybeAddWeakOrUnsafe:
181       return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
182     }
183   }
184
185   void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) {
186     llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I;
187     I = ActionOnProp.find(props[0].PropD->getIdentifier());
188     if (I == ActionOnProp.end())
189       return;
190
191     doPropAction(I->second, props, atLoc, false);
192   }
193
194   void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
195     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
196     
197     if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
198                      ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
199                      ObjCPropertyDecl::OBJC_PR_strong |
200                      ObjCPropertyDecl::OBJC_PR_weak))
201       return;
202
203     if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
204       // strong is the default.
205       return doPropAction(PropAction_RetainReplacedWithStrong, props, atLoc);
206     }
207
208     bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
209
210     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
211       if (HasIvarAssignedAPlusOneObject)
212         return doPropAction(PropAction_AssignRemoved, props, atLoc);
213       return doPropAction(PropAction_AssignRewritten, props, atLoc);
214     }
215
216     if (HasIvarAssignedAPlusOneObject ||
217         (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
218       return; // 'strong' by default.
219
220     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
221   }
222
223   void removeAssignForDefaultStrong(PropsTy &props,
224                                     SourceLocation atLoc) const {
225     removeAttribute("retain", atLoc);
226     if (!removeAttribute("assign", atLoc))
227       return;
228
229     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
230       if (I->ImplD)
231         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
232                                 diag::err_arc_assign_property_ownership,
233                                 diag::err_arc_inconsistent_property_ownership,
234                                 I->IvarD->getLocation());
235     }
236   }
237
238   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
239     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
240                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
241     const char *toWhich = 
242       (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "strong" :
243       (canUseWeak ? "weak" : "unsafe_unretained");
244
245     bool rewroteAttr = rewriteAttribute("assign", toWhich, atLoc);
246     if (!rewroteAttr)
247       canUseWeak = false;
248
249     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
250       if (isUserDeclared(I->IvarD)) {
251         if (I->IvarD &&
252             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak) {
253           const char *toWhich = 
254             (Pass.isGCMigration() && !hasGCWeak(props, atLoc)) ? "__strong " :
255               (canUseWeak ? "__weak " : "__unsafe_unretained ");
256           Pass.TA.insert(I->IvarD->getLocation(), toWhich);
257         }
258       }
259       if (I->ImplD)
260         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
261                                 diag::err_arc_assign_property_ownership,
262                                 diag::err_arc_inconsistent_property_ownership,
263                                 I->IvarD->getLocation());
264     }
265   }
266
267   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
268                                           SourceLocation atLoc) const {
269     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
270                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
271
272     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
273                                   atLoc);
274     if (!addedAttr)
275       canUseWeak = false;
276
277     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
278       if (isUserDeclared(I->IvarD)) {
279         if (I->IvarD &&
280             I->IvarD->getType().getObjCLifetime() != Qualifiers::OCL_Weak)
281           Pass.TA.insert(I->IvarD->getLocation(),
282                          canUseWeak ? "__weak " : "__unsafe_unretained ");
283       }
284       if (I->ImplD) {
285         Pass.TA.clearDiagnostic(diag::err_arc_strong_property_ownership,
286                                 diag::err_arc_assign_property_ownership,
287                                 diag::err_arc_inconsistent_property_ownership,
288                                 I->IvarD->getLocation());
289         Pass.TA.clearDiagnostic(
290                            diag::err_arc_objc_property_default_assign_on_object,
291                            I->ImplD->getLocation());
292       }
293     }
294   }
295
296   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
297     return MigrateCtx.removePropertyAttribute(fromAttr, atLoc);
298   }
299
300   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
301                         SourceLocation atLoc) const {
302     return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
303   }
304
305   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
306     return MigrateCtx.addPropertyAttribute(attr, atLoc);
307   }
308
309   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
310     ObjCIvarDecl *Ivar;
311   public:
312     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
313
314     bool VisitBinAssign(BinaryOperator *E) {
315       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
316       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
317         if (RE->getDecl() != Ivar)
318           return true;
319
320         if (isPlusOneAssign(E))
321           return false;
322       }
323
324       return true;
325     }
326   };
327
328   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
329     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
330       PlusOneAssign oneAssign(I->IvarD);
331       bool notFound = oneAssign.TraverseDecl(CurImplD);
332       if (!notFound)
333         return true;
334     }
335
336     return false;
337   }
338
339   bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
340     if (Pass.isGCMigration())
341       return false;
342
343     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
344       if (isUserDeclared(I->IvarD)) {
345         if (isa<AttributedType>(I->IvarD->getType()))
346           return true;
347         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
348               != Qualifiers::OCL_Strong)
349           return true;
350       }
351     }
352
353     return false;    
354   }
355
356   bool hasAllIvarsBacked(PropsTy &props) const {
357     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
358       if (!isUserDeclared(I->IvarD))
359         return false;
360
361     return true;
362   }
363
364   // \brief Returns true if all declarations in the @property have GC __weak.
365   bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
366     if (!Pass.isGCMigration())
367       return false;
368     if (props.empty())
369       return false;
370     return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
371   }
372
373   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
374     return ivarD && !ivarD->getSynthesize();
375   }
376
377   QualType getPropertyType(PropsTy &props) const {
378     assert(!props.empty());
379     QualType ty = props[0].PropD->getType().getUnqualifiedType();
380
381 #ifndef NDEBUG
382     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
383       assert(ty == I->PropD->getType().getUnqualifiedType());
384 #endif
385
386     return ty;
387   }
388
389   ObjCPropertyDecl::PropertyAttributeKind
390   getPropertyAttrs(PropsTy &props) const {
391     assert(!props.empty());
392     ObjCPropertyDecl::PropertyAttributeKind
393       attrs = props[0].PropD->getPropertyAttributesAsWritten();
394
395 #ifndef NDEBUG
396     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
397       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
398 #endif
399
400     return attrs;
401   }
402 };
403
404 } // anonymous namespace
405
406 void PropertyRewriteTraverser::traverseObjCImplementation(
407                                            ObjCImplementationContext &ImplCtx) {
408   PropertiesRewriter(ImplCtx.getMigrationContext())
409                                   .doTransform(ImplCtx.getImplementationDecl());
410 }