xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/ARCMigrate/ObjCMT.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg 
97330f729Sjoerg #include "Transforms.h"
107330f729Sjoerg #include "clang/Analysis/RetainSummaryManager.h"
117330f729Sjoerg #include "clang/ARCMigrate/ARCMT.h"
127330f729Sjoerg #include "clang/ARCMigrate/ARCMTActions.h"
137330f729Sjoerg #include "clang/AST/ASTConsumer.h"
147330f729Sjoerg #include "clang/AST/ASTContext.h"
157330f729Sjoerg #include "clang/AST/Attr.h"
167330f729Sjoerg #include "clang/AST/NSAPI.h"
177330f729Sjoerg #include "clang/AST/ParentMap.h"
187330f729Sjoerg #include "clang/AST/RecursiveASTVisitor.h"
197330f729Sjoerg #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
207330f729Sjoerg #include "clang/Basic/FileManager.h"
217330f729Sjoerg #include "clang/Edit/Commit.h"
227330f729Sjoerg #include "clang/Edit/EditedSource.h"
237330f729Sjoerg #include "clang/Edit/EditsReceiver.h"
247330f729Sjoerg #include "clang/Edit/Rewriters.h"
257330f729Sjoerg #include "clang/Frontend/CompilerInstance.h"
267330f729Sjoerg #include "clang/Frontend/MultiplexConsumer.h"
277330f729Sjoerg #include "clang/Lex/PPConditionalDirectiveRecord.h"
287330f729Sjoerg #include "clang/Lex/Preprocessor.h"
297330f729Sjoerg #include "clang/Rewrite/Core/Rewriter.h"
307330f729Sjoerg #include "llvm/ADT/SmallString.h"
317330f729Sjoerg #include "llvm/ADT/StringSet.h"
327330f729Sjoerg #include "llvm/Support/Path.h"
337330f729Sjoerg #include "llvm/Support/SourceMgr.h"
347330f729Sjoerg #include "llvm/Support/YAMLParser.h"
357330f729Sjoerg 
367330f729Sjoerg using namespace clang;
377330f729Sjoerg using namespace arcmt;
387330f729Sjoerg using namespace ento;
397330f729Sjoerg 
407330f729Sjoerg namespace {
417330f729Sjoerg 
427330f729Sjoerg class ObjCMigrateASTConsumer : public ASTConsumer {
437330f729Sjoerg   enum CF_BRIDGING_KIND {
447330f729Sjoerg     CF_BRIDGING_NONE,
457330f729Sjoerg     CF_BRIDGING_ENABLE,
467330f729Sjoerg     CF_BRIDGING_MAY_INCLUDE
477330f729Sjoerg   };
487330f729Sjoerg 
497330f729Sjoerg   void migrateDecl(Decl *D);
507330f729Sjoerg   void migrateObjCContainerDecl(ASTContext &Ctx, ObjCContainerDecl *D);
517330f729Sjoerg   void migrateProtocolConformance(ASTContext &Ctx,
527330f729Sjoerg                                   const ObjCImplementationDecl *ImpDecl);
537330f729Sjoerg   void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl);
547330f729Sjoerg   bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,
557330f729Sjoerg                      const TypedefDecl *TypedefDcl);
567330f729Sjoerg   void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);
577330f729Sjoerg   void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,
587330f729Sjoerg                                  ObjCMethodDecl *OM);
597330f729Sjoerg   bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM);
607330f729Sjoerg   void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM);
617330f729Sjoerg   void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P);
627330f729Sjoerg   void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl,
637330f729Sjoerg                             ObjCMethodDecl *OM,
647330f729Sjoerg                             ObjCInstanceTypeFamily OIT_Family = OIT_None);
657330f729Sjoerg 
667330f729Sjoerg   void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl);
677330f729Sjoerg   void AddCFAnnotations(ASTContext &Ctx,
687330f729Sjoerg                         const RetainSummary *RS,
697330f729Sjoerg                         const FunctionDecl *FuncDecl, bool ResultAnnotated);
707330f729Sjoerg   void AddCFAnnotations(ASTContext &Ctx,
717330f729Sjoerg                         const RetainSummary *RS,
727330f729Sjoerg                         const ObjCMethodDecl *MethodDecl, bool ResultAnnotated);
737330f729Sjoerg 
747330f729Sjoerg   void AnnotateImplicitBridging(ASTContext &Ctx);
757330f729Sjoerg 
767330f729Sjoerg   CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx,
777330f729Sjoerg                                                 const FunctionDecl *FuncDecl);
787330f729Sjoerg 
797330f729Sjoerg   void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl);
807330f729Sjoerg 
817330f729Sjoerg   void migrateAddMethodAnnotation(ASTContext &Ctx,
827330f729Sjoerg                                   const ObjCMethodDecl *MethodDecl);
837330f729Sjoerg 
847330f729Sjoerg   void inferDesignatedInitializers(ASTContext &Ctx,
857330f729Sjoerg                                    const ObjCImplementationDecl *ImplD);
867330f729Sjoerg 
877330f729Sjoerg   bool InsertFoundation(ASTContext &Ctx, SourceLocation Loc);
887330f729Sjoerg 
897330f729Sjoerg   std::unique_ptr<RetainSummaryManager> Summaries;
907330f729Sjoerg 
917330f729Sjoerg public:
927330f729Sjoerg   std::string MigrateDir;
937330f729Sjoerg   unsigned ASTMigrateActions;
947330f729Sjoerg   FileID FileId;
957330f729Sjoerg   const TypedefDecl *NSIntegerTypedefed;
967330f729Sjoerg   const TypedefDecl *NSUIntegerTypedefed;
977330f729Sjoerg   std::unique_ptr<NSAPI> NSAPIObj;
987330f729Sjoerg   std::unique_ptr<edit::EditedSource> Editor;
997330f729Sjoerg   FileRemapper &Remapper;
1007330f729Sjoerg   FileManager &FileMgr;
1017330f729Sjoerg   const PPConditionalDirectiveRecord *PPRec;
1027330f729Sjoerg   Preprocessor &PP;
1037330f729Sjoerg   bool IsOutputFile;
1047330f729Sjoerg   bool FoundationIncluded;
1057330f729Sjoerg   llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;
1067330f729Sjoerg   llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates;
1077330f729Sjoerg   llvm::StringSet<> WhiteListFilenames;
1087330f729Sjoerg 
getSummaryManager(ASTContext & Ctx)1097330f729Sjoerg   RetainSummaryManager &getSummaryManager(ASTContext &Ctx) {
1107330f729Sjoerg     if (!Summaries)
1117330f729Sjoerg       Summaries.reset(new RetainSummaryManager(Ctx,
1127330f729Sjoerg                                                /*TrackNSCFObjects=*/true,
1137330f729Sjoerg                                                /*trackOSObjects=*/false));
1147330f729Sjoerg     return *Summaries;
1157330f729Sjoerg   }
1167330f729Sjoerg 
ObjCMigrateASTConsumer(StringRef migrateDir,unsigned astMigrateActions,FileRemapper & remapper,FileManager & fileMgr,const PPConditionalDirectiveRecord * PPRec,Preprocessor & PP,bool isOutputFile,ArrayRef<std::string> WhiteList)117*e038c9c4Sjoerg   ObjCMigrateASTConsumer(StringRef migrateDir, unsigned astMigrateActions,
118*e038c9c4Sjoerg                          FileRemapper &remapper, FileManager &fileMgr,
1197330f729Sjoerg                          const PPConditionalDirectiveRecord *PPRec,
120*e038c9c4Sjoerg                          Preprocessor &PP, bool isOutputFile,
1217330f729Sjoerg                          ArrayRef<std::string> WhiteList)
122*e038c9c4Sjoerg       : MigrateDir(migrateDir), ASTMigrateActions(astMigrateActions),
1237330f729Sjoerg         NSIntegerTypedefed(nullptr), NSUIntegerTypedefed(nullptr),
1247330f729Sjoerg         Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
125*e038c9c4Sjoerg         IsOutputFile(isOutputFile), FoundationIncluded(false) {
1267330f729Sjoerg     // FIXME: StringSet should have insert(iter, iter) to use here.
1277330f729Sjoerg     for (const std::string &Val : WhiteList)
1287330f729Sjoerg       WhiteListFilenames.insert(Val);
1297330f729Sjoerg   }
1307330f729Sjoerg 
1317330f729Sjoerg protected:
Initialize(ASTContext & Context)1327330f729Sjoerg   void Initialize(ASTContext &Context) override {
1337330f729Sjoerg     NSAPIObj.reset(new NSAPI(Context));
1347330f729Sjoerg     Editor.reset(new edit::EditedSource(Context.getSourceManager(),
1357330f729Sjoerg                                         Context.getLangOpts(),
1367330f729Sjoerg                                         PPRec));
1377330f729Sjoerg   }
1387330f729Sjoerg 
HandleTopLevelDecl(DeclGroupRef DG)1397330f729Sjoerg   bool HandleTopLevelDecl(DeclGroupRef DG) override {
1407330f729Sjoerg     for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
1417330f729Sjoerg       migrateDecl(*I);
1427330f729Sjoerg     return true;
1437330f729Sjoerg   }
HandleInterestingDecl(DeclGroupRef DG)1447330f729Sjoerg   void HandleInterestingDecl(DeclGroupRef DG) override {
1457330f729Sjoerg     // Ignore decls from the PCH.
1467330f729Sjoerg   }
HandleTopLevelDeclInObjCContainer(DeclGroupRef DG)1477330f729Sjoerg   void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override {
1487330f729Sjoerg     ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
1497330f729Sjoerg   }
1507330f729Sjoerg 
1517330f729Sjoerg   void HandleTranslationUnit(ASTContext &Ctx) override;
1527330f729Sjoerg 
canModifyFile(StringRef Path)1537330f729Sjoerg   bool canModifyFile(StringRef Path) {
1547330f729Sjoerg     if (WhiteListFilenames.empty())
1557330f729Sjoerg       return true;
1567330f729Sjoerg     return WhiteListFilenames.find(llvm::sys::path::filename(Path))
1577330f729Sjoerg         != WhiteListFilenames.end();
1587330f729Sjoerg   }
canModifyFile(Optional<FileEntryRef> FE)159*e038c9c4Sjoerg   bool canModifyFile(Optional<FileEntryRef> FE) {
1607330f729Sjoerg     if (!FE)
1617330f729Sjoerg       return false;
1627330f729Sjoerg     return canModifyFile(FE->getName());
1637330f729Sjoerg   }
canModifyFile(FileID FID)1647330f729Sjoerg   bool canModifyFile(FileID FID) {
1657330f729Sjoerg     if (FID.isInvalid())
1667330f729Sjoerg       return false;
167*e038c9c4Sjoerg     return canModifyFile(PP.getSourceManager().getFileEntryRefForID(FID));
1687330f729Sjoerg   }
1697330f729Sjoerg 
canModify(const Decl * D)1707330f729Sjoerg   bool canModify(const Decl *D) {
1717330f729Sjoerg     if (!D)
1727330f729Sjoerg       return false;
1737330f729Sjoerg     if (const ObjCCategoryImplDecl *CatImpl = dyn_cast<ObjCCategoryImplDecl>(D))
1747330f729Sjoerg       return canModify(CatImpl->getCategoryDecl());
1757330f729Sjoerg     if (const ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(D))
1767330f729Sjoerg       return canModify(Impl->getClassInterface());
1777330f729Sjoerg     if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
1787330f729Sjoerg       return canModify(cast<Decl>(MD->getDeclContext()));
1797330f729Sjoerg 
1807330f729Sjoerg     FileID FID = PP.getSourceManager().getFileID(D->getLocation());
1817330f729Sjoerg     return canModifyFile(FID);
1827330f729Sjoerg   }
1837330f729Sjoerg };
1847330f729Sjoerg 
1857330f729Sjoerg } // end anonymous namespace
1867330f729Sjoerg 
ObjCMigrateAction(std::unique_ptr<FrontendAction> WrappedAction,StringRef migrateDir,unsigned migrateAction)1877330f729Sjoerg ObjCMigrateAction::ObjCMigrateAction(
188*e038c9c4Sjoerg     std::unique_ptr<FrontendAction> WrappedAction, StringRef migrateDir,
1897330f729Sjoerg     unsigned migrateAction)
1907330f729Sjoerg     : WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir),
191*e038c9c4Sjoerg       ObjCMigAction(migrateAction), CompInst(nullptr) {
1927330f729Sjoerg   if (MigrateDir.empty())
1937330f729Sjoerg     MigrateDir = "."; // user current directory if none is given.
1947330f729Sjoerg }
1957330f729Sjoerg 
1967330f729Sjoerg std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)1977330f729Sjoerg ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
1987330f729Sjoerg   PPConditionalDirectiveRecord *
1997330f729Sjoerg     PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
2007330f729Sjoerg   CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec));
2017330f729Sjoerg   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
2027330f729Sjoerg   Consumers.push_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile));
2037330f729Sjoerg   Consumers.push_back(std::make_unique<ObjCMigrateASTConsumer>(
2047330f729Sjoerg       MigrateDir, ObjCMigAction, Remapper, CompInst->getFileManager(), PPRec,
2057330f729Sjoerg       CompInst->getPreprocessor(), false, None));
2067330f729Sjoerg   return std::make_unique<MultiplexConsumer>(std::move(Consumers));
2077330f729Sjoerg }
2087330f729Sjoerg 
BeginInvocation(CompilerInstance & CI)2097330f729Sjoerg bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
2107330f729Sjoerg   Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
2117330f729Sjoerg                         /*ignoreIfFilesChanged=*/true);
2127330f729Sjoerg   CompInst = &CI;
2137330f729Sjoerg   CI.getDiagnostics().setIgnoreAllWarnings(true);
2147330f729Sjoerg   return true;
2157330f729Sjoerg }
2167330f729Sjoerg 
2177330f729Sjoerg namespace {
2187330f729Sjoerg   // FIXME. This duplicates one in RewriteObjCFoundationAPI.cpp
subscriptOperatorNeedsParens(const Expr * FullExpr)2197330f729Sjoerg   bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
2207330f729Sjoerg     const Expr* Expr = FullExpr->IgnoreImpCasts();
2217330f729Sjoerg     return !(isa<ArraySubscriptExpr>(Expr) || isa<CallExpr>(Expr) ||
2227330f729Sjoerg              isa<DeclRefExpr>(Expr) || isa<CXXNamedCastExpr>(Expr) ||
2237330f729Sjoerg              isa<CXXConstructExpr>(Expr) || isa<CXXThisExpr>(Expr) ||
2247330f729Sjoerg              isa<CXXTypeidExpr>(Expr) ||
2257330f729Sjoerg              isa<CXXUnresolvedConstructExpr>(Expr) ||
2267330f729Sjoerg              isa<ObjCMessageExpr>(Expr) || isa<ObjCPropertyRefExpr>(Expr) ||
2277330f729Sjoerg              isa<ObjCProtocolExpr>(Expr) || isa<MemberExpr>(Expr) ||
2287330f729Sjoerg              isa<ObjCIvarRefExpr>(Expr) || isa<ParenExpr>(FullExpr) ||
2297330f729Sjoerg              isa<ParenListExpr>(Expr) || isa<SizeOfPackExpr>(Expr));
2307330f729Sjoerg   }
2317330f729Sjoerg 
2327330f729Sjoerg   /// - Rewrite message expression for Objective-C setter and getters into
2337330f729Sjoerg   /// property-dot syntax.
rewriteToPropertyDotSyntax(const ObjCMessageExpr * Msg,Preprocessor & PP,const NSAPI & NS,edit::Commit & commit,const ParentMap * PMap)2347330f729Sjoerg   bool rewriteToPropertyDotSyntax(const ObjCMessageExpr *Msg,
2357330f729Sjoerg                                   Preprocessor &PP,
2367330f729Sjoerg                                   const NSAPI &NS, edit::Commit &commit,
2377330f729Sjoerg                                   const ParentMap *PMap) {
2387330f729Sjoerg     if (!Msg || Msg->isImplicit() ||
2397330f729Sjoerg         (Msg->getReceiverKind() != ObjCMessageExpr::Instance &&
2407330f729Sjoerg          Msg->getReceiverKind() != ObjCMessageExpr::SuperInstance))
2417330f729Sjoerg       return false;
2427330f729Sjoerg     if (const Expr *Receiver = Msg->getInstanceReceiver())
2437330f729Sjoerg       if (Receiver->getType()->isObjCBuiltinType())
2447330f729Sjoerg         return false;
2457330f729Sjoerg 
2467330f729Sjoerg     const ObjCMethodDecl *Method = Msg->getMethodDecl();
2477330f729Sjoerg     if (!Method)
2487330f729Sjoerg       return false;
2497330f729Sjoerg     if (!Method->isPropertyAccessor())
2507330f729Sjoerg       return false;
2517330f729Sjoerg 
2527330f729Sjoerg     const ObjCPropertyDecl *Prop = Method->findPropertyDecl();
2537330f729Sjoerg     if (!Prop)
2547330f729Sjoerg       return false;
2557330f729Sjoerg 
2567330f729Sjoerg     SourceRange MsgRange = Msg->getSourceRange();
2577330f729Sjoerg     bool ReceiverIsSuper =
2587330f729Sjoerg       (Msg->getReceiverKind() == ObjCMessageExpr::SuperInstance);
2597330f729Sjoerg     // for 'super' receiver is nullptr.
2607330f729Sjoerg     const Expr *receiver = Msg->getInstanceReceiver();
2617330f729Sjoerg     bool NeedsParen =
2627330f729Sjoerg       ReceiverIsSuper ? false : subscriptOperatorNeedsParens(receiver);
2637330f729Sjoerg     bool IsGetter = (Msg->getNumArgs() == 0);
2647330f729Sjoerg     if (IsGetter) {
2657330f729Sjoerg       // Find space location range between receiver expression and getter method.
2667330f729Sjoerg       SourceLocation BegLoc =
2677330f729Sjoerg           ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc();
2687330f729Sjoerg       BegLoc = PP.getLocForEndOfToken(BegLoc);
2697330f729Sjoerg       SourceLocation EndLoc = Msg->getSelectorLoc(0);
2707330f729Sjoerg       SourceRange SpaceRange(BegLoc, EndLoc);
2717330f729Sjoerg       std::string PropertyDotString;
2727330f729Sjoerg       // rewrite getter method expression into: receiver.property or
2737330f729Sjoerg       // (receiver).property
2747330f729Sjoerg       if (NeedsParen) {
2757330f729Sjoerg         commit.insertBefore(receiver->getBeginLoc(), "(");
2767330f729Sjoerg         PropertyDotString = ").";
2777330f729Sjoerg       }
2787330f729Sjoerg       else
2797330f729Sjoerg         PropertyDotString = ".";
2807330f729Sjoerg       PropertyDotString += Prop->getName();
2817330f729Sjoerg       commit.replace(SpaceRange, PropertyDotString);
2827330f729Sjoerg 
2837330f729Sjoerg       // remove '[' ']'
2847330f729Sjoerg       commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), "");
2857330f729Sjoerg       commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), "");
2867330f729Sjoerg     } else {
2877330f729Sjoerg       if (NeedsParen)
2887330f729Sjoerg         commit.insertWrap("(", receiver->getSourceRange(), ")");
2897330f729Sjoerg       std::string PropertyDotString = ".";
2907330f729Sjoerg       PropertyDotString += Prop->getName();
2917330f729Sjoerg       PropertyDotString += " =";
2927330f729Sjoerg       const Expr*const* Args = Msg->getArgs();
2937330f729Sjoerg       const Expr *RHS = Args[0];
2947330f729Sjoerg       if (!RHS)
2957330f729Sjoerg         return false;
2967330f729Sjoerg       SourceLocation BegLoc =
2977330f729Sjoerg           ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc();
2987330f729Sjoerg       BegLoc = PP.getLocForEndOfToken(BegLoc);
2997330f729Sjoerg       SourceLocation EndLoc = RHS->getBeginLoc();
3007330f729Sjoerg       EndLoc = EndLoc.getLocWithOffset(-1);
3017330f729Sjoerg       const char *colon = PP.getSourceManager().getCharacterData(EndLoc);
3027330f729Sjoerg       // Add a space after '=' if there is no space between RHS and '='
3037330f729Sjoerg       if (colon && colon[0] == ':')
3047330f729Sjoerg         PropertyDotString += " ";
3057330f729Sjoerg       SourceRange Range(BegLoc, EndLoc);
3067330f729Sjoerg       commit.replace(Range, PropertyDotString);
3077330f729Sjoerg       // remove '[' ']'
3087330f729Sjoerg       commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), "");
3097330f729Sjoerg       commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), "");
3107330f729Sjoerg     }
3117330f729Sjoerg     return true;
3127330f729Sjoerg   }
3137330f729Sjoerg 
3147330f729Sjoerg class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
3157330f729Sjoerg   ObjCMigrateASTConsumer &Consumer;
3167330f729Sjoerg   ParentMap &PMap;
3177330f729Sjoerg 
3187330f729Sjoerg public:
ObjCMigrator(ObjCMigrateASTConsumer & consumer,ParentMap & PMap)3197330f729Sjoerg   ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
3207330f729Sjoerg     : Consumer(consumer), PMap(PMap) { }
3217330f729Sjoerg 
shouldVisitTemplateInstantiations() const3227330f729Sjoerg   bool shouldVisitTemplateInstantiations() const { return false; }
shouldWalkTypesOfTypeLocs() const3237330f729Sjoerg   bool shouldWalkTypesOfTypeLocs() const { return false; }
3247330f729Sjoerg 
VisitObjCMessageExpr(ObjCMessageExpr * E)3257330f729Sjoerg   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
3267330f729Sjoerg     if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) {
3277330f729Sjoerg       edit::Commit commit(*Consumer.Editor);
3287330f729Sjoerg       edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
3297330f729Sjoerg       Consumer.Editor->commit(commit);
3307330f729Sjoerg     }
3317330f729Sjoerg 
3327330f729Sjoerg     if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) {
3337330f729Sjoerg       edit::Commit commit(*Consumer.Editor);
3347330f729Sjoerg       edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
3357330f729Sjoerg       Consumer.Editor->commit(commit);
3367330f729Sjoerg     }
3377330f729Sjoerg 
3387330f729Sjoerg     if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_PropertyDotSyntax) {
3397330f729Sjoerg       edit::Commit commit(*Consumer.Editor);
3407330f729Sjoerg       rewriteToPropertyDotSyntax(E, Consumer.PP, *Consumer.NSAPIObj,
3417330f729Sjoerg                                  commit, &PMap);
3427330f729Sjoerg       Consumer.Editor->commit(commit);
3437330f729Sjoerg     }
3447330f729Sjoerg 
3457330f729Sjoerg     return true;
3467330f729Sjoerg   }
3477330f729Sjoerg 
TraverseObjCMessageExpr(ObjCMessageExpr * E)3487330f729Sjoerg   bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
3497330f729Sjoerg     // Do depth first; we want to rewrite the subexpressions first so that if
3507330f729Sjoerg     // we have to move expressions we will move them already rewritten.
3517330f729Sjoerg     for (Stmt *SubStmt : E->children())
3527330f729Sjoerg       if (!TraverseStmt(SubStmt))
3537330f729Sjoerg         return false;
3547330f729Sjoerg 
3557330f729Sjoerg     return WalkUpFromObjCMessageExpr(E);
3567330f729Sjoerg   }
3577330f729Sjoerg };
3587330f729Sjoerg 
3597330f729Sjoerg class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
3607330f729Sjoerg   ObjCMigrateASTConsumer &Consumer;
3617330f729Sjoerg   std::unique_ptr<ParentMap> PMap;
3627330f729Sjoerg 
3637330f729Sjoerg public:
BodyMigrator(ObjCMigrateASTConsumer & consumer)3647330f729Sjoerg   BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
3657330f729Sjoerg 
shouldVisitTemplateInstantiations() const3667330f729Sjoerg   bool shouldVisitTemplateInstantiations() const { return false; }
shouldWalkTypesOfTypeLocs() const3677330f729Sjoerg   bool shouldWalkTypesOfTypeLocs() const { return false; }
3687330f729Sjoerg 
TraverseStmt(Stmt * S)3697330f729Sjoerg   bool TraverseStmt(Stmt *S) {
3707330f729Sjoerg     PMap.reset(new ParentMap(S));
3717330f729Sjoerg     ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
3727330f729Sjoerg     return true;
3737330f729Sjoerg   }
3747330f729Sjoerg };
3757330f729Sjoerg } // end anonymous namespace
3767330f729Sjoerg 
migrateDecl(Decl * D)3777330f729Sjoerg void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
3787330f729Sjoerg   if (!D)
3797330f729Sjoerg     return;
3807330f729Sjoerg   if (isa<ObjCMethodDecl>(D))
3817330f729Sjoerg     return; // Wait for the ObjC container declaration.
3827330f729Sjoerg 
3837330f729Sjoerg   BodyMigrator(*this).TraverseDecl(D);
3847330f729Sjoerg }
3857330f729Sjoerg 
append_attr(std::string & PropertyString,const char * attr,bool & LParenAdded)3867330f729Sjoerg static void append_attr(std::string &PropertyString, const char *attr,
3877330f729Sjoerg                         bool &LParenAdded) {
3887330f729Sjoerg   if (!LParenAdded) {
3897330f729Sjoerg     PropertyString += "(";
3907330f729Sjoerg     LParenAdded = true;
3917330f729Sjoerg   }
3927330f729Sjoerg   else
3937330f729Sjoerg     PropertyString += ", ";
3947330f729Sjoerg   PropertyString += attr;
3957330f729Sjoerg }
3967330f729Sjoerg 
3977330f729Sjoerg static
MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString,const std::string & TypeString,const char * name)3987330f729Sjoerg void MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString,
3997330f729Sjoerg                                                const std::string& TypeString,
4007330f729Sjoerg                                                const char *name) {
4017330f729Sjoerg   const char *argPtr = TypeString.c_str();
4027330f729Sjoerg   int paren = 0;
4037330f729Sjoerg   while (*argPtr) {
4047330f729Sjoerg     switch (*argPtr) {
4057330f729Sjoerg       case '(':
4067330f729Sjoerg         PropertyString += *argPtr;
4077330f729Sjoerg         paren++;
4087330f729Sjoerg         break;
4097330f729Sjoerg       case ')':
4107330f729Sjoerg         PropertyString += *argPtr;
4117330f729Sjoerg         paren--;
4127330f729Sjoerg         break;
4137330f729Sjoerg       case '^':
4147330f729Sjoerg       case '*':
4157330f729Sjoerg         PropertyString += (*argPtr);
4167330f729Sjoerg         if (paren == 1) {
4177330f729Sjoerg           PropertyString += name;
4187330f729Sjoerg           name = "";
4197330f729Sjoerg         }
4207330f729Sjoerg         break;
4217330f729Sjoerg       default:
4227330f729Sjoerg         PropertyString += *argPtr;
4237330f729Sjoerg         break;
4247330f729Sjoerg     }
4257330f729Sjoerg     argPtr++;
4267330f729Sjoerg   }
4277330f729Sjoerg }
4287330f729Sjoerg 
PropertyMemoryAttribute(ASTContext & Context,QualType ArgType)4297330f729Sjoerg static const char *PropertyMemoryAttribute(ASTContext &Context, QualType ArgType) {
4307330f729Sjoerg   Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
4317330f729Sjoerg   bool RetainableObject = ArgType->isObjCRetainableType();
4327330f729Sjoerg   if (RetainableObject &&
4337330f729Sjoerg       (propertyLifetime == Qualifiers::OCL_Strong
4347330f729Sjoerg        || propertyLifetime == Qualifiers::OCL_None)) {
4357330f729Sjoerg     if (const ObjCObjectPointerType *ObjPtrTy =
4367330f729Sjoerg         ArgType->getAs<ObjCObjectPointerType>()) {
4377330f729Sjoerg       ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
4387330f729Sjoerg       if (IDecl &&
4397330f729Sjoerg           IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
4407330f729Sjoerg         return "copy";
4417330f729Sjoerg       else
4427330f729Sjoerg         return "strong";
4437330f729Sjoerg     }
4447330f729Sjoerg     else if (ArgType->isBlockPointerType())
4457330f729Sjoerg       return "copy";
4467330f729Sjoerg   } else if (propertyLifetime == Qualifiers::OCL_Weak)
4477330f729Sjoerg     // TODO. More precise determination of 'weak' attribute requires
4487330f729Sjoerg     // looking into setter's implementation for backing weak ivar.
4497330f729Sjoerg     return "weak";
4507330f729Sjoerg   else if (RetainableObject)
4517330f729Sjoerg     return ArgType->isBlockPointerType() ? "copy" : "strong";
4527330f729Sjoerg   return nullptr;
4537330f729Sjoerg }
4547330f729Sjoerg 
rewriteToObjCProperty(const ObjCMethodDecl * Getter,const ObjCMethodDecl * Setter,const NSAPI & NS,edit::Commit & commit,unsigned LengthOfPrefix,bool Atomic,bool UseNsIosOnlyMacro,bool AvailabilityArgsMatch)4557330f729Sjoerg static void rewriteToObjCProperty(const ObjCMethodDecl *Getter,
4567330f729Sjoerg                                   const ObjCMethodDecl *Setter,
4577330f729Sjoerg                                   const NSAPI &NS, edit::Commit &commit,
4587330f729Sjoerg                                   unsigned LengthOfPrefix,
4597330f729Sjoerg                                   bool Atomic, bool UseNsIosOnlyMacro,
4607330f729Sjoerg                                   bool AvailabilityArgsMatch) {
4617330f729Sjoerg   ASTContext &Context = NS.getASTContext();
4627330f729Sjoerg   bool LParenAdded = false;
4637330f729Sjoerg   std::string PropertyString = "@property ";
4647330f729Sjoerg   if (UseNsIosOnlyMacro && NS.isMacroDefined("NS_NONATOMIC_IOSONLY")) {
4657330f729Sjoerg     PropertyString += "(NS_NONATOMIC_IOSONLY";
4667330f729Sjoerg     LParenAdded = true;
4677330f729Sjoerg   } else if (!Atomic) {
4687330f729Sjoerg     PropertyString += "(nonatomic";
4697330f729Sjoerg     LParenAdded = true;
4707330f729Sjoerg   }
4717330f729Sjoerg 
4727330f729Sjoerg   std::string PropertyNameString = Getter->getNameAsString();
4737330f729Sjoerg   StringRef PropertyName(PropertyNameString);
4747330f729Sjoerg   if (LengthOfPrefix > 0) {
4757330f729Sjoerg     if (!LParenAdded) {
4767330f729Sjoerg       PropertyString += "(getter=";
4777330f729Sjoerg       LParenAdded = true;
4787330f729Sjoerg     }
4797330f729Sjoerg     else
4807330f729Sjoerg       PropertyString += ", getter=";
4817330f729Sjoerg     PropertyString += PropertyNameString;
4827330f729Sjoerg   }
4837330f729Sjoerg   // Property with no setter may be suggested as a 'readonly' property.
4847330f729Sjoerg   if (!Setter)
4857330f729Sjoerg     append_attr(PropertyString, "readonly", LParenAdded);
4867330f729Sjoerg 
4877330f729Sjoerg 
4887330f729Sjoerg   // Short circuit 'delegate' properties that contain the name "delegate" or
4897330f729Sjoerg   // "dataSource", or have exact name "target" to have 'assign' attribute.
4907330f729Sjoerg   if (PropertyName.equals("target") ||
4917330f729Sjoerg       (PropertyName.find("delegate") != StringRef::npos) ||
4927330f729Sjoerg       (PropertyName.find("dataSource") != StringRef::npos)) {
4937330f729Sjoerg     QualType QT = Getter->getReturnType();
4947330f729Sjoerg     if (!QT->isRealType())
4957330f729Sjoerg       append_attr(PropertyString, "assign", LParenAdded);
4967330f729Sjoerg   } else if (!Setter) {
4977330f729Sjoerg     QualType ResType = Context.getCanonicalType(Getter->getReturnType());
4987330f729Sjoerg     if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ResType))
4997330f729Sjoerg       append_attr(PropertyString, MemoryManagementAttr, LParenAdded);
5007330f729Sjoerg   } else {
5017330f729Sjoerg     const ParmVarDecl *argDecl = *Setter->param_begin();
5027330f729Sjoerg     QualType ArgType = Context.getCanonicalType(argDecl->getType());
5037330f729Sjoerg     if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ArgType))
5047330f729Sjoerg       append_attr(PropertyString, MemoryManagementAttr, LParenAdded);
5057330f729Sjoerg   }
5067330f729Sjoerg   if (LParenAdded)
5077330f729Sjoerg     PropertyString += ')';
5087330f729Sjoerg   QualType RT = Getter->getReturnType();
5097330f729Sjoerg   if (!isa<TypedefType>(RT)) {
5107330f729Sjoerg     // strip off any ARC lifetime qualifier.
5117330f729Sjoerg     QualType CanResultTy = Context.getCanonicalType(RT);
5127330f729Sjoerg     if (CanResultTy.getQualifiers().hasObjCLifetime()) {
5137330f729Sjoerg       Qualifiers Qs = CanResultTy.getQualifiers();
5147330f729Sjoerg       Qs.removeObjCLifetime();
5157330f729Sjoerg       RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
5167330f729Sjoerg     }
5177330f729Sjoerg   }
5187330f729Sjoerg   PropertyString += " ";
5197330f729Sjoerg   PrintingPolicy SubPolicy(Context.getPrintingPolicy());
5207330f729Sjoerg   SubPolicy.SuppressStrongLifetime = true;
5217330f729Sjoerg   SubPolicy.SuppressLifetimeQualifiers = true;
5227330f729Sjoerg   std::string TypeString = RT.getAsString(SubPolicy);
5237330f729Sjoerg   if (LengthOfPrefix > 0) {
5247330f729Sjoerg     // property name must strip off "is" and lower case the first character
5257330f729Sjoerg     // after that; e.g. isContinuous will become continuous.
5267330f729Sjoerg     StringRef PropertyNameStringRef(PropertyNameString);
5277330f729Sjoerg     PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix);
528*e038c9c4Sjoerg     PropertyNameString = std::string(PropertyNameStringRef);
5297330f729Sjoerg     bool NoLowering = (isUppercase(PropertyNameString[0]) &&
5307330f729Sjoerg                        PropertyNameString.size() > 1 &&
5317330f729Sjoerg                        isUppercase(PropertyNameString[1]));
5327330f729Sjoerg     if (!NoLowering)
5337330f729Sjoerg       PropertyNameString[0] = toLowercase(PropertyNameString[0]);
5347330f729Sjoerg   }
5357330f729Sjoerg   if (RT->isBlockPointerType() || RT->isFunctionPointerType())
5367330f729Sjoerg     MigrateBlockOrFunctionPointerTypeVariable(PropertyString,
5377330f729Sjoerg                                               TypeString,
5387330f729Sjoerg                                               PropertyNameString.c_str());
5397330f729Sjoerg   else {
5407330f729Sjoerg     char LastChar = TypeString[TypeString.size()-1];
5417330f729Sjoerg     PropertyString += TypeString;
5427330f729Sjoerg     if (LastChar != '*')
5437330f729Sjoerg       PropertyString += ' ';
5447330f729Sjoerg     PropertyString += PropertyNameString;
5457330f729Sjoerg   }
5467330f729Sjoerg   SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc();
5477330f729Sjoerg   Selector GetterSelector = Getter->getSelector();
5487330f729Sjoerg 
5497330f729Sjoerg   SourceLocation EndGetterSelectorLoc =
5507330f729Sjoerg     StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size());
5517330f729Sjoerg   commit.replace(CharSourceRange::getCharRange(Getter->getBeginLoc(),
5527330f729Sjoerg                                                EndGetterSelectorLoc),
5537330f729Sjoerg                  PropertyString);
5547330f729Sjoerg   if (Setter && AvailabilityArgsMatch) {
5557330f729Sjoerg     SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
5567330f729Sjoerg     // Get location past ';'
5577330f729Sjoerg     EndLoc = EndLoc.getLocWithOffset(1);
5587330f729Sjoerg     SourceLocation BeginOfSetterDclLoc = Setter->getBeginLoc();
5597330f729Sjoerg     // FIXME. This assumes that setter decl; is immediately preceded by eoln.
5607330f729Sjoerg     // It is trying to remove the setter method decl. line entirely.
5617330f729Sjoerg     BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1);
5627330f729Sjoerg     commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc));
5637330f729Sjoerg   }
5647330f729Sjoerg }
5657330f729Sjoerg 
IsCategoryNameWithDeprecatedSuffix(ObjCContainerDecl * D)5667330f729Sjoerg static bool IsCategoryNameWithDeprecatedSuffix(ObjCContainerDecl *D) {
5677330f729Sjoerg   if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(D)) {
5687330f729Sjoerg     StringRef Name = CatDecl->getName();
5697330f729Sjoerg     return Name.endswith("Deprecated");
5707330f729Sjoerg   }
5717330f729Sjoerg   return false;
5727330f729Sjoerg }
5737330f729Sjoerg 
migrateObjCContainerDecl(ASTContext & Ctx,ObjCContainerDecl * D)5747330f729Sjoerg void ObjCMigrateASTConsumer::migrateObjCContainerDecl(ASTContext &Ctx,
5757330f729Sjoerg                                                       ObjCContainerDecl *D) {
5767330f729Sjoerg   if (D->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(D))
5777330f729Sjoerg     return;
5787330f729Sjoerg 
5797330f729Sjoerg   for (auto *Method : D->methods()) {
5807330f729Sjoerg     if (Method->isDeprecated())
5817330f729Sjoerg       continue;
5827330f729Sjoerg     bool PropertyInferred = migrateProperty(Ctx, D, Method);
5837330f729Sjoerg     // If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to
5847330f729Sjoerg     // the getter method as it ends up on the property itself which we don't want
5857330f729Sjoerg     // to do unless -objcmt-returns-innerpointer-property  option is on.
5867330f729Sjoerg     if (!PropertyInferred ||
5877330f729Sjoerg         (ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))
5887330f729Sjoerg       if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
5897330f729Sjoerg         migrateNsReturnsInnerPointer(Ctx, Method);
5907330f729Sjoerg   }
5917330f729Sjoerg   if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))
5927330f729Sjoerg     return;
5937330f729Sjoerg 
5947330f729Sjoerg   for (auto *Prop : D->instance_properties()) {
5957330f729Sjoerg     if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
5967330f729Sjoerg         !Prop->isDeprecated())
5977330f729Sjoerg       migratePropertyNsReturnsInnerPointer(Ctx, Prop);
5987330f729Sjoerg   }
5997330f729Sjoerg }
6007330f729Sjoerg 
6017330f729Sjoerg static bool
ClassImplementsAllMethodsAndProperties(ASTContext & Ctx,const ObjCImplementationDecl * ImpDecl,const ObjCInterfaceDecl * IDecl,ObjCProtocolDecl * Protocol)6027330f729Sjoerg ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
6037330f729Sjoerg                                       const ObjCImplementationDecl *ImpDecl,
6047330f729Sjoerg                                        const ObjCInterfaceDecl *IDecl,
6057330f729Sjoerg                                       ObjCProtocolDecl *Protocol) {
6067330f729Sjoerg   // In auto-synthesis, protocol properties are not synthesized. So,
6077330f729Sjoerg   // a conforming protocol must have its required properties declared
6087330f729Sjoerg   // in class interface.
6097330f729Sjoerg   bool HasAtleastOneRequiredProperty = false;
6107330f729Sjoerg   if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
6117330f729Sjoerg     for (const auto *Property : PDecl->instance_properties()) {
6127330f729Sjoerg       if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
6137330f729Sjoerg         continue;
6147330f729Sjoerg       HasAtleastOneRequiredProperty = true;
6157330f729Sjoerg       DeclContext::lookup_result R = IDecl->lookup(Property->getDeclName());
616*e038c9c4Sjoerg       if (R.empty()) {
6177330f729Sjoerg         // Relax the rule and look into class's implementation for a synthesize
6187330f729Sjoerg         // or dynamic declaration. Class is implementing a property coming from
6197330f729Sjoerg         // another protocol. This still makes the target protocol as conforming.
6207330f729Sjoerg         if (!ImpDecl->FindPropertyImplDecl(
6217330f729Sjoerg                                   Property->getDeclName().getAsIdentifierInfo(),
6227330f729Sjoerg                                   Property->getQueryKind()))
6237330f729Sjoerg           return false;
624*e038c9c4Sjoerg       } else if (auto *ClassProperty = R.find_first<ObjCPropertyDecl>()) {
625*e038c9c4Sjoerg         if ((ClassProperty->getPropertyAttributes() !=
626*e038c9c4Sjoerg              Property->getPropertyAttributes()) ||
6277330f729Sjoerg             !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
6287330f729Sjoerg           return false;
629*e038c9c4Sjoerg       } else
6307330f729Sjoerg         return false;
6317330f729Sjoerg     }
6327330f729Sjoerg 
6337330f729Sjoerg   // At this point, all required properties in this protocol conform to those
6347330f729Sjoerg   // declared in the class.
6357330f729Sjoerg   // Check that class implements the required methods of the protocol too.
6367330f729Sjoerg   bool HasAtleastOneRequiredMethod = false;
6377330f729Sjoerg   if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
6387330f729Sjoerg     if (PDecl->meth_begin() == PDecl->meth_end())
6397330f729Sjoerg       return HasAtleastOneRequiredProperty;
6407330f729Sjoerg     for (const auto *MD : PDecl->methods()) {
6417330f729Sjoerg       if (MD->isImplicit())
6427330f729Sjoerg         continue;
6437330f729Sjoerg       if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
6447330f729Sjoerg         continue;
6457330f729Sjoerg       DeclContext::lookup_result R = ImpDecl->lookup(MD->getDeclName());
646*e038c9c4Sjoerg       if (R.empty())
6477330f729Sjoerg         return false;
6487330f729Sjoerg       bool match = false;
6497330f729Sjoerg       HasAtleastOneRequiredMethod = true;
650*e038c9c4Sjoerg       for (NamedDecl *ND : R)
651*e038c9c4Sjoerg         if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(ND))
6527330f729Sjoerg           if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
6537330f729Sjoerg             match = true;
6547330f729Sjoerg             break;
6557330f729Sjoerg           }
6567330f729Sjoerg       if (!match)
6577330f729Sjoerg         return false;
6587330f729Sjoerg     }
6597330f729Sjoerg   }
6607330f729Sjoerg   return HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod;
6617330f729Sjoerg }
6627330f729Sjoerg 
rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl * IDecl,llvm::SmallVectorImpl<ObjCProtocolDecl * > & ConformingProtocols,const NSAPI & NS,edit::Commit & commit)6637330f729Sjoerg static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
6647330f729Sjoerg                     llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
6657330f729Sjoerg                     const NSAPI &NS, edit::Commit &commit) {
6667330f729Sjoerg   const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
6677330f729Sjoerg   std::string ClassString;
6687330f729Sjoerg   SourceLocation EndLoc =
6697330f729Sjoerg   IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
6707330f729Sjoerg 
6717330f729Sjoerg   if (Protocols.empty()) {
6727330f729Sjoerg     ClassString = '<';
6737330f729Sjoerg     for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
6747330f729Sjoerg       ClassString += ConformingProtocols[i]->getNameAsString();
6757330f729Sjoerg       if (i != (e-1))
6767330f729Sjoerg         ClassString += ", ";
6777330f729Sjoerg     }
6787330f729Sjoerg     ClassString += "> ";
6797330f729Sjoerg   }
6807330f729Sjoerg   else {
6817330f729Sjoerg     ClassString = ", ";
6827330f729Sjoerg     for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
6837330f729Sjoerg       ClassString += ConformingProtocols[i]->getNameAsString();
6847330f729Sjoerg       if (i != (e-1))
6857330f729Sjoerg         ClassString += ", ";
6867330f729Sjoerg     }
6877330f729Sjoerg     ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
6887330f729Sjoerg     EndLoc = *PL;
6897330f729Sjoerg   }
6907330f729Sjoerg 
6917330f729Sjoerg   commit.insertAfterToken(EndLoc, ClassString);
6927330f729Sjoerg   return true;
6937330f729Sjoerg }
6947330f729Sjoerg 
GetUnsignedName(StringRef NSIntegerName)6957330f729Sjoerg static StringRef GetUnsignedName(StringRef NSIntegerName) {
6967330f729Sjoerg   StringRef UnsignedName = llvm::StringSwitch<StringRef>(NSIntegerName)
6977330f729Sjoerg     .Case("int8_t", "uint8_t")
6987330f729Sjoerg     .Case("int16_t", "uint16_t")
6997330f729Sjoerg     .Case("int32_t", "uint32_t")
7007330f729Sjoerg     .Case("NSInteger", "NSUInteger")
7017330f729Sjoerg     .Case("int64_t", "uint64_t")
7027330f729Sjoerg     .Default(NSIntegerName);
7037330f729Sjoerg   return UnsignedName;
7047330f729Sjoerg }
7057330f729Sjoerg 
rewriteToNSEnumDecl(const EnumDecl * EnumDcl,const TypedefDecl * TypedefDcl,const NSAPI & NS,edit::Commit & commit,StringRef NSIntegerName,bool NSOptions)7067330f729Sjoerg static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
7077330f729Sjoerg                                 const TypedefDecl *TypedefDcl,
7087330f729Sjoerg                                 const NSAPI &NS, edit::Commit &commit,
7097330f729Sjoerg                                 StringRef NSIntegerName,
7107330f729Sjoerg                                 bool NSOptions) {
7117330f729Sjoerg   std::string ClassString;
7127330f729Sjoerg   if (NSOptions) {
7137330f729Sjoerg     ClassString = "typedef NS_OPTIONS(";
7147330f729Sjoerg     ClassString += GetUnsignedName(NSIntegerName);
7157330f729Sjoerg   }
7167330f729Sjoerg   else {
7177330f729Sjoerg     ClassString = "typedef NS_ENUM(";
7187330f729Sjoerg     ClassString += NSIntegerName;
7197330f729Sjoerg   }
7207330f729Sjoerg   ClassString += ", ";
7217330f729Sjoerg 
7227330f729Sjoerg   ClassString += TypedefDcl->getIdentifier()->getName();
7237330f729Sjoerg   ClassString += ')';
7247330f729Sjoerg   SourceRange R(EnumDcl->getBeginLoc(), EnumDcl->getBeginLoc());
7257330f729Sjoerg   commit.replace(R, ClassString);
7267330f729Sjoerg   SourceLocation EndOfEnumDclLoc = EnumDcl->getEndLoc();
7277330f729Sjoerg   EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc,
7287330f729Sjoerg                                                  NS.getASTContext(), /*IsDecl*/true);
7297330f729Sjoerg   if (EndOfEnumDclLoc.isValid()) {
7307330f729Sjoerg     SourceRange EnumDclRange(EnumDcl->getBeginLoc(), EndOfEnumDclLoc);
7317330f729Sjoerg     commit.insertFromRange(TypedefDcl->getBeginLoc(), EnumDclRange);
7327330f729Sjoerg   }
7337330f729Sjoerg   else
7347330f729Sjoerg     return false;
7357330f729Sjoerg 
7367330f729Sjoerg   SourceLocation EndTypedefDclLoc = TypedefDcl->getEndLoc();
7377330f729Sjoerg   EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc,
7387330f729Sjoerg                                                  NS.getASTContext(), /*IsDecl*/true);
7397330f729Sjoerg   if (EndTypedefDclLoc.isValid()) {
7407330f729Sjoerg     SourceRange TDRange(TypedefDcl->getBeginLoc(), EndTypedefDclLoc);
7417330f729Sjoerg     commit.remove(TDRange);
7427330f729Sjoerg   }
7437330f729Sjoerg   else
7447330f729Sjoerg     return false;
7457330f729Sjoerg 
7467330f729Sjoerg   EndOfEnumDclLoc =
7477330f729Sjoerg       trans::findLocationAfterSemi(EnumDcl->getEndLoc(), NS.getASTContext(),
7487330f729Sjoerg                                    /*IsDecl*/ true);
7497330f729Sjoerg   if (EndOfEnumDclLoc.isValid()) {
7507330f729Sjoerg     SourceLocation BeginOfEnumDclLoc = EnumDcl->getBeginLoc();
7517330f729Sjoerg     // FIXME. This assumes that enum decl; is immediately preceded by eoln.
7527330f729Sjoerg     // It is trying to remove the enum decl. lines entirely.
7537330f729Sjoerg     BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1);
7547330f729Sjoerg     commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc));
7557330f729Sjoerg     return true;
7567330f729Sjoerg   }
7577330f729Sjoerg   return false;
7587330f729Sjoerg }
7597330f729Sjoerg 
rewriteToNSMacroDecl(ASTContext & Ctx,const EnumDecl * EnumDcl,const TypedefDecl * TypedefDcl,const NSAPI & NS,edit::Commit & commit,bool IsNSIntegerType)7607330f729Sjoerg static void rewriteToNSMacroDecl(ASTContext &Ctx,
7617330f729Sjoerg                                  const EnumDecl *EnumDcl,
7627330f729Sjoerg                                 const TypedefDecl *TypedefDcl,
7637330f729Sjoerg                                 const NSAPI &NS, edit::Commit &commit,
7647330f729Sjoerg                                  bool IsNSIntegerType) {
7657330f729Sjoerg   QualType DesignatedEnumType = EnumDcl->getIntegerType();
7667330f729Sjoerg   assert(!DesignatedEnumType.isNull()
7677330f729Sjoerg          && "rewriteToNSMacroDecl - underlying enum type is null");
7687330f729Sjoerg 
7697330f729Sjoerg   PrintingPolicy Policy(Ctx.getPrintingPolicy());
7707330f729Sjoerg   std::string TypeString = DesignatedEnumType.getAsString(Policy);
7717330f729Sjoerg   std::string ClassString = IsNSIntegerType ? "NS_ENUM(" : "NS_OPTIONS(";
7727330f729Sjoerg   ClassString += TypeString;
7737330f729Sjoerg   ClassString += ", ";
7747330f729Sjoerg 
7757330f729Sjoerg   ClassString += TypedefDcl->getIdentifier()->getName();
7767330f729Sjoerg   ClassString += ") ";
7777330f729Sjoerg   SourceLocation EndLoc = EnumDcl->getBraceRange().getBegin();
7787330f729Sjoerg   if (EndLoc.isInvalid())
7797330f729Sjoerg     return;
7807330f729Sjoerg   CharSourceRange R =
7817330f729Sjoerg       CharSourceRange::getCharRange(EnumDcl->getBeginLoc(), EndLoc);
7827330f729Sjoerg   commit.replace(R, ClassString);
7837330f729Sjoerg   // This is to remove spaces between '}' and typedef name.
7847330f729Sjoerg   SourceLocation StartTypedefLoc = EnumDcl->getEndLoc();
7857330f729Sjoerg   StartTypedefLoc = StartTypedefLoc.getLocWithOffset(+1);
7867330f729Sjoerg   SourceLocation EndTypedefLoc = TypedefDcl->getEndLoc();
7877330f729Sjoerg 
7887330f729Sjoerg   commit.remove(SourceRange(StartTypedefLoc, EndTypedefLoc));
7897330f729Sjoerg }
7907330f729Sjoerg 
UseNSOptionsMacro(Preprocessor & PP,ASTContext & Ctx,const EnumDecl * EnumDcl)7917330f729Sjoerg static bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx,
7927330f729Sjoerg                               const EnumDecl *EnumDcl) {
7937330f729Sjoerg   bool PowerOfTwo = true;
7947330f729Sjoerg   bool AllHexdecimalEnumerator = true;
7957330f729Sjoerg   uint64_t MaxPowerOfTwoVal = 0;
7967330f729Sjoerg   for (auto Enumerator : EnumDcl->enumerators()) {
7977330f729Sjoerg     const Expr *InitExpr = Enumerator->getInitExpr();
7987330f729Sjoerg     if (!InitExpr) {
7997330f729Sjoerg       PowerOfTwo = false;
8007330f729Sjoerg       AllHexdecimalEnumerator = false;
8017330f729Sjoerg       continue;
8027330f729Sjoerg     }
8037330f729Sjoerg     InitExpr = InitExpr->IgnoreParenCasts();
8047330f729Sjoerg     if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
8057330f729Sjoerg       if (BO->isShiftOp() || BO->isBitwiseOp())
8067330f729Sjoerg         return true;
8077330f729Sjoerg 
8087330f729Sjoerg     uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
8097330f729Sjoerg     if (PowerOfTwo && EnumVal) {
8107330f729Sjoerg       if (!llvm::isPowerOf2_64(EnumVal))
8117330f729Sjoerg         PowerOfTwo = false;
8127330f729Sjoerg       else if (EnumVal > MaxPowerOfTwoVal)
8137330f729Sjoerg         MaxPowerOfTwoVal = EnumVal;
8147330f729Sjoerg     }
8157330f729Sjoerg     if (AllHexdecimalEnumerator && EnumVal) {
8167330f729Sjoerg       bool FoundHexdecimalEnumerator = false;
8177330f729Sjoerg       SourceLocation EndLoc = Enumerator->getEndLoc();
8187330f729Sjoerg       Token Tok;
8197330f729Sjoerg       if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true))
8207330f729Sjoerg         if (Tok.isLiteral() && Tok.getLength() > 2) {
8217330f729Sjoerg           if (const char *StringLit = Tok.getLiteralData())
8227330f729Sjoerg             FoundHexdecimalEnumerator =
8237330f729Sjoerg               (StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x'));
8247330f729Sjoerg         }
8257330f729Sjoerg       if (!FoundHexdecimalEnumerator)
8267330f729Sjoerg         AllHexdecimalEnumerator = false;
8277330f729Sjoerg     }
8287330f729Sjoerg   }
8297330f729Sjoerg   return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2));
8307330f729Sjoerg }
8317330f729Sjoerg 
migrateProtocolConformance(ASTContext & Ctx,const ObjCImplementationDecl * ImpDecl)8327330f729Sjoerg void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
8337330f729Sjoerg                                             const ObjCImplementationDecl *ImpDecl) {
8347330f729Sjoerg   const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
8357330f729Sjoerg   if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated())
8367330f729Sjoerg     return;
8377330f729Sjoerg   // Find all implicit conforming protocols for this class
8387330f729Sjoerg   // and make them explicit.
8397330f729Sjoerg   llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
8407330f729Sjoerg   Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
8417330f729Sjoerg   llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
8427330f729Sjoerg 
8437330f729Sjoerg   for (ObjCProtocolDecl *ProtDecl : ObjCProtocolDecls)
8447330f729Sjoerg     if (!ExplicitProtocols.count(ProtDecl))
8457330f729Sjoerg       PotentialImplicitProtocols.push_back(ProtDecl);
8467330f729Sjoerg 
8477330f729Sjoerg   if (PotentialImplicitProtocols.empty())
8487330f729Sjoerg     return;
8497330f729Sjoerg 
8507330f729Sjoerg   // go through list of non-optional methods and properties in each protocol
8517330f729Sjoerg   // in the PotentialImplicitProtocols list. If class implements every one of the
8527330f729Sjoerg   // methods and properties, then this class conforms to this protocol.
8537330f729Sjoerg   llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
8547330f729Sjoerg   for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
8557330f729Sjoerg     if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
8567330f729Sjoerg                                               PotentialImplicitProtocols[i]))
8577330f729Sjoerg       ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
8587330f729Sjoerg 
8597330f729Sjoerg   if (ConformingProtocols.empty())
8607330f729Sjoerg     return;
8617330f729Sjoerg 
8627330f729Sjoerg   // Further reduce number of conforming protocols. If protocol P1 is in the list
8637330f729Sjoerg   // protocol P2 (P2<P1>), No need to include P1.
8647330f729Sjoerg   llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
8657330f729Sjoerg   for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
8667330f729Sjoerg     bool DropIt = false;
8677330f729Sjoerg     ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
8687330f729Sjoerg     for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
8697330f729Sjoerg       ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
8707330f729Sjoerg       if (PDecl == TargetPDecl)
8717330f729Sjoerg         continue;
8727330f729Sjoerg       if (PDecl->lookupProtocolNamed(
8737330f729Sjoerg             TargetPDecl->getDeclName().getAsIdentifierInfo())) {
8747330f729Sjoerg         DropIt = true;
8757330f729Sjoerg         break;
8767330f729Sjoerg       }
8777330f729Sjoerg     }
8787330f729Sjoerg     if (!DropIt)
8797330f729Sjoerg       MinimalConformingProtocols.push_back(TargetPDecl);
8807330f729Sjoerg   }
8817330f729Sjoerg   if (MinimalConformingProtocols.empty())
8827330f729Sjoerg     return;
8837330f729Sjoerg   edit::Commit commit(*Editor);
8847330f729Sjoerg   rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
8857330f729Sjoerg                              *NSAPIObj, commit);
8867330f729Sjoerg   Editor->commit(commit);
8877330f729Sjoerg }
8887330f729Sjoerg 
CacheObjCNSIntegerTypedefed(const TypedefDecl * TypedefDcl)8897330f729Sjoerg void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed(
8907330f729Sjoerg                                           const TypedefDecl *TypedefDcl) {
8917330f729Sjoerg 
8927330f729Sjoerg   QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
8937330f729Sjoerg   if (NSAPIObj->isObjCNSIntegerType(qt))
8947330f729Sjoerg     NSIntegerTypedefed = TypedefDcl;
8957330f729Sjoerg   else if (NSAPIObj->isObjCNSUIntegerType(qt))
8967330f729Sjoerg     NSUIntegerTypedefed = TypedefDcl;
8977330f729Sjoerg }
8987330f729Sjoerg 
migrateNSEnumDecl(ASTContext & Ctx,const EnumDecl * EnumDcl,const TypedefDecl * TypedefDcl)8997330f729Sjoerg bool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
9007330f729Sjoerg                                            const EnumDecl *EnumDcl,
9017330f729Sjoerg                                            const TypedefDecl *TypedefDcl) {
9027330f729Sjoerg   if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
9037330f729Sjoerg       EnumDcl->isDeprecated())
9047330f729Sjoerg     return false;
9057330f729Sjoerg   if (!TypedefDcl) {
9067330f729Sjoerg     if (NSIntegerTypedefed) {
9077330f729Sjoerg       TypedefDcl = NSIntegerTypedefed;
9087330f729Sjoerg       NSIntegerTypedefed = nullptr;
9097330f729Sjoerg     }
9107330f729Sjoerg     else if (NSUIntegerTypedefed) {
9117330f729Sjoerg       TypedefDcl = NSUIntegerTypedefed;
9127330f729Sjoerg       NSUIntegerTypedefed = nullptr;
9137330f729Sjoerg     }
9147330f729Sjoerg     else
9157330f729Sjoerg       return false;
9167330f729Sjoerg     FileID FileIdOfTypedefDcl =
9177330f729Sjoerg       PP.getSourceManager().getFileID(TypedefDcl->getLocation());
9187330f729Sjoerg     FileID FileIdOfEnumDcl =
9197330f729Sjoerg       PP.getSourceManager().getFileID(EnumDcl->getLocation());
9207330f729Sjoerg     if (FileIdOfTypedefDcl != FileIdOfEnumDcl)
9217330f729Sjoerg       return false;
9227330f729Sjoerg   }
9237330f729Sjoerg   if (TypedefDcl->isDeprecated())
9247330f729Sjoerg     return false;
9257330f729Sjoerg 
9267330f729Sjoerg   QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
9277330f729Sjoerg   StringRef NSIntegerName = NSAPIObj->GetNSIntegralKind(qt);
9287330f729Sjoerg 
9297330f729Sjoerg   if (NSIntegerName.empty()) {
9307330f729Sjoerg     // Also check for typedef enum {...} TD;
9317330f729Sjoerg     if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
9327330f729Sjoerg       if (EnumTy->getDecl() == EnumDcl) {
9337330f729Sjoerg         bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);
9347330f729Sjoerg         if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc()))
9357330f729Sjoerg           return false;
9367330f729Sjoerg         edit::Commit commit(*Editor);
9377330f729Sjoerg         rewriteToNSMacroDecl(Ctx, EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
9387330f729Sjoerg         Editor->commit(commit);
9397330f729Sjoerg         return true;
9407330f729Sjoerg       }
9417330f729Sjoerg     }
9427330f729Sjoerg     return false;
9437330f729Sjoerg   }
9447330f729Sjoerg 
9457330f729Sjoerg   // We may still use NS_OPTIONS based on what we find in the enumertor list.
9467330f729Sjoerg   bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);
9477330f729Sjoerg   if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc()))
9487330f729Sjoerg     return false;
9497330f729Sjoerg   edit::Commit commit(*Editor);
9507330f729Sjoerg   bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj,
9517330f729Sjoerg                                  commit, NSIntegerName, NSOptions);
9527330f729Sjoerg   Editor->commit(commit);
9537330f729Sjoerg   return Res;
9547330f729Sjoerg }
9557330f729Sjoerg 
ReplaceWithInstancetype(ASTContext & Ctx,const ObjCMigrateASTConsumer & ASTC,ObjCMethodDecl * OM)9567330f729Sjoerg static void ReplaceWithInstancetype(ASTContext &Ctx,
9577330f729Sjoerg                                     const ObjCMigrateASTConsumer &ASTC,
9587330f729Sjoerg                                     ObjCMethodDecl *OM) {
9597330f729Sjoerg   if (OM->getReturnType() == Ctx.getObjCInstanceType())
9607330f729Sjoerg     return; // already has instancetype.
9617330f729Sjoerg 
9627330f729Sjoerg   SourceRange R;
9637330f729Sjoerg   std::string ClassString;
9647330f729Sjoerg   if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) {
9657330f729Sjoerg     TypeLoc TL = TSInfo->getTypeLoc();
9667330f729Sjoerg     R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
9677330f729Sjoerg     ClassString = "instancetype";
9687330f729Sjoerg   }
9697330f729Sjoerg   else {
9707330f729Sjoerg     R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc());
9717330f729Sjoerg     ClassString = OM->isInstanceMethod() ? '-' : '+';
9727330f729Sjoerg     ClassString += " (instancetype)";
9737330f729Sjoerg   }
9747330f729Sjoerg   edit::Commit commit(*ASTC.Editor);
9757330f729Sjoerg   commit.replace(R, ClassString);
9767330f729Sjoerg   ASTC.Editor->commit(commit);
9777330f729Sjoerg }
9787330f729Sjoerg 
ReplaceWithClasstype(const ObjCMigrateASTConsumer & ASTC,ObjCMethodDecl * OM)9797330f729Sjoerg static void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC,
9807330f729Sjoerg                                     ObjCMethodDecl *OM) {
9817330f729Sjoerg   ObjCInterfaceDecl *IDecl = OM->getClassInterface();
9827330f729Sjoerg   SourceRange R;
9837330f729Sjoerg   std::string ClassString;
9847330f729Sjoerg   if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) {
9857330f729Sjoerg     TypeLoc TL = TSInfo->getTypeLoc();
9867330f729Sjoerg     R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); {
987*e038c9c4Sjoerg       ClassString = std::string(IDecl->getName());
9887330f729Sjoerg       ClassString += "*";
9897330f729Sjoerg     }
9907330f729Sjoerg   }
9917330f729Sjoerg   else {
9927330f729Sjoerg     R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc());
9937330f729Sjoerg     ClassString = "+ (";
9947330f729Sjoerg     ClassString += IDecl->getName(); ClassString += "*)";
9957330f729Sjoerg   }
9967330f729Sjoerg   edit::Commit commit(*ASTC.Editor);
9977330f729Sjoerg   commit.replace(R, ClassString);
9987330f729Sjoerg   ASTC.Editor->commit(commit);
9997330f729Sjoerg }
10007330f729Sjoerg 
migrateMethodInstanceType(ASTContext & Ctx,ObjCContainerDecl * CDecl,ObjCMethodDecl * OM)10017330f729Sjoerg void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
10027330f729Sjoerg                                                        ObjCContainerDecl *CDecl,
10037330f729Sjoerg                                                        ObjCMethodDecl *OM) {
10047330f729Sjoerg   ObjCInstanceTypeFamily OIT_Family =
10057330f729Sjoerg     Selector::getInstTypeMethodFamily(OM->getSelector());
10067330f729Sjoerg 
10077330f729Sjoerg   std::string ClassName;
10087330f729Sjoerg   switch (OIT_Family) {
10097330f729Sjoerg     case OIT_None:
10107330f729Sjoerg       migrateFactoryMethod(Ctx, CDecl, OM);
10117330f729Sjoerg       return;
10127330f729Sjoerg     case OIT_Array:
10137330f729Sjoerg       ClassName = "NSArray";
10147330f729Sjoerg       break;
10157330f729Sjoerg     case OIT_Dictionary:
10167330f729Sjoerg       ClassName = "NSDictionary";
10177330f729Sjoerg       break;
10187330f729Sjoerg     case OIT_Singleton:
10197330f729Sjoerg       migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton);
10207330f729Sjoerg       return;
10217330f729Sjoerg     case OIT_Init:
10227330f729Sjoerg       if (OM->getReturnType()->isObjCIdType())
10237330f729Sjoerg         ReplaceWithInstancetype(Ctx, *this, OM);
10247330f729Sjoerg       return;
10257330f729Sjoerg     case OIT_ReturnsSelf:
10267330f729Sjoerg       migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf);
10277330f729Sjoerg       return;
10287330f729Sjoerg   }
10297330f729Sjoerg   if (!OM->getReturnType()->isObjCIdType())
10307330f729Sjoerg     return;
10317330f729Sjoerg 
10327330f729Sjoerg   ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
10337330f729Sjoerg   if (!IDecl) {
10347330f729Sjoerg     if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
10357330f729Sjoerg       IDecl = CatDecl->getClassInterface();
10367330f729Sjoerg     else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
10377330f729Sjoerg       IDecl = ImpDecl->getClassInterface();
10387330f729Sjoerg   }
10397330f729Sjoerg   if (!IDecl ||
10407330f729Sjoerg       !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {
10417330f729Sjoerg     migrateFactoryMethod(Ctx, CDecl, OM);
10427330f729Sjoerg     return;
10437330f729Sjoerg   }
10447330f729Sjoerg   ReplaceWithInstancetype(Ctx, *this, OM);
10457330f729Sjoerg }
10467330f729Sjoerg 
TypeIsInnerPointer(QualType T)10477330f729Sjoerg static bool TypeIsInnerPointer(QualType T) {
10487330f729Sjoerg   if (!T->isAnyPointerType())
10497330f729Sjoerg     return false;
10507330f729Sjoerg   if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() ||
10517330f729Sjoerg       T->isBlockPointerType() || T->isFunctionPointerType() ||
10527330f729Sjoerg       ento::coreFoundation::isCFObjectRef(T))
10537330f729Sjoerg     return false;
10547330f729Sjoerg   // Also, typedef-of-pointer-to-incomplete-struct is something that we assume
10557330f729Sjoerg   // is not an innter pointer type.
10567330f729Sjoerg   QualType OrigT = T;
10577330f729Sjoerg   while (const TypedefType *TD = dyn_cast<TypedefType>(T.getTypePtr()))
10587330f729Sjoerg     T = TD->getDecl()->getUnderlyingType();
10597330f729Sjoerg   if (OrigT == T || !T->isPointerType())
10607330f729Sjoerg     return true;
10617330f729Sjoerg   const PointerType* PT = T->getAs<PointerType>();
10627330f729Sjoerg   QualType UPointeeT = PT->getPointeeType().getUnqualifiedType();
10637330f729Sjoerg   if (UPointeeT->isRecordType()) {
10647330f729Sjoerg     const RecordType *RecordTy = UPointeeT->getAs<RecordType>();
10657330f729Sjoerg     if (!RecordTy->getDecl()->isCompleteDefinition())
10667330f729Sjoerg       return false;
10677330f729Sjoerg   }
10687330f729Sjoerg   return true;
10697330f729Sjoerg }
10707330f729Sjoerg 
10717330f729Sjoerg /// Check whether the two versions match.
versionsMatch(const VersionTuple & X,const VersionTuple & Y)10727330f729Sjoerg static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) {
10737330f729Sjoerg   return (X == Y);
10747330f729Sjoerg }
10757330f729Sjoerg 
10767330f729Sjoerg /// AvailabilityAttrsMatch - This routine checks that if comparing two
10777330f729Sjoerg /// availability attributes, all their components match. It returns
10787330f729Sjoerg /// true, if not dealing with availability or when all components of
10797330f729Sjoerg /// availability attributes match. This routine is only called when
10807330f729Sjoerg /// the attributes are of the same kind.
AvailabilityAttrsMatch(Attr * At1,Attr * At2)10817330f729Sjoerg static bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) {
10827330f729Sjoerg   const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1);
10837330f729Sjoerg   if (!AA1)
10847330f729Sjoerg     return true;
1085*e038c9c4Sjoerg   const AvailabilityAttr *AA2 = cast<AvailabilityAttr>(At2);
10867330f729Sjoerg 
10877330f729Sjoerg   VersionTuple Introduced1 = AA1->getIntroduced();
10887330f729Sjoerg   VersionTuple Deprecated1 = AA1->getDeprecated();
10897330f729Sjoerg   VersionTuple Obsoleted1 = AA1->getObsoleted();
10907330f729Sjoerg   bool IsUnavailable1 = AA1->getUnavailable();
10917330f729Sjoerg   VersionTuple Introduced2 = AA2->getIntroduced();
10927330f729Sjoerg   VersionTuple Deprecated2 = AA2->getDeprecated();
10937330f729Sjoerg   VersionTuple Obsoleted2 = AA2->getObsoleted();
10947330f729Sjoerg   bool IsUnavailable2 = AA2->getUnavailable();
10957330f729Sjoerg   return (versionsMatch(Introduced1, Introduced2) &&
10967330f729Sjoerg           versionsMatch(Deprecated1, Deprecated2) &&
10977330f729Sjoerg           versionsMatch(Obsoleted1, Obsoleted2) &&
10987330f729Sjoerg           IsUnavailable1 == IsUnavailable2);
10997330f729Sjoerg }
11007330f729Sjoerg 
MatchTwoAttributeLists(const AttrVec & Attrs1,const AttrVec & Attrs2,bool & AvailabilityArgsMatch)11017330f729Sjoerg static bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2,
11027330f729Sjoerg                                    bool &AvailabilityArgsMatch) {
11037330f729Sjoerg   // This list is very small, so this need not be optimized.
11047330f729Sjoerg   for (unsigned i = 0, e = Attrs1.size(); i != e; i++) {
11057330f729Sjoerg     bool match = false;
11067330f729Sjoerg     for (unsigned j = 0, f = Attrs2.size(); j != f; j++) {
11077330f729Sjoerg       // Matching attribute kind only. Except for Availability attributes,
11087330f729Sjoerg       // we are not getting into details of the attributes. For all practical purposes
11097330f729Sjoerg       // this is sufficient.
11107330f729Sjoerg       if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) {
11117330f729Sjoerg         if (AvailabilityArgsMatch)
11127330f729Sjoerg           AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]);
11137330f729Sjoerg         match = true;
11147330f729Sjoerg         break;
11157330f729Sjoerg       }
11167330f729Sjoerg     }
11177330f729Sjoerg     if (!match)
11187330f729Sjoerg       return false;
11197330f729Sjoerg   }
11207330f729Sjoerg   return true;
11217330f729Sjoerg }
11227330f729Sjoerg 
11237330f729Sjoerg /// AttributesMatch - This routine checks list of attributes for two
11247330f729Sjoerg /// decls. It returns false, if there is a mismatch in kind of
11257330f729Sjoerg /// attributes seen in the decls. It returns true if the two decls
11267330f729Sjoerg /// have list of same kind of attributes. Furthermore, when there
11277330f729Sjoerg /// are availability attributes in the two decls, it sets the
11287330f729Sjoerg /// AvailabilityArgsMatch to false if availability attributes have
11297330f729Sjoerg /// different versions, etc.
AttributesMatch(const Decl * Decl1,const Decl * Decl2,bool & AvailabilityArgsMatch)11307330f729Sjoerg static bool AttributesMatch(const Decl *Decl1, const Decl *Decl2,
11317330f729Sjoerg                             bool &AvailabilityArgsMatch) {
11327330f729Sjoerg   if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) {
11337330f729Sjoerg     AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs());
11347330f729Sjoerg     return true;
11357330f729Sjoerg   }
11367330f729Sjoerg   AvailabilityArgsMatch = true;
11377330f729Sjoerg   const AttrVec &Attrs1 = Decl1->getAttrs();
11387330f729Sjoerg   const AttrVec &Attrs2 = Decl2->getAttrs();
11397330f729Sjoerg   bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch);
11407330f729Sjoerg   if (match && (Attrs2.size() > Attrs1.size()))
11417330f729Sjoerg     return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch);
11427330f729Sjoerg   return match;
11437330f729Sjoerg }
11447330f729Sjoerg 
IsValidIdentifier(ASTContext & Ctx,const char * Name)11457330f729Sjoerg static bool IsValidIdentifier(ASTContext &Ctx,
11467330f729Sjoerg                               const char *Name) {
11477330f729Sjoerg   if (!isIdentifierHead(Name[0]))
11487330f729Sjoerg     return false;
11497330f729Sjoerg   std::string NameString = Name;
11507330f729Sjoerg   NameString[0] = toLowercase(NameString[0]);
11517330f729Sjoerg   IdentifierInfo *II = &Ctx.Idents.get(NameString);
11527330f729Sjoerg   return II->getTokenID() ==  tok::identifier;
11537330f729Sjoerg }
11547330f729Sjoerg 
migrateProperty(ASTContext & Ctx,ObjCContainerDecl * D,ObjCMethodDecl * Method)11557330f729Sjoerg bool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx,
11567330f729Sjoerg                              ObjCContainerDecl *D,
11577330f729Sjoerg                              ObjCMethodDecl *Method) {
11587330f729Sjoerg   if (Method->isPropertyAccessor() || !Method->isInstanceMethod() ||
11597330f729Sjoerg       Method->param_size() != 0)
11607330f729Sjoerg     return false;
11617330f729Sjoerg   // Is this method candidate to be a getter?
11627330f729Sjoerg   QualType GRT = Method->getReturnType();
11637330f729Sjoerg   if (GRT->isVoidType())
11647330f729Sjoerg     return false;
11657330f729Sjoerg 
11667330f729Sjoerg   Selector GetterSelector = Method->getSelector();
11677330f729Sjoerg   ObjCInstanceTypeFamily OIT_Family =
11687330f729Sjoerg     Selector::getInstTypeMethodFamily(GetterSelector);
11697330f729Sjoerg 
11707330f729Sjoerg   if (OIT_Family != OIT_None)
11717330f729Sjoerg     return false;
11727330f729Sjoerg 
11737330f729Sjoerg   IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
11747330f729Sjoerg   Selector SetterSelector =
11757330f729Sjoerg   SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
11767330f729Sjoerg                                          PP.getSelectorTable(),
11777330f729Sjoerg                                          getterName);
11787330f729Sjoerg   ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector);
11797330f729Sjoerg   unsigned LengthOfPrefix = 0;
11807330f729Sjoerg   if (!SetterMethod) {
11817330f729Sjoerg     // try a different naming convention for getter: isXxxxx
11827330f729Sjoerg     StringRef getterNameString = getterName->getName();
11837330f729Sjoerg     bool IsPrefix = getterNameString.startswith("is");
11847330f729Sjoerg     // Note that we don't want to change an isXXX method of retainable object
11857330f729Sjoerg     // type to property (readonly or otherwise).
11867330f729Sjoerg     if (IsPrefix && GRT->isObjCRetainableType())
11877330f729Sjoerg       return false;
11887330f729Sjoerg     if (IsPrefix || getterNameString.startswith("get")) {
11897330f729Sjoerg       LengthOfPrefix = (IsPrefix ? 2 : 3);
11907330f729Sjoerg       const char *CGetterName = getterNameString.data() + LengthOfPrefix;
11917330f729Sjoerg       // Make sure that first character after "is" or "get" prefix can
11927330f729Sjoerg       // start an identifier.
11937330f729Sjoerg       if (!IsValidIdentifier(Ctx, CGetterName))
11947330f729Sjoerg         return false;
11957330f729Sjoerg       if (CGetterName[0] && isUppercase(CGetterName[0])) {
11967330f729Sjoerg         getterName = &Ctx.Idents.get(CGetterName);
11977330f729Sjoerg         SetterSelector =
11987330f729Sjoerg         SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
11997330f729Sjoerg                                                PP.getSelectorTable(),
12007330f729Sjoerg                                                getterName);
12017330f729Sjoerg         SetterMethod = D->getInstanceMethod(SetterSelector);
12027330f729Sjoerg       }
12037330f729Sjoerg     }
12047330f729Sjoerg   }
12057330f729Sjoerg 
12067330f729Sjoerg   if (SetterMethod) {
12077330f729Sjoerg     if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0)
12087330f729Sjoerg       return false;
12097330f729Sjoerg     bool AvailabilityArgsMatch;
12107330f729Sjoerg     if (SetterMethod->isDeprecated() ||
12117330f729Sjoerg         !AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch))
12127330f729Sjoerg       return false;
12137330f729Sjoerg 
12147330f729Sjoerg     // Is this a valid setter, matching the target getter?
12157330f729Sjoerg     QualType SRT = SetterMethod->getReturnType();
12167330f729Sjoerg     if (!SRT->isVoidType())
12177330f729Sjoerg       return false;
12187330f729Sjoerg     const ParmVarDecl *argDecl = *SetterMethod->param_begin();
12197330f729Sjoerg     QualType ArgType = argDecl->getType();
12207330f729Sjoerg     if (!Ctx.hasSameUnqualifiedType(ArgType, GRT))
12217330f729Sjoerg       return false;
12227330f729Sjoerg     edit::Commit commit(*Editor);
12237330f729Sjoerg     rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit,
12247330f729Sjoerg                           LengthOfPrefix,
12257330f729Sjoerg                           (ASTMigrateActions &
12267330f729Sjoerg                            FrontendOptions::ObjCMT_AtomicProperty) != 0,
12277330f729Sjoerg                           (ASTMigrateActions &
12287330f729Sjoerg                            FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0,
12297330f729Sjoerg                           AvailabilityArgsMatch);
12307330f729Sjoerg     Editor->commit(commit);
12317330f729Sjoerg     return true;
12327330f729Sjoerg   }
12337330f729Sjoerg   else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) {
12347330f729Sjoerg     // Try a non-void method with no argument (and no setter or property of same name
12357330f729Sjoerg     // as a 'readonly' property.
12367330f729Sjoerg     edit::Commit commit(*Editor);
12377330f729Sjoerg     rewriteToObjCProperty(Method, nullptr /*SetterMethod*/, *NSAPIObj, commit,
12387330f729Sjoerg                           LengthOfPrefix,
12397330f729Sjoerg                           (ASTMigrateActions &
12407330f729Sjoerg                            FrontendOptions::ObjCMT_AtomicProperty) != 0,
12417330f729Sjoerg                           (ASTMigrateActions &
12427330f729Sjoerg                            FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0,
12437330f729Sjoerg                           /*AvailabilityArgsMatch*/false);
12447330f729Sjoerg     Editor->commit(commit);
12457330f729Sjoerg     return true;
12467330f729Sjoerg   }
12477330f729Sjoerg   return false;
12487330f729Sjoerg }
12497330f729Sjoerg 
migrateNsReturnsInnerPointer(ASTContext & Ctx,ObjCMethodDecl * OM)12507330f729Sjoerg void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx,
12517330f729Sjoerg                                                           ObjCMethodDecl *OM) {
12527330f729Sjoerg   if (OM->isImplicit() ||
12537330f729Sjoerg       !OM->isInstanceMethod() ||
12547330f729Sjoerg       OM->hasAttr<ObjCReturnsInnerPointerAttr>())
12557330f729Sjoerg     return;
12567330f729Sjoerg 
12577330f729Sjoerg   QualType RT = OM->getReturnType();
12587330f729Sjoerg   if (!TypeIsInnerPointer(RT) ||
12597330f729Sjoerg       !NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER"))
12607330f729Sjoerg     return;
12617330f729Sjoerg 
12627330f729Sjoerg   edit::Commit commit(*Editor);
12637330f729Sjoerg   commit.insertBefore(OM->getEndLoc(), " NS_RETURNS_INNER_POINTER");
12647330f729Sjoerg   Editor->commit(commit);
12657330f729Sjoerg }
12667330f729Sjoerg 
migratePropertyNsReturnsInnerPointer(ASTContext & Ctx,ObjCPropertyDecl * P)12677330f729Sjoerg void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx,
12687330f729Sjoerg                                                                   ObjCPropertyDecl *P) {
12697330f729Sjoerg   QualType T = P->getType();
12707330f729Sjoerg 
12717330f729Sjoerg   if (!TypeIsInnerPointer(T) ||
12727330f729Sjoerg       !NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER"))
12737330f729Sjoerg     return;
12747330f729Sjoerg   edit::Commit commit(*Editor);
12757330f729Sjoerg   commit.insertBefore(P->getEndLoc(), " NS_RETURNS_INNER_POINTER ");
12767330f729Sjoerg   Editor->commit(commit);
12777330f729Sjoerg }
12787330f729Sjoerg 
migrateAllMethodInstaceType(ASTContext & Ctx,ObjCContainerDecl * CDecl)12797330f729Sjoerg void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx,
12807330f729Sjoerg                                                  ObjCContainerDecl *CDecl) {
12817330f729Sjoerg   if (CDecl->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(CDecl))
12827330f729Sjoerg     return;
12837330f729Sjoerg 
12847330f729Sjoerg   // migrate methods which can have instancetype as their result type.
12857330f729Sjoerg   for (auto *Method : CDecl->methods()) {
12867330f729Sjoerg     if (Method->isDeprecated())
12877330f729Sjoerg       continue;
12887330f729Sjoerg     migrateMethodInstanceType(Ctx, CDecl, Method);
12897330f729Sjoerg   }
12907330f729Sjoerg }
12917330f729Sjoerg 
migrateFactoryMethod(ASTContext & Ctx,ObjCContainerDecl * CDecl,ObjCMethodDecl * OM,ObjCInstanceTypeFamily OIT_Family)12927330f729Sjoerg void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,
12937330f729Sjoerg                                                   ObjCContainerDecl *CDecl,
12947330f729Sjoerg                                                   ObjCMethodDecl *OM,
12957330f729Sjoerg                                                   ObjCInstanceTypeFamily OIT_Family) {
12967330f729Sjoerg   if (OM->isInstanceMethod() ||
12977330f729Sjoerg       OM->getReturnType() == Ctx.getObjCInstanceType() ||
12987330f729Sjoerg       !OM->getReturnType()->isObjCIdType())
12997330f729Sjoerg     return;
13007330f729Sjoerg 
13017330f729Sjoerg   // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class
13027330f729Sjoerg   // NSYYYNamE with matching names be at least 3 characters long.
13037330f729Sjoerg   ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
13047330f729Sjoerg   if (!IDecl) {
13057330f729Sjoerg     if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
13067330f729Sjoerg       IDecl = CatDecl->getClassInterface();
13077330f729Sjoerg     else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
13087330f729Sjoerg       IDecl = ImpDecl->getClassInterface();
13097330f729Sjoerg   }
13107330f729Sjoerg   if (!IDecl)
13117330f729Sjoerg     return;
13127330f729Sjoerg 
1313*e038c9c4Sjoerg   std::string StringClassName = std::string(IDecl->getName());
13147330f729Sjoerg   StringRef LoweredClassName(StringClassName);
13157330f729Sjoerg   std::string StringLoweredClassName = LoweredClassName.lower();
13167330f729Sjoerg   LoweredClassName = StringLoweredClassName;
13177330f729Sjoerg 
13187330f729Sjoerg   IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0);
13197330f729Sjoerg   // Handle method with no name at its first selector slot; e.g. + (id):(int)x.
13207330f729Sjoerg   if (!MethodIdName)
13217330f729Sjoerg     return;
13227330f729Sjoerg 
1323*e038c9c4Sjoerg   std::string MethodName = std::string(MethodIdName->getName());
13247330f729Sjoerg   if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) {
13257330f729Sjoerg     StringRef STRefMethodName(MethodName);
13267330f729Sjoerg     size_t len = 0;
13277330f729Sjoerg     if (STRefMethodName.startswith("standard"))
13287330f729Sjoerg       len = strlen("standard");
13297330f729Sjoerg     else if (STRefMethodName.startswith("shared"))
13307330f729Sjoerg       len = strlen("shared");
13317330f729Sjoerg     else if (STRefMethodName.startswith("default"))
13327330f729Sjoerg       len = strlen("default");
13337330f729Sjoerg     else
13347330f729Sjoerg       return;
1335*e038c9c4Sjoerg     MethodName = std::string(STRefMethodName.substr(len));
13367330f729Sjoerg   }
13377330f729Sjoerg   std::string MethodNameSubStr = MethodName.substr(0, 3);
13387330f729Sjoerg   StringRef MethodNamePrefix(MethodNameSubStr);
13397330f729Sjoerg   std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
13407330f729Sjoerg   MethodNamePrefix = StringLoweredMethodNamePrefix;
13417330f729Sjoerg   size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
13427330f729Sjoerg   if (Ix == StringRef::npos)
13437330f729Sjoerg     return;
1344*e038c9c4Sjoerg   std::string ClassNamePostfix = std::string(LoweredClassName.substr(Ix));
13457330f729Sjoerg   StringRef LoweredMethodName(MethodName);
13467330f729Sjoerg   std::string StringLoweredMethodName = LoweredMethodName.lower();
13477330f729Sjoerg   LoweredMethodName = StringLoweredMethodName;
13487330f729Sjoerg   if (!LoweredMethodName.startswith(ClassNamePostfix))
13497330f729Sjoerg     return;
13507330f729Sjoerg   if (OIT_Family == OIT_ReturnsSelf)
13517330f729Sjoerg     ReplaceWithClasstype(*this, OM);
13527330f729Sjoerg   else
13537330f729Sjoerg     ReplaceWithInstancetype(Ctx, *this, OM);
13547330f729Sjoerg }
13557330f729Sjoerg 
IsVoidStarType(QualType Ty)13567330f729Sjoerg static bool IsVoidStarType(QualType Ty) {
13577330f729Sjoerg   if (!Ty->isPointerType())
13587330f729Sjoerg     return false;
13597330f729Sjoerg 
13607330f729Sjoerg   while (const TypedefType *TD = dyn_cast<TypedefType>(Ty.getTypePtr()))
13617330f729Sjoerg     Ty = TD->getDecl()->getUnderlyingType();
13627330f729Sjoerg 
13637330f729Sjoerg   // Is the type void*?
1364*e038c9c4Sjoerg   const PointerType* PT = Ty->castAs<PointerType>();
13657330f729Sjoerg   if (PT->getPointeeType().getUnqualifiedType()->isVoidType())
13667330f729Sjoerg     return true;
13677330f729Sjoerg   return IsVoidStarType(PT->getPointeeType());
13687330f729Sjoerg }
13697330f729Sjoerg 
13707330f729Sjoerg /// AuditedType - This routine audits the type AT and returns false if it is one of known
13717330f729Sjoerg /// CF object types or of the "void *" variety. It returns true if we don't care about the type
13727330f729Sjoerg /// such as a non-pointer or pointers which have no ownership issues (such as "int *").
AuditedType(QualType AT)13737330f729Sjoerg static bool AuditedType (QualType AT) {
13747330f729Sjoerg   if (!AT->isAnyPointerType() && !AT->isBlockPointerType())
13757330f729Sjoerg     return true;
13767330f729Sjoerg   // FIXME. There isn't much we can say about CF pointer type; or is there?
13777330f729Sjoerg   if (ento::coreFoundation::isCFObjectRef(AT) ||
13787330f729Sjoerg       IsVoidStarType(AT) ||
13797330f729Sjoerg       // If an ObjC object is type, assuming that it is not a CF function and
13807330f729Sjoerg       // that it is an un-audited function.
13817330f729Sjoerg       AT->isObjCObjectPointerType() || AT->isObjCBuiltinType())
13827330f729Sjoerg     return false;
13837330f729Sjoerg   // All other pointers are assumed audited as harmless.
13847330f729Sjoerg   return true;
13857330f729Sjoerg }
13867330f729Sjoerg 
AnnotateImplicitBridging(ASTContext & Ctx)13877330f729Sjoerg void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) {
13887330f729Sjoerg   if (CFFunctionIBCandidates.empty())
13897330f729Sjoerg     return;
13907330f729Sjoerg   if (!NSAPIObj->isMacroDefined("CF_IMPLICIT_BRIDGING_ENABLED")) {
13917330f729Sjoerg     CFFunctionIBCandidates.clear();
13927330f729Sjoerg     FileId = FileID();
13937330f729Sjoerg     return;
13947330f729Sjoerg   }
13957330f729Sjoerg   // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED
13967330f729Sjoerg   const Decl *FirstFD = CFFunctionIBCandidates[0];
13977330f729Sjoerg   const Decl *LastFD  =
13987330f729Sjoerg     CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1];
13997330f729Sjoerg   const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n";
14007330f729Sjoerg   edit::Commit commit(*Editor);
14017330f729Sjoerg   commit.insertBefore(FirstFD->getBeginLoc(), PragmaString);
14027330f729Sjoerg   PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n";
14037330f729Sjoerg   SourceLocation EndLoc = LastFD->getEndLoc();
14047330f729Sjoerg   // get location just past end of function location.
14057330f729Sjoerg   EndLoc = PP.getLocForEndOfToken(EndLoc);
14067330f729Sjoerg   if (isa<FunctionDecl>(LastFD)) {
14077330f729Sjoerg     // For Methods, EndLoc points to the ending semcolon. So,
14087330f729Sjoerg     // not of these extra work is needed.
14097330f729Sjoerg     Token Tok;
14107330f729Sjoerg     // get locaiton of token that comes after end of function.
14117330f729Sjoerg     bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true);
14127330f729Sjoerg     if (!Failed)
14137330f729Sjoerg       EndLoc = Tok.getLocation();
14147330f729Sjoerg   }
14157330f729Sjoerg   commit.insertAfterToken(EndLoc, PragmaString);
14167330f729Sjoerg   Editor->commit(commit);
14177330f729Sjoerg   FileId = FileID();
14187330f729Sjoerg   CFFunctionIBCandidates.clear();
14197330f729Sjoerg }
14207330f729Sjoerg 
migrateCFAnnotation(ASTContext & Ctx,const Decl * Decl)14217330f729Sjoerg void ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) {
14227330f729Sjoerg   if (Decl->isDeprecated())
14237330f729Sjoerg     return;
14247330f729Sjoerg 
14257330f729Sjoerg   if (Decl->hasAttr<CFAuditedTransferAttr>()) {
14267330f729Sjoerg     assert(CFFunctionIBCandidates.empty() &&
14277330f729Sjoerg            "Cannot have audited functions/methods inside user "
14287330f729Sjoerg            "provided CF_IMPLICIT_BRIDGING_ENABLE");
14297330f729Sjoerg     return;
14307330f729Sjoerg   }
14317330f729Sjoerg 
14327330f729Sjoerg   // Finction must be annotated first.
14337330f729Sjoerg   if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) {
14347330f729Sjoerg     CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl);
14357330f729Sjoerg     if (AuditKind == CF_BRIDGING_ENABLE) {
14367330f729Sjoerg       CFFunctionIBCandidates.push_back(Decl);
14377330f729Sjoerg       if (FileId.isInvalid())
14387330f729Sjoerg         FileId = PP.getSourceManager().getFileID(Decl->getLocation());
14397330f729Sjoerg     }
14407330f729Sjoerg     else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) {
14417330f729Sjoerg       if (!CFFunctionIBCandidates.empty()) {
14427330f729Sjoerg         CFFunctionIBCandidates.push_back(Decl);
14437330f729Sjoerg         if (FileId.isInvalid())
14447330f729Sjoerg           FileId = PP.getSourceManager().getFileID(Decl->getLocation());
14457330f729Sjoerg       }
14467330f729Sjoerg     }
14477330f729Sjoerg     else
14487330f729Sjoerg       AnnotateImplicitBridging(Ctx);
14497330f729Sjoerg   }
14507330f729Sjoerg   else {
14517330f729Sjoerg     migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl));
14527330f729Sjoerg     AnnotateImplicitBridging(Ctx);
14537330f729Sjoerg   }
14547330f729Sjoerg }
14557330f729Sjoerg 
AddCFAnnotations(ASTContext & Ctx,const RetainSummary * RS,const FunctionDecl * FuncDecl,bool ResultAnnotated)14567330f729Sjoerg void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,
14577330f729Sjoerg                                               const RetainSummary *RS,
14587330f729Sjoerg                                               const FunctionDecl *FuncDecl,
14597330f729Sjoerg                                               bool ResultAnnotated) {
14607330f729Sjoerg   // Annotate function.
14617330f729Sjoerg   if (!ResultAnnotated) {
14627330f729Sjoerg     RetEffect Ret = RS->getRetEffect();
14637330f729Sjoerg     const char *AnnotationString = nullptr;
14647330f729Sjoerg     if (Ret.getObjKind() == ObjKind::CF) {
14657330f729Sjoerg       if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED"))
14667330f729Sjoerg         AnnotationString = " CF_RETURNS_RETAINED";
14677330f729Sjoerg       else if (Ret.notOwned() &&
14687330f729Sjoerg                NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED"))
14697330f729Sjoerg         AnnotationString = " CF_RETURNS_NOT_RETAINED";
14707330f729Sjoerg     }
14717330f729Sjoerg     else if (Ret.getObjKind() == ObjKind::ObjC) {
14727330f729Sjoerg       if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED"))
14737330f729Sjoerg         AnnotationString = " NS_RETURNS_RETAINED";
14747330f729Sjoerg     }
14757330f729Sjoerg 
14767330f729Sjoerg     if (AnnotationString) {
14777330f729Sjoerg       edit::Commit commit(*Editor);
14787330f729Sjoerg       commit.insertAfterToken(FuncDecl->getEndLoc(), AnnotationString);
14797330f729Sjoerg       Editor->commit(commit);
14807330f729Sjoerg     }
14817330f729Sjoerg   }
14827330f729Sjoerg   unsigned i = 0;
14837330f729Sjoerg   for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
14847330f729Sjoerg        pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
14857330f729Sjoerg     const ParmVarDecl *pd = *pi;
14867330f729Sjoerg     ArgEffect AE = RS->getArg(i);
14877330f729Sjoerg     if (AE.getKind() == DecRef && AE.getObjKind() == ObjKind::CF &&
14887330f729Sjoerg         !pd->hasAttr<CFConsumedAttr>() &&
14897330f729Sjoerg         NSAPIObj->isMacroDefined("CF_CONSUMED")) {
14907330f729Sjoerg       edit::Commit commit(*Editor);
14917330f729Sjoerg       commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");
14927330f729Sjoerg       Editor->commit(commit);
14937330f729Sjoerg     } else if (AE.getKind() == DecRef && AE.getObjKind() == ObjKind::ObjC &&
14947330f729Sjoerg                !pd->hasAttr<NSConsumedAttr>() &&
14957330f729Sjoerg                NSAPIObj->isMacroDefined("NS_CONSUMED")) {
14967330f729Sjoerg       edit::Commit commit(*Editor);
14977330f729Sjoerg       commit.insertBefore(pd->getLocation(), "NS_CONSUMED ");
14987330f729Sjoerg       Editor->commit(commit);
14997330f729Sjoerg     }
15007330f729Sjoerg   }
15017330f729Sjoerg }
15027330f729Sjoerg 
15037330f729Sjoerg ObjCMigrateASTConsumer::CF_BRIDGING_KIND
migrateAddFunctionAnnotation(ASTContext & Ctx,const FunctionDecl * FuncDecl)15047330f729Sjoerg   ObjCMigrateASTConsumer::migrateAddFunctionAnnotation(
15057330f729Sjoerg                                                   ASTContext &Ctx,
15067330f729Sjoerg                                                   const FunctionDecl *FuncDecl) {
15077330f729Sjoerg   if (FuncDecl->hasBody())
15087330f729Sjoerg     return CF_BRIDGING_NONE;
15097330f729Sjoerg 
15107330f729Sjoerg   const RetainSummary *RS =
15117330f729Sjoerg       getSummaryManager(Ctx).getSummary(AnyCall(FuncDecl));
15127330f729Sjoerg   bool FuncIsReturnAnnotated = (FuncDecl->hasAttr<CFReturnsRetainedAttr>() ||
15137330f729Sjoerg                                 FuncDecl->hasAttr<CFReturnsNotRetainedAttr>() ||
15147330f729Sjoerg                                 FuncDecl->hasAttr<NSReturnsRetainedAttr>() ||
15157330f729Sjoerg                                 FuncDecl->hasAttr<NSReturnsNotRetainedAttr>() ||
15167330f729Sjoerg                                 FuncDecl->hasAttr<NSReturnsAutoreleasedAttr>());
15177330f729Sjoerg 
15187330f729Sjoerg   // Trivial case of when function is annotated and has no argument.
15197330f729Sjoerg   if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0)
15207330f729Sjoerg     return CF_BRIDGING_NONE;
15217330f729Sjoerg 
15227330f729Sjoerg   bool ReturnCFAudited = false;
15237330f729Sjoerg   if (!FuncIsReturnAnnotated) {
15247330f729Sjoerg     RetEffect Ret = RS->getRetEffect();
15257330f729Sjoerg     if (Ret.getObjKind() == ObjKind::CF &&
15267330f729Sjoerg         (Ret.isOwned() || Ret.notOwned()))
15277330f729Sjoerg       ReturnCFAudited = true;
15287330f729Sjoerg     else if (!AuditedType(FuncDecl->getReturnType()))
15297330f729Sjoerg       return CF_BRIDGING_NONE;
15307330f729Sjoerg   }
15317330f729Sjoerg 
15327330f729Sjoerg   // At this point result type is audited for potential inclusion.
15337330f729Sjoerg   unsigned i = 0;
15347330f729Sjoerg   bool ArgCFAudited = false;
15357330f729Sjoerg   for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
15367330f729Sjoerg        pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
15377330f729Sjoerg     const ParmVarDecl *pd = *pi;
15387330f729Sjoerg     ArgEffect AE = RS->getArg(i);
15397330f729Sjoerg     if ((AE.getKind() == DecRef /*CFConsumed annotated*/ ||
15407330f729Sjoerg          AE.getKind() == IncRef) && AE.getObjKind() == ObjKind::CF) {
15417330f729Sjoerg       if (AE.getKind() == DecRef && !pd->hasAttr<CFConsumedAttr>())
15427330f729Sjoerg         ArgCFAudited = true;
15437330f729Sjoerg       else if (AE.getKind() == IncRef)
15447330f729Sjoerg         ArgCFAudited = true;
15457330f729Sjoerg     } else {
15467330f729Sjoerg       QualType AT = pd->getType();
15477330f729Sjoerg       if (!AuditedType(AT)) {
15487330f729Sjoerg         AddCFAnnotations(Ctx, RS, FuncDecl, FuncIsReturnAnnotated);
15497330f729Sjoerg         return CF_BRIDGING_NONE;
15507330f729Sjoerg       }
15517330f729Sjoerg     }
15527330f729Sjoerg   }
15537330f729Sjoerg   if (ReturnCFAudited || ArgCFAudited)
15547330f729Sjoerg     return CF_BRIDGING_ENABLE;
15557330f729Sjoerg 
15567330f729Sjoerg   return CF_BRIDGING_MAY_INCLUDE;
15577330f729Sjoerg }
15587330f729Sjoerg 
migrateARCSafeAnnotation(ASTContext & Ctx,ObjCContainerDecl * CDecl)15597330f729Sjoerg void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx,
15607330f729Sjoerg                                                  ObjCContainerDecl *CDecl) {
15617330f729Sjoerg   if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated())
15627330f729Sjoerg     return;
15637330f729Sjoerg 
15647330f729Sjoerg   // migrate methods which can have instancetype as their result type.
15657330f729Sjoerg   for (const auto *Method : CDecl->methods())
15667330f729Sjoerg     migrateCFAnnotation(Ctx, Method);
15677330f729Sjoerg }
15687330f729Sjoerg 
AddCFAnnotations(ASTContext & Ctx,const RetainSummary * RS,const ObjCMethodDecl * MethodDecl,bool ResultAnnotated)15697330f729Sjoerg void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,
15707330f729Sjoerg                                               const RetainSummary *RS,
15717330f729Sjoerg                                               const ObjCMethodDecl *MethodDecl,
15727330f729Sjoerg                                               bool ResultAnnotated) {
15737330f729Sjoerg   // Annotate function.
15747330f729Sjoerg   if (!ResultAnnotated) {
15757330f729Sjoerg     RetEffect Ret = RS->getRetEffect();
15767330f729Sjoerg     const char *AnnotationString = nullptr;
15777330f729Sjoerg     if (Ret.getObjKind() == ObjKind::CF) {
15787330f729Sjoerg       if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED"))
15797330f729Sjoerg         AnnotationString = " CF_RETURNS_RETAINED";
15807330f729Sjoerg       else if (Ret.notOwned() &&
15817330f729Sjoerg                NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED"))
15827330f729Sjoerg         AnnotationString = " CF_RETURNS_NOT_RETAINED";
15837330f729Sjoerg     }
15847330f729Sjoerg     else if (Ret.getObjKind() == ObjKind::ObjC) {
15857330f729Sjoerg       ObjCMethodFamily OMF = MethodDecl->getMethodFamily();
15867330f729Sjoerg       switch (OMF) {
15877330f729Sjoerg         case clang::OMF_alloc:
15887330f729Sjoerg         case clang::OMF_new:
15897330f729Sjoerg         case clang::OMF_copy:
15907330f729Sjoerg         case clang::OMF_init:
15917330f729Sjoerg         case clang::OMF_mutableCopy:
15927330f729Sjoerg           break;
15937330f729Sjoerg 
15947330f729Sjoerg         default:
15957330f729Sjoerg           if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED"))
15967330f729Sjoerg             AnnotationString = " NS_RETURNS_RETAINED";
15977330f729Sjoerg           break;
15987330f729Sjoerg       }
15997330f729Sjoerg     }
16007330f729Sjoerg 
16017330f729Sjoerg     if (AnnotationString) {
16027330f729Sjoerg       edit::Commit commit(*Editor);
16037330f729Sjoerg       commit.insertBefore(MethodDecl->getEndLoc(), AnnotationString);
16047330f729Sjoerg       Editor->commit(commit);
16057330f729Sjoerg     }
16067330f729Sjoerg   }
16077330f729Sjoerg   unsigned i = 0;
16087330f729Sjoerg   for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),
16097330f729Sjoerg        pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {
16107330f729Sjoerg     const ParmVarDecl *pd = *pi;
16117330f729Sjoerg     ArgEffect AE = RS->getArg(i);
16127330f729Sjoerg     if (AE.getKind() == DecRef
16137330f729Sjoerg         && AE.getObjKind() == ObjKind::CF
16147330f729Sjoerg         && !pd->hasAttr<CFConsumedAttr>() &&
16157330f729Sjoerg         NSAPIObj->isMacroDefined("CF_CONSUMED")) {
16167330f729Sjoerg       edit::Commit commit(*Editor);
16177330f729Sjoerg       commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");
16187330f729Sjoerg       Editor->commit(commit);
16197330f729Sjoerg     }
16207330f729Sjoerg   }
16217330f729Sjoerg }
16227330f729Sjoerg 
migrateAddMethodAnnotation(ASTContext & Ctx,const ObjCMethodDecl * MethodDecl)16237330f729Sjoerg void ObjCMigrateASTConsumer::migrateAddMethodAnnotation(
16247330f729Sjoerg                                             ASTContext &Ctx,
16257330f729Sjoerg                                             const ObjCMethodDecl *MethodDecl) {
16267330f729Sjoerg   if (MethodDecl->hasBody() || MethodDecl->isImplicit())
16277330f729Sjoerg     return;
16287330f729Sjoerg 
16297330f729Sjoerg   const RetainSummary *RS =
16307330f729Sjoerg       getSummaryManager(Ctx).getSummary(AnyCall(MethodDecl));
16317330f729Sjoerg 
16327330f729Sjoerg   bool MethodIsReturnAnnotated =
16337330f729Sjoerg       (MethodDecl->hasAttr<CFReturnsRetainedAttr>() ||
16347330f729Sjoerg        MethodDecl->hasAttr<CFReturnsNotRetainedAttr>() ||
16357330f729Sjoerg        MethodDecl->hasAttr<NSReturnsRetainedAttr>() ||
16367330f729Sjoerg        MethodDecl->hasAttr<NSReturnsNotRetainedAttr>() ||
16377330f729Sjoerg        MethodDecl->hasAttr<NSReturnsAutoreleasedAttr>());
16387330f729Sjoerg 
16397330f729Sjoerg   if (RS->getReceiverEffect().getKind() == DecRef &&
16407330f729Sjoerg       !MethodDecl->hasAttr<NSConsumesSelfAttr>() &&
16417330f729Sjoerg       MethodDecl->getMethodFamily() != OMF_init &&
16427330f729Sjoerg       MethodDecl->getMethodFamily() != OMF_release &&
16437330f729Sjoerg       NSAPIObj->isMacroDefined("NS_CONSUMES_SELF")) {
16447330f729Sjoerg     edit::Commit commit(*Editor);
16457330f729Sjoerg     commit.insertBefore(MethodDecl->getEndLoc(), " NS_CONSUMES_SELF");
16467330f729Sjoerg     Editor->commit(commit);
16477330f729Sjoerg   }
16487330f729Sjoerg 
16497330f729Sjoerg   // Trivial case of when function is annotated and has no argument.
16507330f729Sjoerg   if (MethodIsReturnAnnotated &&
16517330f729Sjoerg       (MethodDecl->param_begin() == MethodDecl->param_end()))
16527330f729Sjoerg     return;
16537330f729Sjoerg 
16547330f729Sjoerg   if (!MethodIsReturnAnnotated) {
16557330f729Sjoerg     RetEffect Ret = RS->getRetEffect();
16567330f729Sjoerg     if ((Ret.getObjKind() == ObjKind::CF ||
16577330f729Sjoerg          Ret.getObjKind() == ObjKind::ObjC) &&
16587330f729Sjoerg         (Ret.isOwned() || Ret.notOwned())) {
16597330f729Sjoerg       AddCFAnnotations(Ctx, RS, MethodDecl, false);
16607330f729Sjoerg       return;
16617330f729Sjoerg     } else if (!AuditedType(MethodDecl->getReturnType()))
16627330f729Sjoerg       return;
16637330f729Sjoerg   }
16647330f729Sjoerg 
16657330f729Sjoerg   // At this point result type is either annotated or audited.
16667330f729Sjoerg   unsigned i = 0;
16677330f729Sjoerg   for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),
16687330f729Sjoerg        pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {
16697330f729Sjoerg     const ParmVarDecl *pd = *pi;
16707330f729Sjoerg     ArgEffect AE = RS->getArg(i);
16717330f729Sjoerg     if ((AE.getKind() == DecRef && !pd->hasAttr<CFConsumedAttr>()) ||
16727330f729Sjoerg         AE.getKind() == IncRef || !AuditedType(pd->getType())) {
16737330f729Sjoerg       AddCFAnnotations(Ctx, RS, MethodDecl, MethodIsReturnAnnotated);
16747330f729Sjoerg       return;
16757330f729Sjoerg     }
16767330f729Sjoerg   }
16777330f729Sjoerg }
16787330f729Sjoerg 
16797330f729Sjoerg namespace {
16807330f729Sjoerg class SuperInitChecker : public RecursiveASTVisitor<SuperInitChecker> {
16817330f729Sjoerg public:
shouldVisitTemplateInstantiations() const16827330f729Sjoerg   bool shouldVisitTemplateInstantiations() const { return false; }
shouldWalkTypesOfTypeLocs() const16837330f729Sjoerg   bool shouldWalkTypesOfTypeLocs() const { return false; }
16847330f729Sjoerg 
VisitObjCMessageExpr(ObjCMessageExpr * E)16857330f729Sjoerg   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
16867330f729Sjoerg     if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) {
16877330f729Sjoerg       if (E->getMethodFamily() == OMF_init)
16887330f729Sjoerg         return false;
16897330f729Sjoerg     }
16907330f729Sjoerg     return true;
16917330f729Sjoerg   }
16927330f729Sjoerg };
16937330f729Sjoerg } // end anonymous namespace
16947330f729Sjoerg 
hasSuperInitCall(const ObjCMethodDecl * MD)16957330f729Sjoerg static bool hasSuperInitCall(const ObjCMethodDecl *MD) {
16967330f729Sjoerg   return !SuperInitChecker().TraverseStmt(MD->getBody());
16977330f729Sjoerg }
16987330f729Sjoerg 
inferDesignatedInitializers(ASTContext & Ctx,const ObjCImplementationDecl * ImplD)16997330f729Sjoerg void ObjCMigrateASTConsumer::inferDesignatedInitializers(
17007330f729Sjoerg     ASTContext &Ctx,
17017330f729Sjoerg     const ObjCImplementationDecl *ImplD) {
17027330f729Sjoerg 
17037330f729Sjoerg   const ObjCInterfaceDecl *IFace = ImplD->getClassInterface();
17047330f729Sjoerg   if (!IFace || IFace->hasDesignatedInitializers())
17057330f729Sjoerg     return;
17067330f729Sjoerg   if (!NSAPIObj->isMacroDefined("NS_DESIGNATED_INITIALIZER"))
17077330f729Sjoerg     return;
17087330f729Sjoerg 
17097330f729Sjoerg   for (const auto *MD : ImplD->instance_methods()) {
17107330f729Sjoerg     if (MD->isDeprecated() ||
17117330f729Sjoerg         MD->getMethodFamily() != OMF_init ||
17127330f729Sjoerg         MD->isDesignatedInitializerForTheInterface())
17137330f729Sjoerg       continue;
17147330f729Sjoerg     const ObjCMethodDecl *IFaceM = IFace->getMethod(MD->getSelector(),
17157330f729Sjoerg                                                     /*isInstance=*/true);
17167330f729Sjoerg     if (!IFaceM)
17177330f729Sjoerg       continue;
17187330f729Sjoerg     if (hasSuperInitCall(MD)) {
17197330f729Sjoerg       edit::Commit commit(*Editor);
17207330f729Sjoerg       commit.insert(IFaceM->getEndLoc(), " NS_DESIGNATED_INITIALIZER");
17217330f729Sjoerg       Editor->commit(commit);
17227330f729Sjoerg     }
17237330f729Sjoerg   }
17247330f729Sjoerg }
17257330f729Sjoerg 
InsertFoundation(ASTContext & Ctx,SourceLocation Loc)17267330f729Sjoerg bool ObjCMigrateASTConsumer::InsertFoundation(ASTContext &Ctx,
17277330f729Sjoerg                                               SourceLocation  Loc) {
17287330f729Sjoerg   if (FoundationIncluded)
17297330f729Sjoerg     return true;
17307330f729Sjoerg   if (Loc.isInvalid())
17317330f729Sjoerg     return false;
17327330f729Sjoerg   auto *nsEnumId = &Ctx.Idents.get("NS_ENUM");
17337330f729Sjoerg   if (PP.getMacroDefinitionAtLoc(nsEnumId, Loc)) {
17347330f729Sjoerg     FoundationIncluded = true;
17357330f729Sjoerg     return true;
17367330f729Sjoerg   }
17377330f729Sjoerg   edit::Commit commit(*Editor);
17387330f729Sjoerg   if (Ctx.getLangOpts().Modules)
17397330f729Sjoerg     commit.insert(Loc, "#ifndef NS_ENUM\n@import Foundation;\n#endif\n");
17407330f729Sjoerg   else
17417330f729Sjoerg     commit.insert(Loc, "#ifndef NS_ENUM\n#import <Foundation/Foundation.h>\n#endif\n");
17427330f729Sjoerg   Editor->commit(commit);
17437330f729Sjoerg   FoundationIncluded = true;
17447330f729Sjoerg   return true;
17457330f729Sjoerg }
17467330f729Sjoerg 
17477330f729Sjoerg namespace {
17487330f729Sjoerg 
17497330f729Sjoerg class RewritesReceiver : public edit::EditsReceiver {
17507330f729Sjoerg   Rewriter &Rewrite;
17517330f729Sjoerg 
17527330f729Sjoerg public:
RewritesReceiver(Rewriter & Rewrite)17537330f729Sjoerg   RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
17547330f729Sjoerg 
insert(SourceLocation loc,StringRef text)17557330f729Sjoerg   void insert(SourceLocation loc, StringRef text) override {
17567330f729Sjoerg     Rewrite.InsertText(loc, text);
17577330f729Sjoerg   }
replace(CharSourceRange range,StringRef text)17587330f729Sjoerg   void replace(CharSourceRange range, StringRef text) override {
17597330f729Sjoerg     Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
17607330f729Sjoerg   }
17617330f729Sjoerg };
17627330f729Sjoerg 
17637330f729Sjoerg class JSONEditWriter : public edit::EditsReceiver {
17647330f729Sjoerg   SourceManager &SourceMgr;
17657330f729Sjoerg   llvm::raw_ostream &OS;
17667330f729Sjoerg 
17677330f729Sjoerg public:
JSONEditWriter(SourceManager & SM,llvm::raw_ostream & OS)17687330f729Sjoerg   JSONEditWriter(SourceManager &SM, llvm::raw_ostream &OS)
17697330f729Sjoerg     : SourceMgr(SM), OS(OS) {
17707330f729Sjoerg     OS << "[\n";
17717330f729Sjoerg   }
~JSONEditWriter()17727330f729Sjoerg   ~JSONEditWriter() override { OS << "]\n"; }
17737330f729Sjoerg 
17747330f729Sjoerg private:
17757330f729Sjoerg   struct EntryWriter {
17767330f729Sjoerg     SourceManager &SourceMgr;
17777330f729Sjoerg     llvm::raw_ostream &OS;
17787330f729Sjoerg 
EntryWriter__anon0304ea920411::JSONEditWriter::EntryWriter17797330f729Sjoerg     EntryWriter(SourceManager &SM, llvm::raw_ostream &OS)
17807330f729Sjoerg       : SourceMgr(SM), OS(OS) {
17817330f729Sjoerg       OS << " {\n";
17827330f729Sjoerg     }
~EntryWriter__anon0304ea920411::JSONEditWriter::EntryWriter17837330f729Sjoerg     ~EntryWriter() {
17847330f729Sjoerg       OS << " },\n";
17857330f729Sjoerg     }
17867330f729Sjoerg 
writeLoc__anon0304ea920411::JSONEditWriter::EntryWriter17877330f729Sjoerg     void writeLoc(SourceLocation Loc) {
17887330f729Sjoerg       FileID FID;
17897330f729Sjoerg       unsigned Offset;
17907330f729Sjoerg       std::tie(FID, Offset) = SourceMgr.getDecomposedLoc(Loc);
17917330f729Sjoerg       assert(FID.isValid());
17927330f729Sjoerg       SmallString<200> Path =
17937330f729Sjoerg           StringRef(SourceMgr.getFileEntryForID(FID)->getName());
17947330f729Sjoerg       llvm::sys::fs::make_absolute(Path);
17957330f729Sjoerg       OS << "  \"file\": \"";
17967330f729Sjoerg       OS.write_escaped(Path.str()) << "\",\n";
17977330f729Sjoerg       OS << "  \"offset\": " << Offset << ",\n";
17987330f729Sjoerg     }
17997330f729Sjoerg 
writeRemove__anon0304ea920411::JSONEditWriter::EntryWriter18007330f729Sjoerg     void writeRemove(CharSourceRange Range) {
18017330f729Sjoerg       assert(Range.isCharRange());
18027330f729Sjoerg       std::pair<FileID, unsigned> Begin =
18037330f729Sjoerg           SourceMgr.getDecomposedLoc(Range.getBegin());
18047330f729Sjoerg       std::pair<FileID, unsigned> End =
18057330f729Sjoerg           SourceMgr.getDecomposedLoc(Range.getEnd());
18067330f729Sjoerg       assert(Begin.first == End.first);
18077330f729Sjoerg       assert(Begin.second <= End.second);
18087330f729Sjoerg       unsigned Length = End.second - Begin.second;
18097330f729Sjoerg 
18107330f729Sjoerg       OS << "  \"remove\": " << Length << ",\n";
18117330f729Sjoerg     }
18127330f729Sjoerg 
writeText__anon0304ea920411::JSONEditWriter::EntryWriter18137330f729Sjoerg     void writeText(StringRef Text) {
18147330f729Sjoerg       OS << "  \"text\": \"";
18157330f729Sjoerg       OS.write_escaped(Text) << "\",\n";
18167330f729Sjoerg     }
18177330f729Sjoerg   };
18187330f729Sjoerg 
insert(SourceLocation Loc,StringRef Text)18197330f729Sjoerg   void insert(SourceLocation Loc, StringRef Text) override {
18207330f729Sjoerg     EntryWriter Writer(SourceMgr, OS);
18217330f729Sjoerg     Writer.writeLoc(Loc);
18227330f729Sjoerg     Writer.writeText(Text);
18237330f729Sjoerg   }
18247330f729Sjoerg 
replace(CharSourceRange Range,StringRef Text)18257330f729Sjoerg   void replace(CharSourceRange Range, StringRef Text) override {
18267330f729Sjoerg     EntryWriter Writer(SourceMgr, OS);
18277330f729Sjoerg     Writer.writeLoc(Range.getBegin());
18287330f729Sjoerg     Writer.writeRemove(Range);
18297330f729Sjoerg     Writer.writeText(Text);
18307330f729Sjoerg   }
18317330f729Sjoerg 
remove(CharSourceRange Range)18327330f729Sjoerg   void remove(CharSourceRange Range) override {
18337330f729Sjoerg     EntryWriter Writer(SourceMgr, OS);
18347330f729Sjoerg     Writer.writeLoc(Range.getBegin());
18357330f729Sjoerg     Writer.writeRemove(Range);
18367330f729Sjoerg   }
18377330f729Sjoerg };
18387330f729Sjoerg 
18397330f729Sjoerg } // end anonymous namespace
18407330f729Sjoerg 
HandleTranslationUnit(ASTContext & Ctx)18417330f729Sjoerg void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
18427330f729Sjoerg 
18437330f729Sjoerg   TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
18447330f729Sjoerg   if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) {
18457330f729Sjoerg     for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
18467330f729Sjoerg          D != DEnd; ++D) {
18477330f729Sjoerg       FileID FID = PP.getSourceManager().getFileID((*D)->getLocation());
18487330f729Sjoerg       if (FID.isValid())
18497330f729Sjoerg         if (FileId.isValid() && FileId != FID) {
18507330f729Sjoerg           if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
18517330f729Sjoerg             AnnotateImplicitBridging(Ctx);
18527330f729Sjoerg         }
18537330f729Sjoerg 
18547330f729Sjoerg       if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
18557330f729Sjoerg         if (canModify(CDecl))
18567330f729Sjoerg           migrateObjCContainerDecl(Ctx, CDecl);
18577330f729Sjoerg       if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) {
18587330f729Sjoerg         if (canModify(CatDecl))
18597330f729Sjoerg           migrateObjCContainerDecl(Ctx, CatDecl);
18607330f729Sjoerg       }
18617330f729Sjoerg       else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) {
18627330f729Sjoerg         ObjCProtocolDecls.insert(PDecl->getCanonicalDecl());
18637330f729Sjoerg         if (canModify(PDecl))
18647330f729Sjoerg           migrateObjCContainerDecl(Ctx, PDecl);
18657330f729Sjoerg       }
18667330f729Sjoerg       else if (const ObjCImplementationDecl *ImpDecl =
18677330f729Sjoerg                dyn_cast<ObjCImplementationDecl>(*D)) {
18687330f729Sjoerg         if ((ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance) &&
18697330f729Sjoerg             canModify(ImpDecl))
18707330f729Sjoerg           migrateProtocolConformance(Ctx, ImpDecl);
18717330f729Sjoerg       }
18727330f729Sjoerg       else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
18737330f729Sjoerg         if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))
18747330f729Sjoerg           continue;
18757330f729Sjoerg         if (!canModify(ED))
18767330f729Sjoerg           continue;
18777330f729Sjoerg         DeclContext::decl_iterator N = D;
18787330f729Sjoerg         if (++N != DEnd) {
18797330f729Sjoerg           const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N);
18807330f729Sjoerg           if (migrateNSEnumDecl(Ctx, ED, TD) && TD)
18817330f729Sjoerg             D++;
18827330f729Sjoerg         }
18837330f729Sjoerg         else
18847330f729Sjoerg           migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */nullptr);
18857330f729Sjoerg       }
18867330f729Sjoerg       else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) {
18877330f729Sjoerg         if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))
18887330f729Sjoerg           continue;
18897330f729Sjoerg         if (!canModify(TD))
18907330f729Sjoerg           continue;
18917330f729Sjoerg         DeclContext::decl_iterator N = D;
18927330f729Sjoerg         if (++N == DEnd)
18937330f729Sjoerg           continue;
18947330f729Sjoerg         if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) {
18957330f729Sjoerg           if (canModify(ED)) {
18967330f729Sjoerg             if (++N != DEnd)
18977330f729Sjoerg               if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) {
18987330f729Sjoerg                 // prefer typedef-follows-enum to enum-follows-typedef pattern.
18997330f729Sjoerg                 if (migrateNSEnumDecl(Ctx, ED, TDF)) {
19007330f729Sjoerg                   ++D; ++D;
19017330f729Sjoerg                   CacheObjCNSIntegerTypedefed(TD);
19027330f729Sjoerg                   continue;
19037330f729Sjoerg                 }
19047330f729Sjoerg               }
19057330f729Sjoerg             if (migrateNSEnumDecl(Ctx, ED, TD)) {
19067330f729Sjoerg               ++D;
19077330f729Sjoerg               continue;
19087330f729Sjoerg             }
19097330f729Sjoerg           }
19107330f729Sjoerg         }
19117330f729Sjoerg         CacheObjCNSIntegerTypedefed(TD);
19127330f729Sjoerg       }
19137330f729Sjoerg       else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) {
19147330f729Sjoerg         if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
19157330f729Sjoerg             canModify(FD))
19167330f729Sjoerg           migrateCFAnnotation(Ctx, FD);
19177330f729Sjoerg       }
19187330f729Sjoerg 
19197330f729Sjoerg       if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) {
19207330f729Sjoerg         bool CanModify = canModify(CDecl);
19217330f729Sjoerg         // migrate methods which can have instancetype as their result type.
19227330f729Sjoerg         if ((ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype) &&
19237330f729Sjoerg             CanModify)
19247330f729Sjoerg           migrateAllMethodInstaceType(Ctx, CDecl);
19257330f729Sjoerg         // annotate methods with CF annotations.
19267330f729Sjoerg         if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
19277330f729Sjoerg             CanModify)
19287330f729Sjoerg           migrateARCSafeAnnotation(Ctx, CDecl);
19297330f729Sjoerg       }
19307330f729Sjoerg 
19317330f729Sjoerg       if (const ObjCImplementationDecl *
19327330f729Sjoerg             ImplD = dyn_cast<ObjCImplementationDecl>(*D)) {
19337330f729Sjoerg         if ((ASTMigrateActions & FrontendOptions::ObjCMT_DesignatedInitializer) &&
19347330f729Sjoerg             canModify(ImplD))
19357330f729Sjoerg           inferDesignatedInitializers(Ctx, ImplD);
19367330f729Sjoerg       }
19377330f729Sjoerg     }
19387330f729Sjoerg     if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
19397330f729Sjoerg       AnnotateImplicitBridging(Ctx);
19407330f729Sjoerg   }
19417330f729Sjoerg 
19427330f729Sjoerg  if (IsOutputFile) {
19437330f729Sjoerg    std::error_code EC;
19447330f729Sjoerg    llvm::raw_fd_ostream OS(MigrateDir, EC, llvm::sys::fs::OF_None);
19457330f729Sjoerg    if (EC) {
19467330f729Sjoerg       DiagnosticsEngine &Diags = Ctx.getDiagnostics();
19477330f729Sjoerg       Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
19487330f729Sjoerg           << EC.message();
19497330f729Sjoerg       return;
19507330f729Sjoerg     }
19517330f729Sjoerg 
19527330f729Sjoerg    JSONEditWriter Writer(Ctx.getSourceManager(), OS);
19537330f729Sjoerg    Editor->applyRewrites(Writer);
19547330f729Sjoerg    return;
19557330f729Sjoerg  }
19567330f729Sjoerg 
19577330f729Sjoerg   Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
19587330f729Sjoerg   RewritesReceiver Rec(rewriter);
19597330f729Sjoerg   Editor->applyRewrites(Rec);
19607330f729Sjoerg 
19617330f729Sjoerg   for (Rewriter::buffer_iterator
19627330f729Sjoerg         I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
19637330f729Sjoerg     FileID FID = I->first;
19647330f729Sjoerg     RewriteBuffer &buf = I->second;
1965*e038c9c4Sjoerg     Optional<FileEntryRef> file = Ctx.getSourceManager().getFileEntryRefForID(FID);
19667330f729Sjoerg     assert(file);
19677330f729Sjoerg     SmallString<512> newText;
19687330f729Sjoerg     llvm::raw_svector_ostream vecOS(newText);
19697330f729Sjoerg     buf.write(vecOS);
19707330f729Sjoerg     std::unique_ptr<llvm::MemoryBuffer> memBuf(
19717330f729Sjoerg         llvm::MemoryBuffer::getMemBufferCopy(
19727330f729Sjoerg             StringRef(newText.data(), newText.size()), file->getName()));
19737330f729Sjoerg     SmallString<64> filePath(file->getName());
19747330f729Sjoerg     FileMgr.FixupRelativePath(filePath);
19757330f729Sjoerg     Remapper.remap(filePath.str(), std::move(memBuf));
19767330f729Sjoerg   }
19777330f729Sjoerg 
19787330f729Sjoerg   if (IsOutputFile) {
19797330f729Sjoerg     Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
19807330f729Sjoerg   } else {
19817330f729Sjoerg     Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
19827330f729Sjoerg   }
19837330f729Sjoerg }
19847330f729Sjoerg 
BeginInvocation(CompilerInstance & CI)19857330f729Sjoerg bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
19867330f729Sjoerg   CI.getDiagnostics().setIgnoreAllWarnings(true);
19877330f729Sjoerg   return true;
19887330f729Sjoerg }
19897330f729Sjoerg 
getWhiteListFilenames(StringRef DirPath)19907330f729Sjoerg static std::vector<std::string> getWhiteListFilenames(StringRef DirPath) {
19917330f729Sjoerg   using namespace llvm::sys::fs;
19927330f729Sjoerg   using namespace llvm::sys::path;
19937330f729Sjoerg 
19947330f729Sjoerg   std::vector<std::string> Filenames;
19957330f729Sjoerg   if (DirPath.empty() || !is_directory(DirPath))
19967330f729Sjoerg     return Filenames;
19977330f729Sjoerg 
19987330f729Sjoerg   std::error_code EC;
19997330f729Sjoerg   directory_iterator DI = directory_iterator(DirPath, EC);
20007330f729Sjoerg   directory_iterator DE;
20017330f729Sjoerg   for (; !EC && DI != DE; DI = DI.increment(EC)) {
20027330f729Sjoerg     if (is_regular_file(DI->path()))
2003*e038c9c4Sjoerg       Filenames.push_back(std::string(filename(DI->path())));
20047330f729Sjoerg   }
20057330f729Sjoerg 
20067330f729Sjoerg   return Filenames;
20077330f729Sjoerg }
20087330f729Sjoerg 
20097330f729Sjoerg std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)20107330f729Sjoerg MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
20117330f729Sjoerg   PPConditionalDirectiveRecord *
20127330f729Sjoerg     PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
20137330f729Sjoerg   unsigned ObjCMTAction = CI.getFrontendOpts().ObjCMTAction;
20147330f729Sjoerg   unsigned ObjCMTOpts = ObjCMTAction;
20157330f729Sjoerg   // These are companion flags, they do not enable transformations.
20167330f729Sjoerg   ObjCMTOpts &= ~(FrontendOptions::ObjCMT_AtomicProperty |
20177330f729Sjoerg                   FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty);
20187330f729Sjoerg   if (ObjCMTOpts == FrontendOptions::ObjCMT_None) {
20197330f729Sjoerg     // If no specific option was given, enable literals+subscripting transforms
20207330f729Sjoerg     // by default.
20217330f729Sjoerg     ObjCMTAction |= FrontendOptions::ObjCMT_Literals |
20227330f729Sjoerg                     FrontendOptions::ObjCMT_Subscripting;
20237330f729Sjoerg   }
20247330f729Sjoerg   CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec));
20257330f729Sjoerg   std::vector<std::string> WhiteList =
20267330f729Sjoerg     getWhiteListFilenames(CI.getFrontendOpts().ObjCMTWhiteListPath);
20277330f729Sjoerg   return std::make_unique<ObjCMigrateASTConsumer>(
20287330f729Sjoerg       CI.getFrontendOpts().OutputFile, ObjCMTAction, Remapper,
20297330f729Sjoerg       CI.getFileManager(), PPRec, CI.getPreprocessor(),
20307330f729Sjoerg       /*isOutputFile=*/true, WhiteList);
20317330f729Sjoerg }
20327330f729Sjoerg 
20337330f729Sjoerg namespace {
20347330f729Sjoerg struct EditEntry {
2035*e038c9c4Sjoerg   Optional<FileEntryRef> File;
2036*e038c9c4Sjoerg   unsigned Offset = 0;
2037*e038c9c4Sjoerg   unsigned RemoveLen = 0;
20387330f729Sjoerg   std::string Text;
20397330f729Sjoerg };
20407330f729Sjoerg } // end anonymous namespace
20417330f729Sjoerg 
20427330f729Sjoerg namespace llvm {
20437330f729Sjoerg template<> struct DenseMapInfo<EditEntry> {
getEmptyKeyllvm::DenseMapInfo20447330f729Sjoerg   static inline EditEntry getEmptyKey() {
20457330f729Sjoerg     EditEntry Entry;
20467330f729Sjoerg     Entry.Offset = unsigned(-1);
20477330f729Sjoerg     return Entry;
20487330f729Sjoerg   }
getTombstoneKeyllvm::DenseMapInfo20497330f729Sjoerg   static inline EditEntry getTombstoneKey() {
20507330f729Sjoerg     EditEntry Entry;
20517330f729Sjoerg     Entry.Offset = unsigned(-2);
20527330f729Sjoerg     return Entry;
20537330f729Sjoerg   }
getHashValuellvm::DenseMapInfo20547330f729Sjoerg   static unsigned getHashValue(const EditEntry& Val) {
2055*e038c9c4Sjoerg     return (unsigned)llvm::hash_combine(Val.File, Val.Offset, Val.RemoveLen,
2056*e038c9c4Sjoerg                                         Val.Text);
20577330f729Sjoerg   }
isEqualllvm::DenseMapInfo20587330f729Sjoerg   static bool isEqual(const EditEntry &LHS, const EditEntry &RHS) {
20597330f729Sjoerg     return LHS.File == RHS.File &&
20607330f729Sjoerg         LHS.Offset == RHS.Offset &&
20617330f729Sjoerg         LHS.RemoveLen == RHS.RemoveLen &&
20627330f729Sjoerg         LHS.Text == RHS.Text;
20637330f729Sjoerg   }
20647330f729Sjoerg };
20657330f729Sjoerg } // end namespace llvm
20667330f729Sjoerg 
20677330f729Sjoerg namespace {
20687330f729Sjoerg class RemapFileParser {
20697330f729Sjoerg   FileManager &FileMgr;
20707330f729Sjoerg 
20717330f729Sjoerg public:
RemapFileParser(FileManager & FileMgr)20727330f729Sjoerg   RemapFileParser(FileManager &FileMgr) : FileMgr(FileMgr) { }
20737330f729Sjoerg 
parse(StringRef File,SmallVectorImpl<EditEntry> & Entries)20747330f729Sjoerg   bool parse(StringRef File, SmallVectorImpl<EditEntry> &Entries) {
20757330f729Sjoerg     using namespace llvm::yaml;
20767330f729Sjoerg 
20777330f729Sjoerg     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
20787330f729Sjoerg         llvm::MemoryBuffer::getFile(File);
20797330f729Sjoerg     if (!FileBufOrErr)
20807330f729Sjoerg       return true;
20817330f729Sjoerg 
20827330f729Sjoerg     llvm::SourceMgr SM;
20837330f729Sjoerg     Stream YAMLStream(FileBufOrErr.get()->getMemBufferRef(), SM);
20847330f729Sjoerg     document_iterator I = YAMLStream.begin();
20857330f729Sjoerg     if (I == YAMLStream.end())
20867330f729Sjoerg       return true;
20877330f729Sjoerg     Node *Root = I->getRoot();
20887330f729Sjoerg     if (!Root)
20897330f729Sjoerg       return true;
20907330f729Sjoerg 
20917330f729Sjoerg     SequenceNode *SeqNode = dyn_cast<SequenceNode>(Root);
20927330f729Sjoerg     if (!SeqNode)
20937330f729Sjoerg       return true;
20947330f729Sjoerg 
20957330f729Sjoerg     for (SequenceNode::iterator
20967330f729Sjoerg            AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) {
20977330f729Sjoerg       MappingNode *MapNode = dyn_cast<MappingNode>(&*AI);
20987330f729Sjoerg       if (!MapNode)
20997330f729Sjoerg         continue;
21007330f729Sjoerg       parseEdit(MapNode, Entries);
21017330f729Sjoerg     }
21027330f729Sjoerg 
21037330f729Sjoerg     return false;
21047330f729Sjoerg   }
21057330f729Sjoerg 
21067330f729Sjoerg private:
parseEdit(llvm::yaml::MappingNode * Node,SmallVectorImpl<EditEntry> & Entries)21077330f729Sjoerg   void parseEdit(llvm::yaml::MappingNode *Node,
21087330f729Sjoerg                  SmallVectorImpl<EditEntry> &Entries) {
21097330f729Sjoerg     using namespace llvm::yaml;
21107330f729Sjoerg     EditEntry Entry;
21117330f729Sjoerg     bool Ignore = false;
21127330f729Sjoerg 
21137330f729Sjoerg     for (MappingNode::iterator
21147330f729Sjoerg            KVI = Node->begin(), KVE = Node->end(); KVI != KVE; ++KVI) {
21157330f729Sjoerg       ScalarNode *KeyString = dyn_cast<ScalarNode>((*KVI).getKey());
21167330f729Sjoerg       if (!KeyString)
21177330f729Sjoerg         continue;
21187330f729Sjoerg       SmallString<10> KeyStorage;
21197330f729Sjoerg       StringRef Key = KeyString->getValue(KeyStorage);
21207330f729Sjoerg 
21217330f729Sjoerg       ScalarNode *ValueString = dyn_cast<ScalarNode>((*KVI).getValue());
21227330f729Sjoerg       if (!ValueString)
21237330f729Sjoerg         continue;
21247330f729Sjoerg       SmallString<64> ValueStorage;
21257330f729Sjoerg       StringRef Val = ValueString->getValue(ValueStorage);
21267330f729Sjoerg 
21277330f729Sjoerg       if (Key == "file") {
2128*e038c9c4Sjoerg         if (auto File = FileMgr.getOptionalFileRef(Val))
2129*e038c9c4Sjoerg           Entry.File = File;
21307330f729Sjoerg         else
21317330f729Sjoerg           Ignore = true;
21327330f729Sjoerg       } else if (Key == "offset") {
21337330f729Sjoerg         if (Val.getAsInteger(10, Entry.Offset))
21347330f729Sjoerg           Ignore = true;
21357330f729Sjoerg       } else if (Key == "remove") {
21367330f729Sjoerg         if (Val.getAsInteger(10, Entry.RemoveLen))
21377330f729Sjoerg           Ignore = true;
21387330f729Sjoerg       } else if (Key == "text") {
2139*e038c9c4Sjoerg         Entry.Text = std::string(Val);
21407330f729Sjoerg       }
21417330f729Sjoerg     }
21427330f729Sjoerg 
21437330f729Sjoerg     if (!Ignore)
21447330f729Sjoerg       Entries.push_back(Entry);
21457330f729Sjoerg   }
21467330f729Sjoerg };
21477330f729Sjoerg } // end anonymous namespace
21487330f729Sjoerg 
reportDiag(const Twine & Err,DiagnosticsEngine & Diag)21497330f729Sjoerg static bool reportDiag(const Twine &Err, DiagnosticsEngine &Diag) {
21507330f729Sjoerg   Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
21517330f729Sjoerg       << Err.str();
21527330f729Sjoerg   return true;
21537330f729Sjoerg }
21547330f729Sjoerg 
applyEditsToTemp(FileEntryRef FE,ArrayRef<EditEntry> Edits,FileManager & FileMgr,DiagnosticsEngine & Diag)2155*e038c9c4Sjoerg static std::string applyEditsToTemp(FileEntryRef FE,
21567330f729Sjoerg                                     ArrayRef<EditEntry> Edits,
21577330f729Sjoerg                                     FileManager &FileMgr,
21587330f729Sjoerg                                     DiagnosticsEngine &Diag) {
21597330f729Sjoerg   using namespace llvm::sys;
21607330f729Sjoerg 
21617330f729Sjoerg   SourceManager SM(Diag, FileMgr);
21627330f729Sjoerg   FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);
21637330f729Sjoerg   LangOptions LangOpts;
21647330f729Sjoerg   edit::EditedSource Editor(SM, LangOpts);
21657330f729Sjoerg   for (ArrayRef<EditEntry>::iterator
21667330f729Sjoerg         I = Edits.begin(), E = Edits.end(); I != E; ++I) {
21677330f729Sjoerg     const EditEntry &Entry = *I;
21687330f729Sjoerg     assert(Entry.File == FE);
21697330f729Sjoerg     SourceLocation Loc =
21707330f729Sjoerg         SM.getLocForStartOfFile(FID).getLocWithOffset(Entry.Offset);
21717330f729Sjoerg     CharSourceRange Range;
21727330f729Sjoerg     if (Entry.RemoveLen != 0) {
21737330f729Sjoerg       Range = CharSourceRange::getCharRange(Loc,
21747330f729Sjoerg                                          Loc.getLocWithOffset(Entry.RemoveLen));
21757330f729Sjoerg     }
21767330f729Sjoerg 
21777330f729Sjoerg     edit::Commit commit(Editor);
21787330f729Sjoerg     if (Range.isInvalid()) {
21797330f729Sjoerg       commit.insert(Loc, Entry.Text);
21807330f729Sjoerg     } else if (Entry.Text.empty()) {
21817330f729Sjoerg       commit.remove(Range);
21827330f729Sjoerg     } else {
21837330f729Sjoerg       commit.replace(Range, Entry.Text);
21847330f729Sjoerg     }
21857330f729Sjoerg     Editor.commit(commit);
21867330f729Sjoerg   }
21877330f729Sjoerg 
21887330f729Sjoerg   Rewriter rewriter(SM, LangOpts);
21897330f729Sjoerg   RewritesReceiver Rec(rewriter);
21907330f729Sjoerg   Editor.applyRewrites(Rec, /*adjustRemovals=*/false);
21917330f729Sjoerg 
21927330f729Sjoerg   const RewriteBuffer *Buf = rewriter.getRewriteBufferFor(FID);
21937330f729Sjoerg   SmallString<512> NewText;
21947330f729Sjoerg   llvm::raw_svector_ostream OS(NewText);
21957330f729Sjoerg   Buf->write(OS);
21967330f729Sjoerg 
21977330f729Sjoerg   SmallString<64> TempPath;
21987330f729Sjoerg   int FD;
2199*e038c9c4Sjoerg   if (fs::createTemporaryFile(path::filename(FE.getName()),
2200*e038c9c4Sjoerg                               path::extension(FE.getName()).drop_front(), FD,
22017330f729Sjoerg                               TempPath)) {
22027330f729Sjoerg     reportDiag("Could not create file: " + TempPath.str(), Diag);
22037330f729Sjoerg     return std::string();
22047330f729Sjoerg   }
22057330f729Sjoerg 
22067330f729Sjoerg   llvm::raw_fd_ostream TmpOut(FD, /*shouldClose=*/true);
22077330f729Sjoerg   TmpOut.write(NewText.data(), NewText.size());
22087330f729Sjoerg   TmpOut.close();
22097330f729Sjoerg 
2210*e038c9c4Sjoerg   return std::string(TempPath.str());
22117330f729Sjoerg }
22127330f729Sjoerg 
getFileRemappingsFromFileList(std::vector<std::pair<std::string,std::string>> & remap,ArrayRef<StringRef> remapFiles,DiagnosticConsumer * DiagClient)22137330f729Sjoerg bool arcmt::getFileRemappingsFromFileList(
22147330f729Sjoerg                         std::vector<std::pair<std::string,std::string> > &remap,
22157330f729Sjoerg                         ArrayRef<StringRef> remapFiles,
22167330f729Sjoerg                         DiagnosticConsumer *DiagClient) {
22177330f729Sjoerg   bool hasErrorOccurred = false;
22187330f729Sjoerg 
22197330f729Sjoerg   FileSystemOptions FSOpts;
22207330f729Sjoerg   FileManager FileMgr(FSOpts);
22217330f729Sjoerg   RemapFileParser Parser(FileMgr);
22227330f729Sjoerg 
22237330f729Sjoerg   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
22247330f729Sjoerg   IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
22257330f729Sjoerg       new DiagnosticsEngine(DiagID, new DiagnosticOptions,
22267330f729Sjoerg                             DiagClient, /*ShouldOwnClient=*/false));
22277330f729Sjoerg 
2228*e038c9c4Sjoerg   typedef llvm::DenseMap<FileEntryRef, std::vector<EditEntry> >
22297330f729Sjoerg       FileEditEntriesTy;
22307330f729Sjoerg   FileEditEntriesTy FileEditEntries;
22317330f729Sjoerg 
22327330f729Sjoerg   llvm::DenseSet<EditEntry> EntriesSet;
22337330f729Sjoerg 
22347330f729Sjoerg   for (ArrayRef<StringRef>::iterator
22357330f729Sjoerg          I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
22367330f729Sjoerg     SmallVector<EditEntry, 16> Entries;
22377330f729Sjoerg     if (Parser.parse(*I, Entries))
22387330f729Sjoerg       continue;
22397330f729Sjoerg 
22407330f729Sjoerg     for (SmallVectorImpl<EditEntry>::iterator
22417330f729Sjoerg            EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) {
22427330f729Sjoerg       EditEntry &Entry = *EI;
22437330f729Sjoerg       if (!Entry.File)
22447330f729Sjoerg         continue;
22457330f729Sjoerg       std::pair<llvm::DenseSet<EditEntry>::iterator, bool>
22467330f729Sjoerg         Insert = EntriesSet.insert(Entry);
22477330f729Sjoerg       if (!Insert.second)
22487330f729Sjoerg         continue;
22497330f729Sjoerg 
2250*e038c9c4Sjoerg       FileEditEntries[*Entry.File].push_back(Entry);
22517330f729Sjoerg     }
22527330f729Sjoerg   }
22537330f729Sjoerg 
22547330f729Sjoerg   for (FileEditEntriesTy::iterator
22557330f729Sjoerg          I = FileEditEntries.begin(), E = FileEditEntries.end(); I != E; ++I) {
22567330f729Sjoerg     std::string TempFile = applyEditsToTemp(I->first, I->second,
22577330f729Sjoerg                                             FileMgr, *Diags);
22587330f729Sjoerg     if (TempFile.empty()) {
22597330f729Sjoerg       hasErrorOccurred = true;
22607330f729Sjoerg       continue;
22617330f729Sjoerg     }
22627330f729Sjoerg 
2263*e038c9c4Sjoerg     remap.emplace_back(std::string(I->first.getName()), TempFile);
22647330f729Sjoerg   }
22657330f729Sjoerg 
22667330f729Sjoerg   return hasErrorOccurred;
22677330f729Sjoerg }
2268