1 //===--- Tranforms.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 #include "Transforms.h"
11 #include "Internals.h"
12 #include "clang/Sema/SemaDiagnostic.h"
13 #include "clang/AST/RecursiveASTVisitor.h"
14 #include "clang/AST/StmtVisitor.h"
15 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
16 #include "clang/Lex/Lexer.h"
17 #include "clang/Basic/SourceManager.h"
18 #include "llvm/ADT/StringSwitch.h"
19 #include "llvm/ADT/DenseSet.h"
20 #include <map>
21 
22 using namespace clang;
23 using namespace arcmt;
24 using namespace trans;
25 
26 ASTTraverser::~ASTTraverser() { }
27 
28 //===----------------------------------------------------------------------===//
29 // Helpers.
30 //===----------------------------------------------------------------------===//
31 
32 /// \brief True if the class is one that does not support weak.
33 static bool isClassInWeakBlacklist(ObjCInterfaceDecl *cls) {
34   if (!cls)
35     return false;
36 
37   bool inList = llvm::StringSwitch<bool>(cls->getName())
38                  .Case("NSColorSpace", true)
39                  .Case("NSFont", true)
40                  .Case("NSFontPanel", true)
41                  .Case("NSImage", true)
42                  .Case("NSLazyBrowserCell", true)
43                  .Case("NSWindow", true)
44                  .Case("NSWindowController", true)
45                  .Case("NSViewController", true)
46                  .Case("NSMenuView", true)
47                  .Case("NSPersistentUIWindowInfo", true)
48                  .Case("NSTableCellView", true)
49                  .Case("NSATSTypeSetter", true)
50                  .Case("NSATSGlyphStorage", true)
51                  .Case("NSLineFragmentRenderingContext", true)
52                  .Case("NSAttributeDictionary", true)
53                  .Case("NSParagraphStyle", true)
54                  .Case("NSTextTab", true)
55                  .Case("NSSimpleHorizontalTypesetter", true)
56                  .Case("_NSCachedAttributedString", true)
57                  .Case("NSStringDrawingTextStorage", true)
58                  .Case("NSTextView", true)
59                  .Case("NSSubTextStorage", true)
60                  .Default(false);
61 
62   if (inList)
63     return true;
64 
65   return isClassInWeakBlacklist(cls->getSuperClass());
66 }
67 
68 bool trans::canApplyWeak(ASTContext &Ctx, QualType type,
69                          bool AllowOnUnknownClass) {
70   if (!Ctx.getLangOptions().ObjCRuntimeHasWeak)
71     return false;
72 
73   QualType T = type;
74   if (T.isNull())
75     return false;
76 
77   while (const PointerType *ptr = T->getAs<PointerType>())
78     T = ptr->getPointeeType();
79   if (const ObjCObjectPointerType *ObjT = T->getAs<ObjCObjectPointerType>()) {
80     ObjCInterfaceDecl *Class = ObjT->getInterfaceDecl();
81     if (!AllowOnUnknownClass && (!Class || Class->getName() == "NSObject"))
82       return false; // id/NSObject is not safe for weak.
83     if (!AllowOnUnknownClass && !Class->hasDefinition())
84       return false; // forward classes are not verifiable, therefore not safe.
85     if (Class->isArcWeakrefUnavailable())
86       return false;
87     if (isClassInWeakBlacklist(Class))
88       return false;
89   }
90 
91   return true;
92 }
93 
94 /// \brief 'Loc' is the end of a statement range. This returns the location
95 /// immediately after the semicolon following the statement.
96 /// If no semicolon is found or the location is inside a macro, the returned
97 /// source location will be invalid.
98 SourceLocation trans::findLocationAfterSemi(SourceLocation loc,
99                                             ASTContext &Ctx) {
100   SourceLocation SemiLoc = findSemiAfterLocation(loc, Ctx);
101   if (SemiLoc.isInvalid())
102     return SourceLocation();
103   return SemiLoc.getLocWithOffset(1);
104 }
105 
106 /// \brief \arg Loc is the end of a statement range. This returns the location
107 /// of the semicolon following the statement.
108 /// If no semicolon is found or the location is inside a macro, the returned
109 /// source location will be invalid.
110 SourceLocation trans::findSemiAfterLocation(SourceLocation loc,
111                                             ASTContext &Ctx) {
112   SourceManager &SM = Ctx.getSourceManager();
113   if (loc.isMacroID()) {
114     if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOptions()))
115       return SourceLocation();
116     loc = SM.getExpansionRange(loc).second;
117   }
118   loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOptions());
119 
120   // Break down the source location.
121   std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
122 
123   // Try to load the file buffer.
124   bool invalidTemp = false;
125   StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
126   if (invalidTemp)
127     return SourceLocation();
128 
129   const char *tokenBegin = file.data() + locInfo.second;
130 
131   // Lex from the start of the given location.
132   Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
133               Ctx.getLangOptions(),
134               file.begin(), tokenBegin, file.end());
135   Token tok;
136   lexer.LexFromRawLexer(tok);
137   if (tok.isNot(tok::semi))
138     return SourceLocation();
139 
140   return tok.getLocation();
141 }
142 
143 bool trans::hasSideEffects(Expr *E, ASTContext &Ctx) {
144   if (!E || !E->HasSideEffects(Ctx))
145     return false;
146 
147   E = E->IgnoreParenCasts();
148   ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
149   if (!ME)
150     return true;
151   switch (ME->getMethodFamily()) {
152   case OMF_autorelease:
153   case OMF_dealloc:
154   case OMF_release:
155   case OMF_retain:
156     switch (ME->getReceiverKind()) {
157     case ObjCMessageExpr::SuperInstance:
158       return false;
159     case ObjCMessageExpr::Instance:
160       return hasSideEffects(ME->getInstanceReceiver(), Ctx);
161     default:
162       break;
163     }
164     break;
165   default:
166     break;
167   }
168 
169   return true;
170 }
171 
172 bool trans::isGlobalVar(Expr *E) {
173   E = E->IgnoreParenCasts();
174   if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
175     return DRE->getDecl()->getDeclContext()->isFileContext() &&
176            DRE->getDecl()->getLinkage() == ExternalLinkage;
177   if (ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(E))
178     return isGlobalVar(condOp->getTrueExpr()) &&
179            isGlobalVar(condOp->getFalseExpr());
180 
181   return false;
182 }
183 
184 StringRef trans::getNilString(ASTContext &Ctx) {
185   if (Ctx.Idents.get("nil").hasMacroDefinition())
186     return "nil";
187   else
188     return "0";
189 }
190 
191 namespace {
192 
193 class ReferenceClear : public RecursiveASTVisitor<ReferenceClear> {
194   ExprSet &Refs;
195 public:
196   ReferenceClear(ExprSet &refs) : Refs(refs) { }
197   bool VisitDeclRefExpr(DeclRefExpr *E) { Refs.erase(E); return true; }
198   bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { Refs.erase(E); return true; }
199 };
200 
201 class ReferenceCollector : public RecursiveASTVisitor<ReferenceCollector> {
202   ValueDecl *Dcl;
203   ExprSet &Refs;
204 
205 public:
206   ReferenceCollector(ValueDecl *D, ExprSet &refs)
207     : Dcl(D), Refs(refs) { }
208 
209   bool VisitDeclRefExpr(DeclRefExpr *E) {
210     if (E->getDecl() == Dcl)
211       Refs.insert(E);
212     return true;
213   }
214 
215   bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) {
216     if (E->getDecl() == Dcl)
217       Refs.insert(E);
218     return true;
219   }
220 };
221 
222 class RemovablesCollector : public RecursiveASTVisitor<RemovablesCollector> {
223   ExprSet &Removables;
224 
225 public:
226   RemovablesCollector(ExprSet &removables)
227   : Removables(removables) { }
228 
229   bool shouldWalkTypesOfTypeLocs() const { return false; }
230 
231   bool TraverseStmtExpr(StmtExpr *E) {
232     CompoundStmt *S = E->getSubStmt();
233     for (CompoundStmt::body_iterator
234         I = S->body_begin(), E = S->body_end(); I != E; ++I) {
235       if (I != E - 1)
236         mark(*I);
237       TraverseStmt(*I);
238     }
239     return true;
240   }
241 
242   bool VisitCompoundStmt(CompoundStmt *S) {
243     for (CompoundStmt::body_iterator
244         I = S->body_begin(), E = S->body_end(); I != E; ++I)
245       mark(*I);
246     return true;
247   }
248 
249   bool VisitIfStmt(IfStmt *S) {
250     mark(S->getThen());
251     mark(S->getElse());
252     return true;
253   }
254 
255   bool VisitWhileStmt(WhileStmt *S) {
256     mark(S->getBody());
257     return true;
258   }
259 
260   bool VisitDoStmt(DoStmt *S) {
261     mark(S->getBody());
262     return true;
263   }
264 
265   bool VisitForStmt(ForStmt *S) {
266     mark(S->getInit());
267     mark(S->getInc());
268     mark(S->getBody());
269     return true;
270   }
271 
272 private:
273   void mark(Stmt *S) {
274     if (!S) return;
275 
276     while (LabelStmt *Label = dyn_cast<LabelStmt>(S))
277       S = Label->getSubStmt();
278     S = S->IgnoreImplicit();
279     if (Expr *E = dyn_cast<Expr>(S))
280       Removables.insert(E);
281   }
282 };
283 
284 } // end anonymous namespace
285 
286 void trans::clearRefsIn(Stmt *S, ExprSet &refs) {
287   ReferenceClear(refs).TraverseStmt(S);
288 }
289 
290 void trans::collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs) {
291   ReferenceCollector(D, refs).TraverseStmt(S);
292 }
293 
294 void trans::collectRemovables(Stmt *S, ExprSet &exprs) {
295   RemovablesCollector(exprs).TraverseStmt(S);
296 }
297 
298 //===----------------------------------------------------------------------===//
299 // MigrationContext
300 //===----------------------------------------------------------------------===//
301 
302 namespace {
303 
304 class ASTTransform : public RecursiveASTVisitor<ASTTransform> {
305   MigrationContext &MigrateCtx;
306   typedef RecursiveASTVisitor<ASTTransform> base;
307 
308 public:
309   ASTTransform(MigrationContext &MigrateCtx) : MigrateCtx(MigrateCtx) { }
310 
311   bool shouldWalkTypesOfTypeLocs() const { return false; }
312 
313   bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
314     ObjCImplementationContext ImplCtx(MigrateCtx, D);
315     for (MigrationContext::traverser_iterator
316            I = MigrateCtx.traversers_begin(),
317            E = MigrateCtx.traversers_end(); I != E; ++I)
318       (*I)->traverseObjCImplementation(ImplCtx);
319 
320     return base::TraverseObjCImplementationDecl(D);
321   }
322 
323   bool TraverseStmt(Stmt *rootS) {
324     if (!rootS)
325       return true;
326 
327     BodyContext BodyCtx(MigrateCtx, rootS);
328     for (MigrationContext::traverser_iterator
329            I = MigrateCtx.traversers_begin(),
330            E = MigrateCtx.traversers_end(); I != E; ++I)
331       (*I)->traverseBody(BodyCtx);
332 
333     return true;
334   }
335 };
336 
337 }
338 
339 MigrationContext::~MigrationContext() {
340   for (traverser_iterator
341          I = traversers_begin(), E = traversers_end(); I != E; ++I)
342     delete *I;
343 }
344 
345 bool MigrationContext::isGCOwnedNonObjC(QualType T) {
346   while (!T.isNull()) {
347     if (const AttributedType *AttrT = T->getAs<AttributedType>()) {
348       if (AttrT->getAttrKind() == AttributedType::attr_objc_ownership)
349         return !AttrT->getModifiedType()->isObjCRetainableType();
350     }
351 
352     if (T->isArrayType())
353       T = Pass.Ctx.getBaseElementType(T);
354     else if (const PointerType *PT = T->getAs<PointerType>())
355       T = PT->getPointeeType();
356     else if (const ReferenceType *RT = T->getAs<ReferenceType>())
357       T = RT->getPointeeType();
358     else
359       break;
360   }
361 
362   return false;
363 }
364 
365 bool MigrationContext::rewritePropertyAttribute(StringRef fromAttr,
366                                                 StringRef toAttr,
367                                                 SourceLocation atLoc) {
368   if (atLoc.isMacroID())
369     return false;
370 
371   SourceManager &SM = Pass.Ctx.getSourceManager();
372 
373   // Break down the source location.
374   std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
375 
376   // Try to load the file buffer.
377   bool invalidTemp = false;
378   StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
379   if (invalidTemp)
380     return false;
381 
382   const char *tokenBegin = file.data() + locInfo.second;
383 
384   // Lex from the start of the given location.
385   Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
386               Pass.Ctx.getLangOptions(),
387               file.begin(), tokenBegin, file.end());
388   Token tok;
389   lexer.LexFromRawLexer(tok);
390   if (tok.isNot(tok::at)) return false;
391   lexer.LexFromRawLexer(tok);
392   if (tok.isNot(tok::raw_identifier)) return false;
393   if (StringRef(tok.getRawIdentifierData(), tok.getLength())
394         != "property")
395     return false;
396   lexer.LexFromRawLexer(tok);
397   if (tok.isNot(tok::l_paren)) return false;
398 
399   Token BeforeTok = tok;
400   Token AfterTok;
401   AfterTok.startToken();
402   SourceLocation AttrLoc;
403 
404   lexer.LexFromRawLexer(tok);
405   if (tok.is(tok::r_paren))
406     return false;
407 
408   while (1) {
409     if (tok.isNot(tok::raw_identifier)) return false;
410     StringRef ident(tok.getRawIdentifierData(), tok.getLength());
411     if (ident == fromAttr) {
412       if (!toAttr.empty()) {
413         Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
414         return true;
415       }
416       // We want to remove the attribute.
417       AttrLoc = tok.getLocation();
418     }
419 
420     do {
421       lexer.LexFromRawLexer(tok);
422       if (AttrLoc.isValid() && AfterTok.is(tok::unknown))
423         AfterTok = tok;
424     } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
425     if (tok.is(tok::r_paren))
426       break;
427     if (AttrLoc.isInvalid())
428       BeforeTok = tok;
429     lexer.LexFromRawLexer(tok);
430   }
431 
432   if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) {
433     // We want to remove the attribute.
434     if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) {
435       Pass.TA.remove(SourceRange(BeforeTok.getLocation(),
436                                  AfterTok.getLocation()));
437     } else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) {
438       Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation()));
439     } else {
440       Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc));
441     }
442 
443     return true;
444   }
445 
446   return false;
447 }
448 
449 bool MigrationContext::addPropertyAttribute(StringRef attr,
450                                             SourceLocation atLoc) {
451   if (atLoc.isMacroID())
452     return false;
453 
454   SourceManager &SM = Pass.Ctx.getSourceManager();
455 
456   // Break down the source location.
457   std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
458 
459   // Try to load the file buffer.
460   bool invalidTemp = false;
461   StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
462   if (invalidTemp)
463     return false;
464 
465   const char *tokenBegin = file.data() + locInfo.second;
466 
467   // Lex from the start of the given location.
468   Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
469               Pass.Ctx.getLangOptions(),
470               file.begin(), tokenBegin, file.end());
471   Token tok;
472   lexer.LexFromRawLexer(tok);
473   if (tok.isNot(tok::at)) return false;
474   lexer.LexFromRawLexer(tok);
475   if (tok.isNot(tok::raw_identifier)) return false;
476   if (StringRef(tok.getRawIdentifierData(), tok.getLength())
477         != "property")
478     return false;
479   lexer.LexFromRawLexer(tok);
480 
481   if (tok.isNot(tok::l_paren)) {
482     Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
483     return true;
484   }
485 
486   lexer.LexFromRawLexer(tok);
487   if (tok.is(tok::r_paren)) {
488     Pass.TA.insert(tok.getLocation(), attr);
489     return true;
490   }
491 
492   if (tok.isNot(tok::raw_identifier)) return false;
493 
494   Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
495   return true;
496 }
497 
498 void MigrationContext::traverse(TranslationUnitDecl *TU) {
499   for (traverser_iterator
500          I = traversers_begin(), E = traversers_end(); I != E; ++I)
501     (*I)->traverseTU(*this);
502 
503   ASTTransform(*this).TraverseDecl(TU);
504 }
505 
506 //===----------------------------------------------------------------------===//
507 // getAllTransformations.
508 //===----------------------------------------------------------------------===//
509 
510 static void traverseAST(MigrationPass &pass) {
511   MigrationContext MigrateCtx(pass);
512 
513   if (pass.isGCMigration()) {
514     MigrateCtx.addTraverser(new GCCollectableCallsTraverser);
515     MigrateCtx.addTraverser(new GCAttrsTraverser());
516   }
517   MigrateCtx.addTraverser(new PropertyRewriteTraverser());
518 
519   MigrateCtx.traverse(pass.Ctx.getTranslationUnitDecl());
520 }
521 
522 static void independentTransforms(MigrationPass &pass) {
523   rewriteAutoreleasePool(pass);
524   removeRetainReleaseDeallocFinalize(pass);
525   rewriteUnusedInitDelegate(pass);
526   removeZeroOutPropsInDeallocFinalize(pass);
527   makeAssignARCSafe(pass);
528   rewriteUnbridgedCasts(pass);
529   rewriteBlockObjCVariable(pass);
530   checkAPIUses(pass);
531   traverseAST(pass);
532 }
533 
534 std::vector<TransformFn> arcmt::getAllTransformations(
535                                                LangOptions::GCMode OrigGCMode) {
536   std::vector<TransformFn> transforms;
537 
538   transforms.push_back(independentTransforms);
539   // This depends on previous transformations removing various expressions.
540   transforms.push_back(removeEmptyStatementsAndDeallocFinalize);
541 
542   return transforms;
543 }
544