10b57cec5SDimitry Andric //===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric 90b57cec5SDimitry Andric #include "Transforms.h" 100b57cec5SDimitry Andric #include "clang/Analysis/RetainSummaryManager.h" 110b57cec5SDimitry Andric #include "clang/ARCMigrate/ARCMT.h" 120b57cec5SDimitry Andric #include "clang/ARCMigrate/ARCMTActions.h" 130b57cec5SDimitry Andric #include "clang/AST/ASTConsumer.h" 140b57cec5SDimitry Andric #include "clang/AST/ASTContext.h" 150b57cec5SDimitry Andric #include "clang/AST/Attr.h" 160b57cec5SDimitry Andric #include "clang/AST/NSAPI.h" 170b57cec5SDimitry Andric #include "clang/AST/ParentMap.h" 180b57cec5SDimitry Andric #include "clang/AST/RecursiveASTVisitor.h" 190b57cec5SDimitry Andric #include "clang/Analysis/DomainSpecific/CocoaConventions.h" 200b57cec5SDimitry Andric #include "clang/Basic/FileManager.h" 210b57cec5SDimitry Andric #include "clang/Edit/Commit.h" 220b57cec5SDimitry Andric #include "clang/Edit/EditedSource.h" 230b57cec5SDimitry Andric #include "clang/Edit/EditsReceiver.h" 240b57cec5SDimitry Andric #include "clang/Edit/Rewriters.h" 250b57cec5SDimitry Andric #include "clang/Frontend/CompilerInstance.h" 260b57cec5SDimitry Andric #include "clang/Frontend/MultiplexConsumer.h" 270b57cec5SDimitry Andric #include "clang/Lex/PPConditionalDirectiveRecord.h" 280b57cec5SDimitry Andric #include "clang/Lex/Preprocessor.h" 290b57cec5SDimitry Andric #include "clang/Rewrite/Core/Rewriter.h" 300b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 310b57cec5SDimitry Andric #include "llvm/ADT/StringSet.h" 320b57cec5SDimitry Andric #include "llvm/Support/Path.h" 330b57cec5SDimitry Andric #include "llvm/Support/SourceMgr.h" 340b57cec5SDimitry Andric #include "llvm/Support/YAMLParser.h" 350b57cec5SDimitry Andric 360b57cec5SDimitry Andric using namespace clang; 370b57cec5SDimitry Andric using namespace arcmt; 380b57cec5SDimitry Andric using namespace ento; 390b57cec5SDimitry Andric 400b57cec5SDimitry Andric namespace { 410b57cec5SDimitry Andric 420b57cec5SDimitry Andric class ObjCMigrateASTConsumer : public ASTConsumer { 430b57cec5SDimitry Andric enum CF_BRIDGING_KIND { 440b57cec5SDimitry Andric CF_BRIDGING_NONE, 450b57cec5SDimitry Andric CF_BRIDGING_ENABLE, 460b57cec5SDimitry Andric CF_BRIDGING_MAY_INCLUDE 470b57cec5SDimitry Andric }; 480b57cec5SDimitry Andric 490b57cec5SDimitry Andric void migrateDecl(Decl *D); 500b57cec5SDimitry Andric void migrateObjCContainerDecl(ASTContext &Ctx, ObjCContainerDecl *D); 510b57cec5SDimitry Andric void migrateProtocolConformance(ASTContext &Ctx, 520b57cec5SDimitry Andric const ObjCImplementationDecl *ImpDecl); 530b57cec5SDimitry Andric void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl); 540b57cec5SDimitry Andric bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl, 550b57cec5SDimitry Andric const TypedefDecl *TypedefDcl); 560b57cec5SDimitry Andric void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl); 570b57cec5SDimitry Andric void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl, 580b57cec5SDimitry Andric ObjCMethodDecl *OM); 590b57cec5SDimitry Andric bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM); 600b57cec5SDimitry Andric void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM); 610b57cec5SDimitry Andric void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P); 620b57cec5SDimitry Andric void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl, 630b57cec5SDimitry Andric ObjCMethodDecl *OM, 640b57cec5SDimitry Andric ObjCInstanceTypeFamily OIT_Family = OIT_None); 650b57cec5SDimitry Andric 660b57cec5SDimitry Andric void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl); 670b57cec5SDimitry Andric void AddCFAnnotations(ASTContext &Ctx, 680b57cec5SDimitry Andric const RetainSummary *RS, 690b57cec5SDimitry Andric const FunctionDecl *FuncDecl, bool ResultAnnotated); 700b57cec5SDimitry Andric void AddCFAnnotations(ASTContext &Ctx, 710b57cec5SDimitry Andric const RetainSummary *RS, 720b57cec5SDimitry Andric const ObjCMethodDecl *MethodDecl, bool ResultAnnotated); 730b57cec5SDimitry Andric 740b57cec5SDimitry Andric void AnnotateImplicitBridging(ASTContext &Ctx); 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx, 770b57cec5SDimitry Andric const FunctionDecl *FuncDecl); 780b57cec5SDimitry Andric 790b57cec5SDimitry Andric void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl); 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric void migrateAddMethodAnnotation(ASTContext &Ctx, 820b57cec5SDimitry Andric const ObjCMethodDecl *MethodDecl); 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric void inferDesignatedInitializers(ASTContext &Ctx, 850b57cec5SDimitry Andric const ObjCImplementationDecl *ImplD); 860b57cec5SDimitry Andric 870b57cec5SDimitry Andric bool InsertFoundation(ASTContext &Ctx, SourceLocation Loc); 880b57cec5SDimitry Andric 890b57cec5SDimitry Andric std::unique_ptr<RetainSummaryManager> Summaries; 900b57cec5SDimitry Andric 910b57cec5SDimitry Andric public: 920b57cec5SDimitry Andric std::string MigrateDir; 930b57cec5SDimitry Andric unsigned ASTMigrateActions; 940b57cec5SDimitry Andric FileID FileId; 950b57cec5SDimitry Andric const TypedefDecl *NSIntegerTypedefed; 960b57cec5SDimitry Andric const TypedefDecl *NSUIntegerTypedefed; 970b57cec5SDimitry Andric std::unique_ptr<NSAPI> NSAPIObj; 980b57cec5SDimitry Andric std::unique_ptr<edit::EditedSource> Editor; 990b57cec5SDimitry Andric FileRemapper &Remapper; 1000b57cec5SDimitry Andric FileManager &FileMgr; 1010b57cec5SDimitry Andric const PPConditionalDirectiveRecord *PPRec; 1020b57cec5SDimitry Andric Preprocessor &PP; 1030b57cec5SDimitry Andric bool IsOutputFile; 1040b57cec5SDimitry Andric bool FoundationIncluded; 1050b57cec5SDimitry Andric llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls; 1060b57cec5SDimitry Andric llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates; 107349cc55cSDimitry Andric llvm::StringSet<> AllowListFilenames; 1080b57cec5SDimitry Andric 1090b57cec5SDimitry Andric RetainSummaryManager &getSummaryManager(ASTContext &Ctx) { 1100b57cec5SDimitry Andric if (!Summaries) 1110b57cec5SDimitry Andric Summaries.reset(new RetainSummaryManager(Ctx, 1120b57cec5SDimitry Andric /*TrackNSCFObjects=*/true, 1130b57cec5SDimitry Andric /*trackOSObjects=*/false)); 1140b57cec5SDimitry Andric return *Summaries; 1150b57cec5SDimitry Andric } 1160b57cec5SDimitry Andric 1175ffd83dbSDimitry Andric ObjCMigrateASTConsumer(StringRef migrateDir, unsigned astMigrateActions, 1185ffd83dbSDimitry Andric FileRemapper &remapper, FileManager &fileMgr, 1190b57cec5SDimitry Andric const PPConditionalDirectiveRecord *PPRec, 1205ffd83dbSDimitry Andric Preprocessor &PP, bool isOutputFile, 121349cc55cSDimitry Andric ArrayRef<std::string> AllowList) 1225ffd83dbSDimitry Andric : MigrateDir(migrateDir), ASTMigrateActions(astMigrateActions), 1230b57cec5SDimitry Andric NSIntegerTypedefed(nullptr), NSUIntegerTypedefed(nullptr), 1240b57cec5SDimitry Andric Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP), 1255ffd83dbSDimitry Andric IsOutputFile(isOutputFile), FoundationIncluded(false) { 1265f757f3fSDimitry Andric AllowListFilenames.insert(AllowList.begin(), AllowList.end()); 1270b57cec5SDimitry Andric } 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric protected: 1300b57cec5SDimitry Andric void Initialize(ASTContext &Context) override { 1310b57cec5SDimitry Andric NSAPIObj.reset(new NSAPI(Context)); 1320b57cec5SDimitry Andric Editor.reset(new edit::EditedSource(Context.getSourceManager(), 1330b57cec5SDimitry Andric Context.getLangOpts(), 1340b57cec5SDimitry Andric PPRec)); 1350b57cec5SDimitry Andric } 1360b57cec5SDimitry Andric 1370b57cec5SDimitry Andric bool HandleTopLevelDecl(DeclGroupRef DG) override { 1380b57cec5SDimitry Andric for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) 1390b57cec5SDimitry Andric migrateDecl(*I); 1400b57cec5SDimitry Andric return true; 1410b57cec5SDimitry Andric } 1420b57cec5SDimitry Andric void HandleInterestingDecl(DeclGroupRef DG) override { 1430b57cec5SDimitry Andric // Ignore decls from the PCH. 1440b57cec5SDimitry Andric } 1450b57cec5SDimitry Andric void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override { 1460b57cec5SDimitry Andric ObjCMigrateASTConsumer::HandleTopLevelDecl(DG); 1470b57cec5SDimitry Andric } 1480b57cec5SDimitry Andric 1490b57cec5SDimitry Andric void HandleTranslationUnit(ASTContext &Ctx) override; 1500b57cec5SDimitry Andric 1510b57cec5SDimitry Andric bool canModifyFile(StringRef Path) { 152349cc55cSDimitry Andric if (AllowListFilenames.empty()) 1530b57cec5SDimitry Andric return true; 15406c3fb27SDimitry Andric return AllowListFilenames.contains(llvm::sys::path::filename(Path)); 1550b57cec5SDimitry Andric } 156bdd1243dSDimitry Andric bool canModifyFile(OptionalFileEntryRef FE) { 1570b57cec5SDimitry Andric if (!FE) 1580b57cec5SDimitry Andric return false; 1590b57cec5SDimitry Andric return canModifyFile(FE->getName()); 1600b57cec5SDimitry Andric } 1610b57cec5SDimitry Andric bool canModifyFile(FileID FID) { 1620b57cec5SDimitry Andric if (FID.isInvalid()) 1630b57cec5SDimitry Andric return false; 164e8d8bef9SDimitry Andric return canModifyFile(PP.getSourceManager().getFileEntryRefForID(FID)); 1650b57cec5SDimitry Andric } 1660b57cec5SDimitry Andric 1670b57cec5SDimitry Andric bool canModify(const Decl *D) { 1680b57cec5SDimitry Andric if (!D) 1690b57cec5SDimitry Andric return false; 1700b57cec5SDimitry Andric if (const ObjCCategoryImplDecl *CatImpl = dyn_cast<ObjCCategoryImplDecl>(D)) 1710b57cec5SDimitry Andric return canModify(CatImpl->getCategoryDecl()); 1720b57cec5SDimitry Andric if (const ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(D)) 1730b57cec5SDimitry Andric return canModify(Impl->getClassInterface()); 1740b57cec5SDimitry Andric if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) 1750b57cec5SDimitry Andric return canModify(cast<Decl>(MD->getDeclContext())); 1760b57cec5SDimitry Andric 1770b57cec5SDimitry Andric FileID FID = PP.getSourceManager().getFileID(D->getLocation()); 1780b57cec5SDimitry Andric return canModifyFile(FID); 1790b57cec5SDimitry Andric } 1800b57cec5SDimitry Andric }; 1810b57cec5SDimitry Andric 1820b57cec5SDimitry Andric } // end anonymous namespace 1830b57cec5SDimitry Andric 1840b57cec5SDimitry Andric ObjCMigrateAction::ObjCMigrateAction( 1855ffd83dbSDimitry Andric std::unique_ptr<FrontendAction> WrappedAction, StringRef migrateDir, 1860b57cec5SDimitry Andric unsigned migrateAction) 1870b57cec5SDimitry Andric : WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir), 1885ffd83dbSDimitry Andric ObjCMigAction(migrateAction), CompInst(nullptr) { 1890b57cec5SDimitry Andric if (MigrateDir.empty()) 1900b57cec5SDimitry Andric MigrateDir = "."; // user current directory if none is given. 1910b57cec5SDimitry Andric } 1920b57cec5SDimitry Andric 1930b57cec5SDimitry Andric std::unique_ptr<ASTConsumer> 1940b57cec5SDimitry Andric ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 1950b57cec5SDimitry Andric PPConditionalDirectiveRecord * 1960b57cec5SDimitry Andric PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager()); 1970b57cec5SDimitry Andric CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec)); 1980b57cec5SDimitry Andric std::vector<std::unique_ptr<ASTConsumer>> Consumers; 1990b57cec5SDimitry Andric Consumers.push_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile)); 200a7dea167SDimitry Andric Consumers.push_back(std::make_unique<ObjCMigrateASTConsumer>( 2010b57cec5SDimitry Andric MigrateDir, ObjCMigAction, Remapper, CompInst->getFileManager(), PPRec, 202bdd1243dSDimitry Andric CompInst->getPreprocessor(), false, std::nullopt)); 203a7dea167SDimitry Andric return std::make_unique<MultiplexConsumer>(std::move(Consumers)); 2040b57cec5SDimitry Andric } 2050b57cec5SDimitry Andric 2060b57cec5SDimitry Andric bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) { 2070b57cec5SDimitry Andric Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(), 2080b57cec5SDimitry Andric /*ignoreIfFilesChanged=*/true); 2090b57cec5SDimitry Andric CompInst = &CI; 2100b57cec5SDimitry Andric CI.getDiagnostics().setIgnoreAllWarnings(true); 2110b57cec5SDimitry Andric return true; 2120b57cec5SDimitry Andric } 2130b57cec5SDimitry Andric 2140b57cec5SDimitry Andric namespace { 2150b57cec5SDimitry Andric // FIXME. This duplicates one in RewriteObjCFoundationAPI.cpp 2160b57cec5SDimitry Andric bool subscriptOperatorNeedsParens(const Expr *FullExpr) { 2170b57cec5SDimitry Andric const Expr* Expr = FullExpr->IgnoreImpCasts(); 2180b57cec5SDimitry Andric return !(isa<ArraySubscriptExpr>(Expr) || isa<CallExpr>(Expr) || 2190b57cec5SDimitry Andric isa<DeclRefExpr>(Expr) || isa<CXXNamedCastExpr>(Expr) || 2200b57cec5SDimitry Andric isa<CXXConstructExpr>(Expr) || isa<CXXThisExpr>(Expr) || 2210b57cec5SDimitry Andric isa<CXXTypeidExpr>(Expr) || 2220b57cec5SDimitry Andric isa<CXXUnresolvedConstructExpr>(Expr) || 2230b57cec5SDimitry Andric isa<ObjCMessageExpr>(Expr) || isa<ObjCPropertyRefExpr>(Expr) || 2240b57cec5SDimitry Andric isa<ObjCProtocolExpr>(Expr) || isa<MemberExpr>(Expr) || 2250b57cec5SDimitry Andric isa<ObjCIvarRefExpr>(Expr) || isa<ParenExpr>(FullExpr) || 2260b57cec5SDimitry Andric isa<ParenListExpr>(Expr) || isa<SizeOfPackExpr>(Expr)); 2270b57cec5SDimitry Andric } 2280b57cec5SDimitry Andric 2290b57cec5SDimitry Andric /// - Rewrite message expression for Objective-C setter and getters into 2300b57cec5SDimitry Andric /// property-dot syntax. 2310b57cec5SDimitry Andric bool rewriteToPropertyDotSyntax(const ObjCMessageExpr *Msg, 2320b57cec5SDimitry Andric Preprocessor &PP, 2330b57cec5SDimitry Andric const NSAPI &NS, edit::Commit &commit, 2340b57cec5SDimitry Andric const ParentMap *PMap) { 2350b57cec5SDimitry Andric if (!Msg || Msg->isImplicit() || 2360b57cec5SDimitry Andric (Msg->getReceiverKind() != ObjCMessageExpr::Instance && 2370b57cec5SDimitry Andric Msg->getReceiverKind() != ObjCMessageExpr::SuperInstance)) 2380b57cec5SDimitry Andric return false; 2390b57cec5SDimitry Andric if (const Expr *Receiver = Msg->getInstanceReceiver()) 2400b57cec5SDimitry Andric if (Receiver->getType()->isObjCBuiltinType()) 2410b57cec5SDimitry Andric return false; 2420b57cec5SDimitry Andric 2430b57cec5SDimitry Andric const ObjCMethodDecl *Method = Msg->getMethodDecl(); 2440b57cec5SDimitry Andric if (!Method) 2450b57cec5SDimitry Andric return false; 2460b57cec5SDimitry Andric if (!Method->isPropertyAccessor()) 2470b57cec5SDimitry Andric return false; 2480b57cec5SDimitry Andric 2490b57cec5SDimitry Andric const ObjCPropertyDecl *Prop = Method->findPropertyDecl(); 2500b57cec5SDimitry Andric if (!Prop) 2510b57cec5SDimitry Andric return false; 2520b57cec5SDimitry Andric 2530b57cec5SDimitry Andric SourceRange MsgRange = Msg->getSourceRange(); 2540b57cec5SDimitry Andric bool ReceiverIsSuper = 2550b57cec5SDimitry Andric (Msg->getReceiverKind() == ObjCMessageExpr::SuperInstance); 2560b57cec5SDimitry Andric // for 'super' receiver is nullptr. 2570b57cec5SDimitry Andric const Expr *receiver = Msg->getInstanceReceiver(); 2580b57cec5SDimitry Andric bool NeedsParen = 2590b57cec5SDimitry Andric ReceiverIsSuper ? false : subscriptOperatorNeedsParens(receiver); 2600b57cec5SDimitry Andric bool IsGetter = (Msg->getNumArgs() == 0); 2610b57cec5SDimitry Andric if (IsGetter) { 2620b57cec5SDimitry Andric // Find space location range between receiver expression and getter method. 2630b57cec5SDimitry Andric SourceLocation BegLoc = 2640b57cec5SDimitry Andric ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc(); 2650b57cec5SDimitry Andric BegLoc = PP.getLocForEndOfToken(BegLoc); 2660b57cec5SDimitry Andric SourceLocation EndLoc = Msg->getSelectorLoc(0); 2670b57cec5SDimitry Andric SourceRange SpaceRange(BegLoc, EndLoc); 2680b57cec5SDimitry Andric std::string PropertyDotString; 2690b57cec5SDimitry Andric // rewrite getter method expression into: receiver.property or 2700b57cec5SDimitry Andric // (receiver).property 2710b57cec5SDimitry Andric if (NeedsParen) { 2720b57cec5SDimitry Andric commit.insertBefore(receiver->getBeginLoc(), "("); 2730b57cec5SDimitry Andric PropertyDotString = ")."; 2740b57cec5SDimitry Andric } 2750b57cec5SDimitry Andric else 2760b57cec5SDimitry Andric PropertyDotString = "."; 2770b57cec5SDimitry Andric PropertyDotString += Prop->getName(); 2780b57cec5SDimitry Andric commit.replace(SpaceRange, PropertyDotString); 2790b57cec5SDimitry Andric 2800b57cec5SDimitry Andric // remove '[' ']' 2810b57cec5SDimitry Andric commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), ""); 2820b57cec5SDimitry Andric commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), ""); 2830b57cec5SDimitry Andric } else { 2840b57cec5SDimitry Andric if (NeedsParen) 2850b57cec5SDimitry Andric commit.insertWrap("(", receiver->getSourceRange(), ")"); 2860b57cec5SDimitry Andric std::string PropertyDotString = "."; 2870b57cec5SDimitry Andric PropertyDotString += Prop->getName(); 2880b57cec5SDimitry Andric PropertyDotString += " ="; 2890b57cec5SDimitry Andric const Expr*const* Args = Msg->getArgs(); 2900b57cec5SDimitry Andric const Expr *RHS = Args[0]; 2910b57cec5SDimitry Andric if (!RHS) 2920b57cec5SDimitry Andric return false; 2930b57cec5SDimitry Andric SourceLocation BegLoc = 2940b57cec5SDimitry Andric ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc(); 2950b57cec5SDimitry Andric BegLoc = PP.getLocForEndOfToken(BegLoc); 2960b57cec5SDimitry Andric SourceLocation EndLoc = RHS->getBeginLoc(); 2970b57cec5SDimitry Andric EndLoc = EndLoc.getLocWithOffset(-1); 2980b57cec5SDimitry Andric const char *colon = PP.getSourceManager().getCharacterData(EndLoc); 2990b57cec5SDimitry Andric // Add a space after '=' if there is no space between RHS and '=' 3000b57cec5SDimitry Andric if (colon && colon[0] == ':') 3010b57cec5SDimitry Andric PropertyDotString += " "; 3020b57cec5SDimitry Andric SourceRange Range(BegLoc, EndLoc); 3030b57cec5SDimitry Andric commit.replace(Range, PropertyDotString); 3040b57cec5SDimitry Andric // remove '[' ']' 3050b57cec5SDimitry Andric commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), ""); 3060b57cec5SDimitry Andric commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), ""); 3070b57cec5SDimitry Andric } 3080b57cec5SDimitry Andric return true; 3090b57cec5SDimitry Andric } 3100b57cec5SDimitry Andric 3110b57cec5SDimitry Andric class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> { 3120b57cec5SDimitry Andric ObjCMigrateASTConsumer &Consumer; 3130b57cec5SDimitry Andric ParentMap &PMap; 3140b57cec5SDimitry Andric 3150b57cec5SDimitry Andric public: 3160b57cec5SDimitry Andric ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap) 3170b57cec5SDimitry Andric : Consumer(consumer), PMap(PMap) { } 3180b57cec5SDimitry Andric 3190b57cec5SDimitry Andric bool shouldVisitTemplateInstantiations() const { return false; } 3200b57cec5SDimitry Andric bool shouldWalkTypesOfTypeLocs() const { return false; } 3210b57cec5SDimitry Andric 3220b57cec5SDimitry Andric bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 3230b57cec5SDimitry Andric if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) { 3240b57cec5SDimitry Andric edit::Commit commit(*Consumer.Editor); 3250b57cec5SDimitry Andric edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap); 3260b57cec5SDimitry Andric Consumer.Editor->commit(commit); 3270b57cec5SDimitry Andric } 3280b57cec5SDimitry Andric 3290b57cec5SDimitry Andric if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) { 3300b57cec5SDimitry Andric edit::Commit commit(*Consumer.Editor); 3310b57cec5SDimitry Andric edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit); 3320b57cec5SDimitry Andric Consumer.Editor->commit(commit); 3330b57cec5SDimitry Andric } 3340b57cec5SDimitry Andric 3350b57cec5SDimitry Andric if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_PropertyDotSyntax) { 3360b57cec5SDimitry Andric edit::Commit commit(*Consumer.Editor); 3370b57cec5SDimitry Andric rewriteToPropertyDotSyntax(E, Consumer.PP, *Consumer.NSAPIObj, 3380b57cec5SDimitry Andric commit, &PMap); 3390b57cec5SDimitry Andric Consumer.Editor->commit(commit); 3400b57cec5SDimitry Andric } 3410b57cec5SDimitry Andric 3420b57cec5SDimitry Andric return true; 3430b57cec5SDimitry Andric } 3440b57cec5SDimitry Andric 3450b57cec5SDimitry Andric bool TraverseObjCMessageExpr(ObjCMessageExpr *E) { 3460b57cec5SDimitry Andric // Do depth first; we want to rewrite the subexpressions first so that if 3470b57cec5SDimitry Andric // we have to move expressions we will move them already rewritten. 3480b57cec5SDimitry Andric for (Stmt *SubStmt : E->children()) 3490b57cec5SDimitry Andric if (!TraverseStmt(SubStmt)) 3500b57cec5SDimitry Andric return false; 3510b57cec5SDimitry Andric 3520b57cec5SDimitry Andric return WalkUpFromObjCMessageExpr(E); 3530b57cec5SDimitry Andric } 3540b57cec5SDimitry Andric }; 3550b57cec5SDimitry Andric 3560b57cec5SDimitry Andric class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> { 3570b57cec5SDimitry Andric ObjCMigrateASTConsumer &Consumer; 3580b57cec5SDimitry Andric std::unique_ptr<ParentMap> PMap; 3590b57cec5SDimitry Andric 3600b57cec5SDimitry Andric public: 3610b57cec5SDimitry Andric BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { } 3620b57cec5SDimitry Andric 3630b57cec5SDimitry Andric bool shouldVisitTemplateInstantiations() const { return false; } 3640b57cec5SDimitry Andric bool shouldWalkTypesOfTypeLocs() const { return false; } 3650b57cec5SDimitry Andric 3660b57cec5SDimitry Andric bool TraverseStmt(Stmt *S) { 3670b57cec5SDimitry Andric PMap.reset(new ParentMap(S)); 3680b57cec5SDimitry Andric ObjCMigrator(Consumer, *PMap).TraverseStmt(S); 3690b57cec5SDimitry Andric return true; 3700b57cec5SDimitry Andric } 3710b57cec5SDimitry Andric }; 3720b57cec5SDimitry Andric } // end anonymous namespace 3730b57cec5SDimitry Andric 3740b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migrateDecl(Decl *D) { 3750b57cec5SDimitry Andric if (!D) 3760b57cec5SDimitry Andric return; 3770b57cec5SDimitry Andric if (isa<ObjCMethodDecl>(D)) 3780b57cec5SDimitry Andric return; // Wait for the ObjC container declaration. 3790b57cec5SDimitry Andric 3800b57cec5SDimitry Andric BodyMigrator(*this).TraverseDecl(D); 3810b57cec5SDimitry Andric } 3820b57cec5SDimitry Andric 3830b57cec5SDimitry Andric static void append_attr(std::string &PropertyString, const char *attr, 3840b57cec5SDimitry Andric bool &LParenAdded) { 3850b57cec5SDimitry Andric if (!LParenAdded) { 3860b57cec5SDimitry Andric PropertyString += "("; 3870b57cec5SDimitry Andric LParenAdded = true; 3880b57cec5SDimitry Andric } 3890b57cec5SDimitry Andric else 3900b57cec5SDimitry Andric PropertyString += ", "; 3910b57cec5SDimitry Andric PropertyString += attr; 3920b57cec5SDimitry Andric } 3930b57cec5SDimitry Andric 3940b57cec5SDimitry Andric static 3950b57cec5SDimitry Andric void MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString, 3960b57cec5SDimitry Andric const std::string& TypeString, 3970b57cec5SDimitry Andric const char *name) { 3980b57cec5SDimitry Andric const char *argPtr = TypeString.c_str(); 3990b57cec5SDimitry Andric int paren = 0; 4000b57cec5SDimitry Andric while (*argPtr) { 4010b57cec5SDimitry Andric switch (*argPtr) { 4020b57cec5SDimitry Andric case '(': 4030b57cec5SDimitry Andric PropertyString += *argPtr; 4040b57cec5SDimitry Andric paren++; 4050b57cec5SDimitry Andric break; 4060b57cec5SDimitry Andric case ')': 4070b57cec5SDimitry Andric PropertyString += *argPtr; 4080b57cec5SDimitry Andric paren--; 4090b57cec5SDimitry Andric break; 4100b57cec5SDimitry Andric case '^': 4110b57cec5SDimitry Andric case '*': 4120b57cec5SDimitry Andric PropertyString += (*argPtr); 4130b57cec5SDimitry Andric if (paren == 1) { 4140b57cec5SDimitry Andric PropertyString += name; 4150b57cec5SDimitry Andric name = ""; 4160b57cec5SDimitry Andric } 4170b57cec5SDimitry Andric break; 4180b57cec5SDimitry Andric default: 4190b57cec5SDimitry Andric PropertyString += *argPtr; 4200b57cec5SDimitry Andric break; 4210b57cec5SDimitry Andric } 4220b57cec5SDimitry Andric argPtr++; 4230b57cec5SDimitry Andric } 4240b57cec5SDimitry Andric } 4250b57cec5SDimitry Andric 4260b57cec5SDimitry Andric static const char *PropertyMemoryAttribute(ASTContext &Context, QualType ArgType) { 4270b57cec5SDimitry Andric Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime(); 4280b57cec5SDimitry Andric bool RetainableObject = ArgType->isObjCRetainableType(); 4290b57cec5SDimitry Andric if (RetainableObject && 4300b57cec5SDimitry Andric (propertyLifetime == Qualifiers::OCL_Strong 4310b57cec5SDimitry Andric || propertyLifetime == Qualifiers::OCL_None)) { 4320b57cec5SDimitry Andric if (const ObjCObjectPointerType *ObjPtrTy = 4330b57cec5SDimitry Andric ArgType->getAs<ObjCObjectPointerType>()) { 4340b57cec5SDimitry Andric ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface(); 4350b57cec5SDimitry Andric if (IDecl && 4360b57cec5SDimitry Andric IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying"))) 4370b57cec5SDimitry Andric return "copy"; 4380b57cec5SDimitry Andric else 4390b57cec5SDimitry Andric return "strong"; 4400b57cec5SDimitry Andric } 4410b57cec5SDimitry Andric else if (ArgType->isBlockPointerType()) 4420b57cec5SDimitry Andric return "copy"; 4430b57cec5SDimitry Andric } else if (propertyLifetime == Qualifiers::OCL_Weak) 4440b57cec5SDimitry Andric // TODO. More precise determination of 'weak' attribute requires 4450b57cec5SDimitry Andric // looking into setter's implementation for backing weak ivar. 4460b57cec5SDimitry Andric return "weak"; 4470b57cec5SDimitry Andric else if (RetainableObject) 4480b57cec5SDimitry Andric return ArgType->isBlockPointerType() ? "copy" : "strong"; 4490b57cec5SDimitry Andric return nullptr; 4500b57cec5SDimitry Andric } 4510b57cec5SDimitry Andric 4520b57cec5SDimitry Andric static void rewriteToObjCProperty(const ObjCMethodDecl *Getter, 4530b57cec5SDimitry Andric const ObjCMethodDecl *Setter, 4540b57cec5SDimitry Andric const NSAPI &NS, edit::Commit &commit, 4550b57cec5SDimitry Andric unsigned LengthOfPrefix, 4560b57cec5SDimitry Andric bool Atomic, bool UseNsIosOnlyMacro, 4570b57cec5SDimitry Andric bool AvailabilityArgsMatch) { 4580b57cec5SDimitry Andric ASTContext &Context = NS.getASTContext(); 4590b57cec5SDimitry Andric bool LParenAdded = false; 4600b57cec5SDimitry Andric std::string PropertyString = "@property "; 4610b57cec5SDimitry Andric if (UseNsIosOnlyMacro && NS.isMacroDefined("NS_NONATOMIC_IOSONLY")) { 4620b57cec5SDimitry Andric PropertyString += "(NS_NONATOMIC_IOSONLY"; 4630b57cec5SDimitry Andric LParenAdded = true; 4640b57cec5SDimitry Andric } else if (!Atomic) { 4650b57cec5SDimitry Andric PropertyString += "(nonatomic"; 4660b57cec5SDimitry Andric LParenAdded = true; 4670b57cec5SDimitry Andric } 4680b57cec5SDimitry Andric 4690b57cec5SDimitry Andric std::string PropertyNameString = Getter->getNameAsString(); 4700b57cec5SDimitry Andric StringRef PropertyName(PropertyNameString); 4710b57cec5SDimitry Andric if (LengthOfPrefix > 0) { 4720b57cec5SDimitry Andric if (!LParenAdded) { 4730b57cec5SDimitry Andric PropertyString += "(getter="; 4740b57cec5SDimitry Andric LParenAdded = true; 4750b57cec5SDimitry Andric } 4760b57cec5SDimitry Andric else 4770b57cec5SDimitry Andric PropertyString += ", getter="; 4780b57cec5SDimitry Andric PropertyString += PropertyNameString; 4790b57cec5SDimitry Andric } 4800b57cec5SDimitry Andric // Property with no setter may be suggested as a 'readonly' property. 4810b57cec5SDimitry Andric if (!Setter) 4820b57cec5SDimitry Andric append_attr(PropertyString, "readonly", LParenAdded); 4830b57cec5SDimitry Andric 4840b57cec5SDimitry Andric 4850b57cec5SDimitry Andric // Short circuit 'delegate' properties that contain the name "delegate" or 4860b57cec5SDimitry Andric // "dataSource", or have exact name "target" to have 'assign' attribute. 487*0fca6ea1SDimitry Andric if (PropertyName == "target" || PropertyName.contains("delegate") || 488349cc55cSDimitry Andric PropertyName.contains("dataSource")) { 4890b57cec5SDimitry Andric QualType QT = Getter->getReturnType(); 4900b57cec5SDimitry Andric if (!QT->isRealType()) 4910b57cec5SDimitry Andric append_attr(PropertyString, "assign", LParenAdded); 4920b57cec5SDimitry Andric } else if (!Setter) { 4930b57cec5SDimitry Andric QualType ResType = Context.getCanonicalType(Getter->getReturnType()); 4940b57cec5SDimitry Andric if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ResType)) 4950b57cec5SDimitry Andric append_attr(PropertyString, MemoryManagementAttr, LParenAdded); 4960b57cec5SDimitry Andric } else { 4970b57cec5SDimitry Andric const ParmVarDecl *argDecl = *Setter->param_begin(); 4980b57cec5SDimitry Andric QualType ArgType = Context.getCanonicalType(argDecl->getType()); 4990b57cec5SDimitry Andric if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ArgType)) 5000b57cec5SDimitry Andric append_attr(PropertyString, MemoryManagementAttr, LParenAdded); 5010b57cec5SDimitry Andric } 5020b57cec5SDimitry Andric if (LParenAdded) 5030b57cec5SDimitry Andric PropertyString += ')'; 5040b57cec5SDimitry Andric QualType RT = Getter->getReturnType(); 505bdd1243dSDimitry Andric if (!RT->getAs<TypedefType>()) { 5060b57cec5SDimitry Andric // strip off any ARC lifetime qualifier. 5070b57cec5SDimitry Andric QualType CanResultTy = Context.getCanonicalType(RT); 5080b57cec5SDimitry Andric if (CanResultTy.getQualifiers().hasObjCLifetime()) { 5090b57cec5SDimitry Andric Qualifiers Qs = CanResultTy.getQualifiers(); 5100b57cec5SDimitry Andric Qs.removeObjCLifetime(); 5110b57cec5SDimitry Andric RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs); 5120b57cec5SDimitry Andric } 5130b57cec5SDimitry Andric } 5140b57cec5SDimitry Andric PropertyString += " "; 5150b57cec5SDimitry Andric PrintingPolicy SubPolicy(Context.getPrintingPolicy()); 5160b57cec5SDimitry Andric SubPolicy.SuppressStrongLifetime = true; 5170b57cec5SDimitry Andric SubPolicy.SuppressLifetimeQualifiers = true; 5180b57cec5SDimitry Andric std::string TypeString = RT.getAsString(SubPolicy); 5190b57cec5SDimitry Andric if (LengthOfPrefix > 0) { 5200b57cec5SDimitry Andric // property name must strip off "is" and lower case the first character 5210b57cec5SDimitry Andric // after that; e.g. isContinuous will become continuous. 5220b57cec5SDimitry Andric StringRef PropertyNameStringRef(PropertyNameString); 5230b57cec5SDimitry Andric PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix); 5245ffd83dbSDimitry Andric PropertyNameString = std::string(PropertyNameStringRef); 5250b57cec5SDimitry Andric bool NoLowering = (isUppercase(PropertyNameString[0]) && 5260b57cec5SDimitry Andric PropertyNameString.size() > 1 && 5270b57cec5SDimitry Andric isUppercase(PropertyNameString[1])); 5280b57cec5SDimitry Andric if (!NoLowering) 5290b57cec5SDimitry Andric PropertyNameString[0] = toLowercase(PropertyNameString[0]); 5300b57cec5SDimitry Andric } 5310b57cec5SDimitry Andric if (RT->isBlockPointerType() || RT->isFunctionPointerType()) 5320b57cec5SDimitry Andric MigrateBlockOrFunctionPointerTypeVariable(PropertyString, 5330b57cec5SDimitry Andric TypeString, 5340b57cec5SDimitry Andric PropertyNameString.c_str()); 5350b57cec5SDimitry Andric else { 5360b57cec5SDimitry Andric char LastChar = TypeString[TypeString.size()-1]; 5370b57cec5SDimitry Andric PropertyString += TypeString; 5380b57cec5SDimitry Andric if (LastChar != '*') 5390b57cec5SDimitry Andric PropertyString += ' '; 5400b57cec5SDimitry Andric PropertyString += PropertyNameString; 5410b57cec5SDimitry Andric } 5420b57cec5SDimitry Andric SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc(); 5430b57cec5SDimitry Andric Selector GetterSelector = Getter->getSelector(); 5440b57cec5SDimitry Andric 5450b57cec5SDimitry Andric SourceLocation EndGetterSelectorLoc = 5460b57cec5SDimitry Andric StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size()); 5470b57cec5SDimitry Andric commit.replace(CharSourceRange::getCharRange(Getter->getBeginLoc(), 5480b57cec5SDimitry Andric EndGetterSelectorLoc), 5490b57cec5SDimitry Andric PropertyString); 5500b57cec5SDimitry Andric if (Setter && AvailabilityArgsMatch) { 5510b57cec5SDimitry Andric SourceLocation EndLoc = Setter->getDeclaratorEndLoc(); 5520b57cec5SDimitry Andric // Get location past ';' 5530b57cec5SDimitry Andric EndLoc = EndLoc.getLocWithOffset(1); 5540b57cec5SDimitry Andric SourceLocation BeginOfSetterDclLoc = Setter->getBeginLoc(); 5550b57cec5SDimitry Andric // FIXME. This assumes that setter decl; is immediately preceded by eoln. 5560b57cec5SDimitry Andric // It is trying to remove the setter method decl. line entirely. 5570b57cec5SDimitry Andric BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1); 5580b57cec5SDimitry Andric commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc)); 5590b57cec5SDimitry Andric } 5600b57cec5SDimitry Andric } 5610b57cec5SDimitry Andric 5620b57cec5SDimitry Andric static bool IsCategoryNameWithDeprecatedSuffix(ObjCContainerDecl *D) { 5630b57cec5SDimitry Andric if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(D)) { 5640b57cec5SDimitry Andric StringRef Name = CatDecl->getName(); 5655f757f3fSDimitry Andric return Name.ends_with("Deprecated"); 5660b57cec5SDimitry Andric } 5670b57cec5SDimitry Andric return false; 5680b57cec5SDimitry Andric } 5690b57cec5SDimitry Andric 5700b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migrateObjCContainerDecl(ASTContext &Ctx, 5710b57cec5SDimitry Andric ObjCContainerDecl *D) { 5720b57cec5SDimitry Andric if (D->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(D)) 5730b57cec5SDimitry Andric return; 5740b57cec5SDimitry Andric 5750b57cec5SDimitry Andric for (auto *Method : D->methods()) { 5760b57cec5SDimitry Andric if (Method->isDeprecated()) 5770b57cec5SDimitry Andric continue; 5780b57cec5SDimitry Andric bool PropertyInferred = migrateProperty(Ctx, D, Method); 5790b57cec5SDimitry Andric // If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to 5800b57cec5SDimitry Andric // the getter method as it ends up on the property itself which we don't want 5810b57cec5SDimitry Andric // to do unless -objcmt-returns-innerpointer-property option is on. 5820b57cec5SDimitry Andric if (!PropertyInferred || 5830b57cec5SDimitry Andric (ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) 5840b57cec5SDimitry Andric if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 5850b57cec5SDimitry Andric migrateNsReturnsInnerPointer(Ctx, Method); 5860b57cec5SDimitry Andric } 5870b57cec5SDimitry Andric if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty)) 5880b57cec5SDimitry Andric return; 5890b57cec5SDimitry Andric 5900b57cec5SDimitry Andric for (auto *Prop : D->instance_properties()) { 5910b57cec5SDimitry Andric if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && 5920b57cec5SDimitry Andric !Prop->isDeprecated()) 5930b57cec5SDimitry Andric migratePropertyNsReturnsInnerPointer(Ctx, Prop); 5940b57cec5SDimitry Andric } 5950b57cec5SDimitry Andric } 5960b57cec5SDimitry Andric 5970b57cec5SDimitry Andric static bool 5980b57cec5SDimitry Andric ClassImplementsAllMethodsAndProperties(ASTContext &Ctx, 5990b57cec5SDimitry Andric const ObjCImplementationDecl *ImpDecl, 6000b57cec5SDimitry Andric const ObjCInterfaceDecl *IDecl, 6010b57cec5SDimitry Andric ObjCProtocolDecl *Protocol) { 6020b57cec5SDimitry Andric // In auto-synthesis, protocol properties are not synthesized. So, 6030b57cec5SDimitry Andric // a conforming protocol must have its required properties declared 6040b57cec5SDimitry Andric // in class interface. 6050b57cec5SDimitry Andric bool HasAtleastOneRequiredProperty = false; 6060b57cec5SDimitry Andric if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) 6070b57cec5SDimitry Andric for (const auto *Property : PDecl->instance_properties()) { 6080b57cec5SDimitry Andric if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional) 6090b57cec5SDimitry Andric continue; 6100b57cec5SDimitry Andric HasAtleastOneRequiredProperty = true; 6110b57cec5SDimitry Andric DeclContext::lookup_result R = IDecl->lookup(Property->getDeclName()); 612fe6060f1SDimitry Andric if (R.empty()) { 6130b57cec5SDimitry Andric // Relax the rule and look into class's implementation for a synthesize 6140b57cec5SDimitry Andric // or dynamic declaration. Class is implementing a property coming from 6150b57cec5SDimitry Andric // another protocol. This still makes the target protocol as conforming. 6160b57cec5SDimitry Andric if (!ImpDecl->FindPropertyImplDecl( 6170b57cec5SDimitry Andric Property->getDeclName().getAsIdentifierInfo(), 6180b57cec5SDimitry Andric Property->getQueryKind())) 6190b57cec5SDimitry Andric return false; 620fe6060f1SDimitry Andric } else if (auto *ClassProperty = R.find_first<ObjCPropertyDecl>()) { 621fe6060f1SDimitry Andric if ((ClassProperty->getPropertyAttributes() != 622fe6060f1SDimitry Andric Property->getPropertyAttributes()) || 6230b57cec5SDimitry Andric !Ctx.hasSameType(ClassProperty->getType(), Property->getType())) 6240b57cec5SDimitry Andric return false; 625fe6060f1SDimitry Andric } else 6260b57cec5SDimitry Andric return false; 6270b57cec5SDimitry Andric } 6280b57cec5SDimitry Andric 6290b57cec5SDimitry Andric // At this point, all required properties in this protocol conform to those 6300b57cec5SDimitry Andric // declared in the class. 6310b57cec5SDimitry Andric // Check that class implements the required methods of the protocol too. 6320b57cec5SDimitry Andric bool HasAtleastOneRequiredMethod = false; 6330b57cec5SDimitry Andric if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) { 6340b57cec5SDimitry Andric if (PDecl->meth_begin() == PDecl->meth_end()) 6350b57cec5SDimitry Andric return HasAtleastOneRequiredProperty; 6360b57cec5SDimitry Andric for (const auto *MD : PDecl->methods()) { 6370b57cec5SDimitry Andric if (MD->isImplicit()) 6380b57cec5SDimitry Andric continue; 6395f757f3fSDimitry Andric if (MD->getImplementationControl() == ObjCImplementationControl::Optional) 6400b57cec5SDimitry Andric continue; 6410b57cec5SDimitry Andric DeclContext::lookup_result R = ImpDecl->lookup(MD->getDeclName()); 642fe6060f1SDimitry Andric if (R.empty()) 6430b57cec5SDimitry Andric return false; 6440b57cec5SDimitry Andric bool match = false; 6450b57cec5SDimitry Andric HasAtleastOneRequiredMethod = true; 646fe6060f1SDimitry Andric for (NamedDecl *ND : R) 647fe6060f1SDimitry Andric if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(ND)) 6480b57cec5SDimitry Andric if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) { 6490b57cec5SDimitry Andric match = true; 6500b57cec5SDimitry Andric break; 6510b57cec5SDimitry Andric } 6520b57cec5SDimitry Andric if (!match) 6530b57cec5SDimitry Andric return false; 6540b57cec5SDimitry Andric } 6550b57cec5SDimitry Andric } 6560b57cec5SDimitry Andric return HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod; 6570b57cec5SDimitry Andric } 6580b57cec5SDimitry Andric 6590b57cec5SDimitry Andric static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl, 6600b57cec5SDimitry Andric llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols, 6610b57cec5SDimitry Andric const NSAPI &NS, edit::Commit &commit) { 6620b57cec5SDimitry Andric const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols(); 6630b57cec5SDimitry Andric std::string ClassString; 6640b57cec5SDimitry Andric SourceLocation EndLoc = 6650b57cec5SDimitry Andric IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation(); 6660b57cec5SDimitry Andric 6670b57cec5SDimitry Andric if (Protocols.empty()) { 6680b57cec5SDimitry Andric ClassString = '<'; 6690b57cec5SDimitry Andric for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 6700b57cec5SDimitry Andric ClassString += ConformingProtocols[i]->getNameAsString(); 6710b57cec5SDimitry Andric if (i != (e-1)) 6720b57cec5SDimitry Andric ClassString += ", "; 6730b57cec5SDimitry Andric } 6740b57cec5SDimitry Andric ClassString += "> "; 6750b57cec5SDimitry Andric } 6760b57cec5SDimitry Andric else { 6770b57cec5SDimitry Andric ClassString = ", "; 6780b57cec5SDimitry Andric for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 6790b57cec5SDimitry Andric ClassString += ConformingProtocols[i]->getNameAsString(); 6800b57cec5SDimitry Andric if (i != (e-1)) 6810b57cec5SDimitry Andric ClassString += ", "; 6820b57cec5SDimitry Andric } 6830b57cec5SDimitry Andric ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1; 6840b57cec5SDimitry Andric EndLoc = *PL; 6850b57cec5SDimitry Andric } 6860b57cec5SDimitry Andric 6870b57cec5SDimitry Andric commit.insertAfterToken(EndLoc, ClassString); 6880b57cec5SDimitry Andric return true; 6890b57cec5SDimitry Andric } 6900b57cec5SDimitry Andric 6910b57cec5SDimitry Andric static StringRef GetUnsignedName(StringRef NSIntegerName) { 6920b57cec5SDimitry Andric StringRef UnsignedName = llvm::StringSwitch<StringRef>(NSIntegerName) 6930b57cec5SDimitry Andric .Case("int8_t", "uint8_t") 6940b57cec5SDimitry Andric .Case("int16_t", "uint16_t") 6950b57cec5SDimitry Andric .Case("int32_t", "uint32_t") 6960b57cec5SDimitry Andric .Case("NSInteger", "NSUInteger") 6970b57cec5SDimitry Andric .Case("int64_t", "uint64_t") 6980b57cec5SDimitry Andric .Default(NSIntegerName); 6990b57cec5SDimitry Andric return UnsignedName; 7000b57cec5SDimitry Andric } 7010b57cec5SDimitry Andric 7020b57cec5SDimitry Andric static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl, 7030b57cec5SDimitry Andric const TypedefDecl *TypedefDcl, 7040b57cec5SDimitry Andric const NSAPI &NS, edit::Commit &commit, 7050b57cec5SDimitry Andric StringRef NSIntegerName, 7060b57cec5SDimitry Andric bool NSOptions) { 7070b57cec5SDimitry Andric std::string ClassString; 7080b57cec5SDimitry Andric if (NSOptions) { 7090b57cec5SDimitry Andric ClassString = "typedef NS_OPTIONS("; 7100b57cec5SDimitry Andric ClassString += GetUnsignedName(NSIntegerName); 7110b57cec5SDimitry Andric } 7120b57cec5SDimitry Andric else { 7130b57cec5SDimitry Andric ClassString = "typedef NS_ENUM("; 7140b57cec5SDimitry Andric ClassString += NSIntegerName; 7150b57cec5SDimitry Andric } 7160b57cec5SDimitry Andric ClassString += ", "; 7170b57cec5SDimitry Andric 7180b57cec5SDimitry Andric ClassString += TypedefDcl->getIdentifier()->getName(); 7190b57cec5SDimitry Andric ClassString += ')'; 7200b57cec5SDimitry Andric SourceRange R(EnumDcl->getBeginLoc(), EnumDcl->getBeginLoc()); 7210b57cec5SDimitry Andric commit.replace(R, ClassString); 7220b57cec5SDimitry Andric SourceLocation EndOfEnumDclLoc = EnumDcl->getEndLoc(); 7230b57cec5SDimitry Andric EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc, 7240b57cec5SDimitry Andric NS.getASTContext(), /*IsDecl*/true); 7250b57cec5SDimitry Andric if (EndOfEnumDclLoc.isValid()) { 7260b57cec5SDimitry Andric SourceRange EnumDclRange(EnumDcl->getBeginLoc(), EndOfEnumDclLoc); 7270b57cec5SDimitry Andric commit.insertFromRange(TypedefDcl->getBeginLoc(), EnumDclRange); 7280b57cec5SDimitry Andric } 7290b57cec5SDimitry Andric else 7300b57cec5SDimitry Andric return false; 7310b57cec5SDimitry Andric 7320b57cec5SDimitry Andric SourceLocation EndTypedefDclLoc = TypedefDcl->getEndLoc(); 7330b57cec5SDimitry Andric EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc, 7340b57cec5SDimitry Andric NS.getASTContext(), /*IsDecl*/true); 7350b57cec5SDimitry Andric if (EndTypedefDclLoc.isValid()) { 7360b57cec5SDimitry Andric SourceRange TDRange(TypedefDcl->getBeginLoc(), EndTypedefDclLoc); 7370b57cec5SDimitry Andric commit.remove(TDRange); 7380b57cec5SDimitry Andric } 7390b57cec5SDimitry Andric else 7400b57cec5SDimitry Andric return false; 7410b57cec5SDimitry Andric 7420b57cec5SDimitry Andric EndOfEnumDclLoc = 7430b57cec5SDimitry Andric trans::findLocationAfterSemi(EnumDcl->getEndLoc(), NS.getASTContext(), 7440b57cec5SDimitry Andric /*IsDecl*/ true); 7450b57cec5SDimitry Andric if (EndOfEnumDclLoc.isValid()) { 7460b57cec5SDimitry Andric SourceLocation BeginOfEnumDclLoc = EnumDcl->getBeginLoc(); 7470b57cec5SDimitry Andric // FIXME. This assumes that enum decl; is immediately preceded by eoln. 7480b57cec5SDimitry Andric // It is trying to remove the enum decl. lines entirely. 7490b57cec5SDimitry Andric BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1); 7500b57cec5SDimitry Andric commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc)); 7510b57cec5SDimitry Andric return true; 7520b57cec5SDimitry Andric } 7530b57cec5SDimitry Andric return false; 7540b57cec5SDimitry Andric } 7550b57cec5SDimitry Andric 7560b57cec5SDimitry Andric static void rewriteToNSMacroDecl(ASTContext &Ctx, 7570b57cec5SDimitry Andric const EnumDecl *EnumDcl, 7580b57cec5SDimitry Andric const TypedefDecl *TypedefDcl, 7590b57cec5SDimitry Andric const NSAPI &NS, edit::Commit &commit, 7600b57cec5SDimitry Andric bool IsNSIntegerType) { 7610b57cec5SDimitry Andric QualType DesignatedEnumType = EnumDcl->getIntegerType(); 7620b57cec5SDimitry Andric assert(!DesignatedEnumType.isNull() 7630b57cec5SDimitry Andric && "rewriteToNSMacroDecl - underlying enum type is null"); 7640b57cec5SDimitry Andric 7650b57cec5SDimitry Andric PrintingPolicy Policy(Ctx.getPrintingPolicy()); 7660b57cec5SDimitry Andric std::string TypeString = DesignatedEnumType.getAsString(Policy); 7670b57cec5SDimitry Andric std::string ClassString = IsNSIntegerType ? "NS_ENUM(" : "NS_OPTIONS("; 7680b57cec5SDimitry Andric ClassString += TypeString; 7690b57cec5SDimitry Andric ClassString += ", "; 7700b57cec5SDimitry Andric 7710b57cec5SDimitry Andric ClassString += TypedefDcl->getIdentifier()->getName(); 7720b57cec5SDimitry Andric ClassString += ") "; 7730b57cec5SDimitry Andric SourceLocation EndLoc = EnumDcl->getBraceRange().getBegin(); 7740b57cec5SDimitry Andric if (EndLoc.isInvalid()) 7750b57cec5SDimitry Andric return; 7760b57cec5SDimitry Andric CharSourceRange R = 7770b57cec5SDimitry Andric CharSourceRange::getCharRange(EnumDcl->getBeginLoc(), EndLoc); 7780b57cec5SDimitry Andric commit.replace(R, ClassString); 7790b57cec5SDimitry Andric // This is to remove spaces between '}' and typedef name. 7800b57cec5SDimitry Andric SourceLocation StartTypedefLoc = EnumDcl->getEndLoc(); 7810b57cec5SDimitry Andric StartTypedefLoc = StartTypedefLoc.getLocWithOffset(+1); 7820b57cec5SDimitry Andric SourceLocation EndTypedefLoc = TypedefDcl->getEndLoc(); 7830b57cec5SDimitry Andric 7840b57cec5SDimitry Andric commit.remove(SourceRange(StartTypedefLoc, EndTypedefLoc)); 7850b57cec5SDimitry Andric } 7860b57cec5SDimitry Andric 7870b57cec5SDimitry Andric static bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx, 7880b57cec5SDimitry Andric const EnumDecl *EnumDcl) { 7890b57cec5SDimitry Andric bool PowerOfTwo = true; 7900b57cec5SDimitry Andric bool AllHexdecimalEnumerator = true; 7910b57cec5SDimitry Andric uint64_t MaxPowerOfTwoVal = 0; 792bdd1243dSDimitry Andric for (auto *Enumerator : EnumDcl->enumerators()) { 7930b57cec5SDimitry Andric const Expr *InitExpr = Enumerator->getInitExpr(); 7940b57cec5SDimitry Andric if (!InitExpr) { 7950b57cec5SDimitry Andric PowerOfTwo = false; 7960b57cec5SDimitry Andric AllHexdecimalEnumerator = false; 7970b57cec5SDimitry Andric continue; 7980b57cec5SDimitry Andric } 7990b57cec5SDimitry Andric InitExpr = InitExpr->IgnoreParenCasts(); 8000b57cec5SDimitry Andric if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr)) 8010b57cec5SDimitry Andric if (BO->isShiftOp() || BO->isBitwiseOp()) 8020b57cec5SDimitry Andric return true; 8030b57cec5SDimitry Andric 8040b57cec5SDimitry Andric uint64_t EnumVal = Enumerator->getInitVal().getZExtValue(); 8050b57cec5SDimitry Andric if (PowerOfTwo && EnumVal) { 8060b57cec5SDimitry Andric if (!llvm::isPowerOf2_64(EnumVal)) 8070b57cec5SDimitry Andric PowerOfTwo = false; 8080b57cec5SDimitry Andric else if (EnumVal > MaxPowerOfTwoVal) 8090b57cec5SDimitry Andric MaxPowerOfTwoVal = EnumVal; 8100b57cec5SDimitry Andric } 8110b57cec5SDimitry Andric if (AllHexdecimalEnumerator && EnumVal) { 8120b57cec5SDimitry Andric bool FoundHexdecimalEnumerator = false; 8130b57cec5SDimitry Andric SourceLocation EndLoc = Enumerator->getEndLoc(); 8140b57cec5SDimitry Andric Token Tok; 8150b57cec5SDimitry Andric if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true)) 8160b57cec5SDimitry Andric if (Tok.isLiteral() && Tok.getLength() > 2) { 8170b57cec5SDimitry Andric if (const char *StringLit = Tok.getLiteralData()) 8180b57cec5SDimitry Andric FoundHexdecimalEnumerator = 8190b57cec5SDimitry Andric (StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x')); 8200b57cec5SDimitry Andric } 8210b57cec5SDimitry Andric if (!FoundHexdecimalEnumerator) 8220b57cec5SDimitry Andric AllHexdecimalEnumerator = false; 8230b57cec5SDimitry Andric } 8240b57cec5SDimitry Andric } 8250b57cec5SDimitry Andric return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2)); 8260b57cec5SDimitry Andric } 8270b57cec5SDimitry Andric 8280b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx, 8290b57cec5SDimitry Andric const ObjCImplementationDecl *ImpDecl) { 8300b57cec5SDimitry Andric const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface(); 8310b57cec5SDimitry Andric if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated()) 8320b57cec5SDimitry Andric return; 8330b57cec5SDimitry Andric // Find all implicit conforming protocols for this class 8340b57cec5SDimitry Andric // and make them explicit. 8350b57cec5SDimitry Andric llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols; 8360b57cec5SDimitry Andric Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols); 8370b57cec5SDimitry Andric llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols; 8380b57cec5SDimitry Andric 8390b57cec5SDimitry Andric for (ObjCProtocolDecl *ProtDecl : ObjCProtocolDecls) 8400b57cec5SDimitry Andric if (!ExplicitProtocols.count(ProtDecl)) 8410b57cec5SDimitry Andric PotentialImplicitProtocols.push_back(ProtDecl); 8420b57cec5SDimitry Andric 8430b57cec5SDimitry Andric if (PotentialImplicitProtocols.empty()) 8440b57cec5SDimitry Andric return; 8450b57cec5SDimitry Andric 8460b57cec5SDimitry Andric // go through list of non-optional methods and properties in each protocol 8470b57cec5SDimitry Andric // in the PotentialImplicitProtocols list. If class implements every one of the 8480b57cec5SDimitry Andric // methods and properties, then this class conforms to this protocol. 8490b57cec5SDimitry Andric llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols; 8500b57cec5SDimitry Andric for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++) 8510b57cec5SDimitry Andric if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl, 8520b57cec5SDimitry Andric PotentialImplicitProtocols[i])) 8530b57cec5SDimitry Andric ConformingProtocols.push_back(PotentialImplicitProtocols[i]); 8540b57cec5SDimitry Andric 8550b57cec5SDimitry Andric if (ConformingProtocols.empty()) 8560b57cec5SDimitry Andric return; 8570b57cec5SDimitry Andric 8580b57cec5SDimitry Andric // Further reduce number of conforming protocols. If protocol P1 is in the list 8590b57cec5SDimitry Andric // protocol P2 (P2<P1>), No need to include P1. 8600b57cec5SDimitry Andric llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols; 8610b57cec5SDimitry Andric for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) { 8620b57cec5SDimitry Andric bool DropIt = false; 8630b57cec5SDimitry Andric ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i]; 8640b57cec5SDimitry Andric for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) { 8650b57cec5SDimitry Andric ObjCProtocolDecl *PDecl = ConformingProtocols[i1]; 8660b57cec5SDimitry Andric if (PDecl == TargetPDecl) 8670b57cec5SDimitry Andric continue; 8680b57cec5SDimitry Andric if (PDecl->lookupProtocolNamed( 8690b57cec5SDimitry Andric TargetPDecl->getDeclName().getAsIdentifierInfo())) { 8700b57cec5SDimitry Andric DropIt = true; 8710b57cec5SDimitry Andric break; 8720b57cec5SDimitry Andric } 8730b57cec5SDimitry Andric } 8740b57cec5SDimitry Andric if (!DropIt) 8750b57cec5SDimitry Andric MinimalConformingProtocols.push_back(TargetPDecl); 8760b57cec5SDimitry Andric } 8770b57cec5SDimitry Andric if (MinimalConformingProtocols.empty()) 8780b57cec5SDimitry Andric return; 8790b57cec5SDimitry Andric edit::Commit commit(*Editor); 8800b57cec5SDimitry Andric rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols, 8810b57cec5SDimitry Andric *NSAPIObj, commit); 8820b57cec5SDimitry Andric Editor->commit(commit); 8830b57cec5SDimitry Andric } 8840b57cec5SDimitry Andric 8850b57cec5SDimitry Andric void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed( 8860b57cec5SDimitry Andric const TypedefDecl *TypedefDcl) { 8870b57cec5SDimitry Andric 8880b57cec5SDimitry Andric QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); 8890b57cec5SDimitry Andric if (NSAPIObj->isObjCNSIntegerType(qt)) 8900b57cec5SDimitry Andric NSIntegerTypedefed = TypedefDcl; 8910b57cec5SDimitry Andric else if (NSAPIObj->isObjCNSUIntegerType(qt)) 8920b57cec5SDimitry Andric NSUIntegerTypedefed = TypedefDcl; 8930b57cec5SDimitry Andric } 8940b57cec5SDimitry Andric 8950b57cec5SDimitry Andric bool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx, 8960b57cec5SDimitry Andric const EnumDecl *EnumDcl, 8970b57cec5SDimitry Andric const TypedefDecl *TypedefDcl) { 8980b57cec5SDimitry Andric if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() || 8990b57cec5SDimitry Andric EnumDcl->isDeprecated()) 9000b57cec5SDimitry Andric return false; 9010b57cec5SDimitry Andric if (!TypedefDcl) { 9020b57cec5SDimitry Andric if (NSIntegerTypedefed) { 9030b57cec5SDimitry Andric TypedefDcl = NSIntegerTypedefed; 9040b57cec5SDimitry Andric NSIntegerTypedefed = nullptr; 9050b57cec5SDimitry Andric } 9060b57cec5SDimitry Andric else if (NSUIntegerTypedefed) { 9070b57cec5SDimitry Andric TypedefDcl = NSUIntegerTypedefed; 9080b57cec5SDimitry Andric NSUIntegerTypedefed = nullptr; 9090b57cec5SDimitry Andric } 9100b57cec5SDimitry Andric else 9110b57cec5SDimitry Andric return false; 9120b57cec5SDimitry Andric FileID FileIdOfTypedefDcl = 9130b57cec5SDimitry Andric PP.getSourceManager().getFileID(TypedefDcl->getLocation()); 9140b57cec5SDimitry Andric FileID FileIdOfEnumDcl = 9150b57cec5SDimitry Andric PP.getSourceManager().getFileID(EnumDcl->getLocation()); 9160b57cec5SDimitry Andric if (FileIdOfTypedefDcl != FileIdOfEnumDcl) 9170b57cec5SDimitry Andric return false; 9180b57cec5SDimitry Andric } 9190b57cec5SDimitry Andric if (TypedefDcl->isDeprecated()) 9200b57cec5SDimitry Andric return false; 9210b57cec5SDimitry Andric 9220b57cec5SDimitry Andric QualType qt = TypedefDcl->getTypeSourceInfo()->getType(); 9230b57cec5SDimitry Andric StringRef NSIntegerName = NSAPIObj->GetNSIntegralKind(qt); 9240b57cec5SDimitry Andric 9250b57cec5SDimitry Andric if (NSIntegerName.empty()) { 9260b57cec5SDimitry Andric // Also check for typedef enum {...} TD; 9270b57cec5SDimitry Andric if (const EnumType *EnumTy = qt->getAs<EnumType>()) { 9280b57cec5SDimitry Andric if (EnumTy->getDecl() == EnumDcl) { 9290b57cec5SDimitry Andric bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl); 9300b57cec5SDimitry Andric if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc())) 9310b57cec5SDimitry Andric return false; 9320b57cec5SDimitry Andric edit::Commit commit(*Editor); 9330b57cec5SDimitry Andric rewriteToNSMacroDecl(Ctx, EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions); 9340b57cec5SDimitry Andric Editor->commit(commit); 9350b57cec5SDimitry Andric return true; 9360b57cec5SDimitry Andric } 9370b57cec5SDimitry Andric } 9380b57cec5SDimitry Andric return false; 9390b57cec5SDimitry Andric } 9400b57cec5SDimitry Andric 9410b57cec5SDimitry Andric // We may still use NS_OPTIONS based on what we find in the enumertor list. 9420b57cec5SDimitry Andric bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl); 9430b57cec5SDimitry Andric if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc())) 9440b57cec5SDimitry Andric return false; 9450b57cec5SDimitry Andric edit::Commit commit(*Editor); 9460b57cec5SDimitry Andric bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, 9470b57cec5SDimitry Andric commit, NSIntegerName, NSOptions); 9480b57cec5SDimitry Andric Editor->commit(commit); 9490b57cec5SDimitry Andric return Res; 9500b57cec5SDimitry Andric } 9510b57cec5SDimitry Andric 9520b57cec5SDimitry Andric static void ReplaceWithInstancetype(ASTContext &Ctx, 9530b57cec5SDimitry Andric const ObjCMigrateASTConsumer &ASTC, 9540b57cec5SDimitry Andric ObjCMethodDecl *OM) { 9550b57cec5SDimitry Andric if (OM->getReturnType() == Ctx.getObjCInstanceType()) 9560b57cec5SDimitry Andric return; // already has instancetype. 9570b57cec5SDimitry Andric 9580b57cec5SDimitry Andric SourceRange R; 9590b57cec5SDimitry Andric std::string ClassString; 9600b57cec5SDimitry Andric if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) { 9610b57cec5SDimitry Andric TypeLoc TL = TSInfo->getTypeLoc(); 9620b57cec5SDimitry Andric R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); 9630b57cec5SDimitry Andric ClassString = "instancetype"; 9640b57cec5SDimitry Andric } 9650b57cec5SDimitry Andric else { 9660b57cec5SDimitry Andric R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc()); 9670b57cec5SDimitry Andric ClassString = OM->isInstanceMethod() ? '-' : '+'; 9680b57cec5SDimitry Andric ClassString += " (instancetype)"; 9690b57cec5SDimitry Andric } 9700b57cec5SDimitry Andric edit::Commit commit(*ASTC.Editor); 9710b57cec5SDimitry Andric commit.replace(R, ClassString); 9720b57cec5SDimitry Andric ASTC.Editor->commit(commit); 9730b57cec5SDimitry Andric } 9740b57cec5SDimitry Andric 9750b57cec5SDimitry Andric static void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC, 9760b57cec5SDimitry Andric ObjCMethodDecl *OM) { 9770b57cec5SDimitry Andric ObjCInterfaceDecl *IDecl = OM->getClassInterface(); 9780b57cec5SDimitry Andric SourceRange R; 9790b57cec5SDimitry Andric std::string ClassString; 9800b57cec5SDimitry Andric if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) { 9810b57cec5SDimitry Andric TypeLoc TL = TSInfo->getTypeLoc(); 9820b57cec5SDimitry Andric R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); { 9835ffd83dbSDimitry Andric ClassString = std::string(IDecl->getName()); 9840b57cec5SDimitry Andric ClassString += "*"; 9850b57cec5SDimitry Andric } 9860b57cec5SDimitry Andric } 9870b57cec5SDimitry Andric else { 9880b57cec5SDimitry Andric R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc()); 9890b57cec5SDimitry Andric ClassString = "+ ("; 9900b57cec5SDimitry Andric ClassString += IDecl->getName(); ClassString += "*)"; 9910b57cec5SDimitry Andric } 9920b57cec5SDimitry Andric edit::Commit commit(*ASTC.Editor); 9930b57cec5SDimitry Andric commit.replace(R, ClassString); 9940b57cec5SDimitry Andric ASTC.Editor->commit(commit); 9950b57cec5SDimitry Andric } 9960b57cec5SDimitry Andric 9970b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx, 9980b57cec5SDimitry Andric ObjCContainerDecl *CDecl, 9990b57cec5SDimitry Andric ObjCMethodDecl *OM) { 10000b57cec5SDimitry Andric ObjCInstanceTypeFamily OIT_Family = 10010b57cec5SDimitry Andric Selector::getInstTypeMethodFamily(OM->getSelector()); 10020b57cec5SDimitry Andric 10030b57cec5SDimitry Andric std::string ClassName; 10040b57cec5SDimitry Andric switch (OIT_Family) { 10050b57cec5SDimitry Andric case OIT_None: 10060b57cec5SDimitry Andric migrateFactoryMethod(Ctx, CDecl, OM); 10070b57cec5SDimitry Andric return; 10080b57cec5SDimitry Andric case OIT_Array: 10090b57cec5SDimitry Andric ClassName = "NSArray"; 10100b57cec5SDimitry Andric break; 10110b57cec5SDimitry Andric case OIT_Dictionary: 10120b57cec5SDimitry Andric ClassName = "NSDictionary"; 10130b57cec5SDimitry Andric break; 10140b57cec5SDimitry Andric case OIT_Singleton: 10150b57cec5SDimitry Andric migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton); 10160b57cec5SDimitry Andric return; 10170b57cec5SDimitry Andric case OIT_Init: 10180b57cec5SDimitry Andric if (OM->getReturnType()->isObjCIdType()) 10190b57cec5SDimitry Andric ReplaceWithInstancetype(Ctx, *this, OM); 10200b57cec5SDimitry Andric return; 10210b57cec5SDimitry Andric case OIT_ReturnsSelf: 10220b57cec5SDimitry Andric migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf); 10230b57cec5SDimitry Andric return; 10240b57cec5SDimitry Andric } 10250b57cec5SDimitry Andric if (!OM->getReturnType()->isObjCIdType()) 10260b57cec5SDimitry Andric return; 10270b57cec5SDimitry Andric 10280b57cec5SDimitry Andric ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 10290b57cec5SDimitry Andric if (!IDecl) { 10300b57cec5SDimitry Andric if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 10310b57cec5SDimitry Andric IDecl = CatDecl->getClassInterface(); 10320b57cec5SDimitry Andric else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 10330b57cec5SDimitry Andric IDecl = ImpDecl->getClassInterface(); 10340b57cec5SDimitry Andric } 10350b57cec5SDimitry Andric if (!IDecl || 10360b57cec5SDimitry Andric !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) { 10370b57cec5SDimitry Andric migrateFactoryMethod(Ctx, CDecl, OM); 10380b57cec5SDimitry Andric return; 10390b57cec5SDimitry Andric } 10400b57cec5SDimitry Andric ReplaceWithInstancetype(Ctx, *this, OM); 10410b57cec5SDimitry Andric } 10420b57cec5SDimitry Andric 10430b57cec5SDimitry Andric static bool TypeIsInnerPointer(QualType T) { 10440b57cec5SDimitry Andric if (!T->isAnyPointerType()) 10450b57cec5SDimitry Andric return false; 10460b57cec5SDimitry Andric if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() || 10470b57cec5SDimitry Andric T->isBlockPointerType() || T->isFunctionPointerType() || 10480b57cec5SDimitry Andric ento::coreFoundation::isCFObjectRef(T)) 10490b57cec5SDimitry Andric return false; 10500b57cec5SDimitry Andric // Also, typedef-of-pointer-to-incomplete-struct is something that we assume 10510b57cec5SDimitry Andric // is not an innter pointer type. 10520b57cec5SDimitry Andric QualType OrigT = T; 1053bdd1243dSDimitry Andric while (const auto *TD = T->getAs<TypedefType>()) 10540b57cec5SDimitry Andric T = TD->getDecl()->getUnderlyingType(); 10550b57cec5SDimitry Andric if (OrigT == T || !T->isPointerType()) 10560b57cec5SDimitry Andric return true; 10570b57cec5SDimitry Andric const PointerType* PT = T->getAs<PointerType>(); 10580b57cec5SDimitry Andric QualType UPointeeT = PT->getPointeeType().getUnqualifiedType(); 10590b57cec5SDimitry Andric if (UPointeeT->isRecordType()) { 10600b57cec5SDimitry Andric const RecordType *RecordTy = UPointeeT->getAs<RecordType>(); 10610b57cec5SDimitry Andric if (!RecordTy->getDecl()->isCompleteDefinition()) 10620b57cec5SDimitry Andric return false; 10630b57cec5SDimitry Andric } 10640b57cec5SDimitry Andric return true; 10650b57cec5SDimitry Andric } 10660b57cec5SDimitry Andric 10670b57cec5SDimitry Andric /// Check whether the two versions match. 10680b57cec5SDimitry Andric static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) { 10690b57cec5SDimitry Andric return (X == Y); 10700b57cec5SDimitry Andric } 10710b57cec5SDimitry Andric 10720b57cec5SDimitry Andric /// AvailabilityAttrsMatch - This routine checks that if comparing two 10730b57cec5SDimitry Andric /// availability attributes, all their components match. It returns 10740b57cec5SDimitry Andric /// true, if not dealing with availability or when all components of 10750b57cec5SDimitry Andric /// availability attributes match. This routine is only called when 10760b57cec5SDimitry Andric /// the attributes are of the same kind. 10770b57cec5SDimitry Andric static bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) { 10780b57cec5SDimitry Andric const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1); 10790b57cec5SDimitry Andric if (!AA1) 10800b57cec5SDimitry Andric return true; 1081480093f4SDimitry Andric const AvailabilityAttr *AA2 = cast<AvailabilityAttr>(At2); 10820b57cec5SDimitry Andric 10830b57cec5SDimitry Andric VersionTuple Introduced1 = AA1->getIntroduced(); 10840b57cec5SDimitry Andric VersionTuple Deprecated1 = AA1->getDeprecated(); 10850b57cec5SDimitry Andric VersionTuple Obsoleted1 = AA1->getObsoleted(); 10860b57cec5SDimitry Andric bool IsUnavailable1 = AA1->getUnavailable(); 10870b57cec5SDimitry Andric VersionTuple Introduced2 = AA2->getIntroduced(); 10880b57cec5SDimitry Andric VersionTuple Deprecated2 = AA2->getDeprecated(); 10890b57cec5SDimitry Andric VersionTuple Obsoleted2 = AA2->getObsoleted(); 10900b57cec5SDimitry Andric bool IsUnavailable2 = AA2->getUnavailable(); 10910b57cec5SDimitry Andric return (versionsMatch(Introduced1, Introduced2) && 10920b57cec5SDimitry Andric versionsMatch(Deprecated1, Deprecated2) && 10930b57cec5SDimitry Andric versionsMatch(Obsoleted1, Obsoleted2) && 10940b57cec5SDimitry Andric IsUnavailable1 == IsUnavailable2); 10950b57cec5SDimitry Andric } 10960b57cec5SDimitry Andric 10970b57cec5SDimitry Andric static bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2, 10980b57cec5SDimitry Andric bool &AvailabilityArgsMatch) { 10990b57cec5SDimitry Andric // This list is very small, so this need not be optimized. 11000b57cec5SDimitry Andric for (unsigned i = 0, e = Attrs1.size(); i != e; i++) { 11010b57cec5SDimitry Andric bool match = false; 11020b57cec5SDimitry Andric for (unsigned j = 0, f = Attrs2.size(); j != f; j++) { 11030b57cec5SDimitry Andric // Matching attribute kind only. Except for Availability attributes, 11040b57cec5SDimitry Andric // we are not getting into details of the attributes. For all practical purposes 11050b57cec5SDimitry Andric // this is sufficient. 11060b57cec5SDimitry Andric if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) { 11070b57cec5SDimitry Andric if (AvailabilityArgsMatch) 11080b57cec5SDimitry Andric AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]); 11090b57cec5SDimitry Andric match = true; 11100b57cec5SDimitry Andric break; 11110b57cec5SDimitry Andric } 11120b57cec5SDimitry Andric } 11130b57cec5SDimitry Andric if (!match) 11140b57cec5SDimitry Andric return false; 11150b57cec5SDimitry Andric } 11160b57cec5SDimitry Andric return true; 11170b57cec5SDimitry Andric } 11180b57cec5SDimitry Andric 11190b57cec5SDimitry Andric /// AttributesMatch - This routine checks list of attributes for two 11200b57cec5SDimitry Andric /// decls. It returns false, if there is a mismatch in kind of 11210b57cec5SDimitry Andric /// attributes seen in the decls. It returns true if the two decls 11220b57cec5SDimitry Andric /// have list of same kind of attributes. Furthermore, when there 11230b57cec5SDimitry Andric /// are availability attributes in the two decls, it sets the 11240b57cec5SDimitry Andric /// AvailabilityArgsMatch to false if availability attributes have 11250b57cec5SDimitry Andric /// different versions, etc. 11260b57cec5SDimitry Andric static bool AttributesMatch(const Decl *Decl1, const Decl *Decl2, 11270b57cec5SDimitry Andric bool &AvailabilityArgsMatch) { 11280b57cec5SDimitry Andric if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) { 11290b57cec5SDimitry Andric AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs()); 11300b57cec5SDimitry Andric return true; 11310b57cec5SDimitry Andric } 11320b57cec5SDimitry Andric AvailabilityArgsMatch = true; 11330b57cec5SDimitry Andric const AttrVec &Attrs1 = Decl1->getAttrs(); 11340b57cec5SDimitry Andric const AttrVec &Attrs2 = Decl2->getAttrs(); 11350b57cec5SDimitry Andric bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch); 11360b57cec5SDimitry Andric if (match && (Attrs2.size() > Attrs1.size())) 11370b57cec5SDimitry Andric return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch); 11380b57cec5SDimitry Andric return match; 11390b57cec5SDimitry Andric } 11400b57cec5SDimitry Andric 11410b57cec5SDimitry Andric static bool IsValidIdentifier(ASTContext &Ctx, 11420b57cec5SDimitry Andric const char *Name) { 1143349cc55cSDimitry Andric if (!isAsciiIdentifierStart(Name[0])) 11440b57cec5SDimitry Andric return false; 11450b57cec5SDimitry Andric std::string NameString = Name; 11460b57cec5SDimitry Andric NameString[0] = toLowercase(NameString[0]); 1147*0fca6ea1SDimitry Andric const IdentifierInfo *II = &Ctx.Idents.get(NameString); 11480b57cec5SDimitry Andric return II->getTokenID() == tok::identifier; 11490b57cec5SDimitry Andric } 11500b57cec5SDimitry Andric 11510b57cec5SDimitry Andric bool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx, 11520b57cec5SDimitry Andric ObjCContainerDecl *D, 11530b57cec5SDimitry Andric ObjCMethodDecl *Method) { 11540b57cec5SDimitry Andric if (Method->isPropertyAccessor() || !Method->isInstanceMethod() || 11550b57cec5SDimitry Andric Method->param_size() != 0) 11560b57cec5SDimitry Andric return false; 11570b57cec5SDimitry Andric // Is this method candidate to be a getter? 11580b57cec5SDimitry Andric QualType GRT = Method->getReturnType(); 11590b57cec5SDimitry Andric if (GRT->isVoidType()) 11600b57cec5SDimitry Andric return false; 11610b57cec5SDimitry Andric 11620b57cec5SDimitry Andric Selector GetterSelector = Method->getSelector(); 11630b57cec5SDimitry Andric ObjCInstanceTypeFamily OIT_Family = 11640b57cec5SDimitry Andric Selector::getInstTypeMethodFamily(GetterSelector); 11650b57cec5SDimitry Andric 11660b57cec5SDimitry Andric if (OIT_Family != OIT_None) 11670b57cec5SDimitry Andric return false; 11680b57cec5SDimitry Andric 1169*0fca6ea1SDimitry Andric const IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0); 11700b57cec5SDimitry Andric Selector SetterSelector = 11710b57cec5SDimitry Andric SelectorTable::constructSetterSelector(PP.getIdentifierTable(), 11720b57cec5SDimitry Andric PP.getSelectorTable(), 11730b57cec5SDimitry Andric getterName); 11740b57cec5SDimitry Andric ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector); 11750b57cec5SDimitry Andric unsigned LengthOfPrefix = 0; 11760b57cec5SDimitry Andric if (!SetterMethod) { 11770b57cec5SDimitry Andric // try a different naming convention for getter: isXxxxx 11780b57cec5SDimitry Andric StringRef getterNameString = getterName->getName(); 11795f757f3fSDimitry Andric bool IsPrefix = getterNameString.starts_with("is"); 11800b57cec5SDimitry Andric // Note that we don't want to change an isXXX method of retainable object 11810b57cec5SDimitry Andric // type to property (readonly or otherwise). 11820b57cec5SDimitry Andric if (IsPrefix && GRT->isObjCRetainableType()) 11830b57cec5SDimitry Andric return false; 11845f757f3fSDimitry Andric if (IsPrefix || getterNameString.starts_with("get")) { 11850b57cec5SDimitry Andric LengthOfPrefix = (IsPrefix ? 2 : 3); 11860b57cec5SDimitry Andric const char *CGetterName = getterNameString.data() + LengthOfPrefix; 11870b57cec5SDimitry Andric // Make sure that first character after "is" or "get" prefix can 11880b57cec5SDimitry Andric // start an identifier. 11890b57cec5SDimitry Andric if (!IsValidIdentifier(Ctx, CGetterName)) 11900b57cec5SDimitry Andric return false; 11910b57cec5SDimitry Andric if (CGetterName[0] && isUppercase(CGetterName[0])) { 11920b57cec5SDimitry Andric getterName = &Ctx.Idents.get(CGetterName); 11930b57cec5SDimitry Andric SetterSelector = 11940b57cec5SDimitry Andric SelectorTable::constructSetterSelector(PP.getIdentifierTable(), 11950b57cec5SDimitry Andric PP.getSelectorTable(), 11960b57cec5SDimitry Andric getterName); 11970b57cec5SDimitry Andric SetterMethod = D->getInstanceMethod(SetterSelector); 11980b57cec5SDimitry Andric } 11990b57cec5SDimitry Andric } 12000b57cec5SDimitry Andric } 12010b57cec5SDimitry Andric 12020b57cec5SDimitry Andric if (SetterMethod) { 12030b57cec5SDimitry Andric if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0) 12040b57cec5SDimitry Andric return false; 12050b57cec5SDimitry Andric bool AvailabilityArgsMatch; 12060b57cec5SDimitry Andric if (SetterMethod->isDeprecated() || 12070b57cec5SDimitry Andric !AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch)) 12080b57cec5SDimitry Andric return false; 12090b57cec5SDimitry Andric 12100b57cec5SDimitry Andric // Is this a valid setter, matching the target getter? 12110b57cec5SDimitry Andric QualType SRT = SetterMethod->getReturnType(); 12120b57cec5SDimitry Andric if (!SRT->isVoidType()) 12130b57cec5SDimitry Andric return false; 12140b57cec5SDimitry Andric const ParmVarDecl *argDecl = *SetterMethod->param_begin(); 12150b57cec5SDimitry Andric QualType ArgType = argDecl->getType(); 12160b57cec5SDimitry Andric if (!Ctx.hasSameUnqualifiedType(ArgType, GRT)) 12170b57cec5SDimitry Andric return false; 12180b57cec5SDimitry Andric edit::Commit commit(*Editor); 12190b57cec5SDimitry Andric rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit, 12200b57cec5SDimitry Andric LengthOfPrefix, 12210b57cec5SDimitry Andric (ASTMigrateActions & 12220b57cec5SDimitry Andric FrontendOptions::ObjCMT_AtomicProperty) != 0, 12230b57cec5SDimitry Andric (ASTMigrateActions & 12240b57cec5SDimitry Andric FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0, 12250b57cec5SDimitry Andric AvailabilityArgsMatch); 12260b57cec5SDimitry Andric Editor->commit(commit); 12270b57cec5SDimitry Andric return true; 12280b57cec5SDimitry Andric } 12290b57cec5SDimitry Andric else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) { 12300b57cec5SDimitry Andric // Try a non-void method with no argument (and no setter or property of same name 12310b57cec5SDimitry Andric // as a 'readonly' property. 12320b57cec5SDimitry Andric edit::Commit commit(*Editor); 12330b57cec5SDimitry Andric rewriteToObjCProperty(Method, nullptr /*SetterMethod*/, *NSAPIObj, commit, 12340b57cec5SDimitry Andric LengthOfPrefix, 12350b57cec5SDimitry Andric (ASTMigrateActions & 12360b57cec5SDimitry Andric FrontendOptions::ObjCMT_AtomicProperty) != 0, 12370b57cec5SDimitry Andric (ASTMigrateActions & 12380b57cec5SDimitry Andric FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0, 12390b57cec5SDimitry Andric /*AvailabilityArgsMatch*/false); 12400b57cec5SDimitry Andric Editor->commit(commit); 12410b57cec5SDimitry Andric return true; 12420b57cec5SDimitry Andric } 12430b57cec5SDimitry Andric return false; 12440b57cec5SDimitry Andric } 12450b57cec5SDimitry Andric 12460b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx, 12470b57cec5SDimitry Andric ObjCMethodDecl *OM) { 12480b57cec5SDimitry Andric if (OM->isImplicit() || 12490b57cec5SDimitry Andric !OM->isInstanceMethod() || 12500b57cec5SDimitry Andric OM->hasAttr<ObjCReturnsInnerPointerAttr>()) 12510b57cec5SDimitry Andric return; 12520b57cec5SDimitry Andric 12530b57cec5SDimitry Andric QualType RT = OM->getReturnType(); 12540b57cec5SDimitry Andric if (!TypeIsInnerPointer(RT) || 12550b57cec5SDimitry Andric !NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER")) 12560b57cec5SDimitry Andric return; 12570b57cec5SDimitry Andric 12580b57cec5SDimitry Andric edit::Commit commit(*Editor); 12590b57cec5SDimitry Andric commit.insertBefore(OM->getEndLoc(), " NS_RETURNS_INNER_POINTER"); 12600b57cec5SDimitry Andric Editor->commit(commit); 12610b57cec5SDimitry Andric } 12620b57cec5SDimitry Andric 12630b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, 12640b57cec5SDimitry Andric ObjCPropertyDecl *P) { 12650b57cec5SDimitry Andric QualType T = P->getType(); 12660b57cec5SDimitry Andric 12670b57cec5SDimitry Andric if (!TypeIsInnerPointer(T) || 12680b57cec5SDimitry Andric !NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER")) 12690b57cec5SDimitry Andric return; 12700b57cec5SDimitry Andric edit::Commit commit(*Editor); 12710b57cec5SDimitry Andric commit.insertBefore(P->getEndLoc(), " NS_RETURNS_INNER_POINTER "); 12720b57cec5SDimitry Andric Editor->commit(commit); 12730b57cec5SDimitry Andric } 12740b57cec5SDimitry Andric 12750b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx, 12760b57cec5SDimitry Andric ObjCContainerDecl *CDecl) { 12770b57cec5SDimitry Andric if (CDecl->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(CDecl)) 12780b57cec5SDimitry Andric return; 12790b57cec5SDimitry Andric 12800b57cec5SDimitry Andric // migrate methods which can have instancetype as their result type. 12810b57cec5SDimitry Andric for (auto *Method : CDecl->methods()) { 12820b57cec5SDimitry Andric if (Method->isDeprecated()) 12830b57cec5SDimitry Andric continue; 12840b57cec5SDimitry Andric migrateMethodInstanceType(Ctx, CDecl, Method); 12850b57cec5SDimitry Andric } 12860b57cec5SDimitry Andric } 12870b57cec5SDimitry Andric 12880b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx, 12890b57cec5SDimitry Andric ObjCContainerDecl *CDecl, 12900b57cec5SDimitry Andric ObjCMethodDecl *OM, 12910b57cec5SDimitry Andric ObjCInstanceTypeFamily OIT_Family) { 12920b57cec5SDimitry Andric if (OM->isInstanceMethod() || 12930b57cec5SDimitry Andric OM->getReturnType() == Ctx.getObjCInstanceType() || 12940b57cec5SDimitry Andric !OM->getReturnType()->isObjCIdType()) 12950b57cec5SDimitry Andric return; 12960b57cec5SDimitry Andric 12970b57cec5SDimitry Andric // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class 12980b57cec5SDimitry Andric // NSYYYNamE with matching names be at least 3 characters long. 12990b57cec5SDimitry Andric ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl); 13000b57cec5SDimitry Andric if (!IDecl) { 13010b57cec5SDimitry Andric if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) 13020b57cec5SDimitry Andric IDecl = CatDecl->getClassInterface(); 13030b57cec5SDimitry Andric else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl)) 13040b57cec5SDimitry Andric IDecl = ImpDecl->getClassInterface(); 13050b57cec5SDimitry Andric } 13060b57cec5SDimitry Andric if (!IDecl) 13070b57cec5SDimitry Andric return; 13080b57cec5SDimitry Andric 13095ffd83dbSDimitry Andric std::string StringClassName = std::string(IDecl->getName()); 13100b57cec5SDimitry Andric StringRef LoweredClassName(StringClassName); 13110b57cec5SDimitry Andric std::string StringLoweredClassName = LoweredClassName.lower(); 13120b57cec5SDimitry Andric LoweredClassName = StringLoweredClassName; 13130b57cec5SDimitry Andric 1314*0fca6ea1SDimitry Andric const IdentifierInfo *MethodIdName = 1315*0fca6ea1SDimitry Andric OM->getSelector().getIdentifierInfoForSlot(0); 13160b57cec5SDimitry Andric // Handle method with no name at its first selector slot; e.g. + (id):(int)x. 13170b57cec5SDimitry Andric if (!MethodIdName) 13180b57cec5SDimitry Andric return; 13190b57cec5SDimitry Andric 13205ffd83dbSDimitry Andric std::string MethodName = std::string(MethodIdName->getName()); 13210b57cec5SDimitry Andric if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) { 13220b57cec5SDimitry Andric StringRef STRefMethodName(MethodName); 13230b57cec5SDimitry Andric size_t len = 0; 13245f757f3fSDimitry Andric if (STRefMethodName.starts_with("standard")) 13250b57cec5SDimitry Andric len = strlen("standard"); 13265f757f3fSDimitry Andric else if (STRefMethodName.starts_with("shared")) 13270b57cec5SDimitry Andric len = strlen("shared"); 13285f757f3fSDimitry Andric else if (STRefMethodName.starts_with("default")) 13290b57cec5SDimitry Andric len = strlen("default"); 13300b57cec5SDimitry Andric else 13310b57cec5SDimitry Andric return; 13325ffd83dbSDimitry Andric MethodName = std::string(STRefMethodName.substr(len)); 13330b57cec5SDimitry Andric } 13340b57cec5SDimitry Andric std::string MethodNameSubStr = MethodName.substr(0, 3); 13350b57cec5SDimitry Andric StringRef MethodNamePrefix(MethodNameSubStr); 13360b57cec5SDimitry Andric std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower(); 13370b57cec5SDimitry Andric MethodNamePrefix = StringLoweredMethodNamePrefix; 13380b57cec5SDimitry Andric size_t Ix = LoweredClassName.rfind(MethodNamePrefix); 13390b57cec5SDimitry Andric if (Ix == StringRef::npos) 13400b57cec5SDimitry Andric return; 13415ffd83dbSDimitry Andric std::string ClassNamePostfix = std::string(LoweredClassName.substr(Ix)); 13420b57cec5SDimitry Andric StringRef LoweredMethodName(MethodName); 13430b57cec5SDimitry Andric std::string StringLoweredMethodName = LoweredMethodName.lower(); 13440b57cec5SDimitry Andric LoweredMethodName = StringLoweredMethodName; 13455f757f3fSDimitry Andric if (!LoweredMethodName.starts_with(ClassNamePostfix)) 13460b57cec5SDimitry Andric return; 13470b57cec5SDimitry Andric if (OIT_Family == OIT_ReturnsSelf) 13480b57cec5SDimitry Andric ReplaceWithClasstype(*this, OM); 13490b57cec5SDimitry Andric else 13500b57cec5SDimitry Andric ReplaceWithInstancetype(Ctx, *this, OM); 13510b57cec5SDimitry Andric } 13520b57cec5SDimitry Andric 13530b57cec5SDimitry Andric static bool IsVoidStarType(QualType Ty) { 13540b57cec5SDimitry Andric if (!Ty->isPointerType()) 13550b57cec5SDimitry Andric return false; 13560b57cec5SDimitry Andric 13570b57cec5SDimitry Andric // Is the type void*? 1358480093f4SDimitry Andric const PointerType* PT = Ty->castAs<PointerType>(); 13590b57cec5SDimitry Andric if (PT->getPointeeType().getUnqualifiedType()->isVoidType()) 13600b57cec5SDimitry Andric return true; 13610b57cec5SDimitry Andric return IsVoidStarType(PT->getPointeeType()); 13620b57cec5SDimitry Andric } 13630b57cec5SDimitry Andric 13640b57cec5SDimitry Andric /// AuditedType - This routine audits the type AT and returns false if it is one of known 13650b57cec5SDimitry Andric /// CF object types or of the "void *" variety. It returns true if we don't care about the type 13660b57cec5SDimitry Andric /// such as a non-pointer or pointers which have no ownership issues (such as "int *"). 13670b57cec5SDimitry Andric static bool AuditedType (QualType AT) { 13680b57cec5SDimitry Andric if (!AT->isAnyPointerType() && !AT->isBlockPointerType()) 13690b57cec5SDimitry Andric return true; 13700b57cec5SDimitry Andric // FIXME. There isn't much we can say about CF pointer type; or is there? 13710b57cec5SDimitry Andric if (ento::coreFoundation::isCFObjectRef(AT) || 13720b57cec5SDimitry Andric IsVoidStarType(AT) || 13730b57cec5SDimitry Andric // If an ObjC object is type, assuming that it is not a CF function and 13740b57cec5SDimitry Andric // that it is an un-audited function. 13750b57cec5SDimitry Andric AT->isObjCObjectPointerType() || AT->isObjCBuiltinType()) 13760b57cec5SDimitry Andric return false; 13770b57cec5SDimitry Andric // All other pointers are assumed audited as harmless. 13780b57cec5SDimitry Andric return true; 13790b57cec5SDimitry Andric } 13800b57cec5SDimitry Andric 13810b57cec5SDimitry Andric void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) { 13820b57cec5SDimitry Andric if (CFFunctionIBCandidates.empty()) 13830b57cec5SDimitry Andric return; 13840b57cec5SDimitry Andric if (!NSAPIObj->isMacroDefined("CF_IMPLICIT_BRIDGING_ENABLED")) { 13850b57cec5SDimitry Andric CFFunctionIBCandidates.clear(); 13860b57cec5SDimitry Andric FileId = FileID(); 13870b57cec5SDimitry Andric return; 13880b57cec5SDimitry Andric } 13890b57cec5SDimitry Andric // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED 13900b57cec5SDimitry Andric const Decl *FirstFD = CFFunctionIBCandidates[0]; 13910b57cec5SDimitry Andric const Decl *LastFD = 13920b57cec5SDimitry Andric CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1]; 13930b57cec5SDimitry Andric const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n"; 13940b57cec5SDimitry Andric edit::Commit commit(*Editor); 13950b57cec5SDimitry Andric commit.insertBefore(FirstFD->getBeginLoc(), PragmaString); 13960b57cec5SDimitry Andric PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n"; 13970b57cec5SDimitry Andric SourceLocation EndLoc = LastFD->getEndLoc(); 13980b57cec5SDimitry Andric // get location just past end of function location. 13990b57cec5SDimitry Andric EndLoc = PP.getLocForEndOfToken(EndLoc); 14000b57cec5SDimitry Andric if (isa<FunctionDecl>(LastFD)) { 14010b57cec5SDimitry Andric // For Methods, EndLoc points to the ending semcolon. So, 14020b57cec5SDimitry Andric // not of these extra work is needed. 14030b57cec5SDimitry Andric Token Tok; 14040b57cec5SDimitry Andric // get locaiton of token that comes after end of function. 14050b57cec5SDimitry Andric bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true); 14060b57cec5SDimitry Andric if (!Failed) 14070b57cec5SDimitry Andric EndLoc = Tok.getLocation(); 14080b57cec5SDimitry Andric } 14090b57cec5SDimitry Andric commit.insertAfterToken(EndLoc, PragmaString); 14100b57cec5SDimitry Andric Editor->commit(commit); 14110b57cec5SDimitry Andric FileId = FileID(); 14120b57cec5SDimitry Andric CFFunctionIBCandidates.clear(); 14130b57cec5SDimitry Andric } 14140b57cec5SDimitry Andric 14150b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) { 14160b57cec5SDimitry Andric if (Decl->isDeprecated()) 14170b57cec5SDimitry Andric return; 14180b57cec5SDimitry Andric 14190b57cec5SDimitry Andric if (Decl->hasAttr<CFAuditedTransferAttr>()) { 14200b57cec5SDimitry Andric assert(CFFunctionIBCandidates.empty() && 14210b57cec5SDimitry Andric "Cannot have audited functions/methods inside user " 14220b57cec5SDimitry Andric "provided CF_IMPLICIT_BRIDGING_ENABLE"); 14230b57cec5SDimitry Andric return; 14240b57cec5SDimitry Andric } 14250b57cec5SDimitry Andric 14260b57cec5SDimitry Andric // Finction must be annotated first. 14270b57cec5SDimitry Andric if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) { 14280b57cec5SDimitry Andric CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl); 14290b57cec5SDimitry Andric if (AuditKind == CF_BRIDGING_ENABLE) { 14300b57cec5SDimitry Andric CFFunctionIBCandidates.push_back(Decl); 14310b57cec5SDimitry Andric if (FileId.isInvalid()) 14320b57cec5SDimitry Andric FileId = PP.getSourceManager().getFileID(Decl->getLocation()); 14330b57cec5SDimitry Andric } 14340b57cec5SDimitry Andric else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) { 14350b57cec5SDimitry Andric if (!CFFunctionIBCandidates.empty()) { 14360b57cec5SDimitry Andric CFFunctionIBCandidates.push_back(Decl); 14370b57cec5SDimitry Andric if (FileId.isInvalid()) 14380b57cec5SDimitry Andric FileId = PP.getSourceManager().getFileID(Decl->getLocation()); 14390b57cec5SDimitry Andric } 14400b57cec5SDimitry Andric } 14410b57cec5SDimitry Andric else 14420b57cec5SDimitry Andric AnnotateImplicitBridging(Ctx); 14430b57cec5SDimitry Andric } 14440b57cec5SDimitry Andric else { 14450b57cec5SDimitry Andric migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl)); 14460b57cec5SDimitry Andric AnnotateImplicitBridging(Ctx); 14470b57cec5SDimitry Andric } 14480b57cec5SDimitry Andric } 14490b57cec5SDimitry Andric 14500b57cec5SDimitry Andric void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx, 14510b57cec5SDimitry Andric const RetainSummary *RS, 14520b57cec5SDimitry Andric const FunctionDecl *FuncDecl, 14530b57cec5SDimitry Andric bool ResultAnnotated) { 14540b57cec5SDimitry Andric // Annotate function. 14550b57cec5SDimitry Andric if (!ResultAnnotated) { 14560b57cec5SDimitry Andric RetEffect Ret = RS->getRetEffect(); 14570b57cec5SDimitry Andric const char *AnnotationString = nullptr; 14580b57cec5SDimitry Andric if (Ret.getObjKind() == ObjKind::CF) { 14590b57cec5SDimitry Andric if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED")) 14600b57cec5SDimitry Andric AnnotationString = " CF_RETURNS_RETAINED"; 14610b57cec5SDimitry Andric else if (Ret.notOwned() && 14620b57cec5SDimitry Andric NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED")) 14630b57cec5SDimitry Andric AnnotationString = " CF_RETURNS_NOT_RETAINED"; 14640b57cec5SDimitry Andric } 14650b57cec5SDimitry Andric else if (Ret.getObjKind() == ObjKind::ObjC) { 14660b57cec5SDimitry Andric if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED")) 14670b57cec5SDimitry Andric AnnotationString = " NS_RETURNS_RETAINED"; 14680b57cec5SDimitry Andric } 14690b57cec5SDimitry Andric 14700b57cec5SDimitry Andric if (AnnotationString) { 14710b57cec5SDimitry Andric edit::Commit commit(*Editor); 14720b57cec5SDimitry Andric commit.insertAfterToken(FuncDecl->getEndLoc(), AnnotationString); 14730b57cec5SDimitry Andric Editor->commit(commit); 14740b57cec5SDimitry Andric } 14750b57cec5SDimitry Andric } 14760b57cec5SDimitry Andric unsigned i = 0; 14770b57cec5SDimitry Andric for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(), 14780b57cec5SDimitry Andric pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) { 14790b57cec5SDimitry Andric const ParmVarDecl *pd = *pi; 14800b57cec5SDimitry Andric ArgEffect AE = RS->getArg(i); 14810b57cec5SDimitry Andric if (AE.getKind() == DecRef && AE.getObjKind() == ObjKind::CF && 14820b57cec5SDimitry Andric !pd->hasAttr<CFConsumedAttr>() && 14830b57cec5SDimitry Andric NSAPIObj->isMacroDefined("CF_CONSUMED")) { 14840b57cec5SDimitry Andric edit::Commit commit(*Editor); 14850b57cec5SDimitry Andric commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); 14860b57cec5SDimitry Andric Editor->commit(commit); 14870b57cec5SDimitry Andric } else if (AE.getKind() == DecRef && AE.getObjKind() == ObjKind::ObjC && 14880b57cec5SDimitry Andric !pd->hasAttr<NSConsumedAttr>() && 14890b57cec5SDimitry Andric NSAPIObj->isMacroDefined("NS_CONSUMED")) { 14900b57cec5SDimitry Andric edit::Commit commit(*Editor); 14910b57cec5SDimitry Andric commit.insertBefore(pd->getLocation(), "NS_CONSUMED "); 14920b57cec5SDimitry Andric Editor->commit(commit); 14930b57cec5SDimitry Andric } 14940b57cec5SDimitry Andric } 14950b57cec5SDimitry Andric } 14960b57cec5SDimitry Andric 14970b57cec5SDimitry Andric ObjCMigrateASTConsumer::CF_BRIDGING_KIND 14980b57cec5SDimitry Andric ObjCMigrateASTConsumer::migrateAddFunctionAnnotation( 14990b57cec5SDimitry Andric ASTContext &Ctx, 15000b57cec5SDimitry Andric const FunctionDecl *FuncDecl) { 15010b57cec5SDimitry Andric if (FuncDecl->hasBody()) 15020b57cec5SDimitry Andric return CF_BRIDGING_NONE; 15030b57cec5SDimitry Andric 15040b57cec5SDimitry Andric const RetainSummary *RS = 15050b57cec5SDimitry Andric getSummaryManager(Ctx).getSummary(AnyCall(FuncDecl)); 15060b57cec5SDimitry Andric bool FuncIsReturnAnnotated = (FuncDecl->hasAttr<CFReturnsRetainedAttr>() || 15070b57cec5SDimitry Andric FuncDecl->hasAttr<CFReturnsNotRetainedAttr>() || 15080b57cec5SDimitry Andric FuncDecl->hasAttr<NSReturnsRetainedAttr>() || 15090b57cec5SDimitry Andric FuncDecl->hasAttr<NSReturnsNotRetainedAttr>() || 15100b57cec5SDimitry Andric FuncDecl->hasAttr<NSReturnsAutoreleasedAttr>()); 15110b57cec5SDimitry Andric 15120b57cec5SDimitry Andric // Trivial case of when function is annotated and has no argument. 15130b57cec5SDimitry Andric if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0) 15140b57cec5SDimitry Andric return CF_BRIDGING_NONE; 15150b57cec5SDimitry Andric 15160b57cec5SDimitry Andric bool ReturnCFAudited = false; 15170b57cec5SDimitry Andric if (!FuncIsReturnAnnotated) { 15180b57cec5SDimitry Andric RetEffect Ret = RS->getRetEffect(); 15190b57cec5SDimitry Andric if (Ret.getObjKind() == ObjKind::CF && 15200b57cec5SDimitry Andric (Ret.isOwned() || Ret.notOwned())) 15210b57cec5SDimitry Andric ReturnCFAudited = true; 15220b57cec5SDimitry Andric else if (!AuditedType(FuncDecl->getReturnType())) 15230b57cec5SDimitry Andric return CF_BRIDGING_NONE; 15240b57cec5SDimitry Andric } 15250b57cec5SDimitry Andric 15260b57cec5SDimitry Andric // At this point result type is audited for potential inclusion. 15270b57cec5SDimitry Andric unsigned i = 0; 15280b57cec5SDimitry Andric bool ArgCFAudited = false; 15290b57cec5SDimitry Andric for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(), 15300b57cec5SDimitry Andric pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) { 15310b57cec5SDimitry Andric const ParmVarDecl *pd = *pi; 15320b57cec5SDimitry Andric ArgEffect AE = RS->getArg(i); 15330b57cec5SDimitry Andric if ((AE.getKind() == DecRef /*CFConsumed annotated*/ || 15340b57cec5SDimitry Andric AE.getKind() == IncRef) && AE.getObjKind() == ObjKind::CF) { 15350b57cec5SDimitry Andric if (AE.getKind() == DecRef && !pd->hasAttr<CFConsumedAttr>()) 15360b57cec5SDimitry Andric ArgCFAudited = true; 15370b57cec5SDimitry Andric else if (AE.getKind() == IncRef) 15380b57cec5SDimitry Andric ArgCFAudited = true; 15390b57cec5SDimitry Andric } else { 15400b57cec5SDimitry Andric QualType AT = pd->getType(); 15410b57cec5SDimitry Andric if (!AuditedType(AT)) { 15420b57cec5SDimitry Andric AddCFAnnotations(Ctx, RS, FuncDecl, FuncIsReturnAnnotated); 15430b57cec5SDimitry Andric return CF_BRIDGING_NONE; 15440b57cec5SDimitry Andric } 15450b57cec5SDimitry Andric } 15460b57cec5SDimitry Andric } 15470b57cec5SDimitry Andric if (ReturnCFAudited || ArgCFAudited) 15480b57cec5SDimitry Andric return CF_BRIDGING_ENABLE; 15490b57cec5SDimitry Andric 15500b57cec5SDimitry Andric return CF_BRIDGING_MAY_INCLUDE; 15510b57cec5SDimitry Andric } 15520b57cec5SDimitry Andric 15530b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx, 15540b57cec5SDimitry Andric ObjCContainerDecl *CDecl) { 15550b57cec5SDimitry Andric if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated()) 15560b57cec5SDimitry Andric return; 15570b57cec5SDimitry Andric 15580b57cec5SDimitry Andric // migrate methods which can have instancetype as their result type. 15590b57cec5SDimitry Andric for (const auto *Method : CDecl->methods()) 15600b57cec5SDimitry Andric migrateCFAnnotation(Ctx, Method); 15610b57cec5SDimitry Andric } 15620b57cec5SDimitry Andric 15630b57cec5SDimitry Andric void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx, 15640b57cec5SDimitry Andric const RetainSummary *RS, 15650b57cec5SDimitry Andric const ObjCMethodDecl *MethodDecl, 15660b57cec5SDimitry Andric bool ResultAnnotated) { 15670b57cec5SDimitry Andric // Annotate function. 15680b57cec5SDimitry Andric if (!ResultAnnotated) { 15690b57cec5SDimitry Andric RetEffect Ret = RS->getRetEffect(); 15700b57cec5SDimitry Andric const char *AnnotationString = nullptr; 15710b57cec5SDimitry Andric if (Ret.getObjKind() == ObjKind::CF) { 15720b57cec5SDimitry Andric if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED")) 15730b57cec5SDimitry Andric AnnotationString = " CF_RETURNS_RETAINED"; 15740b57cec5SDimitry Andric else if (Ret.notOwned() && 15750b57cec5SDimitry Andric NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED")) 15760b57cec5SDimitry Andric AnnotationString = " CF_RETURNS_NOT_RETAINED"; 15770b57cec5SDimitry Andric } 15780b57cec5SDimitry Andric else if (Ret.getObjKind() == ObjKind::ObjC) { 15790b57cec5SDimitry Andric ObjCMethodFamily OMF = MethodDecl->getMethodFamily(); 15800b57cec5SDimitry Andric switch (OMF) { 15810b57cec5SDimitry Andric case clang::OMF_alloc: 15820b57cec5SDimitry Andric case clang::OMF_new: 15830b57cec5SDimitry Andric case clang::OMF_copy: 15840b57cec5SDimitry Andric case clang::OMF_init: 15850b57cec5SDimitry Andric case clang::OMF_mutableCopy: 15860b57cec5SDimitry Andric break; 15870b57cec5SDimitry Andric 15880b57cec5SDimitry Andric default: 15890b57cec5SDimitry Andric if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED")) 15900b57cec5SDimitry Andric AnnotationString = " NS_RETURNS_RETAINED"; 15910b57cec5SDimitry Andric break; 15920b57cec5SDimitry Andric } 15930b57cec5SDimitry Andric } 15940b57cec5SDimitry Andric 15950b57cec5SDimitry Andric if (AnnotationString) { 15960b57cec5SDimitry Andric edit::Commit commit(*Editor); 15970b57cec5SDimitry Andric commit.insertBefore(MethodDecl->getEndLoc(), AnnotationString); 15980b57cec5SDimitry Andric Editor->commit(commit); 15990b57cec5SDimitry Andric } 16000b57cec5SDimitry Andric } 16010b57cec5SDimitry Andric unsigned i = 0; 16020b57cec5SDimitry Andric for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), 16030b57cec5SDimitry Andric pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { 16040b57cec5SDimitry Andric const ParmVarDecl *pd = *pi; 16050b57cec5SDimitry Andric ArgEffect AE = RS->getArg(i); 16060b57cec5SDimitry Andric if (AE.getKind() == DecRef 16070b57cec5SDimitry Andric && AE.getObjKind() == ObjKind::CF 16080b57cec5SDimitry Andric && !pd->hasAttr<CFConsumedAttr>() && 16090b57cec5SDimitry Andric NSAPIObj->isMacroDefined("CF_CONSUMED")) { 16100b57cec5SDimitry Andric edit::Commit commit(*Editor); 16110b57cec5SDimitry Andric commit.insertBefore(pd->getLocation(), "CF_CONSUMED "); 16120b57cec5SDimitry Andric Editor->commit(commit); 16130b57cec5SDimitry Andric } 16140b57cec5SDimitry Andric } 16150b57cec5SDimitry Andric } 16160b57cec5SDimitry Andric 16170b57cec5SDimitry Andric void ObjCMigrateASTConsumer::migrateAddMethodAnnotation( 16180b57cec5SDimitry Andric ASTContext &Ctx, 16190b57cec5SDimitry Andric const ObjCMethodDecl *MethodDecl) { 16200b57cec5SDimitry Andric if (MethodDecl->hasBody() || MethodDecl->isImplicit()) 16210b57cec5SDimitry Andric return; 16220b57cec5SDimitry Andric 16230b57cec5SDimitry Andric const RetainSummary *RS = 16240b57cec5SDimitry Andric getSummaryManager(Ctx).getSummary(AnyCall(MethodDecl)); 16250b57cec5SDimitry Andric 16260b57cec5SDimitry Andric bool MethodIsReturnAnnotated = 16270b57cec5SDimitry Andric (MethodDecl->hasAttr<CFReturnsRetainedAttr>() || 16280b57cec5SDimitry Andric MethodDecl->hasAttr<CFReturnsNotRetainedAttr>() || 16290b57cec5SDimitry Andric MethodDecl->hasAttr<NSReturnsRetainedAttr>() || 16300b57cec5SDimitry Andric MethodDecl->hasAttr<NSReturnsNotRetainedAttr>() || 16310b57cec5SDimitry Andric MethodDecl->hasAttr<NSReturnsAutoreleasedAttr>()); 16320b57cec5SDimitry Andric 16330b57cec5SDimitry Andric if (RS->getReceiverEffect().getKind() == DecRef && 16340b57cec5SDimitry Andric !MethodDecl->hasAttr<NSConsumesSelfAttr>() && 16350b57cec5SDimitry Andric MethodDecl->getMethodFamily() != OMF_init && 16360b57cec5SDimitry Andric MethodDecl->getMethodFamily() != OMF_release && 16370b57cec5SDimitry Andric NSAPIObj->isMacroDefined("NS_CONSUMES_SELF")) { 16380b57cec5SDimitry Andric edit::Commit commit(*Editor); 16390b57cec5SDimitry Andric commit.insertBefore(MethodDecl->getEndLoc(), " NS_CONSUMES_SELF"); 16400b57cec5SDimitry Andric Editor->commit(commit); 16410b57cec5SDimitry Andric } 16420b57cec5SDimitry Andric 16430b57cec5SDimitry Andric // Trivial case of when function is annotated and has no argument. 16440b57cec5SDimitry Andric if (MethodIsReturnAnnotated && 16450b57cec5SDimitry Andric (MethodDecl->param_begin() == MethodDecl->param_end())) 16460b57cec5SDimitry Andric return; 16470b57cec5SDimitry Andric 16480b57cec5SDimitry Andric if (!MethodIsReturnAnnotated) { 16490b57cec5SDimitry Andric RetEffect Ret = RS->getRetEffect(); 16500b57cec5SDimitry Andric if ((Ret.getObjKind() == ObjKind::CF || 16510b57cec5SDimitry Andric Ret.getObjKind() == ObjKind::ObjC) && 16520b57cec5SDimitry Andric (Ret.isOwned() || Ret.notOwned())) { 16530b57cec5SDimitry Andric AddCFAnnotations(Ctx, RS, MethodDecl, false); 16540b57cec5SDimitry Andric return; 16550b57cec5SDimitry Andric } else if (!AuditedType(MethodDecl->getReturnType())) 16560b57cec5SDimitry Andric return; 16570b57cec5SDimitry Andric } 16580b57cec5SDimitry Andric 16590b57cec5SDimitry Andric // At this point result type is either annotated or audited. 16600b57cec5SDimitry Andric unsigned i = 0; 16610b57cec5SDimitry Andric for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(), 16620b57cec5SDimitry Andric pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) { 16630b57cec5SDimitry Andric const ParmVarDecl *pd = *pi; 16640b57cec5SDimitry Andric ArgEffect AE = RS->getArg(i); 16650b57cec5SDimitry Andric if ((AE.getKind() == DecRef && !pd->hasAttr<CFConsumedAttr>()) || 16660b57cec5SDimitry Andric AE.getKind() == IncRef || !AuditedType(pd->getType())) { 16670b57cec5SDimitry Andric AddCFAnnotations(Ctx, RS, MethodDecl, MethodIsReturnAnnotated); 16680b57cec5SDimitry Andric return; 16690b57cec5SDimitry Andric } 16700b57cec5SDimitry Andric } 16710b57cec5SDimitry Andric } 16720b57cec5SDimitry Andric 16730b57cec5SDimitry Andric namespace { 16740b57cec5SDimitry Andric class SuperInitChecker : public RecursiveASTVisitor<SuperInitChecker> { 16750b57cec5SDimitry Andric public: 16760b57cec5SDimitry Andric bool shouldVisitTemplateInstantiations() const { return false; } 16770b57cec5SDimitry Andric bool shouldWalkTypesOfTypeLocs() const { return false; } 16780b57cec5SDimitry Andric 16790b57cec5SDimitry Andric bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 16800b57cec5SDimitry Andric if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) { 16810b57cec5SDimitry Andric if (E->getMethodFamily() == OMF_init) 16820b57cec5SDimitry Andric return false; 16830b57cec5SDimitry Andric } 16840b57cec5SDimitry Andric return true; 16850b57cec5SDimitry Andric } 16860b57cec5SDimitry Andric }; 16870b57cec5SDimitry Andric } // end anonymous namespace 16880b57cec5SDimitry Andric 16890b57cec5SDimitry Andric static bool hasSuperInitCall(const ObjCMethodDecl *MD) { 16900b57cec5SDimitry Andric return !SuperInitChecker().TraverseStmt(MD->getBody()); 16910b57cec5SDimitry Andric } 16920b57cec5SDimitry Andric 16930b57cec5SDimitry Andric void ObjCMigrateASTConsumer::inferDesignatedInitializers( 16940b57cec5SDimitry Andric ASTContext &Ctx, 16950b57cec5SDimitry Andric const ObjCImplementationDecl *ImplD) { 16960b57cec5SDimitry Andric 16970b57cec5SDimitry Andric const ObjCInterfaceDecl *IFace = ImplD->getClassInterface(); 16980b57cec5SDimitry Andric if (!IFace || IFace->hasDesignatedInitializers()) 16990b57cec5SDimitry Andric return; 17000b57cec5SDimitry Andric if (!NSAPIObj->isMacroDefined("NS_DESIGNATED_INITIALIZER")) 17010b57cec5SDimitry Andric return; 17020b57cec5SDimitry Andric 17030b57cec5SDimitry Andric for (const auto *MD : ImplD->instance_methods()) { 17040b57cec5SDimitry Andric if (MD->isDeprecated() || 17050b57cec5SDimitry Andric MD->getMethodFamily() != OMF_init || 17060b57cec5SDimitry Andric MD->isDesignatedInitializerForTheInterface()) 17070b57cec5SDimitry Andric continue; 17080b57cec5SDimitry Andric const ObjCMethodDecl *IFaceM = IFace->getMethod(MD->getSelector(), 17090b57cec5SDimitry Andric /*isInstance=*/true); 17100b57cec5SDimitry Andric if (!IFaceM) 17110b57cec5SDimitry Andric continue; 17120b57cec5SDimitry Andric if (hasSuperInitCall(MD)) { 17130b57cec5SDimitry Andric edit::Commit commit(*Editor); 17140b57cec5SDimitry Andric commit.insert(IFaceM->getEndLoc(), " NS_DESIGNATED_INITIALIZER"); 17150b57cec5SDimitry Andric Editor->commit(commit); 17160b57cec5SDimitry Andric } 17170b57cec5SDimitry Andric } 17180b57cec5SDimitry Andric } 17190b57cec5SDimitry Andric 17200b57cec5SDimitry Andric bool ObjCMigrateASTConsumer::InsertFoundation(ASTContext &Ctx, 17210b57cec5SDimitry Andric SourceLocation Loc) { 17220b57cec5SDimitry Andric if (FoundationIncluded) 17230b57cec5SDimitry Andric return true; 17240b57cec5SDimitry Andric if (Loc.isInvalid()) 17250b57cec5SDimitry Andric return false; 17260b57cec5SDimitry Andric auto *nsEnumId = &Ctx.Idents.get("NS_ENUM"); 17270b57cec5SDimitry Andric if (PP.getMacroDefinitionAtLoc(nsEnumId, Loc)) { 17280b57cec5SDimitry Andric FoundationIncluded = true; 17290b57cec5SDimitry Andric return true; 17300b57cec5SDimitry Andric } 17310b57cec5SDimitry Andric edit::Commit commit(*Editor); 17320b57cec5SDimitry Andric if (Ctx.getLangOpts().Modules) 17330b57cec5SDimitry Andric commit.insert(Loc, "#ifndef NS_ENUM\n@import Foundation;\n#endif\n"); 17340b57cec5SDimitry Andric else 17350b57cec5SDimitry Andric commit.insert(Loc, "#ifndef NS_ENUM\n#import <Foundation/Foundation.h>\n#endif\n"); 17360b57cec5SDimitry Andric Editor->commit(commit); 17370b57cec5SDimitry Andric FoundationIncluded = true; 17380b57cec5SDimitry Andric return true; 17390b57cec5SDimitry Andric } 17400b57cec5SDimitry Andric 17410b57cec5SDimitry Andric namespace { 17420b57cec5SDimitry Andric 17430b57cec5SDimitry Andric class RewritesReceiver : public edit::EditsReceiver { 17440b57cec5SDimitry Andric Rewriter &Rewrite; 17450b57cec5SDimitry Andric 17460b57cec5SDimitry Andric public: 17470b57cec5SDimitry Andric RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } 17480b57cec5SDimitry Andric 17490b57cec5SDimitry Andric void insert(SourceLocation loc, StringRef text) override { 17500b57cec5SDimitry Andric Rewrite.InsertText(loc, text); 17510b57cec5SDimitry Andric } 17520b57cec5SDimitry Andric void replace(CharSourceRange range, StringRef text) override { 17530b57cec5SDimitry Andric Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); 17540b57cec5SDimitry Andric } 17550b57cec5SDimitry Andric }; 17560b57cec5SDimitry Andric 17570b57cec5SDimitry Andric class JSONEditWriter : public edit::EditsReceiver { 17580b57cec5SDimitry Andric SourceManager &SourceMgr; 17590b57cec5SDimitry Andric llvm::raw_ostream &OS; 17600b57cec5SDimitry Andric 17610b57cec5SDimitry Andric public: 17620b57cec5SDimitry Andric JSONEditWriter(SourceManager &SM, llvm::raw_ostream &OS) 17630b57cec5SDimitry Andric : SourceMgr(SM), OS(OS) { 17640b57cec5SDimitry Andric OS << "[\n"; 17650b57cec5SDimitry Andric } 17660b57cec5SDimitry Andric ~JSONEditWriter() override { OS << "]\n"; } 17670b57cec5SDimitry Andric 17680b57cec5SDimitry Andric private: 17690b57cec5SDimitry Andric struct EntryWriter { 17700b57cec5SDimitry Andric SourceManager &SourceMgr; 17710b57cec5SDimitry Andric llvm::raw_ostream &OS; 17720b57cec5SDimitry Andric 17730b57cec5SDimitry Andric EntryWriter(SourceManager &SM, llvm::raw_ostream &OS) 17740b57cec5SDimitry Andric : SourceMgr(SM), OS(OS) { 17750b57cec5SDimitry Andric OS << " {\n"; 17760b57cec5SDimitry Andric } 17770b57cec5SDimitry Andric ~EntryWriter() { 17780b57cec5SDimitry Andric OS << " },\n"; 17790b57cec5SDimitry Andric } 17800b57cec5SDimitry Andric 17810b57cec5SDimitry Andric void writeLoc(SourceLocation Loc) { 17820b57cec5SDimitry Andric FileID FID; 17830b57cec5SDimitry Andric unsigned Offset; 17840b57cec5SDimitry Andric std::tie(FID, Offset) = SourceMgr.getDecomposedLoc(Loc); 17850b57cec5SDimitry Andric assert(FID.isValid()); 17860b57cec5SDimitry Andric SmallString<200> Path = 17875f757f3fSDimitry Andric StringRef(SourceMgr.getFileEntryRefForID(FID)->getName()); 17880b57cec5SDimitry Andric llvm::sys::fs::make_absolute(Path); 17890b57cec5SDimitry Andric OS << " \"file\": \""; 17900b57cec5SDimitry Andric OS.write_escaped(Path.str()) << "\",\n"; 17910b57cec5SDimitry Andric OS << " \"offset\": " << Offset << ",\n"; 17920b57cec5SDimitry Andric } 17930b57cec5SDimitry Andric 17940b57cec5SDimitry Andric void writeRemove(CharSourceRange Range) { 17950b57cec5SDimitry Andric assert(Range.isCharRange()); 17960b57cec5SDimitry Andric std::pair<FileID, unsigned> Begin = 17970b57cec5SDimitry Andric SourceMgr.getDecomposedLoc(Range.getBegin()); 17980b57cec5SDimitry Andric std::pair<FileID, unsigned> End = 17990b57cec5SDimitry Andric SourceMgr.getDecomposedLoc(Range.getEnd()); 18000b57cec5SDimitry Andric assert(Begin.first == End.first); 18010b57cec5SDimitry Andric assert(Begin.second <= End.second); 18020b57cec5SDimitry Andric unsigned Length = End.second - Begin.second; 18030b57cec5SDimitry Andric 18040b57cec5SDimitry Andric OS << " \"remove\": " << Length << ",\n"; 18050b57cec5SDimitry Andric } 18060b57cec5SDimitry Andric 18070b57cec5SDimitry Andric void writeText(StringRef Text) { 18080b57cec5SDimitry Andric OS << " \"text\": \""; 18090b57cec5SDimitry Andric OS.write_escaped(Text) << "\",\n"; 18100b57cec5SDimitry Andric } 18110b57cec5SDimitry Andric }; 18120b57cec5SDimitry Andric 18130b57cec5SDimitry Andric void insert(SourceLocation Loc, StringRef Text) override { 18140b57cec5SDimitry Andric EntryWriter Writer(SourceMgr, OS); 18150b57cec5SDimitry Andric Writer.writeLoc(Loc); 18160b57cec5SDimitry Andric Writer.writeText(Text); 18170b57cec5SDimitry Andric } 18180b57cec5SDimitry Andric 18190b57cec5SDimitry Andric void replace(CharSourceRange Range, StringRef Text) override { 18200b57cec5SDimitry Andric EntryWriter Writer(SourceMgr, OS); 18210b57cec5SDimitry Andric Writer.writeLoc(Range.getBegin()); 18220b57cec5SDimitry Andric Writer.writeRemove(Range); 18230b57cec5SDimitry Andric Writer.writeText(Text); 18240b57cec5SDimitry Andric } 18250b57cec5SDimitry Andric 18260b57cec5SDimitry Andric void remove(CharSourceRange Range) override { 18270b57cec5SDimitry Andric EntryWriter Writer(SourceMgr, OS); 18280b57cec5SDimitry Andric Writer.writeLoc(Range.getBegin()); 18290b57cec5SDimitry Andric Writer.writeRemove(Range); 18300b57cec5SDimitry Andric } 18310b57cec5SDimitry Andric }; 18320b57cec5SDimitry Andric 18330b57cec5SDimitry Andric } // end anonymous namespace 18340b57cec5SDimitry Andric 18350b57cec5SDimitry Andric void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { 18360b57cec5SDimitry Andric 18370b57cec5SDimitry Andric TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl(); 18380b57cec5SDimitry Andric if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) { 18390b57cec5SDimitry Andric for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end(); 18400b57cec5SDimitry Andric D != DEnd; ++D) { 18410b57cec5SDimitry Andric FileID FID = PP.getSourceManager().getFileID((*D)->getLocation()); 18420b57cec5SDimitry Andric if (FID.isValid()) 18430b57cec5SDimitry Andric if (FileId.isValid() && FileId != FID) { 18440b57cec5SDimitry Andric if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 18450b57cec5SDimitry Andric AnnotateImplicitBridging(Ctx); 18460b57cec5SDimitry Andric } 18470b57cec5SDimitry Andric 18480b57cec5SDimitry Andric if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D)) 18490b57cec5SDimitry Andric if (canModify(CDecl)) 18500b57cec5SDimitry Andric migrateObjCContainerDecl(Ctx, CDecl); 18510b57cec5SDimitry Andric if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) { 18520b57cec5SDimitry Andric if (canModify(CatDecl)) 18530b57cec5SDimitry Andric migrateObjCContainerDecl(Ctx, CatDecl); 18540b57cec5SDimitry Andric } 18550b57cec5SDimitry Andric else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) { 18560b57cec5SDimitry Andric ObjCProtocolDecls.insert(PDecl->getCanonicalDecl()); 18570b57cec5SDimitry Andric if (canModify(PDecl)) 18580b57cec5SDimitry Andric migrateObjCContainerDecl(Ctx, PDecl); 18590b57cec5SDimitry Andric } 18600b57cec5SDimitry Andric else if (const ObjCImplementationDecl *ImpDecl = 18610b57cec5SDimitry Andric dyn_cast<ObjCImplementationDecl>(*D)) { 18620b57cec5SDimitry Andric if ((ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance) && 18630b57cec5SDimitry Andric canModify(ImpDecl)) 18640b57cec5SDimitry Andric migrateProtocolConformance(Ctx, ImpDecl); 18650b57cec5SDimitry Andric } 18660b57cec5SDimitry Andric else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) { 18670b57cec5SDimitry Andric if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros)) 18680b57cec5SDimitry Andric continue; 18690b57cec5SDimitry Andric if (!canModify(ED)) 18700b57cec5SDimitry Andric continue; 18710b57cec5SDimitry Andric DeclContext::decl_iterator N = D; 18720b57cec5SDimitry Andric if (++N != DEnd) { 18730b57cec5SDimitry Andric const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N); 18740b57cec5SDimitry Andric if (migrateNSEnumDecl(Ctx, ED, TD) && TD) 18750b57cec5SDimitry Andric D++; 18760b57cec5SDimitry Andric } 18770b57cec5SDimitry Andric else 18780b57cec5SDimitry Andric migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */nullptr); 18790b57cec5SDimitry Andric } 18800b57cec5SDimitry Andric else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) { 18810b57cec5SDimitry Andric if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros)) 18820b57cec5SDimitry Andric continue; 18830b57cec5SDimitry Andric if (!canModify(TD)) 18840b57cec5SDimitry Andric continue; 18850b57cec5SDimitry Andric DeclContext::decl_iterator N = D; 18860b57cec5SDimitry Andric if (++N == DEnd) 18870b57cec5SDimitry Andric continue; 18880b57cec5SDimitry Andric if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) { 18890b57cec5SDimitry Andric if (canModify(ED)) { 18900b57cec5SDimitry Andric if (++N != DEnd) 18910b57cec5SDimitry Andric if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) { 18920b57cec5SDimitry Andric // prefer typedef-follows-enum to enum-follows-typedef pattern. 18930b57cec5SDimitry Andric if (migrateNSEnumDecl(Ctx, ED, TDF)) { 18940b57cec5SDimitry Andric ++D; ++D; 18950b57cec5SDimitry Andric CacheObjCNSIntegerTypedefed(TD); 18960b57cec5SDimitry Andric continue; 18970b57cec5SDimitry Andric } 18980b57cec5SDimitry Andric } 18990b57cec5SDimitry Andric if (migrateNSEnumDecl(Ctx, ED, TD)) { 19000b57cec5SDimitry Andric ++D; 19010b57cec5SDimitry Andric continue; 19020b57cec5SDimitry Andric } 19030b57cec5SDimitry Andric } 19040b57cec5SDimitry Andric } 19050b57cec5SDimitry Andric CacheObjCNSIntegerTypedefed(TD); 19060b57cec5SDimitry Andric } 19070b57cec5SDimitry Andric else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) { 19080b57cec5SDimitry Andric if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && 19090b57cec5SDimitry Andric canModify(FD)) 19100b57cec5SDimitry Andric migrateCFAnnotation(Ctx, FD); 19110b57cec5SDimitry Andric } 19120b57cec5SDimitry Andric 19130b57cec5SDimitry Andric if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) { 19140b57cec5SDimitry Andric bool CanModify = canModify(CDecl); 19150b57cec5SDimitry Andric // migrate methods which can have instancetype as their result type. 19160b57cec5SDimitry Andric if ((ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype) && 19170b57cec5SDimitry Andric CanModify) 19180b57cec5SDimitry Andric migrateAllMethodInstaceType(Ctx, CDecl); 19190b57cec5SDimitry Andric // annotate methods with CF annotations. 19200b57cec5SDimitry Andric if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) && 19210b57cec5SDimitry Andric CanModify) 19220b57cec5SDimitry Andric migrateARCSafeAnnotation(Ctx, CDecl); 19230b57cec5SDimitry Andric } 19240b57cec5SDimitry Andric 19250b57cec5SDimitry Andric if (const ObjCImplementationDecl * 19260b57cec5SDimitry Andric ImplD = dyn_cast<ObjCImplementationDecl>(*D)) { 19270b57cec5SDimitry Andric if ((ASTMigrateActions & FrontendOptions::ObjCMT_DesignatedInitializer) && 19280b57cec5SDimitry Andric canModify(ImplD)) 19290b57cec5SDimitry Andric inferDesignatedInitializers(Ctx, ImplD); 19300b57cec5SDimitry Andric } 19310b57cec5SDimitry Andric } 19320b57cec5SDimitry Andric if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) 19330b57cec5SDimitry Andric AnnotateImplicitBridging(Ctx); 19340b57cec5SDimitry Andric } 19350b57cec5SDimitry Andric 19360b57cec5SDimitry Andric if (IsOutputFile) { 19370b57cec5SDimitry Andric std::error_code EC; 1938a7dea167SDimitry Andric llvm::raw_fd_ostream OS(MigrateDir, EC, llvm::sys::fs::OF_None); 19390b57cec5SDimitry Andric if (EC) { 19400b57cec5SDimitry Andric DiagnosticsEngine &Diags = Ctx.getDiagnostics(); 19410b57cec5SDimitry Andric Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, "%0")) 19420b57cec5SDimitry Andric << EC.message(); 19430b57cec5SDimitry Andric return; 19440b57cec5SDimitry Andric } 19450b57cec5SDimitry Andric 19460b57cec5SDimitry Andric JSONEditWriter Writer(Ctx.getSourceManager(), OS); 19470b57cec5SDimitry Andric Editor->applyRewrites(Writer); 19480b57cec5SDimitry Andric return; 19490b57cec5SDimitry Andric } 19500b57cec5SDimitry Andric 19510b57cec5SDimitry Andric Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts()); 19520b57cec5SDimitry Andric RewritesReceiver Rec(rewriter); 19530b57cec5SDimitry Andric Editor->applyRewrites(Rec); 19540b57cec5SDimitry Andric 19550b57cec5SDimitry Andric for (Rewriter::buffer_iterator 19560b57cec5SDimitry Andric I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { 19570b57cec5SDimitry Andric FileID FID = I->first; 19580b57cec5SDimitry Andric RewriteBuffer &buf = I->second; 1959bdd1243dSDimitry Andric OptionalFileEntryRef file = 1960bdd1243dSDimitry Andric Ctx.getSourceManager().getFileEntryRefForID(FID); 19610b57cec5SDimitry Andric assert(file); 19620b57cec5SDimitry Andric SmallString<512> newText; 19630b57cec5SDimitry Andric llvm::raw_svector_ostream vecOS(newText); 19640b57cec5SDimitry Andric buf.write(vecOS); 19650b57cec5SDimitry Andric std::unique_ptr<llvm::MemoryBuffer> memBuf( 1966*0fca6ea1SDimitry Andric llvm::MemoryBuffer::getMemBufferCopy(newText.str(), file->getName())); 19670b57cec5SDimitry Andric SmallString<64> filePath(file->getName()); 19680b57cec5SDimitry Andric FileMgr.FixupRelativePath(filePath); 19690b57cec5SDimitry Andric Remapper.remap(filePath.str(), std::move(memBuf)); 19700b57cec5SDimitry Andric } 19710b57cec5SDimitry Andric 19720b57cec5SDimitry Andric if (IsOutputFile) { 19730b57cec5SDimitry Andric Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics()); 19740b57cec5SDimitry Andric } else { 19750b57cec5SDimitry Andric Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics()); 19760b57cec5SDimitry Andric } 19770b57cec5SDimitry Andric } 19780b57cec5SDimitry Andric 19790b57cec5SDimitry Andric bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) { 19800b57cec5SDimitry Andric CI.getDiagnostics().setIgnoreAllWarnings(true); 19810b57cec5SDimitry Andric return true; 19820b57cec5SDimitry Andric } 19830b57cec5SDimitry Andric 1984349cc55cSDimitry Andric static std::vector<std::string> getAllowListFilenames(StringRef DirPath) { 19850b57cec5SDimitry Andric using namespace llvm::sys::fs; 19860b57cec5SDimitry Andric using namespace llvm::sys::path; 19870b57cec5SDimitry Andric 19880b57cec5SDimitry Andric std::vector<std::string> Filenames; 19890b57cec5SDimitry Andric if (DirPath.empty() || !is_directory(DirPath)) 19900b57cec5SDimitry Andric return Filenames; 19910b57cec5SDimitry Andric 19920b57cec5SDimitry Andric std::error_code EC; 19930b57cec5SDimitry Andric directory_iterator DI = directory_iterator(DirPath, EC); 19940b57cec5SDimitry Andric directory_iterator DE; 19950b57cec5SDimitry Andric for (; !EC && DI != DE; DI = DI.increment(EC)) { 19960b57cec5SDimitry Andric if (is_regular_file(DI->path())) 19975ffd83dbSDimitry Andric Filenames.push_back(std::string(filename(DI->path()))); 19980b57cec5SDimitry Andric } 19990b57cec5SDimitry Andric 20000b57cec5SDimitry Andric return Filenames; 20010b57cec5SDimitry Andric } 20020b57cec5SDimitry Andric 20030b57cec5SDimitry Andric std::unique_ptr<ASTConsumer> 20040b57cec5SDimitry Andric MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 20050b57cec5SDimitry Andric PPConditionalDirectiveRecord * 20060b57cec5SDimitry Andric PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager()); 20070b57cec5SDimitry Andric unsigned ObjCMTAction = CI.getFrontendOpts().ObjCMTAction; 20080b57cec5SDimitry Andric unsigned ObjCMTOpts = ObjCMTAction; 20090b57cec5SDimitry Andric // These are companion flags, they do not enable transformations. 20100b57cec5SDimitry Andric ObjCMTOpts &= ~(FrontendOptions::ObjCMT_AtomicProperty | 20110b57cec5SDimitry Andric FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty); 20120b57cec5SDimitry Andric if (ObjCMTOpts == FrontendOptions::ObjCMT_None) { 20130b57cec5SDimitry Andric // If no specific option was given, enable literals+subscripting transforms 20140b57cec5SDimitry Andric // by default. 2015349cc55cSDimitry Andric ObjCMTAction |= 2016349cc55cSDimitry Andric FrontendOptions::ObjCMT_Literals | FrontendOptions::ObjCMT_Subscripting; 20170b57cec5SDimitry Andric } 20180b57cec5SDimitry Andric CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec)); 2019349cc55cSDimitry Andric std::vector<std::string> AllowList = 2020349cc55cSDimitry Andric getAllowListFilenames(CI.getFrontendOpts().ObjCMTAllowListPath); 2021a7dea167SDimitry Andric return std::make_unique<ObjCMigrateASTConsumer>( 20220b57cec5SDimitry Andric CI.getFrontendOpts().OutputFile, ObjCMTAction, Remapper, 20230b57cec5SDimitry Andric CI.getFileManager(), PPRec, CI.getPreprocessor(), 2024349cc55cSDimitry Andric /*isOutputFile=*/true, AllowList); 20250b57cec5SDimitry Andric } 20260b57cec5SDimitry Andric 20270b57cec5SDimitry Andric namespace { 20280b57cec5SDimitry Andric struct EditEntry { 2029bdd1243dSDimitry Andric OptionalFileEntryRef File; 2030e8d8bef9SDimitry Andric unsigned Offset = 0; 2031e8d8bef9SDimitry Andric unsigned RemoveLen = 0; 20320b57cec5SDimitry Andric std::string Text; 20330b57cec5SDimitry Andric }; 20340b57cec5SDimitry Andric } // end anonymous namespace 20350b57cec5SDimitry Andric 20360b57cec5SDimitry Andric namespace llvm { 20370b57cec5SDimitry Andric template<> struct DenseMapInfo<EditEntry> { 20380b57cec5SDimitry Andric static inline EditEntry getEmptyKey() { 20390b57cec5SDimitry Andric EditEntry Entry; 20400b57cec5SDimitry Andric Entry.Offset = unsigned(-1); 20410b57cec5SDimitry Andric return Entry; 20420b57cec5SDimitry Andric } 20430b57cec5SDimitry Andric static inline EditEntry getTombstoneKey() { 20440b57cec5SDimitry Andric EditEntry Entry; 20450b57cec5SDimitry Andric Entry.Offset = unsigned(-2); 20460b57cec5SDimitry Andric return Entry; 20470b57cec5SDimitry Andric } 20480b57cec5SDimitry Andric static unsigned getHashValue(const EditEntry& Val) { 2049e8d8bef9SDimitry Andric return (unsigned)llvm::hash_combine(Val.File, Val.Offset, Val.RemoveLen, 2050e8d8bef9SDimitry Andric Val.Text); 20510b57cec5SDimitry Andric } 20520b57cec5SDimitry Andric static bool isEqual(const EditEntry &LHS, const EditEntry &RHS) { 20530b57cec5SDimitry Andric return LHS.File == RHS.File && 20540b57cec5SDimitry Andric LHS.Offset == RHS.Offset && 20550b57cec5SDimitry Andric LHS.RemoveLen == RHS.RemoveLen && 20560b57cec5SDimitry Andric LHS.Text == RHS.Text; 20570b57cec5SDimitry Andric } 20580b57cec5SDimitry Andric }; 20590b57cec5SDimitry Andric } // end namespace llvm 20600b57cec5SDimitry Andric 20610b57cec5SDimitry Andric namespace { 20620b57cec5SDimitry Andric class RemapFileParser { 20630b57cec5SDimitry Andric FileManager &FileMgr; 20640b57cec5SDimitry Andric 20650b57cec5SDimitry Andric public: 20660b57cec5SDimitry Andric RemapFileParser(FileManager &FileMgr) : FileMgr(FileMgr) { } 20670b57cec5SDimitry Andric 20680b57cec5SDimitry Andric bool parse(StringRef File, SmallVectorImpl<EditEntry> &Entries) { 20690b57cec5SDimitry Andric using namespace llvm::yaml; 20700b57cec5SDimitry Andric 20710b57cec5SDimitry Andric llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr = 20720b57cec5SDimitry Andric llvm::MemoryBuffer::getFile(File); 20730b57cec5SDimitry Andric if (!FileBufOrErr) 20740b57cec5SDimitry Andric return true; 20750b57cec5SDimitry Andric 20760b57cec5SDimitry Andric llvm::SourceMgr SM; 20770b57cec5SDimitry Andric Stream YAMLStream(FileBufOrErr.get()->getMemBufferRef(), SM); 20780b57cec5SDimitry Andric document_iterator I = YAMLStream.begin(); 20790b57cec5SDimitry Andric if (I == YAMLStream.end()) 20800b57cec5SDimitry Andric return true; 20810b57cec5SDimitry Andric Node *Root = I->getRoot(); 20820b57cec5SDimitry Andric if (!Root) 20830b57cec5SDimitry Andric return true; 20840b57cec5SDimitry Andric 20850b57cec5SDimitry Andric SequenceNode *SeqNode = dyn_cast<SequenceNode>(Root); 20860b57cec5SDimitry Andric if (!SeqNode) 20870b57cec5SDimitry Andric return true; 20880b57cec5SDimitry Andric 20890b57cec5SDimitry Andric for (SequenceNode::iterator 20900b57cec5SDimitry Andric AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) { 20910b57cec5SDimitry Andric MappingNode *MapNode = dyn_cast<MappingNode>(&*AI); 20920b57cec5SDimitry Andric if (!MapNode) 20930b57cec5SDimitry Andric continue; 20940b57cec5SDimitry Andric parseEdit(MapNode, Entries); 20950b57cec5SDimitry Andric } 20960b57cec5SDimitry Andric 20970b57cec5SDimitry Andric return false; 20980b57cec5SDimitry Andric } 20990b57cec5SDimitry Andric 21000b57cec5SDimitry Andric private: 21010b57cec5SDimitry Andric void parseEdit(llvm::yaml::MappingNode *Node, 21020b57cec5SDimitry Andric SmallVectorImpl<EditEntry> &Entries) { 21030b57cec5SDimitry Andric using namespace llvm::yaml; 21040b57cec5SDimitry Andric EditEntry Entry; 21050b57cec5SDimitry Andric bool Ignore = false; 21060b57cec5SDimitry Andric 21070b57cec5SDimitry Andric for (MappingNode::iterator 21080b57cec5SDimitry Andric KVI = Node->begin(), KVE = Node->end(); KVI != KVE; ++KVI) { 21090b57cec5SDimitry Andric ScalarNode *KeyString = dyn_cast<ScalarNode>((*KVI).getKey()); 21100b57cec5SDimitry Andric if (!KeyString) 21110b57cec5SDimitry Andric continue; 21120b57cec5SDimitry Andric SmallString<10> KeyStorage; 21130b57cec5SDimitry Andric StringRef Key = KeyString->getValue(KeyStorage); 21140b57cec5SDimitry Andric 21150b57cec5SDimitry Andric ScalarNode *ValueString = dyn_cast<ScalarNode>((*KVI).getValue()); 21160b57cec5SDimitry Andric if (!ValueString) 21170b57cec5SDimitry Andric continue; 21180b57cec5SDimitry Andric SmallString<64> ValueStorage; 21190b57cec5SDimitry Andric StringRef Val = ValueString->getValue(ValueStorage); 21200b57cec5SDimitry Andric 21210b57cec5SDimitry Andric if (Key == "file") { 2122e8d8bef9SDimitry Andric if (auto File = FileMgr.getOptionalFileRef(Val)) 2123e8d8bef9SDimitry Andric Entry.File = File; 2124a7dea167SDimitry Andric else 21250b57cec5SDimitry Andric Ignore = true; 21260b57cec5SDimitry Andric } else if (Key == "offset") { 21270b57cec5SDimitry Andric if (Val.getAsInteger(10, Entry.Offset)) 21280b57cec5SDimitry Andric Ignore = true; 21290b57cec5SDimitry Andric } else if (Key == "remove") { 21300b57cec5SDimitry Andric if (Val.getAsInteger(10, Entry.RemoveLen)) 21310b57cec5SDimitry Andric Ignore = true; 21320b57cec5SDimitry Andric } else if (Key == "text") { 21335ffd83dbSDimitry Andric Entry.Text = std::string(Val); 21340b57cec5SDimitry Andric } 21350b57cec5SDimitry Andric } 21360b57cec5SDimitry Andric 21370b57cec5SDimitry Andric if (!Ignore) 21380b57cec5SDimitry Andric Entries.push_back(Entry); 21390b57cec5SDimitry Andric } 21400b57cec5SDimitry Andric }; 21410b57cec5SDimitry Andric } // end anonymous namespace 21420b57cec5SDimitry Andric 21430b57cec5SDimitry Andric static bool reportDiag(const Twine &Err, DiagnosticsEngine &Diag) { 21440b57cec5SDimitry Andric Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0")) 21450b57cec5SDimitry Andric << Err.str(); 21460b57cec5SDimitry Andric return true; 21470b57cec5SDimitry Andric } 21480b57cec5SDimitry Andric 2149e8d8bef9SDimitry Andric static std::string applyEditsToTemp(FileEntryRef FE, 21500b57cec5SDimitry Andric ArrayRef<EditEntry> Edits, 21510b57cec5SDimitry Andric FileManager &FileMgr, 21520b57cec5SDimitry Andric DiagnosticsEngine &Diag) { 21530b57cec5SDimitry Andric using namespace llvm::sys; 21540b57cec5SDimitry Andric 21550b57cec5SDimitry Andric SourceManager SM(Diag, FileMgr); 21560b57cec5SDimitry Andric FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User); 21570b57cec5SDimitry Andric LangOptions LangOpts; 21580b57cec5SDimitry Andric edit::EditedSource Editor(SM, LangOpts); 21590b57cec5SDimitry Andric for (ArrayRef<EditEntry>::iterator 21600b57cec5SDimitry Andric I = Edits.begin(), E = Edits.end(); I != E; ++I) { 21610b57cec5SDimitry Andric const EditEntry &Entry = *I; 21620b57cec5SDimitry Andric assert(Entry.File == FE); 21630b57cec5SDimitry Andric SourceLocation Loc = 21640b57cec5SDimitry Andric SM.getLocForStartOfFile(FID).getLocWithOffset(Entry.Offset); 21650b57cec5SDimitry Andric CharSourceRange Range; 21660b57cec5SDimitry Andric if (Entry.RemoveLen != 0) { 21670b57cec5SDimitry Andric Range = CharSourceRange::getCharRange(Loc, 21680b57cec5SDimitry Andric Loc.getLocWithOffset(Entry.RemoveLen)); 21690b57cec5SDimitry Andric } 21700b57cec5SDimitry Andric 21710b57cec5SDimitry Andric edit::Commit commit(Editor); 21720b57cec5SDimitry Andric if (Range.isInvalid()) { 21730b57cec5SDimitry Andric commit.insert(Loc, Entry.Text); 21740b57cec5SDimitry Andric } else if (Entry.Text.empty()) { 21750b57cec5SDimitry Andric commit.remove(Range); 21760b57cec5SDimitry Andric } else { 21770b57cec5SDimitry Andric commit.replace(Range, Entry.Text); 21780b57cec5SDimitry Andric } 21790b57cec5SDimitry Andric Editor.commit(commit); 21800b57cec5SDimitry Andric } 21810b57cec5SDimitry Andric 21820b57cec5SDimitry Andric Rewriter rewriter(SM, LangOpts); 21830b57cec5SDimitry Andric RewritesReceiver Rec(rewriter); 21840b57cec5SDimitry Andric Editor.applyRewrites(Rec, /*adjustRemovals=*/false); 21850b57cec5SDimitry Andric 21860b57cec5SDimitry Andric const RewriteBuffer *Buf = rewriter.getRewriteBufferFor(FID); 21870b57cec5SDimitry Andric SmallString<512> NewText; 21880b57cec5SDimitry Andric llvm::raw_svector_ostream OS(NewText); 21890b57cec5SDimitry Andric Buf->write(OS); 21900b57cec5SDimitry Andric 21910b57cec5SDimitry Andric SmallString<64> TempPath; 21920b57cec5SDimitry Andric int FD; 2193e8d8bef9SDimitry Andric if (fs::createTemporaryFile(path::filename(FE.getName()), 2194e8d8bef9SDimitry Andric path::extension(FE.getName()).drop_front(), FD, 21950b57cec5SDimitry Andric TempPath)) { 21960b57cec5SDimitry Andric reportDiag("Could not create file: " + TempPath.str(), Diag); 21970b57cec5SDimitry Andric return std::string(); 21980b57cec5SDimitry Andric } 21990b57cec5SDimitry Andric 22000b57cec5SDimitry Andric llvm::raw_fd_ostream TmpOut(FD, /*shouldClose=*/true); 22010b57cec5SDimitry Andric TmpOut.write(NewText.data(), NewText.size()); 22020b57cec5SDimitry Andric TmpOut.close(); 22030b57cec5SDimitry Andric 22047a6dacacSDimitry Andric return std::string(TempPath); 22050b57cec5SDimitry Andric } 22060b57cec5SDimitry Andric 22070b57cec5SDimitry Andric bool arcmt::getFileRemappingsFromFileList( 22080b57cec5SDimitry Andric std::vector<std::pair<std::string,std::string> > &remap, 22090b57cec5SDimitry Andric ArrayRef<StringRef> remapFiles, 22100b57cec5SDimitry Andric DiagnosticConsumer *DiagClient) { 22110b57cec5SDimitry Andric bool hasErrorOccurred = false; 22120b57cec5SDimitry Andric 22130b57cec5SDimitry Andric FileSystemOptions FSOpts; 22140b57cec5SDimitry Andric FileManager FileMgr(FSOpts); 22150b57cec5SDimitry Andric RemapFileParser Parser(FileMgr); 22160b57cec5SDimitry Andric 22170b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); 22180b57cec5SDimitry Andric IntrusiveRefCntPtr<DiagnosticsEngine> Diags( 22190b57cec5SDimitry Andric new DiagnosticsEngine(DiagID, new DiagnosticOptions, 22200b57cec5SDimitry Andric DiagClient, /*ShouldOwnClient=*/false)); 22210b57cec5SDimitry Andric 2222e8d8bef9SDimitry Andric typedef llvm::DenseMap<FileEntryRef, std::vector<EditEntry> > 22230b57cec5SDimitry Andric FileEditEntriesTy; 22240b57cec5SDimitry Andric FileEditEntriesTy FileEditEntries; 22250b57cec5SDimitry Andric 22260b57cec5SDimitry Andric llvm::DenseSet<EditEntry> EntriesSet; 22270b57cec5SDimitry Andric 22280b57cec5SDimitry Andric for (ArrayRef<StringRef>::iterator 22290b57cec5SDimitry Andric I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) { 22300b57cec5SDimitry Andric SmallVector<EditEntry, 16> Entries; 22310b57cec5SDimitry Andric if (Parser.parse(*I, Entries)) 22320b57cec5SDimitry Andric continue; 22330b57cec5SDimitry Andric 22340b57cec5SDimitry Andric for (SmallVectorImpl<EditEntry>::iterator 22350b57cec5SDimitry Andric EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) { 22360b57cec5SDimitry Andric EditEntry &Entry = *EI; 22370b57cec5SDimitry Andric if (!Entry.File) 22380b57cec5SDimitry Andric continue; 22390b57cec5SDimitry Andric std::pair<llvm::DenseSet<EditEntry>::iterator, bool> 22400b57cec5SDimitry Andric Insert = EntriesSet.insert(Entry); 22410b57cec5SDimitry Andric if (!Insert.second) 22420b57cec5SDimitry Andric continue; 22430b57cec5SDimitry Andric 2244e8d8bef9SDimitry Andric FileEditEntries[*Entry.File].push_back(Entry); 22450b57cec5SDimitry Andric } 22460b57cec5SDimitry Andric } 22470b57cec5SDimitry Andric 22480b57cec5SDimitry Andric for (FileEditEntriesTy::iterator 22490b57cec5SDimitry Andric I = FileEditEntries.begin(), E = FileEditEntries.end(); I != E; ++I) { 22500b57cec5SDimitry Andric std::string TempFile = applyEditsToTemp(I->first, I->second, 22510b57cec5SDimitry Andric FileMgr, *Diags); 22520b57cec5SDimitry Andric if (TempFile.empty()) { 22530b57cec5SDimitry Andric hasErrorOccurred = true; 22540b57cec5SDimitry Andric continue; 22550b57cec5SDimitry Andric } 22560b57cec5SDimitry Andric 2257e8d8bef9SDimitry Andric remap.emplace_back(std::string(I->first.getName()), TempFile); 22580b57cec5SDimitry Andric } 22590b57cec5SDimitry Andric 22600b57cec5SDimitry Andric return hasErrorOccurred; 22610b57cec5SDimitry Andric } 2262