ObjCMT.cpp revision 8d3794e06a63578093bd71c3c2520bd01e6197a3
1//===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===// 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 "clang/ARCMigrate/ARCMTActions.h" 12#include "clang/AST/ASTConsumer.h" 13#include "clang/AST/ASTContext.h" 14#include "clang/AST/NSAPI.h" 15#include "clang/AST/ParentMap.h" 16#include "clang/AST/RecursiveASTVisitor.h" 17#include "clang/Basic/FileManager.h" 18#include "clang/Edit/Commit.h" 19#include "clang/Edit/EditedSource.h" 20#include "clang/Edit/EditsReceiver.h" 21#include "clang/Edit/Rewriters.h" 22#include "clang/Frontend/CompilerInstance.h" 23#include "clang/Frontend/MultiplexConsumer.h" 24#include "clang/Lex/PPConditionalDirectiveRecord.h" 25#include "clang/Lex/Preprocessor.h" 26#include "clang/Rewrite/Core/Rewriter.h" 27#include "llvm/ADT/SmallString.h" 28 29using namespace clang; 30using namespace arcmt; 31 32namespace { 33 34class ObjCMigrateASTConsumer : public ASTConsumer { 35 void migrateDecl(Decl *D); 36 void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCInterfaceDecl *D); 37 void migrateProtocolConformance(ASTContext &Ctx, 38 const ObjCImplementationDecl *ImpDecl); 39 void migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl, 40 const TypedefDecl *TypedefDcl); 41 void migrateInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl); 42 void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl, 43 ObjCMethodDecl *OM); 44 void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl, 45 ObjCMethodDecl *OM, 46 ObjCInstanceTypeFamily OIT_Family = OIT_None); 47 48public: 49 std::string MigrateDir; 50 bool MigrateLiterals; 51 bool MigrateSubscripting; 52 bool MigrateProperty; 53 OwningPtr<NSAPI> NSAPIObj; 54 OwningPtr<edit::EditedSource> Editor; 55 FileRemapper &Remapper; 56 FileManager &FileMgr; 57 const PPConditionalDirectiveRecord *PPRec; 58 Preprocessor &PP; 59 bool IsOutputFile; 60 llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls; 61 62 ObjCMigrateASTConsumer(StringRef migrateDir, 63 bool migrateLiterals, 64 bool migrateSubscripting, 65 bool migrateProperty, 66 FileRemapper &remapper, 67 FileManager &fileMgr, 68 const PPConditionalDirectiveRecord *PPRec, 69 Preprocessor &PP, 70 bool isOutputFile = false) 71 : MigrateDir(migrateDir), 72 MigrateLiterals(migrateLiterals), 73 MigrateSubscripting(migrateSubscripting), 74 MigrateProperty(migrateProperty), 75 Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP), 76 IsOutputFile(isOutputFile) { } 77 78protected: 79 virtual void Initialize(ASTContext &Context) { 80 NSAPIObj.reset(new NSAPI(Context)); 81 Editor.reset(new edit::EditedSource(Context.getSourceManager(), 82 Context.getLangOpts(), 83 PPRec)); 84 } 85 86 virtual bool HandleTopLevelDecl(DeclGroupRef DG) { 87 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) 88 migrateDecl(*I); 89 return true; 90 } 91 virtual void HandleInterestingDecl(DeclGroupRef DG) { 92 // Ignore decls from the PCH. 93 } 94 virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) { 95 ObjCMigrateASTConsumer::HandleTopLevelDecl(DG); 96 } 97 98 virtual void HandleTranslationUnit(ASTContext &Ctx); 99}; 100 101} 102 103ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction, 104 StringRef migrateDir, 105 bool migrateLiterals, 106 bool migrateSubscripting, 107 bool migrateProperty) 108 : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), 109 MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting), 110 MigrateProperty(migrateProperty), 111 CompInst(0) { 112 if (MigrateDir.empty()) 113 MigrateDir = "."; // user current directory if none is given. 114} 115 116ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, 117 StringRef InFile) { 118 PPConditionalDirectiveRecord * 119 PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager()); 120 CompInst->getPreprocessor().addPPCallbacks(PPRec); 121 ASTConsumer * 122 WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); 123 ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir, 124 MigrateLiterals, 125 MigrateSubscripting, 126 MigrateProperty, 127 Remapper, 128 CompInst->getFileManager(), 129 PPRec, 130 CompInst->getPreprocessor()); 131 ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer }; 132 return new MultiplexConsumer(Consumers); 133} 134 135bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) { 136 Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(), 137 /*ignoreIfFilesChanges=*/true); 138 CompInst = &CI; 139 CI.getDiagnostics().setIgnoreAllWarnings(true); 140 return true; 141} 142 143namespace { 144class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> { 145 ObjCMigrateASTConsumer &Consumer; 146 ParentMap &PMap; 147 148public: 149 ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap) 150 : Consumer(consumer), PMap(PMap) { } 151 152 bool shouldVisitTemplateInstantiations() const { return false; } 153 bool shouldWalkTypesOfTypeLocs() const { return false; } 154 155 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 156 if (Consumer.MigrateLiterals) { 157 edit::Commit commit(*Consumer.Editor); 158 edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap); 159 Consumer.Editor->commit(commit); 160 } 161 162 if (Consumer.MigrateSubscripting) { 163 edit::Commit commit(*Consumer.Editor); 164 edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit); 165 Consumer.Editor->commit(commit); 166 } 167 168 return true; 169 } 170 171 bool TraverseObjCMessageExpr(ObjCMessageExpr *E) { 172 // Do depth first; we want to rewrite the subexpressions first so that if 173 // we have to move expressions we will move them already rewritten. 174 for (Stmt::child_range range = E->children(); range; ++range) 175 if (!TraverseStmt(*range)) 176 return false; 177 178 return WalkUpFromObjCMessageExpr(E); 179 } 180}; 181 182class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> { 183 ObjCMigrateASTConsumer &Consumer; 184 OwningPtr<ParentMap> PMap; 185 186public: 187 BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { } 188 189 bool shouldVisitTemplateInstantiations() const { return false; } 190 bool shouldWalkTypesOfTypeLocs() const { return false; } 191 192 bool TraverseStmt(Stmt *S) { 193 PMap.reset(new ParentMap(S)); 194 ObjCMigrator(Consumer, *PMap).TraverseStmt(S); 195 return true; 196 } 197}; 198} 199 200void ObjCMigrateASTConsumer::migrateDecl(Decl *D) { 201 if (!D) 202 return; 203 if (isa<ObjCMethodDecl>(D)) 204 return; // Wait for the ObjC container declaration. 205 206 BodyMigrator(*this).TraverseDecl(D); 207} 208 209static bool rewriteToObjCProperty(const ObjCMethodDecl *Getter, 210 const ObjCMethodDecl *Setter, 211 const NSAPI &NS, edit::Commit &commit) { 212 ASTContext &Context = NS.getASTContext(); 213 std::string PropertyString = "@property"; 214 const ParmVarDecl *argDecl = *Setter->param_begin(); 215 QualType ArgType = Context.getCanonicalType(argDecl->getType()); 216 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime(); 217 218 if (ArgType->isObjCRetainableType() && 219 propertyLifetime == Qualifiers::OCL_Strong) { 220 if (const ObjCObjectPointerType *ObjPtrTy = 221 ArgType->getAs<ObjCObjectPointerType>()) { 222 ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); 223 if (IDecl && 224 IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying"))) 225 PropertyString += "(copy)"; 226 } 227 } 228 else if (propertyLifetime == Qualifiers::OCL_Weak) 229 // TODO. More precise determination of 'weak' attribute requires 230 // looking into setter's implementation for backing weak ivar. 231 PropertyString += "(weak)"; 232 else 233 PropertyString += "(unsafe_unretained)"; 234 235 // strip off any ARC lifetime qualifier. 236 QualType CanResultTy = Context.getCanonicalType(Getter->getResultType()); 237 if (CanResultTy.getQualifiers().hasObjCLifetime()) { 238 Qualifiers Qs = CanResultTy.getQualifiers(); 239 Qs.removeObjCLifetime(); 240 CanResultTy = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs); 241 } 242 PropertyString += " "; 243 PropertyString += CanResultTy.getAsString(Context.getPrintingPolicy()); 244 PropertyString += " "; 245 PropertyString += Getter->getNameAsString(); 246 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(), 247 Getter->getDeclaratorEndLoc()), 248 PropertyString); 249 SourceLocation EndLoc = Setter->getDeclaratorEndLoc(); 250 // Get location past ';' 251 EndLoc = EndLoc.getLocWithOffset(1); 252 commit.remove(CharSourceRange::getCharRange(Setter->getLocStart(), EndLoc)); 253 return true; 254} 255 256void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx, 257 ObjCInterfaceDecl *D) { 258 for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end(); 259 M != MEnd; ++M) { 260 ObjCMethodDecl *Method = (*M); 261 if (Method->isPropertyAccessor() || Method->param_size() != 0) 262 continue; 263 // Is this method candidate to be a getter? 264 QualType GRT = Method->getResultType(); 265 if (GRT->isVoidType()) 266 continue; 267 // FIXME. Don't know what todo with attributes, skip for now. 268 if (Method->hasAttrs()) 269 continue; 270 271 Selector GetterSelector = Method->getSelector(); 272 IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0); 273 Selector SetterSelector = 274 SelectorTable::constructSetterSelector(PP.getIdentifierTable(), 275 PP.getSelectorTable(), 276 getterName); 277 if (ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true)) { 278 // Is this a valid setter, matching the target getter? 279 QualType SRT = SetterMethod->getResultType(); 280 if (!SRT->isVoidType()) 281 continue; 282 const ParmVarDecl *argDecl = *SetterMethod->param_begin(); 283 QualType ArgType = argDecl->getType(); 284 if (!Ctx.hasSameUnqualifiedType(ArgType, GRT) || 285 SetterMethod->hasAttrs()) 286 continue; 287 edit::Commit commit(*Editor); 288 rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit); 289 Editor->commit(commit); 290 } 291 } 292} 293 294static bool 295ClassImplementsAllMethodsAndProperties(ASTContext &Ctx, 296 const ObjCImplementationDecl *ImpDecl, 297 const ObjCInterfaceDecl *IDecl, 298 ObjCProtocolDecl *Protocol) { 299 // In auto-synthesis, protocol properties are not synthesized. So, 300 // a conforming protocol must have its required properties declared 301 // in class interface. 302 bool HasAtleastOneRequiredProperty = false; 303 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) 304 for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), 305 E = PDecl->prop_end(); P != E; ++P) { 306 ObjCPropertyDecl *Property = *P; 307 if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional) 308 continue; 309 HasAtleastOneRequiredProperty = true; 310 DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName()); 311 if (R.size() == 0) { 312 // Relax the rule and look into class's implementation for a synthesize 313 // or dynamic declaration. Class is implementing a property coming from 314 // another protocol. This still makes the target protocol as conforming. 315 if (!ImpDecl->FindPropertyImplDecl( 316 Property->getDeclName().getAsIdentifierInfo())) 317 return false; 318 } 319 else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) { 320 if ((ClassProperty->getPropertyAttributes() 321 != Property->getPropertyAttributes()) || 322 !Ctx.hasSameType(ClassProperty->getType(), Property->getType())) 323 return false; 324 } 325 else 326 return false; 327 } 328 329 // At this point, all required properties in this protocol conform to those 330 // declared in the class. 331 // Check that class implements the required methods of the protocol too. 332 bool HasAtleastOneRequiredMethod = false; 333 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) { 334 if (PDecl->meth_begin() == PDecl->meth_end()) 335 return HasAtleastOneRequiredProperty; 336 for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(), 337 MEnd = PDecl->meth_end(); M != MEnd; ++M) { 338 ObjCMethodDecl *MD = (*M); 339 if (MD->isImplicit()) 340 continue; 341 if (MD->getImplementationControl() == ObjCMethodDecl::Optional) 342 continue; 343 DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName()); 344 if (R.size() == 0) 345 return false; 346 bool match = false; 347 HasAtleastOneRequiredMethod = true; 348 for (unsigned I = 0, N = R.size(); I != N; ++I) 349 if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0])) 350 if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) { 351 match = true; 352 break; 353 } 354 if (!match) 355 return false; 356 } 357 } 358 if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod) 359 return true; 360 return false; 361} 362 363static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl, 364 llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols, 365 const NSAPI &NS, edit::Commit &commit) { 366 const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols(); 367 std::string ClassString; 368 SourceLocation EndLoc = 369 IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation(); 370 371 if (Protocols.empty()) { 372 ClassString = '<'; 373 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 374 ClassString += ConformingProtocols[i]->getNameAsString(); 375 if (i != (e-1)) 376 ClassString += ", "; 377 } 378 ClassString += "> "; 379 } 380 else { 381 ClassString = ", "; 382 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 383 ClassString += ConformingProtocols[i]->getNameAsString(); 384 if (i != (e-1)) 385 ClassString += ", "; 386 } 387 ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1; 388 EndLoc = *PL; 389 } 390 391 commit.insertAfterToken(EndLoc, ClassString); 392 return true; 393} 394 395static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl, 396 const TypedefDecl *TypedefDcl, 397 const NSAPI &NS, edit::Commit &commit, 398 bool IsNSIntegerType) { 399 std::string ClassString = 400 IsNSIntegerType ? "typedef NS_ENUM(NSInteger, " : "typedef NS_OPTIONS(NSUInteger, "; 401 ClassString += TypedefDcl->getIdentifier()->getName(); 402 ClassString += ')'; 403 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); 404 commit.replace(R, ClassString); 405 SourceLocation EndOfTypedefLoc = TypedefDcl->getLocEnd(); 406 EndOfTypedefLoc = trans::findLocationAfterSemi(EndOfTypedefLoc, NS.getASTContext()); 407 if (!EndOfTypedefLoc.isInvalid()) { 408 commit.remove(SourceRange(TypedefDcl->getLocStart(), EndOfTypedefLoc)); 409 return true; 410 } 411 return false; 412} 413 414static bool rewriteToNSMacroDecl(const EnumDecl *EnumDcl, 415 const TypedefDecl *TypedefDcl, 416 const NSAPI &NS, edit::Commit &commit, 417 bool IsNSIntegerType) { 418 std::string ClassString = 419 IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, "; 420 ClassString += TypedefDcl->getIdentifier()->getName(); 421 ClassString += ')'; 422 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); 423 commit.replace(R, ClassString); 424 SourceLocation TypedefLoc = TypedefDcl->getLocEnd(); 425 commit.remove(SourceRange(TypedefLoc, TypedefLoc)); 426 return true; 427} 428 429static bool UseNSOptionsMacro(ASTContext &Ctx, 430 const EnumDecl *EnumDcl) { 431 bool PowerOfTwo = true; 432 for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(), 433 EE = EnumDcl->enumerator_end(); EI != EE; ++EI) { 434 EnumConstantDecl *Enumerator = (*EI); 435 const Expr *InitExpr = Enumerator->getInitExpr(); 436 if (!InitExpr) { 437 PowerOfTwo = false; 438 continue; 439 } 440 InitExpr = InitExpr->IgnoreImpCasts(); 441 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr)) 442 if (BO->isShiftOp() || BO->isBitwiseOp()) 443 return true; 444 445 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue(); 446 if (PowerOfTwo && EnumVal && !llvm::isPowerOf2_64(EnumVal)) 447 PowerOfTwo = false; 448 } 449 return PowerOfTwo; 450} 451 452void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx, 453 const ObjCImplementationDecl *ImpDecl) { 454 const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface(); 455 if (!IDecl || ObjCProtocolDecls.empty()) 456 return; 457 // Find all implicit conforming protocols for this class 458 // and make them explicit. 459 llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols; 460 Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols); 461 llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols; 462 463 for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I = 464 ObjCProtocolDecls.begin(), 465 E = ObjCProtocolDecls.end(); I != E; ++I) 466 if (!ExplicitProtocols.count(*I)) 467 PotentialImplicitProtocols.push_back(*I); 468 469 if (PotentialImplicitProtocols.empty()) 470 return; 471 472 // go through list of non-optional methods and properties in each protocol 473 // in the PotentialImplicitProtocols list. If class implements every one of the 474 // methods and properties, then this class conforms to this protocol. 475 llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols; 476 for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++) 477 if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl, 478 PotentialImplicitProtocols[i])) 479 ConformingProtocols.push_back(PotentialImplicitProtocols[i]); 480 481 if (ConformingProtocols.empty()) 482 return; 483 484 // Further reduce number of conforming protocols. If protocol P1 is in the list 485 // protocol P2 (P2<P1>), No need to include P1. 486 llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols; 487 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 488 bool DropIt = false; 489 ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i]; 490 for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) { 491 ObjCProtocolDecl *PDecl = ConformingProtocols[i1]; 492 if (PDecl == TargetPDecl) 493 continue; 494 if (PDecl->lookupProtocolNamed( 495 TargetPDecl->getDeclName().getAsIdentifierInfo())) { 496 DropIt = true; 497 break; 498 } 499 } 500 if (!DropIt) 501 MinimalConformingProtocols.push_back(TargetPDecl); 502 } 503 edit::Commit commit(*Editor); 504 rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols, 505 *NSAPIObj, commit); 506 Editor->commit(commit); 507} 508 509void ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx, 510 const EnumDecl *EnumDcl, 511 const TypedefDecl *TypedefDcl) { 512 if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() || 513 !TypedefDcl->getIdentifier()) 514 return; 515 516 QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); 517 bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt); 518 bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt); 519 520 if (!IsNSIntegerType && !IsNSUIntegerType) { 521 // Also check for typedef enum {...} TD; 522 if (const EnumType *EnumTy = qt->getAs<EnumType>()) { 523 if (EnumTy->getDecl() == EnumDcl) { 524 bool NSOptions = UseNSOptionsMacro(Ctx, EnumDcl); 525 if (NSOptions) { 526 if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition()) 527 return; 528 } 529 else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition()) 530 return; 531 edit::Commit commit(*Editor); 532 rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions); 533 Editor->commit(commit); 534 } 535 } 536 return; 537 } 538 if (IsNSIntegerType && UseNSOptionsMacro(Ctx, EnumDcl)) { 539 // We may still use NS_OPTIONS based on what we find in the enumertor list. 540 IsNSIntegerType = false; 541 IsNSUIntegerType = true; 542 } 543 544 // NS_ENUM must be available. 545 if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition()) 546 return; 547 // NS_OPTIONS must be available. 548 if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition()) 549 return; 550 edit::Commit commit(*Editor); 551 rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, IsNSIntegerType); 552 Editor->commit(commit); 553} 554 555static void ReplaceWithInstancetype(const ObjCMigrateASTConsumer &ASTC, 556 ObjCMethodDecl *OM) { 557 SourceRange R; 558 std::string ClassString; 559 if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) { 560 TypeLoc TL = TSInfo->getTypeLoc(); 561 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); 562 ClassString = "instancetype"; 563 } 564 else { 565 R = SourceRange(OM->getLocStart(), OM->getLocStart()); 566 ClassString = OM->isInstanceMethod() ? '-' : '+'; 567 ClassString += " (instancetype)"; 568 } 569 edit::Commit commit(*ASTC.Editor); 570 commit.replace(R, ClassString); 571 ASTC.Editor->commit(commit); 572} 573 574void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx, 575 ObjCContainerDecl *CDecl, 576 ObjCMethodDecl *OM) { 577 ObjCInstanceTypeFamily OIT_Family = 578 Selector::getInstTypeMethodFamily(OM->getSelector()); 579 580 std::string ClassName; 581 switch (OIT_Family) { 582 case OIT_None: 583 migrateFactoryMethod(Ctx, CDecl, OM); 584 return; 585 case OIT_Array: 586 ClassName = "NSArray"; 587 break; 588 case OIT_Dictionary: 589 ClassName = "NSDictionary"; 590 break; 591 case OIT_MemManage: 592 ClassName = "NSObject"; 593 break; 594 case OIT_Singleton: 595 migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton); 596 return; 597 } 598 if (!OM->getResultType()->isObjCIdType()) 599 return; 600 601 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 602 if (!IDecl) { 603 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 604 IDecl = CatDecl->getClassInterface(); 605 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 606 IDecl = ImpDecl->getClassInterface(); 607 } 608 if (!IDecl || 609 !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) { 610 migrateFactoryMethod(Ctx, CDecl, OM); 611 return; 612 } 613 ReplaceWithInstancetype(*this, OM); 614} 615 616void ObjCMigrateASTConsumer::migrateInstanceType(ASTContext &Ctx, 617 ObjCContainerDecl *CDecl) { 618 // migrate methods which can have instancetype as their result type. 619 for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(), 620 MEnd = CDecl->meth_end(); 621 M != MEnd; ++M) { 622 ObjCMethodDecl *Method = (*M); 623 migrateMethodInstanceType(Ctx, CDecl, Method); 624 } 625} 626 627void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx, 628 ObjCContainerDecl *CDecl, 629 ObjCMethodDecl *OM, 630 ObjCInstanceTypeFamily OIT_Family) { 631 if (OM->isInstanceMethod() || !OM->getResultType()->isObjCIdType()) 632 return; 633 634 // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class 635 // NSYYYNamE with matching names be at least 3 characters long. 636 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 637 if (!IDecl) { 638 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 639 IDecl = CatDecl->getClassInterface(); 640 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 641 IDecl = ImpDecl->getClassInterface(); 642 } 643 if (!IDecl) 644 return; 645 646 std::string StringClassName = IDecl->getName(); 647 StringRef LoweredClassName(StringClassName); 648 std::string StringLoweredClassName = LoweredClassName.lower(); 649 LoweredClassName = StringLoweredClassName; 650 651 IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0); 652 std::string MethodName = MethodIdName->getName(); 653 if (OIT_Family == OIT_Singleton) { 654 StringRef STRefMethodName(MethodName); 655 size_t len = 0; 656 if (STRefMethodName.startswith("standard")) 657 len = strlen("standard"); 658 else if (STRefMethodName.startswith("shared")) 659 len = strlen("shared"); 660 else if (STRefMethodName.startswith("default")) 661 len = strlen("default"); 662 else 663 return; 664 MethodName = STRefMethodName.substr(len); 665 } 666 std::string MethodNameSubStr = MethodName.substr(0, 3); 667 StringRef MethodNamePrefix(MethodNameSubStr); 668 std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower(); 669 MethodNamePrefix = StringLoweredMethodNamePrefix; 670 size_t Ix = LoweredClassName.rfind(MethodNamePrefix); 671 if (Ix == StringRef::npos) 672 return; 673 std::string ClassNamePostfix = LoweredClassName.substr(Ix); 674 StringRef LoweredMethodName(MethodName); 675 std::string StringLoweredMethodName = LoweredMethodName.lower(); 676 LoweredMethodName = StringLoweredMethodName; 677 if (!LoweredMethodName.startswith(ClassNamePostfix)) 678 return; 679 ReplaceWithInstancetype(*this, OM); 680} 681 682namespace { 683 684class RewritesReceiver : public edit::EditsReceiver { 685 Rewriter &Rewrite; 686 687public: 688 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } 689 690 virtual void insert(SourceLocation loc, StringRef text) { 691 Rewrite.InsertText(loc, text); 692 } 693 virtual void replace(CharSourceRange range, StringRef text) { 694 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); 695 } 696}; 697 698} 699 700void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { 701 702 TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl(); 703 if (MigrateProperty) 704 for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end(); 705 D != DEnd; ++D) { 706 if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D)) 707 migrateObjCInterfaceDecl(Ctx, CDecl); 708 else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) 709 ObjCProtocolDecls.insert(PDecl); 710 else if (const ObjCImplementationDecl *ImpDecl = 711 dyn_cast<ObjCImplementationDecl>(*D)) 712 migrateProtocolConformance(Ctx, ImpDecl); 713 else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) { 714 DeclContext::decl_iterator N = D; 715 ++N; 716 if (N != DEnd) 717 if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N)) 718 migrateNSEnumDecl(Ctx, ED, TD); 719 } 720 // migrate methods which can have instancetype as their result type. 721 if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) 722 migrateInstanceType(Ctx, CDecl); 723 } 724 725 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); 726 RewritesReceiver Rec(rewriter); 727 Editor->applyRewrites(Rec); 728 729 for (Rewriter::buffer_iterator 730 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { 731 FileID FID = I->first; 732 RewriteBuffer &buf = I->second; 733 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); 734 assert(file); 735 SmallString<512> newText; 736 llvm::raw_svector_ostream vecOS(newText); 737 buf.write(vecOS); 738 vecOS.flush(); 739 llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy( 740 StringRef(newText.data(), newText.size()), file->getName()); 741 SmallString<64> filePath(file->getName()); 742 FileMgr.FixupRelativePath(filePath); 743 Remapper.remap(filePath.str(), memBuf); 744 } 745 746 if (IsOutputFile) { 747 Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics()); 748 } else { 749 Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics()); 750 } 751} 752 753bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) { 754 CI.getDiagnostics().setIgnoreAllWarnings(true); 755 return true; 756} 757 758ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, 759 StringRef InFile) { 760 PPConditionalDirectiveRecord * 761 PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager()); 762 CI.getPreprocessor().addPPCallbacks(PPRec); 763 return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile, 764 /*MigrateLiterals=*/true, 765 /*MigrateSubscripting=*/true, 766 /*MigrateProperty*/true, 767 Remapper, 768 CI.getFileManager(), 769 PPRec, 770 CI.getPreprocessor(), 771 /*isOutputFile=*/true); 772} 773