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 29 using namespace clang; 30 using namespace arcmt; 31 32 namespace { 33 34 class 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 48 public: 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 78 protected: 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 103 ObjCMigrateAction::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 116 ASTConsumer *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 135 bool 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 143 namespace { 144 class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> { 145 ObjCMigrateASTConsumer &Consumer; 146 ParentMap &PMap; 147 148 public: 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 182 class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> { 183 ObjCMigrateASTConsumer &Consumer; 184 OwningPtr<ParentMap> PMap; 185 186 public: 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 200 void 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 209 static 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 215 std::string PropertyNameString = Getter->getNameAsString(); 216 StringRef PropertyName(PropertyNameString); 217 // Short circuit properties that contain the name "delegate" or "dataSource", 218 // or have exact name "target" to have unsafe_unretained attribute. 219 if (PropertyName.equals("target") || 220 (PropertyName.find("delegate") != StringRef::npos) || 221 (PropertyName.find("dataSource") != StringRef::npos)) 222 PropertyString += "(unsafe_unretained)"; 223 else { 224 const ParmVarDecl *argDecl = *Setter->param_begin(); 225 QualType ArgType = Context.getCanonicalType(argDecl->getType()); 226 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime(); 227 bool RetainableObject = ArgType->isObjCRetainableType(); 228 if (RetainableObject && propertyLifetime == Qualifiers::OCL_Strong) { 229 if (const ObjCObjectPointerType *ObjPtrTy = 230 ArgType->getAs<ObjCObjectPointerType>()) { 231 ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); 232 if (IDecl && 233 IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying"))) 234 PropertyString += "(copy)"; 235 else 236 PropertyString += "(retain)"; 237 } 238 } else if (propertyLifetime == Qualifiers::OCL_Weak) 239 // TODO. More precise determination of 'weak' attribute requires 240 // looking into setter's implementation for backing weak ivar. 241 PropertyString += "(weak)"; 242 else if (RetainableObject) 243 PropertyString += "(retain)"; 244 } 245 246 // strip off any ARC lifetime qualifier. 247 QualType CanResultTy = Context.getCanonicalType(Getter->getResultType()); 248 if (CanResultTy.getQualifiers().hasObjCLifetime()) { 249 Qualifiers Qs = CanResultTy.getQualifiers(); 250 Qs.removeObjCLifetime(); 251 CanResultTy = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs); 252 } 253 PropertyString += " "; 254 PropertyString += CanResultTy.getAsString(Context.getPrintingPolicy()); 255 PropertyString += " "; 256 PropertyString += PropertyNameString; 257 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(), 258 Getter->getDeclaratorEndLoc()), 259 PropertyString); 260 SourceLocation EndLoc = Setter->getDeclaratorEndLoc(); 261 // Get location past ';' 262 EndLoc = EndLoc.getLocWithOffset(1); 263 commit.remove(CharSourceRange::getCharRange(Setter->getLocStart(), EndLoc)); 264 return true; 265 } 266 267 void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx, 268 ObjCInterfaceDecl *D) { 269 for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end(); 270 M != MEnd; ++M) { 271 ObjCMethodDecl *Method = (*M); 272 if (Method->isPropertyAccessor() || Method->param_size() != 0) 273 continue; 274 // Is this method candidate to be a getter? 275 QualType GRT = Method->getResultType(); 276 if (GRT->isVoidType()) 277 continue; 278 // FIXME. Don't know what todo with attributes, skip for now. 279 if (Method->hasAttrs()) 280 continue; 281 282 Selector GetterSelector = Method->getSelector(); 283 IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0); 284 Selector SetterSelector = 285 SelectorTable::constructSetterSelector(PP.getIdentifierTable(), 286 PP.getSelectorTable(), 287 getterName); 288 if (ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true)) { 289 // Is this a valid setter, matching the target getter? 290 QualType SRT = SetterMethod->getResultType(); 291 if (!SRT->isVoidType()) 292 continue; 293 const ParmVarDecl *argDecl = *SetterMethod->param_begin(); 294 QualType ArgType = argDecl->getType(); 295 if (!Ctx.hasSameUnqualifiedType(ArgType, GRT) || 296 SetterMethod->hasAttrs()) 297 continue; 298 edit::Commit commit(*Editor); 299 rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit); 300 Editor->commit(commit); 301 } 302 } 303 } 304 305 static bool 306 ClassImplementsAllMethodsAndProperties(ASTContext &Ctx, 307 const ObjCImplementationDecl *ImpDecl, 308 const ObjCInterfaceDecl *IDecl, 309 ObjCProtocolDecl *Protocol) { 310 // In auto-synthesis, protocol properties are not synthesized. So, 311 // a conforming protocol must have its required properties declared 312 // in class interface. 313 bool HasAtleastOneRequiredProperty = false; 314 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) 315 for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), 316 E = PDecl->prop_end(); P != E; ++P) { 317 ObjCPropertyDecl *Property = *P; 318 if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional) 319 continue; 320 HasAtleastOneRequiredProperty = true; 321 DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName()); 322 if (R.size() == 0) { 323 // Relax the rule and look into class's implementation for a synthesize 324 // or dynamic declaration. Class is implementing a property coming from 325 // another protocol. This still makes the target protocol as conforming. 326 if (!ImpDecl->FindPropertyImplDecl( 327 Property->getDeclName().getAsIdentifierInfo())) 328 return false; 329 } 330 else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) { 331 if ((ClassProperty->getPropertyAttributes() 332 != Property->getPropertyAttributes()) || 333 !Ctx.hasSameType(ClassProperty->getType(), Property->getType())) 334 return false; 335 } 336 else 337 return false; 338 } 339 340 // At this point, all required properties in this protocol conform to those 341 // declared in the class. 342 // Check that class implements the required methods of the protocol too. 343 bool HasAtleastOneRequiredMethod = false; 344 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) { 345 if (PDecl->meth_begin() == PDecl->meth_end()) 346 return HasAtleastOneRequiredProperty; 347 for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(), 348 MEnd = PDecl->meth_end(); M != MEnd; ++M) { 349 ObjCMethodDecl *MD = (*M); 350 if (MD->isImplicit()) 351 continue; 352 if (MD->getImplementationControl() == ObjCMethodDecl::Optional) 353 continue; 354 DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName()); 355 if (R.size() == 0) 356 return false; 357 bool match = false; 358 HasAtleastOneRequiredMethod = true; 359 for (unsigned I = 0, N = R.size(); I != N; ++I) 360 if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0])) 361 if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) { 362 match = true; 363 break; 364 } 365 if (!match) 366 return false; 367 } 368 } 369 if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod) 370 return true; 371 return false; 372 } 373 374 static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl, 375 llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols, 376 const NSAPI &NS, edit::Commit &commit) { 377 const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols(); 378 std::string ClassString; 379 SourceLocation EndLoc = 380 IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation(); 381 382 if (Protocols.empty()) { 383 ClassString = '<'; 384 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 385 ClassString += ConformingProtocols[i]->getNameAsString(); 386 if (i != (e-1)) 387 ClassString += ", "; 388 } 389 ClassString += "> "; 390 } 391 else { 392 ClassString = ", "; 393 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 394 ClassString += ConformingProtocols[i]->getNameAsString(); 395 if (i != (e-1)) 396 ClassString += ", "; 397 } 398 ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1; 399 EndLoc = *PL; 400 } 401 402 commit.insertAfterToken(EndLoc, ClassString); 403 return true; 404 } 405 406 static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl, 407 const TypedefDecl *TypedefDcl, 408 const NSAPI &NS, edit::Commit &commit, 409 bool IsNSIntegerType) { 410 std::string ClassString = 411 IsNSIntegerType ? "typedef NS_ENUM(NSInteger, " : "typedef NS_OPTIONS(NSUInteger, "; 412 ClassString += TypedefDcl->getIdentifier()->getName(); 413 ClassString += ')'; 414 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); 415 commit.replace(R, ClassString); 416 SourceLocation EndOfTypedefLoc = TypedefDcl->getLocEnd(); 417 EndOfTypedefLoc = trans::findLocationAfterSemi(EndOfTypedefLoc, NS.getASTContext()); 418 if (!EndOfTypedefLoc.isInvalid()) { 419 commit.remove(SourceRange(TypedefDcl->getLocStart(), EndOfTypedefLoc)); 420 return true; 421 } 422 return false; 423 } 424 425 static bool rewriteToNSMacroDecl(const EnumDecl *EnumDcl, 426 const TypedefDecl *TypedefDcl, 427 const NSAPI &NS, edit::Commit &commit, 428 bool IsNSIntegerType) { 429 std::string ClassString = 430 IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, "; 431 ClassString += TypedefDcl->getIdentifier()->getName(); 432 ClassString += ')'; 433 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); 434 commit.replace(R, ClassString); 435 SourceLocation TypedefLoc = TypedefDcl->getLocEnd(); 436 commit.remove(SourceRange(TypedefLoc, TypedefLoc)); 437 return true; 438 } 439 440 static bool UseNSOptionsMacro(ASTContext &Ctx, 441 const EnumDecl *EnumDcl) { 442 bool PowerOfTwo = true; 443 for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(), 444 EE = EnumDcl->enumerator_end(); EI != EE; ++EI) { 445 EnumConstantDecl *Enumerator = (*EI); 446 const Expr *InitExpr = Enumerator->getInitExpr(); 447 if (!InitExpr) { 448 PowerOfTwo = false; 449 continue; 450 } 451 InitExpr = InitExpr->IgnoreImpCasts(); 452 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr)) 453 if (BO->isShiftOp() || BO->isBitwiseOp()) 454 return true; 455 456 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue(); 457 if (PowerOfTwo && EnumVal && !llvm::isPowerOf2_64(EnumVal)) 458 PowerOfTwo = false; 459 } 460 return PowerOfTwo; 461 } 462 463 void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx, 464 const ObjCImplementationDecl *ImpDecl) { 465 const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface(); 466 if (!IDecl || ObjCProtocolDecls.empty()) 467 return; 468 // Find all implicit conforming protocols for this class 469 // and make them explicit. 470 llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols; 471 Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols); 472 llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols; 473 474 for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I = 475 ObjCProtocolDecls.begin(), 476 E = ObjCProtocolDecls.end(); I != E; ++I) 477 if (!ExplicitProtocols.count(*I)) 478 PotentialImplicitProtocols.push_back(*I); 479 480 if (PotentialImplicitProtocols.empty()) 481 return; 482 483 // go through list of non-optional methods and properties in each protocol 484 // in the PotentialImplicitProtocols list. If class implements every one of the 485 // methods and properties, then this class conforms to this protocol. 486 llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols; 487 for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++) 488 if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl, 489 PotentialImplicitProtocols[i])) 490 ConformingProtocols.push_back(PotentialImplicitProtocols[i]); 491 492 if (ConformingProtocols.empty()) 493 return; 494 495 // Further reduce number of conforming protocols. If protocol P1 is in the list 496 // protocol P2 (P2<P1>), No need to include P1. 497 llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols; 498 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 499 bool DropIt = false; 500 ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i]; 501 for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) { 502 ObjCProtocolDecl *PDecl = ConformingProtocols[i1]; 503 if (PDecl == TargetPDecl) 504 continue; 505 if (PDecl->lookupProtocolNamed( 506 TargetPDecl->getDeclName().getAsIdentifierInfo())) { 507 DropIt = true; 508 break; 509 } 510 } 511 if (!DropIt) 512 MinimalConformingProtocols.push_back(TargetPDecl); 513 } 514 edit::Commit commit(*Editor); 515 rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols, 516 *NSAPIObj, commit); 517 Editor->commit(commit); 518 } 519 520 void ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx, 521 const EnumDecl *EnumDcl, 522 const TypedefDecl *TypedefDcl) { 523 if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() || 524 !TypedefDcl->getIdentifier()) 525 return; 526 527 QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); 528 bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt); 529 bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt); 530 531 if (!IsNSIntegerType && !IsNSUIntegerType) { 532 // Also check for typedef enum {...} TD; 533 if (const EnumType *EnumTy = qt->getAs<EnumType>()) { 534 if (EnumTy->getDecl() == EnumDcl) { 535 bool NSOptions = UseNSOptionsMacro(Ctx, EnumDcl); 536 if (NSOptions) { 537 if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition()) 538 return; 539 } 540 else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition()) 541 return; 542 edit::Commit commit(*Editor); 543 rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions); 544 Editor->commit(commit); 545 } 546 } 547 return; 548 } 549 if (IsNSIntegerType && UseNSOptionsMacro(Ctx, EnumDcl)) { 550 // We may still use NS_OPTIONS based on what we find in the enumertor list. 551 IsNSIntegerType = false; 552 IsNSUIntegerType = true; 553 } 554 555 // NS_ENUM must be available. 556 if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition()) 557 return; 558 // NS_OPTIONS must be available. 559 if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition()) 560 return; 561 edit::Commit commit(*Editor); 562 rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, IsNSIntegerType); 563 Editor->commit(commit); 564 } 565 566 static void ReplaceWithInstancetype(const ObjCMigrateASTConsumer &ASTC, 567 ObjCMethodDecl *OM) { 568 SourceRange R; 569 std::string ClassString; 570 if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) { 571 TypeLoc TL = TSInfo->getTypeLoc(); 572 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); 573 ClassString = "instancetype"; 574 } 575 else { 576 R = SourceRange(OM->getLocStart(), OM->getLocStart()); 577 ClassString = OM->isInstanceMethod() ? '-' : '+'; 578 ClassString += " (instancetype)"; 579 } 580 edit::Commit commit(*ASTC.Editor); 581 commit.replace(R, ClassString); 582 ASTC.Editor->commit(commit); 583 } 584 585 void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx, 586 ObjCContainerDecl *CDecl, 587 ObjCMethodDecl *OM) { 588 ObjCInstanceTypeFamily OIT_Family = 589 Selector::getInstTypeMethodFamily(OM->getSelector()); 590 591 std::string ClassName; 592 switch (OIT_Family) { 593 case OIT_None: 594 migrateFactoryMethod(Ctx, CDecl, OM); 595 return; 596 case OIT_Array: 597 ClassName = "NSArray"; 598 break; 599 case OIT_Dictionary: 600 ClassName = "NSDictionary"; 601 break; 602 case OIT_MemManage: 603 ClassName = "NSObject"; 604 break; 605 case OIT_Singleton: 606 migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton); 607 return; 608 } 609 if (!OM->getResultType()->isObjCIdType()) 610 return; 611 612 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 613 if (!IDecl) { 614 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 615 IDecl = CatDecl->getClassInterface(); 616 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 617 IDecl = ImpDecl->getClassInterface(); 618 } 619 if (!IDecl || 620 !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) { 621 migrateFactoryMethod(Ctx, CDecl, OM); 622 return; 623 } 624 ReplaceWithInstancetype(*this, OM); 625 } 626 627 void ObjCMigrateASTConsumer::migrateInstanceType(ASTContext &Ctx, 628 ObjCContainerDecl *CDecl) { 629 // migrate methods which can have instancetype as their result type. 630 for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(), 631 MEnd = CDecl->meth_end(); 632 M != MEnd; ++M) { 633 ObjCMethodDecl *Method = (*M); 634 migrateMethodInstanceType(Ctx, CDecl, Method); 635 } 636 } 637 638 void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx, 639 ObjCContainerDecl *CDecl, 640 ObjCMethodDecl *OM, 641 ObjCInstanceTypeFamily OIT_Family) { 642 if (OM->isInstanceMethod() || 643 OM->getResultType() == Ctx.getObjCInstanceType() || 644 !OM->getResultType()->isObjCIdType()) 645 return; 646 647 // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class 648 // NSYYYNamE with matching names be at least 3 characters long. 649 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 650 if (!IDecl) { 651 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 652 IDecl = CatDecl->getClassInterface(); 653 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 654 IDecl = ImpDecl->getClassInterface(); 655 } 656 if (!IDecl) 657 return; 658 659 std::string StringClassName = IDecl->getName(); 660 StringRef LoweredClassName(StringClassName); 661 std::string StringLoweredClassName = LoweredClassName.lower(); 662 LoweredClassName = StringLoweredClassName; 663 664 IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0); 665 std::string MethodName = MethodIdName->getName(); 666 if (OIT_Family == OIT_Singleton) { 667 StringRef STRefMethodName(MethodName); 668 size_t len = 0; 669 if (STRefMethodName.startswith("standard")) 670 len = strlen("standard"); 671 else if (STRefMethodName.startswith("shared")) 672 len = strlen("shared"); 673 else if (STRefMethodName.startswith("default")) 674 len = strlen("default"); 675 else 676 return; 677 MethodName = STRefMethodName.substr(len); 678 } 679 std::string MethodNameSubStr = MethodName.substr(0, 3); 680 StringRef MethodNamePrefix(MethodNameSubStr); 681 std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower(); 682 MethodNamePrefix = StringLoweredMethodNamePrefix; 683 size_t Ix = LoweredClassName.rfind(MethodNamePrefix); 684 if (Ix == StringRef::npos) 685 return; 686 std::string ClassNamePostfix = LoweredClassName.substr(Ix); 687 StringRef LoweredMethodName(MethodName); 688 std::string StringLoweredMethodName = LoweredMethodName.lower(); 689 LoweredMethodName = StringLoweredMethodName; 690 if (!LoweredMethodName.startswith(ClassNamePostfix)) 691 return; 692 ReplaceWithInstancetype(*this, OM); 693 } 694 695 namespace { 696 697 class RewritesReceiver : public edit::EditsReceiver { 698 Rewriter &Rewrite; 699 700 public: 701 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } 702 703 virtual void insert(SourceLocation loc, StringRef text) { 704 Rewrite.InsertText(loc, text); 705 } 706 virtual void replace(CharSourceRange range, StringRef text) { 707 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); 708 } 709 }; 710 711 } 712 713 void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { 714 715 TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl(); 716 if (MigrateProperty) 717 for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end(); 718 D != DEnd; ++D) { 719 if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D)) 720 migrateObjCInterfaceDecl(Ctx, CDecl); 721 else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) 722 ObjCProtocolDecls.insert(PDecl); 723 else if (const ObjCImplementationDecl *ImpDecl = 724 dyn_cast<ObjCImplementationDecl>(*D)) 725 migrateProtocolConformance(Ctx, ImpDecl); 726 else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) { 727 DeclContext::decl_iterator N = D; 728 ++N; 729 if (N != DEnd) 730 if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N)) 731 migrateNSEnumDecl(Ctx, ED, TD); 732 } 733 // migrate methods which can have instancetype as their result type. 734 if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) 735 migrateInstanceType(Ctx, CDecl); 736 } 737 738 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); 739 RewritesReceiver Rec(rewriter); 740 Editor->applyRewrites(Rec); 741 742 for (Rewriter::buffer_iterator 743 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { 744 FileID FID = I->first; 745 RewriteBuffer &buf = I->second; 746 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); 747 assert(file); 748 SmallString<512> newText; 749 llvm::raw_svector_ostream vecOS(newText); 750 buf.write(vecOS); 751 vecOS.flush(); 752 llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy( 753 StringRef(newText.data(), newText.size()), file->getName()); 754 SmallString<64> filePath(file->getName()); 755 FileMgr.FixupRelativePath(filePath); 756 Remapper.remap(filePath.str(), memBuf); 757 } 758 759 if (IsOutputFile) { 760 Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics()); 761 } else { 762 Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics()); 763 } 764 } 765 766 bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) { 767 CI.getDiagnostics().setIgnoreAllWarnings(true); 768 return true; 769 } 770 771 ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, 772 StringRef InFile) { 773 PPConditionalDirectiveRecord * 774 PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager()); 775 CI.getPreprocessor().addPPCallbacks(PPRec); 776 return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile, 777 /*MigrateLiterals=*/true, 778 /*MigrateSubscripting=*/true, 779 /*MigrateProperty*/true, 780 Remapper, 781 CI.getFileManager(), 782 PPRec, 783 CI.getPreprocessor(), 784 /*isOutputFile=*/true); 785 } 786