blob: 5d5383002283f49dd094efa45d3931e1bc71cd70 [file] [log] [blame]
Ted Kremenekf7639e12012-03-06 20:06:33 +00001//===--- ObjCMT.cpp - ObjC Migrate Tool -----------------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Fariborz Jahanian85e988b2013-07-18 22:17:33 +000010#include "Transforms.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000011#include "clang/ARCMigrate/ARCMTActions.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000012#include "clang/AST/ASTConsumer.h"
13#include "clang/AST/ASTContext.h"
14#include "clang/AST/NSAPI.h"
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +000015#include "clang/AST/ParentMap.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000016#include "clang/AST/RecursiveASTVisitor.h"
17#include "clang/Basic/FileManager.h"
18#include "clang/Edit/Commit.h"
19#include "clang/Edit/EditedSource.h"
20#include "clang/Edit/EditsReceiver.h"
21#include "clang/Edit/Rewriters.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000022#include "clang/Frontend/CompilerInstance.h"
23#include "clang/Frontend/MultiplexConsumer.h"
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +000024#include "clang/Lex/PPConditionalDirectiveRecord.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000025#include "clang/Lex/Preprocessor.h"
26#include "clang/Rewrite/Core/Rewriter.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000027#include "llvm/ADT/SmallString.h"
28
29using namespace clang;
30using namespace arcmt;
31
32namespace {
33
34class ObjCMigrateASTConsumer : public ASTConsumer {
35 void migrateDecl(Decl *D);
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000036 void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCInterfaceDecl *D);
Fariborz Jahanian1be01532013-07-12 22:32:19 +000037 void migrateProtocolConformance(ASTContext &Ctx,
38 const ObjCImplementationDecl *ImpDecl);
Fariborz Jahanian92463272013-07-18 20:11:45 +000039 void migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,
40 const TypedefDecl *TypedefDcl);
Fariborz Jahanian71221352013-07-23 22:42:28 +000041 void migrateInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);
Fariborz Jahanian670ef262013-07-23 23:34:42 +000042 void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,
43 ObjCMethodDecl *OM);
Ted Kremenekf7639e12012-03-06 20:06:33 +000044
45public:
46 std::string MigrateDir;
47 bool MigrateLiterals;
48 bool MigrateSubscripting;
Fariborz Jahaniand83ef842013-07-09 16:59:14 +000049 bool MigrateProperty;
Dmitri Gribenkof8579502013-01-12 19:30:44 +000050 OwningPtr<NSAPI> NSAPIObj;
51 OwningPtr<edit::EditedSource> Editor;
Ted Kremenekf7639e12012-03-06 20:06:33 +000052 FileRemapper &Remapper;
53 FileManager &FileMgr;
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +000054 const PPConditionalDirectiveRecord *PPRec;
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000055 Preprocessor &PP;
Ted Kremenekf7639e12012-03-06 20:06:33 +000056 bool IsOutputFile;
Fariborz Jahanian1be01532013-07-12 22:32:19 +000057 llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;
58
Ted Kremenekf7639e12012-03-06 20:06:33 +000059 ObjCMigrateASTConsumer(StringRef migrateDir,
60 bool migrateLiterals,
61 bool migrateSubscripting,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +000062 bool migrateProperty,
Ted Kremenekf7639e12012-03-06 20:06:33 +000063 FileRemapper &remapper,
64 FileManager &fileMgr,
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +000065 const PPConditionalDirectiveRecord *PPRec,
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000066 Preprocessor &PP,
Ted Kremenekf7639e12012-03-06 20:06:33 +000067 bool isOutputFile = false)
68 : MigrateDir(migrateDir),
69 MigrateLiterals(migrateLiterals),
70 MigrateSubscripting(migrateSubscripting),
Fariborz Jahaniand83ef842013-07-09 16:59:14 +000071 MigrateProperty(migrateProperty),
Fariborz Jahaniana7437f02013-07-03 23:05:00 +000072 Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
Ted Kremenekf7639e12012-03-06 20:06:33 +000073 IsOutputFile(isOutputFile) { }
74
75protected:
76 virtual void Initialize(ASTContext &Context) {
77 NSAPIObj.reset(new NSAPI(Context));
78 Editor.reset(new edit::EditedSource(Context.getSourceManager(),
David Blaikiebbafb8a2012-03-11 07:00:24 +000079 Context.getLangOpts(),
Ted Kremenekf7639e12012-03-06 20:06:33 +000080 PPRec));
81 }
82
83 virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
84 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
85 migrateDecl(*I);
86 return true;
87 }
88 virtual void HandleInterestingDecl(DeclGroupRef DG) {
89 // Ignore decls from the PCH.
90 }
91 virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
92 ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
93 }
94
95 virtual void HandleTranslationUnit(ASTContext &Ctx);
96};
97
98}
99
100ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction,
101 StringRef migrateDir,
102 bool migrateLiterals,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000103 bool migrateSubscripting,
104 bool migrateProperty)
Ted Kremenekf7639e12012-03-06 20:06:33 +0000105 : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir),
106 MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting),
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000107 MigrateProperty(migrateProperty),
Ted Kremenekf7639e12012-03-06 20:06:33 +0000108 CompInst(0) {
109 if (MigrateDir.empty())
110 MigrateDir = "."; // user current directory if none is given.
111}
112
113ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI,
114 StringRef InFile) {
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +0000115 PPConditionalDirectiveRecord *
116 PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
117 CompInst->getPreprocessor().addPPCallbacks(PPRec);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000118 ASTConsumer *
119 WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
120 ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir,
121 MigrateLiterals,
122 MigrateSubscripting,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000123 MigrateProperty,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000124 Remapper,
125 CompInst->getFileManager(),
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000126 PPRec,
127 CompInst->getPreprocessor());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000128 ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer };
129 return new MultiplexConsumer(Consumers);
130}
131
132bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
133 Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
134 /*ignoreIfFilesChanges=*/true);
135 CompInst = &CI;
136 CI.getDiagnostics().setIgnoreAllWarnings(true);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000137 return true;
138}
139
140namespace {
141class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
142 ObjCMigrateASTConsumer &Consumer;
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000143 ParentMap &PMap;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000144
145public:
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000146 ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
147 : Consumer(consumer), PMap(PMap) { }
Ted Kremenekf7639e12012-03-06 20:06:33 +0000148
149 bool shouldVisitTemplateInstantiations() const { return false; }
150 bool shouldWalkTypesOfTypeLocs() const { return false; }
151
152 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
153 if (Consumer.MigrateLiterals) {
154 edit::Commit commit(*Consumer.Editor);
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000155 edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000156 Consumer.Editor->commit(commit);
157 }
158
159 if (Consumer.MigrateSubscripting) {
160 edit::Commit commit(*Consumer.Editor);
161 edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
162 Consumer.Editor->commit(commit);
163 }
164
165 return true;
166 }
167
168 bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
169 // Do depth first; we want to rewrite the subexpressions first so that if
170 // we have to move expressions we will move them already rewritten.
171 for (Stmt::child_range range = E->children(); range; ++range)
172 if (!TraverseStmt(*range))
173 return false;
174
175 return WalkUpFromObjCMessageExpr(E);
176 }
177};
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000178
179class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
180 ObjCMigrateASTConsumer &Consumer;
181 OwningPtr<ParentMap> PMap;
182
183public:
184 BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
185
186 bool shouldVisitTemplateInstantiations() const { return false; }
187 bool shouldWalkTypesOfTypeLocs() const { return false; }
188
189 bool TraverseStmt(Stmt *S) {
190 PMap.reset(new ParentMap(S));
191 ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
192 return true;
193 }
194};
Ted Kremenekf7639e12012-03-06 20:06:33 +0000195}
196
197void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
198 if (!D)
199 return;
200 if (isa<ObjCMethodDecl>(D))
201 return; // Wait for the ObjC container declaration.
202
Argyrios Kyrtzidis6b4f3412013-01-16 23:54:48 +0000203 BodyMigrator(*this).TraverseDecl(D);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000204}
205
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000206static bool rewriteToObjCProperty(const ObjCMethodDecl *Getter,
207 const ObjCMethodDecl *Setter,
208 const NSAPI &NS, edit::Commit &commit) {
209 ASTContext &Context = NS.getASTContext();
210 std::string PropertyString = "@property";
211 const ParmVarDecl *argDecl = *Setter->param_begin();
212 QualType ArgType = Context.getCanonicalType(argDecl->getType());
213 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
214
215 if (ArgType->isObjCRetainableType() &&
216 propertyLifetime == Qualifiers::OCL_Strong) {
217 if (const ObjCObjectPointerType *ObjPtrTy =
218 ArgType->getAs<ObjCObjectPointerType>()) {
219 ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
220 if (IDecl &&
221 IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
222 PropertyString += "(copy)";
223 }
224 }
225 else if (propertyLifetime == Qualifiers::OCL_Weak)
226 // TODO. More precise determination of 'weak' attribute requires
227 // looking into setter's implementation for backing weak ivar.
228 PropertyString += "(weak)";
229 else
230 PropertyString += "(unsafe_unretained)";
231
232 // strip off any ARC lifetime qualifier.
233 QualType CanResultTy = Context.getCanonicalType(Getter->getResultType());
234 if (CanResultTy.getQualifiers().hasObjCLifetime()) {
235 Qualifiers Qs = CanResultTy.getQualifiers();
236 Qs.removeObjCLifetime();
237 CanResultTy = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
238 }
239 PropertyString += " ";
240 PropertyString += CanResultTy.getAsString(Context.getPrintingPolicy());
241 PropertyString += " ";
242 PropertyString += Getter->getNameAsString();
243 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(),
244 Getter->getDeclaratorEndLoc()),
245 PropertyString);
246 SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
247 // Get location past ';'
248 EndLoc = EndLoc.getLocWithOffset(1);
249 commit.remove(CharSourceRange::getCharRange(Setter->getLocStart(), EndLoc));
250 return true;
251}
252
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000253void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx,
254 ObjCInterfaceDecl *D) {
255 for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end();
256 M != MEnd; ++M) {
257 ObjCMethodDecl *Method = (*M);
Fariborz Jahaniande661002013-07-03 23:44:11 +0000258 if (Method->isPropertyAccessor() || Method->param_size() != 0)
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000259 continue;
260 // Is this method candidate to be a getter?
Fariborz Jahaniande661002013-07-03 23:44:11 +0000261 QualType GRT = Method->getResultType();
262 if (GRT->isVoidType())
263 continue;
Fariborz Jahanian7ac20e12013-07-08 22:49:25 +0000264 // FIXME. Don't know what todo with attributes, skip for now.
265 if (Method->hasAttrs())
266 continue;
267
Fariborz Jahaniande661002013-07-03 23:44:11 +0000268 Selector GetterSelector = Method->getSelector();
269 IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
270 Selector SetterSelector =
271 SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
272 PP.getSelectorTable(),
273 getterName);
274 if (ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true)) {
275 // Is this a valid setter, matching the target getter?
276 QualType SRT = SetterMethod->getResultType();
277 if (!SRT->isVoidType())
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000278 continue;
Fariborz Jahaniande661002013-07-03 23:44:11 +0000279 const ParmVarDecl *argDecl = *SetterMethod->param_begin();
Fariborz Jahanian43bbaac2013-07-04 00:24:32 +0000280 QualType ArgType = argDecl->getType();
Fariborz Jahanian7ac20e12013-07-08 22:49:25 +0000281 if (!Ctx.hasSameUnqualifiedType(ArgType, GRT) ||
282 SetterMethod->hasAttrs())
Fariborz Jahanian43bbaac2013-07-04 00:24:32 +0000283 continue;
Fariborz Jahanian266926d2013-07-05 20:46:03 +0000284 edit::Commit commit(*Editor);
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000285 rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit);
Fariborz Jahanian266926d2013-07-05 20:46:03 +0000286 Editor->commit(commit);
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000287 }
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000288 }
289}
290
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000291static bool
Fariborz Jahanian9a3512b2013-07-13 17:16:41 +0000292ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000293 const ObjCImplementationDecl *ImpDecl,
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000294 const ObjCInterfaceDecl *IDecl,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000295 ObjCProtocolDecl *Protocol) {
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000296 // In auto-synthesis, protocol properties are not synthesized. So,
297 // a conforming protocol must have its required properties declared
298 // in class interface.
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000299 bool HasAtleastOneRequiredProperty = false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000300 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
301 for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
302 E = PDecl->prop_end(); P != E; ++P) {
303 ObjCPropertyDecl *Property = *P;
304 if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
305 continue;
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000306 HasAtleastOneRequiredProperty = true;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000307 DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName());
Fariborz Jahanian2bc3dda2013-07-16 21:59:42 +0000308 if (R.size() == 0) {
309 // Relax the rule and look into class's implementation for a synthesize
310 // or dynamic declaration. Class is implementing a property coming from
311 // another protocol. This still makes the target protocol as conforming.
312 if (!ImpDecl->FindPropertyImplDecl(
313 Property->getDeclName().getAsIdentifierInfo()))
314 return false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000315 }
Fariborz Jahanian2bc3dda2013-07-16 21:59:42 +0000316 else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) {
317 if ((ClassProperty->getPropertyAttributes()
318 != Property->getPropertyAttributes()) ||
319 !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
320 return false;
321 }
322 else
323 return false;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000324 }
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000325
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000326 // At this point, all required properties in this protocol conform to those
327 // declared in the class.
328 // Check that class implements the required methods of the protocol too.
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000329 bool HasAtleastOneRequiredMethod = false;
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000330 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
331 if (PDecl->meth_begin() == PDecl->meth_end())
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000332 return HasAtleastOneRequiredProperty;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000333 for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(),
334 MEnd = PDecl->meth_end(); M != MEnd; ++M) {
335 ObjCMethodDecl *MD = (*M);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000336 if (MD->isImplicit())
337 continue;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000338 if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
339 continue;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000340 DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName());
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000341 if (R.size() == 0)
342 return false;
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000343 bool match = false;
344 HasAtleastOneRequiredMethod = true;
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000345 for (unsigned I = 0, N = R.size(); I != N; ++I)
346 if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0]))
347 if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
348 match = true;
349 break;
350 }
351 if (!match)
352 return false;
353 }
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000354 }
Fariborz Jahanian9e543af2013-07-22 23:50:04 +0000355 if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod)
356 return true;
357 return false;
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000358}
359
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000360static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
361 llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
362 const NSAPI &NS, edit::Commit &commit) {
363 const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
364 std::string ClassString;
365 SourceLocation EndLoc =
366 IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
367
368 if (Protocols.empty()) {
369 ClassString = '<';
370 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
371 ClassString += ConformingProtocols[i]->getNameAsString();
372 if (i != (e-1))
373 ClassString += ", ";
374 }
375 ClassString += "> ";
376 }
377 else {
378 ClassString = ", ";
379 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
380 ClassString += ConformingProtocols[i]->getNameAsString();
381 if (i != (e-1))
382 ClassString += ", ";
383 }
384 ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
385 EndLoc = *PL;
386 }
387
388 commit.insertAfterToken(EndLoc, ClassString);
389 return true;
390}
391
392static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
393 const TypedefDecl *TypedefDcl,
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000394 const NSAPI &NS, edit::Commit &commit,
395 bool IsNSIntegerType) {
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000396 std::string ClassString =
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000397 IsNSIntegerType ? "typedef NS_ENUM(NSInteger, " : "typedef NS_OPTIONS(NSUInteger, ";
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000398 ClassString += TypedefDcl->getIdentifier()->getName();
399 ClassString += ')';
400 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
401 commit.replace(R, ClassString);
402 SourceLocation EndOfTypedefLoc = TypedefDcl->getLocEnd();
403 EndOfTypedefLoc = trans::findLocationAfterSemi(EndOfTypedefLoc, NS.getASTContext());
404 if (!EndOfTypedefLoc.isInvalid()) {
405 commit.remove(SourceRange(TypedefDcl->getLocStart(), EndOfTypedefLoc));
406 return true;
407 }
408 return false;
409}
410
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000411static bool rewriteToNSMacroDecl(const EnumDecl *EnumDcl,
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000412 const TypedefDecl *TypedefDcl,
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000413 const NSAPI &NS, edit::Commit &commit,
414 bool IsNSIntegerType) {
415 std::string ClassString =
416 IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, ";
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000417 ClassString += TypedefDcl->getIdentifier()->getName();
418 ClassString += ')';
419 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
420 commit.replace(R, ClassString);
421 SourceLocation TypedefLoc = TypedefDcl->getLocEnd();
422 commit.remove(SourceRange(TypedefLoc, TypedefLoc));
423 return true;
424}
425
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000426static bool UseNSOptionsMacro(ASTContext &Ctx,
427 const EnumDecl *EnumDcl) {
428 bool PowerOfTwo = true;
429 for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(),
430 EE = EnumDcl->enumerator_end(); EI != EE; ++EI) {
431 EnumConstantDecl *Enumerator = (*EI);
432 const Expr *InitExpr = Enumerator->getInitExpr();
433 if (!InitExpr) {
434 PowerOfTwo = false;
435 continue;
436 }
437 InitExpr = InitExpr->IgnoreImpCasts();
438 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
439 if (BO->isShiftOp() || BO->isBitwiseOp())
440 return true;
441
442 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
443 if (PowerOfTwo && EnumVal && !llvm::isPowerOf2_64(EnumVal))
444 PowerOfTwo = false;
445 }
446 return PowerOfTwo;
447}
448
Fariborz Jahanian008ef722013-07-19 17:44:32 +0000449void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000450 const ObjCImplementationDecl *ImpDecl) {
451 const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
452 if (!IDecl || ObjCProtocolDecls.empty())
453 return;
454 // Find all implicit conforming protocols for this class
455 // and make them explicit.
456 llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
457 Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000458 llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000459
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000460 for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I =
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000461 ObjCProtocolDecls.begin(),
462 E = ObjCProtocolDecls.end(); I != E; ++I)
463 if (!ExplicitProtocols.count(*I))
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000464 PotentialImplicitProtocols.push_back(*I);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000465
466 if (PotentialImplicitProtocols.empty())
467 return;
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000468
469 // go through list of non-optional methods and properties in each protocol
470 // in the PotentialImplicitProtocols list. If class implements every one of the
471 // methods and properties, then this class conforms to this protocol.
472 llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
473 for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
Fariborz Jahaniand36150d2013-07-15 21:22:08 +0000474 if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
Fariborz Jahanian9eabf452013-07-13 00:04:20 +0000475 PotentialImplicitProtocols[i]))
476 ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000477
478 if (ConformingProtocols.empty())
479 return;
Fariborz Jahaniancb7b8de2013-07-17 00:02:22 +0000480
481 // Further reduce number of conforming protocols. If protocol P1 is in the list
482 // protocol P2 (P2<P1>), No need to include P1.
483 llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
484 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
485 bool DropIt = false;
486 ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
487 for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
488 ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
489 if (PDecl == TargetPDecl)
490 continue;
491 if (PDecl->lookupProtocolNamed(
492 TargetPDecl->getDeclName().getAsIdentifierInfo())) {
493 DropIt = true;
494 break;
495 }
496 }
497 if (!DropIt)
498 MinimalConformingProtocols.push_back(TargetPDecl);
499 }
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000500 edit::Commit commit(*Editor);
Fariborz Jahanian85e988b2013-07-18 22:17:33 +0000501 rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
502 *NSAPIObj, commit);
Fariborz Jahanian5bd5aff2013-07-16 00:20:21 +0000503 Editor->commit(commit);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000504}
505
Fariborz Jahanian92463272013-07-18 20:11:45 +0000506void ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
507 const EnumDecl *EnumDcl,
508 const TypedefDecl *TypedefDcl) {
509 if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
510 !TypedefDcl->getIdentifier())
511 return;
512
513 QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000514 bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt);
515 bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000516
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000517 if (!IsNSIntegerType && !IsNSUIntegerType) {
518 // Also check for typedef enum {...} TD;
519 if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
520 if (EnumTy->getDecl() == EnumDcl) {
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000521 bool NSOptions = UseNSOptionsMacro(Ctx, EnumDcl);
522 if (NSOptions) {
523 if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
524 return;
525 }
526 else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000527 return;
528 edit::Commit commit(*Editor);
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000529 rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000530 Editor->commit(commit);
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000531 }
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000532 }
Fariborz Jahaniand0f6f792013-07-22 18:53:45 +0000533 return;
534 }
535 if (IsNSIntegerType && UseNSOptionsMacro(Ctx, EnumDcl)) {
536 // We may still use NS_OPTIONS based on what we find in the enumertor list.
537 IsNSIntegerType = false;
538 IsNSUIntegerType = true;
Fariborz Jahanianc1c44f62013-07-19 20:18:36 +0000539 }
Fariborz Jahanian92463272013-07-18 20:11:45 +0000540
541 // NS_ENUM must be available.
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000542 if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
543 return;
544 // NS_OPTIONS must be available.
545 if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
Fariborz Jahanian92463272013-07-18 20:11:45 +0000546 return;
547 edit::Commit commit(*Editor);
Fariborz Jahanianb0057bb2013-07-19 01:05:49 +0000548 rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, IsNSIntegerType);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000549 Editor->commit(commit);
550}
551
Fariborz Jahanian670ef262013-07-23 23:34:42 +0000552void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
553 ObjCContainerDecl *CDecl,
554 ObjCMethodDecl *OM) {
Fariborz Jahanian71221352013-07-23 22:42:28 +0000555 ObjCInstanceTypeFamily OIT_Family =
556 Selector::getInstTypeMethodFamily(OM->getSelector());
557 if (OIT_Family == OIT_None)
558 return;
559 // TODO. Many more to come
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000560 switch (OIT_Family) {
561 case OIT_Array:
562 break;
563 case OIT_Dictionary:
564 break;
565 default:
566 return;
567 }
Fariborz Jahanian71221352013-07-23 22:42:28 +0000568 if (!OM->getResultType()->isObjCIdType())
569 return;
570
571 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
572 if (!IDecl) {
573 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
574 IDecl = CatDecl->getClassInterface();
575 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
576 IDecl = ImpDecl->getClassInterface();
577 }
Fariborz Jahanian631925f2013-07-23 23:55:55 +0000578 if (!IDecl)
579 return;
580
581 if (OIT_Family == OIT_Array &&
582 !IDecl->lookupInheritedClass(&Ctx.Idents.get("NSArray")))
583 return;
584 else if (OIT_Family == OIT_Dictionary &&
585 !IDecl->lookupInheritedClass(&Ctx.Idents.get("NSDictionary")))
Fariborz Jahanian71221352013-07-23 22:42:28 +0000586 return;
587
Fariborz Jahanian670ef262013-07-23 23:34:42 +0000588 TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo();
589 TypeLoc TL = TSInfo->getTypeLoc();
590 SourceRange R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
591 edit::Commit commit(*Editor);
592 std::string ClassString = "instancetype";
593 commit.replace(R, ClassString);
594 Editor->commit(commit);
Fariborz Jahanian71221352013-07-23 22:42:28 +0000595}
596
597void ObjCMigrateASTConsumer::migrateInstanceType(ASTContext &Ctx,
598 ObjCContainerDecl *CDecl) {
599 // migrate methods which can have instancetype as their result type.
600 for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(),
601 MEnd = CDecl->meth_end();
602 M != MEnd; ++M) {
603 ObjCMethodDecl *Method = (*M);
604 migrateMethodInstanceType(Ctx, CDecl, Method);
605 }
606}
607
Ted Kremenekf7639e12012-03-06 20:06:33 +0000608namespace {
609
610class RewritesReceiver : public edit::EditsReceiver {
611 Rewriter &Rewrite;
612
613public:
614 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
615
616 virtual void insert(SourceLocation loc, StringRef text) {
617 Rewrite.InsertText(loc, text);
618 }
619 virtual void replace(CharSourceRange range, StringRef text) {
620 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
621 }
622};
623
624}
625
626void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000627
628 TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000629 if (MigrateProperty)
630 for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
631 D != DEnd; ++D) {
632 if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
633 migrateObjCInterfaceDecl(Ctx, CDecl);
Fariborz Jahanian1be01532013-07-12 22:32:19 +0000634 else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D))
635 ObjCProtocolDecls.insert(PDecl);
636 else if (const ObjCImplementationDecl *ImpDecl =
637 dyn_cast<ObjCImplementationDecl>(*D))
638 migrateProtocolConformance(Ctx, ImpDecl);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000639 else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
640 DeclContext::decl_iterator N = D;
641 ++N;
Fariborz Jahanian008ef722013-07-19 17:44:32 +0000642 if (N != DEnd)
643 if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N))
644 migrateNSEnumDecl(Ctx, ED, TD);
Fariborz Jahanian92463272013-07-18 20:11:45 +0000645 }
Fariborz Jahanian71221352013-07-23 22:42:28 +0000646 // migrate methods which can have instancetype as their result type.
647 if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D))
648 migrateInstanceType(Ctx, CDecl);
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000649 }
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000650
David Blaikiebbafb8a2012-03-11 07:00:24 +0000651 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000652 RewritesReceiver Rec(rewriter);
653 Editor->applyRewrites(Rec);
654
655 for (Rewriter::buffer_iterator
656 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
657 FileID FID = I->first;
658 RewriteBuffer &buf = I->second;
659 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
660 assert(file);
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000661 SmallString<512> newText;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000662 llvm::raw_svector_ostream vecOS(newText);
663 buf.write(vecOS);
664 vecOS.flush();
665 llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
666 StringRef(newText.data(), newText.size()), file->getName());
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000667 SmallString<64> filePath(file->getName());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000668 FileMgr.FixupRelativePath(filePath);
669 Remapper.remap(filePath.str(), memBuf);
670 }
671
672 if (IsOutputFile) {
673 Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
674 } else {
675 Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
676 }
677}
678
679bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
Argyrios Kyrtzidisb4822602012-05-24 16:48:23 +0000680 CI.getDiagnostics().setIgnoreAllWarnings(true);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000681 return true;
682}
683
684ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
685 StringRef InFile) {
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +0000686 PPConditionalDirectiveRecord *
687 PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
688 CI.getPreprocessor().addPPCallbacks(PPRec);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000689 return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
690 /*MigrateLiterals=*/true,
691 /*MigrateSubscripting=*/true,
Fariborz Jahaniand83ef842013-07-09 16:59:14 +0000692 /*MigrateProperty*/true,
Ted Kremenekf7639e12012-03-06 20:06:33 +0000693 Remapper,
694 CI.getFileManager(),
Argyrios Kyrtzidisf3d587e2012-12-04 07:27:05 +0000695 PPRec,
Fariborz Jahaniana7437f02013-07-03 23:05:00 +0000696 CI.getPreprocessor(),
Ted Kremenekf7639e12012-03-06 20:06:33 +0000697 /*isOutputFile=*/true);
698}