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