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