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   enum PropActionKind {
51     PropAction_None,
52     PropAction_RetainToStrong,
53     PropAction_RetainRemoved,
54     PropAction_AssignToStrong,
55     PropAction_AssignRewritten,
56     PropAction_MaybeAddStrong,
57     PropAction_MaybeAddWeakOrUnsafe
58   };
59 
60   struct PropData {
61     ObjCPropertyDecl *PropD;
62     ObjCIvarDecl *IvarD;
63     ObjCPropertyImplDecl *ImplD;
64 
65     PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { }
66   };
67 
68   typedef SmallVector<PropData, 2> PropsTy;
69   typedef std::map<unsigned, PropsTy> AtPropDeclsTy;
70   AtPropDeclsTy AtProps;
71   llvm::DenseMap<IdentifierInfo *, PropActionKind> ActionOnProp;
72 
73 public:
74   PropertiesRewriter(MigrationPass &pass) : Pass(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       QualType ty = getPropertyType(props);
129       if (!ty->isObjCRetainableType())
130         continue;
131       if (hasIvarWithExplicitOwnership(props))
132         continue;
133 
134       Transaction Trans(Pass.TA);
135       rewriteProperty(props, atLoc);
136     }
137 
138     AtPropDeclsTy AtExtProps;
139     // Look through extensions.
140     for (ObjCCategoryDecl *Cat = iface->getCategoryList();
141            Cat; Cat = Cat->getNextClassCategory())
142       if (Cat->IsClassExtension())
143         collectProperties(Cat, AtExtProps);
144 
145     for (AtPropDeclsTy::iterator
146            I = AtExtProps.begin(), E = AtExtProps.end(); I != E; ++I) {
147       SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first);
148       PropsTy &props = I->second;
149       Transaction Trans(Pass.TA);
150       doActionForExtensionProp(props, atLoc);
151     }
152   }
153 
154 private:
155   void doPropAction(PropActionKind kind,
156                     PropsTy &props, SourceLocation atLoc,
157                     bool markAction = true) {
158     if (markAction)
159       for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
160         ActionOnProp[I->PropD->getIdentifier()] = kind;
161 
162     switch (kind) {
163     case PropAction_None:
164       return;
165     case PropAction_RetainToStrong:
166       rewriteAttribute("retain", "strong", atLoc);
167       return;
168     case PropAction_RetainRemoved:
169       removeAttribute("retain", atLoc);
170       return;
171     case PropAction_AssignToStrong:
172       rewriteAttribute("assign", "strong", atLoc);
173       return;
174     case PropAction_AssignRewritten:
175       return rewriteAssign(props, atLoc);
176     case PropAction_MaybeAddStrong:
177       return maybeAddStrongAttr(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       if (propAttrs & ObjCPropertyDecl::OBJC_PR_readonly)
203         return doPropAction(PropAction_RetainToStrong, props, atLoc);
204       else
205         // strong is the default.
206         return doPropAction(PropAction_RetainRemoved, props, atLoc);
207     }
208 
209     if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) {
210       if (hasIvarAssignedAPlusOneObject(props)) {
211         return doPropAction(PropAction_AssignToStrong, props, atLoc);
212       }
213       return doPropAction(PropAction_AssignRewritten, props, atLoc);
214     }
215 
216     if (hasIvarAssignedAPlusOneObject(props))
217       return doPropAction(PropAction_MaybeAddStrong, props, atLoc);
218 
219     return doPropAction(PropAction_MaybeAddWeakOrUnsafe, props, atLoc);
220   }
221 
222   void rewriteAssign(PropsTy &props, SourceLocation atLoc) const {
223     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
224 
225     bool rewroteAttr = rewriteAttribute("assign",
226                                      canUseWeak ? "weak" : "unsafe_unretained",
227                                          atLoc);
228     if (!rewroteAttr)
229       canUseWeak = false;
230 
231     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
232       if (isUserDeclared(I->IvarD))
233         Pass.TA.insert(I->IvarD->getLocation(),
234                        canUseWeak ? "__weak " : "__unsafe_unretained ");
235       if (I->ImplD)
236         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
237                                 I->ImplD->getLocation());
238     }
239   }
240 
241   void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props,
242                                           SourceLocation atLoc) const {
243     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
244 
245     bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props));
246     if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
247         !hasAllIvarsBacked(props)) {
248       bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained",
249                                     atLoc);
250       if (!addedAttr)
251         canUseWeak = false;
252     }
253 
254     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
255       if (isUserDeclared(I->IvarD))
256         Pass.TA.insert(I->IvarD->getLocation(),
257                        canUseWeak ? "__weak " : "__unsafe_unretained ");
258       if (I->ImplD) {
259         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
260                                 I->ImplD->getLocation());
261         Pass.TA.clearDiagnostic(
262                            diag::err_arc_objc_property_default_assign_on_object,
263                            I->ImplD->getLocation());
264       }
265     }
266   }
267 
268   void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const {
269     ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props);
270 
271     if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) ||
272         !hasAllIvarsBacked(props)) {
273       addAttribute("strong", atLoc);
274     }
275 
276     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
277       if (I->ImplD) {
278         Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership,
279                                 I->ImplD->getLocation());
280         Pass.TA.clearDiagnostic(
281                            diag::err_arc_objc_property_default_assign_on_object,
282                            I->ImplD->getLocation());
283       }
284     }
285   }
286 
287   bool removeAttribute(StringRef fromAttr, SourceLocation atLoc) const {
288     return rewriteAttribute(fromAttr, StringRef(), atLoc);
289   }
290 
291   bool rewriteAttribute(StringRef fromAttr, StringRef toAttr,
292                         SourceLocation atLoc) const {
293     if (atLoc.isMacroID())
294       return false;
295 
296     SourceManager &SM = Pass.Ctx.getSourceManager();
297 
298     // Break down the source location.
299     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
300 
301     // Try to load the file buffer.
302     bool invalidTemp = false;
303     StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
304     if (invalidTemp)
305       return false;
306 
307     const char *tokenBegin = file.data() + locInfo.second;
308 
309     // Lex from the start of the given location.
310     Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
311                 Pass.Ctx.getLangOptions(),
312                 file.begin(), tokenBegin, file.end());
313     Token tok;
314     lexer.LexFromRawLexer(tok);
315     if (tok.isNot(tok::at)) return false;
316     lexer.LexFromRawLexer(tok);
317     if (tok.isNot(tok::raw_identifier)) return false;
318     if (StringRef(tok.getRawIdentifierData(), tok.getLength())
319           != "property")
320       return false;
321     lexer.LexFromRawLexer(tok);
322     if (tok.isNot(tok::l_paren)) return false;
323 
324     Token BeforeTok = tok;
325     Token AfterTok;
326     AfterTok.startToken();
327     SourceLocation AttrLoc;
328 
329     lexer.LexFromRawLexer(tok);
330     if (tok.is(tok::r_paren))
331       return false;
332 
333     while (1) {
334       if (tok.isNot(tok::raw_identifier)) return false;
335       StringRef ident(tok.getRawIdentifierData(), tok.getLength());
336       if (ident == fromAttr) {
337         if (!toAttr.empty()) {
338           Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
339           return true;
340         }
341         // We want to remove the attribute.
342         AttrLoc = tok.getLocation();
343       }
344 
345       do {
346         lexer.LexFromRawLexer(tok);
347         if (AttrLoc.isValid() && AfterTok.is(tok::unknown))
348           AfterTok = tok;
349       } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
350       if (tok.is(tok::r_paren))
351         break;
352       if (AttrLoc.isInvalid())
353         BeforeTok = tok;
354       lexer.LexFromRawLexer(tok);
355     }
356 
357     if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) {
358       // We want to remove the attribute.
359       if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) {
360         Pass.TA.remove(SourceRange(BeforeTok.getLocation(),
361                                    AfterTok.getLocation()));
362       } else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) {
363         Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation()));
364       } else {
365         Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc));
366       }
367 
368       return true;
369     }
370 
371     return false;
372   }
373 
374   bool addAttribute(StringRef attr, SourceLocation atLoc) const {
375     if (atLoc.isMacroID())
376       return false;
377 
378     SourceManager &SM = Pass.Ctx.getSourceManager();
379 
380     // Break down the source location.
381     std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
382 
383     // Try to load the file buffer.
384     bool invalidTemp = false;
385     StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
386     if (invalidTemp)
387       return false;
388 
389     const char *tokenBegin = file.data() + locInfo.second;
390 
391     // Lex from the start of the given location.
392     Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
393                 Pass.Ctx.getLangOptions(),
394                 file.begin(), tokenBegin, file.end());
395     Token tok;
396     lexer.LexFromRawLexer(tok);
397     if (tok.isNot(tok::at)) return false;
398     lexer.LexFromRawLexer(tok);
399     if (tok.isNot(tok::raw_identifier)) return false;
400     if (StringRef(tok.getRawIdentifierData(), tok.getLength())
401           != "property")
402       return false;
403     lexer.LexFromRawLexer(tok);
404 
405     if (tok.isNot(tok::l_paren)) {
406       Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
407       return true;
408     }
409 
410     lexer.LexFromRawLexer(tok);
411     if (tok.is(tok::r_paren)) {
412       Pass.TA.insert(tok.getLocation(), attr);
413       return true;
414     }
415 
416     if (tok.isNot(tok::raw_identifier)) return false;
417 
418     Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
419     return true;
420   }
421 
422   class PlusOneAssign : public RecursiveASTVisitor<PlusOneAssign> {
423     ObjCIvarDecl *Ivar;
424   public:
425     PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {}
426 
427     bool VisitBinAssign(BinaryOperator *E) {
428       Expr *lhs = E->getLHS()->IgnoreParenImpCasts();
429       if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(lhs)) {
430         if (RE->getDecl() != Ivar)
431           return true;
432 
433       if (ObjCMessageExpr *
434             ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
435         if (ME->getMethodFamily() == OMF_retain)
436           return false;
437 
438       ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
439       while (implCE && implCE->getCastKind() ==  CK_BitCast)
440         implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
441 
442       if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
443         return false;
444       }
445 
446       return true;
447     }
448   };
449 
450   bool hasIvarAssignedAPlusOneObject(PropsTy &props) const {
451     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
452       PlusOneAssign oneAssign(I->IvarD);
453       bool notFound = oneAssign.TraverseDecl(CurImplD);
454       if (!notFound)
455         return true;
456     }
457 
458     return false;
459   }
460 
461   bool hasIvarWithExplicitOwnership(PropsTy &props) const {
462     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) {
463       if (isUserDeclared(I->IvarD)) {
464         if (isa<AttributedType>(I->IvarD->getType()))
465           return true;
466         if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime()
467               != Qualifiers::OCL_Strong)
468           return true;
469       }
470     }
471 
472     return false;
473   }
474 
475   bool hasAllIvarsBacked(PropsTy &props) const {
476     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
477       if (!isUserDeclared(I->IvarD))
478         return false;
479 
480     return true;
481   }
482 
483   bool isUserDeclared(ObjCIvarDecl *ivarD) const {
484     return ivarD && !ivarD->getSynthesize();
485   }
486 
487   QualType getPropertyType(PropsTy &props) const {
488     assert(!props.empty());
489     QualType ty = props[0].PropD->getType();
490 
491 #ifndef NDEBUG
492     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
493       assert(ty == I->PropD->getType());
494 #endif
495 
496     return ty;
497   }
498 
499   ObjCPropertyDecl::PropertyAttributeKind
500   getPropertyAttrs(PropsTy &props) const {
501     assert(!props.empty());
502     ObjCPropertyDecl::PropertyAttributeKind
503       attrs = props[0].PropD->getPropertyAttributesAsWritten();
504 
505 #ifndef NDEBUG
506     for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I)
507       assert(attrs == I->PropD->getPropertyAttributesAsWritten());
508 #endif
509 
510     return attrs;
511   }
512 };
513 
514 class ImplementationChecker :
515                              public RecursiveASTVisitor<ImplementationChecker> {
516   MigrationPass &Pass;
517 
518 public:
519   ImplementationChecker(MigrationPass &pass) : Pass(pass) { }
520 
521   bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
522     PropertiesRewriter(Pass).doTransform(D);
523     return true;
524   }
525 };
526 
527 } // anonymous namespace
528 
529 void trans::rewriteProperties(MigrationPass &pass) {
530   ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
531 }
532