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_RetainRemoved,
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     for (ObjCInterfaceDecl::prop_iterator
78            propI = D->prop_begin(),
79            propE = D->prop_end(); propI != propE; ++propI) {
80       if (propI->getAtLoc().isInvalid())
81         continue;
82       PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()];
83       props.push_back(*propI);
84     }
85   }
86 
87   void doTransform(ObjCImplementationDecl *D) {
88     CurImplD = D;
89     ObjCInterfaceDecl *iface = D->getClassInterface();
90     if (!iface)
91       return;
92 
93     collectProperties(iface, AtProps);
94 
95     typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl>
96         prop_impl_iterator;
97     for (prop_impl_iterator
98            I = prop_impl_iterator(D->decls_begin()),
99            E = prop_impl_iterator(D->decls_end()); I != E; ++I) {
100       ObjCPropertyImplDecl *implD = *I;
101       if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
102         continue;
103       ObjCPropertyDecl *propD = implD->getPropertyDecl();
104       if (!propD || propD->isInvalidDecl())
105         continue;
106       ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl();
107       if (!ivarD || ivarD->isInvalidDecl())
108         continue;
109       unsigned rawAtLoc = propD->getAtLoc().getRawEncoding();
110       AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc);
111       if (findAtLoc == AtProps.end())
112         continue;
113 
114       PropsTy &props = findAtLoc->second;
115       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
116         if (I->PropD == propD) {
117           I->IvarD = ivarD;
118           I->ImplD = implD;
119           break;
120         }
121       }
122     }
123 
124     for (AtPropDeclsTy::iterator
125            I = AtProps.begin(), E = AtProps.end(); I != E; ++I) {
126       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
127       PropsTy &props = I->second;
128       if (!getPropertyType(props)->isObjCRetainableType())
129         continue;
130       if (hasIvarWithExplicitARCOwnership(props))
131         continue;
132 
133       Transaction Trans(Pass.TA);
134       rewriteProperty(props, atLoc);
135     }
136 
137     AtPropDeclsTy AtExtProps;
138     // Look through extensions.
139     for (ObjCCategoryDecl *Cat = iface->getCategoryList();
140            Cat; Cat = Cat->getNextClassCategory())
141       if (Cat->IsClassExtension())
142         collectProperties(Cat, AtExtProps);
143 
144     for (AtPropDeclsTy::iterator
145            I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) {
146       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
147       PropsTy &props = I->second;
148       Transaction Trans(Pass.TA);
149       doActionForExtensionProp(props, atLoc);
150     }
151   }
152 
153 private:
154   void doPropAction(PropActionKind kind,
155                     PropsTy &props, SourceLocation atLoc,
156                     bool markAction = true) {
157     if (markAction)
158       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
159         ActionOnProp[I->PropD->getIdentifier()] = kind;
160 
161     switch (kind) {
162     case PropAction_None:
163       return;
164     case PropAction_RetainRemoved:
165       removeAttribute("retain", atLoc);
166       return;
167     case PropAction_AssignRemoved:
168       return removeAssignForDefaultStrong(props, atLoc);
169     case PropAction_AssignRewritten:
170       return rewriteAssign(props, atLoc);
171     case PropAction_MaybeAddWeakOrUnsafe:
172       return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc);
173     }
174   }
175 
176   void doActionForExtensionProp(PropsTy &props, SourceLocation atLoc) {
177     llvm::DenseMap<IdentifierInfo *, PropActionKind>::iterator I;
178     I = ActionOnProp.find(props[0].PropD->getIdentifier());
179     if (I == ActionOnProp.end())
180       return;
181 
182     doPropAction(I->second, props, atLoc, false);
183   }
184 
185   void rewriteProperty(PropsTy &props, SourceLocation atLoc) {
186     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
187 
188     if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy |
189                      ObjCPropertyDecl::OBJC_PR_unsafe_unretained |
190                      ObjCPropertyDecl::OBJC_PR_strong |
191                      ObjCPropertyDecl::OBJC_PR_weak))
192       return;
193 
194     if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) {
195       // strong is the default.
196       return doPropAction(PropAction_RetainRemoved, props, atLoc);
197     }
198 
199     bool HasIvarAssignedAPlusOneObject = hasIvarAssignedAPlusOneObject(props);
200 
201     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
202       if (HasIvarAssignedAPlusOneObject ||
203           (Pass.isGCMigration() && !hasGCWeak(props, atLoc))) {
204         return doPropAction(PropAction_AssignRemoved, props, atLoc);
205       }
206       return doPropAction(PropAction_AssignRewritten, props, atLoc);
207     }
208 
209     if (HasIvarAssignedAPlusOneObject ||
210         (Pass.isGCMigration() && !hasGCWeak(props, atLoc)))
211       return; // 'strong' by default.
212 
213     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
214   }
215 
216   void removeAssignForDefaultStrong(PropsTy &props,
217                                     SourceLocation atLoc) const {
218     removeAttribute("retain", atLoc);
219     if (!removeAttribute("assign", atLoc))
220       return;
221 
222     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
223       if (I->ImplD)
224         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
225                                 I->ImplD->getLocation());
226     }
227   }
228 
229   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
230     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
231                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
232 
233     bool rewroteAttr = rewriteAttribute("assign",
234                                      canUseWeak ? "weak" : "unsafe_unretained",
235                                          atLoc);
236     if (!rewroteAttr)
237       canUseWeak = false;
238 
239     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
240       if (isUserDeclared(I->IvarD))
241         Pass.TA.insert(I->IvarD->getLocation(),
242                        canUseWeak ? "__weak " : "__unsafe_unretained ");
243       if (I->ImplD)
244         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
245                                 I->ImplD->getLocation());
246     }
247   }
248 
249   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
250                                           SourceLocation atLoc) const {
251     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props),
252                                   /*AllowOnUnknownClass=*/Pass.isGCMigration());
253 
254     bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
255                                   atLoc);
256     if (!addedAttr)
257       canUseWeak = false;
258 
259     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
260       if (isUserDeclared(I->IvarD))
261         Pass.TA.insert(I->IvarD->getLocation(),
262                        canUseWeak ? "__weak " : "__unsafe_unretained ");
263       if (I->ImplD) {
264         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
265                                 I->ImplD->getLocation());
266         Pass.TA.clearDiagnostic(
267                            diag::err_arc_objc_property_default_assign_on_object,
268                            I->ImplD->getLocation());
269       }
270     }
271   }
272 
273   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
274     return rewriteAttribute(fromAttr, StringRef(), atLoc);
275   }
276 
277   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
278                         SourceLocation atLoc) const {
279     return MigrateCtx.rewritePropertyAttribute(fromAttr, toAttr, atLoc);
280   }
281 
282   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
283     if (atLoc.isMacroID())
284       return false;
285 
286     SourceManager &SM = Pass.Ctx.getSourceManager();
287 
288     // Break down the source location.
289     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
290 
291     // Try to load the file buffer.
292     bool invalidTemp = false;
293     StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
294     if (invalidTemp)
295       return false;
296 
297     const char *tokenBegin = file.data() + locInfo.second;
298 
299     // Lex from the start of the given location.
300     Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
301                 Pass.Ctx.getLangOptions(),
302                 file.begin(), tokenBegin, file.end());
303     Token tok;
304     lexer.LexFromRawLexer(tok);
305     if (tok.isNot(tok::at)) return false;
306     lexer.LexFromRawLexer(tok);
307     if (tok.isNot(tok::raw_identifier)) return false;
308     if (StringRef(tok.getRawIdentifierData(), tok.getLength())
309           != "property")
310       return false;
311     lexer.LexFromRawLexer(tok);
312 
313     if (tok.isNot(tok::l_paren)) {
314       Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
315       return true;
316     }
317 
318     lexer.LexFromRawLexer(tok);
319     if (tok.is(tok::r_paren)) {
320       Pass.TA.insert(tok.getLocation(), attr);
321       return true;
322     }
323 
324     if (tok.isNot(tok::raw_identifier)) return false;
325 
326     Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
327     return true;
328   }
329 
330   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
331     ObjCIvarDecl *Ivar;
332   public:
333     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
334 
335     bool VisitBinAssign(BinaryOperator *E) {
336       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
337       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
338         if (RE->getDecl() != Ivar)
339           return true;
340 
341       if (ObjCMessageExpr *
342             ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
343         if (ME->getMethodFamily() == OMF_retain)
344           return false;
345 
346       ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
347       while (implCE && implCE->getCastKind() ==  CK_BitCast)
348         implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
349 
350       if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
351         return false;
352       }
353 
354       return true;
355     }
356   };
357 
358   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
359     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
360       PlusOneAssign oneAssign(I->IvarD);
361       bool notFound = oneAssign.TraverseDecl(CurImplD);
362       if (!notFound)
363         return true;
364     }
365 
366     return false;
367   }
368 
369   bool hasIvarWithExplicitARCOwnership(PropsTy &props) const {
370     if (Pass.isGCMigration())
371       return false;
372 
373     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
374       if (isUserDeclared(I->IvarD)) {
375         if (isa<AttributedType>(I->IvarD->getType()))
376           return true;
377         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
378               != Qualifiers::OCL_Strong)
379           return true;
380       }
381     }
382 
383     return false;
384   }
385 
386   bool hasAllIvarsBacked(PropsTy &props) const {
387     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
388       if (!isUserDeclared(I->IvarD))
389         return false;
390 
391     return true;
392   }
393 
394   // \brief Returns true if all declarations in the @property have GC __weak.
395   bool hasGCWeak(PropsTy &props, SourceLocation atLoc) const {
396     if (!Pass.isGCMigration())
397       return false;
398     if (props.empty())
399       return false;
400     return MigrateCtx.AtPropsWeak.count(atLoc.getRawEncoding());
401   }
402 
403   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
404     return ivarD && !ivarD->getSynthesize();
405   }
406 
407   QualType getPropertyType(PropsTy &props) const {
408     assert(!props.empty());
409     QualType ty = props[0].PropD->getType().getUnqualifiedType();
410 
411 #ifndef NDEBUG
412     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
413       assert(ty == I->PropD->getType().getUnqualifiedType());
414 #endif
415 
416     return ty;
417   }
418 
419   ObjCPropertyDecl::PropertyAttributeKind
420   getPropertyAttrs(PropsTy &props) const {
421     assert(!props.empty());
422     ObjCPropertyDecl::PropertyAttributeKind
423       attrs = props[0].PropD->getPropertyAttributesAsWritten();
424 
425 #ifndef NDEBUG
426     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
427       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
428 #endif
429 
430     return attrs;
431   }
432 };
433 
434 } // anonymous namespace
435 
436 void PropertyRewriteTraverser::traverseObjCImplementation(
437                                            ObjCImplementationContext &ImplCtx) {
438   PropertiesRewriter(ImplCtx.getMigrationContext())
439                                   .doTransform(ImplCtx.getImplementationDecl());
440 }
441