Transforms.cpp revision 263d66718365aadc621c3feb9badedf46d787e0e
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->isForwardDecl())
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()))
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
143bool 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
172bool 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
184StringRef trans::getNilString(ASTContext &Ctx) {
185  if (Ctx.Idents.get("nil").hasMacroDefinition())
186    return "nil";
187  else
188    return "0";
189}
190
191namespace {
192
193class ReferenceClear : public RecursiveASTVisitor<ReferenceClear> {
194  ExprSet &Refs;
195public:
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
201class ReferenceCollector : public RecursiveASTVisitor<ReferenceCollector> {
202  ValueDecl *Dcl;
203  ExprSet &Refs;
204
205public:
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
222class RemovablesCollector : public RecursiveASTVisitor<RemovablesCollector> {
223  ExprSet &Removables;
224
225public:
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
272private:
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
286void trans::clearRefsIn(Stmt *S, ExprSet &refs) {
287  ReferenceClear(refs).TraverseStmt(S);
288}
289
290void trans::collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs) {
291  ReferenceCollector(D, refs).TraverseStmt(S);
292}
293
294void trans::collectRemovables(Stmt *S, ExprSet &exprs) {
295  RemovablesCollector(exprs).TraverseStmt(S);
296}
297
298//===----------------------------------------------------------------------===//
299// MigrationContext
300//===----------------------------------------------------------------------===//
301
302namespace {
303
304class ASTTransform : public RecursiveASTVisitor<ASTTransform> {
305  MigrationContext &MigrateCtx;
306  typedef RecursiveASTVisitor<ASTTransform> base;
307
308public:
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
339MigrationContext::~MigrationContext() {
340  for (traverser_iterator
341         I = traversers_begin(), E = traversers_end(); I != E; ++I)
342    delete *I;
343}
344
345bool 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
365bool 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
449void MigrationContext::traverse(TranslationUnitDecl *TU) {
450  for (traverser_iterator
451         I = traversers_begin(), E = traversers_end(); I != E; ++I)
452    (*I)->traverseTU(*this);
453
454  ASTTransform(*this).TraverseDecl(TU);
455}
456
457//===----------------------------------------------------------------------===//
458// getAllTransformations.
459//===----------------------------------------------------------------------===//
460
461static void traverseAST(MigrationPass &pass) {
462  MigrationContext MigrateCtx(pass);
463
464  if (pass.isGCMigration()) {
465    MigrateCtx.addTraverser(new GCCollectableCallsTraverser);
466    MigrateCtx.addTraverser(new GCAttrsTraverser());
467  }
468  MigrateCtx.addTraverser(new PropertyRewriteTraverser());
469
470  MigrateCtx.traverse(pass.Ctx.getTranslationUnitDecl());
471}
472
473static void independentTransforms(MigrationPass &pass) {
474  rewriteAutoreleasePool(pass);
475  removeRetainReleaseDeallocFinalize(pass);
476  rewriteUnusedInitDelegate(pass);
477  removeZeroOutPropsInDeallocFinalize(pass);
478  makeAssignARCSafe(pass);
479  rewriteUnbridgedCasts(pass);
480  rewriteBlockObjCVariable(pass);
481  checkAPIUses(pass);
482  traverseAST(pass);
483}
484
485std::vector<TransformFn> arcmt::getAllTransformations(
486                                               LangOptions::GCMode OrigGCMode) {
487  std::vector<TransformFn> transforms;
488
489  transforms.push_back(independentTransforms);
490  // This depends on previous transformations removing various expressions.
491  transforms.push_back(removeEmptyStatementsAndDeallocFinalize);
492
493  return transforms;
494}
495