xref: /openbsd-src/gnu/llvm/clang/lib/ARCMigrate/ObjCMT.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick 
9e5dd7070Spatrick #include "Transforms.h"
10e5dd7070Spatrick #include "clang/Analysis/RetainSummaryManager.h"
11e5dd7070Spatrick #include "clang/ARCMigrate/ARCMT.h"
12e5dd7070Spatrick #include "clang/ARCMigrate/ARCMTActions.h"
13e5dd7070Spatrick #include "clang/AST/ASTConsumer.h"
14e5dd7070Spatrick #include "clang/AST/ASTContext.h"
15e5dd7070Spatrick #include "clang/AST/Attr.h"
16e5dd7070Spatrick #include "clang/AST/NSAPI.h"
17e5dd7070Spatrick #include "clang/AST/ParentMap.h"
18e5dd7070Spatrick #include "clang/AST/RecursiveASTVisitor.h"
19e5dd7070Spatrick #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
20e5dd7070Spatrick #include "clang/Basic/FileManager.h"
21e5dd7070Spatrick #include "clang/Edit/Commit.h"
22e5dd7070Spatrick #include "clang/Edit/EditedSource.h"
23e5dd7070Spatrick #include "clang/Edit/EditsReceiver.h"
24e5dd7070Spatrick #include "clang/Edit/Rewriters.h"
25e5dd7070Spatrick #include "clang/Frontend/CompilerInstance.h"
26e5dd7070Spatrick #include "clang/Frontend/MultiplexConsumer.h"
27e5dd7070Spatrick #include "clang/Lex/PPConditionalDirectiveRecord.h"
28e5dd7070Spatrick #include "clang/Lex/Preprocessor.h"
29e5dd7070Spatrick #include "clang/Rewrite/Core/Rewriter.h"
30e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
31e5dd7070Spatrick #include "llvm/ADT/StringSet.h"
32e5dd7070Spatrick #include "llvm/Support/Path.h"
33e5dd7070Spatrick #include "llvm/Support/SourceMgr.h"
34e5dd7070Spatrick #include "llvm/Support/YAMLParser.h"
35e5dd7070Spatrick 
36e5dd7070Spatrick using namespace clang;
37e5dd7070Spatrick using namespace arcmt;
38e5dd7070Spatrick using namespace ento;
39e5dd7070Spatrick 
40e5dd7070Spatrick namespace {
41e5dd7070Spatrick 
42e5dd7070Spatrick class ObjCMigrateASTConsumer : public ASTConsumer {
43e5dd7070Spatrick   enum CF_BRIDGING_KIND {
44e5dd7070Spatrick     CF_BRIDGING_NONE,
45e5dd7070Spatrick     CF_BRIDGING_ENABLE,
46e5dd7070Spatrick     CF_BRIDGING_MAY_INCLUDE
47e5dd7070Spatrick   };
48e5dd7070Spatrick 
49e5dd7070Spatrick   void migrateDecl(Decl *D);
50e5dd7070Spatrick   void migrateObjCContainerDecl(ASTContext &Ctx, ObjCContainerDecl *D);
51e5dd7070Spatrick   void migrateProtocolConformance(ASTContext &Ctx,
52e5dd7070Spatrick                                   const ObjCImplementationDecl *ImpDecl);
53e5dd7070Spatrick   void CacheObjCNSIntegerTypedefed(const TypedefDecl *TypedefDcl);
54e5dd7070Spatrick   bool migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,
55e5dd7070Spatrick                      const TypedefDecl *TypedefDcl);
56e5dd7070Spatrick   void migrateAllMethodInstaceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);
57e5dd7070Spatrick   void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,
58e5dd7070Spatrick                                  ObjCMethodDecl *OM);
59e5dd7070Spatrick   bool migrateProperty(ASTContext &Ctx, ObjCContainerDecl *D, ObjCMethodDecl *OM);
60e5dd7070Spatrick   void migrateNsReturnsInnerPointer(ASTContext &Ctx, ObjCMethodDecl *OM);
61e5dd7070Spatrick   void migratePropertyNsReturnsInnerPointer(ASTContext &Ctx, ObjCPropertyDecl *P);
62e5dd7070Spatrick   void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl,
63e5dd7070Spatrick                             ObjCMethodDecl *OM,
64e5dd7070Spatrick                             ObjCInstanceTypeFamily OIT_Family = OIT_None);
65e5dd7070Spatrick 
66e5dd7070Spatrick   void migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl);
67e5dd7070Spatrick   void AddCFAnnotations(ASTContext &Ctx,
68e5dd7070Spatrick                         const RetainSummary *RS,
69e5dd7070Spatrick                         const FunctionDecl *FuncDecl, bool ResultAnnotated);
70e5dd7070Spatrick   void AddCFAnnotations(ASTContext &Ctx,
71e5dd7070Spatrick                         const RetainSummary *RS,
72e5dd7070Spatrick                         const ObjCMethodDecl *MethodDecl, bool ResultAnnotated);
73e5dd7070Spatrick 
74e5dd7070Spatrick   void AnnotateImplicitBridging(ASTContext &Ctx);
75e5dd7070Spatrick 
76e5dd7070Spatrick   CF_BRIDGING_KIND migrateAddFunctionAnnotation(ASTContext &Ctx,
77e5dd7070Spatrick                                                 const FunctionDecl *FuncDecl);
78e5dd7070Spatrick 
79e5dd7070Spatrick   void migrateARCSafeAnnotation(ASTContext &Ctx, ObjCContainerDecl *CDecl);
80e5dd7070Spatrick 
81e5dd7070Spatrick   void migrateAddMethodAnnotation(ASTContext &Ctx,
82e5dd7070Spatrick                                   const ObjCMethodDecl *MethodDecl);
83e5dd7070Spatrick 
84e5dd7070Spatrick   void inferDesignatedInitializers(ASTContext &Ctx,
85e5dd7070Spatrick                                    const ObjCImplementationDecl *ImplD);
86e5dd7070Spatrick 
87e5dd7070Spatrick   bool InsertFoundation(ASTContext &Ctx, SourceLocation Loc);
88e5dd7070Spatrick 
89e5dd7070Spatrick   std::unique_ptr<RetainSummaryManager> Summaries;
90e5dd7070Spatrick 
91e5dd7070Spatrick public:
92e5dd7070Spatrick   std::string MigrateDir;
93e5dd7070Spatrick   unsigned ASTMigrateActions;
94e5dd7070Spatrick   FileID FileId;
95e5dd7070Spatrick   const TypedefDecl *NSIntegerTypedefed;
96e5dd7070Spatrick   const TypedefDecl *NSUIntegerTypedefed;
97e5dd7070Spatrick   std::unique_ptr<NSAPI> NSAPIObj;
98e5dd7070Spatrick   std::unique_ptr<edit::EditedSource> Editor;
99e5dd7070Spatrick   FileRemapper &Remapper;
100e5dd7070Spatrick   FileManager &FileMgr;
101e5dd7070Spatrick   const PPConditionalDirectiveRecord *PPRec;
102e5dd7070Spatrick   Preprocessor &PP;
103e5dd7070Spatrick   bool IsOutputFile;
104e5dd7070Spatrick   bool FoundationIncluded;
105e5dd7070Spatrick   llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;
106e5dd7070Spatrick   llvm::SmallVector<const Decl *, 8> CFFunctionIBCandidates;
107*12c85518Srobert   llvm::StringSet<> AllowListFilenames;
108e5dd7070Spatrick 
getSummaryManager(ASTContext & Ctx)109e5dd7070Spatrick   RetainSummaryManager &getSummaryManager(ASTContext &Ctx) {
110e5dd7070Spatrick     if (!Summaries)
111e5dd7070Spatrick       Summaries.reset(new RetainSummaryManager(Ctx,
112e5dd7070Spatrick                                                /*TrackNSCFObjects=*/true,
113e5dd7070Spatrick                                                /*trackOSObjects=*/false));
114e5dd7070Spatrick     return *Summaries;
115e5dd7070Spatrick   }
116e5dd7070Spatrick 
ObjCMigrateASTConsumer(StringRef migrateDir,unsigned astMigrateActions,FileRemapper & remapper,FileManager & fileMgr,const PPConditionalDirectiveRecord * PPRec,Preprocessor & PP,bool isOutputFile,ArrayRef<std::string> AllowList)117ec727ea7Spatrick   ObjCMigrateASTConsumer(StringRef migrateDir, unsigned astMigrateActions,
118ec727ea7Spatrick                          FileRemapper &remapper, FileManager &fileMgr,
119e5dd7070Spatrick                          const PPConditionalDirectiveRecord *PPRec,
120ec727ea7Spatrick                          Preprocessor &PP, bool isOutputFile,
121*12c85518Srobert                          ArrayRef<std::string> AllowList)
122ec727ea7Spatrick       : MigrateDir(migrateDir), ASTMigrateActions(astMigrateActions),
123e5dd7070Spatrick         NSIntegerTypedefed(nullptr), NSUIntegerTypedefed(nullptr),
124e5dd7070Spatrick         Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
125ec727ea7Spatrick         IsOutputFile(isOutputFile), FoundationIncluded(false) {
126e5dd7070Spatrick     // FIXME: StringSet should have insert(iter, iter) to use here.
127*12c85518Srobert     for (const std::string &Val : AllowList)
128*12c85518Srobert       AllowListFilenames.insert(Val);
129e5dd7070Spatrick   }
130e5dd7070Spatrick 
131e5dd7070Spatrick protected:
Initialize(ASTContext & Context)132e5dd7070Spatrick   void Initialize(ASTContext &Context) override {
133e5dd7070Spatrick     NSAPIObj.reset(new NSAPI(Context));
134e5dd7070Spatrick     Editor.reset(new edit::EditedSource(Context.getSourceManager(),
135e5dd7070Spatrick                                         Context.getLangOpts(),
136e5dd7070Spatrick                                         PPRec));
137e5dd7070Spatrick   }
138e5dd7070Spatrick 
HandleTopLevelDecl(DeclGroupRef DG)139e5dd7070Spatrick   bool HandleTopLevelDecl(DeclGroupRef DG) override {
140e5dd7070Spatrick     for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
141e5dd7070Spatrick       migrateDecl(*I);
142e5dd7070Spatrick     return true;
143e5dd7070Spatrick   }
HandleInterestingDecl(DeclGroupRef DG)144e5dd7070Spatrick   void HandleInterestingDecl(DeclGroupRef DG) override {
145e5dd7070Spatrick     // Ignore decls from the PCH.
146e5dd7070Spatrick   }
HandleTopLevelDeclInObjCContainer(DeclGroupRef DG)147e5dd7070Spatrick   void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) override {
148e5dd7070Spatrick     ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
149e5dd7070Spatrick   }
150e5dd7070Spatrick 
151e5dd7070Spatrick   void HandleTranslationUnit(ASTContext &Ctx) override;
152e5dd7070Spatrick 
canModifyFile(StringRef Path)153e5dd7070Spatrick   bool canModifyFile(StringRef Path) {
154*12c85518Srobert     if (AllowListFilenames.empty())
155e5dd7070Spatrick       return true;
156*12c85518Srobert     return AllowListFilenames.find(llvm::sys::path::filename(Path)) !=
157*12c85518Srobert            AllowListFilenames.end();
158e5dd7070Spatrick   }
canModifyFile(OptionalFileEntryRef FE)159*12c85518Srobert   bool canModifyFile(OptionalFileEntryRef FE) {
160e5dd7070Spatrick     if (!FE)
161e5dd7070Spatrick       return false;
162e5dd7070Spatrick     return canModifyFile(FE->getName());
163e5dd7070Spatrick   }
canModifyFile(FileID FID)164e5dd7070Spatrick   bool canModifyFile(FileID FID) {
165e5dd7070Spatrick     if (FID.isInvalid())
166e5dd7070Spatrick       return false;
167a9ac8606Spatrick     return canModifyFile(PP.getSourceManager().getFileEntryRefForID(FID));
168e5dd7070Spatrick   }
169e5dd7070Spatrick 
canModify(const Decl * D)170e5dd7070Spatrick   bool canModify(const Decl *D) {
171e5dd7070Spatrick     if (!D)
172e5dd7070Spatrick       return false;
173e5dd7070Spatrick     if (const ObjCCategoryImplDecl *CatImpl = dyn_cast<ObjCCategoryImplDecl>(D))
174e5dd7070Spatrick       return canModify(CatImpl->getCategoryDecl());
175e5dd7070Spatrick     if (const ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(D))
176e5dd7070Spatrick       return canModify(Impl->getClassInterface());
177e5dd7070Spatrick     if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D))
178e5dd7070Spatrick       return canModify(cast<Decl>(MD->getDeclContext()));
179e5dd7070Spatrick 
180e5dd7070Spatrick     FileID FID = PP.getSourceManager().getFileID(D->getLocation());
181e5dd7070Spatrick     return canModifyFile(FID);
182e5dd7070Spatrick   }
183e5dd7070Spatrick };
184e5dd7070Spatrick 
185e5dd7070Spatrick } // end anonymous namespace
186e5dd7070Spatrick 
ObjCMigrateAction(std::unique_ptr<FrontendAction> WrappedAction,StringRef migrateDir,unsigned migrateAction)187e5dd7070Spatrick ObjCMigrateAction::ObjCMigrateAction(
188ec727ea7Spatrick     std::unique_ptr<FrontendAction> WrappedAction, StringRef migrateDir,
189e5dd7070Spatrick     unsigned migrateAction)
190e5dd7070Spatrick     : WrapperFrontendAction(std::move(WrappedAction)), MigrateDir(migrateDir),
191ec727ea7Spatrick       ObjCMigAction(migrateAction), CompInst(nullptr) {
192e5dd7070Spatrick   if (MigrateDir.empty())
193e5dd7070Spatrick     MigrateDir = "."; // user current directory if none is given.
194e5dd7070Spatrick }
195e5dd7070Spatrick 
196e5dd7070Spatrick std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)197e5dd7070Spatrick ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
198e5dd7070Spatrick   PPConditionalDirectiveRecord *
199e5dd7070Spatrick     PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
200e5dd7070Spatrick   CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec));
201e5dd7070Spatrick   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
202e5dd7070Spatrick   Consumers.push_back(WrapperFrontendAction::CreateASTConsumer(CI, InFile));
203e5dd7070Spatrick   Consumers.push_back(std::make_unique<ObjCMigrateASTConsumer>(
204e5dd7070Spatrick       MigrateDir, ObjCMigAction, Remapper, CompInst->getFileManager(), PPRec,
205*12c85518Srobert       CompInst->getPreprocessor(), false, std::nullopt));
206e5dd7070Spatrick   return std::make_unique<MultiplexConsumer>(std::move(Consumers));
207e5dd7070Spatrick }
208e5dd7070Spatrick 
BeginInvocation(CompilerInstance & CI)209e5dd7070Spatrick bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
210e5dd7070Spatrick   Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
211e5dd7070Spatrick                         /*ignoreIfFilesChanged=*/true);
212e5dd7070Spatrick   CompInst = &CI;
213e5dd7070Spatrick   CI.getDiagnostics().setIgnoreAllWarnings(true);
214e5dd7070Spatrick   return true;
215e5dd7070Spatrick }
216e5dd7070Spatrick 
217e5dd7070Spatrick namespace {
218e5dd7070Spatrick   // FIXME. This duplicates one in RewriteObjCFoundationAPI.cpp
subscriptOperatorNeedsParens(const Expr * FullExpr)219e5dd7070Spatrick   bool subscriptOperatorNeedsParens(const Expr *FullExpr) {
220e5dd7070Spatrick     const Expr* Expr = FullExpr->IgnoreImpCasts();
221e5dd7070Spatrick     return !(isa<ArraySubscriptExpr>(Expr) || isa<CallExpr>(Expr) ||
222e5dd7070Spatrick              isa<DeclRefExpr>(Expr) || isa<CXXNamedCastExpr>(Expr) ||
223e5dd7070Spatrick              isa<CXXConstructExpr>(Expr) || isa<CXXThisExpr>(Expr) ||
224e5dd7070Spatrick              isa<CXXTypeidExpr>(Expr) ||
225e5dd7070Spatrick              isa<CXXUnresolvedConstructExpr>(Expr) ||
226e5dd7070Spatrick              isa<ObjCMessageExpr>(Expr) || isa<ObjCPropertyRefExpr>(Expr) ||
227e5dd7070Spatrick              isa<ObjCProtocolExpr>(Expr) || isa<MemberExpr>(Expr) ||
228e5dd7070Spatrick              isa<ObjCIvarRefExpr>(Expr) || isa<ParenExpr>(FullExpr) ||
229e5dd7070Spatrick              isa<ParenListExpr>(Expr) || isa<SizeOfPackExpr>(Expr));
230e5dd7070Spatrick   }
231e5dd7070Spatrick 
232e5dd7070Spatrick   /// - Rewrite message expression for Objective-C setter and getters into
233e5dd7070Spatrick   /// property-dot syntax.
rewriteToPropertyDotSyntax(const ObjCMessageExpr * Msg,Preprocessor & PP,const NSAPI & NS,edit::Commit & commit,const ParentMap * PMap)234e5dd7070Spatrick   bool rewriteToPropertyDotSyntax(const ObjCMessageExpr *Msg,
235e5dd7070Spatrick                                   Preprocessor &PP,
236e5dd7070Spatrick                                   const NSAPI &NS, edit::Commit &commit,
237e5dd7070Spatrick                                   const ParentMap *PMap) {
238e5dd7070Spatrick     if (!Msg || Msg->isImplicit() ||
239e5dd7070Spatrick         (Msg->getReceiverKind() != ObjCMessageExpr::Instance &&
240e5dd7070Spatrick          Msg->getReceiverKind() != ObjCMessageExpr::SuperInstance))
241e5dd7070Spatrick       return false;
242e5dd7070Spatrick     if (const Expr *Receiver = Msg->getInstanceReceiver())
243e5dd7070Spatrick       if (Receiver->getType()->isObjCBuiltinType())
244e5dd7070Spatrick         return false;
245e5dd7070Spatrick 
246e5dd7070Spatrick     const ObjCMethodDecl *Method = Msg->getMethodDecl();
247e5dd7070Spatrick     if (!Method)
248e5dd7070Spatrick       return false;
249e5dd7070Spatrick     if (!Method->isPropertyAccessor())
250e5dd7070Spatrick       return false;
251e5dd7070Spatrick 
252e5dd7070Spatrick     const ObjCPropertyDecl *Prop = Method->findPropertyDecl();
253e5dd7070Spatrick     if (!Prop)
254e5dd7070Spatrick       return false;
255e5dd7070Spatrick 
256e5dd7070Spatrick     SourceRange MsgRange = Msg->getSourceRange();
257e5dd7070Spatrick     bool ReceiverIsSuper =
258e5dd7070Spatrick       (Msg->getReceiverKind() == ObjCMessageExpr::SuperInstance);
259e5dd7070Spatrick     // for 'super' receiver is nullptr.
260e5dd7070Spatrick     const Expr *receiver = Msg->getInstanceReceiver();
261e5dd7070Spatrick     bool NeedsParen =
262e5dd7070Spatrick       ReceiverIsSuper ? false : subscriptOperatorNeedsParens(receiver);
263e5dd7070Spatrick     bool IsGetter = (Msg->getNumArgs() == 0);
264e5dd7070Spatrick     if (IsGetter) {
265e5dd7070Spatrick       // Find space location range between receiver expression and getter method.
266e5dd7070Spatrick       SourceLocation BegLoc =
267e5dd7070Spatrick           ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc();
268e5dd7070Spatrick       BegLoc = PP.getLocForEndOfToken(BegLoc);
269e5dd7070Spatrick       SourceLocation EndLoc = Msg->getSelectorLoc(0);
270e5dd7070Spatrick       SourceRange SpaceRange(BegLoc, EndLoc);
271e5dd7070Spatrick       std::string PropertyDotString;
272e5dd7070Spatrick       // rewrite getter method expression into: receiver.property or
273e5dd7070Spatrick       // (receiver).property
274e5dd7070Spatrick       if (NeedsParen) {
275e5dd7070Spatrick         commit.insertBefore(receiver->getBeginLoc(), "(");
276e5dd7070Spatrick         PropertyDotString = ").";
277e5dd7070Spatrick       }
278e5dd7070Spatrick       else
279e5dd7070Spatrick         PropertyDotString = ".";
280e5dd7070Spatrick       PropertyDotString += Prop->getName();
281e5dd7070Spatrick       commit.replace(SpaceRange, PropertyDotString);
282e5dd7070Spatrick 
283e5dd7070Spatrick       // remove '[' ']'
284e5dd7070Spatrick       commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), "");
285e5dd7070Spatrick       commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), "");
286e5dd7070Spatrick     } else {
287e5dd7070Spatrick       if (NeedsParen)
288e5dd7070Spatrick         commit.insertWrap("(", receiver->getSourceRange(), ")");
289e5dd7070Spatrick       std::string PropertyDotString = ".";
290e5dd7070Spatrick       PropertyDotString += Prop->getName();
291e5dd7070Spatrick       PropertyDotString += " =";
292e5dd7070Spatrick       const Expr*const* Args = Msg->getArgs();
293e5dd7070Spatrick       const Expr *RHS = Args[0];
294e5dd7070Spatrick       if (!RHS)
295e5dd7070Spatrick         return false;
296e5dd7070Spatrick       SourceLocation BegLoc =
297e5dd7070Spatrick           ReceiverIsSuper ? Msg->getSuperLoc() : receiver->getEndLoc();
298e5dd7070Spatrick       BegLoc = PP.getLocForEndOfToken(BegLoc);
299e5dd7070Spatrick       SourceLocation EndLoc = RHS->getBeginLoc();
300e5dd7070Spatrick       EndLoc = EndLoc.getLocWithOffset(-1);
301e5dd7070Spatrick       const char *colon = PP.getSourceManager().getCharacterData(EndLoc);
302e5dd7070Spatrick       // Add a space after '=' if there is no space between RHS and '='
303e5dd7070Spatrick       if (colon && colon[0] == ':')
304e5dd7070Spatrick         PropertyDotString += " ";
305e5dd7070Spatrick       SourceRange Range(BegLoc, EndLoc);
306e5dd7070Spatrick       commit.replace(Range, PropertyDotString);
307e5dd7070Spatrick       // remove '[' ']'
308e5dd7070Spatrick       commit.replace(SourceRange(MsgRange.getBegin(), MsgRange.getBegin()), "");
309e5dd7070Spatrick       commit.replace(SourceRange(MsgRange.getEnd(), MsgRange.getEnd()), "");
310e5dd7070Spatrick     }
311e5dd7070Spatrick     return true;
312e5dd7070Spatrick   }
313e5dd7070Spatrick 
314e5dd7070Spatrick class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
315e5dd7070Spatrick   ObjCMigrateASTConsumer &Consumer;
316e5dd7070Spatrick   ParentMap &PMap;
317e5dd7070Spatrick 
318e5dd7070Spatrick public:
ObjCMigrator(ObjCMigrateASTConsumer & consumer,ParentMap & PMap)319e5dd7070Spatrick   ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
320e5dd7070Spatrick     : Consumer(consumer), PMap(PMap) { }
321e5dd7070Spatrick 
shouldVisitTemplateInstantiations() const322e5dd7070Spatrick   bool shouldVisitTemplateInstantiations() const { return false; }
shouldWalkTypesOfTypeLocs() const323e5dd7070Spatrick   bool shouldWalkTypesOfTypeLocs() const { return false; }
324e5dd7070Spatrick 
VisitObjCMessageExpr(ObjCMessageExpr * E)325e5dd7070Spatrick   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
326e5dd7070Spatrick     if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Literals) {
327e5dd7070Spatrick       edit::Commit commit(*Consumer.Editor);
328e5dd7070Spatrick       edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
329e5dd7070Spatrick       Consumer.Editor->commit(commit);
330e5dd7070Spatrick     }
331e5dd7070Spatrick 
332e5dd7070Spatrick     if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_Subscripting) {
333e5dd7070Spatrick       edit::Commit commit(*Consumer.Editor);
334e5dd7070Spatrick       edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
335e5dd7070Spatrick       Consumer.Editor->commit(commit);
336e5dd7070Spatrick     }
337e5dd7070Spatrick 
338e5dd7070Spatrick     if (Consumer.ASTMigrateActions & FrontendOptions::ObjCMT_PropertyDotSyntax) {
339e5dd7070Spatrick       edit::Commit commit(*Consumer.Editor);
340e5dd7070Spatrick       rewriteToPropertyDotSyntax(E, Consumer.PP, *Consumer.NSAPIObj,
341e5dd7070Spatrick                                  commit, &PMap);
342e5dd7070Spatrick       Consumer.Editor->commit(commit);
343e5dd7070Spatrick     }
344e5dd7070Spatrick 
345e5dd7070Spatrick     return true;
346e5dd7070Spatrick   }
347e5dd7070Spatrick 
TraverseObjCMessageExpr(ObjCMessageExpr * E)348e5dd7070Spatrick   bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
349e5dd7070Spatrick     // Do depth first; we want to rewrite the subexpressions first so that if
350e5dd7070Spatrick     // we have to move expressions we will move them already rewritten.
351e5dd7070Spatrick     for (Stmt *SubStmt : E->children())
352e5dd7070Spatrick       if (!TraverseStmt(SubStmt))
353e5dd7070Spatrick         return false;
354e5dd7070Spatrick 
355e5dd7070Spatrick     return WalkUpFromObjCMessageExpr(E);
356e5dd7070Spatrick   }
357e5dd7070Spatrick };
358e5dd7070Spatrick 
359e5dd7070Spatrick class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
360e5dd7070Spatrick   ObjCMigrateASTConsumer &Consumer;
361e5dd7070Spatrick   std::unique_ptr<ParentMap> PMap;
362e5dd7070Spatrick 
363e5dd7070Spatrick public:
BodyMigrator(ObjCMigrateASTConsumer & consumer)364e5dd7070Spatrick   BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
365e5dd7070Spatrick 
shouldVisitTemplateInstantiations() const366e5dd7070Spatrick   bool shouldVisitTemplateInstantiations() const { return false; }
shouldWalkTypesOfTypeLocs() const367e5dd7070Spatrick   bool shouldWalkTypesOfTypeLocs() const { return false; }
368e5dd7070Spatrick 
TraverseStmt(Stmt * S)369e5dd7070Spatrick   bool TraverseStmt(Stmt *S) {
370e5dd7070Spatrick     PMap.reset(new ParentMap(S));
371e5dd7070Spatrick     ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
372e5dd7070Spatrick     return true;
373e5dd7070Spatrick   }
374e5dd7070Spatrick };
375e5dd7070Spatrick } // end anonymous namespace
376e5dd7070Spatrick 
migrateDecl(Decl * D)377e5dd7070Spatrick void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
378e5dd7070Spatrick   if (!D)
379e5dd7070Spatrick     return;
380e5dd7070Spatrick   if (isa<ObjCMethodDecl>(D))
381e5dd7070Spatrick     return; // Wait for the ObjC container declaration.
382e5dd7070Spatrick 
383e5dd7070Spatrick   BodyMigrator(*this).TraverseDecl(D);
384e5dd7070Spatrick }
385e5dd7070Spatrick 
append_attr(std::string & PropertyString,const char * attr,bool & LParenAdded)386e5dd7070Spatrick static void append_attr(std::string &PropertyString, const char *attr,
387e5dd7070Spatrick                         bool &LParenAdded) {
388e5dd7070Spatrick   if (!LParenAdded) {
389e5dd7070Spatrick     PropertyString += "(";
390e5dd7070Spatrick     LParenAdded = true;
391e5dd7070Spatrick   }
392e5dd7070Spatrick   else
393e5dd7070Spatrick     PropertyString += ", ";
394e5dd7070Spatrick   PropertyString += attr;
395e5dd7070Spatrick }
396e5dd7070Spatrick 
397e5dd7070Spatrick static
MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString,const std::string & TypeString,const char * name)398e5dd7070Spatrick void MigrateBlockOrFunctionPointerTypeVariable(std::string & PropertyString,
399e5dd7070Spatrick                                                const std::string& TypeString,
400e5dd7070Spatrick                                                const char *name) {
401e5dd7070Spatrick   const char *argPtr = TypeString.c_str();
402e5dd7070Spatrick   int paren = 0;
403e5dd7070Spatrick   while (*argPtr) {
404e5dd7070Spatrick     switch (*argPtr) {
405e5dd7070Spatrick       case '(':
406e5dd7070Spatrick         PropertyString += *argPtr;
407e5dd7070Spatrick         paren++;
408e5dd7070Spatrick         break;
409e5dd7070Spatrick       case ')':
410e5dd7070Spatrick         PropertyString += *argPtr;
411e5dd7070Spatrick         paren--;
412e5dd7070Spatrick         break;
413e5dd7070Spatrick       case '^':
414e5dd7070Spatrick       case '*':
415e5dd7070Spatrick         PropertyString += (*argPtr);
416e5dd7070Spatrick         if (paren == 1) {
417e5dd7070Spatrick           PropertyString += name;
418e5dd7070Spatrick           name = "";
419e5dd7070Spatrick         }
420e5dd7070Spatrick         break;
421e5dd7070Spatrick       default:
422e5dd7070Spatrick         PropertyString += *argPtr;
423e5dd7070Spatrick         break;
424e5dd7070Spatrick     }
425e5dd7070Spatrick     argPtr++;
426e5dd7070Spatrick   }
427e5dd7070Spatrick }
428e5dd7070Spatrick 
PropertyMemoryAttribute(ASTContext & Context,QualType ArgType)429e5dd7070Spatrick static const char *PropertyMemoryAttribute(ASTContext &Context, QualType ArgType) {
430e5dd7070Spatrick   Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
431e5dd7070Spatrick   bool RetainableObject = ArgType->isObjCRetainableType();
432e5dd7070Spatrick   if (RetainableObject &&
433e5dd7070Spatrick       (propertyLifetime == Qualifiers::OCL_Strong
434e5dd7070Spatrick        || propertyLifetime == Qualifiers::OCL_None)) {
435e5dd7070Spatrick     if (const ObjCObjectPointerType *ObjPtrTy =
436e5dd7070Spatrick         ArgType->getAs<ObjCObjectPointerType>()) {
437e5dd7070Spatrick       ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
438e5dd7070Spatrick       if (IDecl &&
439e5dd7070Spatrick           IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
440e5dd7070Spatrick         return "copy";
441e5dd7070Spatrick       else
442e5dd7070Spatrick         return "strong";
443e5dd7070Spatrick     }
444e5dd7070Spatrick     else if (ArgType->isBlockPointerType())
445e5dd7070Spatrick       return "copy";
446e5dd7070Spatrick   } else if (propertyLifetime == Qualifiers::OCL_Weak)
447e5dd7070Spatrick     // TODO. More precise determination of 'weak' attribute requires
448e5dd7070Spatrick     // looking into setter's implementation for backing weak ivar.
449e5dd7070Spatrick     return "weak";
450e5dd7070Spatrick   else if (RetainableObject)
451e5dd7070Spatrick     return ArgType->isBlockPointerType() ? "copy" : "strong";
452e5dd7070Spatrick   return nullptr;
453e5dd7070Spatrick }
454e5dd7070Spatrick 
rewriteToObjCProperty(const ObjCMethodDecl * Getter,const ObjCMethodDecl * Setter,const NSAPI & NS,edit::Commit & commit,unsigned LengthOfPrefix,bool Atomic,bool UseNsIosOnlyMacro,bool AvailabilityArgsMatch)455e5dd7070Spatrick static void rewriteToObjCProperty(const ObjCMethodDecl *Getter,
456e5dd7070Spatrick                                   const ObjCMethodDecl *Setter,
457e5dd7070Spatrick                                   const NSAPI &NS, edit::Commit &commit,
458e5dd7070Spatrick                                   unsigned LengthOfPrefix,
459e5dd7070Spatrick                                   bool Atomic, bool UseNsIosOnlyMacro,
460e5dd7070Spatrick                                   bool AvailabilityArgsMatch) {
461e5dd7070Spatrick   ASTContext &Context = NS.getASTContext();
462e5dd7070Spatrick   bool LParenAdded = false;
463e5dd7070Spatrick   std::string PropertyString = "@property ";
464e5dd7070Spatrick   if (UseNsIosOnlyMacro && NS.isMacroDefined("NS_NONATOMIC_IOSONLY")) {
465e5dd7070Spatrick     PropertyString += "(NS_NONATOMIC_IOSONLY";
466e5dd7070Spatrick     LParenAdded = true;
467e5dd7070Spatrick   } else if (!Atomic) {
468e5dd7070Spatrick     PropertyString += "(nonatomic";
469e5dd7070Spatrick     LParenAdded = true;
470e5dd7070Spatrick   }
471e5dd7070Spatrick 
472e5dd7070Spatrick   std::string PropertyNameString = Getter->getNameAsString();
473e5dd7070Spatrick   StringRef PropertyName(PropertyNameString);
474e5dd7070Spatrick   if (LengthOfPrefix > 0) {
475e5dd7070Spatrick     if (!LParenAdded) {
476e5dd7070Spatrick       PropertyString += "(getter=";
477e5dd7070Spatrick       LParenAdded = true;
478e5dd7070Spatrick     }
479e5dd7070Spatrick     else
480e5dd7070Spatrick       PropertyString += ", getter=";
481e5dd7070Spatrick     PropertyString += PropertyNameString;
482e5dd7070Spatrick   }
483e5dd7070Spatrick   // Property with no setter may be suggested as a 'readonly' property.
484e5dd7070Spatrick   if (!Setter)
485e5dd7070Spatrick     append_attr(PropertyString, "readonly", LParenAdded);
486e5dd7070Spatrick 
487e5dd7070Spatrick 
488e5dd7070Spatrick   // Short circuit 'delegate' properties that contain the name "delegate" or
489e5dd7070Spatrick   // "dataSource", or have exact name "target" to have 'assign' attribute.
490*12c85518Srobert   if (PropertyName.equals("target") || PropertyName.contains("delegate") ||
491*12c85518Srobert       PropertyName.contains("dataSource")) {
492e5dd7070Spatrick     QualType QT = Getter->getReturnType();
493e5dd7070Spatrick     if (!QT->isRealType())
494e5dd7070Spatrick       append_attr(PropertyString, "assign", LParenAdded);
495e5dd7070Spatrick   } else if (!Setter) {
496e5dd7070Spatrick     QualType ResType = Context.getCanonicalType(Getter->getReturnType());
497e5dd7070Spatrick     if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ResType))
498e5dd7070Spatrick       append_attr(PropertyString, MemoryManagementAttr, LParenAdded);
499e5dd7070Spatrick   } else {
500e5dd7070Spatrick     const ParmVarDecl *argDecl = *Setter->param_begin();
501e5dd7070Spatrick     QualType ArgType = Context.getCanonicalType(argDecl->getType());
502e5dd7070Spatrick     if (const char *MemoryManagementAttr = PropertyMemoryAttribute(Context, ArgType))
503e5dd7070Spatrick       append_attr(PropertyString, MemoryManagementAttr, LParenAdded);
504e5dd7070Spatrick   }
505e5dd7070Spatrick   if (LParenAdded)
506e5dd7070Spatrick     PropertyString += ')';
507e5dd7070Spatrick   QualType RT = Getter->getReturnType();
508*12c85518Srobert   if (!RT->getAs<TypedefType>()) {
509e5dd7070Spatrick     // strip off any ARC lifetime qualifier.
510e5dd7070Spatrick     QualType CanResultTy = Context.getCanonicalType(RT);
511e5dd7070Spatrick     if (CanResultTy.getQualifiers().hasObjCLifetime()) {
512e5dd7070Spatrick       Qualifiers Qs = CanResultTy.getQualifiers();
513e5dd7070Spatrick       Qs.removeObjCLifetime();
514e5dd7070Spatrick       RT = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
515e5dd7070Spatrick     }
516e5dd7070Spatrick   }
517e5dd7070Spatrick   PropertyString += " ";
518e5dd7070Spatrick   PrintingPolicy SubPolicy(Context.getPrintingPolicy());
519e5dd7070Spatrick   SubPolicy.SuppressStrongLifetime = true;
520e5dd7070Spatrick   SubPolicy.SuppressLifetimeQualifiers = true;
521e5dd7070Spatrick   std::string TypeString = RT.getAsString(SubPolicy);
522e5dd7070Spatrick   if (LengthOfPrefix > 0) {
523e5dd7070Spatrick     // property name must strip off "is" and lower case the first character
524e5dd7070Spatrick     // after that; e.g. isContinuous will become continuous.
525e5dd7070Spatrick     StringRef PropertyNameStringRef(PropertyNameString);
526e5dd7070Spatrick     PropertyNameStringRef = PropertyNameStringRef.drop_front(LengthOfPrefix);
527ec727ea7Spatrick     PropertyNameString = std::string(PropertyNameStringRef);
528e5dd7070Spatrick     bool NoLowering = (isUppercase(PropertyNameString[0]) &&
529e5dd7070Spatrick                        PropertyNameString.size() > 1 &&
530e5dd7070Spatrick                        isUppercase(PropertyNameString[1]));
531e5dd7070Spatrick     if (!NoLowering)
532e5dd7070Spatrick       PropertyNameString[0] = toLowercase(PropertyNameString[0]);
533e5dd7070Spatrick   }
534e5dd7070Spatrick   if (RT->isBlockPointerType() || RT->isFunctionPointerType())
535e5dd7070Spatrick     MigrateBlockOrFunctionPointerTypeVariable(PropertyString,
536e5dd7070Spatrick                                               TypeString,
537e5dd7070Spatrick                                               PropertyNameString.c_str());
538e5dd7070Spatrick   else {
539e5dd7070Spatrick     char LastChar = TypeString[TypeString.size()-1];
540e5dd7070Spatrick     PropertyString += TypeString;
541e5dd7070Spatrick     if (LastChar != '*')
542e5dd7070Spatrick       PropertyString += ' ';
543e5dd7070Spatrick     PropertyString += PropertyNameString;
544e5dd7070Spatrick   }
545e5dd7070Spatrick   SourceLocation StartGetterSelectorLoc = Getter->getSelectorStartLoc();
546e5dd7070Spatrick   Selector GetterSelector = Getter->getSelector();
547e5dd7070Spatrick 
548e5dd7070Spatrick   SourceLocation EndGetterSelectorLoc =
549e5dd7070Spatrick     StartGetterSelectorLoc.getLocWithOffset(GetterSelector.getNameForSlot(0).size());
550e5dd7070Spatrick   commit.replace(CharSourceRange::getCharRange(Getter->getBeginLoc(),
551e5dd7070Spatrick                                                EndGetterSelectorLoc),
552e5dd7070Spatrick                  PropertyString);
553e5dd7070Spatrick   if (Setter && AvailabilityArgsMatch) {
554e5dd7070Spatrick     SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
555e5dd7070Spatrick     // Get location past ';'
556e5dd7070Spatrick     EndLoc = EndLoc.getLocWithOffset(1);
557e5dd7070Spatrick     SourceLocation BeginOfSetterDclLoc = Setter->getBeginLoc();
558e5dd7070Spatrick     // FIXME. This assumes that setter decl; is immediately preceded by eoln.
559e5dd7070Spatrick     // It is trying to remove the setter method decl. line entirely.
560e5dd7070Spatrick     BeginOfSetterDclLoc = BeginOfSetterDclLoc.getLocWithOffset(-1);
561e5dd7070Spatrick     commit.remove(SourceRange(BeginOfSetterDclLoc, EndLoc));
562e5dd7070Spatrick   }
563e5dd7070Spatrick }
564e5dd7070Spatrick 
IsCategoryNameWithDeprecatedSuffix(ObjCContainerDecl * D)565e5dd7070Spatrick static bool IsCategoryNameWithDeprecatedSuffix(ObjCContainerDecl *D) {
566e5dd7070Spatrick   if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(D)) {
567e5dd7070Spatrick     StringRef Name = CatDecl->getName();
568e5dd7070Spatrick     return Name.endswith("Deprecated");
569e5dd7070Spatrick   }
570e5dd7070Spatrick   return false;
571e5dd7070Spatrick }
572e5dd7070Spatrick 
migrateObjCContainerDecl(ASTContext & Ctx,ObjCContainerDecl * D)573e5dd7070Spatrick void ObjCMigrateASTConsumer::migrateObjCContainerDecl(ASTContext &Ctx,
574e5dd7070Spatrick                                                       ObjCContainerDecl *D) {
575e5dd7070Spatrick   if (D->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(D))
576e5dd7070Spatrick     return;
577e5dd7070Spatrick 
578e5dd7070Spatrick   for (auto *Method : D->methods()) {
579e5dd7070Spatrick     if (Method->isDeprecated())
580e5dd7070Spatrick       continue;
581e5dd7070Spatrick     bool PropertyInferred = migrateProperty(Ctx, D, Method);
582e5dd7070Spatrick     // If a property is inferred, do not attempt to attach NS_RETURNS_INNER_POINTER to
583e5dd7070Spatrick     // the getter method as it ends up on the property itself which we don't want
584e5dd7070Spatrick     // to do unless -objcmt-returns-innerpointer-property  option is on.
585e5dd7070Spatrick     if (!PropertyInferred ||
586e5dd7070Spatrick         (ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))
587e5dd7070Spatrick       if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
588e5dd7070Spatrick         migrateNsReturnsInnerPointer(Ctx, Method);
589e5dd7070Spatrick   }
590e5dd7070Spatrick   if (!(ASTMigrateActions & FrontendOptions::ObjCMT_ReturnsInnerPointerProperty))
591e5dd7070Spatrick     return;
592e5dd7070Spatrick 
593e5dd7070Spatrick   for (auto *Prop : D->instance_properties()) {
594e5dd7070Spatrick     if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
595e5dd7070Spatrick         !Prop->isDeprecated())
596e5dd7070Spatrick       migratePropertyNsReturnsInnerPointer(Ctx, Prop);
597e5dd7070Spatrick   }
598e5dd7070Spatrick }
599e5dd7070Spatrick 
600e5dd7070Spatrick static bool
ClassImplementsAllMethodsAndProperties(ASTContext & Ctx,const ObjCImplementationDecl * ImpDecl,const ObjCInterfaceDecl * IDecl,ObjCProtocolDecl * Protocol)601e5dd7070Spatrick ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
602e5dd7070Spatrick                                       const ObjCImplementationDecl *ImpDecl,
603e5dd7070Spatrick                                        const ObjCInterfaceDecl *IDecl,
604e5dd7070Spatrick                                       ObjCProtocolDecl *Protocol) {
605e5dd7070Spatrick   // In auto-synthesis, protocol properties are not synthesized. So,
606e5dd7070Spatrick   // a conforming protocol must have its required properties declared
607e5dd7070Spatrick   // in class interface.
608e5dd7070Spatrick   bool HasAtleastOneRequiredProperty = false;
609e5dd7070Spatrick   if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
610e5dd7070Spatrick     for (const auto *Property : PDecl->instance_properties()) {
611e5dd7070Spatrick       if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
612e5dd7070Spatrick         continue;
613e5dd7070Spatrick       HasAtleastOneRequiredProperty = true;
614e5dd7070Spatrick       DeclContext::lookup_result R = IDecl->lookup(Property->getDeclName());
615a9ac8606Spatrick       if (R.empty()) {
616e5dd7070Spatrick         // Relax the rule and look into class's implementation for a synthesize
617e5dd7070Spatrick         // or dynamic declaration. Class is implementing a property coming from
618e5dd7070Spatrick         // another protocol. This still makes the target protocol as conforming.
619e5dd7070Spatrick         if (!ImpDecl->FindPropertyImplDecl(
620e5dd7070Spatrick                                   Property->getDeclName().getAsIdentifierInfo(),
621e5dd7070Spatrick                                   Property->getQueryKind()))
622e5dd7070Spatrick           return false;
623a9ac8606Spatrick       } else if (auto *ClassProperty = R.find_first<ObjCPropertyDecl>()) {
624a9ac8606Spatrick         if ((ClassProperty->getPropertyAttributes() !=
625a9ac8606Spatrick              Property->getPropertyAttributes()) ||
626e5dd7070Spatrick             !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
627e5dd7070Spatrick           return false;
628a9ac8606Spatrick       } else
629e5dd7070Spatrick         return false;
630e5dd7070Spatrick     }
631e5dd7070Spatrick 
632e5dd7070Spatrick   // At this point, all required properties in this protocol conform to those
633e5dd7070Spatrick   // declared in the class.
634e5dd7070Spatrick   // Check that class implements the required methods of the protocol too.
635e5dd7070Spatrick   bool HasAtleastOneRequiredMethod = false;
636e5dd7070Spatrick   if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
637e5dd7070Spatrick     if (PDecl->meth_begin() == PDecl->meth_end())
638e5dd7070Spatrick       return HasAtleastOneRequiredProperty;
639e5dd7070Spatrick     for (const auto *MD : PDecl->methods()) {
640e5dd7070Spatrick       if (MD->isImplicit())
641e5dd7070Spatrick         continue;
642e5dd7070Spatrick       if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
643e5dd7070Spatrick         continue;
644e5dd7070Spatrick       DeclContext::lookup_result R = ImpDecl->lookup(MD->getDeclName());
645a9ac8606Spatrick       if (R.empty())
646e5dd7070Spatrick         return false;
647e5dd7070Spatrick       bool match = false;
648e5dd7070Spatrick       HasAtleastOneRequiredMethod = true;
649a9ac8606Spatrick       for (NamedDecl *ND : R)
650a9ac8606Spatrick         if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(ND))
651e5dd7070Spatrick           if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
652e5dd7070Spatrick             match = true;
653e5dd7070Spatrick             break;
654e5dd7070Spatrick           }
655e5dd7070Spatrick       if (!match)
656e5dd7070Spatrick         return false;
657e5dd7070Spatrick     }
658e5dd7070Spatrick   }
659e5dd7070Spatrick   return HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod;
660e5dd7070Spatrick }
661e5dd7070Spatrick 
rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl * IDecl,llvm::SmallVectorImpl<ObjCProtocolDecl * > & ConformingProtocols,const NSAPI & NS,edit::Commit & commit)662e5dd7070Spatrick static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
663e5dd7070Spatrick                     llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
664e5dd7070Spatrick                     const NSAPI &NS, edit::Commit &commit) {
665e5dd7070Spatrick   const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
666e5dd7070Spatrick   std::string ClassString;
667e5dd7070Spatrick   SourceLocation EndLoc =
668e5dd7070Spatrick   IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
669e5dd7070Spatrick 
670e5dd7070Spatrick   if (Protocols.empty()) {
671e5dd7070Spatrick     ClassString = '<';
672e5dd7070Spatrick     for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
673e5dd7070Spatrick       ClassString += ConformingProtocols[i]->getNameAsString();
674e5dd7070Spatrick       if (i != (e-1))
675e5dd7070Spatrick         ClassString += ", ";
676e5dd7070Spatrick     }
677e5dd7070Spatrick     ClassString += "> ";
678e5dd7070Spatrick   }
679e5dd7070Spatrick   else {
680e5dd7070Spatrick     ClassString = ", ";
681e5dd7070Spatrick     for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
682e5dd7070Spatrick       ClassString += ConformingProtocols[i]->getNameAsString();
683e5dd7070Spatrick       if (i != (e-1))
684e5dd7070Spatrick         ClassString += ", ";
685e5dd7070Spatrick     }
686e5dd7070Spatrick     ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
687e5dd7070Spatrick     EndLoc = *PL;
688e5dd7070Spatrick   }
689e5dd7070Spatrick 
690e5dd7070Spatrick   commit.insertAfterToken(EndLoc, ClassString);
691e5dd7070Spatrick   return true;
692e5dd7070Spatrick }
693e5dd7070Spatrick 
GetUnsignedName(StringRef NSIntegerName)694e5dd7070Spatrick static StringRef GetUnsignedName(StringRef NSIntegerName) {
695e5dd7070Spatrick   StringRef UnsignedName = llvm::StringSwitch<StringRef>(NSIntegerName)
696e5dd7070Spatrick     .Case("int8_t", "uint8_t")
697e5dd7070Spatrick     .Case("int16_t", "uint16_t")
698e5dd7070Spatrick     .Case("int32_t", "uint32_t")
699e5dd7070Spatrick     .Case("NSInteger", "NSUInteger")
700e5dd7070Spatrick     .Case("int64_t", "uint64_t")
701e5dd7070Spatrick     .Default(NSIntegerName);
702e5dd7070Spatrick   return UnsignedName;
703e5dd7070Spatrick }
704e5dd7070Spatrick 
rewriteToNSEnumDecl(const EnumDecl * EnumDcl,const TypedefDecl * TypedefDcl,const NSAPI & NS,edit::Commit & commit,StringRef NSIntegerName,bool NSOptions)705e5dd7070Spatrick static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
706e5dd7070Spatrick                                 const TypedefDecl *TypedefDcl,
707e5dd7070Spatrick                                 const NSAPI &NS, edit::Commit &commit,
708e5dd7070Spatrick                                 StringRef NSIntegerName,
709e5dd7070Spatrick                                 bool NSOptions) {
710e5dd7070Spatrick   std::string ClassString;
711e5dd7070Spatrick   if (NSOptions) {
712e5dd7070Spatrick     ClassString = "typedef NS_OPTIONS(";
713e5dd7070Spatrick     ClassString += GetUnsignedName(NSIntegerName);
714e5dd7070Spatrick   }
715e5dd7070Spatrick   else {
716e5dd7070Spatrick     ClassString = "typedef NS_ENUM(";
717e5dd7070Spatrick     ClassString += NSIntegerName;
718e5dd7070Spatrick   }
719e5dd7070Spatrick   ClassString += ", ";
720e5dd7070Spatrick 
721e5dd7070Spatrick   ClassString += TypedefDcl->getIdentifier()->getName();
722e5dd7070Spatrick   ClassString += ')';
723e5dd7070Spatrick   SourceRange R(EnumDcl->getBeginLoc(), EnumDcl->getBeginLoc());
724e5dd7070Spatrick   commit.replace(R, ClassString);
725e5dd7070Spatrick   SourceLocation EndOfEnumDclLoc = EnumDcl->getEndLoc();
726e5dd7070Spatrick   EndOfEnumDclLoc = trans::findSemiAfterLocation(EndOfEnumDclLoc,
727e5dd7070Spatrick                                                  NS.getASTContext(), /*IsDecl*/true);
728e5dd7070Spatrick   if (EndOfEnumDclLoc.isValid()) {
729e5dd7070Spatrick     SourceRange EnumDclRange(EnumDcl->getBeginLoc(), EndOfEnumDclLoc);
730e5dd7070Spatrick     commit.insertFromRange(TypedefDcl->getBeginLoc(), EnumDclRange);
731e5dd7070Spatrick   }
732e5dd7070Spatrick   else
733e5dd7070Spatrick     return false;
734e5dd7070Spatrick 
735e5dd7070Spatrick   SourceLocation EndTypedefDclLoc = TypedefDcl->getEndLoc();
736e5dd7070Spatrick   EndTypedefDclLoc = trans::findSemiAfterLocation(EndTypedefDclLoc,
737e5dd7070Spatrick                                                  NS.getASTContext(), /*IsDecl*/true);
738e5dd7070Spatrick   if (EndTypedefDclLoc.isValid()) {
739e5dd7070Spatrick     SourceRange TDRange(TypedefDcl->getBeginLoc(), EndTypedefDclLoc);
740e5dd7070Spatrick     commit.remove(TDRange);
741e5dd7070Spatrick   }
742e5dd7070Spatrick   else
743e5dd7070Spatrick     return false;
744e5dd7070Spatrick 
745e5dd7070Spatrick   EndOfEnumDclLoc =
746e5dd7070Spatrick       trans::findLocationAfterSemi(EnumDcl->getEndLoc(), NS.getASTContext(),
747e5dd7070Spatrick                                    /*IsDecl*/ true);
748e5dd7070Spatrick   if (EndOfEnumDclLoc.isValid()) {
749e5dd7070Spatrick     SourceLocation BeginOfEnumDclLoc = EnumDcl->getBeginLoc();
750e5dd7070Spatrick     // FIXME. This assumes that enum decl; is immediately preceded by eoln.
751e5dd7070Spatrick     // It is trying to remove the enum decl. lines entirely.
752e5dd7070Spatrick     BeginOfEnumDclLoc = BeginOfEnumDclLoc.getLocWithOffset(-1);
753e5dd7070Spatrick     commit.remove(SourceRange(BeginOfEnumDclLoc, EndOfEnumDclLoc));
754e5dd7070Spatrick     return true;
755e5dd7070Spatrick   }
756e5dd7070Spatrick   return false;
757e5dd7070Spatrick }
758e5dd7070Spatrick 
rewriteToNSMacroDecl(ASTContext & Ctx,const EnumDecl * EnumDcl,const TypedefDecl * TypedefDcl,const NSAPI & NS,edit::Commit & commit,bool IsNSIntegerType)759e5dd7070Spatrick static void rewriteToNSMacroDecl(ASTContext &Ctx,
760e5dd7070Spatrick                                  const EnumDecl *EnumDcl,
761e5dd7070Spatrick                                 const TypedefDecl *TypedefDcl,
762e5dd7070Spatrick                                 const NSAPI &NS, edit::Commit &commit,
763e5dd7070Spatrick                                  bool IsNSIntegerType) {
764e5dd7070Spatrick   QualType DesignatedEnumType = EnumDcl->getIntegerType();
765e5dd7070Spatrick   assert(!DesignatedEnumType.isNull()
766e5dd7070Spatrick          && "rewriteToNSMacroDecl - underlying enum type is null");
767e5dd7070Spatrick 
768e5dd7070Spatrick   PrintingPolicy Policy(Ctx.getPrintingPolicy());
769e5dd7070Spatrick   std::string TypeString = DesignatedEnumType.getAsString(Policy);
770e5dd7070Spatrick   std::string ClassString = IsNSIntegerType ? "NS_ENUM(" : "NS_OPTIONS(";
771e5dd7070Spatrick   ClassString += TypeString;
772e5dd7070Spatrick   ClassString += ", ";
773e5dd7070Spatrick 
774e5dd7070Spatrick   ClassString += TypedefDcl->getIdentifier()->getName();
775e5dd7070Spatrick   ClassString += ") ";
776e5dd7070Spatrick   SourceLocation EndLoc = EnumDcl->getBraceRange().getBegin();
777e5dd7070Spatrick   if (EndLoc.isInvalid())
778e5dd7070Spatrick     return;
779e5dd7070Spatrick   CharSourceRange R =
780e5dd7070Spatrick       CharSourceRange::getCharRange(EnumDcl->getBeginLoc(), EndLoc);
781e5dd7070Spatrick   commit.replace(R, ClassString);
782e5dd7070Spatrick   // This is to remove spaces between '}' and typedef name.
783e5dd7070Spatrick   SourceLocation StartTypedefLoc = EnumDcl->getEndLoc();
784e5dd7070Spatrick   StartTypedefLoc = StartTypedefLoc.getLocWithOffset(+1);
785e5dd7070Spatrick   SourceLocation EndTypedefLoc = TypedefDcl->getEndLoc();
786e5dd7070Spatrick 
787e5dd7070Spatrick   commit.remove(SourceRange(StartTypedefLoc, EndTypedefLoc));
788e5dd7070Spatrick }
789e5dd7070Spatrick 
UseNSOptionsMacro(Preprocessor & PP,ASTContext & Ctx,const EnumDecl * EnumDcl)790e5dd7070Spatrick static bool UseNSOptionsMacro(Preprocessor &PP, ASTContext &Ctx,
791e5dd7070Spatrick                               const EnumDecl *EnumDcl) {
792e5dd7070Spatrick   bool PowerOfTwo = true;
793e5dd7070Spatrick   bool AllHexdecimalEnumerator = true;
794e5dd7070Spatrick   uint64_t MaxPowerOfTwoVal = 0;
795*12c85518Srobert   for (auto *Enumerator : EnumDcl->enumerators()) {
796e5dd7070Spatrick     const Expr *InitExpr = Enumerator->getInitExpr();
797e5dd7070Spatrick     if (!InitExpr) {
798e5dd7070Spatrick       PowerOfTwo = false;
799e5dd7070Spatrick       AllHexdecimalEnumerator = false;
800e5dd7070Spatrick       continue;
801e5dd7070Spatrick     }
802e5dd7070Spatrick     InitExpr = InitExpr->IgnoreParenCasts();
803e5dd7070Spatrick     if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
804e5dd7070Spatrick       if (BO->isShiftOp() || BO->isBitwiseOp())
805e5dd7070Spatrick         return true;
806e5dd7070Spatrick 
807e5dd7070Spatrick     uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
808e5dd7070Spatrick     if (PowerOfTwo && EnumVal) {
809e5dd7070Spatrick       if (!llvm::isPowerOf2_64(EnumVal))
810e5dd7070Spatrick         PowerOfTwo = false;
811e5dd7070Spatrick       else if (EnumVal > MaxPowerOfTwoVal)
812e5dd7070Spatrick         MaxPowerOfTwoVal = EnumVal;
813e5dd7070Spatrick     }
814e5dd7070Spatrick     if (AllHexdecimalEnumerator && EnumVal) {
815e5dd7070Spatrick       bool FoundHexdecimalEnumerator = false;
816e5dd7070Spatrick       SourceLocation EndLoc = Enumerator->getEndLoc();
817e5dd7070Spatrick       Token Tok;
818e5dd7070Spatrick       if (!PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true))
819e5dd7070Spatrick         if (Tok.isLiteral() && Tok.getLength() > 2) {
820e5dd7070Spatrick           if (const char *StringLit = Tok.getLiteralData())
821e5dd7070Spatrick             FoundHexdecimalEnumerator =
822e5dd7070Spatrick               (StringLit[0] == '0' && (toLowercase(StringLit[1]) == 'x'));
823e5dd7070Spatrick         }
824e5dd7070Spatrick       if (!FoundHexdecimalEnumerator)
825e5dd7070Spatrick         AllHexdecimalEnumerator = false;
826e5dd7070Spatrick     }
827e5dd7070Spatrick   }
828e5dd7070Spatrick   return AllHexdecimalEnumerator || (PowerOfTwo && (MaxPowerOfTwoVal > 2));
829e5dd7070Spatrick }
830e5dd7070Spatrick 
migrateProtocolConformance(ASTContext & Ctx,const ObjCImplementationDecl * ImpDecl)831e5dd7070Spatrick void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
832e5dd7070Spatrick                                             const ObjCImplementationDecl *ImpDecl) {
833e5dd7070Spatrick   const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
834e5dd7070Spatrick   if (!IDecl || ObjCProtocolDecls.empty() || IDecl->isDeprecated())
835e5dd7070Spatrick     return;
836e5dd7070Spatrick   // Find all implicit conforming protocols for this class
837e5dd7070Spatrick   // and make them explicit.
838e5dd7070Spatrick   llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
839e5dd7070Spatrick   Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
840e5dd7070Spatrick   llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
841e5dd7070Spatrick 
842e5dd7070Spatrick   for (ObjCProtocolDecl *ProtDecl : ObjCProtocolDecls)
843e5dd7070Spatrick     if (!ExplicitProtocols.count(ProtDecl))
844e5dd7070Spatrick       PotentialImplicitProtocols.push_back(ProtDecl);
845e5dd7070Spatrick 
846e5dd7070Spatrick   if (PotentialImplicitProtocols.empty())
847e5dd7070Spatrick     return;
848e5dd7070Spatrick 
849e5dd7070Spatrick   // go through list of non-optional methods and properties in each protocol
850e5dd7070Spatrick   // in the PotentialImplicitProtocols list. If class implements every one of the
851e5dd7070Spatrick   // methods and properties, then this class conforms to this protocol.
852e5dd7070Spatrick   llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
853e5dd7070Spatrick   for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
854e5dd7070Spatrick     if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
855e5dd7070Spatrick                                               PotentialImplicitProtocols[i]))
856e5dd7070Spatrick       ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
857e5dd7070Spatrick 
858e5dd7070Spatrick   if (ConformingProtocols.empty())
859e5dd7070Spatrick     return;
860e5dd7070Spatrick 
861e5dd7070Spatrick   // Further reduce number of conforming protocols. If protocol P1 is in the list
862e5dd7070Spatrick   // protocol P2 (P2<P1>), No need to include P1.
863e5dd7070Spatrick   llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
864e5dd7070Spatrick   for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
865e5dd7070Spatrick     bool DropIt = false;
866e5dd7070Spatrick     ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
867e5dd7070Spatrick     for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
868e5dd7070Spatrick       ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
869e5dd7070Spatrick       if (PDecl == TargetPDecl)
870e5dd7070Spatrick         continue;
871e5dd7070Spatrick       if (PDecl->lookupProtocolNamed(
872e5dd7070Spatrick             TargetPDecl->getDeclName().getAsIdentifierInfo())) {
873e5dd7070Spatrick         DropIt = true;
874e5dd7070Spatrick         break;
875e5dd7070Spatrick       }
876e5dd7070Spatrick     }
877e5dd7070Spatrick     if (!DropIt)
878e5dd7070Spatrick       MinimalConformingProtocols.push_back(TargetPDecl);
879e5dd7070Spatrick   }
880e5dd7070Spatrick   if (MinimalConformingProtocols.empty())
881e5dd7070Spatrick     return;
882e5dd7070Spatrick   edit::Commit commit(*Editor);
883e5dd7070Spatrick   rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
884e5dd7070Spatrick                              *NSAPIObj, commit);
885e5dd7070Spatrick   Editor->commit(commit);
886e5dd7070Spatrick }
887e5dd7070Spatrick 
CacheObjCNSIntegerTypedefed(const TypedefDecl * TypedefDcl)888e5dd7070Spatrick void ObjCMigrateASTConsumer::CacheObjCNSIntegerTypedefed(
889e5dd7070Spatrick                                           const TypedefDecl *TypedefDcl) {
890e5dd7070Spatrick 
891e5dd7070Spatrick   QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
892e5dd7070Spatrick   if (NSAPIObj->isObjCNSIntegerType(qt))
893e5dd7070Spatrick     NSIntegerTypedefed = TypedefDcl;
894e5dd7070Spatrick   else if (NSAPIObj->isObjCNSUIntegerType(qt))
895e5dd7070Spatrick     NSUIntegerTypedefed = TypedefDcl;
896e5dd7070Spatrick }
897e5dd7070Spatrick 
migrateNSEnumDecl(ASTContext & Ctx,const EnumDecl * EnumDcl,const TypedefDecl * TypedefDcl)898e5dd7070Spatrick bool ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
899e5dd7070Spatrick                                            const EnumDecl *EnumDcl,
900e5dd7070Spatrick                                            const TypedefDecl *TypedefDcl) {
901e5dd7070Spatrick   if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
902e5dd7070Spatrick       EnumDcl->isDeprecated())
903e5dd7070Spatrick     return false;
904e5dd7070Spatrick   if (!TypedefDcl) {
905e5dd7070Spatrick     if (NSIntegerTypedefed) {
906e5dd7070Spatrick       TypedefDcl = NSIntegerTypedefed;
907e5dd7070Spatrick       NSIntegerTypedefed = nullptr;
908e5dd7070Spatrick     }
909e5dd7070Spatrick     else if (NSUIntegerTypedefed) {
910e5dd7070Spatrick       TypedefDcl = NSUIntegerTypedefed;
911e5dd7070Spatrick       NSUIntegerTypedefed = nullptr;
912e5dd7070Spatrick     }
913e5dd7070Spatrick     else
914e5dd7070Spatrick       return false;
915e5dd7070Spatrick     FileID FileIdOfTypedefDcl =
916e5dd7070Spatrick       PP.getSourceManager().getFileID(TypedefDcl->getLocation());
917e5dd7070Spatrick     FileID FileIdOfEnumDcl =
918e5dd7070Spatrick       PP.getSourceManager().getFileID(EnumDcl->getLocation());
919e5dd7070Spatrick     if (FileIdOfTypedefDcl != FileIdOfEnumDcl)
920e5dd7070Spatrick       return false;
921e5dd7070Spatrick   }
922e5dd7070Spatrick   if (TypedefDcl->isDeprecated())
923e5dd7070Spatrick     return false;
924e5dd7070Spatrick 
925e5dd7070Spatrick   QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
926e5dd7070Spatrick   StringRef NSIntegerName = NSAPIObj->GetNSIntegralKind(qt);
927e5dd7070Spatrick 
928e5dd7070Spatrick   if (NSIntegerName.empty()) {
929e5dd7070Spatrick     // Also check for typedef enum {...} TD;
930e5dd7070Spatrick     if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
931e5dd7070Spatrick       if (EnumTy->getDecl() == EnumDcl) {
932e5dd7070Spatrick         bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);
933e5dd7070Spatrick         if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc()))
934e5dd7070Spatrick           return false;
935e5dd7070Spatrick         edit::Commit commit(*Editor);
936e5dd7070Spatrick         rewriteToNSMacroDecl(Ctx, EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
937e5dd7070Spatrick         Editor->commit(commit);
938e5dd7070Spatrick         return true;
939e5dd7070Spatrick       }
940e5dd7070Spatrick     }
941e5dd7070Spatrick     return false;
942e5dd7070Spatrick   }
943e5dd7070Spatrick 
944e5dd7070Spatrick   // We may still use NS_OPTIONS based on what we find in the enumertor list.
945e5dd7070Spatrick   bool NSOptions = UseNSOptionsMacro(PP, Ctx, EnumDcl);
946e5dd7070Spatrick   if (!InsertFoundation(Ctx, TypedefDcl->getBeginLoc()))
947e5dd7070Spatrick     return false;
948e5dd7070Spatrick   edit::Commit commit(*Editor);
949e5dd7070Spatrick   bool Res = rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj,
950e5dd7070Spatrick                                  commit, NSIntegerName, NSOptions);
951e5dd7070Spatrick   Editor->commit(commit);
952e5dd7070Spatrick   return Res;
953e5dd7070Spatrick }
954e5dd7070Spatrick 
ReplaceWithInstancetype(ASTContext & Ctx,const ObjCMigrateASTConsumer & ASTC,ObjCMethodDecl * OM)955e5dd7070Spatrick static void ReplaceWithInstancetype(ASTContext &Ctx,
956e5dd7070Spatrick                                     const ObjCMigrateASTConsumer &ASTC,
957e5dd7070Spatrick                                     ObjCMethodDecl *OM) {
958e5dd7070Spatrick   if (OM->getReturnType() == Ctx.getObjCInstanceType())
959e5dd7070Spatrick     return; // already has instancetype.
960e5dd7070Spatrick 
961e5dd7070Spatrick   SourceRange R;
962e5dd7070Spatrick   std::string ClassString;
963e5dd7070Spatrick   if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) {
964e5dd7070Spatrick     TypeLoc TL = TSInfo->getTypeLoc();
965e5dd7070Spatrick     R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
966e5dd7070Spatrick     ClassString = "instancetype";
967e5dd7070Spatrick   }
968e5dd7070Spatrick   else {
969e5dd7070Spatrick     R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc());
970e5dd7070Spatrick     ClassString = OM->isInstanceMethod() ? '-' : '+';
971e5dd7070Spatrick     ClassString += " (instancetype)";
972e5dd7070Spatrick   }
973e5dd7070Spatrick   edit::Commit commit(*ASTC.Editor);
974e5dd7070Spatrick   commit.replace(R, ClassString);
975e5dd7070Spatrick   ASTC.Editor->commit(commit);
976e5dd7070Spatrick }
977e5dd7070Spatrick 
ReplaceWithClasstype(const ObjCMigrateASTConsumer & ASTC,ObjCMethodDecl * OM)978e5dd7070Spatrick static void ReplaceWithClasstype(const ObjCMigrateASTConsumer &ASTC,
979e5dd7070Spatrick                                     ObjCMethodDecl *OM) {
980e5dd7070Spatrick   ObjCInterfaceDecl *IDecl = OM->getClassInterface();
981e5dd7070Spatrick   SourceRange R;
982e5dd7070Spatrick   std::string ClassString;
983e5dd7070Spatrick   if (TypeSourceInfo *TSInfo = OM->getReturnTypeSourceInfo()) {
984e5dd7070Spatrick     TypeLoc TL = TSInfo->getTypeLoc();
985e5dd7070Spatrick     R = SourceRange(TL.getBeginLoc(), TL.getEndLoc()); {
986ec727ea7Spatrick       ClassString = std::string(IDecl->getName());
987e5dd7070Spatrick       ClassString += "*";
988e5dd7070Spatrick     }
989e5dd7070Spatrick   }
990e5dd7070Spatrick   else {
991e5dd7070Spatrick     R = SourceRange(OM->getBeginLoc(), OM->getBeginLoc());
992e5dd7070Spatrick     ClassString = "+ (";
993e5dd7070Spatrick     ClassString += IDecl->getName(); ClassString += "*)";
994e5dd7070Spatrick   }
995e5dd7070Spatrick   edit::Commit commit(*ASTC.Editor);
996e5dd7070Spatrick   commit.replace(R, ClassString);
997e5dd7070Spatrick   ASTC.Editor->commit(commit);
998e5dd7070Spatrick }
999e5dd7070Spatrick 
migrateMethodInstanceType(ASTContext & Ctx,ObjCContainerDecl * CDecl,ObjCMethodDecl * OM)1000e5dd7070Spatrick void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
1001e5dd7070Spatrick                                                        ObjCContainerDecl *CDecl,
1002e5dd7070Spatrick                                                        ObjCMethodDecl *OM) {
1003e5dd7070Spatrick   ObjCInstanceTypeFamily OIT_Family =
1004e5dd7070Spatrick     Selector::getInstTypeMethodFamily(OM->getSelector());
1005e5dd7070Spatrick 
1006e5dd7070Spatrick   std::string ClassName;
1007e5dd7070Spatrick   switch (OIT_Family) {
1008e5dd7070Spatrick     case OIT_None:
1009e5dd7070Spatrick       migrateFactoryMethod(Ctx, CDecl, OM);
1010e5dd7070Spatrick       return;
1011e5dd7070Spatrick     case OIT_Array:
1012e5dd7070Spatrick       ClassName = "NSArray";
1013e5dd7070Spatrick       break;
1014e5dd7070Spatrick     case OIT_Dictionary:
1015e5dd7070Spatrick       ClassName = "NSDictionary";
1016e5dd7070Spatrick       break;
1017e5dd7070Spatrick     case OIT_Singleton:
1018e5dd7070Spatrick       migrateFactoryMethod(Ctx, CDecl, OM, OIT_Singleton);
1019e5dd7070Spatrick       return;
1020e5dd7070Spatrick     case OIT_Init:
1021e5dd7070Spatrick       if (OM->getReturnType()->isObjCIdType())
1022e5dd7070Spatrick         ReplaceWithInstancetype(Ctx, *this, OM);
1023e5dd7070Spatrick       return;
1024e5dd7070Spatrick     case OIT_ReturnsSelf:
1025e5dd7070Spatrick       migrateFactoryMethod(Ctx, CDecl, OM, OIT_ReturnsSelf);
1026e5dd7070Spatrick       return;
1027e5dd7070Spatrick   }
1028e5dd7070Spatrick   if (!OM->getReturnType()->isObjCIdType())
1029e5dd7070Spatrick     return;
1030e5dd7070Spatrick 
1031e5dd7070Spatrick   ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
1032e5dd7070Spatrick   if (!IDecl) {
1033e5dd7070Spatrick     if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
1034e5dd7070Spatrick       IDecl = CatDecl->getClassInterface();
1035e5dd7070Spatrick     else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
1036e5dd7070Spatrick       IDecl = ImpDecl->getClassInterface();
1037e5dd7070Spatrick   }
1038e5dd7070Spatrick   if (!IDecl ||
1039e5dd7070Spatrick       !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {
1040e5dd7070Spatrick     migrateFactoryMethod(Ctx, CDecl, OM);
1041e5dd7070Spatrick     return;
1042e5dd7070Spatrick   }
1043e5dd7070Spatrick   ReplaceWithInstancetype(Ctx, *this, OM);
1044e5dd7070Spatrick }
1045e5dd7070Spatrick 
TypeIsInnerPointer(QualType T)1046e5dd7070Spatrick static bool TypeIsInnerPointer(QualType T) {
1047e5dd7070Spatrick   if (!T->isAnyPointerType())
1048e5dd7070Spatrick     return false;
1049e5dd7070Spatrick   if (T->isObjCObjectPointerType() || T->isObjCBuiltinType() ||
1050e5dd7070Spatrick       T->isBlockPointerType() || T->isFunctionPointerType() ||
1051e5dd7070Spatrick       ento::coreFoundation::isCFObjectRef(T))
1052e5dd7070Spatrick     return false;
1053e5dd7070Spatrick   // Also, typedef-of-pointer-to-incomplete-struct is something that we assume
1054e5dd7070Spatrick   // is not an innter pointer type.
1055e5dd7070Spatrick   QualType OrigT = T;
1056*12c85518Srobert   while (const auto *TD = T->getAs<TypedefType>())
1057e5dd7070Spatrick     T = TD->getDecl()->getUnderlyingType();
1058e5dd7070Spatrick   if (OrigT == T || !T->isPointerType())
1059e5dd7070Spatrick     return true;
1060e5dd7070Spatrick   const PointerType* PT = T->getAs<PointerType>();
1061e5dd7070Spatrick   QualType UPointeeT = PT->getPointeeType().getUnqualifiedType();
1062e5dd7070Spatrick   if (UPointeeT->isRecordType()) {
1063e5dd7070Spatrick     const RecordType *RecordTy = UPointeeT->getAs<RecordType>();
1064e5dd7070Spatrick     if (!RecordTy->getDecl()->isCompleteDefinition())
1065e5dd7070Spatrick       return false;
1066e5dd7070Spatrick   }
1067e5dd7070Spatrick   return true;
1068e5dd7070Spatrick }
1069e5dd7070Spatrick 
1070e5dd7070Spatrick /// Check whether the two versions match.
versionsMatch(const VersionTuple & X,const VersionTuple & Y)1071e5dd7070Spatrick static bool versionsMatch(const VersionTuple &X, const VersionTuple &Y) {
1072e5dd7070Spatrick   return (X == Y);
1073e5dd7070Spatrick }
1074e5dd7070Spatrick 
1075e5dd7070Spatrick /// AvailabilityAttrsMatch - This routine checks that if comparing two
1076e5dd7070Spatrick /// availability attributes, all their components match. It returns
1077e5dd7070Spatrick /// true, if not dealing with availability or when all components of
1078e5dd7070Spatrick /// availability attributes match. This routine is only called when
1079e5dd7070Spatrick /// the attributes are of the same kind.
AvailabilityAttrsMatch(Attr * At1,Attr * At2)1080e5dd7070Spatrick static bool AvailabilityAttrsMatch(Attr *At1, Attr *At2) {
1081e5dd7070Spatrick   const AvailabilityAttr *AA1 = dyn_cast<AvailabilityAttr>(At1);
1082e5dd7070Spatrick   if (!AA1)
1083e5dd7070Spatrick     return true;
1084e5dd7070Spatrick   const AvailabilityAttr *AA2 = cast<AvailabilityAttr>(At2);
1085e5dd7070Spatrick 
1086e5dd7070Spatrick   VersionTuple Introduced1 = AA1->getIntroduced();
1087e5dd7070Spatrick   VersionTuple Deprecated1 = AA1->getDeprecated();
1088e5dd7070Spatrick   VersionTuple Obsoleted1 = AA1->getObsoleted();
1089e5dd7070Spatrick   bool IsUnavailable1 = AA1->getUnavailable();
1090e5dd7070Spatrick   VersionTuple Introduced2 = AA2->getIntroduced();
1091e5dd7070Spatrick   VersionTuple Deprecated2 = AA2->getDeprecated();
1092e5dd7070Spatrick   VersionTuple Obsoleted2 = AA2->getObsoleted();
1093e5dd7070Spatrick   bool IsUnavailable2 = AA2->getUnavailable();
1094e5dd7070Spatrick   return (versionsMatch(Introduced1, Introduced2) &&
1095e5dd7070Spatrick           versionsMatch(Deprecated1, Deprecated2) &&
1096e5dd7070Spatrick           versionsMatch(Obsoleted1, Obsoleted2) &&
1097e5dd7070Spatrick           IsUnavailable1 == IsUnavailable2);
1098e5dd7070Spatrick }
1099e5dd7070Spatrick 
MatchTwoAttributeLists(const AttrVec & Attrs1,const AttrVec & Attrs2,bool & AvailabilityArgsMatch)1100e5dd7070Spatrick static bool MatchTwoAttributeLists(const AttrVec &Attrs1, const AttrVec &Attrs2,
1101e5dd7070Spatrick                                    bool &AvailabilityArgsMatch) {
1102e5dd7070Spatrick   // This list is very small, so this need not be optimized.
1103e5dd7070Spatrick   for (unsigned i = 0, e = Attrs1.size(); i != e; i++) {
1104e5dd7070Spatrick     bool match = false;
1105e5dd7070Spatrick     for (unsigned j = 0, f = Attrs2.size(); j != f; j++) {
1106e5dd7070Spatrick       // Matching attribute kind only. Except for Availability attributes,
1107e5dd7070Spatrick       // we are not getting into details of the attributes. For all practical purposes
1108e5dd7070Spatrick       // this is sufficient.
1109e5dd7070Spatrick       if (Attrs1[i]->getKind() == Attrs2[j]->getKind()) {
1110e5dd7070Spatrick         if (AvailabilityArgsMatch)
1111e5dd7070Spatrick           AvailabilityArgsMatch = AvailabilityAttrsMatch(Attrs1[i], Attrs2[j]);
1112e5dd7070Spatrick         match = true;
1113e5dd7070Spatrick         break;
1114e5dd7070Spatrick       }
1115e5dd7070Spatrick     }
1116e5dd7070Spatrick     if (!match)
1117e5dd7070Spatrick       return false;
1118e5dd7070Spatrick   }
1119e5dd7070Spatrick   return true;
1120e5dd7070Spatrick }
1121e5dd7070Spatrick 
1122e5dd7070Spatrick /// AttributesMatch - This routine checks list of attributes for two
1123e5dd7070Spatrick /// decls. It returns false, if there is a mismatch in kind of
1124e5dd7070Spatrick /// attributes seen in the decls. It returns true if the two decls
1125e5dd7070Spatrick /// have list of same kind of attributes. Furthermore, when there
1126e5dd7070Spatrick /// are availability attributes in the two decls, it sets the
1127e5dd7070Spatrick /// AvailabilityArgsMatch to false if availability attributes have
1128e5dd7070Spatrick /// different versions, etc.
AttributesMatch(const Decl * Decl1,const Decl * Decl2,bool & AvailabilityArgsMatch)1129e5dd7070Spatrick static bool AttributesMatch(const Decl *Decl1, const Decl *Decl2,
1130e5dd7070Spatrick                             bool &AvailabilityArgsMatch) {
1131e5dd7070Spatrick   if (!Decl1->hasAttrs() || !Decl2->hasAttrs()) {
1132e5dd7070Spatrick     AvailabilityArgsMatch = (Decl1->hasAttrs() == Decl2->hasAttrs());
1133e5dd7070Spatrick     return true;
1134e5dd7070Spatrick   }
1135e5dd7070Spatrick   AvailabilityArgsMatch = true;
1136e5dd7070Spatrick   const AttrVec &Attrs1 = Decl1->getAttrs();
1137e5dd7070Spatrick   const AttrVec &Attrs2 = Decl2->getAttrs();
1138e5dd7070Spatrick   bool match = MatchTwoAttributeLists(Attrs1, Attrs2, AvailabilityArgsMatch);
1139e5dd7070Spatrick   if (match && (Attrs2.size() > Attrs1.size()))
1140e5dd7070Spatrick     return MatchTwoAttributeLists(Attrs2, Attrs1, AvailabilityArgsMatch);
1141e5dd7070Spatrick   return match;
1142e5dd7070Spatrick }
1143e5dd7070Spatrick 
IsValidIdentifier(ASTContext & Ctx,const char * Name)1144e5dd7070Spatrick static bool IsValidIdentifier(ASTContext &Ctx,
1145e5dd7070Spatrick                               const char *Name) {
1146*12c85518Srobert   if (!isAsciiIdentifierStart(Name[0]))
1147e5dd7070Spatrick     return false;
1148e5dd7070Spatrick   std::string NameString = Name;
1149e5dd7070Spatrick   NameString[0] = toLowercase(NameString[0]);
1150e5dd7070Spatrick   IdentifierInfo *II = &Ctx.Idents.get(NameString);
1151e5dd7070Spatrick   return II->getTokenID() ==  tok::identifier;
1152e5dd7070Spatrick }
1153e5dd7070Spatrick 
migrateProperty(ASTContext & Ctx,ObjCContainerDecl * D,ObjCMethodDecl * Method)1154e5dd7070Spatrick bool ObjCMigrateASTConsumer::migrateProperty(ASTContext &Ctx,
1155e5dd7070Spatrick                              ObjCContainerDecl *D,
1156e5dd7070Spatrick                              ObjCMethodDecl *Method) {
1157e5dd7070Spatrick   if (Method->isPropertyAccessor() || !Method->isInstanceMethod() ||
1158e5dd7070Spatrick       Method->param_size() != 0)
1159e5dd7070Spatrick     return false;
1160e5dd7070Spatrick   // Is this method candidate to be a getter?
1161e5dd7070Spatrick   QualType GRT = Method->getReturnType();
1162e5dd7070Spatrick   if (GRT->isVoidType())
1163e5dd7070Spatrick     return false;
1164e5dd7070Spatrick 
1165e5dd7070Spatrick   Selector GetterSelector = Method->getSelector();
1166e5dd7070Spatrick   ObjCInstanceTypeFamily OIT_Family =
1167e5dd7070Spatrick     Selector::getInstTypeMethodFamily(GetterSelector);
1168e5dd7070Spatrick 
1169e5dd7070Spatrick   if (OIT_Family != OIT_None)
1170e5dd7070Spatrick     return false;
1171e5dd7070Spatrick 
1172e5dd7070Spatrick   IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
1173e5dd7070Spatrick   Selector SetterSelector =
1174e5dd7070Spatrick   SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
1175e5dd7070Spatrick                                          PP.getSelectorTable(),
1176e5dd7070Spatrick                                          getterName);
1177e5dd7070Spatrick   ObjCMethodDecl *SetterMethod = D->getInstanceMethod(SetterSelector);
1178e5dd7070Spatrick   unsigned LengthOfPrefix = 0;
1179e5dd7070Spatrick   if (!SetterMethod) {
1180e5dd7070Spatrick     // try a different naming convention for getter: isXxxxx
1181e5dd7070Spatrick     StringRef getterNameString = getterName->getName();
1182e5dd7070Spatrick     bool IsPrefix = getterNameString.startswith("is");
1183e5dd7070Spatrick     // Note that we don't want to change an isXXX method of retainable object
1184e5dd7070Spatrick     // type to property (readonly or otherwise).
1185e5dd7070Spatrick     if (IsPrefix && GRT->isObjCRetainableType())
1186e5dd7070Spatrick       return false;
1187e5dd7070Spatrick     if (IsPrefix || getterNameString.startswith("get")) {
1188e5dd7070Spatrick       LengthOfPrefix = (IsPrefix ? 2 : 3);
1189e5dd7070Spatrick       const char *CGetterName = getterNameString.data() + LengthOfPrefix;
1190e5dd7070Spatrick       // Make sure that first character after "is" or "get" prefix can
1191e5dd7070Spatrick       // start an identifier.
1192e5dd7070Spatrick       if (!IsValidIdentifier(Ctx, CGetterName))
1193e5dd7070Spatrick         return false;
1194e5dd7070Spatrick       if (CGetterName[0] && isUppercase(CGetterName[0])) {
1195e5dd7070Spatrick         getterName = &Ctx.Idents.get(CGetterName);
1196e5dd7070Spatrick         SetterSelector =
1197e5dd7070Spatrick         SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
1198e5dd7070Spatrick                                                PP.getSelectorTable(),
1199e5dd7070Spatrick                                                getterName);
1200e5dd7070Spatrick         SetterMethod = D->getInstanceMethod(SetterSelector);
1201e5dd7070Spatrick       }
1202e5dd7070Spatrick     }
1203e5dd7070Spatrick   }
1204e5dd7070Spatrick 
1205e5dd7070Spatrick   if (SetterMethod) {
1206e5dd7070Spatrick     if ((ASTMigrateActions & FrontendOptions::ObjCMT_ReadwriteProperty) == 0)
1207e5dd7070Spatrick       return false;
1208e5dd7070Spatrick     bool AvailabilityArgsMatch;
1209e5dd7070Spatrick     if (SetterMethod->isDeprecated() ||
1210e5dd7070Spatrick         !AttributesMatch(Method, SetterMethod, AvailabilityArgsMatch))
1211e5dd7070Spatrick       return false;
1212e5dd7070Spatrick 
1213e5dd7070Spatrick     // Is this a valid setter, matching the target getter?
1214e5dd7070Spatrick     QualType SRT = SetterMethod->getReturnType();
1215e5dd7070Spatrick     if (!SRT->isVoidType())
1216e5dd7070Spatrick       return false;
1217e5dd7070Spatrick     const ParmVarDecl *argDecl = *SetterMethod->param_begin();
1218e5dd7070Spatrick     QualType ArgType = argDecl->getType();
1219e5dd7070Spatrick     if (!Ctx.hasSameUnqualifiedType(ArgType, GRT))
1220e5dd7070Spatrick       return false;
1221e5dd7070Spatrick     edit::Commit commit(*Editor);
1222e5dd7070Spatrick     rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit,
1223e5dd7070Spatrick                           LengthOfPrefix,
1224e5dd7070Spatrick                           (ASTMigrateActions &
1225e5dd7070Spatrick                            FrontendOptions::ObjCMT_AtomicProperty) != 0,
1226e5dd7070Spatrick                           (ASTMigrateActions &
1227e5dd7070Spatrick                            FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0,
1228e5dd7070Spatrick                           AvailabilityArgsMatch);
1229e5dd7070Spatrick     Editor->commit(commit);
1230e5dd7070Spatrick     return true;
1231e5dd7070Spatrick   }
1232e5dd7070Spatrick   else if (ASTMigrateActions & FrontendOptions::ObjCMT_ReadonlyProperty) {
1233e5dd7070Spatrick     // Try a non-void method with no argument (and no setter or property of same name
1234e5dd7070Spatrick     // as a 'readonly' property.
1235e5dd7070Spatrick     edit::Commit commit(*Editor);
1236e5dd7070Spatrick     rewriteToObjCProperty(Method, nullptr /*SetterMethod*/, *NSAPIObj, commit,
1237e5dd7070Spatrick                           LengthOfPrefix,
1238e5dd7070Spatrick                           (ASTMigrateActions &
1239e5dd7070Spatrick                            FrontendOptions::ObjCMT_AtomicProperty) != 0,
1240e5dd7070Spatrick                           (ASTMigrateActions &
1241e5dd7070Spatrick                            FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty) != 0,
1242e5dd7070Spatrick                           /*AvailabilityArgsMatch*/false);
1243e5dd7070Spatrick     Editor->commit(commit);
1244e5dd7070Spatrick     return true;
1245e5dd7070Spatrick   }
1246e5dd7070Spatrick   return false;
1247e5dd7070Spatrick }
1248e5dd7070Spatrick 
migrateNsReturnsInnerPointer(ASTContext & Ctx,ObjCMethodDecl * OM)1249e5dd7070Spatrick void ObjCMigrateASTConsumer::migrateNsReturnsInnerPointer(ASTContext &Ctx,
1250e5dd7070Spatrick                                                           ObjCMethodDecl *OM) {
1251e5dd7070Spatrick   if (OM->isImplicit() ||
1252e5dd7070Spatrick       !OM->isInstanceMethod() ||
1253e5dd7070Spatrick       OM->hasAttr<ObjCReturnsInnerPointerAttr>())
1254e5dd7070Spatrick     return;
1255e5dd7070Spatrick 
1256e5dd7070Spatrick   QualType RT = OM->getReturnType();
1257e5dd7070Spatrick   if (!TypeIsInnerPointer(RT) ||
1258e5dd7070Spatrick       !NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER"))
1259e5dd7070Spatrick     return;
1260e5dd7070Spatrick 
1261e5dd7070Spatrick   edit::Commit commit(*Editor);
1262e5dd7070Spatrick   commit.insertBefore(OM->getEndLoc(), " NS_RETURNS_INNER_POINTER");
1263e5dd7070Spatrick   Editor->commit(commit);
1264e5dd7070Spatrick }
1265e5dd7070Spatrick 
migratePropertyNsReturnsInnerPointer(ASTContext & Ctx,ObjCPropertyDecl * P)1266e5dd7070Spatrick void ObjCMigrateASTConsumer::migratePropertyNsReturnsInnerPointer(ASTContext &Ctx,
1267e5dd7070Spatrick                                                                   ObjCPropertyDecl *P) {
1268e5dd7070Spatrick   QualType T = P->getType();
1269e5dd7070Spatrick 
1270e5dd7070Spatrick   if (!TypeIsInnerPointer(T) ||
1271e5dd7070Spatrick       !NSAPIObj->isMacroDefined("NS_RETURNS_INNER_POINTER"))
1272e5dd7070Spatrick     return;
1273e5dd7070Spatrick   edit::Commit commit(*Editor);
1274e5dd7070Spatrick   commit.insertBefore(P->getEndLoc(), " NS_RETURNS_INNER_POINTER ");
1275e5dd7070Spatrick   Editor->commit(commit);
1276e5dd7070Spatrick }
1277e5dd7070Spatrick 
migrateAllMethodInstaceType(ASTContext & Ctx,ObjCContainerDecl * CDecl)1278e5dd7070Spatrick void ObjCMigrateASTConsumer::migrateAllMethodInstaceType(ASTContext &Ctx,
1279e5dd7070Spatrick                                                  ObjCContainerDecl *CDecl) {
1280e5dd7070Spatrick   if (CDecl->isDeprecated() || IsCategoryNameWithDeprecatedSuffix(CDecl))
1281e5dd7070Spatrick     return;
1282e5dd7070Spatrick 
1283e5dd7070Spatrick   // migrate methods which can have instancetype as their result type.
1284e5dd7070Spatrick   for (auto *Method : CDecl->methods()) {
1285e5dd7070Spatrick     if (Method->isDeprecated())
1286e5dd7070Spatrick       continue;
1287e5dd7070Spatrick     migrateMethodInstanceType(Ctx, CDecl, Method);
1288e5dd7070Spatrick   }
1289e5dd7070Spatrick }
1290e5dd7070Spatrick 
migrateFactoryMethod(ASTContext & Ctx,ObjCContainerDecl * CDecl,ObjCMethodDecl * OM,ObjCInstanceTypeFamily OIT_Family)1291e5dd7070Spatrick void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,
1292e5dd7070Spatrick                                                   ObjCContainerDecl *CDecl,
1293e5dd7070Spatrick                                                   ObjCMethodDecl *OM,
1294e5dd7070Spatrick                                                   ObjCInstanceTypeFamily OIT_Family) {
1295e5dd7070Spatrick   if (OM->isInstanceMethod() ||
1296e5dd7070Spatrick       OM->getReturnType() == Ctx.getObjCInstanceType() ||
1297e5dd7070Spatrick       !OM->getReturnType()->isObjCIdType())
1298e5dd7070Spatrick     return;
1299e5dd7070Spatrick 
1300e5dd7070Spatrick   // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class
1301e5dd7070Spatrick   // NSYYYNamE with matching names be at least 3 characters long.
1302e5dd7070Spatrick   ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
1303e5dd7070Spatrick   if (!IDecl) {
1304e5dd7070Spatrick     if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
1305e5dd7070Spatrick       IDecl = CatDecl->getClassInterface();
1306e5dd7070Spatrick     else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
1307e5dd7070Spatrick       IDecl = ImpDecl->getClassInterface();
1308e5dd7070Spatrick   }
1309e5dd7070Spatrick   if (!IDecl)
1310e5dd7070Spatrick     return;
1311e5dd7070Spatrick 
1312ec727ea7Spatrick   std::string StringClassName = std::string(IDecl->getName());
1313e5dd7070Spatrick   StringRef LoweredClassName(StringClassName);
1314e5dd7070Spatrick   std::string StringLoweredClassName = LoweredClassName.lower();
1315e5dd7070Spatrick   LoweredClassName = StringLoweredClassName;
1316e5dd7070Spatrick 
1317e5dd7070Spatrick   IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0);
1318e5dd7070Spatrick   // Handle method with no name at its first selector slot; e.g. + (id):(int)x.
1319e5dd7070Spatrick   if (!MethodIdName)
1320e5dd7070Spatrick     return;
1321e5dd7070Spatrick 
1322ec727ea7Spatrick   std::string MethodName = std::string(MethodIdName->getName());
1323e5dd7070Spatrick   if (OIT_Family == OIT_Singleton || OIT_Family == OIT_ReturnsSelf) {
1324e5dd7070Spatrick     StringRef STRefMethodName(MethodName);
1325e5dd7070Spatrick     size_t len = 0;
1326e5dd7070Spatrick     if (STRefMethodName.startswith("standard"))
1327e5dd7070Spatrick       len = strlen("standard");
1328e5dd7070Spatrick     else if (STRefMethodName.startswith("shared"))
1329e5dd7070Spatrick       len = strlen("shared");
1330e5dd7070Spatrick     else if (STRefMethodName.startswith("default"))
1331e5dd7070Spatrick       len = strlen("default");
1332e5dd7070Spatrick     else
1333e5dd7070Spatrick       return;
1334ec727ea7Spatrick     MethodName = std::string(STRefMethodName.substr(len));
1335e5dd7070Spatrick   }
1336e5dd7070Spatrick   std::string MethodNameSubStr = MethodName.substr(0, 3);
1337e5dd7070Spatrick   StringRef MethodNamePrefix(MethodNameSubStr);
1338e5dd7070Spatrick   std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
1339e5dd7070Spatrick   MethodNamePrefix = StringLoweredMethodNamePrefix;
1340e5dd7070Spatrick   size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
1341e5dd7070Spatrick   if (Ix == StringRef::npos)
1342e5dd7070Spatrick     return;
1343ec727ea7Spatrick   std::string ClassNamePostfix = std::string(LoweredClassName.substr(Ix));
1344e5dd7070Spatrick   StringRef LoweredMethodName(MethodName);
1345e5dd7070Spatrick   std::string StringLoweredMethodName = LoweredMethodName.lower();
1346e5dd7070Spatrick   LoweredMethodName = StringLoweredMethodName;
1347e5dd7070Spatrick   if (!LoweredMethodName.startswith(ClassNamePostfix))
1348e5dd7070Spatrick     return;
1349e5dd7070Spatrick   if (OIT_Family == OIT_ReturnsSelf)
1350e5dd7070Spatrick     ReplaceWithClasstype(*this, OM);
1351e5dd7070Spatrick   else
1352e5dd7070Spatrick     ReplaceWithInstancetype(Ctx, *this, OM);
1353e5dd7070Spatrick }
1354e5dd7070Spatrick 
IsVoidStarType(QualType Ty)1355e5dd7070Spatrick static bool IsVoidStarType(QualType Ty) {
1356e5dd7070Spatrick   if (!Ty->isPointerType())
1357e5dd7070Spatrick     return false;
1358e5dd7070Spatrick 
1359e5dd7070Spatrick   // Is the type void*?
1360e5dd7070Spatrick   const PointerType* PT = Ty->castAs<PointerType>();
1361e5dd7070Spatrick   if (PT->getPointeeType().getUnqualifiedType()->isVoidType())
1362e5dd7070Spatrick     return true;
1363e5dd7070Spatrick   return IsVoidStarType(PT->getPointeeType());
1364e5dd7070Spatrick }
1365e5dd7070Spatrick 
1366e5dd7070Spatrick /// AuditedType - This routine audits the type AT and returns false if it is one of known
1367e5dd7070Spatrick /// CF object types or of the "void *" variety. It returns true if we don't care about the type
1368e5dd7070Spatrick /// such as a non-pointer or pointers which have no ownership issues (such as "int *").
AuditedType(QualType AT)1369e5dd7070Spatrick static bool AuditedType (QualType AT) {
1370e5dd7070Spatrick   if (!AT->isAnyPointerType() && !AT->isBlockPointerType())
1371e5dd7070Spatrick     return true;
1372e5dd7070Spatrick   // FIXME. There isn't much we can say about CF pointer type; or is there?
1373e5dd7070Spatrick   if (ento::coreFoundation::isCFObjectRef(AT) ||
1374e5dd7070Spatrick       IsVoidStarType(AT) ||
1375e5dd7070Spatrick       // If an ObjC object is type, assuming that it is not a CF function and
1376e5dd7070Spatrick       // that it is an un-audited function.
1377e5dd7070Spatrick       AT->isObjCObjectPointerType() || AT->isObjCBuiltinType())
1378e5dd7070Spatrick     return false;
1379e5dd7070Spatrick   // All other pointers are assumed audited as harmless.
1380e5dd7070Spatrick   return true;
1381e5dd7070Spatrick }
1382e5dd7070Spatrick 
AnnotateImplicitBridging(ASTContext & Ctx)1383e5dd7070Spatrick void ObjCMigrateASTConsumer::AnnotateImplicitBridging(ASTContext &Ctx) {
1384e5dd7070Spatrick   if (CFFunctionIBCandidates.empty())
1385e5dd7070Spatrick     return;
1386e5dd7070Spatrick   if (!NSAPIObj->isMacroDefined("CF_IMPLICIT_BRIDGING_ENABLED")) {
1387e5dd7070Spatrick     CFFunctionIBCandidates.clear();
1388e5dd7070Spatrick     FileId = FileID();
1389e5dd7070Spatrick     return;
1390e5dd7070Spatrick   }
1391e5dd7070Spatrick   // Insert CF_IMPLICIT_BRIDGING_ENABLE/CF_IMPLICIT_BRIDGING_DISABLED
1392e5dd7070Spatrick   const Decl *FirstFD = CFFunctionIBCandidates[0];
1393e5dd7070Spatrick   const Decl *LastFD  =
1394e5dd7070Spatrick     CFFunctionIBCandidates[CFFunctionIBCandidates.size()-1];
1395e5dd7070Spatrick   const char *PragmaString = "\nCF_IMPLICIT_BRIDGING_ENABLED\n\n";
1396e5dd7070Spatrick   edit::Commit commit(*Editor);
1397e5dd7070Spatrick   commit.insertBefore(FirstFD->getBeginLoc(), PragmaString);
1398e5dd7070Spatrick   PragmaString = "\n\nCF_IMPLICIT_BRIDGING_DISABLED\n";
1399e5dd7070Spatrick   SourceLocation EndLoc = LastFD->getEndLoc();
1400e5dd7070Spatrick   // get location just past end of function location.
1401e5dd7070Spatrick   EndLoc = PP.getLocForEndOfToken(EndLoc);
1402e5dd7070Spatrick   if (isa<FunctionDecl>(LastFD)) {
1403e5dd7070Spatrick     // For Methods, EndLoc points to the ending semcolon. So,
1404e5dd7070Spatrick     // not of these extra work is needed.
1405e5dd7070Spatrick     Token Tok;
1406e5dd7070Spatrick     // get locaiton of token that comes after end of function.
1407e5dd7070Spatrick     bool Failed = PP.getRawToken(EndLoc, Tok, /*IgnoreWhiteSpace=*/true);
1408e5dd7070Spatrick     if (!Failed)
1409e5dd7070Spatrick       EndLoc = Tok.getLocation();
1410e5dd7070Spatrick   }
1411e5dd7070Spatrick   commit.insertAfterToken(EndLoc, PragmaString);
1412e5dd7070Spatrick   Editor->commit(commit);
1413e5dd7070Spatrick   FileId = FileID();
1414e5dd7070Spatrick   CFFunctionIBCandidates.clear();
1415e5dd7070Spatrick }
1416e5dd7070Spatrick 
migrateCFAnnotation(ASTContext & Ctx,const Decl * Decl)1417e5dd7070Spatrick void ObjCMigrateASTConsumer::migrateCFAnnotation(ASTContext &Ctx, const Decl *Decl) {
1418e5dd7070Spatrick   if (Decl->isDeprecated())
1419e5dd7070Spatrick     return;
1420e5dd7070Spatrick 
1421e5dd7070Spatrick   if (Decl->hasAttr<CFAuditedTransferAttr>()) {
1422e5dd7070Spatrick     assert(CFFunctionIBCandidates.empty() &&
1423e5dd7070Spatrick            "Cannot have audited functions/methods inside user "
1424e5dd7070Spatrick            "provided CF_IMPLICIT_BRIDGING_ENABLE");
1425e5dd7070Spatrick     return;
1426e5dd7070Spatrick   }
1427e5dd7070Spatrick 
1428e5dd7070Spatrick   // Finction must be annotated first.
1429e5dd7070Spatrick   if (const FunctionDecl *FuncDecl = dyn_cast<FunctionDecl>(Decl)) {
1430e5dd7070Spatrick     CF_BRIDGING_KIND AuditKind = migrateAddFunctionAnnotation(Ctx, FuncDecl);
1431e5dd7070Spatrick     if (AuditKind == CF_BRIDGING_ENABLE) {
1432e5dd7070Spatrick       CFFunctionIBCandidates.push_back(Decl);
1433e5dd7070Spatrick       if (FileId.isInvalid())
1434e5dd7070Spatrick         FileId = PP.getSourceManager().getFileID(Decl->getLocation());
1435e5dd7070Spatrick     }
1436e5dd7070Spatrick     else if (AuditKind == CF_BRIDGING_MAY_INCLUDE) {
1437e5dd7070Spatrick       if (!CFFunctionIBCandidates.empty()) {
1438e5dd7070Spatrick         CFFunctionIBCandidates.push_back(Decl);
1439e5dd7070Spatrick         if (FileId.isInvalid())
1440e5dd7070Spatrick           FileId = PP.getSourceManager().getFileID(Decl->getLocation());
1441e5dd7070Spatrick       }
1442e5dd7070Spatrick     }
1443e5dd7070Spatrick     else
1444e5dd7070Spatrick       AnnotateImplicitBridging(Ctx);
1445e5dd7070Spatrick   }
1446e5dd7070Spatrick   else {
1447e5dd7070Spatrick     migrateAddMethodAnnotation(Ctx, cast<ObjCMethodDecl>(Decl));
1448e5dd7070Spatrick     AnnotateImplicitBridging(Ctx);
1449e5dd7070Spatrick   }
1450e5dd7070Spatrick }
1451e5dd7070Spatrick 
AddCFAnnotations(ASTContext & Ctx,const RetainSummary * RS,const FunctionDecl * FuncDecl,bool ResultAnnotated)1452e5dd7070Spatrick void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,
1453e5dd7070Spatrick                                               const RetainSummary *RS,
1454e5dd7070Spatrick                                               const FunctionDecl *FuncDecl,
1455e5dd7070Spatrick                                               bool ResultAnnotated) {
1456e5dd7070Spatrick   // Annotate function.
1457e5dd7070Spatrick   if (!ResultAnnotated) {
1458e5dd7070Spatrick     RetEffect Ret = RS->getRetEffect();
1459e5dd7070Spatrick     const char *AnnotationString = nullptr;
1460e5dd7070Spatrick     if (Ret.getObjKind() == ObjKind::CF) {
1461e5dd7070Spatrick       if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED"))
1462e5dd7070Spatrick         AnnotationString = " CF_RETURNS_RETAINED";
1463e5dd7070Spatrick       else if (Ret.notOwned() &&
1464e5dd7070Spatrick                NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED"))
1465e5dd7070Spatrick         AnnotationString = " CF_RETURNS_NOT_RETAINED";
1466e5dd7070Spatrick     }
1467e5dd7070Spatrick     else if (Ret.getObjKind() == ObjKind::ObjC) {
1468e5dd7070Spatrick       if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED"))
1469e5dd7070Spatrick         AnnotationString = " NS_RETURNS_RETAINED";
1470e5dd7070Spatrick     }
1471e5dd7070Spatrick 
1472e5dd7070Spatrick     if (AnnotationString) {
1473e5dd7070Spatrick       edit::Commit commit(*Editor);
1474e5dd7070Spatrick       commit.insertAfterToken(FuncDecl->getEndLoc(), AnnotationString);
1475e5dd7070Spatrick       Editor->commit(commit);
1476e5dd7070Spatrick     }
1477e5dd7070Spatrick   }
1478e5dd7070Spatrick   unsigned i = 0;
1479e5dd7070Spatrick   for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
1480e5dd7070Spatrick        pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
1481e5dd7070Spatrick     const ParmVarDecl *pd = *pi;
1482e5dd7070Spatrick     ArgEffect AE = RS->getArg(i);
1483e5dd7070Spatrick     if (AE.getKind() == DecRef && AE.getObjKind() == ObjKind::CF &&
1484e5dd7070Spatrick         !pd->hasAttr<CFConsumedAttr>() &&
1485e5dd7070Spatrick         NSAPIObj->isMacroDefined("CF_CONSUMED")) {
1486e5dd7070Spatrick       edit::Commit commit(*Editor);
1487e5dd7070Spatrick       commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");
1488e5dd7070Spatrick       Editor->commit(commit);
1489e5dd7070Spatrick     } else if (AE.getKind() == DecRef && AE.getObjKind() == ObjKind::ObjC &&
1490e5dd7070Spatrick                !pd->hasAttr<NSConsumedAttr>() &&
1491e5dd7070Spatrick                NSAPIObj->isMacroDefined("NS_CONSUMED")) {
1492e5dd7070Spatrick       edit::Commit commit(*Editor);
1493e5dd7070Spatrick       commit.insertBefore(pd->getLocation(), "NS_CONSUMED ");
1494e5dd7070Spatrick       Editor->commit(commit);
1495e5dd7070Spatrick     }
1496e5dd7070Spatrick   }
1497e5dd7070Spatrick }
1498e5dd7070Spatrick 
1499e5dd7070Spatrick ObjCMigrateASTConsumer::CF_BRIDGING_KIND
migrateAddFunctionAnnotation(ASTContext & Ctx,const FunctionDecl * FuncDecl)1500e5dd7070Spatrick   ObjCMigrateASTConsumer::migrateAddFunctionAnnotation(
1501e5dd7070Spatrick                                                   ASTContext &Ctx,
1502e5dd7070Spatrick                                                   const FunctionDecl *FuncDecl) {
1503e5dd7070Spatrick   if (FuncDecl->hasBody())
1504e5dd7070Spatrick     return CF_BRIDGING_NONE;
1505e5dd7070Spatrick 
1506e5dd7070Spatrick   const RetainSummary *RS =
1507e5dd7070Spatrick       getSummaryManager(Ctx).getSummary(AnyCall(FuncDecl));
1508e5dd7070Spatrick   bool FuncIsReturnAnnotated = (FuncDecl->hasAttr<CFReturnsRetainedAttr>() ||
1509e5dd7070Spatrick                                 FuncDecl->hasAttr<CFReturnsNotRetainedAttr>() ||
1510e5dd7070Spatrick                                 FuncDecl->hasAttr<NSReturnsRetainedAttr>() ||
1511e5dd7070Spatrick                                 FuncDecl->hasAttr<NSReturnsNotRetainedAttr>() ||
1512e5dd7070Spatrick                                 FuncDecl->hasAttr<NSReturnsAutoreleasedAttr>());
1513e5dd7070Spatrick 
1514e5dd7070Spatrick   // Trivial case of when function is annotated and has no argument.
1515e5dd7070Spatrick   if (FuncIsReturnAnnotated && FuncDecl->getNumParams() == 0)
1516e5dd7070Spatrick     return CF_BRIDGING_NONE;
1517e5dd7070Spatrick 
1518e5dd7070Spatrick   bool ReturnCFAudited = false;
1519e5dd7070Spatrick   if (!FuncIsReturnAnnotated) {
1520e5dd7070Spatrick     RetEffect Ret = RS->getRetEffect();
1521e5dd7070Spatrick     if (Ret.getObjKind() == ObjKind::CF &&
1522e5dd7070Spatrick         (Ret.isOwned() || Ret.notOwned()))
1523e5dd7070Spatrick       ReturnCFAudited = true;
1524e5dd7070Spatrick     else if (!AuditedType(FuncDecl->getReturnType()))
1525e5dd7070Spatrick       return CF_BRIDGING_NONE;
1526e5dd7070Spatrick   }
1527e5dd7070Spatrick 
1528e5dd7070Spatrick   // At this point result type is audited for potential inclusion.
1529e5dd7070Spatrick   unsigned i = 0;
1530e5dd7070Spatrick   bool ArgCFAudited = false;
1531e5dd7070Spatrick   for (FunctionDecl::param_const_iterator pi = FuncDecl->param_begin(),
1532e5dd7070Spatrick        pe = FuncDecl->param_end(); pi != pe; ++pi, ++i) {
1533e5dd7070Spatrick     const ParmVarDecl *pd = *pi;
1534e5dd7070Spatrick     ArgEffect AE = RS->getArg(i);
1535e5dd7070Spatrick     if ((AE.getKind() == DecRef /*CFConsumed annotated*/ ||
1536e5dd7070Spatrick          AE.getKind() == IncRef) && AE.getObjKind() == ObjKind::CF) {
1537e5dd7070Spatrick       if (AE.getKind() == DecRef && !pd->hasAttr<CFConsumedAttr>())
1538e5dd7070Spatrick         ArgCFAudited = true;
1539e5dd7070Spatrick       else if (AE.getKind() == IncRef)
1540e5dd7070Spatrick         ArgCFAudited = true;
1541e5dd7070Spatrick     } else {
1542e5dd7070Spatrick       QualType AT = pd->getType();
1543e5dd7070Spatrick       if (!AuditedType(AT)) {
1544e5dd7070Spatrick         AddCFAnnotations(Ctx, RS, FuncDecl, FuncIsReturnAnnotated);
1545e5dd7070Spatrick         return CF_BRIDGING_NONE;
1546e5dd7070Spatrick       }
1547e5dd7070Spatrick     }
1548e5dd7070Spatrick   }
1549e5dd7070Spatrick   if (ReturnCFAudited || ArgCFAudited)
1550e5dd7070Spatrick     return CF_BRIDGING_ENABLE;
1551e5dd7070Spatrick 
1552e5dd7070Spatrick   return CF_BRIDGING_MAY_INCLUDE;
1553e5dd7070Spatrick }
1554e5dd7070Spatrick 
migrateARCSafeAnnotation(ASTContext & Ctx,ObjCContainerDecl * CDecl)1555e5dd7070Spatrick void ObjCMigrateASTConsumer::migrateARCSafeAnnotation(ASTContext &Ctx,
1556e5dd7070Spatrick                                                  ObjCContainerDecl *CDecl) {
1557e5dd7070Spatrick   if (!isa<ObjCInterfaceDecl>(CDecl) || CDecl->isDeprecated())
1558e5dd7070Spatrick     return;
1559e5dd7070Spatrick 
1560e5dd7070Spatrick   // migrate methods which can have instancetype as their result type.
1561e5dd7070Spatrick   for (const auto *Method : CDecl->methods())
1562e5dd7070Spatrick     migrateCFAnnotation(Ctx, Method);
1563e5dd7070Spatrick }
1564e5dd7070Spatrick 
AddCFAnnotations(ASTContext & Ctx,const RetainSummary * RS,const ObjCMethodDecl * MethodDecl,bool ResultAnnotated)1565e5dd7070Spatrick void ObjCMigrateASTConsumer::AddCFAnnotations(ASTContext &Ctx,
1566e5dd7070Spatrick                                               const RetainSummary *RS,
1567e5dd7070Spatrick                                               const ObjCMethodDecl *MethodDecl,
1568e5dd7070Spatrick                                               bool ResultAnnotated) {
1569e5dd7070Spatrick   // Annotate function.
1570e5dd7070Spatrick   if (!ResultAnnotated) {
1571e5dd7070Spatrick     RetEffect Ret = RS->getRetEffect();
1572e5dd7070Spatrick     const char *AnnotationString = nullptr;
1573e5dd7070Spatrick     if (Ret.getObjKind() == ObjKind::CF) {
1574e5dd7070Spatrick       if (Ret.isOwned() && NSAPIObj->isMacroDefined("CF_RETURNS_RETAINED"))
1575e5dd7070Spatrick         AnnotationString = " CF_RETURNS_RETAINED";
1576e5dd7070Spatrick       else if (Ret.notOwned() &&
1577e5dd7070Spatrick                NSAPIObj->isMacroDefined("CF_RETURNS_NOT_RETAINED"))
1578e5dd7070Spatrick         AnnotationString = " CF_RETURNS_NOT_RETAINED";
1579e5dd7070Spatrick     }
1580e5dd7070Spatrick     else if (Ret.getObjKind() == ObjKind::ObjC) {
1581e5dd7070Spatrick       ObjCMethodFamily OMF = MethodDecl->getMethodFamily();
1582e5dd7070Spatrick       switch (OMF) {
1583e5dd7070Spatrick         case clang::OMF_alloc:
1584e5dd7070Spatrick         case clang::OMF_new:
1585e5dd7070Spatrick         case clang::OMF_copy:
1586e5dd7070Spatrick         case clang::OMF_init:
1587e5dd7070Spatrick         case clang::OMF_mutableCopy:
1588e5dd7070Spatrick           break;
1589e5dd7070Spatrick 
1590e5dd7070Spatrick         default:
1591e5dd7070Spatrick           if (Ret.isOwned() && NSAPIObj->isMacroDefined("NS_RETURNS_RETAINED"))
1592e5dd7070Spatrick             AnnotationString = " NS_RETURNS_RETAINED";
1593e5dd7070Spatrick           break;
1594e5dd7070Spatrick       }
1595e5dd7070Spatrick     }
1596e5dd7070Spatrick 
1597e5dd7070Spatrick     if (AnnotationString) {
1598e5dd7070Spatrick       edit::Commit commit(*Editor);
1599e5dd7070Spatrick       commit.insertBefore(MethodDecl->getEndLoc(), AnnotationString);
1600e5dd7070Spatrick       Editor->commit(commit);
1601e5dd7070Spatrick     }
1602e5dd7070Spatrick   }
1603e5dd7070Spatrick   unsigned i = 0;
1604e5dd7070Spatrick   for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),
1605e5dd7070Spatrick        pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {
1606e5dd7070Spatrick     const ParmVarDecl *pd = *pi;
1607e5dd7070Spatrick     ArgEffect AE = RS->getArg(i);
1608e5dd7070Spatrick     if (AE.getKind() == DecRef
1609e5dd7070Spatrick         && AE.getObjKind() == ObjKind::CF
1610e5dd7070Spatrick         && !pd->hasAttr<CFConsumedAttr>() &&
1611e5dd7070Spatrick         NSAPIObj->isMacroDefined("CF_CONSUMED")) {
1612e5dd7070Spatrick       edit::Commit commit(*Editor);
1613e5dd7070Spatrick       commit.insertBefore(pd->getLocation(), "CF_CONSUMED ");
1614e5dd7070Spatrick       Editor->commit(commit);
1615e5dd7070Spatrick     }
1616e5dd7070Spatrick   }
1617e5dd7070Spatrick }
1618e5dd7070Spatrick 
migrateAddMethodAnnotation(ASTContext & Ctx,const ObjCMethodDecl * MethodDecl)1619e5dd7070Spatrick void ObjCMigrateASTConsumer::migrateAddMethodAnnotation(
1620e5dd7070Spatrick                                             ASTContext &Ctx,
1621e5dd7070Spatrick                                             const ObjCMethodDecl *MethodDecl) {
1622e5dd7070Spatrick   if (MethodDecl->hasBody() || MethodDecl->isImplicit())
1623e5dd7070Spatrick     return;
1624e5dd7070Spatrick 
1625e5dd7070Spatrick   const RetainSummary *RS =
1626e5dd7070Spatrick       getSummaryManager(Ctx).getSummary(AnyCall(MethodDecl));
1627e5dd7070Spatrick 
1628e5dd7070Spatrick   bool MethodIsReturnAnnotated =
1629e5dd7070Spatrick       (MethodDecl->hasAttr<CFReturnsRetainedAttr>() ||
1630e5dd7070Spatrick        MethodDecl->hasAttr<CFReturnsNotRetainedAttr>() ||
1631e5dd7070Spatrick        MethodDecl->hasAttr<NSReturnsRetainedAttr>() ||
1632e5dd7070Spatrick        MethodDecl->hasAttr<NSReturnsNotRetainedAttr>() ||
1633e5dd7070Spatrick        MethodDecl->hasAttr<NSReturnsAutoreleasedAttr>());
1634e5dd7070Spatrick 
1635e5dd7070Spatrick   if (RS->getReceiverEffect().getKind() == DecRef &&
1636e5dd7070Spatrick       !MethodDecl->hasAttr<NSConsumesSelfAttr>() &&
1637e5dd7070Spatrick       MethodDecl->getMethodFamily() != OMF_init &&
1638e5dd7070Spatrick       MethodDecl->getMethodFamily() != OMF_release &&
1639e5dd7070Spatrick       NSAPIObj->isMacroDefined("NS_CONSUMES_SELF")) {
1640e5dd7070Spatrick     edit::Commit commit(*Editor);
1641e5dd7070Spatrick     commit.insertBefore(MethodDecl->getEndLoc(), " NS_CONSUMES_SELF");
1642e5dd7070Spatrick     Editor->commit(commit);
1643e5dd7070Spatrick   }
1644e5dd7070Spatrick 
1645e5dd7070Spatrick   // Trivial case of when function is annotated and has no argument.
1646e5dd7070Spatrick   if (MethodIsReturnAnnotated &&
1647e5dd7070Spatrick       (MethodDecl->param_begin() == MethodDecl->param_end()))
1648e5dd7070Spatrick     return;
1649e5dd7070Spatrick 
1650e5dd7070Spatrick   if (!MethodIsReturnAnnotated) {
1651e5dd7070Spatrick     RetEffect Ret = RS->getRetEffect();
1652e5dd7070Spatrick     if ((Ret.getObjKind() == ObjKind::CF ||
1653e5dd7070Spatrick          Ret.getObjKind() == ObjKind::ObjC) &&
1654e5dd7070Spatrick         (Ret.isOwned() || Ret.notOwned())) {
1655e5dd7070Spatrick       AddCFAnnotations(Ctx, RS, MethodDecl, false);
1656e5dd7070Spatrick       return;
1657e5dd7070Spatrick     } else if (!AuditedType(MethodDecl->getReturnType()))
1658e5dd7070Spatrick       return;
1659e5dd7070Spatrick   }
1660e5dd7070Spatrick 
1661e5dd7070Spatrick   // At this point result type is either annotated or audited.
1662e5dd7070Spatrick   unsigned i = 0;
1663e5dd7070Spatrick   for (ObjCMethodDecl::param_const_iterator pi = MethodDecl->param_begin(),
1664e5dd7070Spatrick        pe = MethodDecl->param_end(); pi != pe; ++pi, ++i) {
1665e5dd7070Spatrick     const ParmVarDecl *pd = *pi;
1666e5dd7070Spatrick     ArgEffect AE = RS->getArg(i);
1667e5dd7070Spatrick     if ((AE.getKind() == DecRef && !pd->hasAttr<CFConsumedAttr>()) ||
1668e5dd7070Spatrick         AE.getKind() == IncRef || !AuditedType(pd->getType())) {
1669e5dd7070Spatrick       AddCFAnnotations(Ctx, RS, MethodDecl, MethodIsReturnAnnotated);
1670e5dd7070Spatrick       return;
1671e5dd7070Spatrick     }
1672e5dd7070Spatrick   }
1673e5dd7070Spatrick }
1674e5dd7070Spatrick 
1675e5dd7070Spatrick namespace {
1676e5dd7070Spatrick class SuperInitChecker : public RecursiveASTVisitor<SuperInitChecker> {
1677e5dd7070Spatrick public:
shouldVisitTemplateInstantiations() const1678e5dd7070Spatrick   bool shouldVisitTemplateInstantiations() const { return false; }
shouldWalkTypesOfTypeLocs() const1679e5dd7070Spatrick   bool shouldWalkTypesOfTypeLocs() const { return false; }
1680e5dd7070Spatrick 
VisitObjCMessageExpr(ObjCMessageExpr * E)1681e5dd7070Spatrick   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
1682e5dd7070Spatrick     if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) {
1683e5dd7070Spatrick       if (E->getMethodFamily() == OMF_init)
1684e5dd7070Spatrick         return false;
1685e5dd7070Spatrick     }
1686e5dd7070Spatrick     return true;
1687e5dd7070Spatrick   }
1688e5dd7070Spatrick };
1689e5dd7070Spatrick } // end anonymous namespace
1690e5dd7070Spatrick 
hasSuperInitCall(const ObjCMethodDecl * MD)1691e5dd7070Spatrick static bool hasSuperInitCall(const ObjCMethodDecl *MD) {
1692e5dd7070Spatrick   return !SuperInitChecker().TraverseStmt(MD->getBody());
1693e5dd7070Spatrick }
1694e5dd7070Spatrick 
inferDesignatedInitializers(ASTContext & Ctx,const ObjCImplementationDecl * ImplD)1695e5dd7070Spatrick void ObjCMigrateASTConsumer::inferDesignatedInitializers(
1696e5dd7070Spatrick     ASTContext &Ctx,
1697e5dd7070Spatrick     const ObjCImplementationDecl *ImplD) {
1698e5dd7070Spatrick 
1699e5dd7070Spatrick   const ObjCInterfaceDecl *IFace = ImplD->getClassInterface();
1700e5dd7070Spatrick   if (!IFace || IFace->hasDesignatedInitializers())
1701e5dd7070Spatrick     return;
1702e5dd7070Spatrick   if (!NSAPIObj->isMacroDefined("NS_DESIGNATED_INITIALIZER"))
1703e5dd7070Spatrick     return;
1704e5dd7070Spatrick 
1705e5dd7070Spatrick   for (const auto *MD : ImplD->instance_methods()) {
1706e5dd7070Spatrick     if (MD->isDeprecated() ||
1707e5dd7070Spatrick         MD->getMethodFamily() != OMF_init ||
1708e5dd7070Spatrick         MD->isDesignatedInitializerForTheInterface())
1709e5dd7070Spatrick       continue;
1710e5dd7070Spatrick     const ObjCMethodDecl *IFaceM = IFace->getMethod(MD->getSelector(),
1711e5dd7070Spatrick                                                     /*isInstance=*/true);
1712e5dd7070Spatrick     if (!IFaceM)
1713e5dd7070Spatrick       continue;
1714e5dd7070Spatrick     if (hasSuperInitCall(MD)) {
1715e5dd7070Spatrick       edit::Commit commit(*Editor);
1716e5dd7070Spatrick       commit.insert(IFaceM->getEndLoc(), " NS_DESIGNATED_INITIALIZER");
1717e5dd7070Spatrick       Editor->commit(commit);
1718e5dd7070Spatrick     }
1719e5dd7070Spatrick   }
1720e5dd7070Spatrick }
1721e5dd7070Spatrick 
InsertFoundation(ASTContext & Ctx,SourceLocation Loc)1722e5dd7070Spatrick bool ObjCMigrateASTConsumer::InsertFoundation(ASTContext &Ctx,
1723e5dd7070Spatrick                                               SourceLocation  Loc) {
1724e5dd7070Spatrick   if (FoundationIncluded)
1725e5dd7070Spatrick     return true;
1726e5dd7070Spatrick   if (Loc.isInvalid())
1727e5dd7070Spatrick     return false;
1728e5dd7070Spatrick   auto *nsEnumId = &Ctx.Idents.get("NS_ENUM");
1729e5dd7070Spatrick   if (PP.getMacroDefinitionAtLoc(nsEnumId, Loc)) {
1730e5dd7070Spatrick     FoundationIncluded = true;
1731e5dd7070Spatrick     return true;
1732e5dd7070Spatrick   }
1733e5dd7070Spatrick   edit::Commit commit(*Editor);
1734e5dd7070Spatrick   if (Ctx.getLangOpts().Modules)
1735e5dd7070Spatrick     commit.insert(Loc, "#ifndef NS_ENUM\n@import Foundation;\n#endif\n");
1736e5dd7070Spatrick   else
1737e5dd7070Spatrick     commit.insert(Loc, "#ifndef NS_ENUM\n#import <Foundation/Foundation.h>\n#endif\n");
1738e5dd7070Spatrick   Editor->commit(commit);
1739e5dd7070Spatrick   FoundationIncluded = true;
1740e5dd7070Spatrick   return true;
1741e5dd7070Spatrick }
1742e5dd7070Spatrick 
1743e5dd7070Spatrick namespace {
1744e5dd7070Spatrick 
1745e5dd7070Spatrick class RewritesReceiver : public edit::EditsReceiver {
1746e5dd7070Spatrick   Rewriter &Rewrite;
1747e5dd7070Spatrick 
1748e5dd7070Spatrick public:
RewritesReceiver(Rewriter & Rewrite)1749e5dd7070Spatrick   RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
1750e5dd7070Spatrick 
insert(SourceLocation loc,StringRef text)1751e5dd7070Spatrick   void insert(SourceLocation loc, StringRef text) override {
1752e5dd7070Spatrick     Rewrite.InsertText(loc, text);
1753e5dd7070Spatrick   }
replace(CharSourceRange range,StringRef text)1754e5dd7070Spatrick   void replace(CharSourceRange range, StringRef text) override {
1755e5dd7070Spatrick     Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
1756e5dd7070Spatrick   }
1757e5dd7070Spatrick };
1758e5dd7070Spatrick 
1759e5dd7070Spatrick class JSONEditWriter : public edit::EditsReceiver {
1760e5dd7070Spatrick   SourceManager &SourceMgr;
1761e5dd7070Spatrick   llvm::raw_ostream &OS;
1762e5dd7070Spatrick 
1763e5dd7070Spatrick public:
JSONEditWriter(SourceManager & SM,llvm::raw_ostream & OS)1764e5dd7070Spatrick   JSONEditWriter(SourceManager &SM, llvm::raw_ostream &OS)
1765e5dd7070Spatrick     : SourceMgr(SM), OS(OS) {
1766e5dd7070Spatrick     OS << "[\n";
1767e5dd7070Spatrick   }
~JSONEditWriter()1768e5dd7070Spatrick   ~JSONEditWriter() override { OS << "]\n"; }
1769e5dd7070Spatrick 
1770e5dd7070Spatrick private:
1771e5dd7070Spatrick   struct EntryWriter {
1772e5dd7070Spatrick     SourceManager &SourceMgr;
1773e5dd7070Spatrick     llvm::raw_ostream &OS;
1774e5dd7070Spatrick 
EntryWriter__anon1ecb545e0411::JSONEditWriter::EntryWriter1775e5dd7070Spatrick     EntryWriter(SourceManager &SM, llvm::raw_ostream &OS)
1776e5dd7070Spatrick       : SourceMgr(SM), OS(OS) {
1777e5dd7070Spatrick       OS << " {\n";
1778e5dd7070Spatrick     }
~EntryWriter__anon1ecb545e0411::JSONEditWriter::EntryWriter1779e5dd7070Spatrick     ~EntryWriter() {
1780e5dd7070Spatrick       OS << " },\n";
1781e5dd7070Spatrick     }
1782e5dd7070Spatrick 
writeLoc__anon1ecb545e0411::JSONEditWriter::EntryWriter1783e5dd7070Spatrick     void writeLoc(SourceLocation Loc) {
1784e5dd7070Spatrick       FileID FID;
1785e5dd7070Spatrick       unsigned Offset;
1786e5dd7070Spatrick       std::tie(FID, Offset) = SourceMgr.getDecomposedLoc(Loc);
1787e5dd7070Spatrick       assert(FID.isValid());
1788e5dd7070Spatrick       SmallString<200> Path =
1789e5dd7070Spatrick           StringRef(SourceMgr.getFileEntryForID(FID)->getName());
1790e5dd7070Spatrick       llvm::sys::fs::make_absolute(Path);
1791e5dd7070Spatrick       OS << "  \"file\": \"";
1792e5dd7070Spatrick       OS.write_escaped(Path.str()) << "\",\n";
1793e5dd7070Spatrick       OS << "  \"offset\": " << Offset << ",\n";
1794e5dd7070Spatrick     }
1795e5dd7070Spatrick 
writeRemove__anon1ecb545e0411::JSONEditWriter::EntryWriter1796e5dd7070Spatrick     void writeRemove(CharSourceRange Range) {
1797e5dd7070Spatrick       assert(Range.isCharRange());
1798e5dd7070Spatrick       std::pair<FileID, unsigned> Begin =
1799e5dd7070Spatrick           SourceMgr.getDecomposedLoc(Range.getBegin());
1800e5dd7070Spatrick       std::pair<FileID, unsigned> End =
1801e5dd7070Spatrick           SourceMgr.getDecomposedLoc(Range.getEnd());
1802e5dd7070Spatrick       assert(Begin.first == End.first);
1803e5dd7070Spatrick       assert(Begin.second <= End.second);
1804e5dd7070Spatrick       unsigned Length = End.second - Begin.second;
1805e5dd7070Spatrick 
1806e5dd7070Spatrick       OS << "  \"remove\": " << Length << ",\n";
1807e5dd7070Spatrick     }
1808e5dd7070Spatrick 
writeText__anon1ecb545e0411::JSONEditWriter::EntryWriter1809e5dd7070Spatrick     void writeText(StringRef Text) {
1810e5dd7070Spatrick       OS << "  \"text\": \"";
1811e5dd7070Spatrick       OS.write_escaped(Text) << "\",\n";
1812e5dd7070Spatrick     }
1813e5dd7070Spatrick   };
1814e5dd7070Spatrick 
insert(SourceLocation Loc,StringRef Text)1815e5dd7070Spatrick   void insert(SourceLocation Loc, StringRef Text) override {
1816e5dd7070Spatrick     EntryWriter Writer(SourceMgr, OS);
1817e5dd7070Spatrick     Writer.writeLoc(Loc);
1818e5dd7070Spatrick     Writer.writeText(Text);
1819e5dd7070Spatrick   }
1820e5dd7070Spatrick 
replace(CharSourceRange Range,StringRef Text)1821e5dd7070Spatrick   void replace(CharSourceRange Range, StringRef Text) override {
1822e5dd7070Spatrick     EntryWriter Writer(SourceMgr, OS);
1823e5dd7070Spatrick     Writer.writeLoc(Range.getBegin());
1824e5dd7070Spatrick     Writer.writeRemove(Range);
1825e5dd7070Spatrick     Writer.writeText(Text);
1826e5dd7070Spatrick   }
1827e5dd7070Spatrick 
remove(CharSourceRange Range)1828e5dd7070Spatrick   void remove(CharSourceRange Range) override {
1829e5dd7070Spatrick     EntryWriter Writer(SourceMgr, OS);
1830e5dd7070Spatrick     Writer.writeLoc(Range.getBegin());
1831e5dd7070Spatrick     Writer.writeRemove(Range);
1832e5dd7070Spatrick   }
1833e5dd7070Spatrick };
1834e5dd7070Spatrick 
1835e5dd7070Spatrick } // end anonymous namespace
1836e5dd7070Spatrick 
HandleTranslationUnit(ASTContext & Ctx)1837e5dd7070Spatrick void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
1838e5dd7070Spatrick 
1839e5dd7070Spatrick   TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
1840e5dd7070Spatrick   if (ASTMigrateActions & FrontendOptions::ObjCMT_MigrateDecls) {
1841e5dd7070Spatrick     for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
1842e5dd7070Spatrick          D != DEnd; ++D) {
1843e5dd7070Spatrick       FileID FID = PP.getSourceManager().getFileID((*D)->getLocation());
1844e5dd7070Spatrick       if (FID.isValid())
1845e5dd7070Spatrick         if (FileId.isValid() && FileId != FID) {
1846e5dd7070Spatrick           if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
1847e5dd7070Spatrick             AnnotateImplicitBridging(Ctx);
1848e5dd7070Spatrick         }
1849e5dd7070Spatrick 
1850e5dd7070Spatrick       if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
1851e5dd7070Spatrick         if (canModify(CDecl))
1852e5dd7070Spatrick           migrateObjCContainerDecl(Ctx, CDecl);
1853e5dd7070Spatrick       if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(*D)) {
1854e5dd7070Spatrick         if (canModify(CatDecl))
1855e5dd7070Spatrick           migrateObjCContainerDecl(Ctx, CatDecl);
1856e5dd7070Spatrick       }
1857e5dd7070Spatrick       else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D)) {
1858e5dd7070Spatrick         ObjCProtocolDecls.insert(PDecl->getCanonicalDecl());
1859e5dd7070Spatrick         if (canModify(PDecl))
1860e5dd7070Spatrick           migrateObjCContainerDecl(Ctx, PDecl);
1861e5dd7070Spatrick       }
1862e5dd7070Spatrick       else if (const ObjCImplementationDecl *ImpDecl =
1863e5dd7070Spatrick                dyn_cast<ObjCImplementationDecl>(*D)) {
1864e5dd7070Spatrick         if ((ASTMigrateActions & FrontendOptions::ObjCMT_ProtocolConformance) &&
1865e5dd7070Spatrick             canModify(ImpDecl))
1866e5dd7070Spatrick           migrateProtocolConformance(Ctx, ImpDecl);
1867e5dd7070Spatrick       }
1868e5dd7070Spatrick       else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
1869e5dd7070Spatrick         if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))
1870e5dd7070Spatrick           continue;
1871e5dd7070Spatrick         if (!canModify(ED))
1872e5dd7070Spatrick           continue;
1873e5dd7070Spatrick         DeclContext::decl_iterator N = D;
1874e5dd7070Spatrick         if (++N != DEnd) {
1875e5dd7070Spatrick           const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N);
1876e5dd7070Spatrick           if (migrateNSEnumDecl(Ctx, ED, TD) && TD)
1877e5dd7070Spatrick             D++;
1878e5dd7070Spatrick         }
1879e5dd7070Spatrick         else
1880e5dd7070Spatrick           migrateNSEnumDecl(Ctx, ED, /*TypedefDecl */nullptr);
1881e5dd7070Spatrick       }
1882e5dd7070Spatrick       else if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*D)) {
1883e5dd7070Spatrick         if (!(ASTMigrateActions & FrontendOptions::ObjCMT_NsMacros))
1884e5dd7070Spatrick           continue;
1885e5dd7070Spatrick         if (!canModify(TD))
1886e5dd7070Spatrick           continue;
1887e5dd7070Spatrick         DeclContext::decl_iterator N = D;
1888e5dd7070Spatrick         if (++N == DEnd)
1889e5dd7070Spatrick           continue;
1890e5dd7070Spatrick         if (const EnumDecl *ED = dyn_cast<EnumDecl>(*N)) {
1891e5dd7070Spatrick           if (canModify(ED)) {
1892e5dd7070Spatrick             if (++N != DEnd)
1893e5dd7070Spatrick               if (const TypedefDecl *TDF = dyn_cast<TypedefDecl>(*N)) {
1894e5dd7070Spatrick                 // prefer typedef-follows-enum to enum-follows-typedef pattern.
1895e5dd7070Spatrick                 if (migrateNSEnumDecl(Ctx, ED, TDF)) {
1896e5dd7070Spatrick                   ++D; ++D;
1897e5dd7070Spatrick                   CacheObjCNSIntegerTypedefed(TD);
1898e5dd7070Spatrick                   continue;
1899e5dd7070Spatrick                 }
1900e5dd7070Spatrick               }
1901e5dd7070Spatrick             if (migrateNSEnumDecl(Ctx, ED, TD)) {
1902e5dd7070Spatrick               ++D;
1903e5dd7070Spatrick               continue;
1904e5dd7070Spatrick             }
1905e5dd7070Spatrick           }
1906e5dd7070Spatrick         }
1907e5dd7070Spatrick         CacheObjCNSIntegerTypedefed(TD);
1908e5dd7070Spatrick       }
1909e5dd7070Spatrick       else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*D)) {
1910e5dd7070Spatrick         if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
1911e5dd7070Spatrick             canModify(FD))
1912e5dd7070Spatrick           migrateCFAnnotation(Ctx, FD);
1913e5dd7070Spatrick       }
1914e5dd7070Spatrick 
1915e5dd7070Spatrick       if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D)) {
1916e5dd7070Spatrick         bool CanModify = canModify(CDecl);
1917e5dd7070Spatrick         // migrate methods which can have instancetype as their result type.
1918e5dd7070Spatrick         if ((ASTMigrateActions & FrontendOptions::ObjCMT_Instancetype) &&
1919e5dd7070Spatrick             CanModify)
1920e5dd7070Spatrick           migrateAllMethodInstaceType(Ctx, CDecl);
1921e5dd7070Spatrick         // annotate methods with CF annotations.
1922e5dd7070Spatrick         if ((ASTMigrateActions & FrontendOptions::ObjCMT_Annotation) &&
1923e5dd7070Spatrick             CanModify)
1924e5dd7070Spatrick           migrateARCSafeAnnotation(Ctx, CDecl);
1925e5dd7070Spatrick       }
1926e5dd7070Spatrick 
1927e5dd7070Spatrick       if (const ObjCImplementationDecl *
1928e5dd7070Spatrick             ImplD = dyn_cast<ObjCImplementationDecl>(*D)) {
1929e5dd7070Spatrick         if ((ASTMigrateActions & FrontendOptions::ObjCMT_DesignatedInitializer) &&
1930e5dd7070Spatrick             canModify(ImplD))
1931e5dd7070Spatrick           inferDesignatedInitializers(Ctx, ImplD);
1932e5dd7070Spatrick       }
1933e5dd7070Spatrick     }
1934e5dd7070Spatrick     if (ASTMigrateActions & FrontendOptions::ObjCMT_Annotation)
1935e5dd7070Spatrick       AnnotateImplicitBridging(Ctx);
1936e5dd7070Spatrick   }
1937e5dd7070Spatrick 
1938e5dd7070Spatrick  if (IsOutputFile) {
1939e5dd7070Spatrick    std::error_code EC;
1940e5dd7070Spatrick    llvm::raw_fd_ostream OS(MigrateDir, EC, llvm::sys::fs::OF_None);
1941e5dd7070Spatrick    if (EC) {
1942e5dd7070Spatrick       DiagnosticsEngine &Diags = Ctx.getDiagnostics();
1943e5dd7070Spatrick       Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
1944e5dd7070Spatrick           << EC.message();
1945e5dd7070Spatrick       return;
1946e5dd7070Spatrick     }
1947e5dd7070Spatrick 
1948e5dd7070Spatrick    JSONEditWriter Writer(Ctx.getSourceManager(), OS);
1949e5dd7070Spatrick    Editor->applyRewrites(Writer);
1950e5dd7070Spatrick    return;
1951e5dd7070Spatrick  }
1952e5dd7070Spatrick 
1953e5dd7070Spatrick   Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
1954e5dd7070Spatrick   RewritesReceiver Rec(rewriter);
1955e5dd7070Spatrick   Editor->applyRewrites(Rec);
1956e5dd7070Spatrick 
1957e5dd7070Spatrick   for (Rewriter::buffer_iterator
1958e5dd7070Spatrick         I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
1959e5dd7070Spatrick     FileID FID = I->first;
1960e5dd7070Spatrick     RewriteBuffer &buf = I->second;
1961*12c85518Srobert     OptionalFileEntryRef file =
1962*12c85518Srobert         Ctx.getSourceManager().getFileEntryRefForID(FID);
1963e5dd7070Spatrick     assert(file);
1964e5dd7070Spatrick     SmallString<512> newText;
1965e5dd7070Spatrick     llvm::raw_svector_ostream vecOS(newText);
1966e5dd7070Spatrick     buf.write(vecOS);
1967e5dd7070Spatrick     std::unique_ptr<llvm::MemoryBuffer> memBuf(
1968e5dd7070Spatrick         llvm::MemoryBuffer::getMemBufferCopy(
1969e5dd7070Spatrick             StringRef(newText.data(), newText.size()), file->getName()));
1970e5dd7070Spatrick     SmallString<64> filePath(file->getName());
1971e5dd7070Spatrick     FileMgr.FixupRelativePath(filePath);
1972e5dd7070Spatrick     Remapper.remap(filePath.str(), std::move(memBuf));
1973e5dd7070Spatrick   }
1974e5dd7070Spatrick 
1975e5dd7070Spatrick   if (IsOutputFile) {
1976e5dd7070Spatrick     Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
1977e5dd7070Spatrick   } else {
1978e5dd7070Spatrick     Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
1979e5dd7070Spatrick   }
1980e5dd7070Spatrick }
1981e5dd7070Spatrick 
BeginInvocation(CompilerInstance & CI)1982e5dd7070Spatrick bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
1983e5dd7070Spatrick   CI.getDiagnostics().setIgnoreAllWarnings(true);
1984e5dd7070Spatrick   return true;
1985e5dd7070Spatrick }
1986e5dd7070Spatrick 
getAllowListFilenames(StringRef DirPath)1987*12c85518Srobert static std::vector<std::string> getAllowListFilenames(StringRef DirPath) {
1988e5dd7070Spatrick   using namespace llvm::sys::fs;
1989e5dd7070Spatrick   using namespace llvm::sys::path;
1990e5dd7070Spatrick 
1991e5dd7070Spatrick   std::vector<std::string> Filenames;
1992e5dd7070Spatrick   if (DirPath.empty() || !is_directory(DirPath))
1993e5dd7070Spatrick     return Filenames;
1994e5dd7070Spatrick 
1995e5dd7070Spatrick   std::error_code EC;
1996e5dd7070Spatrick   directory_iterator DI = directory_iterator(DirPath, EC);
1997e5dd7070Spatrick   directory_iterator DE;
1998e5dd7070Spatrick   for (; !EC && DI != DE; DI = DI.increment(EC)) {
1999e5dd7070Spatrick     if (is_regular_file(DI->path()))
2000ec727ea7Spatrick       Filenames.push_back(std::string(filename(DI->path())));
2001e5dd7070Spatrick   }
2002e5dd7070Spatrick 
2003e5dd7070Spatrick   return Filenames;
2004e5dd7070Spatrick }
2005e5dd7070Spatrick 
2006e5dd7070Spatrick std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)2007e5dd7070Spatrick MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
2008e5dd7070Spatrick   PPConditionalDirectiveRecord *
2009e5dd7070Spatrick     PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
2010e5dd7070Spatrick   unsigned ObjCMTAction = CI.getFrontendOpts().ObjCMTAction;
2011e5dd7070Spatrick   unsigned ObjCMTOpts = ObjCMTAction;
2012e5dd7070Spatrick   // These are companion flags, they do not enable transformations.
2013e5dd7070Spatrick   ObjCMTOpts &= ~(FrontendOptions::ObjCMT_AtomicProperty |
2014e5dd7070Spatrick                   FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty);
2015e5dd7070Spatrick   if (ObjCMTOpts == FrontendOptions::ObjCMT_None) {
2016e5dd7070Spatrick     // If no specific option was given, enable literals+subscripting transforms
2017e5dd7070Spatrick     // by default.
2018*12c85518Srobert     ObjCMTAction |=
2019*12c85518Srobert         FrontendOptions::ObjCMT_Literals | FrontendOptions::ObjCMT_Subscripting;
2020e5dd7070Spatrick   }
2021e5dd7070Spatrick   CI.getPreprocessor().addPPCallbacks(std::unique_ptr<PPCallbacks>(PPRec));
2022*12c85518Srobert   std::vector<std::string> AllowList =
2023*12c85518Srobert       getAllowListFilenames(CI.getFrontendOpts().ObjCMTAllowListPath);
2024e5dd7070Spatrick   return std::make_unique<ObjCMigrateASTConsumer>(
2025e5dd7070Spatrick       CI.getFrontendOpts().OutputFile, ObjCMTAction, Remapper,
2026e5dd7070Spatrick       CI.getFileManager(), PPRec, CI.getPreprocessor(),
2027*12c85518Srobert       /*isOutputFile=*/true, AllowList);
2028e5dd7070Spatrick }
2029e5dd7070Spatrick 
2030e5dd7070Spatrick namespace {
2031e5dd7070Spatrick struct EditEntry {
2032*12c85518Srobert   OptionalFileEntryRef File;
2033a9ac8606Spatrick   unsigned Offset = 0;
2034a9ac8606Spatrick   unsigned RemoveLen = 0;
2035e5dd7070Spatrick   std::string Text;
2036e5dd7070Spatrick };
2037e5dd7070Spatrick } // end anonymous namespace
2038e5dd7070Spatrick 
2039e5dd7070Spatrick namespace llvm {
2040e5dd7070Spatrick template<> struct DenseMapInfo<EditEntry> {
getEmptyKeyllvm::DenseMapInfo2041e5dd7070Spatrick   static inline EditEntry getEmptyKey() {
2042e5dd7070Spatrick     EditEntry Entry;
2043e5dd7070Spatrick     Entry.Offset = unsigned(-1);
2044e5dd7070Spatrick     return Entry;
2045e5dd7070Spatrick   }
getTombstoneKeyllvm::DenseMapInfo2046e5dd7070Spatrick   static inline EditEntry getTombstoneKey() {
2047e5dd7070Spatrick     EditEntry Entry;
2048e5dd7070Spatrick     Entry.Offset = unsigned(-2);
2049e5dd7070Spatrick     return Entry;
2050e5dd7070Spatrick   }
getHashValuellvm::DenseMapInfo2051e5dd7070Spatrick   static unsigned getHashValue(const EditEntry& Val) {
2052a9ac8606Spatrick     return (unsigned)llvm::hash_combine(Val.File, Val.Offset, Val.RemoveLen,
2053a9ac8606Spatrick                                         Val.Text);
2054e5dd7070Spatrick   }
isEqualllvm::DenseMapInfo2055e5dd7070Spatrick   static bool isEqual(const EditEntry &LHS, const EditEntry &RHS) {
2056e5dd7070Spatrick     return LHS.File == RHS.File &&
2057e5dd7070Spatrick         LHS.Offset == RHS.Offset &&
2058e5dd7070Spatrick         LHS.RemoveLen == RHS.RemoveLen &&
2059e5dd7070Spatrick         LHS.Text == RHS.Text;
2060e5dd7070Spatrick   }
2061e5dd7070Spatrick };
2062e5dd7070Spatrick } // end namespace llvm
2063e5dd7070Spatrick 
2064e5dd7070Spatrick namespace {
2065e5dd7070Spatrick class RemapFileParser {
2066e5dd7070Spatrick   FileManager &FileMgr;
2067e5dd7070Spatrick 
2068e5dd7070Spatrick public:
RemapFileParser(FileManager & FileMgr)2069e5dd7070Spatrick   RemapFileParser(FileManager &FileMgr) : FileMgr(FileMgr) { }
2070e5dd7070Spatrick 
parse(StringRef File,SmallVectorImpl<EditEntry> & Entries)2071e5dd7070Spatrick   bool parse(StringRef File, SmallVectorImpl<EditEntry> &Entries) {
2072e5dd7070Spatrick     using namespace llvm::yaml;
2073e5dd7070Spatrick 
2074e5dd7070Spatrick     llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
2075e5dd7070Spatrick         llvm::MemoryBuffer::getFile(File);
2076e5dd7070Spatrick     if (!FileBufOrErr)
2077e5dd7070Spatrick       return true;
2078e5dd7070Spatrick 
2079e5dd7070Spatrick     llvm::SourceMgr SM;
2080e5dd7070Spatrick     Stream YAMLStream(FileBufOrErr.get()->getMemBufferRef(), SM);
2081e5dd7070Spatrick     document_iterator I = YAMLStream.begin();
2082e5dd7070Spatrick     if (I == YAMLStream.end())
2083e5dd7070Spatrick       return true;
2084e5dd7070Spatrick     Node *Root = I->getRoot();
2085e5dd7070Spatrick     if (!Root)
2086e5dd7070Spatrick       return true;
2087e5dd7070Spatrick 
2088e5dd7070Spatrick     SequenceNode *SeqNode = dyn_cast<SequenceNode>(Root);
2089e5dd7070Spatrick     if (!SeqNode)
2090e5dd7070Spatrick       return true;
2091e5dd7070Spatrick 
2092e5dd7070Spatrick     for (SequenceNode::iterator
2093e5dd7070Spatrick            AI = SeqNode->begin(), AE = SeqNode->end(); AI != AE; ++AI) {
2094e5dd7070Spatrick       MappingNode *MapNode = dyn_cast<MappingNode>(&*AI);
2095e5dd7070Spatrick       if (!MapNode)
2096e5dd7070Spatrick         continue;
2097e5dd7070Spatrick       parseEdit(MapNode, Entries);
2098e5dd7070Spatrick     }
2099e5dd7070Spatrick 
2100e5dd7070Spatrick     return false;
2101e5dd7070Spatrick   }
2102e5dd7070Spatrick 
2103e5dd7070Spatrick private:
parseEdit(llvm::yaml::MappingNode * Node,SmallVectorImpl<EditEntry> & Entries)2104e5dd7070Spatrick   void parseEdit(llvm::yaml::MappingNode *Node,
2105e5dd7070Spatrick                  SmallVectorImpl<EditEntry> &Entries) {
2106e5dd7070Spatrick     using namespace llvm::yaml;
2107e5dd7070Spatrick     EditEntry Entry;
2108e5dd7070Spatrick     bool Ignore = false;
2109e5dd7070Spatrick 
2110e5dd7070Spatrick     for (MappingNode::iterator
2111e5dd7070Spatrick            KVI = Node->begin(), KVE = Node->end(); KVI != KVE; ++KVI) {
2112e5dd7070Spatrick       ScalarNode *KeyString = dyn_cast<ScalarNode>((*KVI).getKey());
2113e5dd7070Spatrick       if (!KeyString)
2114e5dd7070Spatrick         continue;
2115e5dd7070Spatrick       SmallString<10> KeyStorage;
2116e5dd7070Spatrick       StringRef Key = KeyString->getValue(KeyStorage);
2117e5dd7070Spatrick 
2118e5dd7070Spatrick       ScalarNode *ValueString = dyn_cast<ScalarNode>((*KVI).getValue());
2119e5dd7070Spatrick       if (!ValueString)
2120e5dd7070Spatrick         continue;
2121e5dd7070Spatrick       SmallString<64> ValueStorage;
2122e5dd7070Spatrick       StringRef Val = ValueString->getValue(ValueStorage);
2123e5dd7070Spatrick 
2124e5dd7070Spatrick       if (Key == "file") {
2125a9ac8606Spatrick         if (auto File = FileMgr.getOptionalFileRef(Val))
2126a9ac8606Spatrick           Entry.File = File;
2127e5dd7070Spatrick         else
2128e5dd7070Spatrick           Ignore = true;
2129e5dd7070Spatrick       } else if (Key == "offset") {
2130e5dd7070Spatrick         if (Val.getAsInteger(10, Entry.Offset))
2131e5dd7070Spatrick           Ignore = true;
2132e5dd7070Spatrick       } else if (Key == "remove") {
2133e5dd7070Spatrick         if (Val.getAsInteger(10, Entry.RemoveLen))
2134e5dd7070Spatrick           Ignore = true;
2135e5dd7070Spatrick       } else if (Key == "text") {
2136ec727ea7Spatrick         Entry.Text = std::string(Val);
2137e5dd7070Spatrick       }
2138e5dd7070Spatrick     }
2139e5dd7070Spatrick 
2140e5dd7070Spatrick     if (!Ignore)
2141e5dd7070Spatrick       Entries.push_back(Entry);
2142e5dd7070Spatrick   }
2143e5dd7070Spatrick };
2144e5dd7070Spatrick } // end anonymous namespace
2145e5dd7070Spatrick 
reportDiag(const Twine & Err,DiagnosticsEngine & Diag)2146e5dd7070Spatrick static bool reportDiag(const Twine &Err, DiagnosticsEngine &Diag) {
2147e5dd7070Spatrick   Diag.Report(Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0"))
2148e5dd7070Spatrick       << Err.str();
2149e5dd7070Spatrick   return true;
2150e5dd7070Spatrick }
2151e5dd7070Spatrick 
applyEditsToTemp(FileEntryRef FE,ArrayRef<EditEntry> Edits,FileManager & FileMgr,DiagnosticsEngine & Diag)2152a9ac8606Spatrick static std::string applyEditsToTemp(FileEntryRef FE,
2153e5dd7070Spatrick                                     ArrayRef<EditEntry> Edits,
2154e5dd7070Spatrick                                     FileManager &FileMgr,
2155e5dd7070Spatrick                                     DiagnosticsEngine &Diag) {
2156e5dd7070Spatrick   using namespace llvm::sys;
2157e5dd7070Spatrick 
2158e5dd7070Spatrick   SourceManager SM(Diag, FileMgr);
2159e5dd7070Spatrick   FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User);
2160e5dd7070Spatrick   LangOptions LangOpts;
2161e5dd7070Spatrick   edit::EditedSource Editor(SM, LangOpts);
2162e5dd7070Spatrick   for (ArrayRef<EditEntry>::iterator
2163e5dd7070Spatrick         I = Edits.begin(), E = Edits.end(); I != E; ++I) {
2164e5dd7070Spatrick     const EditEntry &Entry = *I;
2165e5dd7070Spatrick     assert(Entry.File == FE);
2166e5dd7070Spatrick     SourceLocation Loc =
2167e5dd7070Spatrick         SM.getLocForStartOfFile(FID).getLocWithOffset(Entry.Offset);
2168e5dd7070Spatrick     CharSourceRange Range;
2169e5dd7070Spatrick     if (Entry.RemoveLen != 0) {
2170e5dd7070Spatrick       Range = CharSourceRange::getCharRange(Loc,
2171e5dd7070Spatrick                                          Loc.getLocWithOffset(Entry.RemoveLen));
2172e5dd7070Spatrick     }
2173e5dd7070Spatrick 
2174e5dd7070Spatrick     edit::Commit commit(Editor);
2175e5dd7070Spatrick     if (Range.isInvalid()) {
2176e5dd7070Spatrick       commit.insert(Loc, Entry.Text);
2177e5dd7070Spatrick     } else if (Entry.Text.empty()) {
2178e5dd7070Spatrick       commit.remove(Range);
2179e5dd7070Spatrick     } else {
2180e5dd7070Spatrick       commit.replace(Range, Entry.Text);
2181e5dd7070Spatrick     }
2182e5dd7070Spatrick     Editor.commit(commit);
2183e5dd7070Spatrick   }
2184e5dd7070Spatrick 
2185e5dd7070Spatrick   Rewriter rewriter(SM, LangOpts);
2186e5dd7070Spatrick   RewritesReceiver Rec(rewriter);
2187e5dd7070Spatrick   Editor.applyRewrites(Rec, /*adjustRemovals=*/false);
2188e5dd7070Spatrick 
2189e5dd7070Spatrick   const RewriteBuffer *Buf = rewriter.getRewriteBufferFor(FID);
2190e5dd7070Spatrick   SmallString<512> NewText;
2191e5dd7070Spatrick   llvm::raw_svector_ostream OS(NewText);
2192e5dd7070Spatrick   Buf->write(OS);
2193e5dd7070Spatrick 
2194e5dd7070Spatrick   SmallString<64> TempPath;
2195e5dd7070Spatrick   int FD;
2196a9ac8606Spatrick   if (fs::createTemporaryFile(path::filename(FE.getName()),
2197a9ac8606Spatrick                               path::extension(FE.getName()).drop_front(), FD,
2198e5dd7070Spatrick                               TempPath)) {
2199e5dd7070Spatrick     reportDiag("Could not create file: " + TempPath.str(), Diag);
2200e5dd7070Spatrick     return std::string();
2201e5dd7070Spatrick   }
2202e5dd7070Spatrick 
2203e5dd7070Spatrick   llvm::raw_fd_ostream TmpOut(FD, /*shouldClose=*/true);
2204e5dd7070Spatrick   TmpOut.write(NewText.data(), NewText.size());
2205e5dd7070Spatrick   TmpOut.close();
2206e5dd7070Spatrick 
2207ec727ea7Spatrick   return std::string(TempPath.str());
2208e5dd7070Spatrick }
2209e5dd7070Spatrick 
getFileRemappingsFromFileList(std::vector<std::pair<std::string,std::string>> & remap,ArrayRef<StringRef> remapFiles,DiagnosticConsumer * DiagClient)2210e5dd7070Spatrick bool arcmt::getFileRemappingsFromFileList(
2211e5dd7070Spatrick                         std::vector<std::pair<std::string,std::string> > &remap,
2212e5dd7070Spatrick                         ArrayRef<StringRef> remapFiles,
2213e5dd7070Spatrick                         DiagnosticConsumer *DiagClient) {
2214e5dd7070Spatrick   bool hasErrorOccurred = false;
2215e5dd7070Spatrick 
2216e5dd7070Spatrick   FileSystemOptions FSOpts;
2217e5dd7070Spatrick   FileManager FileMgr(FSOpts);
2218e5dd7070Spatrick   RemapFileParser Parser(FileMgr);
2219e5dd7070Spatrick 
2220e5dd7070Spatrick   IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
2221e5dd7070Spatrick   IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
2222e5dd7070Spatrick       new DiagnosticsEngine(DiagID, new DiagnosticOptions,
2223e5dd7070Spatrick                             DiagClient, /*ShouldOwnClient=*/false));
2224e5dd7070Spatrick 
2225a9ac8606Spatrick   typedef llvm::DenseMap<FileEntryRef, std::vector<EditEntry> >
2226e5dd7070Spatrick       FileEditEntriesTy;
2227e5dd7070Spatrick   FileEditEntriesTy FileEditEntries;
2228e5dd7070Spatrick 
2229e5dd7070Spatrick   llvm::DenseSet<EditEntry> EntriesSet;
2230e5dd7070Spatrick 
2231e5dd7070Spatrick   for (ArrayRef<StringRef>::iterator
2232e5dd7070Spatrick          I = remapFiles.begin(), E = remapFiles.end(); I != E; ++I) {
2233e5dd7070Spatrick     SmallVector<EditEntry, 16> Entries;
2234e5dd7070Spatrick     if (Parser.parse(*I, Entries))
2235e5dd7070Spatrick       continue;
2236e5dd7070Spatrick 
2237e5dd7070Spatrick     for (SmallVectorImpl<EditEntry>::iterator
2238e5dd7070Spatrick            EI = Entries.begin(), EE = Entries.end(); EI != EE; ++EI) {
2239e5dd7070Spatrick       EditEntry &Entry = *EI;
2240e5dd7070Spatrick       if (!Entry.File)
2241e5dd7070Spatrick         continue;
2242e5dd7070Spatrick       std::pair<llvm::DenseSet<EditEntry>::iterator, bool>
2243e5dd7070Spatrick         Insert = EntriesSet.insert(Entry);
2244e5dd7070Spatrick       if (!Insert.second)
2245e5dd7070Spatrick         continue;
2246e5dd7070Spatrick 
2247a9ac8606Spatrick       FileEditEntries[*Entry.File].push_back(Entry);
2248e5dd7070Spatrick     }
2249e5dd7070Spatrick   }
2250e5dd7070Spatrick 
2251e5dd7070Spatrick   for (FileEditEntriesTy::iterator
2252e5dd7070Spatrick          I = FileEditEntries.begin(), E = FileEditEntries.end(); I != E; ++I) {
2253e5dd7070Spatrick     std::string TempFile = applyEditsToTemp(I->first, I->second,
2254e5dd7070Spatrick                                             FileMgr, *Diags);
2255e5dd7070Spatrick     if (TempFile.empty()) {
2256e5dd7070Spatrick       hasErrorOccurred = true;
2257e5dd7070Spatrick       continue;
2258e5dd7070Spatrick     }
2259e5dd7070Spatrick 
2260a9ac8606Spatrick     remap.emplace_back(std::string(I->first.getName()), TempFile);
2261e5dd7070Spatrick   }
2262e5dd7070Spatrick 
2263e5dd7070Spatrick   return hasErrorOccurred;
2264e5dd7070Spatrick }
2265