1234287Sdim//===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===// 2234287Sdim// 3234287Sdim// The LLVM Compiler Infrastructure 4234287Sdim// 5234287Sdim// This file is distributed under the University of Illinois Open Source 6234287Sdim// License. See LICENSE.TXT for details. 7234287Sdim// 8234287Sdim//===----------------------------------------------------------------------===// 9234287Sdim 10263509Sdim#include "Transforms.h" 11234287Sdim#include "clang/ARCMigrate/ARCMTActions.h" 12252723Sdim#include "clang/AST/ASTConsumer.h" 13245431Sdim#include "clang/AST/ASTContext.h" 14252723Sdim#include "clang/AST/NSAPI.h" 15252723Sdim#include "clang/AST/ParentMap.h" 16234287Sdim#include "clang/AST/RecursiveASTVisitor.h" 17252723Sdim#include "clang/Basic/FileManager.h" 18252723Sdim#include "clang/Edit/Commit.h" 19234287Sdim#include "clang/Edit/EditedSource.h" 20234287Sdim#include "clang/Edit/EditsReceiver.h" 21252723Sdim#include "clang/Edit/Rewriters.h" 22252723Sdim#include "clang/Frontend/CompilerInstance.h" 23252723Sdim#include "clang/Frontend/MultiplexConsumer.h" 24252723Sdim#include "clang/Lex/PPConditionalDirectiveRecord.h" 25252723Sdim#include "clang/Lex/Preprocessor.h" 26245431Sdim#include "clang/Rewrite/Core/Rewriter.h" 27263509Sdim#include "clang/Analysis/DomainSpecific/CocoaConventions.h" 28263509Sdim#include "clang/StaticAnalyzer/Checkers/ObjCRetainCount.h" 29263509Sdim#include "clang/AST/Attr.h" 30234287Sdim#include "llvm/ADT/SmallString.h" 31263509Sdim#include "llvm/Support/Path.h" 32234287Sdim 33234287Sdimusing namespace clang; 34234287Sdimusing namespace arcmt; 35263509Sdimusing namespace ento::objc_retain; 36234287Sdim 37234287Sdimnamespace { 38234287Sdim 39234287Sdimclass ObjCMigrateASTConsumer : public ASTConsumer { 40263509Sdim enum CF_BRIDGING_KIND { 41263509Sdim CF_BRIDGING_NONE, 42263509Sdim CF_BRIDGING_ENABLE, 43263509Sdim CF_BRIDGING_MAY_INCLUDE 44263509Sdim }; 45263509Sdim 46234287Sdim void migrateDecl(Decl *D); 47263509Sdim void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCContainerDecl *D); 48263509Sdim void migrateDeprecatedAnnotation(ASTContext &Ctx, ObjCCategoryDecl *CatDecl); 49263509Sdim void migrateProtocolConformance(ASTContext &Ctx, 50263509Sdim const ObjCImplementationDecl *ImpDecl); 51263509Sdim void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl); 52263509Sdim bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl, 53263509Sdim const TypedefDecl *TypedefDcl); 54263509Sdim void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl); 55263509Sdim void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl, 56263509Sdim ObjCMethodDecl *OM); 57263509Sdim bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM); 58263509Sdim void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM); 59263509Sdim void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P); 60263509Sdim void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl, 61263509Sdim ObjCMethodDecl *OM, 62263509Sdim ObjCInstanceTypeFamily OIT_Family = OIT_None); 63263509Sdim 64263509Sdim void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl); 65263509Sdim void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE, 66263509Sdim const FunctionDecl *FuncDecl, bool ResultAnnotated); 67263509Sdim void AddCFAnnotations(ASTContext &Ctx, const CallEffects &CE, 68263509Sdim const ObjCMethodDecl *MethodDecl, bool ResultAnnotated); 69263509Sdim 70263509Sdim void AnnotateImplicitBridging(ASTContext &Ctx); 71263509Sdim 72263509Sdim CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx, 73263509Sdim const FunctionDecl *FuncDecl); 74263509Sdim 75263509Sdim void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl); 76263509Sdim 77263509Sdim void migrateAddMethodAnnotation(ASTContext &Ctx, 78263509Sdim const ObjCMethodDecl *MethodDecl); 79234287Sdimpublic: 80234287Sdim std::string MigrateDir; 81263509Sdim unsigned ASTMigrateActions; 82263509Sdim FileID FileId; 83263509Sdim const TypedefDecl *NSIntegerTypedefed; 84263509Sdim const TypedefDecl *NSUIntegerTypedefed; 85252723Sdim OwningPtr<NSAPI> NSAPIObj; 86252723Sdim OwningPtr<edit::EditedSource> Editor; 87234287Sdim FileRemapper &Remapper; 88234287Sdim FileManager &FileMgr; 89252723Sdim const PPConditionalDirectiveRecord *PPRec; 90263509Sdim Preprocessor &PP; 91234287Sdim bool IsOutputFile; 92263509Sdim llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls; 93263509Sdim llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates; 94263509Sdim llvm::StringMap<char> WhiteListFilenames; 95263509Sdim 96234287Sdim ObjCMigrateASTConsumer(StringRef migrateDir, 97263509Sdim unsigned astMigrateActions, 98234287Sdim FileRemapper &remapper, 99234287Sdim FileManager &fileMgr, 100252723Sdim const PPConditionalDirectiveRecord *PPRec, 101263509Sdim Preprocessor &PP, 102263509Sdim bool isOutputFile, 103263509Sdim ArrayRef<std::string> WhiteList) 104234287Sdim : MigrateDir(migrateDir), 105263509Sdim ASTMigrateActions(astMigrateActions), 106263509Sdim NSIntegerTypedefed(0), NSUIntegerTypedefed(0), 107263509Sdim Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP), 108263509Sdim IsOutputFile(isOutputFile) { 109234287Sdim 110263509Sdim for (ArrayRef<std::string>::iterator 111263509Sdim I = WhiteList.begin(), E = WhiteList.end(); I != E; ++I) { 112263509Sdim WhiteListFilenames.GetOrCreateValue(*I); 113263509Sdim } 114263509Sdim } 115263509Sdim 116234287Sdimprotected: 117234287Sdim virtual void Initialize(ASTContext &Context) { 118234287Sdim NSAPIObj.reset(new NSAPI(Context)); 119234287Sdim Editor.reset(new edit::EditedSource(Context.getSourceManager(), 120234287Sdim Context.getLangOpts(), 121263509Sdim PPRec, false)); 122234287Sdim } 123234287Sdim 124234287Sdim virtual bool HandleTopLevelDecl(DeclGroupRef DG) { 125234287Sdim for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) 126234287Sdim migrateDecl(*I); 127234287Sdim return true; 128234287Sdim } 129234287Sdim virtual void HandleInterestingDecl(DeclGroupRef DG) { 130234287Sdim // Ignore decls from the PCH. 131234287Sdim } 132234287Sdim virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) { 133234287Sdim ObjCMigrateASTConsumer::HandleTopLevelDecl(DG); 134234287Sdim } 135234287Sdim 136234287Sdim virtual void HandleTranslationUnit(ASTContext &Ctx); 137263509Sdim 138263509Sdim bool canModifyFile(StringRef Path) { 139263509Sdim if (WhiteListFilenames.empty()) 140263509Sdim return true; 141263509Sdim return WhiteListFilenames.find(llvm::sys::path::filename(Path)) 142263509Sdim != WhiteListFilenames.end(); 143263509Sdim } 144234287Sdim}; 145234287Sdim 146234287Sdim} 147234287Sdim 148234287SdimObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction, 149263509Sdim StringRef migrateDir, 150263509Sdim unsigned migrateAction) 151234287Sdim : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir), 152263509Sdim ObjCMigAction(migrateAction), 153234287Sdim CompInst(0) { 154234287Sdim if (MigrateDir.empty()) 155234287Sdim MigrateDir = "."; // user current directory if none is given. 156234287Sdim} 157234287Sdim 158234287SdimASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, 159234287Sdim StringRef InFile) { 160252723Sdim PPConditionalDirectiveRecord * 161252723Sdim PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager()); 162252723Sdim CompInst->getPreprocessor().addPPCallbacks(PPRec); 163234287Sdim ASTConsumer * 164234287Sdim WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); 165234287Sdim ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir, 166263509Sdim ObjCMigAction, 167234287Sdim Remapper, 168234287Sdim CompInst->getFileManager(), 169263509Sdim PPRec, 170263509Sdim CompInst->getPreprocessor(), 171263509Sdim false, 172263509Sdim ArrayRef<std::string>()); 173234287Sdim ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer }; 174234287Sdim return new MultiplexConsumer(Consumers); 175234287Sdim} 176234287Sdim 177234287Sdimbool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) { 178234287Sdim Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(), 179234287Sdim /*ignoreIfFilesChanges=*/true); 180234287Sdim CompInst = &CI; 181234287Sdim CI.getDiagnostics().setIgnoreAllWarnings(true); 182234287Sdim return true; 183234287Sdim} 184234287Sdim 185234287Sdimnamespace { 186234287Sdimclass ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> { 187234287Sdim ObjCMigrateASTConsumer &Consumer; 188252723Sdim ParentMap &PMap; 189234287Sdim 190234287Sdimpublic: 191252723Sdim ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap) 192252723Sdim : Consumer(consumer), PMap(PMap) { } 193234287Sdim 194234287Sdim bool shouldVisitTemplateInstantiations() const { return false; } 195234287Sdim bool shouldWalkTypesOfTypeLocs() const { return false; } 196234287Sdim 197234287Sdim bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 198263509Sdim if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) { 199234287Sdim edit::Commit commit(*Consumer.Editor); 200252723Sdim edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap); 201234287Sdim Consumer.Editor->commit(commit); 202234287Sdim } 203234287Sdim 204263509Sdim if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) { 205234287Sdim edit::Commit commit(*Consumer.Editor); 206234287Sdim edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit); 207234287Sdim Consumer.Editor->commit(commit); 208234287Sdim } 209234287Sdim 210234287Sdim return true; 211234287Sdim } 212234287Sdim 213234287Sdim bool TraverseObjCMessageExpr(ObjCMessageExpr *E) { 214234287Sdim // Do depth first; we want to rewrite the subexpressions first so that if 215234287Sdim // we have to move expressions we will move them already rewritten. 216234287Sdim for (Stmt::child_range range = E->children(); range; ++range) 217234287Sdim if (!TraverseStmt(*range)) 218234287Sdim return false; 219234287Sdim 220234287Sdim return WalkUpFromObjCMessageExpr(E); 221234287Sdim } 222234287Sdim}; 223252723Sdim 224252723Sdimclass BodyMigrator : public RecursiveASTVisitor<BodyMigrator> { 225252723Sdim ObjCMigrateASTConsumer &Consumer; 226252723Sdim OwningPtr<ParentMap> PMap; 227252723Sdim 228252723Sdimpublic: 229252723Sdim BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { } 230252723Sdim 231252723Sdim bool shouldVisitTemplateInstantiations() const { return false; } 232252723Sdim bool shouldWalkTypesOfTypeLocs() const { return false; } 233252723Sdim 234252723Sdim bool TraverseStmt(Stmt *S) { 235252723Sdim PMap.reset(new ParentMap(S)); 236252723Sdim ObjCMigrator(Consumer, *PMap).TraverseStmt(S); 237252723Sdim return true; 238252723Sdim } 239252723Sdim}; 240234287Sdim} 241234287Sdim 242234287Sdimvoid ObjCMigrateASTConsumer::migrateDecl(Decl *D) { 243234287Sdim if (!D) 244234287Sdim return; 245234287Sdim if (isa<ObjCMethodDecl>(D)) 246234287Sdim return; // Wait for the ObjC container declaration. 247234287Sdim 248252723Sdim BodyMigrator(*this).TraverseDecl(D); 249234287Sdim} 250234287Sdim 251263509Sdimstatic void append_attr(std::string &PropertyString, const char *attr, 252263509Sdim bool &LParenAdded) { 253263509Sdim if (!LParenAdded) { 254263509Sdim PropertyString += "("; 255263509Sdim LParenAdded = true; 256263509Sdim } 257263509Sdim else 258263509Sdim PropertyString += ", "; 259263509Sdim PropertyString += attr; 260263509Sdim} 261263509Sdim 262263509Sdimstatic 263263509Sdimvoid MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString, 264263509Sdim const std::string& TypeString, 265263509Sdim const char *name) { 266263509Sdim const char *argPtr = TypeString.c_str(); 267263509Sdim int paren = 0; 268263509Sdim while (*argPtr) { 269263509Sdim switch (*argPtr) { 270263509Sdim case '(': 271263509Sdim PropertyString += *argPtr; 272263509Sdim paren++; 273263509Sdim break; 274263509Sdim case ')': 275263509Sdim PropertyString += *argPtr; 276263509Sdim paren--; 277263509Sdim break; 278263509Sdim case '^': 279263509Sdim case '*': 280263509Sdim PropertyString += (*argPtr); 281263509Sdim if (paren == 1) { 282263509Sdim PropertyString += name; 283263509Sdim name = ""; 284263509Sdim } 285263509Sdim break; 286263509Sdim default: 287263509Sdim PropertyString += *argPtr; 288263509Sdim break; 289263509Sdim } 290263509Sdim argPtr++; 291263509Sdim } 292263509Sdim} 293263509Sdim 294263509Sdimstatic const char *PropertyMemoryAttribute(ASTContext &Context, QualType ArgType) { 295263509Sdim Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime(); 296263509Sdim bool RetainableObject = ArgType->isObjCRetainableType(); 297263509Sdim if (RetainableObject && propertyLifetime == Qualifiers::OCL_Strong) { 298263509Sdim if (const ObjCObjectPointerType *ObjPtrTy = 299263509Sdim ArgType->getAs<ObjCObjectPointerType>()) { 300263509Sdim ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); 301263509Sdim if (IDecl && 302263509Sdim IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying"))) 303263509Sdim return "copy"; 304263509Sdim else 305263509Sdim return "retain"; 306263509Sdim } 307263509Sdim else if (ArgType->isBlockPointerType()) 308263509Sdim return "copy"; 309263509Sdim } else if (propertyLifetime == Qualifiers::OCL_Weak) 310263509Sdim // TODO. More precise determination of 'weak' attribute requires 311263509Sdim // looking into setter's implementation for backing weak ivar. 312263509Sdim return "weak"; 313263509Sdim else if (RetainableObject) 314263509Sdim return ArgType->isBlockPointerType() ? "copy" : "retain"; 315263509Sdim return 0; 316263509Sdim} 317263509Sdim 318263509Sdimstatic void rewriteToObjCProperty(const ObjCMethodDecl *Getter, 319263509Sdim const ObjCMethodDecl *Setter, 320263509Sdim const NSAPI &NS, edit::Commit &commit, 321263509Sdim unsigned LengthOfPrefix, 322263509Sdim bool Atomic, bool UseNsIosOnlyMacro, 323263509Sdim bool AvailabilityArgsMatch) { 324263509Sdim ASTContext &Context = NS.getASTContext(); 325263509Sdim bool LParenAdded = false; 326263509Sdim std::string PropertyString = "@property "; 327263509Sdim if (UseNsIosOnlyMacro && Context.Idents.get("NS_NONATOMIC_IOSONLY").hasMacroDefinition()) { 328263509Sdim PropertyString += "(NS_NONATOMIC_IOSONLY"; 329263509Sdim LParenAdded = true; 330263509Sdim } else if (!Atomic) { 331263509Sdim PropertyString += "(nonatomic"; 332263509Sdim LParenAdded = true; 333263509Sdim } 334263509Sdim 335263509Sdim std::string PropertyNameString = Getter->getNameAsString(); 336263509Sdim StringRef PropertyName(PropertyNameString); 337263509Sdim if (LengthOfPrefix > 0) { 338263509Sdim if (!LParenAdded) { 339263509Sdim PropertyString += "(getter="; 340263509Sdim LParenAdded = true; 341263509Sdim } 342263509Sdim else 343263509Sdim PropertyString += ", getter="; 344263509Sdim PropertyString += PropertyNameString; 345263509Sdim } 346263509Sdim // Property with no setter may be suggested as a 'readonly' property. 347263509Sdim if (!Setter) { 348263509Sdim append_attr(PropertyString, "readonly", LParenAdded); 349263509Sdim QualType ResType = Context.getCanonicalType(Getter->getResultType()); 350263509Sdim if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ResType)) 351263509Sdim append_attr(PropertyString, MemoryManagementAttr, LParenAdded); 352263509Sdim } 353263509Sdim 354263509Sdim // Short circuit 'delegate' properties that contain the name "delegate" or 355263509Sdim // "dataSource", or have exact name "target" to have 'assign' attribute. 356263509Sdim if (PropertyName.equals("target") || 357263509Sdim (PropertyName.find("delegate") != StringRef::npos) || 358263509Sdim (PropertyName.find("dataSource") != StringRef::npos)) { 359263509Sdim QualType QT = Getter->getResultType(); 360263509Sdim if (!QT->isRealType()) 361263509Sdim append_attr(PropertyString, "assign", LParenAdded); 362263509Sdim } 363263509Sdim else if (Setter) { 364263509Sdim const ParmVarDecl *argDecl = *Setter->param_begin(); 365263509Sdim QualType ArgType = Context.getCanonicalType(argDecl->getType()); 366263509Sdim if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ArgType)) 367263509Sdim append_attr(PropertyString, MemoryManagementAttr, LParenAdded); 368263509Sdim } 369263509Sdim if (LParenAdded) 370263509Sdim PropertyString += ')'; 371263509Sdim QualType RT = Getter->getResultType(); 372263509Sdim if (!isa<TypedefType>(RT)) { 373263509Sdim // strip off any ARC lifetime qualifier. 374263509Sdim QualType CanResultTy = Context.getCanonicalType(RT); 375263509Sdim if (CanResultTy.getQualifiers().hasObjCLifetime()) { 376263509Sdim Qualifiers Qs = CanResultTy.getQualifiers(); 377263509Sdim Qs.removeObjCLifetime(); 378263509Sdim RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs); 379263509Sdim } 380263509Sdim } 381263509Sdim PropertyString += " "; 382263509Sdim PrintingPolicy SubPolicy(Context.getPrintingPolicy()); 383263509Sdim SubPolicy.SuppressStrongLifetime = true; 384263509Sdim SubPolicy.SuppressLifetimeQualifiers = true; 385263509Sdim std::string TypeString = RT.getAsString(SubPolicy); 386263509Sdim if (LengthOfPrefix > 0) { 387263509Sdim // property name must strip off "is" and lower case the first character 388263509Sdim // after that; e.g. isContinuous will become continuous. 389263509Sdim StringRef PropertyNameStringRef(PropertyNameString); 390263509Sdim PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix); 391263509Sdim PropertyNameString = PropertyNameStringRef; 392263509Sdim bool NoLowering = (isUppercase(PropertyNameString[0]) && 393263509Sdim PropertyNameString.size() > 1 && 394263509Sdim isUppercase(PropertyNameString[1])); 395263509Sdim if (!NoLowering) 396263509Sdim PropertyNameString[0] = toLowercase(PropertyNameString[0]); 397263509Sdim } 398263509Sdim if (RT->isBlockPointerType() || RT->isFunctionPointerType()) 399263509Sdim MigrateBlockOrFunctionPointerTypeVariable(PropertyString, 400263509Sdim TypeString, 401263509Sdim PropertyNameString.c_str()); 402263509Sdim else { 403263509Sdim char LastChar = TypeString[TypeString.size()-1]; 404263509Sdim PropertyString += TypeString; 405263509Sdim if (LastChar != '*') 406263509Sdim PropertyString += ' '; 407263509Sdim PropertyString += PropertyNameString; 408263509Sdim } 409263509Sdim SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc(); 410263509Sdim Selector GetterSelector = Getter->getSelector(); 411263509Sdim 412263509Sdim SourceLocation EndGetterSelectorLoc = 413263509Sdim StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size()); 414263509Sdim commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(), 415263509Sdim EndGetterSelectorLoc), 416263509Sdim PropertyString); 417263509Sdim if (Setter && AvailabilityArgsMatch) { 418263509Sdim SourceLocation EndLoc = Setter->getDeclaratorEndLoc(); 419263509Sdim // Get location past ';' 420263509Sdim EndLoc = EndLoc.getLocWithOffset(1); 421263509Sdim SourceLocation BeginOfSetterDclLoc = Setter->getLocStart(); 422263509Sdim // FIXME. This assumes that setter decl; is immediately preceeded by eoln. 423263509Sdim // It is trying to remove the setter method decl. line entirely. 424263509Sdim BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1); 425263509Sdim commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc)); 426263509Sdim } 427263509Sdim} 428263509Sdim 429263509Sdimvoid ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx, 430263509Sdim ObjCContainerDecl *D) { 431263509Sdim if (D->isDeprecated()) 432263509Sdim return; 433263509Sdim 434263509Sdim for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end(); 435263509Sdim M != MEnd; ++M) { 436263509Sdim ObjCMethodDecl *Method = (*M); 437263509Sdim if (Method->isDeprecated()) 438263509Sdim continue; 439263509Sdim bool PropertyInferred = migrateProperty(Ctx, D, Method); 440263509Sdim // If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to 441263509Sdim // the getter method as it ends up on the property itself which we don't want 442263509Sdim // to do unless -objcmt-returns-innerpointer-property option is on. 443263509Sdim if (!PropertyInferred || 444263509Sdim (ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) 445263509Sdim if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 446263509Sdim migrateNsReturnsInnerPointer(Ctx, Method); 447263509Sdim } 448263509Sdim if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) 449263509Sdim return; 450263509Sdim 451263509Sdim for (ObjCContainerDecl::prop_iterator P = D->prop_begin(), 452263509Sdim E = D->prop_end(); P != E; ++P) { 453263509Sdim ObjCPropertyDecl *Prop = *P; 454263509Sdim if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && 455263509Sdim !Prop->isDeprecated()) 456263509Sdim migratePropertyNsReturnsInnerPointer(Ctx, Prop); 457263509Sdim } 458263509Sdim} 459263509Sdim 460263509Sdimvoid ObjCMigrateASTConsumer::migrateDeprecatedAnnotation(ASTContext &Ctx, 461263509Sdim ObjCCategoryDecl *CatDecl) { 462263509Sdim StringRef Name = CatDecl->getName(); 463263509Sdim if (!Name.endswith("Deprecated")) 464263509Sdim return; 465263509Sdim 466263509Sdim if (!Ctx.Idents.get("DEPRECATED").hasMacroDefinition()) 467263509Sdim return; 468263509Sdim 469263509Sdim ObjCContainerDecl *D = cast<ObjCContainerDecl>(CatDecl); 470263509Sdim 471263509Sdim for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end(); 472263509Sdim M != MEnd; ++M) { 473263509Sdim ObjCMethodDecl *Method = (*M); 474263509Sdim if (Method->isDeprecated() || Method->isImplicit()) 475263509Sdim continue; 476263509Sdim // Annotate with DEPRECATED 477263509Sdim edit::Commit commit(*Editor); 478263509Sdim commit.insertBefore(Method->getLocEnd(), " DEPRECATED"); 479263509Sdim Editor->commit(commit); 480263509Sdim } 481263509Sdim for (ObjCContainerDecl::prop_iterator P = D->prop_begin(), 482263509Sdim E = D->prop_end(); P != E; ++P) { 483263509Sdim ObjCPropertyDecl *Prop = *P; 484263509Sdim if (Prop->isDeprecated()) 485263509Sdim continue; 486263509Sdim // Annotate with DEPRECATED 487263509Sdim edit::Commit commit(*Editor); 488263509Sdim commit.insertAfterToken(Prop->getLocEnd(), " DEPRECATED"); 489263509Sdim Editor->commit(commit); 490263509Sdim } 491263509Sdim} 492263509Sdim 493263509Sdimstatic bool 494263509SdimClassImplementsAllMethodsAndProperties(ASTContext &Ctx, 495263509Sdim const ObjCImplementationDecl *ImpDecl, 496263509Sdim const ObjCInterfaceDecl *IDecl, 497263509Sdim ObjCProtocolDecl *Protocol) { 498263509Sdim // In auto-synthesis, protocol properties are not synthesized. So, 499263509Sdim // a conforming protocol must have its required properties declared 500263509Sdim // in class interface. 501263509Sdim bool HasAtleastOneRequiredProperty = false; 502263509Sdim if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) 503263509Sdim for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(), 504263509Sdim E = PDecl->prop_end(); P != E; ++P) { 505263509Sdim ObjCPropertyDecl *Property = *P; 506263509Sdim if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional) 507263509Sdim continue; 508263509Sdim HasAtleastOneRequiredProperty = true; 509263509Sdim DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName()); 510263509Sdim if (R.size() == 0) { 511263509Sdim // Relax the rule and look into class's implementation for a synthesize 512263509Sdim // or dynamic declaration. Class is implementing a property coming from 513263509Sdim // another protocol. This still makes the target protocol as conforming. 514263509Sdim if (!ImpDecl->FindPropertyImplDecl( 515263509Sdim Property->getDeclName().getAsIdentifierInfo())) 516263509Sdim return false; 517263509Sdim } 518263509Sdim else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) { 519263509Sdim if ((ClassProperty->getPropertyAttributes() 520263509Sdim != Property->getPropertyAttributes()) || 521263509Sdim !Ctx.hasSameType(ClassProperty->getType(), Property->getType())) 522263509Sdim return false; 523263509Sdim } 524263509Sdim else 525263509Sdim return false; 526263509Sdim } 527263509Sdim 528263509Sdim // At this point, all required properties in this protocol conform to those 529263509Sdim // declared in the class. 530263509Sdim // Check that class implements the required methods of the protocol too. 531263509Sdim bool HasAtleastOneRequiredMethod = false; 532263509Sdim if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) { 533263509Sdim if (PDecl->meth_begin() == PDecl->meth_end()) 534263509Sdim return HasAtleastOneRequiredProperty; 535263509Sdim for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(), 536263509Sdim MEnd = PDecl->meth_end(); M != MEnd; ++M) { 537263509Sdim ObjCMethodDecl *MD = (*M); 538263509Sdim if (MD->isImplicit()) 539263509Sdim continue; 540263509Sdim if (MD->getImplementationControl() == ObjCMethodDecl::Optional) 541263509Sdim continue; 542263509Sdim DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName()); 543263509Sdim if (R.size() == 0) 544263509Sdim return false; 545263509Sdim bool match = false; 546263509Sdim HasAtleastOneRequiredMethod = true; 547263509Sdim for (unsigned I = 0, N = R.size(); I != N; ++I) 548263509Sdim if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0])) 549263509Sdim if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) { 550263509Sdim match = true; 551263509Sdim break; 552263509Sdim } 553263509Sdim if (!match) 554263509Sdim return false; 555263509Sdim } 556263509Sdim } 557263509Sdim if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod) 558263509Sdim return true; 559263509Sdim return false; 560263509Sdim} 561263509Sdim 562263509Sdimstatic bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl, 563263509Sdim llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols, 564263509Sdim const NSAPI &NS, edit::Commit &commit) { 565263509Sdim const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols(); 566263509Sdim std::string ClassString; 567263509Sdim SourceLocation EndLoc = 568263509Sdim IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation(); 569263509Sdim 570263509Sdim if (Protocols.empty()) { 571263509Sdim ClassString = '<'; 572263509Sdim for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 573263509Sdim ClassString += ConformingProtocols[i]->getNameAsString(); 574263509Sdim if (i != (e-1)) 575263509Sdim ClassString += ", "; 576263509Sdim } 577263509Sdim ClassString += "> "; 578263509Sdim } 579263509Sdim else { 580263509Sdim ClassString = ", "; 581263509Sdim for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 582263509Sdim ClassString += ConformingProtocols[i]->getNameAsString(); 583263509Sdim if (i != (e-1)) 584263509Sdim ClassString += ", "; 585263509Sdim } 586263509Sdim ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1; 587263509Sdim EndLoc = *PL; 588263509Sdim } 589263509Sdim 590263509Sdim commit.insertAfterToken(EndLoc, ClassString); 591263509Sdim return true; 592263509Sdim} 593263509Sdim 594263509Sdimstatic bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl, 595263509Sdim const TypedefDecl *TypedefDcl, 596263509Sdim const NSAPI &NS, edit::Commit &commit, 597263509Sdim bool IsNSIntegerType, 598263509Sdim bool NSOptions) { 599263509Sdim std::string ClassString; 600263509Sdim if (NSOptions) 601263509Sdim ClassString = "typedef NS_OPTIONS(NSUInteger, "; 602263509Sdim else 603263509Sdim ClassString = 604263509Sdim IsNSIntegerType ? "typedef NS_ENUM(NSInteger, " 605263509Sdim : "typedef NS_ENUM(NSUInteger, "; 606263509Sdim 607263509Sdim ClassString += TypedefDcl->getIdentifier()->getName(); 608263509Sdim ClassString += ')'; 609263509Sdim SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); 610263509Sdim commit.replace(R, ClassString); 611263509Sdim SourceLocation EndOfEnumDclLoc = EnumDcl->getLocEnd(); 612263509Sdim EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc, 613263509Sdim NS.getASTContext(), /*IsDecl*/true); 614263509Sdim if (!EndOfEnumDclLoc.isInvalid()) { 615263509Sdim SourceRange EnumDclRange(EnumDcl->getLocStart(), EndOfEnumDclLoc); 616263509Sdim commit.insertFromRange(TypedefDcl->getLocStart(), EnumDclRange); 617263509Sdim } 618263509Sdim else 619263509Sdim return false; 620263509Sdim 621263509Sdim SourceLocation EndTypedefDclLoc = TypedefDcl->getLocEnd(); 622263509Sdim EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc, 623263509Sdim NS.getASTContext(), /*IsDecl*/true); 624263509Sdim if (!EndTypedefDclLoc.isInvalid()) { 625263509Sdim SourceRange TDRange(TypedefDcl->getLocStart(), EndTypedefDclLoc); 626263509Sdim commit.remove(TDRange); 627263509Sdim } 628263509Sdim else 629263509Sdim return false; 630263509Sdim 631263509Sdim EndOfEnumDclLoc = trans::findLocationAfterSemi(EnumDcl->getLocEnd(), NS.getASTContext(), 632263509Sdim /*IsDecl*/true); 633263509Sdim if (!EndOfEnumDclLoc.isInvalid()) { 634263509Sdim SourceLocation BeginOfEnumDclLoc = EnumDcl->getLocStart(); 635263509Sdim // FIXME. This assumes that enum decl; is immediately preceeded by eoln. 636263509Sdim // It is trying to remove the enum decl. lines entirely. 637263509Sdim BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1); 638263509Sdim commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc)); 639263509Sdim return true; 640263509Sdim } 641263509Sdim return false; 642263509Sdim} 643263509Sdim 644263509Sdimstatic void rewriteToNSMacroDecl(const EnumDecl *EnumDcl, 645263509Sdim const TypedefDecl *TypedefDcl, 646263509Sdim const NSAPI &NS, edit::Commit &commit, 647263509Sdim bool IsNSIntegerType) { 648263509Sdim std::string ClassString = 649263509Sdim IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, "; 650263509Sdim ClassString += TypedefDcl->getIdentifier()->getName(); 651263509Sdim ClassString += ')'; 652263509Sdim SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart()); 653263509Sdim commit.replace(R, ClassString); 654263509Sdim SourceLocation TypedefLoc = TypedefDcl->getLocEnd(); 655263509Sdim commit.remove(SourceRange(TypedefLoc, TypedefLoc)); 656263509Sdim} 657263509Sdim 658263509Sdimstatic bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx, 659263509Sdim const EnumDecl *EnumDcl) { 660263509Sdim bool PowerOfTwo = true; 661263509Sdim bool AllHexdecimalEnumerator = true; 662263509Sdim uint64_t MaxPowerOfTwoVal = 0; 663263509Sdim for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(), 664263509Sdim EE = EnumDcl->enumerator_end(); EI != EE; ++EI) { 665263509Sdim EnumConstantDecl *Enumerator = (*EI); 666263509Sdim const Expr *InitExpr = Enumerator->getInitExpr(); 667263509Sdim if (!InitExpr) { 668263509Sdim PowerOfTwo = false; 669263509Sdim AllHexdecimalEnumerator = false; 670263509Sdim continue; 671263509Sdim } 672263509Sdim InitExpr = InitExpr->IgnoreParenCasts(); 673263509Sdim if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr)) 674263509Sdim if (BO->isShiftOp() || BO->isBitwiseOp()) 675263509Sdim return true; 676263509Sdim 677263509Sdim uint64_t EnumVal = Enumerator->getInitVal().getZExtValue(); 678263509Sdim if (PowerOfTwo && EnumVal) { 679263509Sdim if (!llvm::isPowerOf2_64(EnumVal)) 680263509Sdim PowerOfTwo = false; 681263509Sdim else if (EnumVal > MaxPowerOfTwoVal) 682263509Sdim MaxPowerOfTwoVal = EnumVal; 683263509Sdim } 684263509Sdim if (AllHexdecimalEnumerator && EnumVal) { 685263509Sdim bool FoundHexdecimalEnumerator = false; 686263509Sdim SourceLocation EndLoc = Enumerator->getLocEnd(); 687263509Sdim Token Tok; 688263509Sdim if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true)) 689263509Sdim if (Tok.isLiteral() && Tok.getLength() > 2) { 690263509Sdim if (const char *StringLit = Tok.getLiteralData()) 691263509Sdim FoundHexdecimalEnumerator = 692263509Sdim (StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x')); 693263509Sdim } 694263509Sdim if (!FoundHexdecimalEnumerator) 695263509Sdim AllHexdecimalEnumerator = false; 696263509Sdim } 697263509Sdim } 698263509Sdim return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2)); 699263509Sdim} 700263509Sdim 701263509Sdimvoid ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx, 702263509Sdim const ObjCImplementationDecl *ImpDecl) { 703263509Sdim const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface(); 704263509Sdim if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated()) 705263509Sdim return; 706263509Sdim // Find all implicit conforming protocols for this class 707263509Sdim // and make them explicit. 708263509Sdim llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols; 709263509Sdim Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols); 710263509Sdim llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols; 711263509Sdim 712263509Sdim for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I = 713263509Sdim ObjCProtocolDecls.begin(), 714263509Sdim E = ObjCProtocolDecls.end(); I != E; ++I) 715263509Sdim if (!ExplicitProtocols.count(*I)) 716263509Sdim PotentialImplicitProtocols.push_back(*I); 717263509Sdim 718263509Sdim if (PotentialImplicitProtocols.empty()) 719263509Sdim return; 720263509Sdim 721263509Sdim // go through list of non-optional methods and properties in each protocol 722263509Sdim // in the PotentialImplicitProtocols list. If class implements every one of the 723263509Sdim // methods and properties, then this class conforms to this protocol. 724263509Sdim llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols; 725263509Sdim for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++) 726263509Sdim if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl, 727263509Sdim PotentialImplicitProtocols[i])) 728263509Sdim ConformingProtocols.push_back(PotentialImplicitProtocols[i]); 729263509Sdim 730263509Sdim if (ConformingProtocols.empty()) 731263509Sdim return; 732263509Sdim 733263509Sdim // Further reduce number of conforming protocols. If protocol P1 is in the list 734263509Sdim // protocol P2 (P2<P1>), No need to include P1. 735263509Sdim llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols; 736263509Sdim for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 737263509Sdim bool DropIt = false; 738263509Sdim ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i]; 739263509Sdim for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) { 740263509Sdim ObjCProtocolDecl *PDecl = ConformingProtocols[i1]; 741263509Sdim if (PDecl == TargetPDecl) 742263509Sdim continue; 743263509Sdim if (PDecl->lookupProtocolNamed( 744263509Sdim TargetPDecl->getDeclName().getAsIdentifierInfo())) { 745263509Sdim DropIt = true; 746263509Sdim break; 747263509Sdim } 748263509Sdim } 749263509Sdim if (!DropIt) 750263509Sdim MinimalConformingProtocols.push_back(TargetPDecl); 751263509Sdim } 752263509Sdim edit::Commit commit(*Editor); 753263509Sdim rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols, 754263509Sdim *NSAPIObj, commit); 755263509Sdim Editor->commit(commit); 756263509Sdim} 757263509Sdim 758263509Sdimvoid ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed( 759263509Sdim const TypedefDecl *TypedefDcl) { 760263509Sdim 761263509Sdim QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); 762263509Sdim if (NSAPIObj->isObjCNSIntegerType(qt)) 763263509Sdim NSIntegerTypedefed = TypedefDcl; 764263509Sdim else if (NSAPIObj->isObjCNSUIntegerType(qt)) 765263509Sdim NSUIntegerTypedefed = TypedefDcl; 766263509Sdim} 767263509Sdim 768263509Sdimbool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx, 769263509Sdim const EnumDecl *EnumDcl, 770263509Sdim const TypedefDecl *TypedefDcl) { 771263509Sdim if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() || 772263509Sdim EnumDcl->isDeprecated()) 773263509Sdim return false; 774263509Sdim if (!TypedefDcl) { 775263509Sdim if (NSIntegerTypedefed) { 776263509Sdim TypedefDcl = NSIntegerTypedefed; 777263509Sdim NSIntegerTypedefed = 0; 778263509Sdim } 779263509Sdim else if (NSUIntegerTypedefed) { 780263509Sdim TypedefDcl = NSUIntegerTypedefed; 781263509Sdim NSUIntegerTypedefed = 0; 782263509Sdim } 783263509Sdim else 784263509Sdim return false; 785263509Sdim FileID FileIdOfTypedefDcl = 786263509Sdim PP.getSourceManager().getFileID(TypedefDcl->getLocation()); 787263509Sdim FileID FileIdOfEnumDcl = 788263509Sdim PP.getSourceManager().getFileID(EnumDcl->getLocation()); 789263509Sdim if (FileIdOfTypedefDcl != FileIdOfEnumDcl) 790263509Sdim return false; 791263509Sdim } 792263509Sdim if (TypedefDcl->isDeprecated()) 793263509Sdim return false; 794263509Sdim 795263509Sdim QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); 796263509Sdim bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt); 797263509Sdim bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt); 798263509Sdim 799263509Sdim if (!IsNSIntegerType && !IsNSUIntegerType) { 800263509Sdim // Also check for typedef enum {...} TD; 801263509Sdim if (const EnumType *EnumTy = qt->getAs<EnumType>()) { 802263509Sdim if (EnumTy->getDecl() == EnumDcl) { 803263509Sdim bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl); 804263509Sdim if (NSOptions) { 805263509Sdim if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition()) 806263509Sdim return false; 807263509Sdim } 808263509Sdim else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition()) 809263509Sdim return false; 810263509Sdim edit::Commit commit(*Editor); 811263509Sdim rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions); 812263509Sdim Editor->commit(commit); 813263509Sdim return true; 814263509Sdim } 815263509Sdim } 816263509Sdim return false; 817263509Sdim } 818263509Sdim 819263509Sdim // We may still use NS_OPTIONS based on what we find in the enumertor list. 820263509Sdim bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl); 821263509Sdim // NS_ENUM must be available. 822263509Sdim if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition()) 823263509Sdim return false; 824263509Sdim // NS_OPTIONS must be available. 825263509Sdim if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition()) 826263509Sdim return false; 827263509Sdim edit::Commit commit(*Editor); 828263509Sdim bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, 829263509Sdim commit, IsNSIntegerType, NSOptions); 830263509Sdim Editor->commit(commit); 831263509Sdim return Res; 832263509Sdim} 833263509Sdim 834263509Sdimstatic void ReplaceWithInstancetype(const ObjCMigrateASTConsumer &ASTC, 835263509Sdim ObjCMethodDecl *OM) { 836263509Sdim SourceRange R; 837263509Sdim std::string ClassString; 838263509Sdim if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) { 839263509Sdim TypeLoc TL = TSInfo->getTypeLoc(); 840263509Sdim R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); 841263509Sdim ClassString = "instancetype"; 842263509Sdim } 843263509Sdim else { 844263509Sdim R = SourceRange(OM->getLocStart(), OM->getLocStart()); 845263509Sdim ClassString = OM->isInstanceMethod() ? '-' : '+'; 846263509Sdim ClassString += " (instancetype)"; 847263509Sdim } 848263509Sdim edit::Commit commit(*ASTC.Editor); 849263509Sdim commit.replace(R, ClassString); 850263509Sdim ASTC.Editor->commit(commit); 851263509Sdim} 852263509Sdim 853263509Sdimstatic void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC, 854263509Sdim ObjCMethodDecl *OM) { 855263509Sdim ObjCInterfaceDecl *IDecl = OM->getClassInterface(); 856263509Sdim SourceRange R; 857263509Sdim std::string ClassString; 858263509Sdim if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) { 859263509Sdim TypeLoc TL = TSInfo->getTypeLoc(); 860263509Sdim R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); { 861263509Sdim ClassString = IDecl->getName(); 862263509Sdim ClassString += "*"; 863263509Sdim } 864263509Sdim } 865263509Sdim else { 866263509Sdim R = SourceRange(OM->getLocStart(), OM->getLocStart()); 867263509Sdim ClassString = "+ ("; 868263509Sdim ClassString += IDecl->getName(); ClassString += "*)"; 869263509Sdim } 870263509Sdim edit::Commit commit(*ASTC.Editor); 871263509Sdim commit.replace(R, ClassString); 872263509Sdim ASTC.Editor->commit(commit); 873263509Sdim} 874263509Sdim 875263509Sdimvoid ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx, 876263509Sdim ObjCContainerDecl *CDecl, 877263509Sdim ObjCMethodDecl *OM) { 878263509Sdim ObjCInstanceTypeFamily OIT_Family = 879263509Sdim Selector::getInstTypeMethodFamily(OM->getSelector()); 880263509Sdim 881263509Sdim std::string ClassName; 882263509Sdim switch (OIT_Family) { 883263509Sdim case OIT_None: 884263509Sdim migrateFactoryMethod(Ctx, CDecl, OM); 885263509Sdim return; 886263509Sdim case OIT_Array: 887263509Sdim ClassName = "NSArray"; 888263509Sdim break; 889263509Sdim case OIT_Dictionary: 890263509Sdim ClassName = "NSDictionary"; 891263509Sdim break; 892263509Sdim case OIT_Singleton: 893263509Sdim migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton); 894263509Sdim return; 895263509Sdim case OIT_Init: 896263509Sdim if (OM->getResultType()->isObjCIdType()) 897263509Sdim ReplaceWithInstancetype(*this, OM); 898263509Sdim return; 899263509Sdim case OIT_ReturnsSelf: 900263509Sdim migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf); 901263509Sdim return; 902263509Sdim } 903263509Sdim if (!OM->getResultType()->isObjCIdType()) 904263509Sdim return; 905263509Sdim 906263509Sdim ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 907263509Sdim if (!IDecl) { 908263509Sdim if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 909263509Sdim IDecl = CatDecl->getClassInterface(); 910263509Sdim else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 911263509Sdim IDecl = ImpDecl->getClassInterface(); 912263509Sdim } 913263509Sdim if (!IDecl || 914263509Sdim !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) { 915263509Sdim migrateFactoryMethod(Ctx, CDecl, OM); 916263509Sdim return; 917263509Sdim } 918263509Sdim ReplaceWithInstancetype(*this, OM); 919263509Sdim} 920263509Sdim 921263509Sdimstatic bool TypeIsInnerPointer(QualType T) { 922263509Sdim if (!T->isAnyPointerType()) 923263509Sdim return false; 924263509Sdim if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() || 925263509Sdim T->isBlockPointerType() || T->isFunctionPointerType() || 926263509Sdim ento::coreFoundation::isCFObjectRef(T)) 927263509Sdim return false; 928263509Sdim // Also, typedef-of-pointer-to-incomplete-struct is something that we assume 929263509Sdim // is not an innter pointer type. 930263509Sdim QualType OrigT = T; 931263509Sdim while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr())) 932263509Sdim T = TD->getDecl()->getUnderlyingType(); 933263509Sdim if (OrigT == T || !T->isPointerType()) 934263509Sdim return true; 935263509Sdim const PointerType* PT = T->getAs<PointerType>(); 936263509Sdim QualType UPointeeT = PT->getPointeeType().getUnqualifiedType(); 937263509Sdim if (UPointeeT->isRecordType()) { 938263509Sdim const RecordType *RecordTy = UPointeeT->getAs<RecordType>(); 939263509Sdim if (!RecordTy->getDecl()->isCompleteDefinition()) 940263509Sdim return false; 941263509Sdim } 942263509Sdim return true; 943263509Sdim} 944263509Sdim 945263509Sdim/// \brief Check whether the two versions match. 946263509Sdimstatic bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) { 947263509Sdim return (X == Y); 948263509Sdim} 949263509Sdim 950263509Sdim/// AvailabilityAttrsMatch - This routine checks that if comparing two 951263509Sdim/// availability attributes, all their components match. It returns 952263509Sdim/// true, if not dealing with availability or when all components of 953263509Sdim/// availability attributes match. This routine is only called when 954263509Sdim/// the attributes are of the same kind. 955263509Sdimstatic bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) { 956263509Sdim const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1); 957263509Sdim if (!AA1) 958263509Sdim return true; 959263509Sdim const AvailabilityAttr *AA2 = dyn_cast<AvailabilityAttr>(At2); 960263509Sdim 961263509Sdim VersionTuple Introduced1 = AA1->getIntroduced(); 962263509Sdim VersionTuple Deprecated1 = AA1->getDeprecated(); 963263509Sdim VersionTuple Obsoleted1 = AA1->getObsoleted(); 964263509Sdim bool IsUnavailable1 = AA1->getUnavailable(); 965263509Sdim VersionTuple Introduced2 = AA2->getIntroduced(); 966263509Sdim VersionTuple Deprecated2 = AA2->getDeprecated(); 967263509Sdim VersionTuple Obsoleted2 = AA2->getObsoleted(); 968263509Sdim bool IsUnavailable2 = AA2->getUnavailable(); 969263509Sdim return (versionsMatch(Introduced1, Introduced2) && 970263509Sdim versionsMatch(Deprecated1, Deprecated2) && 971263509Sdim versionsMatch(Obsoleted1, Obsoleted2) && 972263509Sdim IsUnavailable1 == IsUnavailable2); 973263509Sdim 974263509Sdim} 975263509Sdim 976263509Sdimstatic bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2, 977263509Sdim bool &AvailabilityArgsMatch) { 978263509Sdim // This list is very small, so this need not be optimized. 979263509Sdim for (unsigned i = 0, e = Attrs1.size(); i != e; i++) { 980263509Sdim bool match = false; 981263509Sdim for (unsigned j = 0, f = Attrs2.size(); j != f; j++) { 982263509Sdim // Matching attribute kind only. Except for Availabilty attributes, 983263509Sdim // we are not getting into details of the attributes. For all practical purposes 984263509Sdim // this is sufficient. 985263509Sdim if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) { 986263509Sdim if (AvailabilityArgsMatch) 987263509Sdim AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]); 988263509Sdim match = true; 989263509Sdim break; 990263509Sdim } 991263509Sdim } 992263509Sdim if (!match) 993263509Sdim return false; 994263509Sdim } 995263509Sdim return true; 996263509Sdim} 997263509Sdim 998263509Sdim/// AttributesMatch - This routine checks list of attributes for two 999263509Sdim/// decls. It returns false, if there is a mismatch in kind of 1000263509Sdim/// attributes seen in the decls. It returns true if the two decls 1001263509Sdim/// have list of same kind of attributes. Furthermore, when there 1002263509Sdim/// are availability attributes in the two decls, it sets the 1003263509Sdim/// AvailabilityArgsMatch to false if availability attributes have 1004263509Sdim/// different versions, etc. 1005263509Sdimstatic bool AttributesMatch(const Decl *Decl1, const Decl *Decl2, 1006263509Sdim bool &AvailabilityArgsMatch) { 1007263509Sdim if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) { 1008263509Sdim AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs()); 1009263509Sdim return true; 1010263509Sdim } 1011263509Sdim AvailabilityArgsMatch = true; 1012263509Sdim const AttrVec &Attrs1 = Decl1->getAttrs(); 1013263509Sdim const AttrVec &Attrs2 = Decl2->getAttrs(); 1014263509Sdim bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch); 1015263509Sdim if (match && (Attrs2.size() > Attrs1.size())) 1016263509Sdim return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch); 1017263509Sdim return match; 1018263509Sdim} 1019263509Sdim 1020263509Sdimstatic bool IsValidIdentifier(ASTContext &Ctx, 1021263509Sdim const char *Name) { 1022263509Sdim if (!isIdentifierHead(Name[0])) 1023263509Sdim return false; 1024263509Sdim std::string NameString = Name; 1025263509Sdim NameString[0] = toLowercase(NameString[0]); 1026263509Sdim IdentifierInfo *II = &Ctx.Idents.get(NameString); 1027263509Sdim return II->getTokenID() == tok::identifier; 1028263509Sdim} 1029263509Sdim 1030263509Sdimbool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx, 1031263509Sdim ObjCContainerDecl *D, 1032263509Sdim ObjCMethodDecl *Method) { 1033263509Sdim if (Method->isPropertyAccessor() || !Method->isInstanceMethod() || 1034263509Sdim Method->param_size() != 0) 1035263509Sdim return false; 1036263509Sdim // Is this method candidate to be a getter? 1037263509Sdim QualType GRT = Method->getResultType(); 1038263509Sdim if (GRT->isVoidType()) 1039263509Sdim return false; 1040263509Sdim 1041263509Sdim Selector GetterSelector = Method->getSelector(); 1042263509Sdim ObjCInstanceTypeFamily OIT_Family = 1043263509Sdim Selector::getInstTypeMethodFamily(GetterSelector); 1044263509Sdim 1045263509Sdim if (OIT_Family != OIT_None) 1046263509Sdim return false; 1047263509Sdim 1048263509Sdim IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0); 1049263509Sdim Selector SetterSelector = 1050263509Sdim SelectorTable::constructSetterSelector(PP.getIdentifierTable(), 1051263509Sdim PP.getSelectorTable(), 1052263509Sdim getterName); 1053263509Sdim ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector); 1054263509Sdim unsigned LengthOfPrefix = 0; 1055263509Sdim if (!SetterMethod) { 1056263509Sdim // try a different naming convention for getter: isXxxxx 1057263509Sdim StringRef getterNameString = getterName->getName(); 1058263509Sdim bool IsPrefix = getterNameString.startswith("is"); 1059263509Sdim // Note that we don't want to change an isXXX method of retainable object 1060263509Sdim // type to property (readonly or otherwise). 1061263509Sdim if (IsPrefix && GRT->isObjCRetainableType()) 1062263509Sdim return false; 1063263509Sdim if (IsPrefix || getterNameString.startswith("get")) { 1064263509Sdim LengthOfPrefix = (IsPrefix ? 2 : 3); 1065263509Sdim const char *CGetterName = getterNameString.data() + LengthOfPrefix; 1066263509Sdim // Make sure that first character after "is" or "get" prefix can 1067263509Sdim // start an identifier. 1068263509Sdim if (!IsValidIdentifier(Ctx, CGetterName)) 1069263509Sdim return false; 1070263509Sdim if (CGetterName[0] && isUppercase(CGetterName[0])) { 1071263509Sdim getterName = &Ctx.Idents.get(CGetterName); 1072263509Sdim SetterSelector = 1073263509Sdim SelectorTable::constructSetterSelector(PP.getIdentifierTable(), 1074263509Sdim PP.getSelectorTable(), 1075263509Sdim getterName); 1076263509Sdim SetterMethod = D->getInstanceMethod(SetterSelector); 1077263509Sdim } 1078263509Sdim } 1079263509Sdim } 1080263509Sdim 1081263509Sdim if (SetterMethod) { 1082263509Sdim if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0) 1083263509Sdim return false; 1084263509Sdim bool AvailabilityArgsMatch; 1085263509Sdim if (SetterMethod->isDeprecated() || 1086263509Sdim !AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch)) 1087263509Sdim return false; 1088263509Sdim 1089263509Sdim // Is this a valid setter, matching the target getter? 1090263509Sdim QualType SRT = SetterMethod->getResultType(); 1091263509Sdim if (!SRT->isVoidType()) 1092263509Sdim return false; 1093263509Sdim const ParmVarDecl *argDecl = *SetterMethod->param_begin(); 1094263509Sdim QualType ArgType = argDecl->getType(); 1095263509Sdim if (!Ctx.hasSameUnqualifiedType(ArgType, GRT)) 1096263509Sdim return false; 1097263509Sdim edit::Commit commit(*Editor); 1098263509Sdim rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit, 1099263509Sdim LengthOfPrefix, 1100263509Sdim (ASTMigrateActions & 1101263509Sdim FrontendOptions::ObjCMT_AtomicProperty) != 0, 1102263509Sdim (ASTMigrateActions & 1103263509Sdim FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0, 1104263509Sdim AvailabilityArgsMatch); 1105263509Sdim Editor->commit(commit); 1106263509Sdim return true; 1107263509Sdim } 1108263509Sdim else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) { 1109263509Sdim // Try a non-void method with no argument (and no setter or property of same name 1110263509Sdim // as a 'readonly' property. 1111263509Sdim edit::Commit commit(*Editor); 1112263509Sdim rewriteToObjCProperty(Method, 0 /*SetterMethod*/, *NSAPIObj, commit, 1113263509Sdim LengthOfPrefix, 1114263509Sdim (ASTMigrateActions & 1115263509Sdim FrontendOptions::ObjCMT_AtomicProperty) != 0, 1116263509Sdim (ASTMigrateActions & 1117263509Sdim FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0, 1118263509Sdim /*AvailabilityArgsMatch*/false); 1119263509Sdim Editor->commit(commit); 1120263509Sdim return true; 1121263509Sdim } 1122263509Sdim return false; 1123263509Sdim} 1124263509Sdim 1125263509Sdimvoid ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx, 1126263509Sdim ObjCMethodDecl *OM) { 1127263509Sdim if (OM->isImplicit() || 1128263509Sdim !OM->isInstanceMethod() || 1129263509Sdim OM->hasAttr<ObjCReturnsInnerPointerAttr>()) 1130263509Sdim return; 1131263509Sdim 1132263509Sdim QualType RT = OM->getResultType(); 1133263509Sdim if (!TypeIsInnerPointer(RT) || 1134263509Sdim !Ctx.Idents.get("NS_RETURNS_INNER_POINTER").hasMacroDefinition()) 1135263509Sdim return; 1136263509Sdim 1137263509Sdim edit::Commit commit(*Editor); 1138263509Sdim commit.insertBefore(OM->getLocEnd(), " NS_RETURNS_INNER_POINTER"); 1139263509Sdim Editor->commit(commit); 1140263509Sdim} 1141263509Sdim 1142263509Sdimvoid ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, 1143263509Sdim ObjCPropertyDecl *P) { 1144263509Sdim QualType T = P->getType(); 1145263509Sdim 1146263509Sdim if (!TypeIsInnerPointer(T) || 1147263509Sdim !Ctx.Idents.get("NS_RETURNS_INNER_POINTER").hasMacroDefinition()) 1148263509Sdim return; 1149263509Sdim edit::Commit commit(*Editor); 1150263509Sdim commit.insertBefore(P->getLocEnd(), " NS_RETURNS_INNER_POINTER "); 1151263509Sdim Editor->commit(commit); 1152263509Sdim} 1153263509Sdim 1154263509Sdimvoid ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx, 1155263509Sdim ObjCContainerDecl *CDecl) { 1156263509Sdim if (CDecl->isDeprecated()) 1157263509Sdim return; 1158263509Sdim 1159263509Sdim // migrate methods which can have instancetype as their result type. 1160263509Sdim for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(), 1161263509Sdim MEnd = CDecl->meth_end(); 1162263509Sdim M != MEnd; ++M) { 1163263509Sdim ObjCMethodDecl *Method = (*M); 1164263509Sdim if (Method->isDeprecated()) 1165263509Sdim continue; 1166263509Sdim migrateMethodInstanceType(Ctx, CDecl, Method); 1167263509Sdim } 1168263509Sdim} 1169263509Sdim 1170263509Sdimvoid ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx, 1171263509Sdim ObjCContainerDecl *CDecl, 1172263509Sdim ObjCMethodDecl *OM, 1173263509Sdim ObjCInstanceTypeFamily OIT_Family) { 1174263509Sdim if (OM->isInstanceMethod() || 1175263509Sdim OM->getResultType() == Ctx.getObjCInstanceType() || 1176263509Sdim !OM->getResultType()->isObjCIdType()) 1177263509Sdim return; 1178263509Sdim 1179263509Sdim // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class 1180263509Sdim // NSYYYNamE with matching names be at least 3 characters long. 1181263509Sdim ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 1182263509Sdim if (!IDecl) { 1183263509Sdim if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 1184263509Sdim IDecl = CatDecl->getClassInterface(); 1185263509Sdim else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 1186263509Sdim IDecl = ImpDecl->getClassInterface(); 1187263509Sdim } 1188263509Sdim if (!IDecl) 1189263509Sdim return; 1190263509Sdim 1191263509Sdim std::string StringClassName = IDecl->getName(); 1192263509Sdim StringRef LoweredClassName(StringClassName); 1193263509Sdim std::string StringLoweredClassName = LoweredClassName.lower(); 1194263509Sdim LoweredClassName = StringLoweredClassName; 1195263509Sdim 1196263509Sdim IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0); 1197263509Sdim // Handle method with no name at its first selector slot; e.g. + (id):(int)x. 1198263509Sdim if (!MethodIdName) 1199263509Sdim return; 1200263509Sdim 1201263509Sdim std::string MethodName = MethodIdName->getName(); 1202263509Sdim if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) { 1203263509Sdim StringRef STRefMethodName(MethodName); 1204263509Sdim size_t len = 0; 1205263509Sdim if (STRefMethodName.startswith("standard")) 1206263509Sdim len = strlen("standard"); 1207263509Sdim else if (STRefMethodName.startswith("shared")) 1208263509Sdim len = strlen("shared"); 1209263509Sdim else if (STRefMethodName.startswith("default")) 1210263509Sdim len = strlen("default"); 1211263509Sdim else 1212263509Sdim return; 1213263509Sdim MethodName = STRefMethodName.substr(len); 1214263509Sdim } 1215263509Sdim std::string MethodNameSubStr = MethodName.substr(0, 3); 1216263509Sdim StringRef MethodNamePrefix(MethodNameSubStr); 1217263509Sdim std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower(); 1218263509Sdim MethodNamePrefix = StringLoweredMethodNamePrefix; 1219263509Sdim size_t Ix = LoweredClassName.rfind(MethodNamePrefix); 1220263509Sdim if (Ix == StringRef::npos) 1221263509Sdim return; 1222263509Sdim std::string ClassNamePostfix = LoweredClassName.substr(Ix); 1223263509Sdim StringRef LoweredMethodName(MethodName); 1224263509Sdim std::string StringLoweredMethodName = LoweredMethodName.lower(); 1225263509Sdim LoweredMethodName = StringLoweredMethodName; 1226263509Sdim if (!LoweredMethodName.startswith(ClassNamePostfix)) 1227263509Sdim return; 1228263509Sdim if (OIT_Family == OIT_ReturnsSelf) 1229263509Sdim ReplaceWithClasstype(*this, OM); 1230263509Sdim else 1231263509Sdim ReplaceWithInstancetype(*this, OM); 1232263509Sdim} 1233263509Sdim 1234263509Sdimstatic bool IsVoidStarType(QualType Ty) { 1235263509Sdim if (!Ty->isPointerType()) 1236263509Sdim return false; 1237263509Sdim 1238263509Sdim while (const TypedefType *TD = dyn_cast<TypedefType>(Ty.getTypePtr())) 1239263509Sdim Ty = TD->getDecl()->getUnderlyingType(); 1240263509Sdim 1241263509Sdim // Is the type void*? 1242263509Sdim const PointerType* PT = Ty->getAs<PointerType>(); 1243263509Sdim if (PT->getPointeeType().getUnqualifiedType()->isVoidType()) 1244263509Sdim return true; 1245263509Sdim return IsVoidStarType(PT->getPointeeType()); 1246263509Sdim} 1247263509Sdim 1248263509Sdim/// AuditedType - This routine audits the type AT and returns false if it is one of known 1249263509Sdim/// CF object types or of the "void *" variety. It returns true if we don't care about the type 1250263509Sdim/// such as a non-pointer or pointers which have no ownership issues (such as "int *"). 1251263509Sdimstatic bool AuditedType (QualType AT) { 1252263509Sdim if (!AT->isAnyPointerType() && !AT->isBlockPointerType()) 1253263509Sdim return true; 1254263509Sdim // FIXME. There isn't much we can say about CF pointer type; or is there? 1255263509Sdim if (ento::coreFoundation::isCFObjectRef(AT) || 1256263509Sdim IsVoidStarType(AT) || 1257263509Sdim // If an ObjC object is type, assuming that it is not a CF function and 1258263509Sdim // that it is an un-audited function. 1259263509Sdim AT->isObjCObjectPointerType() || AT->isObjCBuiltinType()) 1260263509Sdim return false; 1261263509Sdim // All other pointers are assumed audited as harmless. 1262263509Sdim return true; 1263263509Sdim} 1264263509Sdim 1265263509Sdimvoid ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) { 1266263509Sdim if (CFFunctionIBCandidates.empty()) 1267263509Sdim return; 1268263509Sdim if (!Ctx.Idents.get("CF_IMPLICIT_BRIDGING_ENABLED").hasMacroDefinition()) { 1269263509Sdim CFFunctionIBCandidates.clear(); 1270263509Sdim FileId = FileID(); 1271263509Sdim return; 1272263509Sdim } 1273263509Sdim // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED 1274263509Sdim const Decl *FirstFD = CFFunctionIBCandidates[0]; 1275263509Sdim const Decl *LastFD = 1276263509Sdim CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1]; 1277263509Sdim const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n"; 1278263509Sdim edit::Commit commit(*Editor); 1279263509Sdim commit.insertBefore(FirstFD->getLocStart(), PragmaString); 1280263509Sdim PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n"; 1281263509Sdim SourceLocation EndLoc = LastFD->getLocEnd(); 1282263509Sdim // get location just past end of function location. 1283263509Sdim EndLoc = PP.getLocForEndOfToken(EndLoc); 1284263509Sdim if (isa<FunctionDecl>(LastFD)) { 1285263509Sdim // For Methods, EndLoc points to the ending semcolon. So, 1286263509Sdim // not of these extra work is needed. 1287263509Sdim Token Tok; 1288263509Sdim // get locaiton of token that comes after end of function. 1289263509Sdim bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true); 1290263509Sdim if (!Failed) 1291263509Sdim EndLoc = Tok.getLocation(); 1292263509Sdim } 1293263509Sdim commit.insertAfterToken(EndLoc, PragmaString); 1294263509Sdim Editor->commit(commit); 1295263509Sdim FileId = FileID(); 1296263509Sdim CFFunctionIBCandidates.clear(); 1297263509Sdim} 1298263509Sdim 1299263509Sdimvoid ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) { 1300263509Sdim if (Decl->isDeprecated()) 1301263509Sdim return; 1302263509Sdim 1303263509Sdim if (Decl->hasAttr<CFAuditedTransferAttr>()) { 1304263509Sdim assert(CFFunctionIBCandidates.empty() && 1305263509Sdim "Cannot have audited functions/methods inside user " 1306263509Sdim "provided CF_IMPLICIT_BRIDGING_ENABLE"); 1307263509Sdim return; 1308263509Sdim } 1309263509Sdim 1310263509Sdim // Finction must be annotated first. 1311263509Sdim if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) { 1312263509Sdim CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl); 1313263509Sdim if (AuditKind == CF_BRIDGING_ENABLE) { 1314263509Sdim CFFunctionIBCandidates.push_back(Decl); 1315263509Sdim if (FileId.isInvalid()) 1316263509Sdim FileId = PP.getSourceManager().getFileID(Decl->getLocation()); 1317263509Sdim } 1318263509Sdim else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) { 1319263509Sdim if (!CFFunctionIBCandidates.empty()) { 1320263509Sdim CFFunctionIBCandidates.push_back(Decl); 1321263509Sdim if (FileId.isInvalid()) 1322263509Sdim FileId = PP.getSourceManager().getFileID(Decl->getLocation()); 1323263509Sdim } 1324263509Sdim } 1325263509Sdim else 1326263509Sdim AnnotateImplicitBridging(Ctx); 1327263509Sdim } 1328263509Sdim else { 1329263509Sdim migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl)); 1330263509Sdim AnnotateImplicitBridging(Ctx); 1331263509Sdim } 1332263509Sdim} 1333263509Sdim 1334263509Sdimvoid ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx, 1335263509Sdim const CallEffects &CE, 1336263509Sdim const FunctionDecl *FuncDecl, 1337263509Sdim bool ResultAnnotated) { 1338263509Sdim // Annotate function. 1339263509Sdim if (!ResultAnnotated) { 1340263509Sdim RetEffect Ret = CE.getReturnValue(); 1341263509Sdim const char *AnnotationString = 0; 1342263509Sdim if (Ret.getObjKind() == RetEffect::CF) { 1343263509Sdim if (Ret.isOwned() && 1344263509Sdim Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition()) 1345263509Sdim AnnotationString = " CF_RETURNS_RETAINED"; 1346263509Sdim else if (Ret.notOwned() && 1347263509Sdim Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition()) 1348263509Sdim AnnotationString = " CF_RETURNS_NOT_RETAINED"; 1349263509Sdim } 1350263509Sdim else if (Ret.getObjKind() == RetEffect::ObjC) { 1351263509Sdim if (Ret.isOwned() && 1352263509Sdim Ctx.Idents.get("NS_RETURNS_RETAINED").hasMacroDefinition()) 1353263509Sdim AnnotationString = " NS_RETURNS_RETAINED"; 1354263509Sdim } 1355263509Sdim 1356263509Sdim if (AnnotationString) { 1357263509Sdim edit::Commit commit(*Editor); 1358263509Sdim commit.insertAfterToken(FuncDecl->getLocEnd(), AnnotationString); 1359263509Sdim Editor->commit(commit); 1360263509Sdim } 1361263509Sdim } 1362263509Sdim llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 1363263509Sdim unsigned i = 0; 1364263509Sdim for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(), 1365263509Sdim pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) { 1366263509Sdim const ParmVarDecl *pd = *pi; 1367263509Sdim ArgEffect AE = AEArgs[i]; 1368263509Sdim if (AE == DecRef && !pd->getAttr<CFConsumedAttr>() && 1369263509Sdim Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) { 1370263509Sdim edit::Commit commit(*Editor); 1371263509Sdim commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); 1372263509Sdim Editor->commit(commit); 1373263509Sdim } 1374263509Sdim else if (AE == DecRefMsg && !pd->getAttr<NSConsumedAttr>() && 1375263509Sdim Ctx.Idents.get("NS_CONSUMED").hasMacroDefinition()) { 1376263509Sdim edit::Commit commit(*Editor); 1377263509Sdim commit.insertBefore(pd->getLocation(), "NS_CONSUMED "); 1378263509Sdim Editor->commit(commit); 1379263509Sdim } 1380263509Sdim } 1381263509Sdim} 1382263509Sdim 1383263509Sdim 1384263509SdimObjCMigrateASTConsumer::CF_BRIDGING_KIND 1385263509Sdim ObjCMigrateASTConsumer::migrateAddFunctionAnnotation( 1386263509Sdim ASTContext &Ctx, 1387263509Sdim const FunctionDecl *FuncDecl) { 1388263509Sdim if (FuncDecl->hasBody()) 1389263509Sdim return CF_BRIDGING_NONE; 1390263509Sdim 1391263509Sdim CallEffects CE = CallEffects::getEffect(FuncDecl); 1392263509Sdim bool FuncIsReturnAnnotated = (FuncDecl->getAttr<CFReturnsRetainedAttr>() || 1393263509Sdim FuncDecl->getAttr<CFReturnsNotRetainedAttr>() || 1394263509Sdim FuncDecl->getAttr<NSReturnsRetainedAttr>() || 1395263509Sdim FuncDecl->getAttr<NSReturnsNotRetainedAttr>() || 1396263509Sdim FuncDecl->getAttr<NSReturnsAutoreleasedAttr>()); 1397263509Sdim 1398263509Sdim // Trivial case of when funciton is annotated and has no argument. 1399263509Sdim if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0) 1400263509Sdim return CF_BRIDGING_NONE; 1401263509Sdim 1402263509Sdim bool ReturnCFAudited = false; 1403263509Sdim if (!FuncIsReturnAnnotated) { 1404263509Sdim RetEffect Ret = CE.getReturnValue(); 1405263509Sdim if (Ret.getObjKind() == RetEffect::CF && 1406263509Sdim (Ret.isOwned() || Ret.notOwned())) 1407263509Sdim ReturnCFAudited = true; 1408263509Sdim else if (!AuditedType(FuncDecl->getResultType())) 1409263509Sdim return CF_BRIDGING_NONE; 1410263509Sdim } 1411263509Sdim 1412263509Sdim // At this point result type is audited for potential inclusion. 1413263509Sdim // Now, how about argument types. 1414263509Sdim llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 1415263509Sdim unsigned i = 0; 1416263509Sdim bool ArgCFAudited = false; 1417263509Sdim for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(), 1418263509Sdim pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) { 1419263509Sdim const ParmVarDecl *pd = *pi; 1420263509Sdim ArgEffect AE = AEArgs[i]; 1421263509Sdim if (AE == DecRef /*CFConsumed annotated*/ || AE == IncRef) { 1422263509Sdim if (AE == DecRef && !pd->getAttr<CFConsumedAttr>()) 1423263509Sdim ArgCFAudited = true; 1424263509Sdim else if (AE == IncRef) 1425263509Sdim ArgCFAudited = true; 1426263509Sdim } 1427263509Sdim else { 1428263509Sdim QualType AT = pd->getType(); 1429263509Sdim if (!AuditedType(AT)) { 1430263509Sdim AddCFAnnotations(Ctx, CE, FuncDecl, FuncIsReturnAnnotated); 1431263509Sdim return CF_BRIDGING_NONE; 1432263509Sdim } 1433263509Sdim } 1434263509Sdim } 1435263509Sdim if (ReturnCFAudited || ArgCFAudited) 1436263509Sdim return CF_BRIDGING_ENABLE; 1437263509Sdim 1438263509Sdim return CF_BRIDGING_MAY_INCLUDE; 1439263509Sdim} 1440263509Sdim 1441263509Sdimvoid ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx, 1442263509Sdim ObjCContainerDecl *CDecl) { 1443263509Sdim if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated()) 1444263509Sdim return; 1445263509Sdim 1446263509Sdim // migrate methods which can have instancetype as their result type. 1447263509Sdim for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(), 1448263509Sdim MEnd = CDecl->meth_end(); 1449263509Sdim M != MEnd; ++M) { 1450263509Sdim ObjCMethodDecl *Method = (*M); 1451263509Sdim migrateCFAnnotation(Ctx, Method); 1452263509Sdim } 1453263509Sdim} 1454263509Sdim 1455263509Sdimvoid ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx, 1456263509Sdim const CallEffects &CE, 1457263509Sdim const ObjCMethodDecl *MethodDecl, 1458263509Sdim bool ResultAnnotated) { 1459263509Sdim // Annotate function. 1460263509Sdim if (!ResultAnnotated) { 1461263509Sdim RetEffect Ret = CE.getReturnValue(); 1462263509Sdim const char *AnnotationString = 0; 1463263509Sdim if (Ret.getObjKind() == RetEffect::CF) { 1464263509Sdim if (Ret.isOwned() && 1465263509Sdim Ctx.Idents.get("CF_RETURNS_RETAINED").hasMacroDefinition()) 1466263509Sdim AnnotationString = " CF_RETURNS_RETAINED"; 1467263509Sdim else if (Ret.notOwned() && 1468263509Sdim Ctx.Idents.get("CF_RETURNS_NOT_RETAINED").hasMacroDefinition()) 1469263509Sdim AnnotationString = " CF_RETURNS_NOT_RETAINED"; 1470263509Sdim } 1471263509Sdim else if (Ret.getObjKind() == RetEffect::ObjC) { 1472263509Sdim ObjCMethodFamily OMF = MethodDecl->getMethodFamily(); 1473263509Sdim switch (OMF) { 1474263509Sdim case clang::OMF_alloc: 1475263509Sdim case clang::OMF_new: 1476263509Sdim case clang::OMF_copy: 1477263509Sdim case clang::OMF_init: 1478263509Sdim case clang::OMF_mutableCopy: 1479263509Sdim break; 1480263509Sdim 1481263509Sdim default: 1482263509Sdim if (Ret.isOwned() && 1483263509Sdim Ctx.Idents.get("NS_RETURNS_RETAINED").hasMacroDefinition()) 1484263509Sdim AnnotationString = " NS_RETURNS_RETAINED"; 1485263509Sdim break; 1486263509Sdim } 1487263509Sdim } 1488263509Sdim 1489263509Sdim if (AnnotationString) { 1490263509Sdim edit::Commit commit(*Editor); 1491263509Sdim commit.insertBefore(MethodDecl->getLocEnd(), AnnotationString); 1492263509Sdim Editor->commit(commit); 1493263509Sdim } 1494263509Sdim } 1495263509Sdim llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 1496263509Sdim unsigned i = 0; 1497263509Sdim for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), 1498263509Sdim pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { 1499263509Sdim const ParmVarDecl *pd = *pi; 1500263509Sdim ArgEffect AE = AEArgs[i]; 1501263509Sdim if (AE == DecRef && !pd->getAttr<CFConsumedAttr>() && 1502263509Sdim Ctx.Idents.get("CF_CONSUMED").hasMacroDefinition()) { 1503263509Sdim edit::Commit commit(*Editor); 1504263509Sdim commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); 1505263509Sdim Editor->commit(commit); 1506263509Sdim } 1507263509Sdim } 1508263509Sdim} 1509263509Sdim 1510263509Sdimvoid ObjCMigrateASTConsumer::migrateAddMethodAnnotation( 1511263509Sdim ASTContext &Ctx, 1512263509Sdim const ObjCMethodDecl *MethodDecl) { 1513263509Sdim if (MethodDecl->hasBody() || MethodDecl->isImplicit()) 1514263509Sdim return; 1515263509Sdim 1516263509Sdim CallEffects CE = CallEffects::getEffect(MethodDecl); 1517263509Sdim bool MethodIsReturnAnnotated = (MethodDecl->getAttr<CFReturnsRetainedAttr>() || 1518263509Sdim MethodDecl->getAttr<CFReturnsNotRetainedAttr>() || 1519263509Sdim MethodDecl->getAttr<NSReturnsRetainedAttr>() || 1520263509Sdim MethodDecl->getAttr<NSReturnsNotRetainedAttr>() || 1521263509Sdim MethodDecl->getAttr<NSReturnsAutoreleasedAttr>()); 1522263509Sdim 1523263509Sdim if (CE.getReceiver() == DecRefMsg && 1524263509Sdim !MethodDecl->getAttr<NSConsumesSelfAttr>() && 1525263509Sdim MethodDecl->getMethodFamily() != OMF_init && 1526263509Sdim MethodDecl->getMethodFamily() != OMF_release && 1527263509Sdim Ctx.Idents.get("NS_CONSUMES_SELF").hasMacroDefinition()) { 1528263509Sdim edit::Commit commit(*Editor); 1529263509Sdim commit.insertBefore(MethodDecl->getLocEnd(), " NS_CONSUMES_SELF"); 1530263509Sdim Editor->commit(commit); 1531263509Sdim } 1532263509Sdim 1533263509Sdim // Trivial case of when funciton is annotated and has no argument. 1534263509Sdim if (MethodIsReturnAnnotated && 1535263509Sdim (MethodDecl->param_begin() == MethodDecl->param_end())) 1536263509Sdim return; 1537263509Sdim 1538263509Sdim if (!MethodIsReturnAnnotated) { 1539263509Sdim RetEffect Ret = CE.getReturnValue(); 1540263509Sdim if ((Ret.getObjKind() == RetEffect::CF || 1541263509Sdim Ret.getObjKind() == RetEffect::ObjC) && 1542263509Sdim (Ret.isOwned() || Ret.notOwned())) { 1543263509Sdim AddCFAnnotations(Ctx, CE, MethodDecl, false); 1544263509Sdim return; 1545263509Sdim } 1546263509Sdim else if (!AuditedType(MethodDecl->getResultType())) 1547263509Sdim return; 1548263509Sdim } 1549263509Sdim 1550263509Sdim // At this point result type is either annotated or audited. 1551263509Sdim // Now, how about argument types. 1552263509Sdim llvm::ArrayRef<ArgEffect> AEArgs = CE.getArgs(); 1553263509Sdim unsigned i = 0; 1554263509Sdim for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), 1555263509Sdim pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { 1556263509Sdim const ParmVarDecl *pd = *pi; 1557263509Sdim ArgEffect AE = AEArgs[i]; 1558263509Sdim if ((AE == DecRef && !pd->getAttr<CFConsumedAttr>()) || AE == IncRef || 1559263509Sdim !AuditedType(pd->getType())) { 1560263509Sdim AddCFAnnotations(Ctx, CE, MethodDecl, MethodIsReturnAnnotated); 1561263509Sdim return; 1562263509Sdim } 1563263509Sdim } 1564263509Sdim return; 1565263509Sdim} 1566263509Sdim 1567234287Sdimnamespace { 1568234287Sdim 1569234287Sdimclass RewritesReceiver : public edit::EditsReceiver { 1570234287Sdim Rewriter &Rewrite; 1571234287Sdim 1572234287Sdimpublic: 1573234287Sdim RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } 1574234287Sdim 1575234287Sdim virtual void insert(SourceLocation loc, StringRef text) { 1576234287Sdim Rewrite.InsertText(loc, text); 1577234287Sdim } 1578234287Sdim virtual void replace(CharSourceRange range, StringRef text) { 1579234287Sdim Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); 1580234287Sdim } 1581234287Sdim}; 1582234287Sdim 1583234287Sdim} 1584234287Sdim 1585263509Sdimstatic bool 1586263509SdimIsReallyASystemHeader(ASTContext &Ctx, const FileEntry *file, FileID FID) { 1587263509Sdim bool Invalid = false; 1588263509Sdim const SrcMgr::SLocEntry &SEntry = 1589263509Sdim Ctx.getSourceManager().getSLocEntry(FID, &Invalid); 1590263509Sdim if (!Invalid && SEntry.isFile()) { 1591263509Sdim const SrcMgr::FileInfo &FI = SEntry.getFile(); 1592263509Sdim if (!FI.hasLineDirectives()) { 1593263509Sdim if (FI.getFileCharacteristic() == SrcMgr::C_ExternCSystem) 1594263509Sdim return true; 1595263509Sdim if (FI.getFileCharacteristic() == SrcMgr::C_System) { 1596263509Sdim // This file is in a system header directory. Continue with commiting change 1597263509Sdim // only if it is a user specified system directory because user put a 1598263509Sdim // .system_framework file in the framework directory. 1599263509Sdim StringRef Directory(file->getDir()->getName()); 1600263509Sdim size_t Ix = Directory.rfind(".framework"); 1601263509Sdim if (Ix == StringRef::npos) 1602263509Sdim return true; 1603263509Sdim std::string PatchToSystemFramework = Directory.slice(0, Ix+sizeof(".framework")); 1604263509Sdim PatchToSystemFramework += ".system_framework"; 1605263509Sdim if (!llvm::sys::fs::exists(PatchToSystemFramework.data())) 1606263509Sdim return true; 1607263509Sdim } 1608263509Sdim } 1609263509Sdim } 1610263509Sdim return false; 1611263509Sdim} 1612263509Sdim 1613234287Sdimvoid ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { 1614263509Sdim 1615263509Sdim TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl(); 1616263509Sdim if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) { 1617263509Sdim for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end(); 1618263509Sdim D != DEnd; ++D) { 1619263509Sdim FileID FID = PP.getSourceManager().getFileID((*D)->getLocation()); 1620263509Sdim if (!FID.isInvalid()) 1621263509Sdim if (!FileId.isInvalid() && FileId != FID) { 1622263509Sdim if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 1623263509Sdim AnnotateImplicitBridging(Ctx); 1624263509Sdim } 1625263509Sdim 1626263509Sdim if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D)) 1627263509Sdim migrateObjCInterfaceDecl(Ctx, CDecl); 1628263509Sdim if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) { 1629263509Sdim migrateObjCInterfaceDecl(Ctx, CatDecl); 1630263509Sdim if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 1631263509Sdim migrateDeprecatedAnnotation(Ctx, CatDecl); 1632263509Sdim } 1633263509Sdim else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) 1634263509Sdim ObjCProtocolDecls.insert(PDecl); 1635263509Sdim else if (const ObjCImplementationDecl *ImpDecl = 1636263509Sdim dyn_cast<ObjCImplementationDecl>(*D)) { 1637263509Sdim if (ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance) 1638263509Sdim migrateProtocolConformance(Ctx, ImpDecl); 1639263509Sdim } 1640263509Sdim else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) { 1641263509Sdim if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros)) 1642263509Sdim continue; 1643263509Sdim DeclContext::decl_iterator N = D; 1644263509Sdim if (++N != DEnd) { 1645263509Sdim const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N); 1646263509Sdim if (migrateNSEnumDecl(Ctx, ED, TD) && TD) 1647263509Sdim D++; 1648263509Sdim } 1649263509Sdim else 1650263509Sdim migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */0); 1651263509Sdim } 1652263509Sdim else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) { 1653263509Sdim if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros)) 1654263509Sdim continue; 1655263509Sdim DeclContext::decl_iterator N = D; 1656263509Sdim if (++N == DEnd) 1657263509Sdim continue; 1658263509Sdim if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) { 1659263509Sdim if (++N != DEnd) 1660263509Sdim if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) { 1661263509Sdim // prefer typedef-follows-enum to enum-follows-typedef pattern. 1662263509Sdim if (migrateNSEnumDecl(Ctx, ED, TDF)) { 1663263509Sdim ++D; ++D; 1664263509Sdim CacheObjCNSIntegerTypedefed(TD); 1665263509Sdim continue; 1666263509Sdim } 1667263509Sdim } 1668263509Sdim if (migrateNSEnumDecl(Ctx, ED, TD)) { 1669263509Sdim ++D; 1670263509Sdim continue; 1671263509Sdim } 1672263509Sdim } 1673263509Sdim CacheObjCNSIntegerTypedefed(TD); 1674263509Sdim } 1675263509Sdim else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) { 1676263509Sdim if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 1677263509Sdim migrateCFAnnotation(Ctx, FD); 1678263509Sdim } 1679263509Sdim 1680263509Sdim if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) { 1681263509Sdim // migrate methods which can have instancetype as their result type. 1682263509Sdim if (ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype) 1683263509Sdim migrateAllMethodInstaceType(Ctx, CDecl); 1684263509Sdim // annotate methods with CF annotations. 1685263509Sdim if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 1686263509Sdim migrateARCSafeAnnotation(Ctx, CDecl); 1687263509Sdim } 1688263509Sdim } 1689263509Sdim if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 1690263509Sdim AnnotateImplicitBridging(Ctx); 1691263509Sdim } 1692263509Sdim 1693234287Sdim Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); 1694234287Sdim RewritesReceiver Rec(rewriter); 1695234287Sdim Editor->applyRewrites(Rec); 1696234287Sdim 1697234287Sdim for (Rewriter::buffer_iterator 1698234287Sdim I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { 1699234287Sdim FileID FID = I->first; 1700234287Sdim RewriteBuffer &buf = I->second; 1701234287Sdim const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); 1702234287Sdim assert(file); 1703263509Sdim if (IsReallyASystemHeader(Ctx, file, FID)) 1704263509Sdim continue; 1705263509Sdim if (!canModifyFile(file->getName())) 1706263509Sdim continue; 1707252723Sdim SmallString<512> newText; 1708234287Sdim llvm::raw_svector_ostream vecOS(newText); 1709234287Sdim buf.write(vecOS); 1710234287Sdim vecOS.flush(); 1711234287Sdim llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy( 1712234287Sdim StringRef(newText.data(), newText.size()), file->getName()); 1713252723Sdim SmallString<64> filePath(file->getName()); 1714234287Sdim FileMgr.FixupRelativePath(filePath); 1715234287Sdim Remapper.remap(filePath.str(), memBuf); 1716234287Sdim } 1717234287Sdim 1718234287Sdim if (IsOutputFile) { 1719234287Sdim Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics()); 1720234287Sdim } else { 1721234287Sdim Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics()); 1722234287Sdim } 1723234287Sdim} 1724234287Sdim 1725234287Sdimbool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) { 1726245431Sdim CI.getDiagnostics().setIgnoreAllWarnings(true); 1727234287Sdim return true; 1728234287Sdim} 1729234287Sdim 1730263509Sdimstatic std::vector<std::string> getWhiteListFilenames(StringRef DirPath) { 1731263509Sdim using namespace llvm::sys::fs; 1732263509Sdim using namespace llvm::sys::path; 1733263509Sdim 1734263509Sdim std::vector<std::string> Filenames; 1735263509Sdim if (DirPath.empty() || !is_directory(DirPath)) 1736263509Sdim return Filenames; 1737263509Sdim 1738263509Sdim llvm::error_code EC; 1739263509Sdim directory_iterator DI = directory_iterator(DirPath, EC); 1740263509Sdim directory_iterator DE; 1741263509Sdim for (; !EC && DI != DE; DI = DI.increment(EC)) { 1742263509Sdim if (is_regular_file(DI->path())) 1743263509Sdim Filenames.push_back(filename(DI->path())); 1744263509Sdim } 1745263509Sdim 1746263509Sdim return Filenames; 1747263509Sdim} 1748263509Sdim 1749234287SdimASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, 1750234287Sdim StringRef InFile) { 1751252723Sdim PPConditionalDirectiveRecord * 1752252723Sdim PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager()); 1753263509Sdim unsigned ObjCMTAction = CI.getFrontendOpts().ObjCMTAction; 1754263509Sdim unsigned ObjCMTOpts = ObjCMTAction; 1755263509Sdim // These are companion flags, they do not enable transformations. 1756263509Sdim ObjCMTOpts &= ~(FrontendOptions::ObjCMT_AtomicProperty | 1757263509Sdim FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty); 1758263509Sdim if (ObjCMTOpts == FrontendOptions::ObjCMT_None) { 1759263509Sdim // If no specific option was given, enable literals+subscripting transforms 1760263509Sdim // by default. 1761263509Sdim ObjCMTAction |= FrontendOptions::ObjCMT_Literals | 1762263509Sdim FrontendOptions::ObjCMT_Subscripting; 1763263509Sdim } 1764252723Sdim CI.getPreprocessor().addPPCallbacks(PPRec); 1765263509Sdim std::vector<std::string> WhiteList = 1766263509Sdim getWhiteListFilenames(CI.getFrontendOpts().ObjCMTWhiteListPath); 1767234287Sdim return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile, 1768263509Sdim ObjCMTAction, 1769234287Sdim Remapper, 1770234287Sdim CI.getFileManager(), 1771252723Sdim PPRec, 1772263509Sdim CI.getPreprocessor(), 1773263509Sdim /*isOutputFile=*/true, 1774263509Sdim WhiteList); 1775234287Sdim} 1776