blob: bd1f57f9f8af4877f09c272d1104fc5c7bcea625 [file] [log] [blame]
Ted Kremenek30660a82012-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 Jahanian44b41b12013-07-18 22:17:33 +000010#include "Transforms.h"
Ted Kremenek30660a82012-03-06 20:06:33 +000011#include "clang/ARCMigrate/ARCMTActions.h"
Chandler Carruth55fc8732012-12-04 09:13:33 +000012#include "clang/AST/ASTConsumer.h"
13#include "clang/AST/ASTContext.h"
14#include "clang/AST/NSAPI.h"
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +000015#include "clang/AST/ParentMap.h"
Chandler Carruth55fc8732012-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 Kremenek30660a82012-03-06 20:06:33 +000022#include "clang/Frontend/CompilerInstance.h"
23#include "clang/Frontend/MultiplexConsumer.h"
Argyrios Kyrtzidis37ed1272012-12-04 07:27:05 +000024#include "clang/Lex/PPConditionalDirectiveRecord.h"
Chandler Carruth55fc8732012-12-04 09:13:33 +000025#include "clang/Lex/Preprocessor.h"
26#include "clang/Rewrite/Core/Rewriter.h"
Ted Kremenek30660a82012-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 Jahanianbaf15572013-07-03 23:05:00 +000036 void migrateObjCInterfaceDecl(ASTContext &Ctx, ObjCInterfaceDecl *D);
Fariborz Jahanian75568532013-07-12 22:32:19 +000037 void migrateProtocolConformance(ASTContext &Ctx,
38 const ObjCImplementationDecl *ImpDecl);
Fariborz Jahanianbbbb0fe2013-07-18 20:11:45 +000039 void migrateNSEnumDecl(ASTContext &Ctx, const EnumDecl *EnumDcl,
40 const TypedefDecl *TypedefDcl);
Fariborz Jahanian11638f72013-07-23 22:42:28 +000041 void migrateInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl);
Fariborz Jahanian26cf0462013-07-23 23:34:42 +000042 void migrateMethodInstanceType(ASTContext &Ctx, ObjCContainerDecl *CDecl,
43 ObjCMethodDecl *OM);
Fariborz Jahanianf647b692013-08-02 18:00:53 +000044 void migrateFactoryMethod(ASTContext &Ctx, ObjCContainerDecl *CDecl,
45 ObjCMethodDecl *OM);
Ted Kremenek30660a82012-03-06 20:06:33 +000046
47public:
48 std::string MigrateDir;
49 bool MigrateLiterals;
50 bool MigrateSubscripting;
Fariborz Jahaniand4129992013-07-09 16:59:14 +000051 bool MigrateProperty;
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +000052 OwningPtr<NSAPI> NSAPIObj;
53 OwningPtr<edit::EditedSource> Editor;
Ted Kremenek30660a82012-03-06 20:06:33 +000054 FileRemapper &Remapper;
55 FileManager &FileMgr;
Argyrios Kyrtzidis37ed1272012-12-04 07:27:05 +000056 const PPConditionalDirectiveRecord *PPRec;
Fariborz Jahanianbaf15572013-07-03 23:05:00 +000057 Preprocessor &PP;
Ted Kremenek30660a82012-03-06 20:06:33 +000058 bool IsOutputFile;
Fariborz Jahanian75568532013-07-12 22:32:19 +000059 llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ObjCProtocolDecls;
60
Ted Kremenek30660a82012-03-06 20:06:33 +000061 ObjCMigrateASTConsumer(StringRef migrateDir,
62 bool migrateLiterals,
63 bool migrateSubscripting,
Fariborz Jahaniand4129992013-07-09 16:59:14 +000064 bool migrateProperty,
Ted Kremenek30660a82012-03-06 20:06:33 +000065 FileRemapper &remapper,
66 FileManager &fileMgr,
Argyrios Kyrtzidis37ed1272012-12-04 07:27:05 +000067 const PPConditionalDirectiveRecord *PPRec,
Fariborz Jahanianbaf15572013-07-03 23:05:00 +000068 Preprocessor &PP,
Ted Kremenek30660a82012-03-06 20:06:33 +000069 bool isOutputFile = false)
70 : MigrateDir(migrateDir),
71 MigrateLiterals(migrateLiterals),
72 MigrateSubscripting(migrateSubscripting),
Fariborz Jahaniand4129992013-07-09 16:59:14 +000073 MigrateProperty(migrateProperty),
Fariborz Jahanianbaf15572013-07-03 23:05:00 +000074 Remapper(remapper), FileMgr(fileMgr), PPRec(PPRec), PP(PP),
Ted Kremenek30660a82012-03-06 20:06:33 +000075 IsOutputFile(isOutputFile) { }
76
77protected:
78 virtual void Initialize(ASTContext &Context) {
79 NSAPIObj.reset(new NSAPI(Context));
80 Editor.reset(new edit::EditedSource(Context.getSourceManager(),
David Blaikie4e4d0842012-03-11 07:00:24 +000081 Context.getLangOpts(),
Ted Kremenek30660a82012-03-06 20:06:33 +000082 PPRec));
83 }
84
85 virtual bool HandleTopLevelDecl(DeclGroupRef DG) {
86 for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I)
87 migrateDecl(*I);
88 return true;
89 }
90 virtual void HandleInterestingDecl(DeclGroupRef DG) {
91 // Ignore decls from the PCH.
92 }
93 virtual void HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
94 ObjCMigrateASTConsumer::HandleTopLevelDecl(DG);
95 }
96
97 virtual void HandleTranslationUnit(ASTContext &Ctx);
98};
99
100}
101
102ObjCMigrateAction::ObjCMigrateAction(FrontendAction *WrappedAction,
103 StringRef migrateDir,
104 bool migrateLiterals,
Fariborz Jahaniand4129992013-07-09 16:59:14 +0000105 bool migrateSubscripting,
106 bool migrateProperty)
Ted Kremenek30660a82012-03-06 20:06:33 +0000107 : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir),
108 MigrateLiterals(migrateLiterals), MigrateSubscripting(migrateSubscripting),
Fariborz Jahaniand4129992013-07-09 16:59:14 +0000109 MigrateProperty(migrateProperty),
Ted Kremenek30660a82012-03-06 20:06:33 +0000110 CompInst(0) {
111 if (MigrateDir.empty())
112 MigrateDir = "."; // user current directory if none is given.
113}
114
115ASTConsumer *ObjCMigrateAction::CreateASTConsumer(CompilerInstance &CI,
116 StringRef InFile) {
Argyrios Kyrtzidis37ed1272012-12-04 07:27:05 +0000117 PPConditionalDirectiveRecord *
118 PPRec = new PPConditionalDirectiveRecord(CompInst->getSourceManager());
119 CompInst->getPreprocessor().addPPCallbacks(PPRec);
Ted Kremenek30660a82012-03-06 20:06:33 +0000120 ASTConsumer *
121 WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
122 ASTConsumer *MTConsumer = new ObjCMigrateASTConsumer(MigrateDir,
123 MigrateLiterals,
124 MigrateSubscripting,
Fariborz Jahaniand4129992013-07-09 16:59:14 +0000125 MigrateProperty,
Ted Kremenek30660a82012-03-06 20:06:33 +0000126 Remapper,
127 CompInst->getFileManager(),
Fariborz Jahanianbaf15572013-07-03 23:05:00 +0000128 PPRec,
129 CompInst->getPreprocessor());
Ted Kremenek30660a82012-03-06 20:06:33 +0000130 ASTConsumer *Consumers[] = { MTConsumer, WrappedConsumer };
131 return new MultiplexConsumer(Consumers);
132}
133
134bool ObjCMigrateAction::BeginInvocation(CompilerInstance &CI) {
135 Remapper.initFromDisk(MigrateDir, CI.getDiagnostics(),
136 /*ignoreIfFilesChanges=*/true);
137 CompInst = &CI;
138 CI.getDiagnostics().setIgnoreAllWarnings(true);
Ted Kremenek30660a82012-03-06 20:06:33 +0000139 return true;
140}
141
142namespace {
143class ObjCMigrator : public RecursiveASTVisitor<ObjCMigrator> {
144 ObjCMigrateASTConsumer &Consumer;
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000145 ParentMap &PMap;
Ted Kremenek30660a82012-03-06 20:06:33 +0000146
147public:
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000148 ObjCMigrator(ObjCMigrateASTConsumer &consumer, ParentMap &PMap)
149 : Consumer(consumer), PMap(PMap) { }
Ted Kremenek30660a82012-03-06 20:06:33 +0000150
151 bool shouldVisitTemplateInstantiations() const { return false; }
152 bool shouldWalkTypesOfTypeLocs() const { return false; }
153
154 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
155 if (Consumer.MigrateLiterals) {
156 edit::Commit commit(*Consumer.Editor);
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000157 edit::rewriteToObjCLiteralSyntax(E, *Consumer.NSAPIObj, commit, &PMap);
Ted Kremenek30660a82012-03-06 20:06:33 +0000158 Consumer.Editor->commit(commit);
159 }
160
161 if (Consumer.MigrateSubscripting) {
162 edit::Commit commit(*Consumer.Editor);
163 edit::rewriteToObjCSubscriptSyntax(E, *Consumer.NSAPIObj, commit);
164 Consumer.Editor->commit(commit);
165 }
166
167 return true;
168 }
169
170 bool TraverseObjCMessageExpr(ObjCMessageExpr *E) {
171 // Do depth first; we want to rewrite the subexpressions first so that if
172 // we have to move expressions we will move them already rewritten.
173 for (Stmt::child_range range = E->children(); range; ++range)
174 if (!TraverseStmt(*range))
175 return false;
176
177 return WalkUpFromObjCMessageExpr(E);
178 }
179};
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000180
181class BodyMigrator : public RecursiveASTVisitor<BodyMigrator> {
182 ObjCMigrateASTConsumer &Consumer;
183 OwningPtr<ParentMap> PMap;
184
185public:
186 BodyMigrator(ObjCMigrateASTConsumer &consumer) : Consumer(consumer) { }
187
188 bool shouldVisitTemplateInstantiations() const { return false; }
189 bool shouldWalkTypesOfTypeLocs() const { return false; }
190
191 bool TraverseStmt(Stmt *S) {
192 PMap.reset(new ParentMap(S));
193 ObjCMigrator(Consumer, *PMap).TraverseStmt(S);
194 return true;
195 }
196};
Ted Kremenek30660a82012-03-06 20:06:33 +0000197}
198
199void ObjCMigrateASTConsumer::migrateDecl(Decl *D) {
200 if (!D)
201 return;
202 if (isa<ObjCMethodDecl>(D))
203 return; // Wait for the ObjC container declaration.
204
Argyrios Kyrtzidisa44b9702013-01-16 23:54:48 +0000205 BodyMigrator(*this).TraverseDecl(D);
Ted Kremenek30660a82012-03-06 20:06:33 +0000206}
207
Fariborz Jahanian44b41b12013-07-18 22:17:33 +0000208static bool rewriteToObjCProperty(const ObjCMethodDecl *Getter,
209 const ObjCMethodDecl *Setter,
210 const NSAPI &NS, edit::Commit &commit) {
211 ASTContext &Context = NS.getASTContext();
212 std::string PropertyString = "@property";
213 const ParmVarDecl *argDecl = *Setter->param_begin();
214 QualType ArgType = Context.getCanonicalType(argDecl->getType());
215 Qualifiers::ObjCLifetime propertyLifetime = ArgType.getObjCLifetime();
216
217 if (ArgType->isObjCRetainableType() &&
218 propertyLifetime == Qualifiers::OCL_Strong) {
219 if (const ObjCObjectPointerType *ObjPtrTy =
220 ArgType->getAs<ObjCObjectPointerType>()) {
221 ObjCInterfaceDecl *IDecl = ObjPtrTy->getObjectType()->getInterface();
222 if (IDecl &&
223 IDecl->lookupNestedProtocol(&Context.Idents.get("NSCopying")))
224 PropertyString += "(copy)";
225 }
226 }
227 else if (propertyLifetime == Qualifiers::OCL_Weak)
228 // TODO. More precise determination of 'weak' attribute requires
229 // looking into setter's implementation for backing weak ivar.
230 PropertyString += "(weak)";
231 else
232 PropertyString += "(unsafe_unretained)";
233
234 // strip off any ARC lifetime qualifier.
235 QualType CanResultTy = Context.getCanonicalType(Getter->getResultType());
236 if (CanResultTy.getQualifiers().hasObjCLifetime()) {
237 Qualifiers Qs = CanResultTy.getQualifiers();
238 Qs.removeObjCLifetime();
239 CanResultTy = Context.getQualifiedType(CanResultTy.getUnqualifiedType(), Qs);
240 }
241 PropertyString += " ";
242 PropertyString += CanResultTy.getAsString(Context.getPrintingPolicy());
243 PropertyString += " ";
244 PropertyString += Getter->getNameAsString();
245 commit.replace(CharSourceRange::getCharRange(Getter->getLocStart(),
246 Getter->getDeclaratorEndLoc()),
247 PropertyString);
248 SourceLocation EndLoc = Setter->getDeclaratorEndLoc();
249 // Get location past ';'
250 EndLoc = EndLoc.getLocWithOffset(1);
251 commit.remove(CharSourceRange::getCharRange(Setter->getLocStart(), EndLoc));
252 return true;
253}
254
Fariborz Jahanianbaf15572013-07-03 23:05:00 +0000255void ObjCMigrateASTConsumer::migrateObjCInterfaceDecl(ASTContext &Ctx,
256 ObjCInterfaceDecl *D) {
257 for (ObjCContainerDecl::method_iterator M = D->meth_begin(), MEnd = D->meth_end();
258 M != MEnd; ++M) {
259 ObjCMethodDecl *Method = (*M);
Fariborz Jahaniandca72892013-07-03 23:44:11 +0000260 if (Method->isPropertyAccessor() || Method->param_size() != 0)
Fariborz Jahanianbaf15572013-07-03 23:05:00 +0000261 continue;
262 // Is this method candidate to be a getter?
Fariborz Jahaniandca72892013-07-03 23:44:11 +0000263 QualType GRT = Method->getResultType();
264 if (GRT->isVoidType())
265 continue;
Fariborz Jahanian2a4ebcf2013-07-08 22:49:25 +0000266 // FIXME. Don't know what todo with attributes, skip for now.
267 if (Method->hasAttrs())
268 continue;
269
Fariborz Jahaniandca72892013-07-03 23:44:11 +0000270 Selector GetterSelector = Method->getSelector();
271 IdentifierInfo *getterName = GetterSelector.getIdentifierInfoForSlot(0);
272 Selector SetterSelector =
273 SelectorTable::constructSetterSelector(PP.getIdentifierTable(),
274 PP.getSelectorTable(),
275 getterName);
276 if (ObjCMethodDecl *SetterMethod = D->lookupMethod(SetterSelector, true)) {
277 // Is this a valid setter, matching the target getter?
278 QualType SRT = SetterMethod->getResultType();
279 if (!SRT->isVoidType())
Fariborz Jahanianbaf15572013-07-03 23:05:00 +0000280 continue;
Fariborz Jahaniandca72892013-07-03 23:44:11 +0000281 const ParmVarDecl *argDecl = *SetterMethod->param_begin();
Fariborz Jahanian52477fb2013-07-04 00:24:32 +0000282 QualType ArgType = argDecl->getType();
Fariborz Jahanian2a4ebcf2013-07-08 22:49:25 +0000283 if (!Ctx.hasSameUnqualifiedType(ArgType, GRT) ||
284 SetterMethod->hasAttrs())
Fariborz Jahanian52477fb2013-07-04 00:24:32 +0000285 continue;
Fariborz Jahanianafcb16f2013-07-05 20:46:03 +0000286 edit::Commit commit(*Editor);
Fariborz Jahanian44b41b12013-07-18 22:17:33 +0000287 rewriteToObjCProperty(Method, SetterMethod, *NSAPIObj, commit);
Fariborz Jahanianafcb16f2013-07-05 20:46:03 +0000288 Editor->commit(commit);
Fariborz Jahanianbaf15572013-07-03 23:05:00 +0000289 }
Fariborz Jahanianbaf15572013-07-03 23:05:00 +0000290 }
291}
292
Fariborz Jahaniane99b37c2013-07-13 00:04:20 +0000293static bool
Fariborz Jahanianbf13df22013-07-13 17:16:41 +0000294ClassImplementsAllMethodsAndProperties(ASTContext &Ctx,
Fariborz Jahaniane99b37c2013-07-13 00:04:20 +0000295 const ObjCImplementationDecl *ImpDecl,
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000296 const ObjCInterfaceDecl *IDecl,
Fariborz Jahaniane99b37c2013-07-13 00:04:20 +0000297 ObjCProtocolDecl *Protocol) {
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000298 // In auto-synthesis, protocol properties are not synthesized. So,
299 // a conforming protocol must have its required properties declared
300 // in class interface.
Fariborz Jahanian34cb2a32013-07-22 23:50:04 +0000301 bool HasAtleastOneRequiredProperty = false;
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000302 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition())
303 for (ObjCProtocolDecl::prop_iterator P = PDecl->prop_begin(),
304 E = PDecl->prop_end(); P != E; ++P) {
305 ObjCPropertyDecl *Property = *P;
306 if (Property->getPropertyImplementation() == ObjCPropertyDecl::Optional)
307 continue;
Fariborz Jahanian34cb2a32013-07-22 23:50:04 +0000308 HasAtleastOneRequiredProperty = true;
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000309 DeclContext::lookup_const_result R = IDecl->lookup(Property->getDeclName());
Fariborz Jahanian8d8bfb32013-07-16 21:59:42 +0000310 if (R.size() == 0) {
311 // Relax the rule and look into class's implementation for a synthesize
312 // or dynamic declaration. Class is implementing a property coming from
313 // another protocol. This still makes the target protocol as conforming.
314 if (!ImpDecl->FindPropertyImplDecl(
315 Property->getDeclName().getAsIdentifierInfo()))
316 return false;
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000317 }
Fariborz Jahanian8d8bfb32013-07-16 21:59:42 +0000318 else if (ObjCPropertyDecl *ClassProperty = dyn_cast<ObjCPropertyDecl>(R[0])) {
319 if ((ClassProperty->getPropertyAttributes()
320 != Property->getPropertyAttributes()) ||
321 !Ctx.hasSameType(ClassProperty->getType(), Property->getType()))
322 return false;
323 }
324 else
325 return false;
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000326 }
Fariborz Jahanian34cb2a32013-07-22 23:50:04 +0000327
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000328 // At this point, all required properties in this protocol conform to those
329 // declared in the class.
330 // Check that class implements the required methods of the protocol too.
Fariborz Jahanian34cb2a32013-07-22 23:50:04 +0000331 bool HasAtleastOneRequiredMethod = false;
Fariborz Jahanian8c355832013-07-16 00:20:21 +0000332 if (const ObjCProtocolDecl *PDecl = Protocol->getDefinition()) {
333 if (PDecl->meth_begin() == PDecl->meth_end())
Fariborz Jahanian34cb2a32013-07-22 23:50:04 +0000334 return HasAtleastOneRequiredProperty;
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000335 for (ObjCContainerDecl::method_iterator M = PDecl->meth_begin(),
336 MEnd = PDecl->meth_end(); M != MEnd; ++M) {
337 ObjCMethodDecl *MD = (*M);
Fariborz Jahanian8c355832013-07-16 00:20:21 +0000338 if (MD->isImplicit())
339 continue;
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000340 if (MD->getImplementationControl() == ObjCMethodDecl::Optional)
341 continue;
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000342 DeclContext::lookup_const_result R = ImpDecl->lookup(MD->getDeclName());
Fariborz Jahanian8c355832013-07-16 00:20:21 +0000343 if (R.size() == 0)
344 return false;
Fariborz Jahanian34cb2a32013-07-22 23:50:04 +0000345 bool match = false;
346 HasAtleastOneRequiredMethod = true;
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000347 for (unsigned I = 0, N = R.size(); I != N; ++I)
348 if (ObjCMethodDecl *ImpMD = dyn_cast<ObjCMethodDecl>(R[0]))
349 if (Ctx.ObjCMethodsAreEqual(MD, ImpMD)) {
350 match = true;
351 break;
352 }
353 if (!match)
354 return false;
355 }
Fariborz Jahanian8c355832013-07-16 00:20:21 +0000356 }
Fariborz Jahanian34cb2a32013-07-22 23:50:04 +0000357 if (HasAtleastOneRequiredProperty || HasAtleastOneRequiredMethod)
358 return true;
359 return false;
Fariborz Jahaniane99b37c2013-07-13 00:04:20 +0000360}
361
Fariborz Jahanian44b41b12013-07-18 22:17:33 +0000362static bool rewriteToObjCInterfaceDecl(const ObjCInterfaceDecl *IDecl,
363 llvm::SmallVectorImpl<ObjCProtocolDecl*> &ConformingProtocols,
364 const NSAPI &NS, edit::Commit &commit) {
365 const ObjCList<ObjCProtocolDecl> &Protocols = IDecl->getReferencedProtocols();
366 std::string ClassString;
367 SourceLocation EndLoc =
368 IDecl->getSuperClass() ? IDecl->getSuperClassLoc() : IDecl->getLocation();
369
370 if (Protocols.empty()) {
371 ClassString = '<';
372 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
373 ClassString += ConformingProtocols[i]->getNameAsString();
374 if (i != (e-1))
375 ClassString += ", ";
376 }
377 ClassString += "> ";
378 }
379 else {
380 ClassString = ", ";
381 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
382 ClassString += ConformingProtocols[i]->getNameAsString();
383 if (i != (e-1))
384 ClassString += ", ";
385 }
386 ObjCInterfaceDecl::protocol_loc_iterator PL = IDecl->protocol_loc_end() - 1;
387 EndLoc = *PL;
388 }
389
390 commit.insertAfterToken(EndLoc, ClassString);
391 return true;
392}
393
394static bool rewriteToNSEnumDecl(const EnumDecl *EnumDcl,
395 const TypedefDecl *TypedefDcl,
Fariborz Jahanian9f9e5432013-07-19 01:05:49 +0000396 const NSAPI &NS, edit::Commit &commit,
397 bool IsNSIntegerType) {
Fariborz Jahanianfaae53d2013-07-19 20:18:36 +0000398 std::string ClassString =
Fariborz Jahanian9f9e5432013-07-19 01:05:49 +0000399 IsNSIntegerType ? "typedef NS_ENUM(NSInteger, " : "typedef NS_OPTIONS(NSUInteger, ";
Fariborz Jahanian44b41b12013-07-18 22:17:33 +0000400 ClassString += TypedefDcl->getIdentifier()->getName();
401 ClassString += ')';
402 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
403 commit.replace(R, ClassString);
404 SourceLocation EndOfTypedefLoc = TypedefDcl->getLocEnd();
405 EndOfTypedefLoc = trans::findLocationAfterSemi(EndOfTypedefLoc, NS.getASTContext());
406 if (!EndOfTypedefLoc.isInvalid()) {
407 commit.remove(SourceRange(TypedefDcl->getLocStart(), EndOfTypedefLoc));
408 return true;
409 }
410 return false;
411}
412
Fariborz Jahaniane3069e22013-07-22 18:53:45 +0000413static bool rewriteToNSMacroDecl(const EnumDecl *EnumDcl,
Fariborz Jahanianfaae53d2013-07-19 20:18:36 +0000414 const TypedefDecl *TypedefDcl,
Fariborz Jahaniane3069e22013-07-22 18:53:45 +0000415 const NSAPI &NS, edit::Commit &commit,
416 bool IsNSIntegerType) {
417 std::string ClassString =
418 IsNSIntegerType ? "NS_ENUM(NSInteger, " : "NS_OPTIONS(NSUInteger, ";
Fariborz Jahanianfaae53d2013-07-19 20:18:36 +0000419 ClassString += TypedefDcl->getIdentifier()->getName();
420 ClassString += ')';
421 SourceRange R(EnumDcl->getLocStart(), EnumDcl->getLocStart());
422 commit.replace(R, ClassString);
423 SourceLocation TypedefLoc = TypedefDcl->getLocEnd();
424 commit.remove(SourceRange(TypedefLoc, TypedefLoc));
425 return true;
426}
427
Fariborz Jahaniane3069e22013-07-22 18:53:45 +0000428static bool UseNSOptionsMacro(ASTContext &Ctx,
429 const EnumDecl *EnumDcl) {
430 bool PowerOfTwo = true;
431 for (EnumDecl::enumerator_iterator EI = EnumDcl->enumerator_begin(),
432 EE = EnumDcl->enumerator_end(); EI != EE; ++EI) {
433 EnumConstantDecl *Enumerator = (*EI);
434 const Expr *InitExpr = Enumerator->getInitExpr();
435 if (!InitExpr) {
436 PowerOfTwo = false;
437 continue;
438 }
439 InitExpr = InitExpr->IgnoreImpCasts();
440 if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(InitExpr))
441 if (BO->isShiftOp() || BO->isBitwiseOp())
442 return true;
443
444 uint64_t EnumVal = Enumerator->getInitVal().getZExtValue();
445 if (PowerOfTwo && EnumVal && !llvm::isPowerOf2_64(EnumVal))
446 PowerOfTwo = false;
447 }
448 return PowerOfTwo;
449}
450
Fariborz Jahanian72685632013-07-19 17:44:32 +0000451void ObjCMigrateASTConsumer::migrateProtocolConformance(ASTContext &Ctx,
Fariborz Jahanian75568532013-07-12 22:32:19 +0000452 const ObjCImplementationDecl *ImpDecl) {
453 const ObjCInterfaceDecl *IDecl = ImpDecl->getClassInterface();
454 if (!IDecl || ObjCProtocolDecls.empty())
455 return;
456 // Find all implicit conforming protocols for this class
457 // and make them explicit.
458 llvm::SmallPtrSet<ObjCProtocolDecl *, 8> ExplicitProtocols;
459 Ctx.CollectInheritedProtocols(IDecl, ExplicitProtocols);
Fariborz Jahaniane99b37c2013-07-13 00:04:20 +0000460 llvm::SmallVector<ObjCProtocolDecl *, 8> PotentialImplicitProtocols;
Fariborz Jahanian75568532013-07-12 22:32:19 +0000461
Fariborz Jahaniane99b37c2013-07-13 00:04:20 +0000462 for (llvm::SmallPtrSet<ObjCProtocolDecl*, 32>::iterator I =
Fariborz Jahanian75568532013-07-12 22:32:19 +0000463 ObjCProtocolDecls.begin(),
464 E = ObjCProtocolDecls.end(); I != E; ++I)
465 if (!ExplicitProtocols.count(*I))
Fariborz Jahaniane99b37c2013-07-13 00:04:20 +0000466 PotentialImplicitProtocols.push_back(*I);
Fariborz Jahanian75568532013-07-12 22:32:19 +0000467
468 if (PotentialImplicitProtocols.empty())
469 return;
Fariborz Jahaniane99b37c2013-07-13 00:04:20 +0000470
471 // go through list of non-optional methods and properties in each protocol
472 // in the PotentialImplicitProtocols list. If class implements every one of the
473 // methods and properties, then this class conforms to this protocol.
474 llvm::SmallVector<ObjCProtocolDecl*, 8> ConformingProtocols;
475 for (unsigned i = 0, e = PotentialImplicitProtocols.size(); i != e; i++)
Fariborz Jahanianad4aaf12013-07-15 21:22:08 +0000476 if (ClassImplementsAllMethodsAndProperties(Ctx, ImpDecl, IDecl,
Fariborz Jahaniane99b37c2013-07-13 00:04:20 +0000477 PotentialImplicitProtocols[i]))
478 ConformingProtocols.push_back(PotentialImplicitProtocols[i]);
Fariborz Jahanian8c355832013-07-16 00:20:21 +0000479
480 if (ConformingProtocols.empty())
481 return;
Fariborz Jahaniane8280182013-07-17 00:02:22 +0000482
483 // Further reduce number of conforming protocols. If protocol P1 is in the list
484 // protocol P2 (P2<P1>), No need to include P1.
485 llvm::SmallVector<ObjCProtocolDecl*, 8> MinimalConformingProtocols;
486 for (unsigned i = 0, e = ConformingProtocols.size(); i != e; i++) {
487 bool DropIt = false;
488 ObjCProtocolDecl *TargetPDecl = ConformingProtocols[i];
489 for (unsigned i1 = 0, e1 = ConformingProtocols.size(); i1 != e1; i1++) {
490 ObjCProtocolDecl *PDecl = ConformingProtocols[i1];
491 if (PDecl == TargetPDecl)
492 continue;
493 if (PDecl->lookupProtocolNamed(
494 TargetPDecl->getDeclName().getAsIdentifierInfo())) {
495 DropIt = true;
496 break;
497 }
498 }
499 if (!DropIt)
500 MinimalConformingProtocols.push_back(TargetPDecl);
501 }
Fariborz Jahanian8c355832013-07-16 00:20:21 +0000502 edit::Commit commit(*Editor);
Fariborz Jahanian44b41b12013-07-18 22:17:33 +0000503 rewriteToObjCInterfaceDecl(IDecl, MinimalConformingProtocols,
504 *NSAPIObj, commit);
Fariborz Jahanian8c355832013-07-16 00:20:21 +0000505 Editor->commit(commit);
Fariborz Jahanian75568532013-07-12 22:32:19 +0000506}
507
Fariborz Jahanianbbbb0fe2013-07-18 20:11:45 +0000508void ObjCMigrateASTConsumer::migrateNSEnumDecl(ASTContext &Ctx,
509 const EnumDecl *EnumDcl,
510 const TypedefDecl *TypedefDcl) {
511 if (!EnumDcl->isCompleteDefinition() || EnumDcl->getIdentifier() ||
512 !TypedefDcl->getIdentifier())
513 return;
514
515 QualType qt = TypedefDcl->getTypeSourceInfo()->getType();
Fariborz Jahanian9f9e5432013-07-19 01:05:49 +0000516 bool IsNSIntegerType = NSAPIObj->isObjCNSIntegerType(qt);
517 bool IsNSUIntegerType = !IsNSIntegerType && NSAPIObj->isObjCNSUIntegerType(qt);
Fariborz Jahaniane3069e22013-07-22 18:53:45 +0000518
Fariborz Jahanianfaae53d2013-07-19 20:18:36 +0000519 if (!IsNSIntegerType && !IsNSUIntegerType) {
520 // Also check for typedef enum {...} TD;
521 if (const EnumType *EnumTy = qt->getAs<EnumType>()) {
522 if (EnumTy->getDecl() == EnumDcl) {
Fariborz Jahaniane3069e22013-07-22 18:53:45 +0000523 bool NSOptions = UseNSOptionsMacro(Ctx, EnumDcl);
524 if (NSOptions) {
525 if (!Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
526 return;
527 }
528 else if (!Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
Fariborz Jahanianfaae53d2013-07-19 20:18:36 +0000529 return;
530 edit::Commit commit(*Editor);
Fariborz Jahaniane3069e22013-07-22 18:53:45 +0000531 rewriteToNSMacroDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, !NSOptions);
Fariborz Jahanianfaae53d2013-07-19 20:18:36 +0000532 Editor->commit(commit);
Fariborz Jahanianfaae53d2013-07-19 20:18:36 +0000533 }
Fariborz Jahanianfaae53d2013-07-19 20:18:36 +0000534 }
Fariborz Jahaniane3069e22013-07-22 18:53:45 +0000535 return;
536 }
537 if (IsNSIntegerType && UseNSOptionsMacro(Ctx, EnumDcl)) {
538 // We may still use NS_OPTIONS based on what we find in the enumertor list.
539 IsNSIntegerType = false;
540 IsNSUIntegerType = true;
Fariborz Jahanianfaae53d2013-07-19 20:18:36 +0000541 }
Fariborz Jahanianbbbb0fe2013-07-18 20:11:45 +0000542
543 // NS_ENUM must be available.
Fariborz Jahanian9f9e5432013-07-19 01:05:49 +0000544 if (IsNSIntegerType && !Ctx.Idents.get("NS_ENUM").hasMacroDefinition())
545 return;
546 // NS_OPTIONS must be available.
547 if (IsNSUIntegerType && !Ctx.Idents.get("NS_OPTIONS").hasMacroDefinition())
Fariborz Jahanianbbbb0fe2013-07-18 20:11:45 +0000548 return;
549 edit::Commit commit(*Editor);
Fariborz Jahanian9f9e5432013-07-19 01:05:49 +0000550 rewriteToNSEnumDecl(EnumDcl, TypedefDcl, *NSAPIObj, commit, IsNSIntegerType);
Fariborz Jahanianbbbb0fe2013-07-18 20:11:45 +0000551 Editor->commit(commit);
552}
553
Fariborz Jahanianf647b692013-08-02 18:00:53 +0000554static void ReplaceWithInstancetype(const ObjCMigrateASTConsumer &ASTC,
555 ObjCMethodDecl *OM) {
556 SourceRange R;
557 std::string ClassString;
558 if (TypeSourceInfo *TSInfo = OM->getResultTypeSourceInfo()) {
559 TypeLoc TL = TSInfo->getTypeLoc();
560 R = SourceRange(TL.getBeginLoc(), TL.getEndLoc());
561 ClassString = "instancetype";
562 }
563 else {
564 R = SourceRange(OM->getLocStart(), OM->getLocStart());
565 ClassString = OM->isInstanceMethod() ? '-' : '+';
566 ClassString += " (instancetype)";
567 }
568 edit::Commit commit(*ASTC.Editor);
569 commit.replace(R, ClassString);
570 ASTC.Editor->commit(commit);
571}
572
Fariborz Jahanian26cf0462013-07-23 23:34:42 +0000573void ObjCMigrateASTConsumer::migrateMethodInstanceType(ASTContext &Ctx,
574 ObjCContainerDecl *CDecl,
575 ObjCMethodDecl *OM) {
Fariborz Jahanian11638f72013-07-23 22:42:28 +0000576 ObjCInstanceTypeFamily OIT_Family =
577 Selector::getInstTypeMethodFamily(OM->getSelector());
Fariborz Jahanianf647b692013-08-02 18:00:53 +0000578 if (OIT_Family == OIT_None) {
579 migrateFactoryMethod(Ctx, CDecl, OM);
Fariborz Jahanian11638f72013-07-23 22:42:28 +0000580 return;
Fariborz Jahanianf647b692013-08-02 18:00:53 +0000581 }
Fariborz Jahanian65f1a4c2013-07-24 19:18:37 +0000582 std::string ClassName;
Fariborz Jahanian211b4a22013-07-23 23:55:55 +0000583 switch (OIT_Family) {
584 case OIT_Array:
Fariborz Jahanian65f1a4c2013-07-24 19:18:37 +0000585 ClassName = "NSArray";
Fariborz Jahanian211b4a22013-07-23 23:55:55 +0000586 break;
587 case OIT_Dictionary:
Fariborz Jahanian65f1a4c2013-07-24 19:18:37 +0000588 ClassName = "NSDictionary";
589 break;
590 case OIT_MemManage:
591 ClassName = "NSObject";
Fariborz Jahanian211b4a22013-07-23 23:55:55 +0000592 break;
593 default:
594 return;
595 }
Fariborz Jahanian11638f72013-07-23 22:42:28 +0000596 if (!OM->getResultType()->isObjCIdType())
597 return;
598
599 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
600 if (!IDecl) {
601 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
602 IDecl = CatDecl->getClassInterface();
603 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
604 IDecl = ImpDecl->getClassInterface();
605 }
Fariborz Jahanian65f1a4c2013-07-24 19:18:37 +0000606 if (!IDecl ||
Fariborz Jahanianf647b692013-08-02 18:00:53 +0000607 !IDecl->lookupInheritedClass(&Ctx.Idents.get(ClassName))) {
608 migrateFactoryMethod(Ctx, CDecl, OM);
Fariborz Jahanian11638f72013-07-23 22:42:28 +0000609 return;
Fariborz Jahanian5e151c52013-07-24 18:31:42 +0000610 }
Fariborz Jahanianf647b692013-08-02 18:00:53 +0000611 ReplaceWithInstancetype(*this, OM);
Fariborz Jahanian11638f72013-07-23 22:42:28 +0000612}
613
614void ObjCMigrateASTConsumer::migrateInstanceType(ASTContext &Ctx,
615 ObjCContainerDecl *CDecl) {
616 // migrate methods which can have instancetype as their result type.
617 for (ObjCContainerDecl::method_iterator M = CDecl->meth_begin(),
618 MEnd = CDecl->meth_end();
619 M != MEnd; ++M) {
620 ObjCMethodDecl *Method = (*M);
621 migrateMethodInstanceType(Ctx, CDecl, Method);
622 }
623}
624
Fariborz Jahanianf647b692013-08-02 18:00:53 +0000625void ObjCMigrateASTConsumer::migrateFactoryMethod(ASTContext &Ctx,
626 ObjCContainerDecl *CDecl,
627 ObjCMethodDecl *OM) {
628 if (OM->isInstanceMethod() || !OM->getResultType()->isObjCIdType())
629 return;
630
631 // Candidate factory methods are + (id) NaMeXXX : ... which belong to a class
632 // NSYYYNamE with matching names be at least 3 characters long.
633 ObjCInterfaceDecl *IDecl = dyn_cast<ObjCInterfaceDecl>(CDecl);
634 if (!IDecl) {
635 if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl))
636 IDecl = CatDecl->getClassInterface();
637 else if (ObjCImplDecl *ImpDecl = dyn_cast<ObjCImplDecl>(CDecl))
638 IDecl = ImpDecl->getClassInterface();
639 }
640 if (!IDecl)
641 return;
642
643 std::string StringClassName = IDecl->getName();
644 StringRef LoweredClassName(StringClassName);
645 std::string StringLoweredClassName = LoweredClassName.lower();
646 LoweredClassName = StringLoweredClassName;
647
648 IdentifierInfo *MethodIdName = OM->getSelector().getIdentifierInfoForSlot(0);
649 std::string MethodName = MethodIdName->getName();
650 std::string MethodNameSubStr = MethodName.substr(0, 3);
651 StringRef MethodNamePrefix(MethodNameSubStr);
652 std::string StringLoweredMethodNamePrefix = MethodNamePrefix.lower();
653 MethodNamePrefix = StringLoweredMethodNamePrefix;
654 size_t Ix = LoweredClassName.rfind(MethodNamePrefix);
655 if (Ix == StringRef::npos)
656 return;
657 std::string ClassNamePostfix = LoweredClassName.substr(Ix);
658 StringRef LoweredMethodName(MethodName);
659 std::string StringLoweredMethodName = LoweredMethodName.lower();
660 LoweredMethodName = StringLoweredMethodName;
661 if (!LoweredMethodName.startswith(ClassNamePostfix))
662 return;
663 ReplaceWithInstancetype(*this, OM);
664}
665
Ted Kremenek30660a82012-03-06 20:06:33 +0000666namespace {
667
668class RewritesReceiver : public edit::EditsReceiver {
669 Rewriter &Rewrite;
670
671public:
672 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
673
674 virtual void insert(SourceLocation loc, StringRef text) {
675 Rewrite.InsertText(loc, text);
676 }
677 virtual void replace(CharSourceRange range, StringRef text) {
678 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
679 }
680};
681
682}
683
684void ObjCMigrateASTConsumer::HandleTranslationUnit(ASTContext &Ctx) {
Fariborz Jahanianbaf15572013-07-03 23:05:00 +0000685
686 TranslationUnitDecl *TU = Ctx.getTranslationUnitDecl();
Fariborz Jahaniand4129992013-07-09 16:59:14 +0000687 if (MigrateProperty)
688 for (DeclContext::decl_iterator D = TU->decls_begin(), DEnd = TU->decls_end();
689 D != DEnd; ++D) {
690 if (ObjCInterfaceDecl *CDecl = dyn_cast<ObjCInterfaceDecl>(*D))
691 migrateObjCInterfaceDecl(Ctx, CDecl);
Fariborz Jahanian75568532013-07-12 22:32:19 +0000692 else if (ObjCProtocolDecl *PDecl = dyn_cast<ObjCProtocolDecl>(*D))
693 ObjCProtocolDecls.insert(PDecl);
694 else if (const ObjCImplementationDecl *ImpDecl =
695 dyn_cast<ObjCImplementationDecl>(*D))
696 migrateProtocolConformance(Ctx, ImpDecl);
Fariborz Jahanianbbbb0fe2013-07-18 20:11:45 +0000697 else if (const EnumDecl *ED = dyn_cast<EnumDecl>(*D)) {
698 DeclContext::decl_iterator N = D;
699 ++N;
Fariborz Jahanian72685632013-07-19 17:44:32 +0000700 if (N != DEnd)
701 if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(*N))
702 migrateNSEnumDecl(Ctx, ED, TD);
Fariborz Jahanianbbbb0fe2013-07-18 20:11:45 +0000703 }
Fariborz Jahanian11638f72013-07-23 22:42:28 +0000704 // migrate methods which can have instancetype as their result type.
705 if (ObjCContainerDecl *CDecl = dyn_cast<ObjCContainerDecl>(*D))
706 migrateInstanceType(Ctx, CDecl);
Fariborz Jahaniand4129992013-07-09 16:59:14 +0000707 }
Fariborz Jahanianbaf15572013-07-03 23:05:00 +0000708
David Blaikie4e4d0842012-03-11 07:00:24 +0000709 Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOpts());
Ted Kremenek30660a82012-03-06 20:06:33 +0000710 RewritesReceiver Rec(rewriter);
711 Editor->applyRewrites(Rec);
712
713 for (Rewriter::buffer_iterator
714 I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) {
715 FileID FID = I->first;
716 RewriteBuffer &buf = I->second;
717 const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID);
718 assert(file);
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000719 SmallString<512> newText;
Ted Kremenek30660a82012-03-06 20:06:33 +0000720 llvm::raw_svector_ostream vecOS(newText);
721 buf.write(vecOS);
722 vecOS.flush();
723 llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy(
724 StringRef(newText.data(), newText.size()), file->getName());
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000725 SmallString<64> filePath(file->getName());
Ted Kremenek30660a82012-03-06 20:06:33 +0000726 FileMgr.FixupRelativePath(filePath);
727 Remapper.remap(filePath.str(), memBuf);
728 }
729
730 if (IsOutputFile) {
731 Remapper.flushToFile(MigrateDir, Ctx.getDiagnostics());
732 } else {
733 Remapper.flushToDisk(MigrateDir, Ctx.getDiagnostics());
734 }
735}
736
737bool MigrateSourceAction::BeginInvocation(CompilerInstance &CI) {
Argyrios Kyrtzidis013a2542012-05-24 16:48:23 +0000738 CI.getDiagnostics().setIgnoreAllWarnings(true);
Ted Kremenek30660a82012-03-06 20:06:33 +0000739 return true;
740}
741
742ASTConsumer *MigrateSourceAction::CreateASTConsumer(CompilerInstance &CI,
743 StringRef InFile) {
Argyrios Kyrtzidis37ed1272012-12-04 07:27:05 +0000744 PPConditionalDirectiveRecord *
745 PPRec = new PPConditionalDirectiveRecord(CI.getSourceManager());
746 CI.getPreprocessor().addPPCallbacks(PPRec);
Ted Kremenek30660a82012-03-06 20:06:33 +0000747 return new ObjCMigrateASTConsumer(CI.getFrontendOpts().OutputFile,
748 /*MigrateLiterals=*/true,
749 /*MigrateSubscripting=*/true,
Fariborz Jahaniand4129992013-07-09 16:59:14 +0000750 /*MigrateProperty*/true,
Ted Kremenek30660a82012-03-06 20:06:33 +0000751 Remapper,
752 CI.getFileManager(),
Argyrios Kyrtzidis37ed1272012-12-04 07:27:05 +0000753 PPRec,
Fariborz Jahanianbaf15572013-07-03 23:05:00 +0000754 CI.getPreprocessor(),
Ted Kremenek30660a82012-03-06 20:06:33 +0000755 /*isOutputFile=*/true);
756}