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