Transforms.cpp revision f4b88a45902af1802a1cb42ba48b1c474474f228
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
22using namespace clang;
23using namespace arcmt;
24using namespace trans;
25
26ASTTraverser::~ASTTraverser() { }
27
28//===----------------------------------------------------------------------===//
29// Helpers.
30//===----------------------------------------------------------------------===//
31
32/// \brief True if the class is one that does not support weak.
33static 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
68bool 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.
98SourceLocation 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.
110SourceLocation trans::findSemiAfterLocation(SourceLocation loc,
111                                            ASTContext &Ctx) {
112  SourceManager &SM = Ctx.getSourceManager();
113  if (loc.isMacroID()) {
114    if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOptions(), &loc))
115      return SourceLocation();
116  }
117  loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOptions());
118
119  // Break down the source location.
120  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
121
122  // Try to load the file buffer.
123  bool invalidTemp = false;
124  StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
125  if (invalidTemp)
126    return SourceLocation();
127
128  const char *tokenBegin = file.data() + locInfo.second;
129
130  // Lex from the start of the given location.
131  Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
132              Ctx.getLangOptions(),
133              file.begin(), tokenBegin, file.end());
134  Token tok;
135  lexer.LexFromRawLexer(tok);
136  if (tok.isNot(tok::semi))
137    return SourceLocation();
138
139  return tok.getLocation();
140}
141
142bool trans::hasSideEffects(Expr *E, ASTContext &Ctx) {
143  if (!E || !E->HasSideEffects(Ctx))
144    return false;
145
146  E = E->IgnoreParenCasts();
147  ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
148  if (!ME)
149    return true;
150  switch (ME->getMethodFamily()) {
151  case OMF_autorelease:
152  case OMF_dealloc:
153  case OMF_release:
154  case OMF_retain:
155    switch (ME->getReceiverKind()) {
156    case ObjCMessageExpr::SuperInstance:
157      return false;
158    case ObjCMessageExpr::Instance:
159      return hasSideEffects(ME->getInstanceReceiver(), Ctx);
160    default:
161      break;
162    }
163    break;
164  default:
165    break;
166  }
167
168  return true;
169}
170
171bool trans::isGlobalVar(Expr *E) {
172  E = E->IgnoreParenCasts();
173  if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
174    return DRE->getDecl()->getDeclContext()->isFileContext() &&
175           DRE->getDecl()->getLinkage() == ExternalLinkage;
176  if (ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(E))
177    return isGlobalVar(condOp->getTrueExpr()) &&
178           isGlobalVar(condOp->getFalseExpr());
179
180  return false;
181}
182
183StringRef trans::getNilString(ASTContext &Ctx) {
184  if (Ctx.Idents.get("nil").hasMacroDefinition())
185    return "nil";
186  else
187    return "0";
188}
189
190namespace {
191
192class ReferenceClear : public RecursiveASTVisitor<ReferenceClear> {
193  ExprSet &Refs;
194public:
195  ReferenceClear(ExprSet &refs) : Refs(refs) { }
196  bool VisitDeclRefExpr(DeclRefExpr *E) { Refs.erase(E); return true; }
197};
198
199class ReferenceCollector : public RecursiveASTVisitor<ReferenceCollector> {
200  ValueDecl *Dcl;
201  ExprSet &Refs;
202
203public:
204  ReferenceCollector(ValueDecl *D, ExprSet &refs)
205    : Dcl(D), Refs(refs) { }
206
207  bool VisitDeclRefExpr(DeclRefExpr *E) {
208    if (E->getDecl() == Dcl)
209      Refs.insert(E);
210    return true;
211  }
212};
213
214class RemovablesCollector : public RecursiveASTVisitor<RemovablesCollector> {
215  ExprSet &Removables;
216
217public:
218  RemovablesCollector(ExprSet &removables)
219  : Removables(removables) { }
220
221  bool shouldWalkTypesOfTypeLocs() const { return false; }
222
223  bool TraverseStmtExpr(StmtExpr *E) {
224    CompoundStmt *S = E->getSubStmt();
225    for (CompoundStmt::body_iterator
226        I = S->body_begin(), E = S->body_end(); I != E; ++I) {
227      if (I != E - 1)
228        mark(*I);
229      TraverseStmt(*I);
230    }
231    return true;
232  }
233
234  bool VisitCompoundStmt(CompoundStmt *S) {
235    for (CompoundStmt::body_iterator
236        I = S->body_begin(), E = S->body_end(); I != E; ++I)
237      mark(*I);
238    return true;
239  }
240
241  bool VisitIfStmt(IfStmt *S) {
242    mark(S->getThen());
243    mark(S->getElse());
244    return true;
245  }
246
247  bool VisitWhileStmt(WhileStmt *S) {
248    mark(S->getBody());
249    return true;
250  }
251
252  bool VisitDoStmt(DoStmt *S) {
253    mark(S->getBody());
254    return true;
255  }
256
257  bool VisitForStmt(ForStmt *S) {
258    mark(S->getInit());
259    mark(S->getInc());
260    mark(S->getBody());
261    return true;
262  }
263
264private:
265  void mark(Stmt *S) {
266    if (!S) return;
267
268    while (LabelStmt *Label = dyn_cast<LabelStmt>(S))
269      S = Label->getSubStmt();
270    S = S->IgnoreImplicit();
271    if (Expr *E = dyn_cast<Expr>(S))
272      Removables.insert(E);
273  }
274};
275
276} // end anonymous namespace
277
278void trans::clearRefsIn(Stmt *S, ExprSet &refs) {
279  ReferenceClear(refs).TraverseStmt(S);
280}
281
282void trans::collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs) {
283  ReferenceCollector(D, refs).TraverseStmt(S);
284}
285
286void trans::collectRemovables(Stmt *S, ExprSet &exprs) {
287  RemovablesCollector(exprs).TraverseStmt(S);
288}
289
290//===----------------------------------------------------------------------===//
291// MigrationContext
292//===----------------------------------------------------------------------===//
293
294namespace {
295
296class ASTTransform : public RecursiveASTVisitor<ASTTransform> {
297  MigrationContext &MigrateCtx;
298  typedef RecursiveASTVisitor<ASTTransform> base;
299
300public:
301  ASTTransform(MigrationContext &MigrateCtx) : MigrateCtx(MigrateCtx) { }
302
303  bool shouldWalkTypesOfTypeLocs() const { return false; }
304
305  bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
306    ObjCImplementationContext ImplCtx(MigrateCtx, D);
307    for (MigrationContext::traverser_iterator
308           I = MigrateCtx.traversers_begin(),
309           E = MigrateCtx.traversers_end(); I != E; ++I)
310      (*I)->traverseObjCImplementation(ImplCtx);
311
312    return base::TraverseObjCImplementationDecl(D);
313  }
314
315  bool TraverseStmt(Stmt *rootS) {
316    if (!rootS)
317      return true;
318
319    BodyContext BodyCtx(MigrateCtx, rootS);
320    for (MigrationContext::traverser_iterator
321           I = MigrateCtx.traversers_begin(),
322           E = MigrateCtx.traversers_end(); I != E; ++I)
323      (*I)->traverseBody(BodyCtx);
324
325    return true;
326  }
327};
328
329}
330
331MigrationContext::~MigrationContext() {
332  for (traverser_iterator
333         I = traversers_begin(), E = traversers_end(); I != E; ++I)
334    delete *I;
335}
336
337bool MigrationContext::isGCOwnedNonObjC(QualType T) {
338  while (!T.isNull()) {
339    if (const AttributedType *AttrT = T->getAs<AttributedType>()) {
340      if (AttrT->getAttrKind() == AttributedType::attr_objc_ownership)
341        return !AttrT->getModifiedType()->isObjCRetainableType();
342    }
343
344    if (T->isArrayType())
345      T = Pass.Ctx.getBaseElementType(T);
346    else if (const PointerType *PT = T->getAs<PointerType>())
347      T = PT->getPointeeType();
348    else if (const ReferenceType *RT = T->getAs<ReferenceType>())
349      T = RT->getPointeeType();
350    else
351      break;
352  }
353
354  return false;
355}
356
357bool MigrationContext::rewritePropertyAttribute(StringRef fromAttr,
358                                                StringRef toAttr,
359                                                SourceLocation atLoc) {
360  if (atLoc.isMacroID())
361    return false;
362
363  SourceManager &SM = Pass.Ctx.getSourceManager();
364
365  // Break down the source location.
366  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
367
368  // Try to load the file buffer.
369  bool invalidTemp = false;
370  StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
371  if (invalidTemp)
372    return false;
373
374  const char *tokenBegin = file.data() + locInfo.second;
375
376  // Lex from the start of the given location.
377  Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
378              Pass.Ctx.getLangOptions(),
379              file.begin(), tokenBegin, file.end());
380  Token tok;
381  lexer.LexFromRawLexer(tok);
382  if (tok.isNot(tok::at)) return false;
383  lexer.LexFromRawLexer(tok);
384  if (tok.isNot(tok::raw_identifier)) return false;
385  if (StringRef(tok.getRawIdentifierData(), tok.getLength())
386        != "property")
387    return false;
388  lexer.LexFromRawLexer(tok);
389  if (tok.isNot(tok::l_paren)) return false;
390
391  Token BeforeTok = tok;
392  Token AfterTok;
393  AfterTok.startToken();
394  SourceLocation AttrLoc;
395
396  lexer.LexFromRawLexer(tok);
397  if (tok.is(tok::r_paren))
398    return false;
399
400  while (1) {
401    if (tok.isNot(tok::raw_identifier)) return false;
402    StringRef ident(tok.getRawIdentifierData(), tok.getLength());
403    if (ident == fromAttr) {
404      if (!toAttr.empty()) {
405        Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
406        return true;
407      }
408      // We want to remove the attribute.
409      AttrLoc = tok.getLocation();
410    }
411
412    do {
413      lexer.LexFromRawLexer(tok);
414      if (AttrLoc.isValid() && AfterTok.is(tok::unknown))
415        AfterTok = tok;
416    } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
417    if (tok.is(tok::r_paren))
418      break;
419    if (AttrLoc.isInvalid())
420      BeforeTok = tok;
421    lexer.LexFromRawLexer(tok);
422  }
423
424  if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) {
425    // We want to remove the attribute.
426    if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) {
427      Pass.TA.remove(SourceRange(BeforeTok.getLocation(),
428                                 AfterTok.getLocation()));
429    } else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) {
430      Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation()));
431    } else {
432      Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc));
433    }
434
435    return true;
436  }
437
438  return false;
439}
440
441bool MigrationContext::addPropertyAttribute(StringRef attr,
442                                            SourceLocation atLoc) {
443  if (atLoc.isMacroID())
444    return false;
445
446  SourceManager &SM = Pass.Ctx.getSourceManager();
447
448  // Break down the source location.
449  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
450
451  // Try to load the file buffer.
452  bool invalidTemp = false;
453  StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
454  if (invalidTemp)
455    return false;
456
457  const char *tokenBegin = file.data() + locInfo.second;
458
459  // Lex from the start of the given location.
460  Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
461              Pass.Ctx.getLangOptions(),
462              file.begin(), tokenBegin, file.end());
463  Token tok;
464  lexer.LexFromRawLexer(tok);
465  if (tok.isNot(tok::at)) return false;
466  lexer.LexFromRawLexer(tok);
467  if (tok.isNot(tok::raw_identifier)) return false;
468  if (StringRef(tok.getRawIdentifierData(), tok.getLength())
469        != "property")
470    return false;
471  lexer.LexFromRawLexer(tok);
472
473  if (tok.isNot(tok::l_paren)) {
474    Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
475    return true;
476  }
477
478  lexer.LexFromRawLexer(tok);
479  if (tok.is(tok::r_paren)) {
480    Pass.TA.insert(tok.getLocation(), attr);
481    return true;
482  }
483
484  if (tok.isNot(tok::raw_identifier)) return false;
485
486  Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
487  return true;
488}
489
490void MigrationContext::traverse(TranslationUnitDecl *TU) {
491  for (traverser_iterator
492         I = traversers_begin(), E = traversers_end(); I != E; ++I)
493    (*I)->traverseTU(*this);
494
495  ASTTransform(*this).TraverseDecl(TU);
496}
497
498static void GCRewriteFinalize(MigrationPass &pass) {
499  ASTContext &Ctx = pass.Ctx;
500  TransformActions &TA = pass.TA;
501  DeclContext *DC = Ctx.getTranslationUnitDecl();
502  Selector FinalizeSel =
503   Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize"));
504
505  typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl>
506  impl_iterator;
507  for (impl_iterator I = impl_iterator(DC->decls_begin()),
508       E = impl_iterator(DC->decls_end()); I != E; ++I) {
509    for (ObjCImplementationDecl::instmeth_iterator
510         MI = (*I)->instmeth_begin(),
511         ME = (*I)->instmeth_end(); MI != ME; ++MI) {
512      ObjCMethodDecl *MD = *MI;
513      if (!MD->hasBody())
514        continue;
515
516      if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {
517        ObjCMethodDecl *FinalizeM = MD;
518        Transaction Trans(TA);
519        TA.insert(FinalizeM->getSourceRange().getBegin(),
520                  "#if !__has_feature(objc_arc)\n");
521        CharSourceRange::getTokenRange(FinalizeM->getSourceRange());
522        const SourceManager &SM = pass.Ctx.getSourceManager();
523        const LangOptions &LangOpts = pass.Ctx.getLangOptions();
524        bool Invalid;
525        std::string str = "\n#endif\n";
526        str += Lexer::getSourceText(
527                  CharSourceRange::getTokenRange(FinalizeM->getSourceRange()),
528                                    SM, LangOpts, &Invalid);
529        TA.insertAfterToken(FinalizeM->getSourceRange().getEnd(), str);
530
531        break;
532      }
533    }
534  }
535}
536
537//===----------------------------------------------------------------------===//
538// getAllTransformations.
539//===----------------------------------------------------------------------===//
540
541static void traverseAST(MigrationPass &pass) {
542  MigrationContext MigrateCtx(pass);
543
544  if (pass.isGCMigration()) {
545    MigrateCtx.addTraverser(new GCCollectableCallsTraverser);
546    MigrateCtx.addTraverser(new GCAttrsTraverser());
547  }
548  MigrateCtx.addTraverser(new PropertyRewriteTraverser());
549  MigrateCtx.addTraverser(new BlockObjCVariableTraverser());
550
551  MigrateCtx.traverse(pass.Ctx.getTranslationUnitDecl());
552}
553
554static void independentTransforms(MigrationPass &pass) {
555  rewriteAutoreleasePool(pass);
556  removeRetainReleaseDeallocFinalize(pass);
557  rewriteUnusedInitDelegate(pass);
558  removeZeroOutPropsInDeallocFinalize(pass);
559  makeAssignARCSafe(pass);
560  rewriteUnbridgedCasts(pass);
561  checkAPIUses(pass);
562  traverseAST(pass);
563}
564
565std::vector<TransformFn> arcmt::getAllTransformations(
566                                               LangOptions::GCMode OrigGCMode,
567                                               bool NoFinalizeRemoval) {
568  std::vector<TransformFn> transforms;
569
570  if (OrigGCMode ==  LangOptions::GCOnly && NoFinalizeRemoval)
571    transforms.push_back(GCRewriteFinalize);
572  transforms.push_back(independentTransforms);
573  // This depends on previous transformations removing various expressions.
574  transforms.push_back(removeEmptyStatementsAndDeallocFinalize);
575
576  return transforms;
577}
578