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