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